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,376 @@
|
|
|
1
|
+
import { SKILL_REGISTRY } from "./skills-registry.js";
|
|
2
|
+
export const GAME_TASK_MODES = [
|
|
3
|
+
"auto",
|
|
4
|
+
"new-game",
|
|
5
|
+
"feature",
|
|
6
|
+
"asset",
|
|
7
|
+
"debug",
|
|
8
|
+
"playtest",
|
|
9
|
+
"polish",
|
|
10
|
+
"ship",
|
|
11
|
+
];
|
|
12
|
+
export const GAME_TASK_TARGETS = [
|
|
13
|
+
"auto",
|
|
14
|
+
"2d",
|
|
15
|
+
"3d",
|
|
16
|
+
"ui",
|
|
17
|
+
"audio",
|
|
18
|
+
"animation",
|
|
19
|
+
"level",
|
|
20
|
+
"npc",
|
|
21
|
+
"multiplayer",
|
|
22
|
+
];
|
|
23
|
+
export const ASSET_POLICIES = [
|
|
24
|
+
"reuse-first",
|
|
25
|
+
"ask-before-paid-generation",
|
|
26
|
+
"no-paid-generation",
|
|
27
|
+
"generate-when-clearly-needed",
|
|
28
|
+
];
|
|
29
|
+
export const VERIFICATION_LEVELS = ["none", "fast", "full"];
|
|
30
|
+
const SKILL_BY_NAME = new Map(SKILL_REGISTRY.map((skill) => [skill.name, skill]));
|
|
31
|
+
function includesAny(text, words) {
|
|
32
|
+
return words.some((word) => text.includes(word));
|
|
33
|
+
}
|
|
34
|
+
function inferMode(goal, requested) {
|
|
35
|
+
if (requested !== "auto")
|
|
36
|
+
return requested;
|
|
37
|
+
const text = goal.toLowerCase();
|
|
38
|
+
const gameGenre = includesAny(text, [
|
|
39
|
+
"shooter",
|
|
40
|
+
"platformer",
|
|
41
|
+
"roguelike",
|
|
42
|
+
"rpg",
|
|
43
|
+
"racing",
|
|
44
|
+
"puzzle game",
|
|
45
|
+
"survival",
|
|
46
|
+
"tower defense",
|
|
47
|
+
"metroidvania",
|
|
48
|
+
"arena",
|
|
49
|
+
]);
|
|
50
|
+
const buildVerb = /(^|\s)(make|build|create|start)\s/.test(text);
|
|
51
|
+
if (includesAny(text, ["crash", "bug", "error", "broken", "fix", "debug", "not working"]))
|
|
52
|
+
return "debug";
|
|
53
|
+
if (includesAny(text, ["export", "ship", "release", "build for", "deploy"]))
|
|
54
|
+
return "ship";
|
|
55
|
+
if (includesAny(text, ["playtest", "test the game", "feel", "tune"]))
|
|
56
|
+
return "playtest";
|
|
57
|
+
if (includesAny(text, ["asset", "model", "sprite", "texture", "sound", "music", "voice", "animation"]))
|
|
58
|
+
return "asset";
|
|
59
|
+
if (includesAny(text, ["new game", "start a game", "make a game", "prototype", "from scratch"]) || (buildVerb && gameGenre))
|
|
60
|
+
return "new-game";
|
|
61
|
+
if (includesAny(text, ["polish", "lighting", "performance", "juice", "ui pass"]))
|
|
62
|
+
return "polish";
|
|
63
|
+
return "feature";
|
|
64
|
+
}
|
|
65
|
+
function inferTarget(goal, requested) {
|
|
66
|
+
if (requested !== "auto")
|
|
67
|
+
return requested;
|
|
68
|
+
const text = goal.toLowerCase();
|
|
69
|
+
if (includesAny(text, ["3d", "model", "mesh", "glb", "character", "prop", "vehicle", "tree", "rock"]))
|
|
70
|
+
return "3d";
|
|
71
|
+
if (includesAny(text, ["2d", "sprite", "pixel", "portrait", "icon", "texture", "tileset", "skybox"]))
|
|
72
|
+
return "2d";
|
|
73
|
+
if (includesAny(text, ["sound", "sfx", "music", "audio", "voice", "dialogue", "ambient"]))
|
|
74
|
+
return "audio";
|
|
75
|
+
if (includesAny(text, ["animation", "animate", "idle", "walk", "run", "attack", "motion", "retarget"]))
|
|
76
|
+
return "animation";
|
|
77
|
+
if (includesAny(text, ["ui", "hud", "menu", "button", "inventory"]))
|
|
78
|
+
return "ui";
|
|
79
|
+
if (includesAny(text, ["level", "map", "dungeon", "arena", "room", "world"]))
|
|
80
|
+
return "level";
|
|
81
|
+
if (includesAny(text, ["npc", "enemy", "boss", "dialogue", "behavior"]))
|
|
82
|
+
return "npc";
|
|
83
|
+
if (includesAny(text, ["multiplayer", "network", "server", "host", "peer"]))
|
|
84
|
+
return "multiplayer";
|
|
85
|
+
return "general";
|
|
86
|
+
}
|
|
87
|
+
function addSkill(routes, name, why, weight) {
|
|
88
|
+
if (!SKILL_BY_NAME.has(name))
|
|
89
|
+
return;
|
|
90
|
+
const existing = routes.find((route) => route.name === name);
|
|
91
|
+
if (existing) {
|
|
92
|
+
if (weight > existing.weight) {
|
|
93
|
+
existing.weight = weight;
|
|
94
|
+
existing.why = why;
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
routes.push({ name, why, weight });
|
|
99
|
+
}
|
|
100
|
+
function inferSkills(mode, target, goal) {
|
|
101
|
+
const text = goal.toLowerCase();
|
|
102
|
+
const routes = [];
|
|
103
|
+
const animationIntent = includesAny(text, [
|
|
104
|
+
"animated",
|
|
105
|
+
"animation",
|
|
106
|
+
"animate",
|
|
107
|
+
"idle",
|
|
108
|
+
"walk",
|
|
109
|
+
"run",
|
|
110
|
+
"jump",
|
|
111
|
+
"attack",
|
|
112
|
+
"motion",
|
|
113
|
+
"mocap",
|
|
114
|
+
"locomotion",
|
|
115
|
+
"animationtree",
|
|
116
|
+
"state machine",
|
|
117
|
+
]);
|
|
118
|
+
const assetIntent = mode === "asset" ||
|
|
119
|
+
includesAny(text, [
|
|
120
|
+
"asset",
|
|
121
|
+
"model",
|
|
122
|
+
"mesh",
|
|
123
|
+
"glb",
|
|
124
|
+
"sprite",
|
|
125
|
+
"texture",
|
|
126
|
+
"sound",
|
|
127
|
+
"sfx",
|
|
128
|
+
"music",
|
|
129
|
+
"voice",
|
|
130
|
+
"animation clip",
|
|
131
|
+
]);
|
|
132
|
+
addSkill(routes, "using-summer", "Baseline workflow for building through Summer CLI/MCP.", 100);
|
|
133
|
+
if (mode === "debug") {
|
|
134
|
+
addSkill(routes, "debug", "Primary runtime/script/diagnostics loop.", 300);
|
|
135
|
+
addSkill(routes, "investigating-bugs", "Systematic bug investigation workflow.", 220);
|
|
136
|
+
}
|
|
137
|
+
if (mode === "playtest") {
|
|
138
|
+
addSkill(routes, "play", "Run, observe, and report runtime state.", 260);
|
|
139
|
+
addSkill(routes, "playtesting-a-feature", "Feature-level playtest loop.", 220);
|
|
140
|
+
addSkill(routes, "debugging-game-feel", "Tune feel problems after runtime verification.", 160);
|
|
141
|
+
}
|
|
142
|
+
if (mode === "new-game") {
|
|
143
|
+
addSkill(routes, "new-project", "Create/open the project if needed.", 260);
|
|
144
|
+
addSkill(routes, "brainstorm-game", "Lock a small playable brief before building.", 240);
|
|
145
|
+
addSkill(routes, "make-game", "Build one narrow playable prototype end-to-end.", 300);
|
|
146
|
+
}
|
|
147
|
+
if (mode === "ship") {
|
|
148
|
+
addSkill(routes, "export-and-ship", "Export and release readiness workflow.", 280);
|
|
149
|
+
addSkill(routes, "tune-performance", "Performance pass before shipping.", 180);
|
|
150
|
+
}
|
|
151
|
+
if (mode === "polish") {
|
|
152
|
+
addSkill(routes, "art-direction", "Visual consistency and taste pass.", 200);
|
|
153
|
+
addSkill(routes, "3d-lighting", "Lighting/world environment pass for 3D scenes.", 190);
|
|
154
|
+
addSkill(routes, "game-feel", "Screen shake, hit-stop, juice, and responsiveness.", 180);
|
|
155
|
+
addSkill(routes, "tune-performance", "Frame budget and bottleneck pass.", 170);
|
|
156
|
+
}
|
|
157
|
+
if (assetIntent) {
|
|
158
|
+
addSkill(routes, "asset-strategy", "Choose reuse/import/generate before spending credits.", 260);
|
|
159
|
+
}
|
|
160
|
+
if (target === "3d" && assetIntent) {
|
|
161
|
+
if (includesAny(text, ["character", "player", "npc", "enemy", "boss", "humanoid", "rig"])) {
|
|
162
|
+
addSkill(routes, "character-model", "Humanoid T-pose, mesh, rig, and handoff to animation.", 300);
|
|
163
|
+
}
|
|
164
|
+
else if (includesAny(text, ["vehicle", "car", "ship", "spaceship", "mech", "tank", "bike"])) {
|
|
165
|
+
addSkill(routes, "vehicle-model", "Hard-surface vehicle generation and scene wiring.", 280);
|
|
166
|
+
}
|
|
167
|
+
else if (includesAny(text, ["tree", "rock", "mushroom", "foliage", "plant", "crystal"])) {
|
|
168
|
+
addSkill(routes, "organic-model", "Fast organic 3D asset generation and scatter guidance.", 270);
|
|
169
|
+
}
|
|
170
|
+
else if (includesAny(text, ["kit", "modular", "wall", "floor", "door", "pillar", "dungeon"])) {
|
|
171
|
+
addSkill(routes, "environment-kit", "Style-locked modular kit workflow.", 270);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
addSkill(routes, "prop-model", "Single static 3D prop workflow.", 270);
|
|
175
|
+
}
|
|
176
|
+
addSkill(routes, "scene-composition", "Instantiate/import models into robust scene structure.", 150);
|
|
177
|
+
if (animationIntent && includesAny(text, ["character", "player", "npc", "enemy", "boss", "humanoid", "rig"])) {
|
|
178
|
+
addSkill(routes, "generate-motion", "Generate idle/walk/run/jump/attack clips for the rigged character.", 275);
|
|
179
|
+
addSkill(routes, "animation-tree", "Wire generated clips into playback/state-machine behavior.", 210);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (target === "3d" && !assetIntent) {
|
|
183
|
+
addSkill(routes, "scene-composition", "Build robust 3D scene structure.", 220);
|
|
184
|
+
addSkill(routes, "3d-lighting", "Give the scene readable camera, light, and environment defaults.", 180);
|
|
185
|
+
}
|
|
186
|
+
if (target === "2d" && assetIntent) {
|
|
187
|
+
if (includesAny(text, ["sprite sheet", "spritesheet", "run cycle", "frame grid"])) {
|
|
188
|
+
addSkill(routes, "sprite-sheet", "Frame grid/sprite animation workflow.", 280);
|
|
189
|
+
}
|
|
190
|
+
else if (includesAny(text, ["texture", "tile", "seamless", "wall", "floor"])) {
|
|
191
|
+
addSkill(routes, "tileable-texture", "Seamless texture generation and material use.", 260);
|
|
192
|
+
}
|
|
193
|
+
else if (includesAny(text, ["portrait", "dialogue", "bust"])) {
|
|
194
|
+
addSkill(routes, "character-portrait", "Polished dialogue portrait workflow.", 260);
|
|
195
|
+
}
|
|
196
|
+
else if (includesAny(text, ["ui", "hud", "button", "icon"])) {
|
|
197
|
+
addSkill(routes, "ui-graphics", "HUD/icon/UI image asset workflow.", 260);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
addSkill(routes, "concept-art", "Explore visual direction before final assets.", 220);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (target === "audio" && assetIntent) {
|
|
204
|
+
if (includesAny(text, ["music", "theme", "track", "boss"])) {
|
|
205
|
+
addSkill(routes, "music-track", "Music generation workflow.", 280);
|
|
206
|
+
}
|
|
207
|
+
else if (includesAny(text, ["ambient", "ambience", "atmosphere"])) {
|
|
208
|
+
addSkill(routes, "ambient-bed", "Looping ambience workflow.", 260);
|
|
209
|
+
}
|
|
210
|
+
else if (includesAny(text, ["voice", "dialogue", "vo", "line"])) {
|
|
211
|
+
addSkill(routes, "voice-line", "Voice/dialogue generation workflow.", 260);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
addSkill(routes, "sound-effect", "One-shot SFX workflow.", 260);
|
|
215
|
+
}
|
|
216
|
+
addSkill(routes, "audio-direction", "Keep audio style coherent across the game.", 150);
|
|
217
|
+
}
|
|
218
|
+
if (target === "animation") {
|
|
219
|
+
addSkill(routes, "generate-motion", "Generate curated humanoid motion on a rigged character.", 280);
|
|
220
|
+
if (includesAny(text, ["blend", "state machine", "animationtree", "tree"])) {
|
|
221
|
+
addSkill(routes, "animation-tree", "Wire clips into an AnimationTree/state machine.", 260);
|
|
222
|
+
}
|
|
223
|
+
if (includesAny(text, ["retarget", "reuse", "another character"])) {
|
|
224
|
+
addSkill(routes, "retarget", "Reuse motion across compatible rigs.", 260);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (target === "level") {
|
|
228
|
+
addSkill(routes, "design-level", "Build level structure, pacing, landmarks, and routes.", 270);
|
|
229
|
+
addSkill(routes, "scene-to-level", "Convert scene goals into playable level layout.", 210);
|
|
230
|
+
addSkill(routes, "scene-composition", "Use robust node/scene composition patterns.", 180);
|
|
231
|
+
}
|
|
232
|
+
if (target === "npc") {
|
|
233
|
+
addSkill(routes, "design-npc", "NPC behavior/dialogue integration workflow.", 270);
|
|
234
|
+
addSkill(routes, "gdscript-patterns", "Typed scripts/signals for NPC behavior.", 150);
|
|
235
|
+
}
|
|
236
|
+
if (target === "ui") {
|
|
237
|
+
addSkill(routes, "ui-basics", "HUD/menu/control composition workflow.", 270);
|
|
238
|
+
addSkill(routes, "gdscript-patterns", "Typed signal and state patterns for UI code.", 150);
|
|
239
|
+
}
|
|
240
|
+
if (target === "multiplayer") {
|
|
241
|
+
addSkill(routes, "setup-multiplayer", "Choose networking model before implementation.", 270);
|
|
242
|
+
addSkill(routes, "host-authoritative-state", "Server-authoritative state model.", 220);
|
|
243
|
+
}
|
|
244
|
+
if (includesAny(text, ["player controller", "fps", "first person", "wasd", "camera"])) {
|
|
245
|
+
addSkill(routes, "fps-controller", "Player controller, camera, collision, and input bindings.", 240);
|
|
246
|
+
}
|
|
247
|
+
if (includesAny(text, ["shooter", "arena shooter", "fps"])) {
|
|
248
|
+
addSkill(routes, "fps-controller", "Shooter input/camera/controller foundation.", 245);
|
|
249
|
+
addSkill(routes, "design-mechanic", "Implement the core shooting/combat loop.", 230);
|
|
250
|
+
addSkill(routes, "design-level", "Shape the arena, spawn space, cover, and pacing.", 210);
|
|
251
|
+
}
|
|
252
|
+
addSkill(routes, "gdscript-patterns", "Use direct host file edits for scripts, then verify through MCP.", 90);
|
|
253
|
+
addSkill(routes, "verification-before-completion", "Do not finish without a clean verification story.", 80);
|
|
254
|
+
return routes
|
|
255
|
+
.sort((a, b) => b.weight - a.weight)
|
|
256
|
+
.slice(0, 7)
|
|
257
|
+
.map((route) => {
|
|
258
|
+
const skill = SKILL_BY_NAME.get(route.name);
|
|
259
|
+
return {
|
|
260
|
+
id: `summer:${skill.category}/${skill.name}`,
|
|
261
|
+
name: skill.name,
|
|
262
|
+
category: skill.category,
|
|
263
|
+
why: route.why,
|
|
264
|
+
requiresMcpTools: skill.requiresMcpTools,
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function buildMcpToolPlan(mode, target, assetPolicy, verification, goal = "") {
|
|
269
|
+
const text = goal.toLowerCase();
|
|
270
|
+
const animationIntent = includesAny(text, [
|
|
271
|
+
"animated",
|
|
272
|
+
"animation",
|
|
273
|
+
"animate",
|
|
274
|
+
"idle",
|
|
275
|
+
"walk",
|
|
276
|
+
"run",
|
|
277
|
+
"jump",
|
|
278
|
+
"attack",
|
|
279
|
+
"motion",
|
|
280
|
+
"mocap",
|
|
281
|
+
"locomotion",
|
|
282
|
+
]);
|
|
283
|
+
const start = ["summer_start_game_task", "summer_get_project_context", "summer_get_agent_playbook"];
|
|
284
|
+
const mutate = ["summer_open_main_scene", "summer_get_scene_tree", "summer_add_node", "summer_set_prop", "summer_save_scene"];
|
|
285
|
+
const assets = [];
|
|
286
|
+
const verify = [];
|
|
287
|
+
if (mode === "debug") {
|
|
288
|
+
start.push("summer_get_diagnostics", "summer_get_script_errors", "summer_get_debugger_errors");
|
|
289
|
+
}
|
|
290
|
+
if (target === "animation") {
|
|
291
|
+
assets.push("summer_search_assets", "summer_get_asset", "summer_generate_motion");
|
|
292
|
+
}
|
|
293
|
+
else if (target === "3d" || target === "2d" || target === "audio") {
|
|
294
|
+
assets.push("summer_search_assets", "summer_list_my_assets", "summer_get_asset", "summer_import_asset_by_id");
|
|
295
|
+
if (assetPolicy !== "no-paid-generation") {
|
|
296
|
+
if (target === "3d")
|
|
297
|
+
assets.push("summer_generate_3d");
|
|
298
|
+
if (target === "2d")
|
|
299
|
+
assets.push("summer_generate_image");
|
|
300
|
+
if (target === "audio")
|
|
301
|
+
assets.push("summer_generate_audio");
|
|
302
|
+
if (target === "3d" && animationIntent)
|
|
303
|
+
assets.push("summer_generate_motion");
|
|
304
|
+
assets.push("summer_check_job");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else if (assetPolicy !== "no-paid-generation") {
|
|
308
|
+
assets.push("summer_search_assets", "summer_import_asset");
|
|
309
|
+
}
|
|
310
|
+
if (verification !== "none") {
|
|
311
|
+
verify.push("summer_get_script_errors", "summer_get_diagnostics");
|
|
312
|
+
}
|
|
313
|
+
if (verification === "full") {
|
|
314
|
+
verify.push("summer_clear_console", "summer_play", "summer_get_debugger_errors", "summer_stop");
|
|
315
|
+
}
|
|
316
|
+
return { start, mutate, assets, verify };
|
|
317
|
+
}
|
|
318
|
+
function buildUserGates(mode, assetPolicy) {
|
|
319
|
+
const gates = [
|
|
320
|
+
"Ask before destructive scene changes, deleting assets, or changing locked .summer memory.",
|
|
321
|
+
];
|
|
322
|
+
if (assetPolicy !== "generate-when-clearly-needed") {
|
|
323
|
+
gates.push("Ask before paid generation, rigging, video, or large batch asset creation.");
|
|
324
|
+
}
|
|
325
|
+
if (mode === "new-game") {
|
|
326
|
+
gates.push("Lock one narrow playable loop before expanding scope.");
|
|
327
|
+
}
|
|
328
|
+
if (mode === "asset") {
|
|
329
|
+
gates.push("For generated 3D characters, show the unrigged preview before paid rigging.");
|
|
330
|
+
}
|
|
331
|
+
return gates;
|
|
332
|
+
}
|
|
333
|
+
export function buildGameTaskPlan(options) {
|
|
334
|
+
const goal = options.goal.trim();
|
|
335
|
+
if (!goal)
|
|
336
|
+
throw new Error("goal is required");
|
|
337
|
+
const assetPolicy = options.assetPolicy ?? "ask-before-paid-generation";
|
|
338
|
+
const verification = options.verification ?? "full";
|
|
339
|
+
const mode = inferMode(goal, options.mode ?? "auto");
|
|
340
|
+
const target = inferTarget(goal, options.target ?? "auto");
|
|
341
|
+
return {
|
|
342
|
+
goal,
|
|
343
|
+
mode,
|
|
344
|
+
target,
|
|
345
|
+
assetPolicy,
|
|
346
|
+
verification,
|
|
347
|
+
principles: [
|
|
348
|
+
"The agent should build a playable game slice, not a static demo.",
|
|
349
|
+
"Use host file edits for code/data/docs; use MCP for live scene state, imports, inspector values, play, and diagnostics.",
|
|
350
|
+
"Reuse/import assets before paid generation unless the user explicitly wants custom output.",
|
|
351
|
+
"Prefer exact IDs and structured tool results over search guesses after asset creation.",
|
|
352
|
+
"Verify through the engine before claiming completion.",
|
|
353
|
+
],
|
|
354
|
+
recommendedSkills: inferSkills(mode, target, goal),
|
|
355
|
+
mcpToolPlan: buildMcpToolPlan(mode, target, assetPolicy, verification, goal),
|
|
356
|
+
hostFileWork: [
|
|
357
|
+
"Write and edit .gd/.cs scripts directly with host file tools.",
|
|
358
|
+
"Use direct file edits for JSON, markdown, project notes, and simple resources.",
|
|
359
|
+
"Read relevant .summer memory files when project context reports them.",
|
|
360
|
+
],
|
|
361
|
+
userGates: buildUserGates(mode, assetPolicy),
|
|
362
|
+
nextAgentSteps: [
|
|
363
|
+
"Call summer_get_project_context and read relevant .summer memory.",
|
|
364
|
+
"Open the main scene if no scene is active.",
|
|
365
|
+
"Load the top recommended skill before mutating the project.",
|
|
366
|
+
"Make the smallest coherent change that advances the playable loop.",
|
|
367
|
+
"Save, run diagnostics, and playtest according to the verification level.",
|
|
368
|
+
],
|
|
369
|
+
antiPatterns: [
|
|
370
|
+
"Do not hand-edit .tscn scene structure when Summer MCP can mutate the live scene.",
|
|
371
|
+
"Do not generate assets before checking library/user assets.",
|
|
372
|
+
"Do not search by name for an asset immediately after generation; use the returned asset ID.",
|
|
373
|
+
"Do not finish with only code changes when the request affects gameplay; run engine verification.",
|
|
374
|
+
],
|
|
375
|
+
};
|
|
376
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface ProjectMemoryFileSummary {
|
|
2
|
+
path: string;
|
|
3
|
+
kind: string;
|
|
4
|
+
title: string | null;
|
|
5
|
+
modifiedTime: string;
|
|
6
|
+
bytes: number;
|
|
7
|
+
locked: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ProjectMemorySummary {
|
|
10
|
+
present: boolean;
|
|
11
|
+
root: ".summer";
|
|
12
|
+
reason?: string;
|
|
13
|
+
canonical: {
|
|
14
|
+
gameSoul: ProjectMemoryFileSummary | null;
|
|
15
|
+
artBible: ProjectMemoryFileSummary | null;
|
|
16
|
+
audioBible: ProjectMemoryFileSummary | null;
|
|
17
|
+
buildPlan: ProjectMemoryFileSummary | null;
|
|
18
|
+
legacyVoiceCast: ProjectMemoryFileSummary | null;
|
|
19
|
+
};
|
|
20
|
+
structured: {
|
|
21
|
+
present: boolean;
|
|
22
|
+
indexPresent: boolean;
|
|
23
|
+
fileCount: number;
|
|
24
|
+
lockedCount: number;
|
|
25
|
+
files: ProjectMemoryFileSummary[];
|
|
26
|
+
truncated: boolean;
|
|
27
|
+
};
|
|
28
|
+
files: ProjectMemoryFileSummary[];
|
|
29
|
+
guidance: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function getProjectMemorySummary(projectPath: string | null | undefined, options?: {
|
|
32
|
+
maxFiles?: number;
|
|
33
|
+
}): ProjectMemorySummary;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { closeSync, existsSync, openSync, readSync, readdirSync, statSync, } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
const CANONICAL_FILES = [
|
|
4
|
+
{ key: "gameSoul", relativePath: ".summer/GameSoul.md", kind: "brief" },
|
|
5
|
+
{ key: "artBible", relativePath: ".summer/art-bible.md", kind: "art" },
|
|
6
|
+
{ key: "audioBible", relativePath: ".summer/audio-bible.md", kind: "audio" },
|
|
7
|
+
{ key: "buildPlan", relativePath: ".summer/build-plan.md", kind: "build-plan" },
|
|
8
|
+
{ key: "legacyVoiceCast", relativePath: ".summer/voice-cast.md", kind: "legacy-casting" },
|
|
9
|
+
];
|
|
10
|
+
const SKIPPED_DIRS = new Set(["skills", "local"]);
|
|
11
|
+
const DEFAULT_MAX_FILES = 30;
|
|
12
|
+
const MAX_TITLE_BYTES = 16_384;
|
|
13
|
+
export function getProjectMemorySummary(projectPath, options = {}) {
|
|
14
|
+
const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
|
|
15
|
+
const empty = createEmptySummary();
|
|
16
|
+
if (!projectPath) {
|
|
17
|
+
return {
|
|
18
|
+
...empty,
|
|
19
|
+
reason: "Project path is not available from the running engine.",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const summerDir = join(projectPath, ".summer");
|
|
23
|
+
if (!existsSync(summerDir)) {
|
|
24
|
+
return {
|
|
25
|
+
...empty,
|
|
26
|
+
reason: "No .summer directory found yet.",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const canonical = createCanonicalSummary(projectPath);
|
|
30
|
+
const allFiles = scanMarkdownFiles(projectPath, summerDir, maxFiles);
|
|
31
|
+
const memoryDir = join(projectPath, ".summer", "memory");
|
|
32
|
+
const structuredFiles = existsSync(memoryDir)
|
|
33
|
+
? scanMarkdownFiles(projectPath, memoryDir, maxFiles)
|
|
34
|
+
: [];
|
|
35
|
+
const indexPath = join(projectPath, ".summer", "memory", "index.json");
|
|
36
|
+
return {
|
|
37
|
+
present: true,
|
|
38
|
+
root: ".summer",
|
|
39
|
+
canonical,
|
|
40
|
+
structured: {
|
|
41
|
+
present: existsSync(memoryDir),
|
|
42
|
+
indexPresent: existsSync(indexPath),
|
|
43
|
+
fileCount: Math.min(structuredFiles.length, maxFiles),
|
|
44
|
+
lockedCount: structuredFiles.filter((file) => file.locked).length,
|
|
45
|
+
files: structuredFiles.slice(0, maxFiles),
|
|
46
|
+
truncated: structuredFiles.length > maxFiles,
|
|
47
|
+
},
|
|
48
|
+
files: allFiles.slice(0, maxFiles),
|
|
49
|
+
guidance: "Read relevant .summer Markdown before creative, audio, dialogue, level, or character work. Ask before writing .summer files. Changing priority: locked memory requires explicit user confirmation.",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function createEmptySummary() {
|
|
53
|
+
return {
|
|
54
|
+
present: false,
|
|
55
|
+
root: ".summer",
|
|
56
|
+
canonical: {
|
|
57
|
+
gameSoul: null,
|
|
58
|
+
artBible: null,
|
|
59
|
+
audioBible: null,
|
|
60
|
+
buildPlan: null,
|
|
61
|
+
legacyVoiceCast: null,
|
|
62
|
+
},
|
|
63
|
+
structured: {
|
|
64
|
+
present: false,
|
|
65
|
+
indexPresent: false,
|
|
66
|
+
fileCount: 0,
|
|
67
|
+
lockedCount: 0,
|
|
68
|
+
files: [],
|
|
69
|
+
truncated: false,
|
|
70
|
+
},
|
|
71
|
+
files: [],
|
|
72
|
+
guidance: "Create .summer/GameSoul.md for the project brief. Use .summer/memory/ for locked facts such as character voice IDs, world canon, and provider bindings.",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function createCanonicalSummary(projectPath) {
|
|
76
|
+
const canonical = createEmptySummary().canonical;
|
|
77
|
+
for (const spec of CANONICAL_FILES) {
|
|
78
|
+
const absolutePath = join(projectPath, ...spec.relativePath.split("/"));
|
|
79
|
+
canonical[spec.key] = summarizeFile(projectPath, absolutePath, spec.kind);
|
|
80
|
+
}
|
|
81
|
+
return canonical;
|
|
82
|
+
}
|
|
83
|
+
function scanMarkdownFiles(projectPath, startDir, maxFiles) {
|
|
84
|
+
const files = [];
|
|
85
|
+
walkSummerDir(projectPath, startDir, startDir, files, maxFiles + 1, 0);
|
|
86
|
+
return files;
|
|
87
|
+
}
|
|
88
|
+
function walkSummerDir(projectPath, summerDir, currentDir, files, maxFiles, depth) {
|
|
89
|
+
if (files.length >= maxFiles || depth > 5)
|
|
90
|
+
return;
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = readdirSync(currentDir).sort();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (files.length >= maxFiles)
|
|
100
|
+
return;
|
|
101
|
+
const absolutePath = join(currentDir, entry);
|
|
102
|
+
let stat;
|
|
103
|
+
try {
|
|
104
|
+
stat = statSync(absolutePath);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (stat.isDirectory()) {
|
|
110
|
+
const relativeToSummer = relative(summerDir, absolutePath);
|
|
111
|
+
if (depth === 0 && SKIPPED_DIRS.has(relativeToSummer))
|
|
112
|
+
continue;
|
|
113
|
+
walkSummerDir(projectPath, summerDir, absolutePath, files, maxFiles, depth + 1);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!stat.isFile() || !entry.endsWith(".md"))
|
|
117
|
+
continue;
|
|
118
|
+
const summary = summarizeFile(projectPath, absolutePath, kindForPath(projectPath, absolutePath));
|
|
119
|
+
if (summary)
|
|
120
|
+
files.push(summary);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function summarizeFile(projectPath, absolutePath, kind) {
|
|
124
|
+
if (!existsSync(absolutePath))
|
|
125
|
+
return null;
|
|
126
|
+
let stat;
|
|
127
|
+
try {
|
|
128
|
+
stat = statSync(absolutePath);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (!stat.isFile())
|
|
134
|
+
return null;
|
|
135
|
+
const preview = readPreview(absolutePath);
|
|
136
|
+
return {
|
|
137
|
+
path: normalizePath(relative(projectPath, absolutePath)),
|
|
138
|
+
kind,
|
|
139
|
+
title: extractTitle(preview),
|
|
140
|
+
modifiedTime: stat.mtime.toISOString(),
|
|
141
|
+
bytes: stat.size,
|
|
142
|
+
locked: isLockedMemory(preview),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function readPreview(absolutePath) {
|
|
146
|
+
let fd = null;
|
|
147
|
+
try {
|
|
148
|
+
fd = openSync(absolutePath, "r");
|
|
149
|
+
const buffer = Buffer.alloc(MAX_TITLE_BYTES);
|
|
150
|
+
const bytesRead = readSync(fd, buffer, 0, MAX_TITLE_BYTES, 0);
|
|
151
|
+
return buffer.toString("utf-8", 0, bytesRead);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (fd !== null) {
|
|
158
|
+
try {
|
|
159
|
+
closeSync(fd);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Best-effort summary only.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function extractTitle(markdown) {
|
|
168
|
+
for (const line of markdown.split(/\r?\n/)) {
|
|
169
|
+
const heading = line.match(/^#\s+(.+?)\s*$/);
|
|
170
|
+
if (heading)
|
|
171
|
+
return heading[1];
|
|
172
|
+
}
|
|
173
|
+
for (const line of markdown.split(/\r?\n/)) {
|
|
174
|
+
const trimmed = line.trim();
|
|
175
|
+
if (trimmed && trimmed !== "---" && !trimmed.includes(":")) {
|
|
176
|
+
return trimmed.slice(0, 120);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
function isLockedMemory(markdown) {
|
|
182
|
+
return (/^priority:\s*locked\s*$/im.test(markdown) ||
|
|
183
|
+
/^stable:\s*true\s*$/im.test(markdown) ||
|
|
184
|
+
/\|\s*locked\s*\|/i.test(markdown));
|
|
185
|
+
}
|
|
186
|
+
function kindForPath(projectPath, absolutePath) {
|
|
187
|
+
const path = normalizePath(relative(projectPath, absolutePath));
|
|
188
|
+
if (path === ".summer/GameSoul.md")
|
|
189
|
+
return "brief";
|
|
190
|
+
if (path === ".summer/art-bible.md")
|
|
191
|
+
return "art";
|
|
192
|
+
if (path === ".summer/audio-bible.md")
|
|
193
|
+
return "audio";
|
|
194
|
+
if (path === ".summer/build-plan.md")
|
|
195
|
+
return "build-plan";
|
|
196
|
+
if (path === ".summer/voice-cast.md")
|
|
197
|
+
return "legacy-casting";
|
|
198
|
+
if (path.startsWith(".summer/memory/casting/"))
|
|
199
|
+
return "casting";
|
|
200
|
+
if (path.startsWith(".summer/memory/characters/"))
|
|
201
|
+
return "character";
|
|
202
|
+
if (path.startsWith(".summer/memory/world/"))
|
|
203
|
+
return "world";
|
|
204
|
+
if (path.startsWith(".summer/memory/systems/"))
|
|
205
|
+
return "system";
|
|
206
|
+
if (path.startsWith(".summer/memory/decisions/"))
|
|
207
|
+
return "decision";
|
|
208
|
+
if (path.startsWith(".summer/memory/conflicts/"))
|
|
209
|
+
return "conflict";
|
|
210
|
+
if (path.startsWith(".summer/mechanics/"))
|
|
211
|
+
return "mechanic";
|
|
212
|
+
if (path.startsWith(".summer/levels/"))
|
|
213
|
+
return "level";
|
|
214
|
+
if (path.startsWith(".summer/npcs/"))
|
|
215
|
+
return "npc";
|
|
216
|
+
return "note";
|
|
217
|
+
}
|
|
218
|
+
function normalizePath(path) {
|
|
219
|
+
return path.split(sep).join("/");
|
|
220
|
+
}
|