summer-engine 4.5.0 → 4.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 (114) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +3 -2
  3. package/.codex-plugin/plugin.json +5 -4
  4. package/.cursor-plugin/plugin.json +4 -3
  5. package/.factory-plugin/plugin.json +4 -3
  6. package/CHANGELOG.md +50 -0
  7. package/README.md +3 -3
  8. package/commands/start.md +1 -1
  9. package/dist/cli/commands/api.d.ts +43 -0
  10. package/dist/cli/commands/api.js +191 -0
  11. package/dist/cli/commands/login.d.ts +2 -5
  12. package/dist/cli/commands/login.js +10 -81
  13. package/dist/cli/commands/setup.js +175 -32
  14. package/dist/cli/commands/token.d.ts +17 -0
  15. package/dist/cli/commands/token.js +25 -0
  16. package/dist/cli/index.js +4 -0
  17. package/dist/core/api-client.d.ts +5 -1
  18. package/dist/core/api-client.js +21 -0
  19. package/dist/core/capabilities/doctor.d.ts +1 -1
  20. package/dist/core/capabilities/doctor.js +1 -1
  21. package/dist/core/capabilities/game-task-plan.d.ts +2 -2
  22. package/dist/core/capabilities/navigation/open.d.ts +3 -0
  23. package/dist/core/capabilities/navigation/open.js +53 -0
  24. package/dist/core/capabilities/projects-local.js +2 -1
  25. package/dist/core/capabilities/runtime-control.d.ts +2 -2
  26. package/dist/core/capabilities/seeing/args.d.ts +4 -4
  27. package/dist/core/capabilities/tool-dispatch.js +37 -0
  28. package/dist/core/capabilities/ui-control.d.ts +2 -2
  29. package/dist/core/capabilities/view-set.d.ts +11 -0
  30. package/dist/core/capabilities/view-set.js +15 -0
  31. package/dist/core/capability-skew.d.ts +8 -0
  32. package/dist/core/capability-skew.js +18 -0
  33. package/dist/core/cli-session.d.ts +25 -0
  34. package/dist/core/cli-session.js +97 -0
  35. package/dist/core/engine-self-start.d.ts +20 -0
  36. package/dist/core/engine-self-start.js +108 -5
  37. package/dist/core/oauth.d.ts +15 -2
  38. package/dist/core/oauth.js +26 -3
  39. package/dist/core/ops/areas.d.ts +1 -1
  40. package/dist/core/ops/areas.js +5 -3
  41. package/dist/core/ops/engine-answer.js +5 -2
  42. package/dist/core/ops/engine.js +6 -0
  43. package/dist/core/ops/save-files.d.ts +2 -0
  44. package/dist/core/ops/save-files.js +13 -0
  45. package/dist/core/ops/studio-page.d.ts +22 -0
  46. package/dist/core/ops/studio-page.js +63 -0
  47. package/dist/core/sign-in-link.d.ts +38 -0
  48. package/dist/core/sign-in-link.js +106 -0
  49. package/dist/core/store.js +19 -1
  50. package/dist/core/summer-api.d.ts +114 -0
  51. package/dist/core/summer-api.js +281 -0
  52. package/dist/installer/agent-config.d.ts +29 -0
  53. package/dist/installer/agent-config.js +75 -11
  54. package/dist/installer/agent-detect.d.ts +44 -0
  55. package/dist/installer/agent-detect.js +166 -0
  56. package/dist/installer/agent-table.d.ts +19 -0
  57. package/dist/installer/agent-table.js +130 -0
  58. package/dist/installer/setup-error.d.ts +19 -0
  59. package/dist/installer/setup-error.js +34 -0
  60. package/dist/installer/version-check.d.ts +5 -0
  61. package/dist/installer/version-check.js +21 -0
  62. package/dist/mcp/ops-surface.d.ts +10 -0
  63. package/dist/mcp/ops-surface.js +18 -1
  64. package/dist/mcp/server.js +16 -4
  65. package/dist/mcp/tool-results.d.ts +9 -0
  66. package/dist/mcp/tool-results.js +64 -0
  67. package/dist/mcp/tool-surface.js +7 -1
  68. package/dist/mcp/tools/api-tools.d.ts +50 -0
  69. package/dist/mcp/tools/api-tools.js +65 -0
  70. package/dist/mcp/tools/local-project-tools.d.ts +14 -0
  71. package/dist/mcp/tools/local-project-tools.js +13 -2
  72. package/dist/mcp/tools/navigation-tools.js +1 -0
  73. package/dist/mcp/tools/scene-tools.js +7 -0
  74. package/dist/project-memory/agent-instructions.js +11 -3
  75. package/gemini-extension.json +3 -2
  76. package/library/references/mcp-tools-reference/mcp-tools-reference.md +15 -2
  77. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  78. package/library/skills/README.md +6 -5
  79. package/library/skills/design-npc/SKILL.md +1 -1
  80. package/library/skills/design-npc/resource.yaml +2 -1
  81. package/library/skills/make-game/SKILL.md +4 -4
  82. package/library/skills/navigate-summer/SKILL.md +23 -2
  83. package/library/skills/navigate-summer/resource.yaml +5 -3
  84. package/library/skills/summer-api/SKILL.md +198 -0
  85. package/library/skills/summer-api/resource.yaml +38 -0
  86. package/library/skills/summer-assets/SKILL.md +1 -1
  87. package/library/skills/summer-assets/resource.yaml +3 -2
  88. package/library/skills/summer-router/SKILL.md +251 -0
  89. package/library/skills/summer-router/resource.yaml +35 -0
  90. package/library/tools/api-batch/resource.yaml +82 -0
  91. package/library/tools/api-estimate/resource.yaml +52 -0
  92. package/library/tools/api-job/resource.yaml +58 -0
  93. package/library/tools/api-models/resource.yaml +57 -0
  94. package/library/tools/api-run/resource.yaml +70 -0
  95. package/library/tools/view-set/GUIDE.md +3 -0
  96. package/library/tools/view-set/resource.yaml +78 -0
  97. package/package.json +3 -3
  98. package/registry/generated/aliases.json +7 -0
  99. package/registry/generated/counts.json +3 -3
  100. package/registry/generated/gemini-extension.json +3 -2
  101. package/registry/generated/index.json +317 -52
  102. package/registry/generated/marketplace.claude.json +1 -1
  103. package/registry/generated/plugin.claude-directory.json +4 -3
  104. package/registry/generated/plugin.claude.json +3 -2
  105. package/registry/generated/plugin.codex.json +5 -4
  106. package/registry/generated/plugin.cursor.json +4 -3
  107. package/registry/generated/plugin.factory.json +4 -3
  108. package/registry/generated/server.json +2 -2
  109. package/registry/generated/skills-index.md +6 -5
  110. package/registry/generated/skills-registry.json +29 -15
  111. package/registry/operations/legacy-names.json +30 -0
  112. package/registry/operations/public-catalog.json +9169 -4926
  113. package/library/skills/ai-characters-in-games/SKILL.md +0 -235
  114. package/library/skills/ai-characters-in-games/resource.yaml +0 -30
