summer-engine 2.7.0 → 2.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 (150) hide show
  1. package/.claude-plugin/plugin.json +2 -1
  2. package/.codex-plugin/plugin.json +3 -2
  3. package/.cursor-plugin/plugin.json +2 -1
  4. package/.opencode/INSTALL.md +1 -1
  5. package/.opencode/plugins/summer.js +1 -1
  6. package/AGENTS.md +4 -4
  7. package/CHANGELOG.md +10 -0
  8. package/GEMINI.md +5 -4
  9. package/README.md +58 -19
  10. package/assets/autopilot/README.md +51 -0
  11. package/assets/autopilot/autopilot.gd +222 -0
  12. package/assets/autopilot/probe_base.gd +262 -0
  13. package/assets/autopilot/run.sh +78 -0
  14. package/commands/gameskill.md +11 -2
  15. package/dist/bin/summer.js +8 -0
  16. package/dist/commands/config.d.ts +2 -0
  17. package/dist/commands/config.js +53 -0
  18. package/dist/commands/create.d.ts +1 -0
  19. package/dist/commands/create.js +38 -6
  20. package/dist/commands/list.js +1 -1
  21. package/dist/commands/login.d.ts +19 -0
  22. package/dist/commands/login.js +123 -36
  23. package/dist/commands/logout.js +3 -16
  24. package/dist/commands/logs.d.ts +2 -0
  25. package/dist/commands/logs.js +19 -0
  26. package/dist/commands/mcp.js +7 -2
  27. package/dist/commands/open.js +1 -1
  28. package/dist/commands/publish.d.ts +2 -0
  29. package/dist/commands/publish.js +20 -0
  30. package/dist/commands/releases.d.ts +2 -0
  31. package/dist/commands/releases.js +20 -0
  32. package/dist/commands/run.js +1 -1
  33. package/dist/lib/api-client.d.ts +4 -2
  34. package/dist/lib/api-client.js +28 -4
  35. package/dist/lib/auth.d.ts +40 -12
  36. package/dist/lib/auth.js +173 -37
  37. package/dist/lib/cloud/engine-bridge.d.ts +17 -4
  38. package/dist/lib/cloud/engine-bridge.js +32 -6
  39. package/dist/lib/config.d.ts +20 -0
  40. package/dist/lib/config.js +142 -0
  41. package/dist/lib/creator.d.ts +93 -0
  42. package/dist/lib/creator.js +393 -0
  43. package/dist/lib/engine.d.ts +35 -2
  44. package/dist/lib/engine.js +240 -6
  45. package/dist/lib/public-surface-paths.d.ts +13 -0
  46. package/dist/lib/public-surface-paths.js +42 -0
  47. package/dist/lib/skills-registry.d.ts +8 -0
  48. package/dist/lib/skills-registry.js +9 -0
  49. package/dist/lib/store.d.ts +14 -0
  50. package/dist/lib/store.js +145 -0
  51. package/dist/lib/summer-compatibility.d.ts +6 -0
  52. package/dist/lib/summer-compatibility.js +22 -0
  53. package/dist/mcp/server.d.ts +8 -1
  54. package/dist/mcp/server.js +25 -4
  55. package/dist/mcp/tools/creator-tools.d.ts +2 -0
  56. package/dist/mcp/tools/creator-tools.js +105 -0
  57. package/dist/mcp/tools/generate-tools.js +16 -6
  58. package/dist/mcp/tools/project-tools.js +12 -7
  59. package/dist/mcp/tools/scene-tools.js +6 -6
  60. package/dist/mcp/tools/visual-tools.js +4 -3
  61. package/hooks/pre-commit-doctor.sh +0 -0
  62. package/hooks/run-hook.cmd +0 -0
  63. package/hooks/session-start.sh +5 -5
  64. package/package.json +3 -1
  65. package/references/collaborative-protocol.md +50 -0
  66. package/references/gd-style.md +121 -0
  67. package/references/godot-version.md +51 -0
  68. package/references/mcp-tools-reference.md +199 -0
  69. package/references/public-surface-language.md +30 -0
  70. package/references/summer-folder.md +90 -0
  71. package/references/template-registry.md +73 -0
  72. package/skills/2d-assets/character-portrait/SKILL.md +17 -12
  73. package/skills/2d-assets/concept-art/SKILL.md +10 -9
  74. package/skills/2d-assets/pixel-art/SKILL.md +36 -20
  75. package/skills/2d-assets/skybox-panorama/SKILL.md +32 -37
  76. package/skills/2d-assets/sprite-sheet/SKILL.md +40 -35
  77. package/skills/2d-assets/tileable-texture/SKILL.md +28 -22
  78. package/skills/2d-assets/ui-graphics/SKILL.md +46 -32
  79. package/skills/2d-assets/use-widget-asset/SKILL.md +8 -6
  80. package/skills/3d-assets/character-model/SKILL.md +34 -19
  81. package/skills/3d-assets/environment-kit/SKILL.md +11 -5
  82. package/skills/3d-assets/organic-model/SKILL.md +8 -5
  83. package/skills/3d-assets/prop-model/SKILL.md +9 -3
  84. package/skills/3d-assets/vehicle-model/SKILL.md +32 -19
  85. package/skills/README.md +1 -1
  86. package/skills/ai-and-npcs/design-npc/SKILL.md +31 -13
  87. package/skills/animation/animation-tree/SKILL.md +39 -15
  88. package/skills/animation/facial-and-lipsync/SKILL.md +25 -8
  89. package/skills/animation/generate-motion/SKILL.md +47 -15
  90. package/skills/animation/procedural-animation/SKILL.md +31 -13
  91. package/skills/animation/retarget/SKILL.md +127 -79
  92. package/skills/audio/adaptive-music/SKILL.md +24 -20
  93. package/skills/audio/ambient-bed/SKILL.md +22 -16
  94. package/skills/audio/audio-direction/SKILL.md +15 -10
  95. package/skills/audio/music-track/SKILL.md +18 -12
  96. package/skills/audio/sound-effect/SKILL.md +17 -12
  97. package/skills/audio/voice-line/SKILL.md +42 -42
  98. package/skills/character-controllers/fps-controller/SKILL.md +26 -16
  99. package/skills/debugging/debug/SKILL.md +52 -20
  100. package/skills/deployment/export-and-ship/SKILL.md +105 -38
  101. package/skills/deployment/remote-deploy/SKILL.md +56 -23
  102. package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +26 -1
  103. package/skills/gameplay-mechanics/design-mechanic/SKILL.md +23 -15
  104. package/skills/level-design/design-level/SKILL.md +22 -8
  105. package/skills/level-design/scene-to-level/SKILL.md +13 -7
  106. package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +8 -8
  107. package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +2 -7
  108. package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +26 -18
  109. package/skills/performance/tune-performance/SKILL.md +51 -32
  110. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +34 -5
  111. package/skills/rendering-and-lighting/art-direction/SKILL.md +6 -8
  112. package/skills/scene-and-project/brainstorm-game/SKILL.md +3 -5
  113. package/skills/scene-and-project/make-game/SKILL.md +2 -1
  114. package/skills/scene-and-project/play/SKILL.md +10 -4
  115. package/skills/scene-and-project/scene-composition/SKILL.md +21 -8
  116. package/skills/scene-and-project/summer-cloud/SKILL.md +1 -1
  117. package/skills/ui-and-ux/ui-basics/SKILL.md +16 -7
  118. package/skills/video/animated-loop/SKILL.md +63 -39
  119. package/skills/video/cinematic-cutscene/SKILL.md +66 -30
  120. package/skills/video/trailer-shot/SKILL.md +30 -21
  121. package/skills/visual-effects/_building-blocks/additive-billboard-particles.md +25 -16
  122. package/skills/visual-effects/_building-blocks/trauma-shake-snippet.md +5 -3
  123. package/skills/visual-effects/game-feel/SKILL.md +51 -33
  124. package/skills/visual-effects/recipes/dissolve/SKILL.md +51 -9
  125. package/skills/visual-effects/recipes/fire/SKILL.md +136 -49
  126. package/skills/visual-effects/recipes/hit-spark/SKILL.md +45 -8
  127. package/skills/visual-effects/recipes/lightning/SKILL.md +59 -27
  128. package/skills/visual-effects/recipes/magic-glow/SKILL.md +59 -26
  129. package/skills/visual-effects/recipes/muzzle-flash/SKILL.md +68 -25
  130. package/skills/visual-effects/recipes/smoke/SKILL.md +89 -28
  131. package/skills/visual-effects/recipes/water-ripple/SKILL.md +83 -23
  132. package/skills/workflow/brainstorming/SKILL.md +1 -1
  133. package/skills/workflow/brainstorming/scripts/start-server.sh +0 -0
  134. package/skills/workflow/brainstorming/scripts/stop-server.sh +0 -0
  135. package/skills/workflow/debugging-game-feel/SKILL.md +29 -9
  136. package/skills/workflow/diagnosing-perf-regressions/SKILL.md +46 -21
  137. package/skills/workflow/gameskill/SKILL.md +11 -2
  138. package/skills/workflow/headless-scripting/SKILL.md +407 -0
  139. package/skills/workflow/investigating-bugs/SKILL.md +4 -2
  140. package/skills/workflow/investigating-bugs/find-polluter.sh +0 -0
  141. package/skills/workflow/mcpupdate/SKILL.md +3 -3
  142. package/skills/workflow/playtesting-a-feature/SKILL.md +94 -21
  143. package/skills/workflow/skill-create/SKILL.md +7 -7
  144. package/skills/workflow/skill-test/SKILL.md +3 -3
  145. package/skills/workflow/using-summer/SKILL.md +12 -2
  146. package/skills/workflow/verification-before-completion/SKILL.md +2 -0
  147. package/skills/workflow/writing-plans/SKILL.md +7 -7
  148. package/skills/workflow/writing-skills/SKILL.md +7 -5
  149. package/skills/workflow/writing-skills/render-graphs.js +0 -0
  150. package/skills/workflow/writing-skills/testing-skills-with-subagents.md +1 -1
