summer-engine 1.2.0 → 1.2.1
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/README.md +58 -24
- package/dist/bin/summer.js +4 -0
- package/dist/commands/doctor.d.ts +26 -0
- package/dist/commands/doctor.js +248 -0
- package/dist/commands/doctor.test.d.ts +1 -0
- package/dist/commands/doctor.test.js +29 -0
- package/dist/commands/mcp.js +49 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +128 -0
- package/dist/commands/skills.js +246 -107
- package/dist/lib/agent-config.d.ts +39 -0
- package/dist/lib/agent-config.js +284 -0
- package/dist/lib/agent-config.test.d.ts +1 -0
- package/dist/lib/agent-config.test.js +80 -0
- package/dist/lib/api-client.d.ts +19 -2
- package/dist/lib/api-client.js +129 -14
- package/dist/lib/skills-registry.d.ts +69 -0
- package/dist/lib/skills-registry.js +118 -0
- package/dist/mcp/tools/project-tools.js +100 -23
- package/dist/mcp/tools/with-engine.js +3 -53
- package/package.json +5 -2
- package/skills/gdscript-patterns/SKILL.md +2 -2
- package/skills/make-game/SKILL.md +3 -3
- package/skills/scene-composition/SKILL.md +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export const AGENT_CLIENTS = [
|
|
2
|
+
"summer",
|
|
3
|
+
"codex",
|
|
4
|
+
"claude-code",
|
|
5
|
+
"cursor",
|
|
6
|
+
"windsurf",
|
|
7
|
+
];
|
|
8
|
+
const ALL_CLIENTS = AGENT_CLIENTS;
|
|
9
|
+
export const SKILL_REGISTRY = [
|
|
10
|
+
{
|
|
11
|
+
name: "gdscript-patterns",
|
|
12
|
+
public: true,
|
|
13
|
+
category: "scripting",
|
|
14
|
+
clients: ALL_CLIENTS,
|
|
15
|
+
recommended: true,
|
|
16
|
+
requiresMcpTools: [
|
|
17
|
+
"summer_set_prop",
|
|
18
|
+
"summer_connect_signal",
|
|
19
|
+
"summer_input_map_bind",
|
|
20
|
+
"summer_get_script_errors",
|
|
21
|
+
],
|
|
22
|
+
testScenario: "Create a typed GDScript with a signal, attach it to a node, connect the signal, and verify script errors.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "scene-composition",
|
|
26
|
+
public: true,
|
|
27
|
+
category: "scene",
|
|
28
|
+
clients: ALL_CLIENTS,
|
|
29
|
+
recommended: true,
|
|
30
|
+
requiresMcpTools: [
|
|
31
|
+
"summer_create_scene",
|
|
32
|
+
"summer_open_scene",
|
|
33
|
+
"summer_get_scene_tree",
|
|
34
|
+
"summer_add_node",
|
|
35
|
+
"summer_instantiate_scene",
|
|
36
|
+
"summer_save_scene",
|
|
37
|
+
],
|
|
38
|
+
testScenario: "Build a small reusable player scene, instantiate it in a main scene, and save both scenes through Summer tools.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "fps-controller",
|
|
42
|
+
public: true,
|
|
43
|
+
category: "gameplay",
|
|
44
|
+
clients: ALL_CLIENTS,
|
|
45
|
+
recommended: true,
|
|
46
|
+
requiresMcpTools: [
|
|
47
|
+
"summer_add_node",
|
|
48
|
+
"summer_set_prop",
|
|
49
|
+
"summer_set_resource_property",
|
|
50
|
+
"summer_input_map_bind",
|
|
51
|
+
],
|
|
52
|
+
testScenario: "Create a CharacterBody3D player with collision, camera, input actions, and a host-edited controller script.",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "3d-lighting",
|
|
56
|
+
public: true,
|
|
57
|
+
category: "lighting",
|
|
58
|
+
clients: ALL_CLIENTS,
|
|
59
|
+
recommended: true,
|
|
60
|
+
requiresMcpTools: [
|
|
61
|
+
"summer_add_node",
|
|
62
|
+
"summer_set_prop",
|
|
63
|
+
"summer_set_resource_property",
|
|
64
|
+
],
|
|
65
|
+
testScenario: "Add sun, fill lighting, WorldEnvironment, and shadow settings to a simple 3D scene.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "ui-basics",
|
|
69
|
+
public: true,
|
|
70
|
+
category: "ui",
|
|
71
|
+
clients: ALL_CLIENTS,
|
|
72
|
+
recommended: true,
|
|
73
|
+
requiresMcpTools: [
|
|
74
|
+
"summer_add_node",
|
|
75
|
+
"summer_set_prop",
|
|
76
|
+
"summer_connect_signal",
|
|
77
|
+
],
|
|
78
|
+
testScenario: "Create a HUD CanvasLayer with labels, a ProgressBar, and a connected Button signal.",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "asset-strategy",
|
|
82
|
+
public: true,
|
|
83
|
+
category: "assets",
|
|
84
|
+
clients: ALL_CLIENTS,
|
|
85
|
+
recommended: true,
|
|
86
|
+
requiresMcpTools: [
|
|
87
|
+
"summer_search_assets",
|
|
88
|
+
"summer_import_asset",
|
|
89
|
+
"summer_import_from_url",
|
|
90
|
+
"summer_generate_image",
|
|
91
|
+
"summer_generate_3d",
|
|
92
|
+
"summer_generate_audio",
|
|
93
|
+
],
|
|
94
|
+
testScenario: "Choose library, generated, or primitive assets for a small level and import one asset into the project.",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "make-game",
|
|
98
|
+
public: true,
|
|
99
|
+
category: "workflow",
|
|
100
|
+
clients: ALL_CLIENTS,
|
|
101
|
+
recommended: false,
|
|
102
|
+
requiresMcpTools: [
|
|
103
|
+
"summer_get_agent_playbook",
|
|
104
|
+
"summer_get_project_context",
|
|
105
|
+
"summer_create_scene",
|
|
106
|
+
"summer_open_scene",
|
|
107
|
+
"summer_save_scene",
|
|
108
|
+
"summer_add_node",
|
|
109
|
+
"summer_set_prop",
|
|
110
|
+
"summer_set_resource_property",
|
|
111
|
+
"summer_instantiate_scene",
|
|
112
|
+
"summer_play",
|
|
113
|
+
"summer_stop",
|
|
114
|
+
"summer_get_diagnostics",
|
|
115
|
+
],
|
|
116
|
+
testScenario: "Build one narrow playable prototype from requirements through scene setup, host-edited scripts, and diagnostics.",
|
|
117
|
+
},
|
|
118
|
+
];
|
|
@@ -1,18 +1,83 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { withEngine } from "./with-engine.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
function asRecord(value) {
|
|
4
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: null;
|
|
7
|
+
}
|
|
8
|
+
function stringFrom(value) {
|
|
9
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
10
|
+
}
|
|
11
|
+
function pickString(record, keys) {
|
|
12
|
+
if (!record)
|
|
7
13
|
return null;
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
for (const key of keys) {
|
|
15
|
+
const value = stringFrom(record[key]);
|
|
16
|
+
if (value)
|
|
17
|
+
return value;
|
|
12
18
|
}
|
|
13
|
-
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function getProjectData(projectState) {
|
|
22
|
+
return asRecord(asRecord(projectState)?.data);
|
|
23
|
+
}
|
|
24
|
+
function getProjectSetting(projectState, keys) {
|
|
25
|
+
const data = getProjectData(projectState);
|
|
26
|
+
const entries = data?.entries;
|
|
27
|
+
if (!Array.isArray(entries))
|
|
14
28
|
return null;
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const item = asRecord(entry);
|
|
31
|
+
const key = stringFrom(item?.key);
|
|
32
|
+
if (key && keys.includes(key)) {
|
|
33
|
+
return stringFrom(item?.value);
|
|
34
|
+
}
|
|
15
35
|
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function getProjectPath(projectState) {
|
|
39
|
+
const root = asRecord(projectState);
|
|
40
|
+
const data = getProjectData(projectState);
|
|
41
|
+
return (pickString(data, [
|
|
42
|
+
"projectPath",
|
|
43
|
+
"project_path",
|
|
44
|
+
"projectRoot",
|
|
45
|
+
"project_root",
|
|
46
|
+
"rootPath",
|
|
47
|
+
"root_path",
|
|
48
|
+
]) ??
|
|
49
|
+
pickString(root, ["projectPath", "project_path", "projectRoot", "project_root"]) ??
|
|
50
|
+
null);
|
|
51
|
+
}
|
|
52
|
+
function getProjectName(projectState) {
|
|
53
|
+
const root = asRecord(projectState);
|
|
54
|
+
const data = getProjectData(projectState);
|
|
55
|
+
return (pickString(data, ["projectName", "project_name", "name"]) ??
|
|
56
|
+
pickString(root, ["projectName", "project_name", "name"]) ??
|
|
57
|
+
getProjectSetting(projectState, ["application/config/name", "config/name"]));
|
|
58
|
+
}
|
|
59
|
+
function getMainScene(projectState) {
|
|
60
|
+
const root = asRecord(projectState);
|
|
61
|
+
const data = getProjectData(projectState);
|
|
62
|
+
return (pickString(data, ["mainScene", "main_scene", "mainScenePath", "main_scene_path"]) ??
|
|
63
|
+
pickString(root, ["mainScene", "main_scene", "mainScenePath", "main_scene_path"]) ??
|
|
64
|
+
getProjectSetting(projectState, [
|
|
65
|
+
"application/run/main_scene",
|
|
66
|
+
"run/main_scene",
|
|
67
|
+
]));
|
|
68
|
+
}
|
|
69
|
+
function getCurrentScene(projectState, sceneState) {
|
|
70
|
+
const sceneRoot = asRecord(sceneState);
|
|
71
|
+
const sceneData = asRecord(sceneRoot?.data);
|
|
72
|
+
const sceneProvenance = asRecord(sceneRoot?.provenance);
|
|
73
|
+
const projectRoot = asRecord(projectState);
|
|
74
|
+
const projectData = getProjectData(projectState);
|
|
75
|
+
const projectProvenance = asRecord(projectRoot?.provenance);
|
|
76
|
+
return (pickString(sceneProvenance, ["scenePath", "scene_path"]) ??
|
|
77
|
+
pickString(sceneData, ["scenePath", "scene_path", "currentScene", "current_scene"]) ??
|
|
78
|
+
pickString(projectData, ["currentScene", "current_scene", "scenePath", "scene_path"]) ??
|
|
79
|
+
pickString(projectProvenance, ["scenePath", "scene_path"]) ??
|
|
80
|
+
null);
|
|
16
81
|
}
|
|
17
82
|
export function registerProjectTools(server) {
|
|
18
83
|
server.tool("summer_get_agent_playbook", `AI-first operating guide for Summer Engine MCP.
|
|
@@ -25,7 +90,8 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
25
90
|
text: JSON.stringify({
|
|
26
91
|
startupChecklist: [
|
|
27
92
|
"Decide first: file edit or engine operation.",
|
|
28
|
-
"
|
|
93
|
+
"Write .gd/.cs/.json/docs/simple config with host file-edit tools directly.",
|
|
94
|
+
"Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, screenshots/verification.",
|
|
29
95
|
"Call summer_get_project_context first.",
|
|
30
96
|
"If no scene is open, call summer_open_main_scene.",
|
|
31
97
|
"Call summer_get_scene_tree before structural edits.",
|
|
@@ -33,15 +99,16 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
33
99
|
],
|
|
34
100
|
safeDefaults: [
|
|
35
101
|
"Never guess scene filenames (main.tscn/Main.tscn).",
|
|
36
|
-
"Do not route ordinary
|
|
102
|
+
"Do not route ordinary script, JSON, docs, or simple config edits through MCP when direct file tools are available.",
|
|
37
103
|
"Never remove multiple top-level nodes unless user explicitly requests destructive edits.",
|
|
38
|
-
"
|
|
104
|
+
"Use MCP for scene-tree mutations and inspector-visible values instead of hand-editing live scene state.",
|
|
39
105
|
],
|
|
40
106
|
preferredFlow: [
|
|
41
107
|
"summer_get_project_context",
|
|
42
108
|
"summer_open_main_scene (if needed)",
|
|
43
109
|
"summer_get_scene_tree",
|
|
44
110
|
"edit via summer_add_node / summer_set_prop / summer_set_resource_property",
|
|
111
|
+
"write scripts/data/docs/simple config directly with host file-edit tools",
|
|
45
112
|
"summer_save_scene",
|
|
46
113
|
"summer_get_diagnostics",
|
|
47
114
|
],
|
|
@@ -56,35 +123,45 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
56
123
|
}));
|
|
57
124
|
server.tool("summer_get_project_context", `Get essential project context before editing. Returns:
|
|
58
125
|
- engine health/status
|
|
59
|
-
- project name and path
|
|
126
|
+
- project name and project path when exposed by project state
|
|
60
127
|
- current scene path (if available)
|
|
61
|
-
- main scene path from project
|
|
128
|
+
- main scene path from project settings
|
|
62
129
|
|
|
63
130
|
Use this first in every fresh chat to avoid guessing scene filenames or editing the wrong scene.`, {}, async () => withEngine(async (client) => {
|
|
64
|
-
const [health, projectState] = await Promise.all([
|
|
131
|
+
const [health, projectState, sceneState] = await Promise.all([
|
|
65
132
|
client.health(),
|
|
66
133
|
client.getProjectState(),
|
|
134
|
+
client.getSceneState().catch((err) => ({
|
|
135
|
+
ok: false,
|
|
136
|
+
error: err instanceof Error ? err.message : String(err),
|
|
137
|
+
})),
|
|
67
138
|
]);
|
|
68
|
-
const
|
|
69
|
-
const
|
|
139
|
+
const projectPath = getProjectPath(projectState);
|
|
140
|
+
const projectName = getProjectName(projectState);
|
|
141
|
+
const mainScene = getMainScene(projectState);
|
|
142
|
+
const currentScene = getCurrentScene(projectState, sceneState);
|
|
70
143
|
return {
|
|
71
144
|
health,
|
|
72
145
|
project: projectState,
|
|
146
|
+
scene: sceneState,
|
|
147
|
+
projectName,
|
|
148
|
+
projectPath,
|
|
149
|
+
currentScene,
|
|
73
150
|
mainScene,
|
|
74
151
|
guidance: mainScene
|
|
75
152
|
? "Use `summer_open_scene` with `mainScene` if no scene is open."
|
|
76
|
-
: "Main scene not found in project.
|
|
153
|
+
: "Main scene not found in project state. Open a known scene path explicitly.",
|
|
154
|
+
fileEditingGuidance: "Write .gd/.cs/.json/docs/simple config directly with host file tools. Use MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, and visual verification.",
|
|
77
155
|
};
|
|
78
156
|
}));
|
|
79
|
-
server.tool("summer_open_main_scene", `Open the project's configured main scene from project.
|
|
157
|
+
server.tool("summer_open_main_scene", `Open the project's configured main scene from project settings.
|
|
80
158
|
|
|
81
159
|
Safer than guessing scene names like main.tscn/Main.tscn.
|
|
82
160
|
Call this when you get "no scene open".`, {}, async () => withEngine(async (client) => {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const mainScene = await readMainSceneFromProject(projectPath);
|
|
161
|
+
const projectState = await client.getProjectState();
|
|
162
|
+
const mainScene = getMainScene(projectState);
|
|
86
163
|
if (!mainScene) {
|
|
87
|
-
throw new Error("Could not resolve application/run/main_scene from project.
|
|
164
|
+
throw new Error("Could not resolve application/run/main_scene from project state. Call `summer_get_project_context` and open a scene explicitly.");
|
|
88
165
|
}
|
|
89
166
|
return client.executeOps([{ op: "OpenScene", path: mainScene }]);
|
|
90
167
|
}));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getClient, resetClient } from "../server.js";
|
|
2
|
-
import { getAuthToken } from "../../lib/auth.js";
|
|
3
|
-
const GATEWAY_URL = process.env.SUMMER_GATEWAY_URL || "https://www.summerengine.com";
|
|
4
2
|
function extractOpError(result) {
|
|
5
3
|
if (!result || typeof result !== "object")
|
|
6
4
|
return null;
|
|
7
5
|
const op = result;
|
|
6
|
+
if (op.ok === false && op.error)
|
|
7
|
+
return op.error;
|
|
8
8
|
if (op.status === "error" && op.error)
|
|
9
9
|
return op.error;
|
|
10
10
|
const firstFailed = op.results?.find((r) => r.ok === false && r.error);
|
|
@@ -19,62 +19,12 @@ function buildActionHint(message) {
|
|
|
19
19
|
return "Scene path could not be opened. Call `summer_get_project_context` to get `mainScene`, then open that exact path. Avoid guessing scene filenames.";
|
|
20
20
|
}
|
|
21
21
|
if (normalized.includes("writefile cannot edit .tscn/.scn")) {
|
|
22
|
-
return "
|
|
22
|
+
return "Write .gd/.cs/.json/docs/simple config files with normal file-edit tools. Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, and visual verification.";
|
|
23
23
|
}
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
-
async function checkMcpQuota() {
|
|
27
|
-
const token = await getAuthToken();
|
|
28
|
-
if (!token) {
|
|
29
|
-
return {
|
|
30
|
-
allowed: false,
|
|
31
|
-
message: "Not signed in. The user needs to run this in their terminal:\n npx summer-engine login\nOr open: https://www.summerengine.com/login\nMCP tools require a Summer Engine account (free to create).",
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
const res = await fetch(`${GATEWAY_URL}/api/mcp/log-local-call`, {
|
|
36
|
-
method: "POST",
|
|
37
|
-
headers: {
|
|
38
|
-
Authorization: `Bearer ${token}`,
|
|
39
|
-
"Content-Type": "application/json",
|
|
40
|
-
},
|
|
41
|
-
body: JSON.stringify({}),
|
|
42
|
-
signal: AbortSignal.timeout(10000),
|
|
43
|
-
});
|
|
44
|
-
const data = (await res.json().catch(() => ({})));
|
|
45
|
-
// Hard enforcement errors should block tool execution.
|
|
46
|
-
if ([401, 402, 403, 429].includes(res.status)) {
|
|
47
|
-
return {
|
|
48
|
-
allowed: false,
|
|
49
|
-
message: data.message || "Quota check failed. Try again.",
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
// Soft-fail on service/gateway issues so local tools keep working.
|
|
53
|
-
if (!res.ok) {
|
|
54
|
-
return { allowed: true };
|
|
55
|
-
}
|
|
56
|
-
if (data.allowed === false) {
|
|
57
|
-
return {
|
|
58
|
-
allowed: false,
|
|
59
|
-
message: data.message || "MCP call limit reached. Upgrade at https://summerengine.com/pricing",
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return { allowed: true };
|
|
63
|
-
}
|
|
64
|
-
catch (err) {
|
|
65
|
-
// Network/transient errors should not brick local MCP usage.
|
|
66
|
-
return { allowed: true };
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
26
|
export async function withEngine(fn) {
|
|
70
27
|
try {
|
|
71
|
-
const quota = await checkMcpQuota();
|
|
72
|
-
if (!quota.allowed) {
|
|
73
|
-
return {
|
|
74
|
-
content: [{ type: "text", text: quota.message || "Quota exceeded" }],
|
|
75
|
-
isError: true,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
28
|
const client = await getClient();
|
|
79
29
|
const result = await fn(client);
|
|
80
30
|
const opError = extractOpError(result);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "summer-engine",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "CLI and MCP tools for Summer Engine — the AI-native game engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game-engine",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"build": "tsc",
|
|
32
32
|
"dev": "tsc --watch",
|
|
33
33
|
"summer": "node dist/bin/summer.js",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest",
|
|
34
36
|
"prepublishOnly": "npm run build"
|
|
35
37
|
},
|
|
36
38
|
"engines": {
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@types/diff": "^7.0.0",
|
|
50
52
|
"@types/node": "^20.0.0",
|
|
51
|
-
"typescript": "^5.5.0"
|
|
53
|
+
"typescript": "^5.5.0",
|
|
54
|
+
"vitest": "^3.2.4"
|
|
52
55
|
}
|
|
53
56
|
}
|
|
@@ -89,8 +89,8 @@ var siblings = get_parent().get_children()
|
|
|
89
89
|
|
|
90
90
|
For health systems, input handling, and state machines, see [reference.md](reference.md).
|
|
91
91
|
|
|
92
|
-
##
|
|
92
|
+
## Scene Integration
|
|
93
93
|
|
|
94
|
-
When the AI adds a
|
|
94
|
+
When the AI adds a node with Summer MCP tools and attaches a script, the script path is set via `summer_set_prop(path, "script", "res://path/to/script.gd")`. The script file must exist first. Create or edit `.gd` files with the host agent's normal file-editing tools, then use Summer MCP only to attach the script and connect scene signals.
|
|
95
95
|
|
|
96
96
|
For signal connections, the receiver must have the handler method. Create the script with the method stub before calling `summer_connect_signal`.
|
|
@@ -12,7 +12,7 @@ compatibility:
|
|
|
12
12
|
|
|
13
13
|
Step-by-step workflow for building a complete game. Follow phases 0-6 in order. Each phase must complete before moving to the next.
|
|
14
14
|
|
|
15
|
-
**Rule**: Scene operations (.tscn) use MCP tools. Script files (.gd) use host file-
|
|
15
|
+
**Rule**: Scene operations (.tscn) use Summer MCP tools. Script files (.gd) use the host agent's normal file-editing tools. Never cross the streams.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -218,7 +218,7 @@ summer_save_scene()
|
|
|
218
218
|
|
|
219
219
|
## Phase 4: Scripting
|
|
220
220
|
|
|
221
|
-
**Write .gd files with host file-
|
|
221
|
+
**Write .gd files with the host agent's normal file-editing tools. Attach via MCP.**
|
|
222
222
|
|
|
223
223
|
Workflow:
|
|
224
224
|
1. Write the script file to disk
|
|
@@ -395,7 +395,7 @@ After first playable, ask the user what to improve. Common next steps:
|
|
|
395
395
|
|
|
396
396
|
## Anti-Patterns
|
|
397
397
|
|
|
398
|
-
- Never write .tscn files with host file tools -- always use MCP scene tools
|
|
398
|
+
- Never write .tscn files with host agent file tools -- always use Summer MCP scene tools
|
|
399
399
|
- Never guess node paths -- call `summer_get_scene_tree()` first
|
|
400
400
|
- Never edit scenes while game is running -- call `summer_stop()` first
|
|
401
401
|
- Never skip `summer_save_scene()` -- unsaved changes are editor-only
|
|
@@ -79,7 +79,7 @@ Use `./` for paths relative to the scene root:
|
|
|
79
79
|
|
|
80
80
|
1. Build the hierarchy in the main scene
|
|
81
81
|
2. Select the root of what you want to extract
|
|
82
|
-
3. Save as scene: `summer_save_scene(path="res://scenes/player.tscn")
|
|
82
|
+
3. Save as scene: `summer_save_scene(path="res://scenes/player.tscn")`. SaveScene saves the current open scene, so build reusable scenes in their own open scene and save them there. Do not handwrite `.tscn` files as the preferred path.
|
|
83
83
|
4. In the main scene, add it with `summer_instantiate_scene(parent="./World", scene="res://scenes/player.tscn", name="Player")`
|
|
84
84
|
|
|
85
85
|
**Practical approach:** Create reusable scenes (player.tscn, enemy.tscn) as separate scene files, then instantiate them into levels.
|