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
package/dist/bin/summer.js
CHANGED
|
@@ -10,6 +10,7 @@ import { openCommand } from "../commands/open.js";
|
|
|
10
10
|
import { installCommand } from "../commands/install.js";
|
|
11
11
|
import { createCommand } from "../commands/create.js";
|
|
12
12
|
import { listCommand } from "../commands/list.js";
|
|
13
|
+
import { memoryCommand } from "../commands/memory.js";
|
|
13
14
|
import { skillsCommand } from "../commands/skills.js";
|
|
14
15
|
import { orchestratorCommand } from "../commands/orchestrator.js";
|
|
15
16
|
import { setupCommand } from "../commands/setup.js";
|
|
@@ -34,6 +35,7 @@ program.addCommand(runCommand);
|
|
|
34
35
|
program.addCommand(openCommand);
|
|
35
36
|
program.addCommand(createCommand);
|
|
36
37
|
program.addCommand(listCommand);
|
|
38
|
+
program.addCommand(memoryCommand);
|
|
37
39
|
program.addCommand(skillsCommand);
|
|
38
40
|
program.addCommand(mcpCommand);
|
|
39
41
|
program.addCommand(setupCommand);
|
|
@@ -49,15 +51,21 @@ function printIntro(version) {
|
|
|
49
51
|
console.log("");
|
|
50
52
|
console.log(` ${sym.arrow()} ${c.bold("Summer Engine")} ${c.dim("v" + version)} ${c.dim("·")} AI-native game engine`);
|
|
51
53
|
console.log("");
|
|
52
|
-
console.log(` ${c.bold("
|
|
53
|
-
console.log(`
|
|
54
|
-
console.log(` ${c.
|
|
55
|
-
console.log(
|
|
54
|
+
console.log(` ${c.bold("Setup wizard")}`);
|
|
55
|
+
console.log(` Open Claude Code, Cursor, Codex, Copilot, Windsurf, or another supported agent and paste:`);
|
|
56
|
+
console.log(` ${c.brand("\"Install Summer Engine and let's make a game.\"")}`);
|
|
57
|
+
console.log("");
|
|
58
|
+
console.log(` ${c.bold("Manual commands")}`);
|
|
59
|
+
console.log(` ${c.dim("$")} npx -y summer-engine@latest setup claude-code --yes`);
|
|
60
|
+
console.log(` ${c.dim("$")} npx -y summer-engine@latest create 3d-basic my-game`);
|
|
61
|
+
console.log(` ${c.dim("$")} npx -y summer-engine@latest memory`);
|
|
62
|
+
console.log(` ${c.dim("$")} npx -y summer-engine@latest doctor`);
|
|
56
63
|
console.log("");
|
|
57
64
|
console.log(` ${c.bold("Slash commands")} ${c.dim("(in your AI agent)")}`);
|
|
58
65
|
console.log(` ${c.brand("/debug")} ${c.dim("Triage and fix a bug end-to-end")}`);
|
|
59
66
|
console.log(` ${c.brand("/play")} ${c.dim("Run the game and report state")}`);
|
|
60
67
|
console.log("");
|
|
61
68
|
console.log(` ${c.dim("Docs:")} https://summerengine.com/docs`);
|
|
69
|
+
console.log(` ${c.dim("Source:")} https://github.com/SummerEngine/summer`);
|
|
62
70
|
console.log("");
|
|
63
71
|
}
|
package/dist/commands/create.js
CHANGED
|
@@ -42,6 +42,7 @@ export const createCommand = new Command("create")
|
|
|
42
42
|
console.log(`\nProject created at ${fullPath}`);
|
|
43
43
|
console.log("\nNext steps:");
|
|
44
44
|
console.log(` summer run ${dirName}`);
|
|
45
|
+
console.log(" Ask your agent to run summer:brainstorm-game to create .summer/GameSoul.md");
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
48
|
// Fall back to remote template lookup
|
|
@@ -86,6 +87,7 @@ export const createCommand = new Command("create")
|
|
|
86
87
|
console.log("Source: " + remote.url);
|
|
87
88
|
console.log("\nNext steps:");
|
|
88
89
|
console.log(` summer run ${dirName}`);
|
|
90
|
+
console.log(" Ask your agent to run summer:brainstorm-game to create .summer/GameSoul.md");
|
|
89
91
|
});
|
|
90
92
|
function projectGodot(name, mainScene) {
|
|
91
93
|
return `; Summer Engine Project
|
package/dist/commands/doctor.js
CHANGED
|
@@ -6,6 +6,8 @@ import { Command } from "commander";
|
|
|
6
6
|
import { getAuthToken, getUserInfo } from "../lib/auth.js";
|
|
7
7
|
import { checkEngineHealth, getApiPort, getApiToken } from "../lib/engine.js";
|
|
8
8
|
import { brandLine, c, pad, sym, tildeify } from "../lib/format.js";
|
|
9
|
+
import { getProjectMemorySummary } from "../lib/project-memory.js";
|
|
10
|
+
import { buildCliVersionCheck, buildSkillsVersionCheck, defaultSkillMarkerCandidates, fetchLatestRegistryVersion, } from "../lib/version-check.js";
|
|
9
11
|
const require = createRequire(import.meta.url);
|
|
10
12
|
const { version } = require("../../package.json");
|
|
11
13
|
const MAC_ENGINE_PATHS = [
|
|
@@ -18,7 +20,7 @@ const WIN_ENGINE_PATHS = [
|
|
|
18
20
|
`${process.env.PROGRAMFILES}\\Summer Engine\\Summer.exe`,
|
|
19
21
|
];
|
|
20
22
|
export const doctorCommand = new Command("doctor")
|
|
21
|
-
.description("Diagnose Node, login, engine, local API, and MCP boot")
|
|
23
|
+
.description("Diagnose Node, login, engine, project memory, local API, and MCP boot")
|
|
22
24
|
.option("--json", "Print diagnostics as JSON")
|
|
23
25
|
.action(async (opts) => {
|
|
24
26
|
if (!opts.json) {
|
|
@@ -42,9 +44,12 @@ export async function runDoctor(options = {}) {
|
|
|
42
44
|
status: "ok",
|
|
43
45
|
message: `v${version}`,
|
|
44
46
|
});
|
|
47
|
+
checks.push(await checkCliVersionCurrent());
|
|
48
|
+
checks.push(checkSkillsVersion());
|
|
45
49
|
checks.push(await checkLogin());
|
|
46
50
|
checks.push(checkEngineInstall());
|
|
47
51
|
checks.push(await checkLocalApi());
|
|
52
|
+
checks.push(await checkProjectMemory());
|
|
48
53
|
checks.push(await checkMcpBoot());
|
|
49
54
|
const result = summarizeChecks(checks);
|
|
50
55
|
if (!options.quiet) {
|
|
@@ -74,6 +79,33 @@ function checkNodeVersion() {
|
|
|
74
79
|
message: `${process.version} (need 18+)`,
|
|
75
80
|
};
|
|
76
81
|
}
|
|
82
|
+
async function checkCliVersionCurrent() {
|
|
83
|
+
const registry = await fetchLatestRegistryVersion();
|
|
84
|
+
const result = buildCliVersionCheck({
|
|
85
|
+
installedVersion: version,
|
|
86
|
+
registry,
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
id: "cli-version-current",
|
|
90
|
+
label: "CLI up to date",
|
|
91
|
+
status: result.status,
|
|
92
|
+
message: result.message,
|
|
93
|
+
details: result.details,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function checkSkillsVersion() {
|
|
97
|
+
const result = buildSkillsVersionCheck({
|
|
98
|
+
installedCliVersion: version,
|
|
99
|
+
candidates: defaultSkillMarkerCandidates(),
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
id: "skills-version-stale",
|
|
103
|
+
label: "Skills up to date",
|
|
104
|
+
status: result.status,
|
|
105
|
+
message: result.message,
|
|
106
|
+
details: result.details,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
77
109
|
async function checkLogin() {
|
|
78
110
|
const token = await getAuthToken();
|
|
79
111
|
const user = await getUserInfo();
|
|
@@ -147,6 +179,44 @@ async function checkLocalApi() {
|
|
|
147
179
|
},
|
|
148
180
|
};
|
|
149
181
|
}
|
|
182
|
+
async function checkProjectMemory() {
|
|
183
|
+
const port = await getApiPort();
|
|
184
|
+
const health = await checkEngineHealth(port);
|
|
185
|
+
if (!health?.project_path) {
|
|
186
|
+
return {
|
|
187
|
+
id: "project-memory",
|
|
188
|
+
label: "Project Memory",
|
|
189
|
+
status: "ok",
|
|
190
|
+
message: "no project open",
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const summary = getProjectMemorySummary(health.project_path);
|
|
194
|
+
if (!summary.present) {
|
|
195
|
+
return {
|
|
196
|
+
id: "project-memory",
|
|
197
|
+
label: "Project Memory",
|
|
198
|
+
status: "warning",
|
|
199
|
+
message: ".summer not created yet",
|
|
200
|
+
details: {
|
|
201
|
+
projectPath: health.project_path,
|
|
202
|
+
reason: summary.reason,
|
|
203
|
+
recommendedAction: "Ask your agent to run summer:brainstorm-game.",
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
id: "project-memory",
|
|
209
|
+
label: "Project Memory",
|
|
210
|
+
status: "ok",
|
|
211
|
+
message: `${summary.files.length} files, ${summary.structured.fileCount} memory, ${summary.structured.lockedCount} locked`,
|
|
212
|
+
details: {
|
|
213
|
+
projectPath: health.project_path,
|
|
214
|
+
files: summary.files.length,
|
|
215
|
+
memoryFiles: summary.structured.fileCount,
|
|
216
|
+
locked: summary.structured.lockedCount,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
150
220
|
async function checkMcpBoot() {
|
|
151
221
|
const cliPath = process.argv[1];
|
|
152
222
|
if (!cliPath) {
|
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
/** Guard a download URL from the releases endpoint. A missing/empty URL (an
|
|
3
|
+
* outdated or misconfigured endpoint) otherwise becomes a cryptic
|
|
4
|
+
* fetch(undefined) failure — surface it clearly with a manual fallback. */
|
|
5
|
+
export declare function requireDownloadUrl(value: string | undefined, label: string): string;
|
|
6
|
+
/** Verify a downloaded file's sha256 against the expected hash from the releases
|
|
7
|
+
* endpoint. Skipped when no expected hash is published. Catches a truncated or
|
|
8
|
+
* corrupted download (a ~1GB install over a flaky network) before it becomes a
|
|
9
|
+
* confusing DMG-mount / installer failure downstream. */
|
|
10
|
+
export declare function verifyChecksum(actual: string, expected: string | undefined, label: string): void;
|
|
11
|
+
/** Velopack Setup.exe install args. Velopack is NOT NSIS: silent install is
|
|
12
|
+
* `--silent` and a custom directory is `--installto <dir>` (per Velopack's
|
|
13
|
+
* setup.exe CLI). The old NSIS flags (/S, /D=) are rejected by the Velopack
|
|
14
|
+
* installer with "unexpected argument '/S' found" — this is the wizard bug. */
|
|
15
|
+
export declare function windowsInstallerArgs(customPath?: string): string;
|
|
2
16
|
export declare const installCommand: Command;
|
|
17
|
+
export declare function downloadFile(url: string, dest: string): Promise<{
|
|
18
|
+
sha256: string;
|
|
19
|
+
}>;
|
package/dist/commands/install.js
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
|
+
import { createHash } from "crypto";
|
|
3
4
|
import { createWriteStream, existsSync, mkdirSync } from "fs";
|
|
4
5
|
import { pipeline } from "stream/promises";
|
|
5
6
|
import { Readable } from "stream";
|
|
6
7
|
import { tmpdir, platform } from "os";
|
|
7
8
|
import { join } from "path";
|
|
8
9
|
const RELEASES_URL = "https://www.summerengine.com/api/desktop/releases";
|
|
10
|
+
/** Guard a download URL from the releases endpoint. A missing/empty URL (an
|
|
11
|
+
* outdated or misconfigured endpoint) otherwise becomes a cryptic
|
|
12
|
+
* fetch(undefined) failure — surface it clearly with a manual fallback. */
|
|
13
|
+
export function requireDownloadUrl(value, label) {
|
|
14
|
+
if (!value || value.trim().length === 0) {
|
|
15
|
+
throw new Error(`Release info is missing the ${label} download URL — the releases endpoint may be ` +
|
|
16
|
+
`outdated. Download manually from https://summerengine.com/download`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
/** Verify a downloaded file's sha256 against the expected hash from the releases
|
|
21
|
+
* endpoint. Skipped when no expected hash is published. Catches a truncated or
|
|
22
|
+
* corrupted download (a ~1GB install over a flaky network) before it becomes a
|
|
23
|
+
* confusing DMG-mount / installer failure downstream. */
|
|
24
|
+
export function verifyChecksum(actual, expected, label) {
|
|
25
|
+
if (!expected || expected.trim().length === 0)
|
|
26
|
+
return; // nothing published to verify against
|
|
27
|
+
if (actual.toLowerCase() !== expected.toLowerCase()) {
|
|
28
|
+
throw new Error(`${label} checksum mismatch — the download was corrupted or incomplete ` +
|
|
29
|
+
`(expected ${expected.slice(0, 12)}…, got ${actual.slice(0, 12)}…). Re-run \`summer install\`.`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Velopack Setup.exe install args. Velopack is NOT NSIS: silent install is
|
|
33
|
+
* `--silent` and a custom directory is `--installto <dir>` (per Velopack's
|
|
34
|
+
* setup.exe CLI). The old NSIS flags (/S, /D=) are rejected by the Velopack
|
|
35
|
+
* installer with "unexpected argument '/S' found" — this is the wizard bug. */
|
|
36
|
+
export function windowsInstallerArgs(customPath) {
|
|
37
|
+
return customPath ? `--silent --installto "${customPath}"` : "--silent";
|
|
38
|
+
}
|
|
9
39
|
export const installCommand = new Command("install")
|
|
10
40
|
.description("Download and install Summer Engine")
|
|
11
41
|
.option("--path <dir>", "Custom install directory")
|
|
@@ -42,6 +72,7 @@ async function installMac(releases, customPath) {
|
|
|
42
72
|
console.error("No macOS release found.");
|
|
43
73
|
process.exit(1);
|
|
44
74
|
}
|
|
75
|
+
const dmgUrl = requireDownloadUrl(info.dmg_url, "macOS DMG");
|
|
45
76
|
console.log(`Latest version: ${info.version}`);
|
|
46
77
|
const destApp = customPath || "/Applications/Summer.app";
|
|
47
78
|
if (existsSync(destApp)) {
|
|
@@ -50,7 +81,18 @@ async function installMac(releases, customPath) {
|
|
|
50
81
|
}
|
|
51
82
|
const dmgPath = join(tmpdir(), `Summer-v${info.version}.dmg`);
|
|
52
83
|
console.log(`Downloading Summer Engine v${info.version} (~1 GB, includes bundled Git + runtime)...`);
|
|
53
|
-
|
|
84
|
+
try {
|
|
85
|
+
const { sha256 } = await downloadFile(dmgUrl, dmgPath);
|
|
86
|
+
verifyChecksum(sha256, info.dmg_sha256, "DMG");
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
try {
|
|
90
|
+
execSync(`rm -f "${dmgPath}"`, { stdio: "ignore" });
|
|
91
|
+
}
|
|
92
|
+
catch { /* best effort */ }
|
|
93
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
54
96
|
console.log("Mounting DMG...");
|
|
55
97
|
const mountOutput = execSync(`hdiutil attach "${dmgPath}" -nobrowse -noverify -noautoopen`, {
|
|
56
98
|
encoding: "utf-8",
|
|
@@ -88,13 +130,25 @@ async function installWindows(releases, customPath) {
|
|
|
88
130
|
console.error("No Windows release found.");
|
|
89
131
|
process.exit(1);
|
|
90
132
|
}
|
|
133
|
+
const exeUrl = requireDownloadUrl(info.url, "Windows installer");
|
|
91
134
|
console.log(`Latest version: ${info.version}`);
|
|
92
135
|
const exePath = join(tmpdir(), `Summer-v${info.version}.exe`);
|
|
93
136
|
console.log(`Downloading Summer Engine v${info.version} (~1 GB, includes bundled Git + runtime)...`);
|
|
94
|
-
|
|
137
|
+
try {
|
|
138
|
+
const { sha256 } = await downloadFile(exeUrl, exePath);
|
|
139
|
+
verifyChecksum(sha256, info.sha256, "installer");
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
try {
|
|
143
|
+
execSync(`del "${exePath}"`, { stdio: "ignore" });
|
|
144
|
+
}
|
|
145
|
+
catch { /* best effort */ }
|
|
146
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
95
149
|
console.log("Running installer...");
|
|
96
150
|
try {
|
|
97
|
-
execSync(`"${exePath}"
|
|
151
|
+
execSync(`"${exePath}" ${windowsInstallerArgs(customPath)}`, {
|
|
98
152
|
stdio: "inherit",
|
|
99
153
|
});
|
|
100
154
|
}
|
|
@@ -111,7 +165,7 @@ async function installWindows(releases, customPath) {
|
|
|
111
165
|
console.log(" summer login # Sign in to your account");
|
|
112
166
|
console.log(" summer run # Launch the engine");
|
|
113
167
|
}
|
|
114
|
-
async function downloadFile(url, dest) {
|
|
168
|
+
export async function downloadFile(url, dest) {
|
|
115
169
|
const res = await fetch(url);
|
|
116
170
|
if (!res.ok || !res.body) {
|
|
117
171
|
throw new Error(`Download failed: HTTP ${res.status}`);
|
|
@@ -121,9 +175,13 @@ async function downloadFile(url, dest) {
|
|
|
121
175
|
mkdirSync(dir, { recursive: true });
|
|
122
176
|
const fileStream = createWriteStream(dest);
|
|
123
177
|
const readable = Readable.fromWeb(res.body);
|
|
178
|
+
// Hash while streaming (the 'data' listener and the pipe both see every chunk
|
|
179
|
+
// in flowing mode), so we get integrity for free without a second read pass.
|
|
180
|
+
const hash = createHash("sha256");
|
|
124
181
|
let downloaded = 0;
|
|
125
182
|
const total = parseInt(res.headers.get("content-length") || "0", 10);
|
|
126
183
|
readable.on("data", (chunk) => {
|
|
184
|
+
hash.update(chunk);
|
|
127
185
|
downloaded += chunk.length;
|
|
128
186
|
if (total > 0) {
|
|
129
187
|
const pct = Math.round((downloaded / total) * 100);
|
|
@@ -132,4 +190,5 @@ async function downloadFile(url, dest) {
|
|
|
132
190
|
});
|
|
133
191
|
await pipeline(readable, fileStream);
|
|
134
192
|
process.stdout.write("\n");
|
|
193
|
+
return { sha256: hash.digest("hex") };
|
|
135
194
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type ProjectMemorySummary } from "../lib/project-memory.js";
|
|
3
|
+
interface ResolvedMemoryProject {
|
|
4
|
+
projectPath: string | null;
|
|
5
|
+
source: "option" | "engine" | "cwd" | "none";
|
|
6
|
+
reason?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const memoryCommand: Command;
|
|
9
|
+
export declare function resolveMemoryProject(projectOption?: string): Promise<ResolvedMemoryProject>;
|
|
10
|
+
export declare function findProjectRoot(startDir: string): string | null;
|
|
11
|
+
export declare function resolveMemoryFilePath(projectPath: string, requested: string): string;
|
|
12
|
+
export declare function formatMemorySummary(projectPath: string, summary: ProjectMemorySummary): string;
|
|
13
|
+
export declare function formatStatusMemoryLine(summary: ProjectMemorySummary): string;
|
|
14
|
+
export {};
|
|
@@ -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
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -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],
|