@@ -0,0 +1,166 @@
1
+ /**
2
+ * `summer setup --detect`: which agents from agent-table.ts are on this
3
+ * computer, where each one's MCP config file is, and whether it already lists
4
+ * summer-engine. Reads only; never writes.
5
+ *
6
+ * How an agent counts as installed (any one hit; `detectedBy` names each):
7
+ * - bin: one of its commands is a file on PATH, or in a usual install folder
8
+ * (~/.local/bin, /opt/homebrew/bin, %APPDATA%\npm, ...). A desktop app's
9
+ * installer runs with a short PATH that leaves those out, so they are
10
+ * checked by name. On Windows each name is tried with every PATHEXT ending.
11
+ * - app: a macOS app bundle in /Applications or ~/Applications, or a Windows
12
+ * executable under %LOCALAPPDATA%, %ProgramFiles% or %ProgramFiles(x86)%.
13
+ * - folder: a config, data or install folder the agent makes on first run.
14
+ * - extension: a VS Code extension folder in ~/.vscode/extensions.
15
+ * The per-agent names live in agent-table.ts (`detect`).
16
+ */
17
+ import { accessSync, constants, existsSync, readdirSync, readFileSync, statSync } from "fs";
18
+ import { join } from "path";
19
+ import { allAgentSpecs, defaultPathContext, resolveMcpPath, } from "./agent-table.js";
20
+ import { listsSummerEngine, readRecordedMcpServer } from "./version-check.js";
21
+ import { toStepError } from "./setup-error.js";
22
+ export function detectAgents(options = {}) {
23
+ const ctx = options.ctx ?? defaultPathContext();
24
+ const binDirs = searchedBinDirs(ctx, options.systemBinDirs);
25
+ const macAppDirs = options.macAppDirs ?? ["/Applications", join(ctx.home, "Applications")];
26
+ return allAgentSpecs().map((spec) => detectAgent(spec, ctx, binDirs, macAppDirs));
27
+ }
28
+ function detectAgent(spec, ctx, binDirs, macAppDirs) {
29
+ const detectedBy = [
30
+ ...(spec.detect.bins ?? []).flatMap((name) => findBin(name, binDirs, ctx)).map((path) => ({ kind: "bin", path })),
31
+ ...findApps(spec, ctx, macAppDirs).map((path) => ({ kind: "app", path })),
32
+ ...(spec.detect.dirs?.(ctx) ?? []).filter((path) => existsSync(path)).map((path) => ({ kind: "folder", path })),
33
+ ...findVsCodeExtensions(spec.detect.vscodeExtensions ?? [], ctx).map((path) => ({ kind: "extension", path })),
34
+ ];
35
+ const unique = detectedBy.filter((hit, index) => detectedBy.findIndex((other) => other.path === hit.path) === index);
36
+ const resolved = resolveMcpPath(spec, "user", ctx);
37
+ const override = ctx.env[spec.envOverride];
38
+ const configPath = override || resolved.path;
39
+ const configExists = existsSync(configPath);
40
+ let mcpEntry = false;
41
+ let mcpServer = null;
42
+ let configError;
43
+ if (configExists) {
44
+ try {
45
+ const text = readFileSync(configPath, "utf-8");
46
+ mcpEntry = listsSummerEngine(text, spec.format);
47
+ mcpServer = readRecordedMcpServer(text, spec.format);
48
+ }
49
+ catch (error) {
50
+ configError = toStepError(error, "config_unreadable");
51
+ }
52
+ }
53
+ return {
54
+ id: spec.id,
55
+ label: spec.label,
56
+ status: spec.status,
57
+ installed: unique.length > 0,
58
+ programFound: unique.some((hit) => hit.kind !== "folder"),
59
+ detectedBy: unique,
60
+ configPath,
61
+ configScope: resolved.scope,
62
+ configFormat: spec.format,
63
+ configExists,
64
+ mcpEntry,
65
+ mcpServer,
66
+ summerGames: Boolean(spec.remoteMcp),
67
+ skills: spec.skills !== null,
68
+ ...(configError ? { configError } : {}),
69
+ };
70
+ }
71
+ /** PATH first, then the install folders a GUI-launched process's PATH leaves out. */
72
+ export function searchedBinDirs(ctx, systemBinDirs = ["/opt/homebrew/bin", "/usr/local/bin"]) {
73
+ const path = ctx.env.PATH ?? ctx.env.Path ?? "";
74
+ const sep = ctx.os === "win32" ? ";" : ":";
75
+ const fromPath = path.split(sep).filter(Boolean);
76
+ const home = ctx.home;
77
+ const usual = ctx.os === "win32"
78
+ ? [
79
+ join(home, ".local", "bin"),
80
+ join(ctx.env.APPDATA ?? join(home, "AppData", "Roaming"), "npm"),
81
+ join(localAppData(ctx), "Microsoft", "WinGet", "Links"),
82
+ join(localAppData(ctx), "pnpm"),
83
+ join(home, ".bun", "bin"),
84
+ join(home, ".volta", "bin"),
85
+ join(home, ".lmstudio", "bin"),
86
+ ]
87
+ : [
88
+ join(home, ".local", "bin"),
89
+ join(home, "bin"),
90
+ ...systemBinDirs,
91
+ join(home, ".npm-global", "bin"),
92
+ join(home, ".bun", "bin"),
93
+ join(home, ".volta", "bin"),
94
+ join(home, "Library", "pnpm"),
95
+ join(home, ".local", "share", "pnpm"),
96
+ join(home, ".claude", "local"),
97
+ join(home, ".opencode", "bin"),
98
+ join(home, ".lmstudio", "bin"),
99
+ ...nvmBinDirs(home),
100
+ ];
101
+ return [...new Set([...fromPath, ...usual])];
102
+ }
103
+ /** ~/.nvm/versions/node/<version>/bin for each installed Node (npm -g installs land there). */
104
+ function nvmBinDirs(home) {
105
+ const root = join(home, ".nvm", "versions", "node");
106
+ try {
107
+ return readdirSync(root).map((version) => join(root, version, "bin"));
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ }
113
+ function localAppData(ctx) {
114
+ return ctx.env.LOCALAPPDATA ?? join(ctx.home, "AppData", "Local");
115
+ }
116
+ function findBin(name, dirs, ctx) {
117
+ const names = ctx.os === "win32"
118
+ ? [name, ...(ctx.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean).map((ext) => `${name}${ext.toLowerCase()}`)]
119
+ : [name];
120
+ const hits = [];
121
+ for (const dir of dirs) {
122
+ for (const candidate of names) {
123
+ const path = join(dir, candidate);
124
+ if (isRunnable(path, ctx)) {
125
+ hits.push(path);
126
+ break;
127
+ }
128
+ }
129
+ }
130
+ return hits;
131
+ }
132
+ function isRunnable(path, ctx) {
133
+ try {
134
+ if (!statSync(path).isFile())
135
+ return false;
136
+ if (ctx.os !== "win32")
137
+ accessSync(path, constants.X_OK);
138
+ return true;
139
+ }
140
+ catch {
141
+ return false;
142
+ }
143
+ }
144
+ function findApps(spec, ctx, macAppDirs) {
145
+ if (ctx.os === "darwin") {
146
+ return (spec.detect.macApps ?? []).flatMap((app) => macAppDirs.map((dir) => join(dir, app))).filter((path) => existsSync(path));
147
+ }
148
+ if (ctx.os === "win32") {
149
+ const roots = [localAppData(ctx), ctx.env.ProgramFiles, ctx.env["ProgramFiles(x86)"]].filter((root) => Boolean(root));
150
+ return (spec.detect.winApps ?? []).flatMap((exe) => roots.map((root) => join(root, exe))).filter((path) => existsSync(path));
151
+ }
152
+ return [];
153
+ }
154
+ function findVsCodeExtensions(prefixes, ctx) {
155
+ if (prefixes.length === 0)
156
+ return [];
157
+ const dir = join(ctx.home, ".vscode", "extensions");
158
+ let entries;
159
+ try {
160
+ entries = readdirSync(dir);
161
+ }
162
+ catch {
163
+ return [];
164
+ }
165
+ return entries.filter((entry) => prefixes.some((prefix) => entry.toLowerCase().startsWith(prefix))).map((entry) => join(dir, entry));
166
+ }
@@ -101,6 +101,25 @@ export interface AgentSpec {
101
101
  /** null: no skills/rules mechanism — MCP only. */
102
102
  skills: SkillHome | null;
103
103
  noSkillsNote?: string;
104
+ /** How `summer setup --detect` tells the agent is on this computer (agent-detect.ts). */
105
+ detect: AgentDetect;
106
+ }
107
+ /**
108
+ * Where an agent shows up once it is installed, on macOS and Windows (Linux
109
+ * uses the bins and folders). Any one hit counts as installed; `--detect`
110
+ * names each hit, so a caller can tell a live binary from a leftover folder.
111
+ */
112
+ export interface AgentDetect {
113
+ /** Commands: looked up on PATH and in the usual install folders a GUI app's short PATH misses (agent-detect.ts). */
114
+ bins?: readonly string[];
115
+ /** macOS app bundles, looked up in /Applications and ~/Applications. */
116
+ macApps?: readonly string[];
117
+ /** Windows executables, relative to %LOCALAPPDATA%, %ProgramFiles% and %ProgramFiles(x86)%. */
118
+ winApps?: readonly string[];
119
+ /** Folders the agent creates on install or first run (config, data or install folder). */
120
+ dirs?: (ctx: PathContext) => string[];
121
+ /** VS Code extension id prefixes, looked up in ~/.vscode/extensions (folders named <id>-<version>). */
122
+ vscodeExtensions?: readonly string[];
104
123
  }
105
124
  /** True when this location is the shared agentskills.io folder. */
106
125
  export declare function isSharedSkillsPath(path: string): boolean;
@@ -114,6 +114,11 @@ const SPECS = [
114
114
  signIn: (name) => `In Claude Code, run /mcp, choose ${name} and sign in.`,
115
115
  },
116
116
  mcp: { user: home(".claude.json"), project: project(".mcp.json") },
117
+ detect: {
118
+ // The native installer puts claude in ~/.local/bin (macOS and Windows); older npm installs in ~/.claude/local.
119
+ bins: ["claude"],
120
+ dirs: (ctx) => [join(ctx.home, ".claude"), join(ctx.home, ".claude.json")],
121
+ },
117
122
  restart: "Restart Claude Code or run /mcp in a new session.",
118
123
  skills: skillDirs(home(".claude", "skills"), project(".claude", "skills")),
119
124
  },