@@ -0,0 +1,105 @@
1
+ import { z } from "zod";
2
+ import { CreatorOperationError, listCreatorReleases, publishCreator, readCreatorLogs, } from "../../lib/creator.js";
3
+ import { CONFIG_KEYS, getConfigValue, isConfigKey, readSummerConfig, setConfigValue, unsetConfigValue, } from "../../lib/config.js";
4
+ function textJson(value, isError = false) {
5
+ return {
6
+ content: [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify(value, null, 2),
10
+ },
11
+ ],
12
+ ...(isError ? { isError: true } : {}),
13
+ };
14
+ }
15
+ async function creatorResult(operation) {
16
+ try {
17
+ return textJson(await operation());
18
+ }
19
+ catch (error) {
20
+ if (error instanceof CreatorOperationError) {
21
+ return textJson({
22
+ ok: false,
23
+ code: error.code,
24
+ operation: error.operation,
25
+ message: error.message,
26
+ recovery: error.recovery,
27
+ }, true);
28
+ }
29
+ return textJson({
30
+ ok: false,
31
+ code: "creator_request_invalid",
32
+ message: error instanceof Error ? error.message : String(error),
33
+ }, true);
34
+ }
35
+ }
36
+ export function registerCreatorTools(server) {
37
+ server.tool("summer_creator_publish", "Publish an exact exported Summer .pck through the versioned Summercraft creator API. First call with confirm=false and present the returned project, version, digest, size, artifact path, channel, and notes; set confirm only after the user approves that exact target. The server independently verifies token scope, ownership, bytes, and review state.", {
38
+ project: z.string().optional().describe("Project root. Defaults to the current working directory."),
39
+ artifact: z.string().describe("Exact path to the exported Summer .pck artifact."),
40
+ version: z.string().describe("Immutable release version."),
41
+ manifest: z.string().optional().describe("Optional path to a JSON release manifest."),
42
+ projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
43
+ channel: z.string().optional().describe("Release channel. Defaults to the configured channel or production."),
44
+ notes: z.string().optional().describe("Release notes shown during confirmation and recorded in the local audit."),
45
+ confirm: z.boolean().default(false).describe("Set true only after the user approves the exact release target."),
46
+ }, async (args) => creatorResult(() => publishCreator({
47
+ ...args,
48
+ face: "mcp",
49
+ })));
50
+ server.tool("summer_creator_releases", "List real creator-owned releases from the versioned Summercraft creator API. The server independently verifies the exact publish scope and project ownership.", {
51
+ projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
52
+ limit: z.number().int().min(1).max(100).default(20),
53
+ cursor: z.string().optional().describe("Opaque nextCursor from the previous page."),
54
+ }, async (args) => creatorResult(() => listCreatorReleases({
55
+ ...args,
56
+ face: "mcp",
57
+ })));
58
+ server.tool("summer_creator_logs", "Read creator runtime logs for a project or release. If the required platform route or token scope is unavailable, returns the exact recovery action instead of placeholder logs.", {
59
+ projectId: z.string().optional().describe("Creator project ID. Defaults to ~/.summer/config.json."),
60
+ releaseId: z.string().optional().describe("Optional release ID."),
61
+ limit: z.number().int().min(1).max(1000).default(100),
62
+ }, async (args) => creatorResult(() => readCreatorLogs({
63
+ ...args,
64
+ face: "mcp",
65
+ })));
66
+ server.tool("summer_creator_config", "Read or update the shared non-secret ~/.summer configuration used by the CLI and this MCP. Mutations require confirm=true. Tokens are never returned or accepted by this tool.", {
67
+ action: z.enum(["list", "get", "set", "unset"]),
68
+ key: z.enum(CONFIG_KEYS).optional(),
69
+ value: z.string().optional(),
70
+ confirm: z.boolean().default(false),
71
+ }, async ({ action, key, value, confirm }) => creatorResult(async () => {
72
+ if (action === "list") {
73
+ const config = await readSummerConfig();
74
+ return {
75
+ ok: true,
76
+ values: Object.fromEntries(CONFIG_KEYS.map((name) => [
77
+ name,
78
+ getConfigValue(config, name) ?? null,
79
+ ])),
80
+ };
81
+ }
82
+ if (!key || !isConfigKey(key)) {
83
+ throw new Error(`A valid key is required. Recovery: use one of ${CONFIG_KEYS.join(", ")}.`);
84
+ }
85
+ if (action === "get") {
86
+ return {
87
+ ok: true,
88
+ key,
89
+ value: getConfigValue(await readSummerConfig(), key) ?? null,
90
+ };
91
+ }
92
+ if (!confirm) {
93
+ throw new Error(`Changing ${key} requires confirmation. Recovery: show the exact change to the user, then retry with confirm=true.`);
94
+ }
95
+ if (action === "set") {
96
+ if (value === undefined) {
97
+ throw new Error(`A value is required for ${key}. Recovery: provide value and retry with confirm=true.`);
98
+ }
99
+ const config = await setConfigValue(key, value);
100
+ return { ok: true, key, value: getConfigValue(config, key) };
101
+ }
102
+ const config = await unsetConfigValue(key);
103
+ return { ok: true, key, value: getConfigValue(config, key) ?? null };
104
+ }));
105
+ }
@@ -274,7 +274,9 @@ Known models:
274
274
  - "nano-banana-2" (default) — High quality, supports txt2img and img2img
