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.
Files changed (176) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +20 -5
  3. package/.codex-plugin/plugin.json +15 -5
  4. package/.cursor-plugin/plugin.json +15 -5
  5. package/.factory-plugin/plugin.json +2 -2
  6. package/.opencode/INSTALL.md +1 -1
  7. package/CHANGELOG.md +27 -6
  8. package/CLAUDE.md +2 -2
  9. package/GEMINI.md +2 -3
  10. package/README.md +51 -30
  11. package/_persona/summer/SKILL.md +5 -6
  12. package/commands/gameskill.md +103 -0
  13. package/dist/bin/summer.js +12 -4
  14. package/dist/commands/create.js +2 -0
  15. package/dist/commands/doctor.js +71 -1
  16. package/dist/commands/install.d.ts +17 -0
  17. package/dist/commands/install.js +63 -4
  18. package/dist/commands/memory.d.ts +14 -0
  19. package/dist/commands/memory.js +196 -0
  20. package/dist/commands/setup.js +8 -2
  21. package/dist/commands/skills.js +90 -6
  22. package/dist/commands/status.js +3 -0
  23. package/dist/lib/agent-config.d.ts +2 -2
  24. package/dist/lib/agent-config.js +134 -7
  25. package/dist/lib/api-client.d.ts +13 -0
  26. package/dist/lib/api-client.js +63 -26
  27. package/dist/lib/async-op-lifecycle.d.ts +60 -0
  28. package/dist/lib/async-op-lifecycle.js +83 -0
  29. package/dist/lib/project-memory.d.ts +33 -0
  30. package/dist/lib/project-memory.js +220 -0
  31. package/dist/lib/skills-registry.d.ts +134 -61
  32. package/dist/lib/skills-registry.js +102 -4
  33. package/dist/lib/version-check.d.ts +106 -0
  34. package/dist/lib/version-check.js +338 -0
  35. package/dist/mcp/server.d.ts +2 -0
  36. package/dist/mcp/server.js +81 -0
  37. package/dist/mcp/tools/debug-tools.js +32 -2
  38. package/dist/mcp/tools/project-tools.js +18 -6
  39. package/dist/mcp/tools/with-engine.d.ts +14 -0
  40. package/dist/mcp/tools/with-engine.js +52 -7
  41. package/gemini-extension.json +2 -2
  42. package/hooks/pre-commit-doctor.sh +0 -0
  43. package/hooks/run-hook.cmd +0 -0
  44. package/hooks/session-start.sh +0 -0
  45. package/package.json +6 -4
  46. package/skills/2d-assets/character-portrait/SKILL.md +1 -1
  47. package/skills/2d-assets/concept-art/SKILL.md +1 -1
  48. package/skills/2d-assets/create-asset-sheet/SKILL.md +163 -0
  49. package/skills/2d-assets/instantiate-asset-pack/SKILL.md +245 -0
  50. package/skills/2d-assets/pixel-art/SKILL.md +1 -1
  51. package/skills/2d-assets/skybox-panorama/SKILL.md +1 -1
  52. package/skills/2d-assets/sprite-sheet/SKILL.md +1 -1
  53. package/skills/2d-assets/tileable-texture/SKILL.md +1 -1
  54. package/skills/2d-assets/ui-graphics/SKILL.md +1 -1
  55. package/skills/2d-assets/use-widget-asset/SKILL.md +217 -0
  56. package/skills/3d-assets/character-model/SKILL.md +1 -1
  57. package/skills/3d-assets/organic-model/SKILL.md +1 -1
  58. package/skills/3d-assets/prop-model/SKILL.md +1 -1
  59. package/skills/3d-assets/vehicle-model/SKILL.md +1 -1
  60. package/skills/README.md +37 -30
  61. package/skills/ai-and-npcs/design-npc/SKILL.md +6 -6
  62. package/skills/animation/animation-tree/SKILL.md +2 -2
  63. package/skills/animation/facial-and-lipsync/SKILL.md +1 -1
  64. package/skills/animation/generate-motion/SKILL.md +1 -1
  65. package/skills/animation/procedural-animation/SKILL.md +2 -2
  66. package/skills/audio/adaptive-music/SKILL.md +1 -1
  67. package/skills/audio/audio-direction/SKILL.md +5 -5
  68. package/skills/audio/sound-effect/SKILL.md +2 -2
  69. package/skills/audio/voice-line/SKILL.md +25 -3
  70. package/skills/character-controllers/fps-controller/SKILL.md +5 -5
  71. package/skills/debugging/debug/SKILL.md +39 -1
  72. package/skills/deployment/export-and-ship/SKILL.md +4 -4
  73. package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +170 -0
  74. package/skills/gameplay-mechanics/design-mechanic/SKILL.md +8 -8
  75. package/skills/level-design/design-level/SKILL.md +5 -5
  76. package/skills/level-design/scene-to-level/SKILL.md +125 -0
  77. package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +4 -4
  78. package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +4 -4
  79. package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +7 -7
  80. package/skills/performance/tune-performance/SKILL.md +5 -5
  81. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -1
  82. package/skills/rendering-and-lighting/art-direction/SKILL.md +5 -5
  83. package/skills/scene-and-project/brainstorm-game/SKILL.md +4 -4
  84. package/skills/scene-and-project/play/SKILL.md +1 -1
  85. package/skills/scene-and-project/scene-composition/SKILL.md +30 -1
  86. package/skills/scripting-patterns/gdscript-patterns/SKILL.md +53 -0
  87. package/skills/ui-and-ux/ui-basics/SKILL.md +1 -1
  88. package/skills/video/animated-loop/SKILL.md +1 -1
  89. package/skills/video/cinematic-cutscene/SKILL.md +1 -1
  90. package/skills/video/trailer-shot/SKILL.md +1 -1
  91. package/skills/visual-effects/game-feel/SKILL.md +6 -6
  92. package/skills/workflow/brainstorming/SKILL.md +164 -0
  93. package/skills/workflow/brainstorming/scripts/frame-template.html +214 -0
  94. package/skills/workflow/brainstorming/scripts/helper.js +88 -0
  95. package/skills/workflow/brainstorming/scripts/server.cjs +354 -0
  96. package/skills/workflow/brainstorming/scripts/start-server.sh +148 -0
  97. package/skills/workflow/brainstorming/scripts/stop-server.sh +56 -0
  98. package/skills/workflow/brainstorming/spec-document-reviewer-prompt.md +49 -0
  99. package/skills/workflow/brainstorming/visual-companion.md +287 -0
  100. package/skills/workflow/debugging-game-feel/SKILL.md +174 -0
  101. package/skills/workflow/diagnosing-perf-regressions/SKILL.md +190 -0
  102. package/skills/workflow/dispatching-parallel-agents/SKILL.md +182 -0
  103. package/skills/workflow/gameskill/SKILL.md +111 -0
  104. package/skills/workflow/investigating-bugs/CREATION-LOG.md +119 -0
  105. package/skills/workflow/investigating-bugs/SKILL.md +296 -0
  106. package/skills/workflow/investigating-bugs/condition-based-waiting-example.ts +158 -0
  107. package/skills/workflow/investigating-bugs/condition-based-waiting.md +115 -0
  108. package/skills/workflow/investigating-bugs/defense-in-depth.md +122 -0
  109. package/skills/workflow/investigating-bugs/find-polluter.sh +63 -0
  110. package/skills/workflow/investigating-bugs/root-cause-tracing.md +169 -0
  111. package/skills/workflow/investigating-bugs/test-academic.md +14 -0
  112. package/skills/workflow/investigating-bugs/test-pressure-1.md +58 -0
  113. package/skills/workflow/investigating-bugs/test-pressure-2.md +68 -0
  114. package/skills/workflow/investigating-bugs/test-pressure-3.md +69 -0
  115. package/skills/workflow/playtesting-a-feature/SKILL.md +173 -0
  116. package/skills/{_meta → workflow}/skill-create/SKILL.md +32 -22
  117. package/skills/{_meta → workflow}/skill-improve/SKILL.md +9 -10
  118. package/skills/{_meta → workflow}/skill-test/SKILL.md +12 -12
  119. package/skills/{_meta → workflow}/using-summer/SKILL.md +5 -2
  120. package/skills/workflow/verification-before-completion/SKILL.md +139 -0
  121. package/skills/workflow/writing-plans/SKILL.md +152 -0
  122. package/skills/workflow/writing-plans/plan-document-reviewer-prompt.md +49 -0
  123. package/skills/workflow/writing-skills/SKILL.md +655 -0
  124. package/skills/workflow/writing-skills/anthropic-best-practices.md +1150 -0
  125. package/skills/workflow/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  126. package/skills/workflow/writing-skills/graphviz-conventions.dot +172 -0
  127. package/skills/workflow/writing-skills/persuasion-principles.md +187 -0
  128. package/skills/workflow/writing-skills/render-graphs.js +168 -0
  129. package/skills/workflow/writing-skills/testing-skills-with-subagents.md +384 -0
  130. package/dist/commands/assets.d.ts +0 -2
  131. package/dist/commands/assets.js +0 -189
  132. package/dist/commands/debug.d.ts +0 -2
  133. package/dist/commands/debug.js +0 -127
  134. package/dist/commands/doctor.test.d.ts +0 -1
  135. package/dist/commands/doctor.test.js +0 -29
  136. package/dist/commands/project.d.ts +0 -2
  137. package/dist/commands/project.js +0 -170
  138. package/dist/commands/scene.d.ts +0 -2
  139. package/dist/commands/scene.js +0 -290
  140. package/dist/lib/agent-config.test.d.ts +0 -1
  141. package/dist/lib/agent-config.test.js +0 -80
  142. package/dist/lib/format.test.d.ts +0 -1
  143. package/dist/lib/format.test.js +0 -36
  144. package/dist/lib/plugin-manifests.test.d.ts +0 -1
  145. package/dist/lib/plugin-manifests.test.js +0 -134
  146. package/dist/lib/with-engine.d.ts +0 -14
  147. package/dist/lib/with-engine.js +0 -109
  148. package/skills/_shared/collaborative-protocol.md +0 -50
  149. package/skills/_shared/gd-style.md +0 -121
  150. package/skills/_shared/godot-version.md +0 -32
  151. package/skills/_shared/mcp-tools-reference.md +0 -133
  152. package/skills/_shared/summer-folder.md +0 -44
  153. package/skills/_shared/template-registry.md +0 -40
  154. package/skills/_tests/runner.md +0 -90
  155. package/skills/_tests/specs/3d-lighting.md +0 -19
  156. package/skills/_tests/specs/art-direction.md +0 -115
  157. package/skills/_tests/specs/asset-strategy.md +0 -19
  158. package/skills/_tests/specs/audio-direction.md +0 -123
  159. package/skills/_tests/specs/brainstorm-game.md +0 -99
  160. package/skills/_tests/specs/debug.md +0 -94
  161. package/skills/_tests/specs/design-level.md +0 -113
  162. package/skills/_tests/specs/design-mechanic.md +0 -112
  163. package/skills/_tests/specs/design-npc.md +0 -143
  164. package/skills/_tests/specs/export-and-ship.md +0 -114
  165. package/skills/_tests/specs/fps-controller.md +0 -113
  166. package/skills/_tests/specs/gdscript-patterns.md +0 -19
  167. package/skills/_tests/specs/host-authoritative-state.md +0 -104
  168. package/skills/_tests/specs/make-game.md +0 -19
  169. package/skills/_tests/specs/peer-to-peer-multiplayer.md +0 -99
  170. package/skills/_tests/specs/play.md +0 -88
  171. package/skills/_tests/specs/scene-composition.md +0 -19
  172. package/skills/_tests/specs/setup-multiplayer.md +0 -106
  173. package/skills/_tests/specs/tune-performance.md +0 -104
  174. package/skills/_tests/specs/ui-basics.md +0 -19
  175. package/skills/_tests/specs/vfx.md +0 -116
  176. package/skills/catalog.yaml +0 -991