@@ -129,6 +134,12 @@ const SPECS = [
129
134
  user: (ctx) => join(appConfigRoot(ctx), "Claude", "claude_desktop_config.json"),
130
135
  project: null,
131
136
  },
137
+ detect: {
138
+ macApps: ["Claude.app"],
139
+ // Squirrel install: %LOCALAPPDATA%/AnthropicClaude/claude.exe.
140
+ winApps: [join("AnthropicClaude", "claude.exe")],
141
+ dirs: (ctx) => [join(appConfigRoot(ctx), "Claude")],
142
+ },
132
143
  restart: "Quit and reopen Claude Desktop; the summer-engine tools appear under the tools menu in a new chat.",
133
144
  skills: null,
134
145
  noSkillsNote: "Claude Desktop has no skills folder. The MCP server ships summer_docs_search and summer_docs_read, so the model can pull Summer guidance in-chat.",
@@ -150,6 +161,12 @@ const SPECS = [
150
161
  project: project(".codex", "config.toml"),
151
162
  projectNote: "Codex only loads project .codex/config.toml from trusted projects.",
152
163
  },
164
+ detect: {
165
+ // The codex CLI (npm or Homebrew) and the Codex desktop app share $CODEX_HOME (default ~/.codex).
166
+ bins: ["codex"],
167
+ macApps: ["Codex.app"],
168
+ dirs: (ctx) => [ctx.env.CODEX_HOME || join(ctx.home, ".codex")],
169
+ },
153
170
  restart: "Restart Codex or run /mcp in a new session.",
