summer-engine 3.5.0 → 3.5.2
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -1
- package/.codex-plugin/plugin.json +2 -1
- package/.cursor-plugin/plugin.json +2 -1
- package/.factory-plugin/plugin.json +2 -1
- package/CHANGELOG.md +23 -0
- package/README.md +3 -3
- package/commands/summer.md +1 -0
- package/dist/cli/commands/setup.js +2 -2
- package/dist/core/capabilities/publish-build.d.ts +4 -0
- package/dist/core/capabilities/publish-build.js +67 -34
- package/dist/core/capabilities/runtime-control.d.ts +2 -2
- package/dist/installer/agent-config.d.ts +5 -5
- package/dist/installer/agent-config.js +96 -32
- package/dist/installer/agent-table.d.ts +7 -6
- package/dist/installer/agent-table.js +117 -6
- package/dist/mcp/hosted-mount.js +2 -1
- package/dist/mcp/provider-safe-schema.d.ts +10 -0
- package/dist/mcp/provider-safe-schema.js +96 -0
- package/dist/mcp/server.js +3 -0
- package/gemini-extension.json +2 -1
- package/integrations/README.md +6 -0
- package/library/skills/README.md +3 -2
- package/library/skills/export-and-ship/SKILL.md +2 -0
- package/library/skills/make-game/SKILL.md +21 -2
- package/library/skills/make-game/resource.yaml +1 -0
- package/library/skills/multiplayer/SKILL.md +3 -1
- package/library/skills/multiplayer/resource.yaml +1 -0
- package/library/skills/multiplayer-movement/SKILL.md +1 -1
- package/library/skills/multiplayer-movement/files.md +2 -3
- package/library/skills/multiplayer-project/SKILL.md +20 -4
- package/library/skills/multiplayer-project/files.md +2 -3
- package/library/skills/multiplayer-publish/SKILL.md +86 -5
- package/library/skills/multiplayer-publish/resource.yaml +1 -0
- package/library/skills/multiplayer-state/SKILL.md +1 -1
- package/library/skills/multiplayer-state/files.md +2 -3
- package/library/skills/multiplayer-testing/SKILL.md +13 -0
- package/library/skills/multiplayer-testing/resource.yaml +1 -0
- package/library/skills/publish-your-game/SKILL.md +2 -0
- package/library/skills/release-readiness/SKILL.md +298 -0
- package/library/skills/release-readiness/resource.yaml +41 -0
- package/library/skills/summer-parties/SKILL.md +1 -1
- package/package.json +1 -1
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +2 -1
- package/registry/generated/index.json +63 -11
- package/registry/generated/marketplace.claude.json +1 -1
- package/registry/generated/plugin.claude.json +2 -1
- package/registry/generated/plugin.codex.json +2 -1
- package/registry/generated/plugin.cursor.json +2 -1
- package/registry/generated/plugin.factory.json +2 -1
- package/registry/generated/skills-index.md +3 -2
- package/registry/generated/skills-registry.json +14 -0
|
@@ -108,7 +108,10 @@ const SPECS = [
|
|
|
108
108
|
status: "active",
|
|
109
109
|
envOverride: "SUMMER_CLAUDE_CONFIG_FILE",
|
|
110
110
|
format: "json-stdio",
|
|
111
|
-
remoteMcp: {
|
|
111
|
+
remoteMcp: {
|
|
112
|
+
entry: (url) => ({ type: "http", url }),
|
|
113
|
+
signIn: (name) => `In Claude Code, run /mcp, choose ${name} and sign in.`,
|
|
114
|
+
},
|
|
112
115
|
mcp: { user: home(".claude.json"), project: project(".mcp.json") },
|
|
113
116
|
restart: "Restart Claude Code or run /mcp in a new session.",
|
|
114
117
|
skills: skillDirs(home(".claude", "skills"), project(".claude", "skills")),
|
|
@@ -120,6 +123,7 @@ const SPECS = [
|
|
|
120
123
|
status: "active",
|
|
121
124
|
envOverride: "SUMMER_CLAUDE_DESKTOP_CONFIG_FILE",
|
|
122
125
|
format: "json",
|
|
126
|
+
// No Summer Games entry: Claude Desktop's file takes local servers only; remote ones are added as Connectors in its settings.
|
|
123
127
|
mcp: {
|
|
124
128
|
user: (ctx) => join(appConfigRoot(ctx), "Claude", "claude_desktop_config.json"),
|
|
125
129
|
project: null,
|
|
@@ -136,7 +140,7 @@ const SPECS = [
|
|
|
136
140
|
envOverride: "SUMMER_CODEX_CONFIG_FILE",
|
|
137
141
|
format: "toml",
|
|
138
142
|
remoteMcp: {
|
|
139
|
-
|
|
143
|
+
entry: (url) => ({ url }),
|
|
140
144
|
signIn: (name) => `In Codex, run /mcp login ${name} (or \`codex mcp login ${name}\` in a terminal) and sign in.`,
|
|
141
145
|
},
|
|
142
146
|
mcp: {
|
|
@@ -154,10 +158,10 @@ const SPECS = [
|
|
|
154
158
|
status: "active",
|
|
155
159
|
envOverride: "SUMMER_CURSOR_MCP_CONFIG_FILE",
|
|
156
160
|
format: "json-stdio",
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
remoteMcp: {
|
|
162
|
+
entry: (url) => ({ url }),
|
|
163
|
+
signIn: (name) => `In Cursor Settings, open MCP and click Connect (or Authenticate) next to ${name}.`,
|
|
164
|
+
},
|
|
161
165
|
mcp: { user: home(".cursor", "mcp.json"), project: project(".cursor", "mcp.json") },
|
|
162
166
|
restart: "Restart Cursor and enable the summer-engine MCP server if prompted.",
|
|
163
167
|
skills: sharedSkills({
|
|
@@ -173,6 +177,10 @@ const SPECS = [
|
|
|
173
177
|
status: "active",
|
|
174
178
|
envOverride: "SUMMER_WINDSURF_MCP_CONFIG_FILE",
|
|
175
179
|
format: "json",
|
|
180
|
+
remoteMcp: {
|
|
181
|
+
entry: (url) => ({ serverUrl: url }),
|
|
182
|
+
signIn: (name) => `Devin Desktop opens summer.games in your browser the first time ${name} connects; sign in there.`,
|
|
183
|
+
},
|
|
176
184
|
mcp: {
|
|
177
185
|
user: home(".codeium", "windsurf", "mcp_config.json"),
|
|
178
186
|
project: null,
|
|
@@ -187,6 +195,10 @@ const SPECS = [
|
|
|
187
195
|
status: "active",
|
|
188
196
|
envOverride: "SUMMER_ANTIGRAVITY_CONFIG_FILE",
|
|
189
197
|
format: "json",
|
|
198
|
+
remoteMcp: {
|
|
199
|
+
entry: (url) => ({ serverUrl: url }),
|
|
200
|
+
signIn: (name) => `In Antigravity, open Settings > Customizations, choose Authenticate next to ${name}, sign in on summer.games, then paste the code back and choose Submit.`,
|
|
201
|
+
},
|
|
190
202
|
mcp: {
|
|
191
203
|
user: home(".gemini", "config", "mcp_config.json"),
|
|
192
204
|
project: project(".agents", "mcp_config.json"),
|
|
@@ -202,6 +214,7 @@ const SPECS = [
|
|
|
202
214
|
legacyNote: "Gemini CLI was retired for individual accounts on 2026-06-18 and replaced by Antigravity. Use `summer setup antigravity` unless you are on a Workspace/enterprise Gemini CLI.",
|
|
203
215
|
envOverride: "SUMMER_GEMINI_CONFIG_FILE",
|
|
204
216
|
format: "json-gemini",
|
|
217
|
+
// No Summer Games entry: legacy target; use antigravity.
|
|
205
218
|
mcp: {
|
|
206
219
|
user: home(".gemini", "extensions", "summer-engine", "gemini-extension.json"),
|
|
207
220
|
project: null,
|
|
@@ -216,6 +229,10 @@ const SPECS = [
|
|
|
216
229
|
status: "active",
|
|
217
230
|
envOverride: "SUMMER_CLINE_CONFIG_FILE",
|
|
218
231
|
format: "json",
|
|
232
|
+
remoteMcp: {
|
|
233
|
+
entry: (url) => ({ type: "streamableHttp", url }),
|
|
234
|
+
signIn: (name) => `In Cline, open MCP Servers and click Authenticate next to ${name}.`,
|
|
235
|
+
},
|
|
219
236
|
mcp: {
|
|
220
237
|
user: (ctx) => vsCodeGlobalStorage(ctx, "saoudrizwan.claude-dev", "cline_mcp_settings.json"),
|
|
221
238
|
project: null,
|
|
@@ -230,6 +247,10 @@ const SPECS = [
|
|
|
230
247
|
status: "active",
|
|
231
248
|
envOverride: "SUMMER_CLINE_CLI_CONFIG_FILE",
|
|
232
249
|
format: "json",
|
|
250
|
+
remoteMcp: {
|
|
251
|
+
entry: (url) => ({ type: "streamableHttp", url }),
|
|
252
|
+
signIn: (name) => `Run \`cline mcp\`, pick ${name} and choose Authorize OAuth.`,
|
|
253
|
+
},
|
|
233
254
|
mcp: { user: home(".cline", "data", "settings", "cline_mcp_settings.json"), project: null },
|
|
234
255
|
restart: "Restart the Cline CLI so it reloads its MCP settings.",
|
|
235
256
|
skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")),
|
|
@@ -242,6 +263,7 @@ const SPECS = [
|
|
|
242
263
|
legacyNote: "Roo Code shut down on 2026-05-15. The extension may still run, but it is no longer maintained; consider Cline or Kilo Code.",
|
|
243
264
|
envOverride: "SUMMER_ROO_CODE_CONFIG_FILE",
|
|
244
265
|
format: "json",
|
|
266
|
+
// No Summer Games entry: Roo Code has no OAuth sign-in for remote MCP servers.
|
|
245
267
|
mcp: {
|
|
246
268
|
user: (ctx) => vsCodeGlobalStorage(ctx, "rooveterinaryinc.roo-cline", "cline_mcp_settings.json"),
|
|
247
269
|
project: null,
|
|
@@ -261,6 +283,10 @@ const SPECS = [
|
|
|
261
283
|
status: "active",
|
|
262
284
|
envOverride: "SUMMER_KILO_CODE_CONFIG_FILE",
|
|
263
285
|
format: "json-opencode",
|
|
286
|
+
remoteMcp: {
|
|
287
|
+
entry: (url) => ({ type: "remote", url }),
|
|
288
|
+
signIn: (name) => `In Kilo, click the sign-in notice for ${name} (Kilo CLI: \`kilo mcp auth ${name}\`).`,
|
|
289
|
+
},
|
|
264
290
|
mcp: {
|
|
265
291
|
user: (ctx) => join(xdgOrAppData(ctx), "kilo", "kilo.json"),
|
|
266
292
|
project: project("kilo.json"),
|
|
@@ -275,6 +301,10 @@ const SPECS = [
|
|
|
275
301
|
status: "active",
|
|
276
302
|
envOverride: "SUMMER_GITHUB_COPILOT_CONFIG_FILE",
|
|
277
303
|
format: "json-copilot",
|
|
304
|
+
remoteMcp: {
|
|
305
|
+
entry: (url) => ({ type: "http", url, tools: ["*"] }),
|
|
306
|
+
signIn: (name) => `Copilot CLI opens summer.games to sign in when ${name} first connects; /mcp signs in again.`,
|
|
307
|
+
},
|
|
278
308
|
mcp: { user: home(".copilot", "mcp-config.json"), project: project(".mcp.json") },
|
|
279
309
|
restart: "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session.",
|
|
280
310
|
skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")),
|
|
@@ -286,6 +316,10 @@ const SPECS = [
|
|
|
286
316
|
status: "active",
|
|
287
317
|
envOverride: "SUMMER_VSCODE_COPILOT_CONFIG_FILE",
|
|
288
318
|
format: "json-vscode",
|
|
319
|
+
remoteMcp: {
|
|
320
|
+
entry: (url) => ({ type: "http", url }),
|
|
321
|
+
signIn: (name) => `In VS Code, click Allow when ${name} asks to authenticate, then sign in on summer.games.`,
|
|
322
|
+
},
|
|
289
323
|
mcp: { user: (ctx) => join(vsCodeUserDir(ctx), "mcp.json"), project: project(".vscode", "mcp.json") },
|
|
290
324
|
restart: "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode.",
|
|
291
325
|
skills: sharedSkills({ legacyDirs: { user: home(".copilot", "skills"), project: project(".github", "skills") } }),
|
|
@@ -297,6 +331,7 @@ const SPECS = [
|
|
|
297
331
|
status: "active",
|
|
298
332
|
envOverride: "SUMMER_VISUAL_STUDIO_CONFIG_FILE",
|
|
299
333
|
format: "json-vscode",
|
|
334
|
+
// No Summer Games entry: Visual Studio's OAuth redirect for MCP is not documented; not checked against api.summer.games.
|
|
300
335
|
mcp: { user: home(".mcp.json"), project: project(".mcp.json") },
|
|
301
336
|
restart: "Restart Visual Studio, open Copilot Chat in Agent mode, and enable summer-engine in the tools picker.",
|
|
302
337
|
skills: null,
|
|
@@ -309,6 +344,10 @@ const SPECS = [
|
|
|
309
344
|
status: "active",
|
|
310
345
|
envOverride: "SUMMER_COPILOT_JETBRAINS_CONFIG_FILE",
|
|
311
346
|
format: "json-servers",
|
|
347
|
+
remoteMcp: {
|
|
348
|
+
entry: (url) => ({ url }),
|
|
349
|
+
signIn: (name) => `In your JetBrains IDE, click OK when ${name} asks to authenticate, then sign in on summer.games.`,
|
|
350
|
+
},
|
|
312
351
|
mcp: {
|
|
313
352
|
user: (ctx) => join(xdgOrAppData(ctx), "github-copilot", "intellij", "mcp.json"),
|
|
314
353
|
project: null,
|
|
@@ -324,6 +363,10 @@ const SPECS = [
|
|
|
324
363
|
status: "active",
|
|
325
364
|
envOverride: "SUMMER_OPENCODE_CONFIG_FILE",
|
|
326
365
|
format: "json-opencode",
|
|
366
|
+
remoteMcp: {
|
|
367
|
+
entry: (url) => ({ type: "remote", url }),
|
|
368
|
+
signIn: (name) => `Run \`opencode mcp auth ${name}\`; it opens summer.games to sign in.`,
|
|
369
|
+
},
|
|
327
370
|
mcp: {
|
|
328
371
|
user: (ctx) => join(xdgOrAppData(ctx), "opencode", "opencode.json"),
|
|
329
372
|
project: project("opencode.json"),
|
|
@@ -342,6 +385,10 @@ const SPECS = [
|
|
|
342
385
|
status: "active",
|
|
343
386
|
envOverride: "SUMMER_ZED_CONFIG_FILE",
|
|
344
387
|
format: "json-zed",
|
|
388
|
+
remoteMcp: {
|
|
389
|
+
entry: (url) => ({ url }),
|
|
390
|
+
signIn: (name) => `In Zed, open the agent settings and click Authenticate next to ${name}.`,
|
|
391
|
+
},
|
|
345
392
|
mcp: {
|
|
346
393
|
user: (ctx) => join(xdgOrAppData(ctx), "zed", "settings.json"),
|
|
347
394
|
project: null,
|
|
@@ -356,6 +403,10 @@ const SPECS = [
|
|
|
356
403
|
status: "active",
|
|
357
404
|
envOverride: "SUMMER_KIRO_CONFIG_FILE",
|
|
358
405
|
format: "json",
|
|
406
|
+
remoteMcp: {
|
|
407
|
+
entry: (url) => ({ url }),
|
|
408
|
+
signIn: (name) => `Kiro opens summer.games to sign in when ${name} connects (Kiro CLI: /mcp auth).`,
|
|
409
|
+
},
|
|
359
410
|
mcp: { user: home(".kiro", "settings", "mcp.json"), project: project(".kiro", "settings", "mcp.json") },
|
|
360
411
|
restart: "Kiro reloads MCP config on save; open the MCP Servers view to confirm summer-engine is connected.",
|
|
361
412
|
skills: skillDirs(home(".kiro", "skills"), project(".kiro", "skills")),
|
|
@@ -367,6 +418,19 @@ const SPECS = [
|
|
|
367
418
|
status: "active",
|
|
368
419
|
envOverride: "SUMMER_GOOSE_CONFIG_FILE",
|
|
369
420
|
format: "yaml-goose",
|
|
421
|
+
remoteMcp: {
|
|
422
|
+
entry: (url) => ({
|
|
423
|
+
type: "streamable_http",
|
|
424
|
+
name: "summer-games",
|
|
425
|
+
description: "Summer Games: friends, parties and messages as you",
|
|
426
|
+
uri: url,
|
|
427
|
+
enabled: true,
|
|
428
|
+
timeout: 300,
|
|
429
|
+
env_keys: [],
|
|
430
|
+
envs: {},
|
|
431
|
+
}),
|
|
432
|
+
signIn: (name) => `Goose opens summer.games to sign in when the ${name} extension starts.`,
|
|
433
|
+
},
|
|
370
434
|
mcp: {
|
|
371
435
|
user: (ctx) => ctx.os === "win32"
|
|
372
436
|
? join(appData(ctx), "Block", "goose", "config", "config.yaml")
|
|
@@ -383,6 +447,10 @@ const SPECS = [
|
|
|
383
447
|
status: "active",
|
|
384
448
|
envOverride: "SUMMER_HERMES_CONFIG_FILE",
|
|
385
449
|
format: "yaml-hermes",
|
|
450
|
+
remoteMcp: {
|
|
451
|
+
entry: (url) => ({ url, auth: "oauth" }),
|
|
452
|
+
signIn: (name) => `Run \`hermes mcp login ${name}\`, or sign in on summer.games when Hermes first connects.`,
|
|
453
|
+
},
|
|
386
454
|
mcp: { user: home(".hermes", "config.yaml"), project: null },
|
|
387
455
|
restart: "Run /reload-mcp in Hermes Agent (or restart it).",
|
|
388
456
|
skills: skillDirs(home(".hermes", "skills"), SHARED_SKILLS_PROJECT, {
|
|
@@ -397,6 +465,10 @@ const SPECS = [
|
|
|
397
465
|
status: "active",
|
|
398
466
|
envOverride: "SUMMER_TRAE_CONFIG_FILE",
|
|
399
467
|
format: "json",
|
|
468
|
+
remoteMcp: {
|
|
469
|
+
entry: (url) => ({ url }),
|
|
470
|
+
signIn: (name) => `Trae opens summer.games to sign in when ${name} connects.`,
|
|
471
|
+
},
|
|
400
472
|
mcp: {
|
|
401
473
|
user: null,
|
|
402
474
|
project: project(".trae", "mcp.json"),
|
|
@@ -413,6 +485,10 @@ const SPECS = [
|
|
|
413
485
|
status: "active",
|
|
414
486
|
envOverride: "SUMMER_QWEN_CODE_CONFIG_FILE",
|
|
415
487
|
format: "json",
|
|
488
|
+
remoteMcp: {
|
|
489
|
+
entry: (url) => ({ httpUrl: url }),
|
|
490
|
+
signIn: (name) => `In Qwen Code, run /mcp auth ${name} and sign in.`,
|
|
491
|
+
},
|
|
416
492
|
mcp: { user: home(".qwen", "settings.json"), project: project(".qwen", "settings.json") },
|
|
417
493
|
restart: "Restart Qwen Code or run /mcp in a new session.",
|
|
418
494
|
skills: skillDirs(home(".qwen", "skills"), project(".qwen", "skills")),
|
|
@@ -424,6 +500,10 @@ const SPECS = [
|
|
|
424
500
|
status: "active",
|
|
425
501
|
envOverride: "SUMMER_KIMI_CODE_CONFIG_FILE",
|
|
426
502
|
format: "json",
|
|
503
|
+
remoteMcp: {
|
|
504
|
+
entry: (url) => ({ url, auth: "oauth" }),
|
|
505
|
+
signIn: (name) => `Run \`kimi mcp auth ${name}\`; it opens summer.games to sign in.`,
|
|
506
|
+
},
|
|
427
507
|
mcp: { user: home(".kimi-code", "mcp.json"), project: project(".kimi-code", "mcp.json") },
|
|
428
508
|
restart: "Restart Kimi Code CLI so it reconnects its MCP servers.",
|
|
429
509
|
skills: sharedSkills({ legacyDirs: { user: home(".kimi-code", "skills"), project: project(".kimi-code", "skills") } }),
|
|
@@ -435,6 +515,10 @@ const SPECS = [
|
|
|
435
515
|
status: "active",
|
|
436
516
|
envOverride: "SUMMER_CRUSH_CONFIG_FILE",
|
|
437
517
|
format: "json-crush",
|
|
518
|
+
remoteMcp: {
|
|
519
|
+
entry: (url) => ({ type: "http", url, oauth: true }),
|
|
520
|
+
signIn: (name) => `Crush opens summer.games to sign in when ${name} connects.`,
|
|
521
|
+
},
|
|
438
522
|
mcp: {
|
|
439
523
|
user: home(".config", "crush", "crushrc"),
|
|
440
524
|
project: project(".crushrc"),
|
|
@@ -449,6 +533,10 @@ const SPECS = [
|
|
|
449
533
|
status: "active",
|
|
450
534
|
envOverride: "SUMMER_AMP_CONFIG_FILE",
|
|
451
535
|
format: "json-amp",
|
|
536
|
+
remoteMcp: {
|
|
537
|
+
entry: (url) => ({ url }),
|
|
538
|
+
signIn: (name) => `Amp opens summer.games to sign in for ${name} when it starts.`,
|
|
539
|
+
},
|
|
452
540
|
mcp: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "settings.json"), project: null },
|
|
453
541
|
restart: "Restart Amp so it reloads settings.json.",
|
|
454
542
|
skills: sharedSkills({ legacyDirs: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "skills"), project: null } }),
|
|
@@ -460,6 +548,10 @@ const SPECS = [
|
|
|
460
548
|
status: "active",
|
|
461
549
|
envOverride: "SUMMER_FACTORY_CONFIG_FILE",
|
|
462
550
|
format: "json-stdio",
|
|
551
|
+
remoteMcp: {
|
|
552
|
+
entry: (url) => ({ type: "http", url }),
|
|
553
|
+
signIn: (name) => `In Droid, run /mcp, choose ${name} and Authenticate.`,
|
|
554
|
+
},
|
|
463
555
|
mcp: { user: home(".factory", "mcp.json"), project: project(".factory", "mcp.json") },
|
|
464
556
|
restart: "Restart droid or run /mcp in the active session.",
|
|
465
557
|
skills: sharedSkills({ legacyDirs: { user: home(".factory", "skills"), project: project(".factory", "skills") } }),
|
|
@@ -471,6 +563,10 @@ const SPECS = [
|
|
|
471
563
|
status: "active",
|
|
472
564
|
envOverride: "SUMMER_JUNIE_CONFIG_FILE",
|
|
473
565
|
format: "json",
|
|
566
|
+
remoteMcp: {
|
|
567
|
+
entry: (url) => ({ url }),
|
|
568
|
+
signIn: (name) => `In Junie, run /mcp, open ${name} and choose Authorize.`,
|
|
569
|
+
},
|
|
474
570
|
mcp: { user: home(".junie", "mcp", "mcp.json"), project: project(".junie", "mcp", "mcp.json") },
|
|
475
571
|
restart: "Restart the JetBrains IDE so Junie reloads its MCP config.",
|
|
476
572
|
skills: null,
|
|
@@ -483,6 +579,10 @@ const SPECS = [
|
|
|
483
579
|
status: "active",
|
|
484
580
|
envOverride: "SUMMER_WARP_CONFIG_FILE",
|
|
485
581
|
format: "json",
|
|
582
|
+
remoteMcp: {
|
|
583
|
+
entry: (url) => ({ url }),
|
|
584
|
+
signIn: (name) => `Warp opens summer.games to sign in when ${name} starts.`,
|
|
585
|
+
},
|
|
486
586
|
mcp: { user: home(".warp", ".mcp.json"), project: project(".warp", ".mcp.json") },
|
|
487
587
|
restart: "Warp detects the file and spawns the server; check Settings > AI > MCP servers.",
|
|
488
588
|
skills: sharedSkills({ legacyDirs: { user: home(".warp", "skills"), project: project(".warp", "skills") } }),
|
|
@@ -494,6 +594,7 @@ const SPECS = [
|
|
|
494
594
|
status: "active",
|
|
495
595
|
envOverride: "SUMMER_ROVO_DEV_CONFIG_FILE",
|
|
496
596
|
format: "json-transport",
|
|
597
|
+
// No Summer Games entry: Rovo Dev CLI has no OAuth sign-in for remote MCP servers (headers only).
|
|
497
598
|
mcp: { user: home(".rovodev", "mcp.json"), project: null },
|
|
498
599
|
restart: "Restart Rovo Dev CLI so it reconnects its MCP servers.",
|
|
499
600
|
skills: sharedSkills({ legacyDirs: { user: home(".rovodev", "skills"), project: project(".rovodev", "skills") } }),
|
|
@@ -505,6 +606,7 @@ const SPECS = [
|
|
|
505
606
|
status: "active",
|
|
506
607
|
envOverride: "SUMMER_QODER_CONFIG_FILE",
|
|
507
608
|
format: "json",
|
|
609
|
+
// No Summer Games entry: Qoder's remote-server fields in settings.json are not documented.
|
|
508
610
|
mcp: { user: home(".qoder", "settings.json"), project: project(".mcp.json") },
|
|
509
611
|
restart: "Restart Qoder CLI so it reloads its MCP settings (the Qoder IDE manages servers in its UI).",
|
|
510
612
|
skills: skillDirs(home(".qoder", "skills"), project(".qoder", "skills")),
|
|
@@ -516,6 +618,10 @@ const SPECS = [
|
|
|
516
618
|
status: "active",
|
|
517
619
|
envOverride: "SUMMER_GROK_BUILD_CONFIG_FILE",
|
|
518
620
|
format: "toml",
|
|
621
|
+
remoteMcp: {
|
|
622
|
+
entry: (url) => ({ url }),
|
|
623
|
+
signIn: (name) => `In Grok Build, run /mcps, select ${name} and press i to sign in.`,
|
|
624
|
+
},
|
|
519
625
|
mcp: { user: home(".grok", "config.toml"), project: project(".grok", "config.toml") },
|
|
520
626
|
restart: "Restart Grok Build so it reloads config.toml (it also reads ~/.claude.json and .cursor/mcp.json).",
|
|
521
627
|
skills: sharedSkills({ legacyDirs: { user: home(".grok", "skills"), project: project(".grok", "skills") } }),
|
|
@@ -527,6 +633,10 @@ const SPECS = [
|
|
|
527
633
|
status: "active",
|
|
528
634
|
envOverride: "SUMMER_MISTRAL_VIBE_CONFIG_FILE",
|
|
529
635
|
format: "toml-array",
|
|
636
|
+
remoteMcp: {
|
|
637
|
+
entry: (url) => ({ name: "summer-games", transport: "streamable-http", url, auth: { type: "oauth", scopes: [] } }),
|
|
638
|
+
signIn: (name) => `In Vibe, run /mcp login ${name}.`,
|
|
639
|
+
},
|
|
530
640
|
mcp: { user: home(".vibe", "config.toml"), project: project(".vibe", "config.toml") },
|
|
531
641
|
restart: "Restart Vibe so it reloads config.toml.",
|
|
532
642
|
skills: skillDirs(home(".vibe", "skills"), SHARED_SKILLS_PROJECT, { legacyDirs: { user: home(".vibe", "skills"), project: project(".vibe", "skills") } }),
|
|
@@ -538,6 +648,7 @@ const SPECS = [
|
|
|
538
648
|
status: "active",
|
|
539
649
|
envOverride: "SUMMER_LM_STUDIO_CONFIG_FILE",
|
|
540
650
|
format: "json",
|
|
651
|
+
// No Summer Games entry: LM Studio's MCP client registration is closed and unverified.
|
|
541
652
|
mcp: { user: home(".lmstudio", "mcp.json"), project: null },
|
|
542
653
|
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.",
|
|
543
654
|
skills: null,
|
package/dist/mcp/hosted-mount.js
CHANGED
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { connectHostedMcp } from "../core/hosted-mcp.js";
|
|
4
4
|
import { getStoreAccessToken, OAuthError, resolveMcpResourceUrl } from "../core/oauth.js";
|
|
5
5
|
import { appendMcpLogEvent } from "../core/mcp-log.js";
|
|
6
|
+
import { providerSafeSchema } from "./provider-safe-schema.js";
|
|
6
7
|
/**
|
|
7
8
|
* One Summer Engine MCP. The cloud tools a creator's agent needs to publish
|
|
8
9
|
* (projects, store page, build upload, submit for the owner's approval, the
|
|
@@ -73,7 +74,7 @@ export function prepareHostedMount(server, deps = defaultHostedMountDependencies
|
|
|
73
74
|
...result,
|
|
74
75
|
tools: result.tools.map((tool) => {
|
|
75
76
|
const hosted = schemas.get(tool.name);
|
|
76
|
-
return hosted ? { ...tool, inputSchema: hosted.inputSchema, ...(hosted.annotations ? { annotations: hosted.annotations } : {}), ...(hosted.title ? { title: hosted.title } : {}) } : tool;
|
|
77
|
+
return hosted ? { ...tool, inputSchema: providerSafeSchema(hosted.inputSchema), ...(hosted.annotations ? { annotations: hosted.annotations } : {}), ...(hosted.title ? { title: hosted.title } : {}) } : tool;
|
|
77
78
|
}),
|
|
78
79
|
};
|
|
79
80
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/** String formats OpenAI strict mode accepts; other providers accept no more. */
|
|
3
|
+
export declare const STRICT_PROVIDER_FORMATS: Set<string>;
|
|
4
|
+
export declare function providerSafeSchema<T>(schema: T): T;
|
|
5
|
+
/**
|
|
6
|
+
* Wrap the SDK's tools/list so every tool's inputSchema leaves the server in
|
|
7
|
+
* the provider-safe form. Call after the first registerTool (the SDK installs
|
|
8
|
+
* its tools/list handler then).
|
|
9
|
+
*/
|
|
10
|
+
export declare function serveProviderSafeToolSchemas(server: McpServer): void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
/** String formats OpenAI strict mode accepts; other providers accept no more. */
|
|
3
|
+
export const STRICT_PROVIDER_FORMATS = new Set(["date-time", "time", "date", "duration", "email", "hostname", "ipv4", "ipv6", "uuid"]);
|
|
4
|
+
// Where a schema holds subschemas: one, a list, or a name -> schema map.
|
|
5
|
+
const SCHEMA_KEYS = new Set(["items", "additionalItems", "additionalProperties", "contains", "not", "if", "then", "else", "propertyNames", "unevaluatedItems", "unevaluatedProperties"]);
|
|
6
|
+
const SCHEMA_LIST_KEYS = new Set(["anyOf", "oneOf", "allOf", "prefixItems"]);
|
|
7
|
+
const SCHEMA_MAP_KEYS = new Set(["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"]);
|
|
8
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
function resolvePointer(root, ref) {
|
|
10
|
+
let node = root;
|
|
11
|
+
for (const raw of ref.slice(2).split("/")) {
|
|
12
|
+
if (raw === "")
|
|
13
|
+
continue;
|
|
14
|
+
const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
15
|
+
if (Array.isArray(node))
|
|
16
|
+
node = node[Number(key)];
|
|
17
|
+
else if (isObject(node))
|
|
18
|
+
node = node[key];
|
|
19
|
+
else
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
function uniqueSchemas(schemas) {
|
|
25
|
+
const seen = new Map();
|
|
26
|
+
for (const schema of schemas)
|
|
27
|
+
seen.set(JSON.stringify(schema), schema);
|
|
28
|
+
return [...seen.values()];
|
|
29
|
+
}
|
|
30
|
+
function rewrite(schema, root, expanding) {
|
|
31
|
+
if (!isObject(schema))
|
|
32
|
+
return schema;
|
|
33
|
+
const child = (value) => rewrite(value, root, expanding);
|
|
34
|
+
const { $ref, ...rest } = schema;
|
|
35
|
+
if (typeof $ref === "string" && ($ref === "#" || $ref.startsWith("#/"))) {
|
|
36
|
+
// A recursive shape cannot be inlined; accept any value there and let zod validate it.
|
|
37
|
+
const target = expanding.includes($ref) ? {} : rewrite(resolvePointer(root, $ref), root, [...expanding, $ref]);
|
|
38
|
+
return { ...(isObject(target) ? target : {}), ...child(rest) };
|
|
39
|
+
}
|
|
40
|
+
const out = {};
|
|
41
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
42
|
+
if (key === "additionalItems" || key === "prefixItems")
|
|
43
|
+
continue;
|
|
44
|
+
if (SCHEMA_KEYS.has(key))
|
|
45
|
+
out[key] = Array.isArray(value) ? value.map(child) : child(value);
|
|
46
|
+
else if (SCHEMA_LIST_KEYS.has(key) && Array.isArray(value))
|
|
47
|
+
out[key] = value.map(child);
|
|
48
|
+
else if (SCHEMA_MAP_KEYS.has(key) && isObject(value))
|
|
49
|
+
out[key] = Object.fromEntries(Object.entries(value).map(([name, sub]) => [name, child(sub)]));
|
|
50
|
+
else
|
|
51
|
+
out[key] = value;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(schema.type) && !("anyOf" in out)) {
|
|
54
|
+
delete out.type;
|
|
55
|
+
out.anyOf = schema.type.map((type) => ({ type }));
|
|
56
|
+
}
|
|
57
|
+
if (typeof out.format === "string" && !STRICT_PROVIDER_FORMATS.has(out.format))
|
|
58
|
+
delete out.format;
|
|
59
|
+
if (isObject(out.propertyNames) && Object.keys(out.propertyNames).length === 1 && out.propertyNames.type === "string")
|
|
60
|
+
delete out.propertyNames;
|
|
61
|
+
const tuple = Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.items) ? schema.items : null;
|
|
62
|
+
if (tuple) {
|
|
63
|
+
const extra = Array.isArray(schema.prefixItems) ? schema.items : schema.additionalItems;
|
|
64
|
+
const open = extra !== undefined && extra !== false;
|
|
65
|
+
const members = uniqueSchemas([...tuple, ...(isObject(extra) ? [extra] : [])].map(child));
|
|
66
|
+
out.items = members.length === 1 ? members[0] : members.length === 0 ? {} : { anyOf: members };
|
|
67
|
+
out.minItems ??= tuple.length;
|
|
68
|
+
if (!open)
|
|
69
|
+
out.maxItems ??= tuple.length;
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
export function providerSafeSchema(schema) {
|
|
74
|
+
const out = rewrite(schema, schema, []);
|
|
75
|
+
if (isObject(out)) {
|
|
76
|
+
// Every local $ref is inlined, so the definition tables are dead weight.
|
|
77
|
+
delete out.definitions;
|
|
78
|
+
delete out.$defs;
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Wrap the SDK's tools/list so every tool's inputSchema leaves the server in
|
|
84
|
+
* the provider-safe form. Call after the first registerTool (the SDK installs
|
|
85
|
+
* its tools/list handler then).
|
|
86
|
+
*/
|
|
87
|
+
export function serveProviderSafeToolSchemas(server) {
|
|
88
|
+
const handlers = server.server._requestHandlers;
|
|
89
|
+
const listTools = handlers.get("tools/list");
|
|
90
|
+
if (!listTools)
|
|
91
|
+
return;
|
|
92
|
+
server.server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
|
|
93
|
+
const result = await listTools(request, extra);
|
|
94
|
+
return { ...result, tools: result.tools.map((tool) => ({ ...tool, inputSchema: providerSafeSchema(tool.inputSchema) })) };
|
|
95
|
+
});
|
|
96
|
+
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -27,6 +27,7 @@ import { registerLibraryTools } from "./tools/library-tools.js";
|
|
|
27
27
|
import { getCachedBootDriftNotice as getCachedNotice, setCachedBootDriftNotice, } from "./boot-notice.js";
|
|
28
28
|
import { appendMcpLogEvent } from "../core/mcp-log.js";
|
|
29
29
|
import { prepareHostedMount } from "./hosted-mount.js";
|
|
30
|
+
import { serveProviderSafeToolSchemas } from "./provider-safe-schema.js";
|
|
30
31
|
import { buildBootDriftNotice, fetchLatestRegistryVersion, } from "../installer/version-check.js";
|
|
31
32
|
import { TOOLKIT_VERSION as version } from "../core/version.js";
|
|
32
33
|
let processDiagnosticsInstalled = false;
|
|
@@ -329,6 +330,8 @@ export function createMcpServer() {
|
|
|
329
330
|
// The playbook is also an MCP prompt so prompt-surfacing hosts get it
|
|
330
331
|
// natively (same content as the summer_get_agent_playbook tool).
|
|
331
332
|
registerPlaybookPrompt(server);
|
|
333
|
+
// Strict providers (Kimi, OpenAI strict) reject tuple and $ref schemas.
|
|
334
|
+
serveProviderSafeToolSchemas(server);
|
|
332
335
|
return { server, getRegisteredToolCount };
|
|
333
336
|
}
|
|
334
337
|
export async function startMcpServer(options = {}) {
|
package/gemini-extension.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_generated": "GENERATED from integrations/gemini — do not edit; npm run generate:registry",
|
|
3
3
|
"name": "summer",
|
|
4
|
-
"version": "3.5.
|
|
4
|
+
"version": "3.5.2",
|
|
5
5
|
"description": "Agent tooling for Summer Engine: MCP bridge, context primer, and game-dev skills. Use with the npm CLI for skill files on disk.",
|
|
6
6
|
"contextFileName": "GEMINI.md",
|
|
7
7
|
"mcpServers": {
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"./library/skills/psx-retro-rendering/",
|
|
94
94
|
"./library/skills/publish-your-game/",
|
|
95
95
|
"./library/skills/realtime-wet-surfaces/",
|
|
96
|
+
"./library/skills/release-readiness/",
|
|
96
97
|
"./library/skills/remote-deploy/",
|
|
97
98
|
"./library/skills/retarget/",
|
|
98
99
|
"./library/skills/running-in-the-cloud/",
|
package/integrations/README.md
CHANGED
|
@@ -24,6 +24,12 @@ never sits beside project-scope skills. `--scope project` moves both;
|
|
|
24
24
|
`--recommended` installs only the recommended subset. Clients whose MCP config
|
|
25
25
|
is user-only fall back to user scope with a warning.
|
|
26
26
|
|
|
27
|
+
For every client with `remoteMcp` in `agent-table.ts`, setup also writes the
|
|
28
|
+
remote `summer-games` server (`https://mcp.summer.games/mcp`) into the same
|
|
29
|
+
file, in that client's entry shape, and prints its one sign-in step. A client
|
|
30
|
+
gets `remoteMcp` only after its OAuth redirect is checked against what
|
|
31
|
+
api.summer.games accepts; the table comments name why the rest have none.
|
|
32
|
+
|
|
27
33
|
Generated root files (`.claude-plugin/plugin.json`, `gemini-extension.json`,
|
|
28
34
|
`.mcp.json`, …) are build artifacts of `integrations/<agent>` + `library/`.
|
|
29
35
|
Their `_generated` banner says so. CI `--check` fails on any drift between
|
package/library/skills/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Summer skills
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 123 skills, grouped by their primary domain. **Generated** by `npm run generate:registry` from each skill's `resource.yaml`; `--check` fails when this file and the library disagree. Do not edit by hand.
|
|
4
4
|
|
|
5
5
|
The folder is flat on purpose (`docs/design/DECISIONS.md`, D3): a skill usually belongs to several domains, so categories live in each skill's `facets.domains` and this index is rendered from them. The first domain listed is the primary one. `summer skills list --by-domain` prints the same grouping; `summer skills info <slug>` shows one skill.
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ Status: **stable** unless marked; *preview* = not yet exercised in-engine by the
|
|
|
18
18
|
- [character-controller](#character-controller) (2)
|
|
19
19
|
- [creator](#creator) (4)
|
|
20
20
|
- [debug](#debug) (1)
|
|
21
|
-
- [deployment](#deployment) (
|
|
21
|
+
- [deployment](#deployment) (3)
|
|
22
22
|
- [editor](#editor) (1)
|
|
23
23
|
- [gameplay](#gameplay) (6)
|
|
24
24
|
- [level-design](#level-design) (2)
|
|
@@ -147,6 +147,7 @@ Status: **stable** unless marked; *preview* = not yet exercised in-engine by the
|
|
|
147
147
|
| skill | when to use | also |
|
|
148
148
|
|---|---|---|
|
|
149
149
|
| [export-and-ship](./export-and-ship/SKILL.md) ★ | Assess and prepare a game export: inventory installed templates and targets, validate release assets and config, produce supported builds after approval. | project |
|
|
150
|
+
| [*release-readiness*](./release-readiness/SKILL.md) (preview) | Before releasing a Summer game version, prove players can play it: server scene, online join, saved progress, no fixed Game ID, a released Summer compiles it. | multiplayer, playtest |
|
|
150
151
|
| [remote-deploy](./remote-deploy/SKILL.md) ★ | Run or test the game on a real device — phone, tablet, another computer — via the Remote Deploy button, runnable export presets, and on-device debugging. | project |
|
|
151
152
|
|
|
152
153
|
## editor
|
|
@@ -298,6 +298,8 @@ Per platform, point the user at the correct upload path:
|
|
|
298
298
|
- **summer.games:** `summer_export_game` writes the summer.games `.zip`; `summer_publish_build` uploads it to the game's store listing after the user confirms the exact game, file and version (needs `summer login --store`). If it answers `store_auth_refused`, the user uploads the `.zip` in Studio instead.
|
|
299
299
|
- Per platform, the store takes: iPhone, Android, macOS and Windows in the Summer apps = one summer.games bundle (`summer_export_game` `targets`, format `bundle`; no template, no signing; a game without a server targets `ios`/`android` only). Web = an HTML5 `.zip` on Summer's WebGPU Forward+ template (`targets:["web"]`; the project must render with Forward+ or Mobile). A macOS or Windows download for the store page = `targets:["macos"]` or `["windows"]` with format `download`. Download exports need the template: `summer_export_templates` `install` fetches it from Summer's CDN.
|
|
300
300
|
- Hosted games: if `summer_export_game` fails with "… is in the source graph's authority_engine domain, which the client pack leaves out", an authority root in `source-domains.json` holds a file the client loads (often the composition `.tres`). Narrow that root to the server-only files, or move the shared file under a `shared` path, then export again. Every exporter message is in the `multiplayer-publish` skill's export-errors.md; the folder rules are in `multiplayer-project`.
|
|
301
|
+
- Every summer.games bundle: export with the released Summer editor from the download page, never a development build, and export again after updating Summer. Otherwise its scripts can fail to compile on players' devices and the game opens to an empty window, though every upload check passes. `release-readiness` check 7 proves it.
|
|
302
|
+
- Hosted games, before the upload: a clean export does not prove players can join. Run `release-readiness` on the `.zip`: the bundle's `server.pck`, started with no scene, must load the authority scene (the server preset needs the `summer_authority` custom feature), and `Summer.initialize` must pass no hard-coded Game ID. Otherwise every online join fails with `world_start_timeout`.
|
|
301
303
|
- Store page art: key art, covers and icons carry no text, logos or UI (the store page shows the title). Screenshots are real gameplay and may show the game's own HUD; add no captions, logos or overlays. Capture them with `summer_capture_gameplay` (offscreen, no editor needed; `resolution` `1920x1080`, or `1080x1920` for portrait; get past a title menu with the game's own flags in `args`, e.g. `["--autostart"]`, or `steps` such as `[{"press":"Play"},{"wait":3000},{"shot":true}]`), or `summer_screenshot` target `game` while the editor runs the game.
|
|
302
304
|
- **Steam:** `steamcmd` + Steam Pipe (`builder_app_xxx.vdf`). Set up depots first in Steamworks.
|
|
303
305
|
- **itch.io:** `butler push <path> <user>/<game>:<channel>` (e.g. `butler push build/win <user>/mygame:windows`).
|
|
@@ -47,6 +47,7 @@ These rules from the playbooks apply to every genre:
|
|
|
47
47
|
- **2D scales to the viewport.** Set `display/window/stretch/mode = canvas_items` and `stretch/aspect = expand`; UI uses Control anchors; code-positioned nodes compute from `get_viewport_rect().size`. Never hardcode 1920x1080.
|
|
48
48
|
- **Playable slice, not tech demo.** Name the core loop in one sentence; every input gets feedback (tween, sound, particle, flash, shake); progression is visible; a restart exists. The bar is "feels good for 30 seconds".
|
|
49
49
|
- **Fast first draft when someone is waiting.** The first playable version uses shapes, the palette and any template art, plus at most one or two generated assets the person specifically asked for; generation takes minutes, so it never blocks the first play. Say plainly that it is a first draft to get a feel for the game, that the art is not final, and ask if this is the direction they want. Generate the rest of the art in later passes.
|
|
50
|
+
- **A way back to the title.** From inside play, a pause menu or a **Leave** button returns to the title screen without quitting the app.
|
|
50
51
|
- **Text the default font can show.** No emoji or symbols (▲ ◀ ★ ♥ ✓) in UI text: they render as boxes with hex codes. Use words or icons; see `ui-basics`.
|
|
51
52
|
|
|
52
53
|
## The Pipeline
|
|
@@ -95,6 +96,12 @@ This phase doesn't have a dedicated skill yet — do it inline. Read `.summer/Ga
|
|
|
95
96
|
2. **Core mechanics** — ranked, max 3 (the brief enforces this; respect it)
|
|
96
97
|
3. **Cut list** — what's explicitly NOT in scope
|
|
97
98
|
|
|
99
|
+
If the game has online play, the plan also fixes three product defaults; each one was missed by a real release:
|
|
100
|
+
|
|
101
|
+
- **Online play is reachable by default.** A new player gets to it from the title without changing a setting. Hiding it behind a setting or a grown-ups gate is the owner's explicit decision, recorded in the plan, and it means someone joins online before every release.
|
|
102
|
+
- **Progress is saved by the server.** Online progress goes through authority player data (`summer-player-data`), not only the player's local files.
|
|
103
|
+
- **A way back to the title** from inside an online and an offline session.
|
|
104
|
+
|
|
98
105
|
Save the plan to `.summer/build-plan.md`. Format:
|
|
99
106
|
|
|
100
107
|
```markdown
|
|
@@ -236,8 +243,10 @@ Only if `summer_get_diagnostics` flags rendering or physics hotspots. Don't opti
|
|
|
236
243
|
1. `play` from main scene.
|
|
237
244
|
2. Walk every mechanic from the plan.
|
|
238
245
|
3. Hit the win condition (or lose condition).
|
|
239
|
-
4.
|
|
240
|
-
5. `
|
|
246
|
+
4. Return to the title from inside play, then start again.
|
|
247
|
+
5. Online games: run Local Play (`multiplayer-testing`) and reach online play from the title as a new player, with default settings.
|
|
248
|
+
6. `summer_get_diagnostics` clean.
|
|
249
|
+
7. `debug` until clean if not.
|
|
241
250
|
|
|
242
251
|
**Don't skip this even if every mechanic worked individually.** Integration bugs hide between mechanics.
|
|
243
252
|
|
|
@@ -249,6 +258,14 @@ Skill: export-and-ship
|
|
|
249
258
|
|
|
250
259
|
Pre-flight checklist (icon, store banners, build config) before producing release builds for the targets in the brief.
|
|
251
260
|
|
|
261
|
+
Before any version reaches players, run:
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
Skill: release-readiness
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
It checks the file you upload, not the editor: a hosted game's `server.pck` starts its authority scene, `Summer.initialize` passes no hard-coded Game ID, online play is reachable, progress is saved by the server, and players can get back to the title. Then, after publishing, the user joins online with the released version. Report each check as pass, fail, not applicable or not run. Hosted games publish through `multiplayer-publish`; store pages through `publish-your-game`.
|
|
268
|
+
|
|
252
269
|
If the brief said "jam" or "prototype", skip this phase. If the brief said "game I want to release", run it.
|
|
253
270
|
|
|
254
271
|
## Anti-Patterns
|
|
@@ -260,6 +277,8 @@ If the brief said "jam" or "prototype", skip this phase. If the brief said "game
|
|
|
260
277
|
| Stack art direction on top of unfinished mechanics | Art changes are reversible only if the underlying scene structure is stable. Get mechanics done first. |
|
|
261
278
|
| Treat "polish" as required | If the brief says "jam in 2 days", polish is a cut item. Respect the brief. |
|
|
262
279
|
| Run all phases without confirming | The user owns scope. Stop at every checkpoint. |
|
|
280
|
+
| Ship online play behind a setting nobody changes | Hosted join is never exercised, so it breaks without anyone noticing. Reachable by default, or the owner joins online before every release. |
|
|
281
|
+
| Hard-code a Game ID in `Summer.initialize` | The summer.games bundle has no `gameId`; a different ID fails on the hosted server and every online join times out. Pass no ID. |
|
|
263
282
|
| Build a fully featured menu before there's a playable mechanic | Menu work is endless polish; player-loop work is the actual game. Mechanics first. |
|
|
264
283
|
| Fall back to "let me try a few things" mid-pipeline | If you don't know what to do, name the phase and ask the user, don't improvise around the structure. |
|
|
265
284
|
|
|
@@ -84,7 +84,7 @@ the sender's verified Session. Events are fire-and-forget.
|
|
|
84
84
|
|
|
85
85
|
| Word | Meaning |
|
|
86
86
|
|---|---|
|
|
87
|
-
| Game | Your game on Summer; its id is `gameId` in `summer.build.json
|
|
87
|
+
| Game | Your game on Summer; its id is `gameId` in `summer.build.json`. `Summer.initialize()` takes no ID: Summer knows which game is running |
|
|
88
88
|
| World | One running match or shared place, simulated by one authority |
|
|
89
89
|
| WorldDefinition | `world.json`: which scenes run the client and the authority, lifetime, persistence |
|
|
90
90
|
| Queue | How players find a World; declared in `summer.build.json` |
|
|
@@ -109,6 +109,8 @@ Each skill continues the same small game, so their code fits together:
|
|
|
109
109
|
what a passing run must show.
|
|
110
110
|
5. **`multiplayer-publish`**: export the summer.games bundle and upload it as
|
|
111
111
|
a Build.
|
|
112
|
+
6. **`release-readiness`**: before players get a version, prove the server
|
|
113
|
+
starts its authority scene and a real online join works.
|
|
112
114
|
|
|
113
115
|
## Add Summer services
|
|
114
116
|
|