summer-engine 2.3.0 → 2.6.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 (237) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +24 -8
  3. package/.codex-plugin/plugin.json +19 -8
  4. package/.cursor-plugin/plugin.json +18 -7
  5. package/.factory-plugin/plugin.json +3 -3
  6. package/.opencode/INSTALL.md +1 -1
  7. package/AGENTS.md +6 -4
  8. package/CHANGELOG.md +46 -7
  9. package/CLAUDE.md +2 -2
  10. package/GEMINI.md +5 -6
  11. package/README.md +116 -109
  12. package/_persona/summer/SKILL.md +5 -6
  13. package/commands/gameskill.md +103 -0
  14. package/dist/bin/summer.js +17 -4
  15. package/dist/commands/cloud.d.ts +2 -0
  16. package/dist/commands/cloud.js +80 -0
  17. package/dist/commands/create.js +2 -0
  18. package/dist/commands/doctor.js +90 -1
  19. package/dist/commands/install.d.ts +17 -0
  20. package/dist/commands/install.js +63 -4
  21. package/dist/commands/login.js +4 -1
  22. package/dist/commands/logout.js +1 -1
  23. package/dist/commands/memory.d.ts +14 -0
  24. package/dist/commands/memory.js +196 -0
  25. package/dist/commands/open.js +1 -1
  26. package/dist/commands/plan.d.ts +2 -0
  27. package/dist/commands/plan.js +60 -0
  28. package/dist/commands/run.js +132 -35
  29. package/dist/commands/setup.js +8 -2
  30. package/dist/commands/skills.js +90 -6
  31. package/dist/commands/status.js +3 -0
  32. package/dist/lib/agent-config.d.ts +2 -2
  33. package/dist/lib/agent-config.js +134 -7
  34. package/dist/lib/api-client.d.ts +13 -0
  35. package/dist/lib/api-client.js +63 -26
  36. package/dist/lib/async-op-lifecycle.d.ts +60 -0
  37. package/dist/lib/async-op-lifecycle.js +83 -0
  38. package/dist/lib/auth.d.ts +2 -0
  39. package/dist/lib/auth.js +13 -0
  40. package/dist/lib/cloud/api.d.ts +79 -0
  41. package/dist/lib/cloud/api.js +107 -0
  42. package/dist/lib/cloud/apply.d.ts +32 -0
  43. package/dist/lib/cloud/apply.js +294 -0
  44. package/dist/lib/cloud/checkpoint.d.ts +26 -0
  45. package/dist/lib/cloud/checkpoint.js +142 -0
  46. package/dist/lib/cloud/conflicts.d.ts +55 -0
  47. package/dist/lib/cloud/conflicts.js +131 -0
  48. package/dist/lib/cloud/containment.d.ts +8 -0
  49. package/dist/lib/cloud/containment.js +52 -0
  50. package/dist/lib/cloud/diff.d.ts +39 -0
  51. package/dist/lib/cloud/diff.js +138 -0
  52. package/dist/lib/cloud/engine-bridge.d.ts +21 -0
  53. package/dist/lib/cloud/engine-bridge.js +105 -0
  54. package/dist/lib/cloud/hash-cache.d.ts +23 -0
  55. package/dist/lib/cloud/hash-cache.js +44 -0
  56. package/dist/lib/cloud/hash.d.ts +10 -0
  57. package/dist/lib/cloud/hash.js +111 -0
  58. package/dist/lib/cloud/http.d.ts +17 -0
  59. package/dist/lib/cloud/http.js +47 -0
  60. package/dist/lib/cloud/lock-path.d.ts +1 -0
  61. package/dist/lib/cloud/lock-path.js +5 -0
  62. package/dist/lib/cloud/lock.d.ts +14 -0
  63. package/dist/lib/cloud/lock.js +143 -0
  64. package/dist/lib/cloud/manifest.d.ts +6 -0
  65. package/dist/lib/cloud/manifest.js +41 -0
  66. package/dist/lib/cloud/paths.d.ts +20 -0
  67. package/dist/lib/cloud/paths.js +90 -0
  68. package/dist/lib/cloud/project-godot-merge.d.ts +32 -0
  69. package/dist/lib/cloud/project-godot-merge.js +185 -0
  70. package/dist/lib/cloud/rules.d.ts +39 -0
  71. package/dist/lib/cloud/rules.js +159 -0
  72. package/dist/lib/cloud/sync.d.ts +21 -0
  73. package/dist/lib/cloud/sync.js +518 -0
  74. package/dist/lib/cloud/transfer.d.ts +23 -0
  75. package/dist/lib/cloud/transfer.js +184 -0
  76. package/dist/lib/cloud/types.d.ts +62 -0
  77. package/dist/lib/cloud/validate-path.d.ts +23 -0
  78. package/dist/lib/cloud/validate-path.js +103 -0
  79. package/dist/lib/engine.d.ts +6 -0
  80. package/dist/lib/engine.js +36 -1
  81. package/dist/lib/game-task-plan.d.ts +42 -0
  82. package/dist/lib/game-task-plan.js +376 -0
  83. package/dist/lib/project-memory.d.ts +33 -0
  84. package/dist/lib/project-memory.js +220 -0
  85. package/dist/lib/skills-registry.d.ts +134 -61
  86. package/dist/lib/skills-registry.js +102 -4
  87. package/dist/lib/version-check.d.ts +106 -0
  88. package/dist/lib/version-check.js +338 -0
  89. package/dist/mcp/server.d.ts +2 -0
  90. package/dist/mcp/server.js +83 -0
  91. package/dist/mcp/tools/asset-tools.js +265 -45
  92. package/dist/mcp/tools/cloud-tools.d.ts +2 -0
  93. package/dist/mcp/tools/cloud-tools.js +42 -0
  94. package/dist/mcp/tools/debug-tools.js +32 -2
  95. package/dist/mcp/tools/generate-tools.js +101 -13
  96. package/dist/mcp/tools/project-tools.js +76 -21
  97. package/dist/mcp/tools/with-engine.d.ts +14 -0
  98. package/dist/mcp/tools/with-engine.js +52 -7
  99. package/gemini-extension.json +3 -3
  100. package/hooks/pre-commit-doctor.sh +0 -0
  101. package/hooks/run-hook.cmd +0 -0
  102. package/hooks/session-start.sh +0 -0
  103. package/package.json +20 -7
  104. package/skills/2d-assets/character-portrait/SKILL.md +1 -1
  105. package/skills/2d-assets/concept-art/SKILL.md +1 -1
  106. package/skills/2d-assets/create-asset-sheet/SKILL.md +163 -0
  107. package/skills/2d-assets/instantiate-asset-pack/SKILL.md +245 -0
  108. package/skills/2d-assets/pixel-art/SKILL.md +1 -1
  109. package/skills/2d-assets/skybox-panorama/SKILL.md +1 -1
  110. package/skills/2d-assets/sprite-sheet/SKILL.md +1 -1
  111. package/skills/2d-assets/tileable-texture/SKILL.md +1 -1
  112. package/skills/2d-assets/ui-graphics/SKILL.md +1 -1
  113. package/skills/2d-assets/use-widget-asset/SKILL.md +217 -0
  114. package/skills/3d-assets/character-model/SKILL.md +28 -6
  115. package/skills/3d-assets/environment-kit/SKILL.md +7 -2
  116. package/skills/3d-assets/organic-model/SKILL.md +5 -4
  117. package/skills/3d-assets/prop-model/SKILL.md +17 -9
  118. package/skills/3d-assets/vehicle-model/SKILL.md +5 -4
  119. package/skills/README.md +37 -30
  120. package/skills/ai-and-npcs/design-npc/SKILL.md +6 -6
  121. package/skills/animation/animation-tree/SKILL.md +4 -2
  122. package/skills/animation/facial-and-lipsync/SKILL.md +1 -1
  123. package/skills/animation/generate-motion/SKILL.md +26 -5
  124. package/skills/animation/procedural-animation/SKILL.md +2 -2
  125. package/skills/asset-pipeline/asset-strategy/SKILL.md +3 -2
  126. package/skills/audio/adaptive-music/SKILL.md +1 -1
  127. package/skills/audio/audio-direction/SKILL.md +5 -5
  128. package/skills/audio/sound-effect/SKILL.md +2 -2
  129. package/skills/audio/voice-line/SKILL.md +25 -3
  130. package/skills/character-controllers/fps-controller/SKILL.md +5 -5
  131. package/skills/debugging/debug/SKILL.md +39 -1
  132. package/skills/deployment/export-and-ship/SKILL.md +4 -4
  133. package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +170 -0
  134. package/skills/gameplay-mechanics/design-mechanic/SKILL.md +8 -8
  135. package/skills/level-design/design-level/SKILL.md +5 -5
  136. package/skills/level-design/scene-to-level/SKILL.md +125 -0
  137. package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +4 -4
  138. package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +4 -4
  139. package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +7 -7
  140. package/skills/performance/tune-performance/SKILL.md +5 -5
  141. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -1
  142. package/skills/rendering-and-lighting/art-direction/SKILL.md +5 -5
  143. package/skills/scene-and-project/brainstorm-game/SKILL.md +4 -4
  144. package/skills/scene-and-project/play/SKILL.md +1 -1
  145. package/skills/scene-and-project/scene-composition/SKILL.md +30 -1
  146. package/skills/scripting-patterns/gdscript-patterns/SKILL.md +53 -0
  147. package/skills/ui-and-ux/ui-basics/SKILL.md +1 -1
  148. package/skills/video/animated-loop/SKILL.md +1 -1
  149. package/skills/video/cinematic-cutscene/SKILL.md +1 -1
  150. package/skills/video/trailer-shot/SKILL.md +1 -1
  151. package/skills/visual-effects/game-feel/SKILL.md +6 -6
  152. package/skills/workflow/brainstorming/SKILL.md +164 -0
  153. package/skills/workflow/brainstorming/scripts/frame-template.html +214 -0
  154. package/skills/workflow/brainstorming/scripts/helper.js +88 -0
  155. package/skills/workflow/brainstorming/scripts/server.cjs +354 -0
  156. package/skills/workflow/brainstorming/scripts/start-server.sh +148 -0
  157. package/skills/workflow/brainstorming/scripts/stop-server.sh +56 -0
  158. package/skills/workflow/brainstorming/spec-document-reviewer-prompt.md +49 -0
  159. package/skills/workflow/brainstorming/visual-companion.md +287 -0
  160. package/skills/workflow/debugging-game-feel/SKILL.md +174 -0
  161. package/skills/workflow/diagnosing-perf-regressions/SKILL.md +190 -0
  162. package/skills/workflow/dispatching-parallel-agents/SKILL.md +182 -0
  163. package/skills/workflow/gameskill/SKILL.md +111 -0
  164. package/skills/workflow/investigating-bugs/CREATION-LOG.md +119 -0
  165. package/skills/workflow/investigating-bugs/SKILL.md +296 -0
  166. package/skills/workflow/investigating-bugs/condition-based-waiting-example.ts +158 -0
  167. package/skills/workflow/investigating-bugs/condition-based-waiting.md +115 -0
  168. package/skills/workflow/investigating-bugs/defense-in-depth.md +122 -0
  169. package/skills/workflow/investigating-bugs/find-polluter.sh +63 -0
  170. package/skills/workflow/investigating-bugs/root-cause-tracing.md +169 -0
  171. package/skills/workflow/investigating-bugs/test-academic.md +14 -0
  172. package/skills/workflow/investigating-bugs/test-pressure-1.md +58 -0
  173. package/skills/workflow/investigating-bugs/test-pressure-2.md +68 -0
  174. package/skills/workflow/investigating-bugs/test-pressure-3.md +69 -0
  175. package/skills/workflow/mcpupdate/SKILL.md +134 -0
  176. package/skills/workflow/playtesting-a-feature/SKILL.md +173 -0
  177. package/skills/{_meta → workflow}/skill-create/SKILL.md +32 -22
  178. package/skills/{_meta → workflow}/skill-improve/SKILL.md +9 -10
  179. package/skills/{_meta → workflow}/skill-test/SKILL.md +12 -12
  180. package/skills/{_meta → workflow}/using-summer/SKILL.md +5 -2
  181. package/skills/workflow/verification-before-completion/SKILL.md +139 -0
  182. package/skills/workflow/writing-plans/SKILL.md +152 -0
  183. package/skills/workflow/writing-plans/plan-document-reviewer-prompt.md +49 -0
  184. package/skills/workflow/writing-skills/SKILL.md +655 -0
  185. package/skills/workflow/writing-skills/anthropic-best-practices.md +1150 -0
  186. package/skills/workflow/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  187. package/skills/workflow/writing-skills/graphviz-conventions.dot +172 -0
  188. package/skills/workflow/writing-skills/persuasion-principles.md +187 -0
  189. package/skills/workflow/writing-skills/render-graphs.js +168 -0
  190. package/skills/workflow/writing-skills/testing-skills-with-subagents.md +384 -0
  191. package/dist/commands/assets.d.ts +0 -2
  192. package/dist/commands/assets.js +0 -189
  193. package/dist/commands/debug.d.ts +0 -2
  194. package/dist/commands/debug.js +0 -127
  195. package/dist/commands/doctor.test.js +0 -29
  196. package/dist/commands/project.d.ts +0 -2
  197. package/dist/commands/project.js +0 -170
  198. package/dist/commands/scene.d.ts +0 -2
  199. package/dist/commands/scene.js +0 -290
  200. package/dist/lib/agent-config.test.d.ts +0 -1
  201. package/dist/lib/agent-config.test.js +0 -80
  202. package/dist/lib/format.test.d.ts +0 -1
  203. package/dist/lib/format.test.js +0 -36
  204. package/dist/lib/plugin-manifests.test.d.ts +0 -1
  205. package/dist/lib/plugin-manifests.test.js +0 -134
  206. package/dist/lib/with-engine.d.ts +0 -14
  207. package/dist/lib/with-engine.js +0 -109
  208. package/skills/_shared/collaborative-protocol.md +0 -50
  209. package/skills/_shared/gd-style.md +0 -121
  210. package/skills/_shared/godot-version.md +0 -32
  211. package/skills/_shared/mcp-tools-reference.md +0 -133
  212. package/skills/_shared/summer-folder.md +0 -44
  213. package/skills/_shared/template-registry.md +0 -40
  214. package/skills/_tests/runner.md +0 -90
  215. package/skills/_tests/specs/3d-lighting.md +0 -19
  216. package/skills/_tests/specs/art-direction.md +0 -115
  217. package/skills/_tests/specs/asset-strategy.md +0 -19
  218. package/skills/_tests/specs/audio-direction.md +0 -123
  219. package/skills/_tests/specs/brainstorm-game.md +0 -99
  220. package/skills/_tests/specs/debug.md +0 -94
  221. package/skills/_tests/specs/design-level.md +0 -113
  222. package/skills/_tests/specs/design-mechanic.md +0 -112
  223. package/skills/_tests/specs/design-npc.md +0 -143
  224. package/skills/_tests/specs/export-and-ship.md +0 -114
  225. package/skills/_tests/specs/fps-controller.md +0 -113
  226. package/skills/_tests/specs/gdscript-patterns.md +0 -19
  227. package/skills/_tests/specs/host-authoritative-state.md +0 -104
  228. package/skills/_tests/specs/make-game.md +0 -19
  229. package/skills/_tests/specs/peer-to-peer-multiplayer.md +0 -99
  230. package/skills/_tests/specs/play.md +0 -88
  231. package/skills/_tests/specs/scene-composition.md +0 -19
  232. package/skills/_tests/specs/setup-multiplayer.md +0 -106
  233. package/skills/_tests/specs/tune-performance.md +0 -104
  234. package/skills/_tests/specs/ui-basics.md +0 -19
  235. package/skills/_tests/specs/vfx.md +0 -116
  236. package/skills/catalog.yaml +0 -991
  237. /package/dist/{commands/doctor.test.d.ts → lib/cloud/types.js} +0 -0