154
171
  skills: sharedSkills(),
155
172
  },
@@ -165,6 +182,12 @@ const SPECS = [
165
182
  signIn: (name) => `In Cursor Settings, open MCP and click Connect (or Authenticate) next to ${name}.`,
166
183
  },
167
184
  mcp: { user: home(".cursor", "mcp.json"), project: project(".cursor", "mcp.json") },
185
+ detect: {
186
+ bins: ["cursor", "cursor-agent"],
187
+ macApps: ["Cursor.app"],
188
+ winApps: [join("Programs", "cursor", "Cursor.exe")],
189
+ dirs: (ctx) => [join(ctx.home, ".cursor")],
190
+ },
168
191
  restart: "Restart Cursor and enable the summer-engine MCP server if prompted.",
169
192
  skills: sharedSkills({
170
193
  reloadHint: "Cursor loads skills in the next chat. Summer 3.0 wrote rule files to .cursor/rules and 3.1.0 wrote .cursor/skills; `--force` removes both.",
@@ -189,6 +212,12 @@ const SPECS = [
189
212
  project: null,
190
213
  movedFrom: home(".codeium", "windsurf", "mcp_config.json"),
191
214
  },
215
+ detect: {
216
+ bins: ["windsurf", "devin"],
217
+ macApps: ["Windsurf.app", "Devin.app"],
218
+ winApps: [join("Programs", "Windsurf", "Windsurf.exe"), join("Programs", "Devin", "Devin.exe")],
219
+ dirs: (ctx) => [join(xdgOrAppData(ctx), "devin"), join(ctx.home, ".codeium", "windsurf")],
220
+ },
192
221
  restart: "Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings. Devin's docs say mcp_config.json configures the Cascade agent; for the Devin agent add the server in the app's MCP settings with the same command.",
193
222
  skills: sharedSkills({ legacyDirs: { user: home(".codeium", "windsurf", "skills"), project: project(".windsurf", "skills") } }),
194
223
  },
@@ -207,6 +236,12 @@ const SPECS = [
207
236
  user: home(".gemini", "config", "mcp_config.json"),
208
237
  project: project(".agents", "mcp_config.json"),
209
238
  },
239
+ detect: {
240
+ bins: ["antigravity", "agy"],
241
+ macApps: ["Antigravity.app"],
242
+ winApps: [join("Programs", "Antigravity", "Antigravity.exe")],
243
+ dirs: (ctx) => [join(ctx.home, ".gemini", "antigravity"), join(ctx.home, ".gemini", "config")],
244
+ },
210
245
  restart: "In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list (IDE and CLI share this file).",
211
246
  skills: skillDirs(home(".gemini", "config", "skills"), project(".agents", "skills")),
212
247
  },
@@ -223,6 +258,7 @@ const SPECS = [
223
258
  user: home(".gemini", "extensions", "summer-engine", "gemini-extension.json"),
224
259
  project: null,
225
260
  },
261
+ detect: { bins: ["gemini"], dirs: (ctx) => [join(ctx.home, ".gemini", "extensions")] },
226
262
  restart: "Run `summer skills install --all --agent gemini` if skills were not installed, then restart Gemini CLI (or `gemini extensions enable summer-engine` if it is disabled).",
227
263
  skills: skillDirs(home(".gemini", "extensions", "summer-engine", "skills"), null),
228
264
  },
@@ -241,6 +277,11 @@ const SPECS = [
241
277
  user: (ctx) => vsCodeGlobalStorage(ctx, "saoudrizwan.claude-dev", "cline_mcp_settings.json"),
242
278
  project: null,
243
279
  },
280
+ detect: {
281
+ // A VS Code extension: its install folder, or the storage it makes on first run.
282
+ vscodeExtensions: ["saoudrizwan.claude-dev-"],
283
+ dirs: (ctx) => [join(vsCodeUserDir(ctx), "globalStorage", "saoudrizwan.claude-dev")],
284
+ },
244
285
  restart: "Restart VS Code so Cline reloads its MCP config.",
245
286
  skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")),
246
287
  },
@@ -256,6 +297,7 @@ const SPECS = [
256
297
  signIn: (name) => `Run \`cline mcp\`, pick ${name} and choose Authorize OAuth.`,
257
298
  },
258
299
  mcp: { user: home(".cline", "data", "settings", "cline_mcp_settings.json"), project: null },
300
+ detect: { bins: ["cline"], dirs: (ctx) => [join(ctx.home, ".cline", "data")] },
259
301
  restart: "Restart the Cline CLI so it reloads its MCP settings.",
260
302
  skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")),
261
303
  },
@@ -272,6 +314,10 @@ const SPECS = [
272
314
  user: (ctx) => vsCodeGlobalStorage(ctx, "rooveterinaryinc.roo-cline", "cline_mcp_settings.json"),
273
315
  project: null,
274
316
  },