@@ -1,189 +0,0 @@
1
- import { Command } from "commander";
2
- import { getAuthToken } from "../lib/auth.js";
3
- import { getClient } from "../lib/with-engine.js";
4
- import { outputResult, outputError, isJsonMode } from "../lib/format.js";
5
- const GATEWAY_URL = process.env.SUMMER_GATEWAY_URL || "https://www.summerengine.com";
6
- const KENNEY_URL_PATTERN = /\/kenney\/3d\/([^/]+)\//;
7
- function getPackSlugFromUrl(fileUrl) {
8
- return fileUrl.match(KENNEY_URL_PATTERN)?.[1] ?? null;
9
- }
10
- function buildKenneyTextureUrl(fileUrl) {
11
- const lastSlash = fileUrl.lastIndexOf("/");
12
- const base = lastSlash >= 0 ? fileUrl.slice(0, lastSlash) : fileUrl;
13
- return `${base}/Textures/colormap.png`;
14
- }
15
- async function textureExists(url) {
16
- try {
17
- const res = await fetch(url, {
18
- method: "HEAD",
19
- signal: AbortSignal.timeout(5000),
20
- });
21
- return res.ok;
22
- }
23
- catch {
24
- return false;
25
- }
26
- }
27
- async function buildKenneyImportEntries(fileUrl, packSlug, fileName) {
28
- const textureUrl = buildKenneyTextureUrl(fileUrl);
29
- const hasTexture = await textureExists(textureUrl);
30
- if (!hasTexture) {
31
- return [
32
- {
33
- url: fileUrl,
34
- path: `res://assets/models/kenney/${packSlug}/${fileName}`,
35
- },
36
- ];
37
- }
38
- const glbPath = `res://assets/models/kenney/${packSlug}/${fileName}`;
39
- const glbDir = glbPath.replace(/\/[^/]+$/, "");
40
- const texturePath = `${glbDir}/Textures/colormap.png`;
41
- return [
42
- { url: textureUrl, path: texturePath },
43
- { url: fileUrl, path: glbPath },
44
- ];
45
- }
46
- async function searchAssetsApi(params) {
47
- const token = await getAuthToken();
48
- if (!token) {
49
- return {
50
- error: "Not logged in",
51
- message: "Not signed in. Run: summer login\nOr open: https://www.summerengine.com/login\nAsset search requires a Summer Engine account (free to create).",
52
- };
53
- }
54
- const searchParams = new URLSearchParams();
55
- searchParams.set("query", params.query);
56
- if (params.assetType && params.assetType !== "all") {
57
- searchParams.set("assetType", params.assetType);
58
- }
59
- if (params.limit) {
60
- searchParams.set("limit", String(params.limit));
61
- }
62
- const res = await fetch(`${GATEWAY_URL}/api/mcp/assets?${searchParams}`, {
63
- headers: { Authorization: `Bearer ${token}` },
64
- signal: AbortSignal.timeout(15000),
65
- });
66
- const data = (await res.json());
67
- if (!res.ok) {
68
- if (res.status === 402) {
69
- return {
70
- error: "upgrade_required",
71
- message: (data.message || "Asset search requires a Pro subscription.") +
72
- "\nUpgrade at: https://www.summerengine.com/pricing",
73
- plan: data.plan,
74
- };
75
- }
76
- if (res.status === 401) {
77
- return {
78
- error: "unauthorized",
79
- message: (data.message || "Auth token expired.") +
80
- " Run: summer login --force",
81
- };
82
- }
83
- return {
84
- error: data.error || "Search failed",
85
- message: data.message || "Asset search failed",
86
- };
87
- }
88
- return data;
89
- }
90
- export const assetsCommand = new Command("assets").description("Search and import assets from the Summer Engine library");
91
- assetsCommand
92
- .command("search <query>")
93
- .description('Search the asset library (e.g. summer assets search "low-poly tree")')
94
- .option("--type <type>", "Filter: 2d_image, animation, 3d_model, audio, music, all", "all")
95
- .option("--limit <n>", "Max results (1-20)", "10")
96
- .action(async (query, opts, cmd) => {
97
- const fmt = isJsonMode(cmd);
98
- try {
99
- const result = await searchAssetsApi({
100
- query,
101
- assetType: opts.type,
102
- limit: Math.min(parseInt(opts.limit, 10), 20),
103
- });
104
- if (result.error) {
105
- outputError(result.message || result.error, fmt);
106
- }
107
- outputResult(result, fmt);
108
- }
109
- catch (err) {
110
- outputError(err instanceof Error ? err.message : String(err), fmt);
111
- }
112
- });
113
- assetsCommand
114
- .command("import <query>")
115
- .description("Search and import the best match into the project in one step")
116
- .option("--parent <path>", "Parent node to add the asset under (e.g. ./World)")
117
- .option("--type <type>", "Preferred type: 2d_image, animation, 3d_model, audio, music, all", "3d_model")
118
- .action(async (query, opts, cmd) => {
119
- const fmt = isJsonMode(cmd);
120
- try {
121
- const searchResult = await searchAssetsApi({
122
- query,
123
- assetType: opts.type,
124
- limit: 5,
125
- });
126
- if (searchResult.error) {
127
- outputError(searchResult.message || searchResult.error, fmt);
128
- }
129
- const assets = searchResult.assets || [];
130
- if (assets.length === 0) {
131
- outputError(`No assets found for "${query}". Try different keywords.`, fmt);
132
- }
133
- const best = assets[0];
134
- const fileUrl = best.fileUrl;
135
- if (!fileUrl) {
136
- outputError("Asset has no download URL.", fmt);
137
- }
138
- const fileName = fileUrl.split("/").pop()?.split("?")[0] || "asset";
139
- const packSlug = best.packSlug ?? getPackSlugFromUrl(fileUrl) ?? "misc";
140
- let imports;
141
- if (best.type === "3d_model" && fileUrl.includes("kenney/3d/")) {
142
- imports = await buildKenneyImportEntries(fileUrl, packSlug, fileName);
143
- }
144
- else {
145
- const path = best.type === "3d_model"
146
- ? `res://assets/models/${fileName}`
147
- : `res://assets/${fileName}`;
148
- imports = [{ url: fileUrl, path }];
149
- }
150
- const importPath = imports[imports.length - 1].path;
151
- const client = await getClient();
152
- const importResult = imports.length === 1
153
- ? await client.executeOps([
154
- {
155
- op: "ImportFromUrl",
156
- url: imports[0].url,
157
- path: imports[0].path,
158
- },
159
- ])
160
- : await client.executeOps([
161
- { op: "ImportFromUrlBatch", imports },
162
- ]);
163
- const ok = importResult?.results?.[0]?.ok;
164
- if (!ok) {
165
- outputError(`Import failed for "${best.title}". Check engine logs.`, fmt);
166
- }
167
- if (opts.parent && best.type === "3d_model") {
168
- await client.executeOps([
169
- {
170
- op: "InstantiateScene",
171
- parent: opts.parent,
172
- scene: importPath,
173
- name: best.title.replace(/\s+/g, "_"),
174
- },
175
- ]);
176
- }
177
- outputResult({
178
- success: true,
179
- asset: best.title,
180
- type: best.type,
181
- importedTo: importPath,
182
- addedToScene: !!opts.parent,
183
- parent: opts.parent || null,
184
- }, fmt);
185
- }
186
- catch (err) {
187
- outputError(err instanceof Error ? err.message : String(err), fmt);
188
- }
189
- });
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const debugCommand: Command;
@@ -1,127 +0,0 @@
1
- import { Command } from "commander";
2
- import { withEngineClient } from "../lib/with-engine.js";
3
- import { outputResult, outputError, isJsonMode } from "../lib/format.js";
4
- export const debugCommand = new Command("debug").description("Debug and run commands (requires running engine)");
5
- debugCommand
6
- .command("diagnostics")
7
- .description("Quick overview of all errors and warnings from editor console and debugger")
8
- .action(async (_opts, cmd) => {
9
- const fmt = isJsonMode(cmd);
10
- try {
11
- const result = await withEngineClient((c) => c.getDiagnostics());
12
- outputResult(result, fmt);
13
- }
14
- catch (err) {
15
- outputError(err instanceof Error ? err.message : String(err), fmt);
16
- }
17
- });
18
- debugCommand
19
- .command("play [scene]")
20
- .description("Start running the game (optionally a specific scene)")
21
- .action(async (scene, _opts, cmd) => {
22
- const fmt = isJsonMode(cmd);
23
- try {
24
- const result = await withEngineClient((c) => c.play(scene));
25
- outputResult(result, fmt);
26
- }
27
- catch (err) {
28
- outputError(err instanceof Error ? err.message : String(err), fmt);
29
- }
30
- });
31
- debugCommand
32
- .command("stop")
33
- .description("Stop the running game")
34
- .action(async (_opts, cmd) => {
35
- const fmt = isJsonMode(cmd);
36
- try {
37
- const result = await withEngineClient((c) => c.stop());
38
- outputResult(result, fmt);
39
- }
40
- catch (err) {
41
- outputError(err instanceof Error ? err.message : String(err), fmt);
42
- }
43
- });
44
- debugCommand
45
- .command("console")
46
- .description("Read recent messages from the editor Output panel")
47
- .option("--max-lines <n>", "Max lines to return", "100")
48
- .option("--filter <string>", "Only return lines containing this string")
49
- .option("--type <type>", "Filter by type: error, warning, std, editor")
50
- .action(async (opts, cmd) => {
51
- const fmt = isJsonMode(cmd);
52
- try {
53
- const op = {
54
- op: "GetConsoleOutput",
55
- max_lines: parseInt(opts.maxLines, 10),
56
- };
57
- if (opts.filter)
58
- op.filter = opts.filter;
59
- if (opts.type)
60
- op.type = opts.type;
61
- const result = await withEngineClient((c) => c.executeOps([op]));
62
- outputResult(result, fmt);
63
- }
64
- catch (err) {
65
- outputError(err instanceof Error ? err.message : String(err), fmt);
66
- }
67
- });
68
- debugCommand
69
- .command("errors")
70
- .description("Read runtime errors from the debugger")
71
- .option("--max-errors <n>", "Max errors to return", "50")
72
- .option("--stack", "Include stack traces")
73
- .action(async (opts, cmd) => {
74
- const fmt = isJsonMode(cmd);
75
- try {
76
- const op = {
77
- op: "GetDebuggerErrors",
78
- max_errors: parseInt(opts.maxErrors, 10),
79
- };
80
- if (opts.stack !== undefined)
81
- op.include_stack = opts.stack;
82
- const result = await withEngineClient((c) => c.executeOps([op]));
83
- outputResult(result, fmt);
84
- }
85
- catch (err) {
86
- outputError(err instanceof Error ? err.message : String(err), fmt);
87
- }
88
- });
89
- debugCommand
90
- .command("script-errors <path>")
91
- .description("Check a GDScript file for parse/compile errors (e.g. res://scripts/player.gd)")
92
- .action(async (path, _opts, cmd) => {
93
- const fmt = isJsonMode(cmd);
94
- try {
95
- const result = await withEngineClient((c) => c.getScriptErrors(path));
96
- outputResult(result, fmt);
97
- }
98
- catch (err) {
99
- outputError(err instanceof Error ? err.message : String(err), fmt);
100
- }
101
- });
102
- debugCommand
103
- .command("clear")
104
- .description("Clear the editor Output panel")
105
- .action(async (_opts, cmd) => {
106
- const fmt = isJsonMode(cmd);
107
- try {
108
- const result = await withEngineClient((c) => c.executeOps([{ op: "ClearConsoleOutput" }]));
109
- outputResult(result, fmt);
110
- }
111
- catch (err) {
112
- outputError(err instanceof Error ? err.message : String(err), fmt);
113
- }
114
- });
115
- debugCommand
116
- .command("running")
117
- .description("Check if the game is currently running")
118
- .action(async (_opts, cmd) => {
119
- const fmt = isJsonMode(cmd);
120
- try {
121
- const result = await withEngineClient((c) => c.executeOps([{ op: "IsGameRunning" }]));
122
- outputResult(result, fmt);
123
- }
124
- catch (err) {
125
- outputError(err instanceof Error ? err.message : String(err), fmt);
126
- }
127
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,29 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- function summarize(checks) {
3
- return {
4
- ok: checks.filter((c) => c.status === "ok").length,
5
- warnings: checks.filter((c) => c.status === "warning").length,
6
- failures: checks.filter((c) => c.status === "fail").length,
7
- };
8
- }
9
- describe("doctor summarization", () => {
10
- it("counts mixed statuses", () => {
11
- const summary = summarize([
12
- { id: "a", label: "A", status: "ok", message: "" },
13
- { id: "b", label: "B", status: "warning", message: "" },
14
- { id: "c", label: "C", status: "fail", message: "" },
15
- { id: "d", label: "D", status: "ok", message: "" },
16
- ]);
17
- expect(summary).toEqual({ ok: 2, warnings: 1, failures: 1 });
18
- });
19
- it("counts an all-ok run", () => {
20
- const summary = summarize([
21
- { id: "a", label: "A", status: "ok", message: "" },
22
- { id: "b", label: "B", status: "ok", message: "" },
23
- ]);
24
- expect(summary).toEqual({ ok: 2, warnings: 0, failures: 0 });
25
- });
26
- it("counts an empty check list", () => {
27
- expect(summarize([])).toEqual({ ok: 0, warnings: 0, failures: 0 });
28
- });
29
- });
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const projectCommand: Command;
@@ -1,170 +0,0 @@
1
- import { Command } from "commander";
2
- import { readFile } from "fs/promises";
3
- import { join } from "path";
4
- import { withEngineClient } from "../lib/with-engine.js";
5
- import { outputResult, outputError, parseValue, readJsonArg, isJsonMode, } from "../lib/format.js";
6
- async function readMainSceneFromProject(projectPath) {
7
- if (!projectPath)
8
- return null;
9
- try {
10
- const text = await readFile(join(projectPath, "project.godot"), "utf-8");
11
- const match = text.match(/run\/main_scene="([^"]+)"/);
12
- return match?.[1] ?? null;
13
- }
14
- catch {
15
- return null;
16
- }
17
- }
18
- export const projectCommand = new Command("project").description("Project context and settings (requires running engine)");
19
- projectCommand
20
- .command("context")
21
- .description("Get project context: health, current scene, main scene, project path")
22
- .action(async (_opts, cmd) => {
23
- const fmt = isJsonMode(cmd);
24
- try {
25
- const result = await withEngineClient(async (client) => {
26
- const [health, projectState] = await Promise.all([
27
- client.health(),
28
- client.getProjectState(),
29
- ]);
30
- const healthObj = (health ?? {});
31
- const mainScene = await readMainSceneFromProject(typeof healthObj.project_path === "string"
32
- ? healthObj.project_path
33
- : undefined);
34
- return {
35
- health,
36
- project: projectState,
37
- mainScene,
38
- guidance: mainScene
39
- ? "Use `summer scene open` with mainScene if no scene is open."
40
- : "Main scene not found in project.godot. Open a known scene path explicitly.",
41
- };
42
- });
43
- outputResult(result, fmt);
44
- }
45
- catch (err) {
46
- outputError(err instanceof Error ? err.message : String(err), fmt);
47
- }
48
- });
49
- projectCommand
50
- .command("open-main")
51
- .description("Open the project's main scene from project.godot")
52
- .action(async (_opts, cmd) => {
53
- const fmt = isJsonMode(cmd);
54
- try {
55
- const result = await withEngineClient(async (client) => {
56
- const health = (await client.health());
57
- const projectPath = typeof health.project_path === "string"
58
- ? health.project_path
59
- : undefined;
60
- const mainScene = await readMainSceneFromProject(projectPath);
61
- if (!mainScene) {
62
- throw new Error("Could not resolve main_scene from project.godot. Run `summer project context` and open a scene explicitly.");
63
- }
64
- return client.executeOps([{ op: "OpenScene", path: mainScene }]);
65
- });
66
- outputResult(result, fmt);
67
- }
68
- catch (err) {
69
- outputError(err instanceof Error ? err.message : String(err), fmt);
70
- }
71
- });
72
- projectCommand
73
- .command("setting <key> <value>")
74
- .description('Set a project setting (e.g. summer project setting application/config/name "My Game")')
75
- .action(async (key, value, _opts, cmd) => {
76
- const fmt = isJsonMode(cmd);
77
- try {
78
- const result = await withEngineClient((c) => c.executeOps([
79
- { op: "ProjectSetting", key, value: parseValue(value) },
80
- ]));
81
- outputResult(result, fmt);
82
- }
83
- catch (err) {
84
- outputError(err instanceof Error ? err.message : String(err), fmt);
85
- }
86
- });
87
- projectCommand
88
- .command("input-bind <name> <events>")
89
- .description('Bind input events to an action (e.g. summer project input-bind jump \'[{"type":"key","key":"Space"}]\')')
90
- .action(async (name, eventsJson, _opts, cmd) => {
91
- const fmt = isJsonMode(cmd);
92
- try {
93
- const events = JSON.parse(eventsJson);
94
- const result = await withEngineClient((c) => c.executeOps([
95
- { op: "InputMapAddAction", name },
96
- { op: "InputMapBind", name, events },
97
- ]));
98
- outputResult(result, fmt);
99
- }
100
- catch (err) {
101
- outputError(err instanceof Error ? err.message : String(err), fmt);
102
- }
103
- });
104
- projectCommand
105
- .command("import <url>")
106
- .description("Download a file from a URL and import into the project")
107
- .option("--path <path>", "Target path in project (auto-inferred from URL if omitted)")
108
- .action(async (url, opts, cmd) => {
109
- const fmt = isJsonMode(cmd);
110
- try {
111
- const op = { op: "ImportFromUrl", url };
112
- if (opts.path)
113
- op.path = opts.path;
114
- const result = await withEngineClient((c) => c.executeOps([op]));
115
- outputResult(result, fmt);
116
- }
117
- catch (err) {
118
- outputError(err instanceof Error ? err.message : String(err), fmt);
119
- }
120
- });
121
- projectCommand
122
- .command("import-batch <json>")
123
- .description('Import multiple files. Pass JSON array of {url, path} or "-" for stdin')
124
- .action(async (jsonArg, _opts, cmd) => {
125
- const fmt = isJsonMode(cmd);
126
- try {
127
- const imports = (await readJsonArg(jsonArg));
128
- const result = await withEngineClient((c) => c.executeOps([{ op: "ImportFromUrlBatch", imports }]));
129
- outputResult(result, fmt);
130
- }
131
- catch (err) {
132
- outputError(err instanceof Error ? err.message : String(err), fmt);
133
- }
134
- });
135
- projectCommand
136
- .command("playbook")
137
- .description("Show the AI agent operating guide for Summer Engine")
138
- .action(async (_opts, cmd) => {
139
- const fmt = isJsonMode(cmd);
140
- const playbook = {
141
- startupChecklist: [
142
- "Decide first: file edit or engine operation.",
143
- "For normal code/content edits (.gd/.tscn/.tres/.ts), use host file-edit tools directly.",
144
- "Call `summer project context` first.",
145
- "If no scene is open, call `summer project open-main`.",
146
- "Call `summer scene tree` before structural edits.",
147
- "Call `summer scene save` after edits.",
148
- ],
149
- safeDefaults: [
150
- "Never guess scene filenames (main.tscn/Main.tscn).",
151
- "Do not route ordinary code edits through engine ops when direct file tools are available.",
152
- "Never remove multiple top-level nodes unless user explicitly requests destructive edits.",
153
- "Never write .tscn/.scn with file write tools; use scene commands.",
154
- ],
155
- preferredFlow: [
156
- "summer project context",
157
- "summer project open-main (if needed)",
158
- "summer scene tree",
159
- "edit via summer scene add / summer scene prop / summer scene rprop",
160
- "summer scene save",
161
- "summer debug diagnostics",
162
- ],
163
- recovery: [
164
- "If you see 'no scene open': run summer project open-main.",
165
- "If open fails: re-check mainScene from summer project context.",
166
- "If save fails: verify scene is open and game is not running.",
167
- ],
168
- };
169
- outputResult(playbook, fmt);
170
- });
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const sceneCommand: Command;