@@ -0,0 +1,196 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
+ import { Command } from "commander";
4
+ import { checkEngineHealth, getApiPort } from "../lib/engine.js";
5
+ import { getProjectMemorySummary, } from "../lib/project-memory.js";
6
+ import { c, pad, tildeify } from "../lib/format.js";
7
+ export const memoryCommand = new Command("memory")
8
+ .description("Inspect project memory in .summer")
9
+ .option("--project <path>", "Project directory to inspect")
10
+ .option("--json", "Print memory summary as JSON")
11
+ .action(async (opts) => {
12
+ const resolvedProject = await resolveMemoryProject(opts.project);
13
+ if (!resolvedProject.projectPath) {
14
+ if (opts.json) {
15
+ console.log(JSON.stringify({ ok: false, ...resolvedProject }, null, 2));
16
+ }
17
+ else {
18
+ console.error(resolvedProject.reason ?? "Could not find a Summer project.");
19
+ console.error("Run from a project directory, pass --project <path>, or start Summer Engine.");
20
+ }
21
+ process.exit(1);
22
+ }
23
+ const summary = getProjectMemorySummary(resolvedProject.projectPath);
24
+ if (opts.json) {
25
+ console.log(JSON.stringify({
26
+ ok: true,
27
+ projectPath: resolvedProject.projectPath,
28
+ source: resolvedProject.source,
29
+ memory: summary,
30
+ }, null, 2));
31
+ return;
32
+ }
33
+ console.log(formatMemorySummary(resolvedProject.projectPath, summary));
34
+ });
35
+ memoryCommand
36
+ .command("show <file>")
37
+ .description("Print a .summer memory Markdown file")
38
+ .option("--project <path>", "Project directory to inspect")
39
+ .action(async (file, opts) => {
40
+ const resolvedProject = await resolveMemoryProject(opts.project);
41
+ if (!resolvedProject.projectPath) {
42
+ console.error(resolvedProject.reason ?? "Could not find a Summer project.");
43
+ console.error("Run from a project directory, pass --project <path>, or start Summer Engine.");
44
+ process.exit(1);
45
+ }
46
+ const filePath = resolveMemoryFilePath(resolvedProject.projectPath, file);
47
+ console.log(readFileSync(filePath, "utf-8"));
48
+ });
49
+ memoryCommand
50
+ .command("path")
51
+ .description("Print the .summer directory path for the current project")
52
+ .option("--project <path>", "Project directory to inspect")
53
+ .action(async (opts) => {
54
+ const resolvedProject = await resolveMemoryProject(opts.project);
55
+ if (!resolvedProject.projectPath) {
56
+ console.error(resolvedProject.reason ?? "Could not find a Summer project.");
57
+ console.error("Run from a project directory, pass --project <path>, or start Summer Engine.");
58
+ process.exit(1);
59
+ }
60
+ console.log(join(resolvedProject.projectPath, ".summer"));
61
+ });
62
+ export async function resolveMemoryProject(projectOption) {
63
+ if (projectOption) {
64
+ const projectPath = resolve(projectOption);
65
+ if (!existsSync(projectPath)) {
66
+ return {
67
+ projectPath: null,
68
+ source: "option",
69
+ reason: `Project directory not found: ${projectPath}`,
70
+ };
71
+ }
72
+ if (!existsSync(join(projectPath, "project.godot"))) {
73
+ return {
74
+ projectPath: null,
75
+ source: "option",
76
+ reason: `No project.godot found in ${projectPath}.`,
77
+ };
78
+ }
79
+ return { projectPath, source: "option" };
80
+ }
81
+ const cwdProject = findProjectRoot(process.cwd());
82
+ if (cwdProject)
83
+ return { projectPath: cwdProject, source: "cwd" };
84
+ const port = await getApiPort();
85
+ const health = await checkEngineHealth(port);
86
+ if (health?.project_path && existsSync(join(health.project_path, "project.godot"))) {
87
+ return { projectPath: health.project_path, source: "engine" };
88
+ }
89
+ return {
90
+ projectPath: null,
91
+ source: "none",
92
+ reason: "No Summer project found from engine state or current directory.",
93
+ };
94
+ }
95
+ export function findProjectRoot(startDir) {
96
+ let current = resolve(startDir);
97
+ while (true) {
98
+ if (existsSync(join(current, "project.godot")))
99
+ return current;
100
+ const parent = dirname(current);
101
+ if (parent === current)
102
+ return null;
103
+ current = parent;
104
+ }
105
+ }
106
+ export function resolveMemoryFilePath(projectPath, requested) {
107
+ const summerDir = resolve(projectPath, ".summer");
108
+ const rawPath = requested.startsWith(".summer/")
109
+ ? resolve(projectPath, requested)
110
+ : resolve(summerDir, requested);
111
+ const withinSummer = isPathInside(summerDir, rawPath);
112
+ if (!withinSummer) {
113
+ throw new Error("Memory files must be inside the project's .summer directory.");
114
+ }
115
+ if (!existsSync(rawPath)) {
116
+ throw new Error(`Memory file not found: ${requested}`);
117
+ }
118
+ if (!statSync(rawPath).isFile()) {
119
+ throw new Error(`Memory path is not a file: ${requested}`);
120
+ }
121
+ return rawPath;
122
+ }
123
+ export function formatMemorySummary(projectPath, summary) {
124
+ const lines = [];
125
+ lines.push(c.bold("Project Memory"));
126
+ lines.push("");
127
+ lines.push(` Project: ${tildeify(projectPath)}`);
128
+ lines.push(` Folder: ${tildeify(join(projectPath, ".summer"))}`);
129
+ lines.push("");
130
+ if (!summary.present) {
131
+ lines.push(` ${summary.reason ?? "No .summer memory found yet."}`);
132
+ lines.push("");
133
+ lines.push(" Agents create this through Summer skills.");
134
+ lines.push(" Start with .summer/GameSoul.md for the project brief.");
135
+ lines.push(" Use .summer/memory/ for locked facts like voice IDs and world canon.");
136
+ return lines.join("\n");
137
+ }
138
+ const totalFiles = summary.files.length;
139
+ const structured = summary.structured;
140
+ lines.push(` Status: ${totalFiles} .summer Markdown file${totalFiles === 1 ? "" : "s"}, ` +
141
+ `${structured.fileCount} structured memory file${structured.fileCount === 1 ? "" : "s"}, ` +
142
+ `${structured.lockedCount} locked`);
143
+ lines.push("");
144
+ const canonicalRows = canonicalRowsFromSummary(summary);
145
+ if (canonicalRows.length > 0) {
146
+ lines.push(c.bold("Canonical"));
147
+ for (const [label, file] of canonicalRows) {
148
+ lines.push(formatMemoryFileLine(label, file));
149
+ }
150
+ lines.push("");
151
+ }
152
+ if (structured.files.length > 0) {
153
+ lines.push(c.bold("Structured"));
154
+ for (const file of structured.files) {
155
+ lines.push(formatMemoryFileLine(file.kind, file));
156
+ }
157
+ if (structured.truncated) {
158
+ lines.push(" ...more files omitted from summary");
159
+ }
160
+ lines.push("");
161
+ }
162
+ lines.push("Read a file:");
163
+ lines.push(" summer memory show .summer/memory/casting/voices.md");
164
+ lines.push("");
165
+ lines.push("Rule:");
166
+ lines.push(" Markdown is canonical. Changing priority: locked memory requires explicit user confirmation.");
167
+ return lines.join("\n");
168
+ }
169
+ export function formatStatusMemoryLine(summary) {
170
+ if (!summary.present)
171
+ return "Memory: No .summer folder yet";
172
+ const fileCount = summary.files.length;
173
+ const structuredCount = summary.structured.fileCount;
174
+ const lockedCount = summary.structured.lockedCount;
175
+ return `Memory: ${fileCount} .summer file${fileCount === 1 ? "" : "s"}, ${structuredCount} memory file${structuredCount === 1 ? "" : "s"}, ${lockedCount} locked`;
176
+ }
177
+ function canonicalRowsFromSummary(summary) {
178
+ const rows = [
179
+ ["brief", summary.canonical.gameSoul],
180
+ ["art", summary.canonical.artBible],
181
+ ["audio", summary.canonical.audioBible],
182
+ ["build", summary.canonical.buildPlan],
183
+ ["legacy-casting", summary.canonical.legacyVoiceCast],
184
+ ];
185
+ return rows.filter((row) => row[1] !== null);
186
+ }
187
+ function formatMemoryFileLine(label, file) {
188
+ const lock = file.locked ? "locked" : "";
189
+ const title = file.title ? ` - ${file.title}` : "";
190
+ return ` ${pad(label, 15)} ${pad(file.path, 42)} ${lock}${title}`;
191
+ }
192
+ function isPathInside(parent, child) {
193
+ const relativePath = relative(parent, child);
194
+ return (relativePath === "" ||
195
+ (!relativePath.startsWith("..") && !isAbsolute(relativePath)));
196
+ }
@@ -28,5 +28,5 @@ export const openCommand = new Command("open")
28
28
  }