317
+ detect: {
318
+ vscodeExtensions: ["rooveterinaryinc.roo-cline-"],
319
+ dirs: (ctx) => [join(vsCodeUserDir(ctx), "globalStorage", "rooveterinaryinc.roo-cline")],
320
+ },
275
321
  restart: "Restart VS Code so Roo Code reloads its MCP config.",
276
322
  skills: {
277
323
  kind: "cline-rule-dir",
@@ -295,6 +341,12 @@ const SPECS = [
295
341
  user: (ctx) => join(xdgOrAppData(ctx), "kilo", "kilo.json"),
296
342
  project: project("kilo.json"),
297
343
  },
344
+ detect: {
345
+ // The kilo CLI, or the VS Code extension.
346
+ bins: ["kilo", "kilocode"],
347
+ vscodeExtensions: ["kilocode.kilo-code-"],
348
+ dirs: (ctx) => [join(xdgOrAppData(ctx), "kilo"), join(ctx.home, ".kilo")],
349
+ },
298
350
  restart: "Restart Kilo (CLI or the VS Code extension) so it reloads kilo.json.",
299
351
  skills: skillDirs(home(".kilo", "skills"), project(".kilo", "skills")),
300
352
  },
@@ -310,6 +362,7 @@ const SPECS = [
310
362
  signIn: (name) => `Copilot CLI opens summer.games to sign in when ${name} first connects; /mcp signs in again.`,
311
363
  },
312
364
  mcp: { user: home(".copilot", "mcp-config.json"), project: project(".mcp.json") },
365
+ detect: { bins: ["copilot"], dirs: (ctx) => [join(ctx.home, ".copilot")] },
313
366
  restart: "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session.",
314
367
  skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")),
315
368
  },
@@ -327,6 +380,13 @@ const SPECS = [
327
380
  signIn: (name) => `In VS Code, click Allow when ${name} asks to authenticate, then sign in on summer.games.`,
328
381
  },
329
382
  mcp: { user: (ctx) => join(vsCodeUserDir(ctx), "mcp.json"), project: project(".vscode", "mcp.json") },
383
+ detect: {
384
+ // Copilot Chat ships with VS Code, so VS Code itself is the signal.
385
+ bins: ["code"],
386
+ macApps: ["Visual Studio Code.app"],
387
+ winApps: [join("Programs", "Microsoft VS Code", "Code.exe"), join("Microsoft VS Code", "Code.exe")],
388
+ dirs: (ctx) => [vsCodeUserDir(ctx)],
389
+ },
330
390
  restart: "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode.",
331
391
  skills: sharedSkills({ legacyDirs: { user: home(".copilot", "skills"), project: project(".github", "skills") } }),
332
392
  },
@@ -339,6 +399,10 @@ const SPECS = [
339
399
  format: "json-vscode",
340
400
  // No Summer Games entry: Visual Studio's OAuth redirect for MCP is not documented; not checked against api.summer.games.
341
401
  mcp: { user: home(".mcp.json"), project: project(".mcp.json") },
402
+ detect: {
403
+ // Windows only (Visual Studio for Mac is retired): the install root under Program Files.
404
+ winApps: [join("Microsoft Visual Studio", "Installer", "vswhere.exe")],
405
+ },
342
406
  restart: "Restart Visual Studio, open Copilot Chat in Agent mode, and enable summer-engine in the tools picker.",
343
407
  skills: null,
344
408
  noSkillsNote: "Visual Studio's Copilot has no skills folder yet; the MCP server ships summer_docs_search and summer_docs_read for in-chat guidance.",
@@ -358,6 +422,10 @@ const SPECS = [
358
422
  user: (ctx) => join(xdgOrAppData(ctx), "github-copilot", "intellij", "mcp.json"),
359
423
  project: null,
360
424
  },
425
+ detect: {
426
+ // The Copilot plugin writes its settings here on first run; the IDE itself can be any JetBrains product.
427
+ dirs: (ctx) => [join(xdgOrAppData(ctx), "github-copilot", "intellij")],
428
+ },
361
429
  restart: "Restart the JetBrains IDE and open Copilot Chat in Agent mode; summer-engine shows in the tools list.",
362
430
  skills: null,
363
431
  noSkillsNote: "Copilot in JetBrains documents no skills folder; the MCP server ships summer_docs_search and summer_docs_read for in-chat guidance.",
@@ -377,6 +445,12 @@ const SPECS = [
377
445
  user: (ctx) => join(xdgOrAppData(ctx), "opencode", "opencode.json"),
378
446
  project: project("opencode.json"),
379
447
  },
448
+ detect: {
449
+ // The install script puts opencode in ~/.opencode/bin.
450
+ bins: ["opencode"],
451
+ macApps: ["OpenCode.app"],
452
+ dirs: (ctx) => [join(xdgOrAppData(ctx), "opencode"), join(ctx.home, ".opencode")],
453
+ },
380
454
  restart: "Restart OpenCode so it reloads opencode.json.",
381
455
  skills: sharedSkills({
382
456
  reloadHint: "OpenCode loads skills from this folder on the next session. Summer 3.0 wrote markdown under agents/summer and 3.1.0 wrote opencode/skills; `--force` removes both.",
@@ -399,6 +473,12 @@ const SPECS = [
399
473
  user: (ctx) => join(xdgOrAppData(ctx), "zed", "settings.json"),
400
474
  project: null,
401
475
  },
476
+ detect: {
477
+ bins: ["zed"],
478
+ macApps: ["Zed.app", "Zed Preview.app"],
479
+ winApps: [join("Programs", "Zed", "Zed.exe")],
480
+ dirs: (ctx) => [join(xdgOrAppData(ctx), "zed")],
481
+ },
402
482
  restart: "Zed reloads settings.json live; open the Agent panel and check summer-engine under MCP servers.",
403
483
  skills: sharedSkills(),
404
484
  },
@@ -414,6 +494,12 @@ const SPECS = [
414
494
  signIn: (name) => `Kiro opens summer.games to sign in when ${name} connects (Kiro CLI: /mcp auth).`,
415
495
  },
416
496
  mcp: { user: home(".kiro", "settings", "mcp.json"), project: project(".kiro", "settings", "mcp.json") },
497
+ detect: {
498
+ bins: ["kiro", "kiro-cli"],
499
+ macApps: ["Kiro.app"],
500
+ winApps: [join("Programs", "Kiro", "Kiro.exe")],
501
+ dirs: (ctx) => [join(ctx.home, ".kiro")],
502
+ },
417
503
  restart: "Kiro reloads MCP config on save; open the MCP Servers view to confirm summer-engine is connected.",
418
504
  skills: skillDirs(home(".kiro", "skills"), project(".kiro", "skills")),
419
505
  },
@@ -443,6 +529,12 @@ const SPECS = [
443
529
  : join(xdgConfig(ctx), "goose", "config.yaml"),
444
530
  project: null,
445
531
  },
532
+ detect: {
533
+ bins: ["goose"],
534
+ macApps: ["Goose.app"],
535
+ winApps: [join("Programs", "Goose", "Goose.exe")],
536
+ dirs: (ctx) => [ctx.os === "win32" ? join(appData(ctx), "Block", "goose") : join(xdgConfig(ctx), "goose")],
537
+ },
446
538
  restart: "Restart Goose (CLI or Desktop); summer-engine is listed under Extensions.",
447
539
  skills: skillDirs((ctx) => join(xdgConfig(ctx), "agents", "skills"), project(".agents", "skills")),
448
540
  },
