summer-engine 2.3.0 → 2.5.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +20 -5
- package/.codex-plugin/plugin.json +15 -5
- package/.cursor-plugin/plugin.json +15 -5
- package/.factory-plugin/plugin.json +2 -2
- package/.opencode/INSTALL.md +1 -1
- package/CHANGELOG.md +27 -6
- package/CLAUDE.md +2 -2
- package/GEMINI.md +2 -3
- package/README.md +51 -30
- package/_persona/summer/SKILL.md +5 -6
- package/commands/gameskill.md +103 -0
- package/dist/bin/summer.js +12 -4
- package/dist/commands/create.js +2 -0
- package/dist/commands/doctor.js +71 -1
- package/dist/commands/install.d.ts +17 -0
- package/dist/commands/install.js +63 -4
- package/dist/commands/memory.d.ts +14 -0
- package/dist/commands/memory.js +196 -0
- package/dist/commands/setup.js +8 -2
- package/dist/commands/skills.js +90 -6
- package/dist/commands/status.js +3 -0
- package/dist/lib/agent-config.d.ts +2 -2
- package/dist/lib/agent-config.js +134 -7
- package/dist/lib/api-client.d.ts +13 -0
- package/dist/lib/api-client.js +63 -26
- package/dist/lib/async-op-lifecycle.d.ts +60 -0
- package/dist/lib/async-op-lifecycle.js +83 -0
- package/dist/lib/project-memory.d.ts +33 -0
- package/dist/lib/project-memory.js +220 -0
- package/dist/lib/skills-registry.d.ts +134 -61
- package/dist/lib/skills-registry.js +102 -4
- package/dist/lib/version-check.d.ts +106 -0
- package/dist/lib/version-check.js +338 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +81 -0
- package/dist/mcp/tools/debug-tools.js +32 -2
- package/dist/mcp/tools/project-tools.js +18 -6
- package/dist/mcp/tools/with-engine.d.ts +14 -0
- package/dist/mcp/tools/with-engine.js +52 -7
- package/gemini-extension.json +2 -2
- package/hooks/pre-commit-doctor.sh +0 -0
- package/hooks/run-hook.cmd +0 -0
- package/hooks/session-start.sh +0 -0
- package/package.json +6 -4
- package/skills/2d-assets/character-portrait/SKILL.md +1 -1
- package/skills/2d-assets/concept-art/SKILL.md +1 -1
- package/skills/2d-assets/create-asset-sheet/SKILL.md +163 -0
- package/skills/2d-assets/instantiate-asset-pack/SKILL.md +245 -0
- package/skills/2d-assets/pixel-art/SKILL.md +1 -1
- package/skills/2d-assets/skybox-panorama/SKILL.md +1 -1
- package/skills/2d-assets/sprite-sheet/SKILL.md +1 -1
- package/skills/2d-assets/tileable-texture/SKILL.md +1 -1
- package/skills/2d-assets/ui-graphics/SKILL.md +1 -1
- package/skills/2d-assets/use-widget-asset/SKILL.md +217 -0
- package/skills/3d-assets/character-model/SKILL.md +1 -1
- package/skills/3d-assets/organic-model/SKILL.md +1 -1
- package/skills/3d-assets/prop-model/SKILL.md +1 -1
- package/skills/3d-assets/vehicle-model/SKILL.md +1 -1
- package/skills/README.md +37 -30
- package/skills/ai-and-npcs/design-npc/SKILL.md +6 -6
- package/skills/animation/animation-tree/SKILL.md +2 -2
- package/skills/animation/facial-and-lipsync/SKILL.md +1 -1
- package/skills/animation/generate-motion/SKILL.md +1 -1
- package/skills/animation/procedural-animation/SKILL.md +2 -2
- package/skills/audio/adaptive-music/SKILL.md +1 -1
- package/skills/audio/audio-direction/SKILL.md +5 -5
- package/skills/audio/sound-effect/SKILL.md +2 -2
- package/skills/audio/voice-line/SKILL.md +25 -3
- package/skills/character-controllers/fps-controller/SKILL.md +5 -5
- package/skills/debugging/debug/SKILL.md +39 -1
- package/skills/deployment/export-and-ship/SKILL.md +4 -4
- package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +170 -0
- package/skills/gameplay-mechanics/design-mechanic/SKILL.md +8 -8
- package/skills/level-design/design-level/SKILL.md +5 -5
- package/skills/level-design/scene-to-level/SKILL.md +125 -0
- package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +4 -4
- package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +4 -4
- package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +7 -7
- package/skills/performance/tune-performance/SKILL.md +5 -5
- package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -1
- package/skills/rendering-and-lighting/art-direction/SKILL.md +5 -5
- package/skills/scene-and-project/brainstorm-game/SKILL.md +4 -4
- package/skills/scene-and-project/play/SKILL.md +1 -1
- package/skills/scene-and-project/scene-composition/SKILL.md +30 -1
- package/skills/scripting-patterns/gdscript-patterns/SKILL.md +53 -0
- package/skills/ui-and-ux/ui-basics/SKILL.md +1 -1
- package/skills/video/animated-loop/SKILL.md +1 -1
- package/skills/video/cinematic-cutscene/SKILL.md +1 -1
- package/skills/video/trailer-shot/SKILL.md +1 -1
- package/skills/visual-effects/game-feel/SKILL.md +6 -6
- package/skills/workflow/brainstorming/SKILL.md +164 -0
- package/skills/workflow/brainstorming/scripts/frame-template.html +214 -0
- package/skills/workflow/brainstorming/scripts/helper.js +88 -0
- package/skills/workflow/brainstorming/scripts/server.cjs +354 -0
- package/skills/workflow/brainstorming/scripts/start-server.sh +148 -0
- package/skills/workflow/brainstorming/scripts/stop-server.sh +56 -0
- package/skills/workflow/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/skills/workflow/brainstorming/visual-companion.md +287 -0
- package/skills/workflow/debugging-game-feel/SKILL.md +174 -0
- package/skills/workflow/diagnosing-perf-regressions/SKILL.md +190 -0
- package/skills/workflow/dispatching-parallel-agents/SKILL.md +182 -0
- package/skills/workflow/gameskill/SKILL.md +111 -0
- package/skills/workflow/investigating-bugs/CREATION-LOG.md +119 -0
- package/skills/workflow/investigating-bugs/SKILL.md +296 -0
- package/skills/workflow/investigating-bugs/condition-based-waiting-example.ts +158 -0
- package/skills/workflow/investigating-bugs/condition-based-waiting.md +115 -0
- package/skills/workflow/investigating-bugs/defense-in-depth.md +122 -0
- package/skills/workflow/investigating-bugs/find-polluter.sh +63 -0
- package/skills/workflow/investigating-bugs/root-cause-tracing.md +169 -0
- package/skills/workflow/investigating-bugs/test-academic.md +14 -0
- package/skills/workflow/investigating-bugs/test-pressure-1.md +58 -0
- package/skills/workflow/investigating-bugs/test-pressure-2.md +68 -0
- package/skills/workflow/investigating-bugs/test-pressure-3.md +69 -0
- package/skills/workflow/playtesting-a-feature/SKILL.md +173 -0
- package/skills/{_meta → workflow}/skill-create/SKILL.md +32 -22
- package/skills/{_meta → workflow}/skill-improve/SKILL.md +9 -10
- package/skills/{_meta → workflow}/skill-test/SKILL.md +12 -12
- package/skills/{_meta → workflow}/using-summer/SKILL.md +5 -2
- package/skills/workflow/verification-before-completion/SKILL.md +139 -0
- package/skills/workflow/writing-plans/SKILL.md +152 -0
- package/skills/workflow/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/skills/workflow/writing-skills/SKILL.md +655 -0
- package/skills/workflow/writing-skills/anthropic-best-practices.md +1150 -0
- package/skills/workflow/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/workflow/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/workflow/writing-skills/persuasion-principles.md +187 -0
- package/skills/workflow/writing-skills/render-graphs.js +168 -0
- package/skills/workflow/writing-skills/testing-skills-with-subagents.md +384 -0
- package/dist/commands/assets.d.ts +0 -2
- package/dist/commands/assets.js +0 -189
- package/dist/commands/debug.d.ts +0 -2
- package/dist/commands/debug.js +0 -127
- package/dist/commands/doctor.test.d.ts +0 -1
- package/dist/commands/doctor.test.js +0 -29
- package/dist/commands/project.d.ts +0 -2
- package/dist/commands/project.js +0 -170
- package/dist/commands/scene.d.ts +0 -2
- package/dist/commands/scene.js +0 -290
- package/dist/lib/agent-config.test.d.ts +0 -1
- package/dist/lib/agent-config.test.js +0 -80
- package/dist/lib/format.test.d.ts +0 -1
- package/dist/lib/format.test.js +0 -36
- package/dist/lib/plugin-manifests.test.d.ts +0 -1
- package/dist/lib/plugin-manifests.test.js +0 -134
- package/dist/lib/with-engine.d.ts +0 -14
- package/dist/lib/with-engine.js +0 -109
- package/skills/_shared/collaborative-protocol.md +0 -50
- package/skills/_shared/gd-style.md +0 -121
- package/skills/_shared/godot-version.md +0 -32
- package/skills/_shared/mcp-tools-reference.md +0 -133
- package/skills/_shared/summer-folder.md +0 -44
- package/skills/_shared/template-registry.md +0 -40
- package/skills/_tests/runner.md +0 -90
- package/skills/_tests/specs/3d-lighting.md +0 -19
- package/skills/_tests/specs/art-direction.md +0 -115
- package/skills/_tests/specs/asset-strategy.md +0 -19
- package/skills/_tests/specs/audio-direction.md +0 -123
- package/skills/_tests/specs/brainstorm-game.md +0 -99
- package/skills/_tests/specs/debug.md +0 -94
- package/skills/_tests/specs/design-level.md +0 -113
- package/skills/_tests/specs/design-mechanic.md +0 -112
- package/skills/_tests/specs/design-npc.md +0 -143
- package/skills/_tests/specs/export-and-ship.md +0 -114
- package/skills/_tests/specs/fps-controller.md +0 -113
- package/skills/_tests/specs/gdscript-patterns.md +0 -19
- package/skills/_tests/specs/host-authoritative-state.md +0 -104
- package/skills/_tests/specs/make-game.md +0 -19
- package/skills/_tests/specs/peer-to-peer-multiplayer.md +0 -99
- package/skills/_tests/specs/play.md +0 -88
- package/skills/_tests/specs/scene-composition.md +0 -19
- package/skills/_tests/specs/setup-multiplayer.md +0 -106
- package/skills/_tests/specs/tune-performance.md +0 -104
- package/skills/_tests/specs/ui-basics.md +0 -19
- package/skills/_tests/specs/vfx.md +0 -116
- package/skills/catalog.yaml +0 -991
|
@@ -39,22 +39,52 @@ Use after summer_get_diagnostics indicates console issues.`, {
|
|
|
39
39
|
return result;
|
|
40
40
|
}));
|
|
41
41
|
server.tool("summer_clear_console", "Clear the editor's Output panel. Useful before running the game to get a clean slate for error checking.", {}, async () => withEngine(async (client) => client.executeOps([{ op: "ClearConsoleOutput" }])));
|
|
42
|
-
server.tool("summer_get_debugger_errors", `Read runtime errors
|
|
42
|
+
server.tool("summer_get_debugger_errors", `Read runtime errors from the debugger. These occur while the game is running (null references, missing nodes, physics errors). Different from console output — these come from the debugger, not print statements.
|
|
43
|
+
|
|
44
|
+
For warning text, use summer_get_debugger_warnings (separate tool — engine returns warning count here but not the bodies).
|
|
43
45
|
|
|
44
46
|
Output is deduped: identical errors firing every frame collapse into one entry with a "(×N)" count suffix. A "_filter" summary tells you exactly what was collapsed or truncated. Use raw=true to bypass shaping when you really need every entry.`, {
|
|
45
47
|
max_errors: z.number().optional().default(50).describe("Max errors to return after dedupe"),
|
|
46
48
|
include_stack: z.boolean().optional().describe("Include stack traces for each error"),
|
|
49
|
+
include_warnings: z.boolean().optional().default(false).describe("Forward-compat flag — when engine supports it, returns warning bodies in the same call. Today the engine ignores it; use summer_get_debugger_warnings instead."),
|
|
47
50
|
raw: z.boolean().optional().default(false).describe("Bypass dedupe — return engine output verbatim"),
|
|
48
|
-
}, async ({ max_errors, include_stack, raw }) => withEngine(async (client) => {
|
|
51
|
+
}, async ({ max_errors, include_stack, include_warnings, raw }) => withEngine(async (client) => {
|
|
49
52
|
const op = { op: "GetDebuggerErrors", max_errors };
|
|
50
53
|
if (include_stack !== undefined)
|
|
51
54
|
op.include_stack = include_stack;
|
|
55
|
+
if (include_warnings)
|
|
56
|
+
op.include_warnings = true;
|
|
52
57
|
const engineResult = await client.executeOps([op]);
|
|
53
58
|
if (raw)
|
|
54
59
|
return engineResult;
|
|
55
60
|
const { result } = shapeEngineLogResponse(engineResult, { maxEntries: max_errors });
|
|
56
61
|
return result;
|
|
57
62
|
}));
|
|
63
|
+
server.tool("summer_get_debugger_warnings", `Read runtime warnings from the debugger panel. Warnings are non-fatal issues the game flags during play: missing optional resources, dead signal connections, deprecated API use, large allocations, physics warnings, etc.
|
|
64
|
+
|
|
65
|
+
Returns structured entries with file/line/function/error_descr/callstack — same shape as summer_get_debugger_errors but filtered to severity = "warning". Engine-internal warnings without a source file are filtered out as noise.
|
|
66
|
+
|
|
67
|
+
Use this when summer_get_diagnostics shows a non-zero \`debugger.warnings\` count and you want to see what they actually say.`, {
|
|
68
|
+
max_warnings: z.number().optional().default(50).describe("Max warnings to return after dedupe"),
|
|
69
|
+
include_stack: z.boolean().optional().default(true).describe("Include stack traces"),
|
|
70
|
+
raw: z.boolean().optional().default(false).describe("Bypass dedupe"),
|
|
71
|
+
}, async ({ max_warnings, include_stack, raw }) => withEngine(async (client) => {
|
|
72
|
+
// Reuses the existing GetDebuggerErrors op with type: "warning" filter.
|
|
73
|
+
// Engine-side change: ScriptEditorDebugger::get_errors_data() supports
|
|
74
|
+
// include_warnings; debug_ops::get_debugger_errors honours `type` to
|
|
75
|
+
// return warnings only. Diagnostics also exposes `warnings_data`.
|
|
76
|
+
const op = {
|
|
77
|
+
op: "GetDebuggerErrors",
|
|
78
|
+
max_errors: max_warnings,
|
|
79
|
+
type: "warning",
|
|
80
|
+
include_stack,
|
|
81
|
+
};
|
|
82
|
+
const engineResult = await client.executeOps([op]);
|
|
83
|
+
if (raw)
|
|
84
|
+
return engineResult;
|
|
85
|
+
const { result } = shapeEngineLogResponse(engineResult, { maxEntries: max_warnings });
|
|
86
|
+
return result;
|
|
87
|
+
}));
|
|
58
88
|
server.tool("summer_play", `Start running the game in the engine. The game runs inside Summer Engine's viewport.
|
|
59
89
|
|
|
60
90
|
After starting, use summer_get_diagnostics to check for runtime errors.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { getProjectMemorySummary } from "../../lib/project-memory.js";
|
|
2
3
|
import { withEngine } from "./with-engine.js";
|
|
3
4
|
function asRecord(value) {
|
|
4
5
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
@@ -35,9 +36,10 @@ function getProjectSetting(projectState, keys) {
|
|
|
35
36
|
}
|
|
36
37
|
return null;
|
|
37
38
|
}
|
|
38
|
-
function getProjectPath(projectState) {
|
|
39
|
+
function getProjectPath(projectState, health) {
|
|
39
40
|
const root = asRecord(projectState);
|
|
40
41
|
const data = getProjectData(projectState);
|
|
42
|
+
const healthRoot = asRecord(health);
|
|
41
43
|
return (pickString(data, [
|
|
42
44
|
"projectPath",
|
|
43
45
|
"project_path",
|
|
@@ -47,13 +49,16 @@ function getProjectPath(projectState) {
|
|
|
47
49
|
"root_path",
|
|
48
50
|
]) ??
|
|
49
51
|
pickString(root, ["projectPath", "project_path", "projectRoot", "project_root"]) ??
|
|
52
|
+
pickString(healthRoot, ["project_path", "projectPath", "projectRoot", "project_root"]) ??
|
|
50
53
|
null);
|
|
51
54
|
}
|
|
52
|
-
function getProjectName(projectState) {
|
|
55
|
+
function getProjectName(projectState, health) {
|
|
53
56
|
const root = asRecord(projectState);
|
|
54
57
|
const data = getProjectData(projectState);
|
|
58
|
+
const healthRoot = asRecord(health);
|
|
55
59
|
return (pickString(data, ["projectName", "project_name", "name"]) ??
|
|
56
60
|
pickString(root, ["projectName", "project_name", "name"]) ??
|
|
61
|
+
pickString(healthRoot, ["project_name", "projectName", "name"]) ??
|
|
57
62
|
getProjectSetting(projectState, ["application/config/name", "config/name"]));
|
|
58
63
|
}
|
|
59
64
|
function getMainScene(projectState) {
|
|
@@ -66,17 +71,19 @@ function getMainScene(projectState) {
|
|
|
66
71
|
"run/main_scene",
|
|
67
72
|
]));
|
|
68
73
|
}
|
|
69
|
-
function getCurrentScene(projectState, sceneState) {
|
|
74
|
+
function getCurrentScene(projectState, sceneState, health) {
|
|
70
75
|
const sceneRoot = asRecord(sceneState);
|
|
71
76
|
const sceneData = asRecord(sceneRoot?.data);
|
|
72
77
|
const sceneProvenance = asRecord(sceneRoot?.provenance);
|
|
73
78
|
const projectRoot = asRecord(projectState);
|
|
74
79
|
const projectData = getProjectData(projectState);
|
|
75
80
|
const projectProvenance = asRecord(projectRoot?.provenance);
|
|
81
|
+
const healthRoot = asRecord(health);
|
|
76
82
|
return (pickString(sceneProvenance, ["scenePath", "scene_path"]) ??
|
|
77
83
|
pickString(sceneData, ["scenePath", "scene_path", "currentScene", "current_scene"]) ??
|
|
78
84
|
pickString(projectData, ["currentScene", "current_scene", "scenePath", "scene_path"]) ??
|
|
79
85
|
pickString(projectProvenance, ["scenePath", "scene_path"]) ??
|
|
86
|
+
pickString(healthRoot, ["scene", "scenePath", "scene_path"]) ??
|
|
80
87
|
null);
|
|
81
88
|
}
|
|
82
89
|
export function registerProjectTools(server) {
|
|
@@ -93,6 +100,7 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
93
100
|
"Write .gd/.cs/.json/docs/simple config with host file-edit tools directly.",
|
|
94
101
|
"Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, screenshots/verification.",
|
|
95
102
|
"Call summer_get_project_context first.",
|
|
103
|
+
"Use projectMemory from summer_get_project_context to decide which .summer Markdown files to read before creative/audio/dialogue/level/character work.",
|
|
96
104
|
"If no scene is open, call summer_open_main_scene.",
|
|
97
105
|
"Call summer_get_scene_tree before structural edits.",
|
|
98
106
|
"Call summer_save_scene after edits.",
|
|
@@ -102,9 +110,11 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
102
110
|
"Do not route ordinary script, JSON, docs, or simple config edits through MCP when direct file tools are available.",
|
|
103
111
|
"Never remove multiple top-level nodes unless user explicitly requests destructive edits.",
|
|
104
112
|
"Use MCP for scene-tree mutations and inspector-visible values instead of hand-editing live scene state.",
|
|
113
|
+
"Never change priority: locked .summer memory, voice IDs, canon, or provider bindings without explicit user confirmation.",
|
|
105
114
|
],
|
|
106
115
|
preferredFlow: [
|
|
107
116
|
"summer_get_project_context",
|
|
117
|
+
"read relevant .summer memory with host file tools when projectMemory shows matching files",
|
|
108
118
|
"summer_open_main_scene (if needed)",
|
|
109
119
|
"summer_get_scene_tree",
|
|
110
120
|
"edit via summer_add_node / summer_set_prop / summer_set_resource_property",
|
|
@@ -126,6 +136,7 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
126
136
|
- project name and project path when exposed by project state
|
|
127
137
|
- current scene path (if available)
|
|
128
138
|
- main scene path from project settings
|
|
139
|
+
- lightweight .summer project memory summary when project path is available
|
|
129
140
|
|
|
130
141
|
Use this first in every fresh chat to avoid guessing scene filenames or editing the wrong scene.`, {}, async () => withEngine(async (client) => {
|
|
131
142
|
const [health, projectState, sceneState] = await Promise.all([
|
|
@@ -136,10 +147,10 @@ Use this first in every fresh chat to avoid guessing scene filenames or editing
|
|
|
136
147
|
error: err instanceof Error ? err.message : String(err),
|
|
137
148
|
})),
|
|
138
149
|
]);
|
|
139
|
-
const projectPath = getProjectPath(projectState);
|
|
140
|
-
const projectName = getProjectName(projectState);
|
|
150
|
+
const projectPath = getProjectPath(projectState, health);
|
|
151
|
+
const projectName = getProjectName(projectState, health);
|
|
141
152
|
const mainScene = getMainScene(projectState);
|
|
142
|
-
const currentScene = getCurrentScene(projectState, sceneState);
|
|
153
|
+
const currentScene = getCurrentScene(projectState, sceneState, health);
|
|
143
154
|
return {
|
|
144
155
|
health,
|
|
145
156
|
project: projectState,
|
|
@@ -148,6 +159,7 @@ Use this first in every fresh chat to avoid guessing scene filenames or editing
|
|
|
148
159
|
projectPath,
|
|
149
160
|
currentScene,
|
|
150
161
|
mainScene,
|
|
162
|
+
projectMemory: getProjectMemorySummary(projectPath),
|
|
151
163
|
guidance: mainScene
|
|
152
164
|
? "Use `summer_open_scene` with `mainScene` if no scene is open."
|
|
153
165
|
: "Main scene not found in project state. Open a known scene path explicitly.",
|
|
@@ -6,5 +6,19 @@ type ToolResult = {
|
|
|
6
6
|
}[];
|
|
7
7
|
isError?: boolean;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Decide whether an engine result envelope represents a FAILURE, and if so
|
|
11
|
+
* return a model-visible message. Returns null only for genuine success.
|
|
12
|
+
*
|
|
13
|
+
* Guards the two web bug classes (publicsummerengine cf17134f + contract.ts
|
|
14
|
+
* `isFailureSignal`):
|
|
15
|
+
* - a failure `terminalState` (anything other than applied/no_op) is a failure
|
|
16
|
+
* even when results[] is absent — the cf17134f "no-results envelope looked
|
|
17
|
+
* applied" masking. The poll loop surfaces timed_out/etc. here.
|
|
18
|
+
* - an explicit ok:false / status:"error" / failed op inside results[].
|
|
19
|
+
*
|
|
20
|
+
* Exported for unit tests.
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractOpError(result: unknown): string | null;
|
|
9
23
|
export declare function withEngine<T>(fn: (client: Awaited<ReturnType<typeof getClient>>) => Promise<T>): Promise<ToolResult>;
|
|
10
24
|
export {};
|
|
@@ -1,15 +1,60 @@
|
|
|
1
1
|
import { getClient, resetClient } from "../server.js";
|
|
2
2
|
import { recordMcpSession } from "../../lib/telemetry.js";
|
|
3
|
-
|
|
3
|
+
// 0.5.34 Block E contract (publicsummerengine src/lib/tools/contract.ts §0.1).
|
|
4
|
+
// The async lifecycle (async-op-lifecycle.ts pollOpToTerminal) merges
|
|
5
|
+
// terminalState/errorClass onto the apply dict it returns. ONLY these two are
|
|
6
|
+
// "applied something / applied nothing-on-purpose" — every other terminal state
|
|
7
|
+
// means the op did NOT land and must be surfaced as a failure, not masked.
|
|
8
|
+
const SUCCESS_TERMINAL_STATES = new Set(["applied", "no_op"]);
|
|
9
|
+
// Human-readable fallback when the engine reports a failure terminalState but no
|
|
10
|
+
// `error` string (queue-full / lease-reject / identity-mismatch / no-progress
|
|
11
|
+
// timeout frequently arrive with terminalState set and results[] absent).
|
|
12
|
+
const TERMINAL_STATE_MESSAGES = {
|
|
13
|
+
timed_out: "Engine operation timed out (terminalState: timed_out). Nothing was applied.",
|
|
14
|
+
not_connected: "Summer Engine is not connected (terminalState: not_connected). Nothing was applied.",
|
|
15
|
+
identity_mismatch: "Operation rejected — wrong project/instance (terminalState: identity_mismatch). Nothing was mutated.",
|
|
16
|
+
content_mismatch: "Operation rejected — content changed since last read (terminalState: content_mismatch). Nothing was applied.",
|
|
17
|
+
denied: "Operation denied (terminalState: denied). Nothing was applied.",
|
|
18
|
+
canceled: "Operation canceled (terminalState: canceled). Nothing was applied.",
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Decide whether an engine result envelope represents a FAILURE, and if so
|
|
22
|
+
* return a model-visible message. Returns null only for genuine success.
|
|
23
|
+
*
|
|
24
|
+
* Guards the two web bug classes (publicsummerengine cf17134f + contract.ts
|
|
25
|
+
* `isFailureSignal`):
|
|
26
|
+
* - a failure `terminalState` (anything other than applied/no_op) is a failure
|
|
27
|
+
* even when results[] is absent — the cf17134f "no-results envelope looked
|
|
28
|
+
* applied" masking. The poll loop surfaces timed_out/etc. here.
|
|
29
|
+
* - an explicit ok:false / status:"error" / failed op inside results[].
|
|
30
|
+
*
|
|
31
|
+
* Exported for unit tests.
|
|
32
|
+
*/
|
|
33
|
+
export function extractOpError(result) {
|
|
4
34
|
if (!result || typeof result !== "object")
|
|
5
35
|
return null;
|
|
6
36
|
const op = result;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
37
|
+
// Failure terminalState takes precedence — it is set by the async lifecycle
|
|
38
|
+
// exactly when the op did not land (timeout, backpressure, lease/identity
|
|
39
|
+
// rejection, cancellation), often with NO results[] to inspect.
|
|
40
|
+
const ts = op.terminalState;
|
|
41
|
+
if (typeof ts === "string" && ts.length > 0 && !SUCCESS_TERMINAL_STATES.has(ts)) {
|
|
42
|
+
if (typeof op.error === "string" && op.error.length > 0)
|
|
43
|
+
return op.error;
|
|
44
|
+
return TERMINAL_STATE_MESSAGES[ts] ?? `Engine operation failed (terminalState: ${ts}).`;
|
|
45
|
+
}
|
|
46
|
+
// An explicit ok:false / status:"error" is a failure even when the engine
|
|
47
|
+
// omitted an error string — surface it rather than mask it (matches the web
|
|
48
|
+
// contract `isFailureSignal`).
|
|
49
|
+
if (op.ok === false)
|
|
50
|
+
return op.error || "Engine operation failed (ok: false).";
|
|
51
|
+
if (op.status === "error")
|
|
52
|
+
return op.error || "Engine operation failed (status: error).";
|
|
53
|
+
const firstFailed = op.results?.find((r) => r.ok === false);
|
|
54
|
+
if (firstFailed) {
|
|
55
|
+
return firstFailed.error || `Engine op failed${firstFailed.op ? ` (${firstFailed.op})` : ""}.`;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
13
58
|
}
|
|
14
59
|
function buildActionHint(message) {
|
|
15
60
|
const normalized = message.toLowerCase();
|
package/gemini-extension.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "summer",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "Superpowers for AI game dev. MCP bridge to the local Summer Engine plus a context primer that teaches Gemini the
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"description": "Superpowers for AI game dev. MCP bridge to the local Summer Engine plus a context primer that teaches Gemini the 60 Summer skills. Use with the npm CLI for skill files on disk.",
|
|
5
5
|
"contextFileName": "GEMINI.md",
|
|
6
6
|
"mcpServers": {
|
|
7
7
|
"summer-engine": {
|
|
File without changes
|
package/hooks/run-hook.cmd
CHANGED
|
File without changes
|
package/hooks/session-start.sh
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "summer-engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Summer — superpowers for AI game dev. CLI, skills, and MCP tools for Summer Engine.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game-engine",
|
|
@@ -14,18 +14,19 @@
|
|
|
14
14
|
"homepage": "https://summerengine.com",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "https://github.com/SummerEngine/summer"
|
|
17
|
+
"url": "git+https://github.com/SummerEngine/summer.git"
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"author": "Summer Engine <founders@summerengine.com>",
|
|
21
21
|
"type": "module",
|
|
22
22
|
"main": ".opencode/plugins/summer.js",
|
|
23
23
|
"bin": {
|
|
24
|
-
"summer": "
|
|
24
|
+
"summer": "dist/bin/summer.js"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist/",
|
|
28
28
|
"skills/",
|
|
29
|
+
"commands/",
|
|
29
30
|
"hooks/",
|
|
30
31
|
"assets/",
|
|
31
32
|
"_persona/",
|
|
@@ -44,7 +45,8 @@
|
|
|
44
45
|
"CLAUDE.md"
|
|
45
46
|
],
|
|
46
47
|
"scripts": {
|
|
47
|
-
"
|
|
48
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
49
|
+
"build": "npm run clean && tsc",
|
|
48
50
|
"dev": "tsc --watch",
|
|
49
51
|
"summer": "node dist/bin/summer.js",
|
|
50
52
|
"test": "vitest run",
|
|
@@ -193,4 +193,4 @@ After the portrait is wired:
|
|
|
193
193
|
- `summer:2d-assets/concept-art` — explore the look first if not yet locked.
|
|
194
194
|
- `summer:asset-pipeline/asset-strategy` — meta-router and the 3D pipeline.
|
|
195
195
|
- `summer:scene-composition` — wiring the portrait into a dialogue UI scene.
|
|
196
|
-
- `
|
|
196
|
+
- `references/mcp-tools-reference.md` — `summer_generate_image` parameter schema.
|
|
@@ -149,4 +149,4 @@ Once the user picks a direction:
|
|
|
149
149
|
|
|
150
150
|
- `summer:asset-pipeline/asset-strategy` — meta-router that delegates here vs other 2d-assets skills.
|
|
151
151
|
- `summer:2d-assets/character-portrait` — the polished single-image counterpart.
|
|
152
|
-
- `
|
|
152
|
+
- `references/mcp-tools-reference.md` — `summer_generate_image` parameter schema.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-asset-sheet
|
|
3
|
+
description: Use when the user wants to generate a pack of 2D game assets from a single prompt — a tile sheet, UI kit, menu screen, character pack, or biome set. The skill plans the prompt, generates a coherent sheet, removes the background, vision-detects each item, classifies it, and saves as a single pack ArtAsset. Trigger on "asset sheet", "asset pack", "tile sheet", "tileset", "UI kit", "asset pack for", "make me a sheet of", "generate a bunch of <category> assets".
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: 2d-assets
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Grep Glob Write Edit summer_generate_image summer_search_assets summer_import_from_url
|
|
9
|
+
paths: ["assets/**", "art/**", "sprites/**"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# create-asset-sheet — Generate a coherent pack of 2D assets
|
|
13
|
+
|
|
14
|
+
This skill turns a single prompt into a categorized pack of isolated, named 2D assets ready to drop into a game. It exists because the alternative — generating each asset one-by-one, then losing visual coherence between them, then manually matting backgrounds in Photoshop — is the most-cited pain point from indie devs working with image models in 2026.
|
|
15
|
+
|
|
16
|
+
**Pipeline:**
|
|
17
|
+
|
|
18
|
+
1. **Generate a sheet** of N isolated assets in one image-gen call.
|
|
19
|
+
2. **Remove background** (fal/bria) so each asset can stand on its own.
|
|
20
|
+
3. **Detect + name + classify** each asset in one vision call (Gemini 3 Pro outputs bbox + snake_case name + category per region).
|
|
21
|
+
4. **Crop** each region with sharp.
|
|
22
|
+
5. **Optionally upscale** any small slice via fal flux-2/edit i2i, preserving aspect.
|
|
23
|
+
6. **Save** as a single pack ArtAsset with `packFiles: [{url, name, type}]` and `metadata.sliceBboxes`.
|
|
24
|
+
|
|
25
|
+
Categories the classifier outputs (used to route into downstream pipelines):
|
|
26
|
+
`character | terrain | nature | lighting | water | decorative | buildings | ui | vfx`
|
|
27
|
+
|
|
28
|
+
## When to use
|
|
29
|
+
|
|
30
|
+
- "Make me a Japanese village tileset."
|
|
31
|
+
- "Generate a sci-fi UI kit — buttons, panels, icons."
|
|
32
|
+
- "I need 12 fantasy props for a dungeon — chest, torch, skeleton, gold, etc."
|
|
33
|
+
- "Give me a character pack: 6 cute monsters."
|
|
34
|
+
- "Build a forest biome — trees, rocks, plants, ground tiles."
|
|
35
|
+
|
|
36
|
+
## When NOT to use
|
|
37
|
+
|
|
38
|
+
- The user wants a **single** asset → `summer:2d-assets/pixel-art` or `summer:2d-assets/character-portrait`.
|
|
39
|
+
- The user wants character **animation** (walk cycle, attack frames) → `summer:2d-assets/sprite-sheet`.
|
|
40
|
+
- The user wants **3D models** of a planned pack → not yet supported in one shot; loop `summer:2d-assets/concept-art` → image-to-3D per item, or use `summer:asset-pipeline/asset-strategy` to pick the right path.
|
|
41
|
+
- The user wants **terrain tiles that connect seamlessly** (auto-tiling / Wang tiles) → `summer:2d-assets/tileable-texture` is the right home for edge logic; this skill produces standalone tiles, not edge variants.
|
|
42
|
+
|
|
43
|
+
## The cleanest path: the studio wizard
|
|
44
|
+
|
|
45
|
+
The fastest, most reliable way to invoke this flow today is the **Create Asset Sheet** wizard in the Summer studio:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
https://summerengine.com/studio/create-tileset
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Direct the user there if they're working in the browser. The wizard:
|
|
52
|
+
|
|
53
|
+
- Offers a 2D / 3D mode toggle (2D is the slice flow described here; 3D is a soft pointer to `summer:2d-assets/concept-art` + per-asset image-to-3D for now)
|
|
54
|
+
- Surfaces six prompt templates (tile sheet, UI kit, menu, character pack, biome, freeform)
|
|
55
|
+
- Lets the user upload an existing sheet if they have one (skip generation)
|
|
56
|
+
- Shows variants in a grid with full-screen lightbox + arrow-key toggling
|
|
57
|
+
- Runs the full slice pipeline on click
|
|
58
|
+
- Has per-slice HD upscale + bulk "Upscale all"
|
|
59
|
+
- Saves as a pack ArtAsset visible in the user's library
|
|
60
|
+
|
|
61
|
+
If the user is in a chat / agent context and can't open the browser, fall back to the programmatic path below.
|
|
62
|
+
|
|
63
|
+
## Programmatic path (chat / agent driving the engine)
|
|
64
|
+
|
|
65
|
+
The slicing + classification endpoints are not yet exposed as `summer_*` MCP tools. Until they are, the agent can replicate the flow manually:
|
|
66
|
+
|
|
67
|
+
1. **Generate the sheet.** Call `summer_generate_image` with a prompt like:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
<theme> asset sheet: <comma-separated list of items>. Each item isolated with empty space around it, transparent or neutral background.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- Pick model by style: pixel art → `grok-imagine`, painterly / illustrative / structured grids → `gpt-image-2`.
|
|
74
|
+
- 1024x1024 is the sweet spot. Larger sheets fragment in the slicer when individual assets get below ~64px.
|
|
75
|
+
- Keep the asset list to 6–20 items per sheet. Past 20, model attention degrades and slices get muddy.
|
|
76
|
+
|
|
77
|
+
2. **Remove background.** Call `fal-ai/bria/background/remove` on the generated URL. Cost: $0.05.
|
|
78
|
+
|
|
79
|
+
3. **Detect + name + classify.** Send the transparent PNG to a vision LLM (Gemini 3 Pro is currently the strongest at bbox + category in one shot). Use this system prompt structure:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Output strict JSON array, no prose:
|
|
83
|
+
[{"name":"snake_case_name","category":"character|terrain|nature|lighting|water|decorative|buildings|ui|vfx","x":<0-100>,"y":<0-100>,"w":<0-100>,"h":<0-100>}]
|
|
84
|
+
|
|
85
|
+
Coordinates are percentages of image dimensions, top-left origin.
|
|
86
|
+
Names are 1-3 words, snake_case, specific (stone_lantern, not lamp).
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
4. **Crop** each region with sharp / PIL / equivalent. Pad bboxes by 4px to leave breathing room.
|
|
90
|
+
|
|
91
|
+
5. **Optional upscale.** For any crop where longest side < 256px, send through fal flux-2/edit i2i with prompt:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Recreate this exact <name> at high resolution. Preserve every detail, the exact pose, the exact style. Sharper edges, more detail. Transparent background. Single isolated asset, no scene.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- Scale longest side to 1024, preserve aspect ratio (FLUX requires multiples of 32).
|
|
98
|
+
- Don't blindly upscale everything — high-res slices get diminishing returns and you spend $0.04/slice.
|
|
99
|
+
|
|
100
|
+
6. **Save** as a pack record in `art_assets`: `isPack: true`, `packFiles: [{url, name, type: "image/png"}]`, `metadata.sliceBboxes`, `parentAssetId` pointing to the source sheet.
|
|
101
|
+
|
|
102
|
+
## Prompt patterns that work
|
|
103
|
+
|
|
104
|
+
**Tile sheet (terrain + props + buildings):**
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
<setting> tile sheet: <terrain tiles>, <props>, <buildings>. Each item isolated with empty space around it.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**UI kit:**
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
<theme> game UI kit: round button, square button, panel background, slider, toggle switch, progress bar, icon set, banner header.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Menu screen:**
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
<theme> main menu UI: title banner, Start Game button, Settings button, Quit button, <background style>, ornate frame, decorative element.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Character pack:**
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
<theme> character pack, single front-facing frame each: <character 1>, <character 2>, ... <character N>. Each isolated.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Biome:**
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
<biome name> biome assets: <flora>, <terrain features>, <rocks/water/sky elements>. Each item isolated.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Common failure modes
|
|
135
|
+
|
|
136
|
+
| Symptom | Cause | Fix |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| Slicer returns 1 giant bbox covering the whole sheet | Items overlap / no gaps in the source | Re-prompt with "each item fully isolated, empty space around each item". Re-generate. |
|
|
139
|
+
| Slicer misses items | Items too small in the source, classifier dropped them as noise | Generate at higher resolution (2048) or with fewer items per sheet |
|
|
140
|
+
| Two items merge into one slice | Items touching in the source | Re-prompt with more isolation. Or accept and let the user split manually in step 2's review. |
|
|
141
|
+
| Slice has a sliver of neighbor in it | Vision bbox is off by 5–15px | Expected. The slicer pads but doesn't snap-to-content yet. Live with it or upscale the slice (FLUX often cleans the edge) |
|
|
142
|
+
| Upscaled slice looks invented | Source was too small / too ambiguous | Don't upscale slices below ~32×32. Or accept the hallucination and edit |
|
|
143
|
+
| Detection returns wrong category | Model misjudged context | User can override in the review step; not a blocker |
|
|
144
|
+
|
|
145
|
+
## What this skill does NOT do (yet)
|
|
146
|
+
|
|
147
|
+
- Per-asset planning + individual generation (the Codex-style flow where each asset is generated separately at full resolution). The planner endpoint exists (`POST /api/art/plan-asset-pack`) but the per-asset gen loop is not wired into the wizard yet.
|
|
148
|
+
- 3D pack assembly. The 3D mode in the wizard is a placeholder pointer to `summer:2d-assets/concept-art` + image-to-3D for now.
|
|
149
|
+
- Character animation routing. Once a slice is classified as `character`, the next step (sprite-sheet generation) is a separate skill that the user has to invoke manually. Auto-routing is on the roadmap.
|
|
150
|
+
- Terrain auto-tiling / Wang-tile generation. Use `summer:2d-assets/tileable-texture` for that.
|
|
151
|
+
- Level composition / scene assembly. Use `summer:level-design/design-level`.
|
|
152
|
+
|
|
153
|
+
## Honest cost estimate
|
|
154
|
+
|
|
155
|
+
Per 12-asset sheet, end to end through the wizard:
|
|
156
|
+
|
|
157
|
+
- Generate sheet: $0.04–0.10 (varies by model)
|
|
158
|
+
- Background removal: $0.05
|
|
159
|
+
- Vision detect + classify: $0.02–0.03
|
|
160
|
+
- Save pack: free
|
|
161
|
+
- Optional bulk upscale: ~$0.04 per slice ($0.48 for 12)
|
|
162
|
+
|
|
163
|
+
**Without upscale:** ~$0.12. **With full upscale:** ~$0.60. About a third of what a Photoshop hour would cost.
|