29
29
  console.log(`Opening project: ${fullPath}`);
30
30
  console.log("Note: To switch projects, close the current one in Summer Engine first,\n" +
31
- "or run: summer run " + fullPath);
31
+ "then run: summer run " + fullPath);
32
32
  });
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const planCommand: Command;
@@ -0,0 +1,60 @@
1
+ import { Command } from "commander";
2
+ import { ASSET_POLICIES, GAME_TASK_MODES, GAME_TASK_TARGETS, VERIFICATION_LEVELS, buildGameTaskPlan, } from "../lib/game-task-plan.js";
3
+ import { c } from "../lib/format.js";
4
+ function parseChoice(value, choices, label) {
5
+ if (!value)
6
+ return undefined;
7
+ if (choices.includes(value))
8
+ return value;
9
+ throw new Error(`Invalid ${label}: ${value}. Use one of: ${choices.join(", ")}`);
10
+ }
11
+ function printList(title, items) {
12
+ if (items.length === 0)
13
+ return;
14
+ console.log(c.bold(title));
15
+ for (const item of items) {
16
+ console.log(` - ${item}`);
17
+ }
18
+ console.log("");
19
+ }
20
+ export const planCommand = new Command("plan")
21
+ .description("Plan the right Summer CLI/MCP workflow for an AI game-building task")
22
+ .argument("<goal...>", "What the user wants to build, fix, polish, or create")
23
+ .option("--mode <mode>", `Task mode: ${GAME_TASK_MODES.join("|")}`)
24
+ .option("--target <target>", `Task target: ${GAME_TASK_TARGETS.join("|")}`)
25
+ .option("--asset-policy <policy>", `Asset policy: ${ASSET_POLICIES.join("|")}`)
26
+ .option("--verification <level>", `Verification level: ${VERIFICATION_LEVELS.join("|")}`)
27
+ .option("--json", "Print the plan as JSON")
28
+ .action((goalParts, opts) => {
29
+ const plan = buildGameTaskPlan({
30
+ goal: goalParts.join(" "),
31
+ mode: parseChoice(opts.mode, GAME_TASK_MODES, "mode"),
32
+ target: parseChoice(opts.target, GAME_TASK_TARGETS, "target"),
33
+ assetPolicy: parseChoice(opts.assetPolicy, ASSET_POLICIES, "asset policy"),
34
+ verification: parseChoice(opts.verification, VERIFICATION_LEVELS, "verification"),
35
+ });
36
+ if (opts.json) {
37
+ console.log(JSON.stringify(plan, null, 2));
38
+ return;
39
+ }
40
+ console.log("");
41
+ console.log(c.bold("Summer Game Task Plan"));
42
+ console.log(`Goal: ${plan.goal}`);
43
+ console.log(`Mode: ${plan.mode}`);
44
+ console.log(`Target: ${plan.target}`);
45
+ console.log(`Asset policy: ${plan.assetPolicy}`);
46
+ console.log(`Verification: ${plan.verification}`);
47
+ console.log("");
48
+ console.log(c.bold("Recommended Skills"));
49
+ for (const skill of plan.recommendedSkills) {
50
+ console.log(` - ${skill.id}: ${skill.why}`);
51
+ }
52
+ console.log("");
53
+ printList("MCP Start", plan.mcpToolPlan.start);
54
+ printList("MCP Mutations", plan.mcpToolPlan.mutate);
55
+ printList("MCP Assets", plan.mcpToolPlan.assets);
56
+ printList("MCP Verify", plan.mcpToolPlan.verify);
57
+ printList("Host File Work", plan.hostFileWork);
58
+ printList("User Gates", plan.userGates);
59
+ printList("Next Agent Steps", plan.nextAgentSteps);
60
+ });
@@ -1,7 +1,10 @@
1
1
  import { Command } from "commander";
