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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +24 -8
- package/.codex-plugin/plugin.json +19 -8
- package/.cursor-plugin/plugin.json +18 -7
- package/.factory-plugin/plugin.json +3 -3
- package/.opencode/INSTALL.md +1 -1
- package/AGENTS.md +6 -4
- package/CHANGELOG.md +46 -7
- package/CLAUDE.md +2 -2
- package/GEMINI.md +5 -6
- package/README.md +116 -109
- package/_persona/summer/SKILL.md +5 -6
- package/commands/gameskill.md +103 -0
- package/dist/bin/summer.js +17 -4
- package/dist/commands/cloud.d.ts +2 -0
- package/dist/commands/cloud.js +80 -0
- package/dist/commands/create.js +2 -0
- package/dist/commands/doctor.js +90 -1
- package/dist/commands/install.d.ts +17 -0
- package/dist/commands/install.js +63 -4
- package/dist/commands/login.js +4 -1
- package/dist/commands/logout.js +1 -1
- package/dist/commands/memory.d.ts +14 -0
- package/dist/commands/memory.js +196 -0
- package/dist/commands/open.js +1 -1
- package/dist/commands/plan.d.ts +2 -0
- package/dist/commands/plan.js +60 -0
- package/dist/commands/run.js +132 -35
- 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/auth.d.ts +2 -0
- package/dist/lib/auth.js +13 -0
- package/dist/lib/cloud/api.d.ts +79 -0
- package/dist/lib/cloud/api.js +107 -0
- package/dist/lib/cloud/apply.d.ts +32 -0
- package/dist/lib/cloud/apply.js +294 -0
- package/dist/lib/cloud/checkpoint.d.ts +26 -0
- package/dist/lib/cloud/checkpoint.js +142 -0
- package/dist/lib/cloud/conflicts.d.ts +55 -0
- package/dist/lib/cloud/conflicts.js +131 -0
- package/dist/lib/cloud/containment.d.ts +8 -0
- package/dist/lib/cloud/containment.js +52 -0
- package/dist/lib/cloud/diff.d.ts +39 -0
- package/dist/lib/cloud/diff.js +138 -0
- package/dist/lib/cloud/engine-bridge.d.ts +21 -0
- package/dist/lib/cloud/engine-bridge.js +105 -0
- package/dist/lib/cloud/hash-cache.d.ts +23 -0
- package/dist/lib/cloud/hash-cache.js +44 -0
- package/dist/lib/cloud/hash.d.ts +10 -0
- package/dist/lib/cloud/hash.js +111 -0
- package/dist/lib/cloud/http.d.ts +17 -0
- package/dist/lib/cloud/http.js +47 -0
- package/dist/lib/cloud/lock-path.d.ts +1 -0
- package/dist/lib/cloud/lock-path.js +5 -0
- package/dist/lib/cloud/lock.d.ts +14 -0
- package/dist/lib/cloud/lock.js +143 -0
- package/dist/lib/cloud/manifest.d.ts +6 -0
- package/dist/lib/cloud/manifest.js +41 -0
- package/dist/lib/cloud/paths.d.ts +20 -0
- package/dist/lib/cloud/paths.js +90 -0
- package/dist/lib/cloud/project-godot-merge.d.ts +32 -0
- package/dist/lib/cloud/project-godot-merge.js +185 -0
- package/dist/lib/cloud/rules.d.ts +39 -0
- package/dist/lib/cloud/rules.js +159 -0
- package/dist/lib/cloud/sync.d.ts +21 -0
- package/dist/lib/cloud/sync.js +518 -0
- package/dist/lib/cloud/transfer.d.ts +23 -0
- package/dist/lib/cloud/transfer.js +184 -0
- package/dist/lib/cloud/types.d.ts +62 -0
- package/dist/lib/cloud/validate-path.d.ts +23 -0
- package/dist/lib/cloud/validate-path.js +103 -0
- package/dist/lib/engine.d.ts +6 -0
- package/dist/lib/engine.js +36 -1
- package/dist/lib/game-task-plan.d.ts +42 -0
- package/dist/lib/game-task-plan.js +376 -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 +83 -0
- package/dist/mcp/tools/asset-tools.js +265 -45
- package/dist/mcp/tools/cloud-tools.d.ts +2 -0
- package/dist/mcp/tools/cloud-tools.js +42 -0
- package/dist/mcp/tools/debug-tools.js +32 -2
- package/dist/mcp/tools/generate-tools.js +101 -13
- package/dist/mcp/tools/project-tools.js +76 -21
- package/dist/mcp/tools/with-engine.d.ts +14 -0
- package/dist/mcp/tools/with-engine.js +52 -7
- package/gemini-extension.json +3 -3
- 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 +20 -7
- 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 +28 -6
- package/skills/3d-assets/environment-kit/SKILL.md +7 -2
- package/skills/3d-assets/organic-model/SKILL.md +5 -4
- package/skills/3d-assets/prop-model/SKILL.md +17 -9
- package/skills/3d-assets/vehicle-model/SKILL.md +5 -4
- package/skills/README.md +37 -30
- package/skills/ai-and-npcs/design-npc/SKILL.md +6 -6
- package/skills/animation/animation-tree/SKILL.md +4 -2
- package/skills/animation/facial-and-lipsync/SKILL.md +1 -1
- package/skills/animation/generate-motion/SKILL.md +26 -5
- package/skills/animation/procedural-animation/SKILL.md +2 -2
- package/skills/asset-pipeline/asset-strategy/SKILL.md +3 -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/mcpupdate/SKILL.md +134 -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.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/{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
|
+
}
|
package/dist/commands/open.js
CHANGED
|
@@ -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
|
-
"
|
|
31
|
+
"then run: summer run " + fullPath);
|
|
32
32
|
});
|
|
@@ -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
|
+
});
|
package/dist/commands/run.js
CHANGED
|
@@ -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
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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));
|
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],
|