@@ -458,6 +550,7 @@ const SPECS = [
458
550
  signIn: (name) => `Run \`hermes mcp login ${name}\`, or sign in on summer.games when Hermes first connects.`,
459
551
  },
460
552
  mcp: { user: home(".hermes", "config.yaml"), project: null },
553
+ detect: { bins: ["hermes"], dirs: (ctx) => [join(ctx.home, ".hermes")] },
461
554
  restart: "Run /reload-mcp in Hermes Agent (or restart it).",
462
555
  skills: skillDirs(home(".hermes", "skills"), SHARED_SKILLS_PROJECT, {
463
556
  reloadHint: "Project skills need `hermes skills trust` before Hermes loads them.",
@@ -480,6 +573,12 @@ const SPECS = [
480
573
  project: project(".trae", "mcp.json"),
481
574
  projectNote: "Trae manages user-level MCP servers in its UI; writing the project file .trae/mcp.json instead.",
482
575
  },
576
+ detect: {
577
+ bins: ["trae"],
578
+ macApps: ["Trae.app"],
579
+ winApps: [join("Programs", "Trae", "Trae.exe")],
580
+ dirs: (ctx) => [join(appConfigRoot(ctx), "Trae"), join(ctx.home, ".trae")],
581
+ },
483
582
  restart: "Restart Trae and open the MCP settings; summer-engine should show as connected.",
484
583
  skills: null,
485
584
  noSkillsNote: "Trae documents no skills folder; the MCP server ships summer_docs_search and summer_docs_read for in-chat guidance.",
@@ -496,6 +595,7 @@ const SPECS = [
496
595
  signIn: (name) => `In Qwen Code, run /mcp auth ${name} and sign in.`,
497
596
  },
498
597
  mcp: { user: home(".qwen", "settings.json"), project: project(".qwen", "settings.json") },
598
+ detect: { bins: ["qwen"], dirs: (ctx) => [join(ctx.home, ".qwen")] },
499
599
  restart: "Restart Qwen Code or run /mcp in a new session.",
500
600
  skills: skillDirs(home(".qwen", "skills"), project(".qwen", "skills")),
501
601
  },
@@ -511,6 +611,7 @@ const SPECS = [
511
611
  signIn: (name) => `Run \`kimi mcp auth ${name}\`; it opens summer.games to sign in.`,
512
612
  },
513
613
  mcp: { user: home(".kimi-code", "mcp.json"), project: project(".kimi-code", "mcp.json") },
614
+ detect: { bins: ["kimi"], dirs: (ctx) => [join(ctx.home, ".kimi-code"), join(ctx.home, ".kimi")] },
514
615
  restart: "Restart Kimi Code CLI so it reconnects its MCP servers.",
515
616
  skills: sharedSkills({ legacyDirs: { user: home(".kimi-code", "skills"), project: project(".kimi-code", "skills") } }),
516
617
  },
@@ -529,6 +630,7 @@ const SPECS = [
529
630
  user: home(".config", "crush", "crushrc"),
530
631
  project: project(".crushrc"),
531
632
  },
633
+ detect: { bins: ["crush"], dirs: (ctx) => [join(ctx.home, ".config", "crush"), join(ctx.home, ".local", "share", "crush")] },
532
634
  restart: "Restart Crush so it reloads crushrc.",
533
635
  skills: sharedSkills({ legacyDirs: { user: home(".config", "crush", "skills"), project: project(".crush", "skills") } }),
534
636
  },
@@ -544,6 +646,7 @@ const SPECS = [
544
646
  signIn: (name) => `Amp opens summer.games to sign in for ${name} when it starts.`,
545
647
  },
546
648
  mcp: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "settings.json"), project: null },
649
+ detect: { bins: ["amp"], dirs: (ctx) => [join(xdgOrAppData(ctx), "amp")] },
547
650
  restart: "Restart Amp so it reloads settings.json.",
548
651
  skills: sharedSkills({ legacyDirs: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "skills"), project: null } }),
549
652
  },
@@ -559,6 +662,7 @@ const SPECS = [
559
662
  signIn: (name) => `In Droid, run /mcp, choose ${name} and Authenticate.`,
560
663
  },
561
664
  mcp: { user: home(".factory", "mcp.json"), project: project(".factory", "mcp.json") },
665
+ detect: { bins: ["droid"], dirs: (ctx) => [join(ctx.home, ".factory")] },
562
666
  restart: "Restart droid or run /mcp in the active session.",
563
667
  skills: sharedSkills({ legacyDirs: { user: home(".factory", "skills"), project: project(".factory", "skills") } }),
564
668
  },
@@ -574,6 +678,7 @@ const SPECS = [
574
678
  signIn: (name) => `In Junie, run /mcp, open ${name} and choose Authorize.`,
575
679
  },