2
2
  import { spawn } from "child_process";
3
3
  import { existsSync } from "fs";
4
+ import { mkdir, readFile, rm, writeFile } from "fs/promises";
4
5
  import { platform } from "os";
6
+ import { join, resolve } from "path";
7
+ import { getSummerDir } from "../lib/auth.js";
5
8
  import { getApiPort, checkEngineHealth } from "../lib/engine.js";
6
9
  const MAC_PATHS = [
7
10
  "/Applications/Summer.app/Contents/MacOS/Summer",
@@ -12,6 +15,8 @@ const WIN_PATHS = [
12
15
  `${process.env.LOCALAPPDATA}\\Programs\\Summer Engine\\Summer.exe`,
13
16
  `${process.env.PROGRAMFILES}\\Summer Engine\\Summer.exe`,
14
17
  ];
18
+ const LAUNCH_LOCK_STALE_MS = 60_000;
19
+ const LAUNCH_LOCK_WAIT_MS = 15_000;
15
20
  function findEngineBinary() {
16
21
  const paths = platform() === "darwin" ? MAC_PATHS : WIN_PATHS;
17
22
  for (const p of paths) {
@@ -20,50 +25,142 @@ function findEngineBinary() {
20
25
  }
21
26
  return null;
22
27
  }
28
+ function isProcessAlive(pid) {
29
+ if (!Number.isInteger(pid) || pid <= 0)
30
+ return false;
31
+ try {
32
+ process.kill(pid, 0);
33
+ return true;
34
+ }
35
+ catch (error) {
36
+ const code = error.code;
37
+ return code === "EPERM";
38
+ }
39
+ }
40
+ async function removeStaleLaunchLock(lockDir) {
41
+ const ownerPath = join(lockDir, "owner.json");
42
+ try {
43
+ const raw = await readFile(ownerPath, "utf-8");
44
+ const owner = JSON.parse(raw);
45
+ const pid = typeof owner.pid === "number" ? owner.pid : 0;
46
+ const createdAt = typeof owner.createdAt === "number" ? owner.createdAt : 0;
47
+ const staleByTime = Date.now() - createdAt > LAUNCH_LOCK_STALE_MS;
48
+ const staleByPid = !isProcessAlive(pid);
49
+ if (!staleByTime && !staleByPid) {
50
+ return false;
51
+ }
52
+ }
53
+ catch {
54
+ // Broken owner metadata should not permanently block launches.
55
+ }
56
+ await rm(lockDir, { recursive: true, force: true });
57
+ return true;
58
+ }
59
+ async function withLaunchLock(fn) {
60
+ const summerDir = getSummerDir();
61
+ await mkdir(summerDir, { recursive: true, mode: 0o700 });
62
+ const lockDir = join(summerDir, "launch.lock");
63
+ const ownerPath = join(lockDir, "owner.json");
64
+ const startedAt = Date.now();
65
+ while (true) {
66
+ try {
67
+ await mkdir(lockDir, { mode: 0o700 });
68
+ try {
69
+ await writeFile(ownerPath, JSON.stringify({ pid: process.pid, createdAt: Date.now() }, null, 2), { encoding: "utf-8", mode: 0o600 });
70
+ return await fn();
71
+ }
72
+ finally {
73
+ await rm(lockDir, { recursive: true, force: true });
74
+ }
75
+ }
76
+ catch (error) {
77
+ const code = error.code;
78
+ if (code !== "EEXIST") {
79
+ throw error;
80
+ }
81
+ if (await removeStaleLaunchLock(lockDir)) {
82
+ continue;
83
+ }
84
+ if (Date.now() - startedAt > LAUNCH_LOCK_WAIT_MS) {
85
+ throw new Error("Another `summer run` launch is still in progress. Try again in a few seconds.");
86
+ }
87
+ await sleep(100);
88
+ }
89
+ }
90
+ }
23
91
  export const runCommand = new Command("run")
24
92
  .description("Launch Summer Engine, optionally opening a project")
25
93
  .argument("[path]", "Path to a project directory (must contain project.godot)")
26
94
  .action(async (projectPath) => {
27
- const port = await getApiPort();
28
- const health = await checkEngineHealth(port);
29
- if (health && !projectPath) {
30
- console.log(`Summer Engine is already running (v${health.version}) on port ${port}`);
31
- if (health.project_name) {
32
- console.log(` Project: ${health.project_name}`);
95
+ const fullProjectPath = projectPath ? resolve(projectPath) : null;
96
+ if (fullProjectPath) {
97
+ if (!existsSync(fullProjectPath)) {
98
+ console.error(`Directory not found: ${fullProjectPath}`);
99
+ process.exit(1);
100
+ }
101
+ if (!existsSync(join(fullProjectPath, "project.godot"))) {
102
+ console.error(`No project.godot found in ${fullProjectPath}\n` +
103
+ "This doesn't look like a Godot/Summer Engine project.");
104
+ process.exit(1);
33
105
  }
34
- return;
35
- }
36
- const binary = findEngineBinary();
37
- if (!binary) {
38
- console.error("Summer Engine not found. Install it first:\n" +
39
- " summer install\n" +
40
- " or download from https://summerengine.com/download");
41
- process.exit(1);
42
- }
43
- const args = ["--editor"];
44
- if (projectPath) {
45
- args.unshift("--path", projectPath);
46
106
  }
47
- console.log("Launching Summer Engine...");
48
- const child = spawn(binary, args, { detached: true, stdio: "ignore" });
49
- child.unref();
50
- // Wait for engine to start responding
51
- const startTime = Date.now();
52
- const timeout = 20000;
53
- while (Date.now() - startTime < timeout) {
54
- await sleep(500);
55
- const newPort = await getApiPort();
56
- const h = await checkEngineHealth(newPort);
57
- if (h) {
58
- console.log(`Summer Engine running (v${h.version}) on port ${newPort}`);
59
- if (h.project_name) {
60
- console.log(` Project: ${h.project_name}`);
107
+ await withLaunchLock(async () => {
108
+ const port = await getApiPort();
109
+ const health = await checkEngineHealth(port);
110
+ if (health) {
111
+ const runningProjectPath = health.project_path
112
+ ? resolve(health.project_path)
113
+ : null;
114
+ if (!fullProjectPath || runningProjectPath === fullProjectPath) {
115
+ console.log(`Summer Engine is already running (v${health.version}) on port ${port}`);
116
+ if (health.project_name) {
117
+ console.log(` Project: ${health.project_name}`);
118
+ }
119
+ if (health.project_path) {
120
+ console.log(` Path: ${health.project_path}`);
121
+ }
122
+ return;
61
123
  }
124
+ console.log(`Summer Engine is already running (v${health.version}) on port ${port}`);
125
+ console.log(` Current project: ${health.project_path ?? health.project_name ?? "unknown"}`);
126
+ console.error(`Refusing to launch a second editor for: ${fullProjectPath}\n` +
127
+ "Close or switch the current Summer Engine project first, then run this command again.");
128
+ process.exitCode = 1;
62
129
  return;
63
130
  }
64
- }
65
- console.log("Summer Engine launched but API not responding yet.\n" +
66
- "It may still be loading. Run 'summer status' to check.");
131
+ const binary = findEngineBinary();
132
+ if (!binary) {
133
+ console.error("Summer Engine not found. Install it first:\n" +
134
+ " summer install\n" +
135
+ " or download from https://summerengine.com/download");
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+ const args = ["--editor"];
140
+ if (fullProjectPath) {
141
+ args.unshift("--path", fullProjectPath);
142
+ }
143
+ console.log("Launching Summer Engine...");
144
+ const child = spawn(binary, args, { detached: true, stdio: "ignore" });
145
+ child.unref();
146
+ // Wait for engine to start responding
147
+ const startTime = Date.now();
148
+ const timeout = 20000;
149
+ while (Date.now() - startTime < timeout) {
150
+ await sleep(500);
151
+ const newPort = await getApiPort();
152
+ const h = await checkEngineHealth(newPort);
153
+ if (h) {
154
+ console.log(`Summer Engine running (v${h.version}) on port ${newPort}`);
155
+ if (h.project_name) {
156
+ console.log(` Project: ${h.project_name}`);
157
+ }
158
+ return;
159
+ }
160
+ }
161
+ console.log("Summer Engine launched but API not responding yet.\n" +
162
+ "It may still be loading. Run 'summer status' to check.");
163
+ });
67
164
  });
68
165
  function sleep(ms) {
69
166
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -14,6 +14,8 @@ const AGENT_LABEL = {
14
14
  cline: "Cline",
15
15
  "roo-code": "Roo Code",
16
16
  gemini: "Gemini CLI",
17
+ "github-copilot": "GitHub Copilot CLI",
18
+ "vscode-copilot": "GitHub Copilot in VS Code",
17
19
  opencode: "OpenCode",
18
20
  };
19
21
  export const setupCommand = new Command("setup")
@@ -25,6 +27,7 @@ export const setupCommand = new Command("setup")
25
27
  .option("--print", "Print the MCP config snippet instead of writing files")
26
28
  .option("--yes", "Apply practical setup steps without prompting")
27
29
  .option("--json", "Print setup result as JSON")
30
+ .option("--force", "Overwrite existing skill content (passes --force through to skills install)")
28
31
  .action(async (agentArg, opts) => {
29
32
  const agent = resolveAgent(agentArg, opts.agent);
30
33
  const scope = resolveScope(opts.scope);
@@ -37,6 +40,7 @@ export const setupCommand = new Command("setup")
37
40
  const skills = setupRecommendedSkills(agent, {
38
41
  dryRun: Boolean(opts.dryRun || opts.print),
39
42
  yes: Boolean(opts.yes),
43
+ force: Boolean(opts.force),
40
44
  });
41
45
  const doctor = await runDoctor({ quiet: true });
42
46
  if (opts.json) {
@@ -72,7 +76,7 @@ function resolveScope(scopeOpt) {
72
76
  return parsed;
73
77
  }
74
78
  function setupRecommendedSkills(agent, options) {
75
- const invocation = skillInstallInvocation(agent);
79
+ const invocation = skillInstallInvocation(agent, { force: options.force });
76
80
  if (!invocation) {
77
81
  return {
78
82
  status: "skipped",
@@ -107,13 +111,15 @@ function setupRecommendedSkills(agent, options) {
107
111
  stderr: result.stderr.trim(),
108
112
  };
109
113
  }
110
- function skillInstallInvocation(agent) {
114
+ function skillInstallInvocation(agent, opts = { force: false }) {
111
115
  const cliPath = process.argv[1];
112
116
  if (!cliPath)
113
117
  return null;
114
118
  const command = cliPath.endsWith(".js") ? process.execPath : cliPath;
115
119
  const prefix = cliPath.endsWith(".js") ? [cliPath] : [];
116
120
  const baseArgs = ["skills", "install", "--recommended", "--agent", agent];
121
+ if (opts.force)
122
+ baseArgs.push("--force");
117
123
  return {
118
124
  command,
119
125
  args: [...prefix, ...baseArgs],