275
275
  - "gemini-flash" — Google Gemini 2.5 Flash, fast
276
276
  - "flux-2" — FLUX.2, good for specific styles
277
- - Any fal-ai model ID works as a passthrough
277
+
278
+ Model ids are an ALLOWLIST, not a passthrough. An unrecognised id is rejected
279
+ with 400 and the response lists every accepted id — it is not silently swapped.
278
280
 
279
281
  Two modes:
280
282
  - txt2img (default): just pass prompt
@@ -371,8 +373,12 @@ Capabilities:
371
373
  - "music" — Generate background music. Requires 'prompt'. Optional 'durationSeconds'.
372
374
  - "text_to_dialogue" — Multi-voice dialogue. Requires 'inputs' array of {text, voiceId} objects.
373
375
 
374
- The 'options' object is passed directly to ElevenLabs, so you can set any
375
- provider-specific parameter: stability, similarity_boost, style, speed, etc.
376
+ The 'options' object is spread into the ElevenLabs SDK call at the TOP level, so
377
+ only keys the SDK accepts there have any effect. Voice tuning is NOT top-level and
378
+ is NOT snake_case: pass it as options.voiceSettings with camelCase keys —
379
+ { voiceSettings: { stability, similarityBoost, style, speed, useSpeakerBoost } }.
380
+ Flat snake_case keys are accepted by the schema, silently dropped by the SDK, and
381
+ return a normal 200 with the setting ignored.
376
382
 
377
383
  Returns the generated audio URL and asset metadata.
378
384
  Cloud tool — runs on Summer's servers and works WITHOUT the Summer Engine app open.