576
680
  mcp: { user: home(".junie", "mcp", "mcp.json"), project: project(".junie", "mcp", "mcp.json") },
681
+ detect: { bins: ["junie"], dirs: (ctx) => [join(ctx.home, ".junie")] },
577
682
  restart: "Restart the JetBrains IDE so Junie reloads its MCP config.",
578
683
  skills: null,
579
684
  noSkillsNote: "Junie has no skills folder; put project guidance in .junie/guidelines.md. The MCP server ships summer_docs_search and summer_docs_read for in-chat guidance.",
@@ -590,6 +695,11 @@ const SPECS = [
590
695
  signIn: (name) => `Warp opens summer.games to sign in when ${name} starts.`,
591
696
  },
592
697
  mcp: { user: home(".warp", ".mcp.json"), project: project(".warp", ".mcp.json") },
698
+ detect: {
699
+ macApps: ["Warp.app"],
700
+ winApps: [join("Programs", "Warp", "warp.exe")],
701
+ dirs: (ctx) => [join(ctx.home, ".warp")],
702
+ },
593
703
  restart: "Warp detects the file and spawns the server; check Settings > AI > MCP servers.",
594
704
  skills: sharedSkills({ legacyDirs: { user: home(".warp", "skills"), project: project(".warp", "skills") } }),
595
705
  },
@@ -602,6 +712,11 @@ const SPECS = [
602
712
  format: "json-transport",
603
713
  // No Summer Games entry: Rovo Dev CLI has no OAuth sign-in for remote MCP servers (headers only).
604
714
  mcp: { user: home(".rovodev", "mcp.json"), project: null },
715
+ detect: {
716
+ // Rovo Dev runs as `acli rovodev`; acli alone is the Atlassian CLI, so the ~/.rovodev folder is the firmer signal.
717
+ bins: ["acli"],
718
+ dirs: (ctx) => [join(ctx.home, ".rovodev")],
719
+ },
605
720
  restart: "Restart Rovo Dev CLI so it reconnects its MCP servers.",
606
721
  skills: sharedSkills({ legacyDirs: { user: home(".rovodev", "skills"), project: project(".rovodev", "skills") } }),
607
722
  },
@@ -614,6 +729,12 @@ const SPECS = [
614
729
  format: "json",
615
730
  // No Summer Games entry: Qoder's remote-server fields in settings.json are not documented.
616
731
  mcp: { user: home(".qoder", "settings.json"), project: project(".mcp.json") },
732
+ detect: {
733
+ bins: ["qodercli"],
734
+ macApps: ["Qoder.app"],
735
+ winApps: [join("Programs", "Qoder", "Qoder.exe")],
736
+ dirs: (ctx) => [join(ctx.home, ".qoder")],
737
+ },
617
738
  restart: "Restart Qoder CLI so it reloads its MCP settings (the Qoder IDE manages servers in its UI).",
618
739
  skills: skillDirs(home(".qoder", "skills"), project(".qoder", "skills")),
619
740
  },
@@ -629,6 +750,7 @@ const SPECS = [
629
750
  signIn: (name) => `In Grok Build, run /mcps, select ${name} and press i to sign in.`,
630
751
  },
631
752
  mcp: { user: home(".grok", "config.toml"), project: project(".grok", "config.toml") },
753
+ detect: { bins: ["grok"], dirs: (ctx) => [join(ctx.home, ".grok")] },
632
754
  restart: "Restart Grok Build so it reloads config.toml (it also reads ~/.claude.json and .cursor/mcp.json).",
633
755
  skills: sharedSkills({ legacyDirs: { user: home(".grok", "skills"), project: project(".grok", "skills") } }),
634
756
  },
@@ -644,6 +766,7 @@ const SPECS = [
644
766
  signIn: (name) => `In Vibe, run /mcp login ${name}.`,
645
767
  },
646
768
  mcp: { user: home(".vibe", "config.toml"), project: project(".vibe", "config.toml") },
769
+ detect: { bins: ["vibe"], dirs: (ctx) => [join(ctx.home, ".vibe")] },
647
770
  restart: "Restart Vibe so it reloads config.toml.",
648
771
  skills: skillDirs(home(".vibe", "skills"), SHARED_SKILLS_PROJECT, { legacyDirs: { user: home(".vibe", "skills"), project: project(".vibe", "skills") } }),
649
772
  },