@@ -407,7 +413,9 @@ Requires authentication: run 'npx summer-engine login' first.`, {
407
413
  options: z
408
414
  .record(z.any())
409
415
  .optional()
410
- .describe("Provider-specific params (stability, similarity_boost, style, speed, etc.)"),
416
+ .describe("Provider params, spread at the SDK's top level. Voice tuning goes in " +
417
+ "options.voiceSettings with camelCase keys (stability, similarityBoost, " +
418
+ "style, speed, useSpeakerBoost); flat snake_case is silently ignored."),
411
419
  }, async ({ capability, text, prompt, voiceId, modelId, durationSeconds, inputs, options }) => {
412
420
  const body = { capability };
413
421
  if (text)
@@ -439,7 +447,9 @@ Available models:
439
447
  - "hunyuan" (default) — Hunyuan 3D v3.1 Pro, high quality
440
448
  - "trellis" — Trellis 2, fast and detailed
441
449
  - "meshy" — Meshy, legacy option
442
- - Any fal-ai model ID works as a passthrough
450
+
451
+ Model ids are an ALLOWLIST, not a passthrough. An unrecognised id is rejected
452
+ with 400 listing the accepted ids.
443
453
 
444
454
  Available kinds:
445
455
  - "text-to-3d" (default) — From text description. Requires 'prompt'.
@@ -699,7 +709,7 @@ Cost: ~$0.10 per clip. Confirm with user before spending.
699
709
 
700
710
  Custom prompt-driven motion is on the roadmap; not yet shipped. For one-off
701
711
  signature moves not on the curated list, fall back to hand-authoring in the
702
- Godot editor or importing from Mixamo.
712
+ Summer Engine or importing from Mixamo.
703
713
 
704
714
  Cloud tool — runs on Summer's servers and works WITHOUT the Summer Engine app open.
705
715
  Requires authentication: run 'npx summer-engine login' first.`, {
@@ -180,10 +180,12 @@ anti-patterns, and recovery steps.`, {}, async () => ({
180
180
  "summer_stop -> stop when runtime verification is finished; editor scene mutations are not categorically blocked by a running game, but an existing game instance may need a restart to observe them",
181
181
  ],
182
182
  "4_interactive": [
183
- "To prove input-driven behavior (does jump/move/shoot actually work), you have two routes when the engine build supports them (both go through summer_batch as raw ops — see rawOpsViaBatch):",
184
- "SimulateInput: inject a single action/key/mouse/axis into the RUNNING game, then re-check screenshot/debugger. Requires summer_play first.",
185
- "RunVerification: spawn a hidden, disposable game instance that runs a GDScript probe (press inputs, read state, save frames) and dies — never touches the user's editor. Returns results.json + frames. Best for a scripted 'does X happen when I do Y' assertion.",
186
- "If neither op is available on this engine build (failure_reason:'unsupported' or an unknown-op error comes back), do NOT fake it — ask the user to try the interaction and report what they see.",
183
+ "To prove input-driven behavior (does jump/move/shoot actually work), use RunVerification. It is the ONLY interactive route available to MCP, and it is a real one — do NOT hand this rung to the user.",
184
+ "RunVerification: spawn a hidden, disposable game instance that runs a GDScript probe (press inputs, read state, save frames) and dies — never touches the user's editor. Returns results.json + frames. Send it as a raw op through summer_batch (see rawOpsViaBatch).",
185
+ "Unlike the editor's own --headless mode, the verify instance renders REAL PIXELS, so save_frame() writes a real image and Performance.TIME_FPS reports a real number.",
186
+ "press()/key() are COROUTINES — 'await press(\"move_right\", 500)' or the hold never elapses and the input does nothing.",
187
+ "Assert on physics-frame-derived state (positions after N 'await get_tree().physics_frame'), which is reproducible. press(hold_ms) waits on wall clock, so distance-travelled jitters run to run — assert 'moved more than X', never an exact value.",
188
+ "SimulateInput is NOT reachable from MCP — see rawOpsViaBatch. Do not attempt it as a fallback.",
187
189
  ],
188
190
  },
189
191
  // HONESTY — mirror the in-product agent's vision rules. A capture is
@@ -217,8 +219,10 @@ anti-patterns, and recovery steps.`, {}, async () => ({
217
219
  // engine ops that have no dedicated tool are still reachable.
218
220
  rawOpsViaBatch: [
219
221
  "summer_batch runs an array of raw engine ops in one undo group; each op is passed through untouched, so newer engine ops with no dedicated tool are still callable.",
220
- "SimulateInput (drive the RUNNING game): summer_batch ops:[{op:'SimulateInput', type:'action', action:'jump', pressed:true}]. type is 'action' | 'key' | 'mouse_click' | 'axis'. summer_play first. Structured failure_reasons (incl 'unsupported' on older builds) come back verbatim — surface them.",
221
- "RunVerification (hidden probe instance): summer_batch ops:[{op:'RunVerification', probe_source:'<gdscript>', max_seconds:20}]. probe_source extends SummerProbeBase and uses report()/save_frame()/press()/key()/finish(); returns {ok, results, frames, out_dir} or {ok:false, failure_reason: spawn_failed|timeout|bad_args|no_project}.",
222
+ "RunVerification (hidden probe instance): summer_batch ops:[{op:'RunVerification', probe_source:'<gdscript>', max_seconds:20}]. probe_source extends SummerProbeBase and uses report()/save_frame()/press()/key()/finish(); returns {ok, results, frames, out_dir} or {ok:false, failure_reason: spawn_failed|timeout|bad_args|no_project|probe_not_node}.",
223
+ "SimulateInput is NOT reachable this way, on any engine build. It needs the in-editor chat bridge's async reply channel; every queued caller (MCP, CLI) is answered with {ok:false, failure_reason:'unsupported_transport'}. Use RunVerification's press()/key() instead.",
224
+ "WriteFile and ReplaceText are rejected here by design — use summer_write_file / summer_replace_text so project identity, content guards and same-file ordering are enforced.",
225
+ "You do not need an engine op to run a shell command: your own host already has a shell. The engine binary that runs project scripts is at OS.get_executable_path() (on macOS, /Applications/Summer.app/Contents/MacOS/Summer); see the summer-cli headless-scripting skill.",
222
226
  "These are runtime ops, not scene mutations — the batch undo group is a harmless no-op for them.",
223
227
  ],
224
228
  recovery: [
@@ -228,7 +232,8 @@ anti-patterns, and recovery steps.`, {}, async () => ({
228
232
  "If save fails: use the returned scenePath/error to repair the exact cause. A running game alone is not a generic scene-mutation blocker.",
229
233
  "If a mutation is rejected with identity_mismatch: the engine switched projects — call summer_get_project_context to rebind (only if you meant to follow it), then retry.",
230
234
  "If a guarded file mutation is rejected with content mismatch: call summer_read_file again, review the new content, and retry with its new sha256 only if the edit is still valid.",
231
- "If a run op or SimulateInput returns 'unsupported' / an unknown-op error: this engine build predates it — fall back to summer_play + summer_get_debugger_errors, or ask the user to interact.",
235
+ "If a run op returns 'unsupported' / an unknown-op error: this engine build predates it — fall back to summer_play + summer_get_debugger_errors.",
236
+ "If SimulateInput returns 'unsupported_transport': that is permanent for MCP, not a build gap. Rewrite the check as a RunVerification probe.",
232
237
  ],
233
238
  debugging: [
234
239
  "Set SUMMER_MCP_DEBUG=1 in the MCP server's environment to log a structured stderr line per tool call (tool, ok, durationMs, terminalState, errorClass, failureReason, retried, boundProjectIdHash). With the flag OFF, only failures are logged. Use it to see exactly which op failed and why.",
@@ -120,7 +120,7 @@ Common node types:
120
120
  The parent path uses "./" prefix for relative paths from scene root. E.g., "./World" means the "World" child of the root node.`, {
121
121
  scenePath: z.string().describe("Target scene path, e.g. 'res://main.tscn'"),
122
122
  parent: z.string().describe("Parent node path, e.g. './World' or './World/Enemies'"),
123
- type: z.string().describe("Godot node type, e.g. 'MeshInstance3D', 'CharacterBody3D'"),
123
+ type: z.string().describe("Summer Engine node type, e.g. 'MeshInstance3D', 'CharacterBody3D'"),
124
124
  name: z.string().describe("Name for the new node, e.g. 'Player', 'MainCamera'"),
125
125
  }, async ({ scenePath, parent, type, name }) => withEngine(async (client) => executeSceneMutation(client, scenePath, [{ op: "AddNode", parent, type, name }])));
126
126
  server.tool("summer_set_prop", `Set a property on a node. This is the primary way to configure nodes after adding them.
@@ -147,7 +147,7 @@ COMMON PROPERTIES:
147
147
  scenePath: z.string().describe("Target scene path, e.g. 'res://main.tscn'"),
148
148
  path: z.string().describe("Node path, e.g. './World/Player'"),
149
149
  key: z.string().describe("Property name, e.g. 'position', 'mesh', 'visible'"),
150
- value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in Godot string format for complex types, native JSON for primitives"),
150
+ value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in Summer Engine variant-string format for complex types, native JSON for primitives"),
151
151
  }, async ({ scenePath, path, key, value }) => withEngine(async (client) => executeSceneMutation(client, scenePath, [{ op: "SetProp", path, key, value }])));
152
152
  server.tool("summer_set_resource_property", `Set a nested property on a resource attached to a node.
153
153
 
@@ -159,7 +159,7 @@ Use when you need to modify a sub-property of a resource, like:
159
159
  nodePath: z.string().describe("Node path, e.g. './Player/CollisionShape3D'"),
160
160
  resourceProperty: z.string().describe("Resource property on the node, e.g. 'shape', 'mesh', 'material_override'"),
161
161
  subProperty: z.string().describe("Property on the resource, e.g. 'size', 'radius', 'albedo_color'"),
162
- value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in Godot string format"),
162
+ value: z.union([z.string(), z.number(), z.boolean()]).describe("Value in Summer Engine variant-string format"),
163
163
  }, async ({ scenePath, nodePath, resourceProperty, subProperty, value }) => withEngine(async (client) => executeSceneMutation(client, scenePath, [
164
164
  { op: "SetResourceProperty", nodePath, resourceProperty, subProperty, value },
165
165
  ])));
@@ -260,9 +260,9 @@ Each op in the array uses the same format as the individual tools:
260
260
  - {"op": "SetProp", "path": "Floor", "key": "mesh", "value": "PlaneMesh"}
261
261
  - {"op": "SetResourceProperty", "nodePath": "Floor", "resourceProperty": "mesh", "subProperty": "size", "value": "Vector2(20, 20)"}
262
262
 
263
- RAW RUNTIME OPS (interactive verification — engine-build dependent; structured failure_reason incl "unsupported" passes through verbatim):
264
- - SimulateInput — drive the RUNNING game (summer_play first): {"op": "SimulateInput", "type": "action", "action": "jump", "pressed": true}. type is "action" | "key" | "mouse_click" | "axis".
265
- - RunVerification — spawn a hidden, disposable game instance that runs a GDScript probe and dies (never touches the editor): {"op": "RunVerification", "probe_source": "extends SummerProbeBase\\nfunc _ready(): await super._ready(); report('ok', true); finish()", "max_seconds": 20}. Returns {ok, results, frames, out_dir}. Probe API: report()/save_frame()/press()/key()/finish().
263
+ RAW RUNTIME OPS (interactive verification — structured failure_reason passes through verbatim):
264
+ - RunVerification — spawn a hidden, disposable game instance that runs a GDScript probe and dies (never touches the editor): {"op": "RunVerification", "probe_source": "extends SummerProbeBase\\nfunc _ready(): await super._ready(); report('ok', true); finish()", "max_seconds": 20}. Returns {ok, results, frames, out_dir}. Probe API: report()/save_frame()/press()/key()/finish(). This is the ONLY way to drive input from MCP, and unlike the editor it renders real pixels, so save_frame() produces a real image.
265
+ - SimulateInput is NOT reachable from MCP or the CLI. It requires the in-editor chat bridge's async reply channel; every queued caller gets {"ok": false, "failure_reason": "unsupported_transport"} on every engine build. Do not send it and do not treat that reply as a version problem — use RunVerification's press()/key() instead.
266
266
 
267
267
  Do not mix OpenScene with scene mutations in one batch. OpenScene is a UI action;
268
268
  send it separately. scenePath selects every mutation target. The tool appends one
@@ -10,14 +10,15 @@ import { withEngine } from "./with-engine.js";
10
10
  export function registerVisualTools(server) {
11
11
  server.tool("summer_screenshot", `Capture a frame from Summer Engine and return it as an image you can look at directly.
12
12
 
13
- Use this to visually verify your work: scene layout, asset placement, scale, framing, lighting, materials, missing/untextured assets, or runtime gameplay state. You see the actual pixels — no description layer in between.
13
+ Use this to visually verify your work: scene layout, asset placement, scale, framing, missing/untextured assets, or runtime gameplay state. You see the actual pixels — no description layer in between. Lighting and materials are only truthfully shown by the "viewport" and "game" targets — see the note on "scene" below.
14
14
 
15
15
  target:
16
16
  "viewport" (default) — the editor's CURRENT view (whatever scene/tab is open). No game boot. Use for edit-time checks of how the scene looks right now.
17
- "scene" — an OFFSCREEN render of a scene file (no game boot; physics/particles/animations are static at t=0). Optionally pass scenePath/framing/size/nodePath. Use for a quick composed look at a scene without touching the editor's open tab.
17
+ "scene" — an OFFSCREEN render of a scene file (no game boot; physics/particles/animations are static at t=0). Optionally pass scenePath/framing/size/nodePath. Use for COMPOSITION, SCALE and FRAMING without touching the editor's open tab.
18
+ It does NOT use the scene's environment/sky, and it injects a synthetic camera and light when the scene has none. The scene's WorldEnvironment — sky, fog, tonemap, glow, SSAO, ambient — is replaced by a flat preview environment. So this target CANNOT verify lighting, mood, or any material property that depends on the environment: change them and the frame comes back identical. For those, boot the game or run a RunVerification probe, whose instance renders the real environment.
18
19
  "game" — a frame from the RUNNING game (real runtime state). Start the game first (summer_play). Not available over a plain local connection — needs the Summer desktop app bridge.
19
20
 
20
- Static frame only — one moment, not motion. If a game capture is unavailable, use "viewport" or "scene", or ask the user to share a screenshot of the running game.`, {
21
+ Static frame only — one moment, not motion. For a SEQUENCE of frames over time, or for anything lighting-dependent when the desktop bridge is unavailable, use a RunVerification probe's save_frame() — its instance has a real renderer.`, {
21
22
  target: z
22
23
  .enum(["viewport", "scene", "game"])
23
24
  .optional()
File without changes
File without changes
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Event: SessionStart (matcher: startup|clear|compact)
5
5
  # Purpose: Orient Claude on every fresh Summer-enabled session.
6
- # Detect Godot project + GameSoul + doctor health, surface a one-line
6
+ # Detect Summer project + GameSoul + doctor health, surface a one-line
7
7
  # Bob-style greeting as additional context.
8
8
  #
9
9
  # Input: JSON on stdin with { session_id, source, model, cwd, ... }
@@ -36,7 +36,7 @@ else
36
36
  LINES+=("Summer ready.")
37
37
  fi
38
38
 
39
- # Line 2: Godot project + GameSoul one-liner (only if project.godot exists)
39
+ # Line 2: Summer project + GameSoul one-liner (only if project.godot exists)
40
40
  if [ -f "${PROJECT_DIR}/project.godot" ]; then
41
41
  PROJECT_NAME=$(grep -E '^config/name=' "${PROJECT_DIR}/project.godot" 2>/dev/null \
42
42
  | head -1 | sed 's/^config\/name=//;s/^"//;s/"$//')
@@ -48,12 +48,12 @@ if [ -f "${PROJECT_DIR}/project.godot" ]; then
48
48
  SOUL_LINE=$(awk 'BEGIN{p=""} /^[^#[:space:]]/ && NF>0 {p=$0; exit} END{print p}' \
49
49
  "$SOUL_FILE" 2>/dev/null | cut -c1-120)
50
50
  if [ -n "$SOUL_LINE" ]; then
51
- LINES+=("Godot project: ${PROJECT_NAME}. GameSoul: ${SOUL_LINE}")
51
+ LINES+=("Summer project: ${PROJECT_NAME}. GameSoul: ${SOUL_LINE}")
52
52
  else
53
- LINES+=("Godot project: ${PROJECT_NAME}.")
53
+ LINES+=("Summer project: ${PROJECT_NAME}.")
54
54
  fi
55
55
  else
56
- LINES+=("Godot project: ${PROJECT_NAME}.")
56
+ LINES+=("Summer project: ${PROJECT_NAME}.")
57
57
  fi
58
58
  fi
59
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "summer-engine",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "Local Summer CLI and MCP server for Summer Engine. Install and run the engine, connect Claude Code, Cursor, Codex, Gemini, and other agents, and build real games with bundled skills, hooks, and plugins.",
5
5
  "keywords": [
6
6
  "summer-engine",
@@ -41,6 +41,7 @@
41
41
  "files": [
42
42
  "dist/",
43
43
  "skills/",
44
+ "references/",
44
45
  "commands/",
45
46
  "hooks/",
46
47
  "assets/",
@@ -65,6 +66,7 @@
65
66
  "dev": "tsc --watch",
66
67
  "summer": "node dist/bin/summer.js",
67
68
  "test": "vitest run",
69
+ "test:public-language": "vitest run src/lib/public-surface-language.test.ts",
68
70
  "test:watch": "vitest",
69
71
  "prepublishOnly": "npm run build && npm test"
70
72
  },
@@ -0,0 +1,50 @@
1
+ # Collaborative Protocol — "May I write?"
2
+
3
+ > Every Summer skill must follow this. The agent works *with* the user, not around them.
4
+
5
+ ## The rule
6
+
7
+ **Before any user-visible write step, the agent must explicitly state what it is about to do and ask permission to proceed.**
8
+
9
+ This applies to:
10
+ - Creating files (`.gd`, `.tscn`, `.tres`, JSON, configs)
11
+ - Modifying existing files (especially `.gd` and `.tscn`)
12
+ - Running irreversible operations (deletion, reset, force-overwrite)
13
+ - Setting up project-wide settings (`project.godot`)
14
+ - Installing tools, libraries, or templates
15
+
16
+ Reading is unconditional. Inspection, scene-tree walks, diagnostics — never need permission.
17
+
18
+ ## Phrasing
19
+
20
+ Plain, short, action-first. Examples:
21
+
22
+ > May I create `scenes/player.tscn` and add a CharacterBody3D + Camera3D?
23
+
24
+ > I'm about to overwrite `levels/level_01.tscn`. The current version has 14 nodes — I'll preserve them and add the new lighting setup. Continue?
25
+
26
+ > May I attach `scripts/player_controller.gd` to `./World/Player`?
27
+
28
+ > I'm going to bind `move_forward`, `move_back`, `move_left`, `move_right`, `jump`. OK?
29
+
30
+ ## What this prevents
31
+
32
+ 1. **Silent overwrites.** The agent stomps the user's hand-edited script.
33
+ 2. **Cascading scope creep.** A "small fix" turns into a 30-file refactor.
34
+ 3. **Unclear state.** The user has no idea what changed when something breaks.
35
+ 4. **Trust loss.** One unauthorized edit and the user stops trusting the agent.
36
+
37
+ ## What this is NOT
38
+
39
+ - Not a confirmation dialog for every property change in a single skill execution. Group related writes into one ask: "I'm about to add the player root, attach the controller script, and bind 5 input actions. OK?"
40
+ - Not a request for design input. The skill should know what to do; the ask is for permission, not direction.
41
+ - Not a stop on simple read-only ops or single-property tweaks the user clearly directed.
42
+
43
+ ## Linter check
44
+
45
+ The `workflow/skill-test/SKILL.md` static linter looks for the phrases:
46
+ - "May I" / "May I write" / "May I create" / "May I attach"
47
+ - "I'm about to"
48
+ - "Continue?" / "OK?" / "Proceed?"
49
+
50
+ A skill that mutates files but never says any of these fails the linter.
@@ -0,0 +1,121 @@
1
+ # GDScript Style — Summer Conventions
2
+
3
+ > The bar for GDScript in Summer. Skills should produce code that matches this. Where Godot's official style guide is silent, this is the tiebreaker.
4
+
5
+ ## File structure
6
+
7
+ ```gdscript
8
+ class_name PlayerController
9
+ extends CharacterBody3D
10
+
11
+ # 1. Constants
12
+ const SPEED := 5.0
13
+ const JUMP_VELOCITY := 4.5
14
+ const GRAVITY := 9.8
15
+
16
+ # 2. Exports (designer-tweakable in inspector)
17
+ @export var move_speed: float = 5.0
18
+ @export var jump_height: float = 1.0
19
+ @export_range(0.1, 10.0) var look_sensitivity: float = 1.0
20
+
21
+ # 3. Signals (events this node emits)
22
+ signal died
23
+ signal health_changed(new_value: int)
24
+
25
+ # 4. Onready references (cached node lookups)
26
+ @onready var camera: Camera3D = $Camera3D
27
+ @onready var collision: CollisionShape3D = $CollisionShape3D
28
+
29
+ # 5. Private state (snake_case, leading underscore optional)
30
+ var _is_grounded: bool = false
31
+ var _velocity := Vector3.ZERO
32
+
33
+ # 6. Lifecycle (in canonical order)
34
+ func _ready() -> void:
35
+ pass
36
+
37
+ func _process(delta: float) -> void:
38
+ pass
39
+
40
+ func _physics_process(delta: float) -> void:
41
+ pass
42
+
43
+ func _input(event: InputEvent) -> void:
44
+ pass
45
+
46
+ # 7. Public API (PascalCase or snake_case methods, snake_case for funcs)
47
+ func take_damage(amount: int) -> void:
48
+ pass
49
+
50
+ # 8. Private helpers (leading underscore)
51
+ func _calculate_movement(delta: float) -> Vector3:
52
+ return Vector3.ZERO
53
+ ```
54
+
55
+ ## Type hints
56
+
57
+ **Always type-hint.** No bare `var x = 5`. Use `:=` for inferred typing or `:` for explicit.
58
+
59
+ ```gdscript
60
+ # RIGHT
61
+ var speed: float = 5.0
62
+ var name := "Player"
63
+ func attack(target: Node3D, amount: int) -> bool:
64
+ return true
65
+
66
+ # WRONG
67
+ var speed = 5.0
68
+ func attack(target, amount):
69
+ return true
70
+ ```
71
+
72
+ ## Naming
73
+
74
+ | Thing | Convention | Example |
75
+ |---|---|---|
76
+ | Class | PascalCase | `class_name PlayerController` |
77
+ | File | snake_case | `player_controller.gd` |
78
+ | Variable, function | snake_case | `move_speed`, `take_damage()` |
79
+ | Constant | SCREAMING_SNAKE_CASE | `const MAX_HEALTH := 100` |
80
+ | Signal | snake_case (past tense for events) | `died`, `health_changed` |
81
+ | Private | leading underscore | `_is_grounded`, `_calculate_movement` |
82
+ | Node in scene | PascalCase | `Player`, `Camera3D`, `CollisionShape3D` |
83
+
84
+ ## Signals
85
+
86
+ Define with typed args. Connect in `_ready()` or in scene `.tscn` directly.
87
+
88
+ ```gdscript
89
+ signal health_changed(new_value: int, max_value: int)
90
+
91
+ func _ready() -> void:
92
+ health_changed.connect(_on_health_changed)
93
+
94
+ func _on_health_changed(new_value: int, max_value: int) -> void:
95
+ pass
96
+ ```
97
+
98
+ ## Tweens
99
+
100
+ Use `create_tween()` for animation. Don't manually lerp in `_process` unless you need fine control.
101
+
102
+ ```gdscript
103
+ var tween := create_tween()
104
+ tween.tween_property(self, "modulate", Color.WHITE, 0.3)
105
+ ```
106
+
107
+ ## What to avoid
108
+
109
+ - Bare `var x = something` (no type)
110
+ - `Object` typed parameters (use specific class)
111
+ - `match` for two-branch logic (use `if`)
112
+ - Recreating node lookups in `_process` (cache via `@onready`)
113
+ - Hard-coded strings for node paths in code (use `@onready` + scene-relative paths)
114
+ - Setting `position` in `_process` for physics objects (use `_physics_process` and `velocity`)
115
+
116
+ ## See also
117
+
118
+ - [Official Godot GDScript style guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html)
119
+ - `scripting-patterns/gdscript-patterns/SKILL.md` — patterns and idioms
120
+ - `scripting-patterns/signal-patterns/SKILL.md` — signal usage
121
+ - `scripting-patterns/state-machine-patterns/SKILL.md` — state machine patterns
@@ -0,0 +1,51 @@
1
+ # Summer Engine Compatibility Reference
2
+
3
+ > Read this before writing version-sensitive code, especially shaders,
4
+ > rendering code, extensions, or export configuration.
5
+
6
+ ## Current technical base
7
+
8
+ Summer Engine is the product. Its current upstream technical base is **4.6.1**
9
+ (stable, Mono), the approved next target is **4.7.1**, and Summer follows
10
+ upstream continuously. These are compatibility and lineage facts. They are not
11
+ the Summer product name or a permanent SDK version.
12
+
13
+ The repository source of truth is
14
+ `compatibility/summer-engine.json`. The running engine also reports its measured
15
+ technical version:
16
+
17
+ ```bash
18
+ <engineBinaryPath> --version # measured: 4.6.1.stable.mono.custom_build.b708b1182
19
+ ```
20
+
21
+ Get `engineBinaryPath` from `summer_get_project_context`. Prefer live
22
+ inspection to a copied version string. Creator onboarding and headlines should
23
+ say Summer Engine, Summer game, Summer SDK, and GDScript. Use the upstream name
24
+ only where technical compatibility, migration, extension APIs, contribution
25
+ routing, attribution, or licensing requires it.
26
+
27
+ ## LLM training cutoff versus upstream releases
28
+
29
+ | Domain | Godot 4.x churn | Action |
30
+ |---|---|---|
31
+ | Scripting (GDScript core) | low | Trust your training. Spot-check signatures only when something feels off. |
32
+ | Scene / node tree APIs | low | Trust your training. |
33
+ | Renderer (`RenderingServer`, `Compositor`) | high in 4.4 / 4.5 | Verify before writing. Check `MovieMaker`, `Compositor` effects, `RenderSceneBuffers` — these renamed/added recently. |
34
+ | Shaders (`canvas_item`, `spatial`, `compute`) | medium | Built-in functions stable, but `hint_*` flags shifted. Verify `hint_screen_texture` / `hint_depth_texture` syntax. |
35
+ | Animation (`AnimationTree`, `AnimationMixer`) | high (4.0 → 4.3 rename) | Use `AnimationMixer` for new code, not deprecated `AnimationPlayer`-as-mixer. |
36
+ | Multiplayer (`MultiplayerAPI`) | medium | High-level API stable. `SceneReplicationConfig` settled in 4.0+. |
37
+ | Editor / EditorPlugin | medium | Spot-check. |
38
+ | `Tween` / `SceneTreeTween` | low | Stable since 4.0. |
39
+
40
+ ## Summer-specific deltas from the upstream engine
41
+
42
+ - Local API server runs on `localhost:6550` (set per machine via `~/.summer/api-token`).
43
+ - Engine ships a webview module in `modules/1summer_engine/`. Do not assume the
44
+ unmodified upstream editor for UI patches.
45
+ - Project root contains `.summer/` for project memory; do not delete it.
46
+
47
+ ## When in doubt
48
+
49
+ 1. Use Summer MCP tools (`summer_inspect_node`, `summer_inspect_resource`) to read live editor state instead of guessing.
50
+ 2. Use `summer_get_diagnostics` after every scene change.
51
+ 3. Check `references/mcp-tools-reference.md` for the canonical tool list.