@@ -656,6 +779,13 @@ const SPECS = [
656
779
  format: "json",
657
780
  // No Summer Games entry: LM Studio's MCP client registration is closed and unverified.
658
781
  mcp: { user: home(".lmstudio", "mcp.json"), project: null },
782
+ detect: {
783
+ // The app installs the lms command into ~/.lmstudio/bin.
784
+ bins: ["lms"],
785
+ macApps: ["LM Studio.app"],
786
+ winApps: [join("Programs", "LM Studio", "LM Studio.exe"), join("Programs", "lm-studio", "LM Studio.exe")],
787
+ dirs: (ctx) => [join(ctx.home, ".lmstudio")],
788
+ },
659
789
  restart: "Open LM Studio, toggle on the summer-engine MCP server in the Program tab, and raise the loaded model's context length to 32k or higher.",
660
790
  skills: null,
661
791
  noSkillsNote: "LM Studio has no rules or skills folder. The MCP server ships summer_docs_search and summer_docs_read, so the model can pull Summer guidance in-chat.",
@@ -0,0 +1,19 @@
1
+ /**
2
+ * An error `summer setup` reports with a stable code, so a caller reading
3
+ * `--json` (the Summer desktop installer) can branch on the code instead of
4
+ * the message. The message stays the human sentence it always was.
5
+ */
6
+ export declare class SetupError extends Error {
7
+ readonly code: string;
8
+ constructor(code: string, message: string);
9
+ }
10
+ /**
11
+ * The code for any error a setup step throws: a SetupError's own code, a
12
+ * file-system failure by kind, else `fallback` (the step's generic code).
13
+ */
14
+ export declare function setupErrorCode(error: unknown, fallback: string): string;
15
+ export interface SetupStepError {
16
+ code: string;
17
+ message: string;
18
+ }
19
+ export declare function toStepError(error: unknown, fallback: string): SetupStepError;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * An error `summer setup` reports with a stable code, so a caller reading
3
+ * `--json` (the Summer desktop installer) can branch on the code instead of
4
+ * the message. The message stays the human sentence it always was.
5
+ */
6
+ export class SetupError extends Error {
7
+ code;
8
+ constructor(code, message) {
9
+ super(message);
10
+ this.code = code;
11
+ this.name = "SetupError";
12
+ }
13
+ }
14
+ /** File-system errno codes that mean "this file cannot be written here". */
15
+ const NOT_WRITABLE = new Set(["EACCES", "EPERM", "EROFS", "EISDIR", "ENOTDIR"]);
16
+ /**
17
+ * The code for any error a setup step throws: a SetupError's own code, a
18
+ * file-system failure by kind, else `fallback` (the step's generic code).
19
+ */
20
+ export function setupErrorCode(error, fallback) {
21
+ if (error instanceof SetupError)
22
+ return error.code;
23
+ const errno = error?.code;
24
+ if (typeof errno === "string") {
25
+ if (NOT_WRITABLE.has(errno))
26
+ return "config_not_writable";
27
+ if (errno === "ENOSPC")
28
+ return "disk_full";
29
+ }
30
+ return fallback;
31
+ }
32
+ export function toStepError(error, fallback) {
33
+ return { code: setupErrorCode(error, fallback), message: error instanceof Error ? error.message : String(error) };
34
+ }
@@ -121,6 +121,8 @@ export interface RecordedInstall {
121
121
  * --cli`, or skills installed alone) and `setup --cli` can set the agent
122
122
  * up: the refresh keeps it without an MCP server. */
123
123
  cliOnly?: boolean;
124
+ /** The bundled node executable the entry runs (`setup --runtime <node> --package-dir <dir>`); cliPath is then that package's dist/bin/summer.js. */
125
+ runtime?: string;
124
126
  }
125
127
  /** An npm dist-tag (`latest`, `next`) or an exact version (`3.9.1`). */
126
128
  export type RefreshChannel = string;
@@ -170,6 +172,9 @@ export declare function buildSkillsVersionCheck(input: SkillsVersionCheckInput):
170
172
  * summer-engine entry refreshes with `--cli`, so the refresh adds no MCP server.
171
173
  */
172
174
  export declare function skillsRefreshCommand(agent: string, install: RecordedInstall | null, channel?: RefreshChannel): string;
175
+ /** The shape `summer setup --runtime <node> --package-dir <dir>` writes:
176
+ * `<absolute path to node> <dir>/dist/bin/summer.js mcp` (bundledRuntimeServerConfig). */
177
+ export declare function isBundledRuntimeServerConfig(server: StdioMcpServerConfig): boolean;
173
178
  /** The shape `summer setup --local-dev` writes: `node <…>/dist/bin/summer.js mcp`
174
179
  * (createSummerMcpServerConfig(true)). */
175
180
  export declare function isLocalDevServerConfig(server: StdioMcpServerConfig): boolean;
@@ -405,11 +405,29 @@ export function skillsRefreshCommand(agent, install, channel = "latest") {
405
405
  ? `node ${install.cliPath} setup ${agent} --cli --yes --force`
406
406
  : `npx clear-npx-cache && npx -y summer-engine@${channel} setup ${agent} --cli --yes --force`;
407
407
  }
408
+ if (install?.runtime && install.cliPath) {
409
+ // Keep the bundled runtime: the npx form needs a Node.js the person may not have.
410
+ const packageDir = install.cliPath.replace(/[\\/]dist[\\/]bin[\\/]summer\.js$/, "");
411
+ const runtime = shellArg(install.runtime);
412
+ return `${runtime} ${shellArg(install.cliPath)} setup ${agent} --runtime ${runtime} --package-dir ${shellArg(packageDir)} --yes --force`;
413
+ }
408
414
  if (install?.localDev && install.cliPath) {
409
415
  return `node ${install.cliPath} setup ${agent} --local-dev --yes --force`;
410
416
  }
411
417
  return `npx clear-npx-cache && npx -y summer-engine@${channel} setup ${agent} --yes --force${channel === "latest" ? "" : ` --channel ${channel}`}`;
412
418
  }
419
+ /** A path in a printed command: quoted when it has a space (~/Library/Application Support, C:\\Program Files). */
420
+ function shellArg(value) {
421
+ return /[\s"'$`]/.test(value) ? JSON.stringify(value) : value;
422
+ }
423
+ /** The shape `summer setup --runtime <node> --package-dir <dir>` writes:
424
+ * `<absolute path to node> <dir>/dist/bin/summer.js mcp` (bundledRuntimeServerConfig). */
425
+ export function isBundledRuntimeServerConfig(server) {
426
+ return (/^([a-z]:)?[\\/]/i.test(server.command) &&
427
+ /(^|[\\/])node(\.exe)?$/i.test(server.command) &&
428
+ /[\\/]dist[\\/]bin[\\/]summer\.js$/.test(server.args[0] ?? "") &&
429
+ server.args[1] === "mcp");
430
+ }
413
431
  /** The shape `summer setup --local-dev` writes: `node <…>/dist/bin/summer.js mcp`
414
432
  * (createSummerMcpServerConfig(true)). */
415
433
  export function isLocalDevServerConfig(server) {
@@ -550,6 +568,9 @@ export async function detectRecordedInstall(agent) {
550
568
  const text = existsSync(target.path) ? readFileSync(target.path, "utf-8") : "";
551
569
  const recorded = readRecordedMcpServer(text, target.format);
552
570
  if (recorded) {
571
+ if (isBundledRuntimeServerConfig(recorded)) {
572
+ return { localDev: false, cliPath: recorded.args[0], runtime: recorded.command, source: "agent-config" };
573
+ }
553
574
  const localDev = isLocalDevServerConfig(recorded);
554
575
  return { localDev, cliPath: localDev ? recorded.args[0] ?? null : null, source: "agent-config" };
555
576
  }