summer-engine 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -24
  3. package/dist/bin/summer.js +4 -0
  4. package/dist/commands/doctor.d.ts +26 -0
  5. package/dist/commands/doctor.js +248 -0
  6. package/dist/commands/doctor.test.d.ts +1 -0
  7. package/dist/commands/doctor.test.js +29 -0
  8. package/dist/commands/mcp.js +49 -0
  9. package/dist/commands/setup.d.ts +2 -0
  10. package/dist/commands/setup.js +128 -0
  11. package/dist/commands/skills.js +254 -109
  12. package/dist/lib/agent-config.d.ts +39 -0
  13. package/dist/lib/agent-config.js +284 -0
  14. package/dist/lib/agent-config.test.d.ts +1 -0
  15. package/dist/lib/agent-config.test.js +80 -0
  16. package/dist/lib/api-client.d.ts +19 -2
  17. package/dist/lib/api-client.js +129 -14
  18. package/dist/lib/skills-registry.d.ts +75 -0
  19. package/dist/lib/skills-registry.js +143 -0
  20. package/dist/mcp/tools/project-tools.js +100 -23
  21. package/dist/mcp/tools/with-engine.js +3 -53
  22. package/package.json +5 -2
  23. package/skills/README.md +96 -0
  24. package/skills/_meta/skill-create/SKILL.md +143 -0
  25. package/skills/_meta/skill-improve/SKILL.md +89 -0
  26. package/skills/_meta/skill-test/SKILL.md +86 -0
  27. package/skills/_shared/collaborative-protocol.md +50 -0
  28. package/skills/_shared/gd-style.md +121 -0
  29. package/skills/_shared/godot-version.md +32 -0
  30. package/skills/_shared/mcp-tools-reference.md +133 -0
  31. package/skills/_shared/template-registry.md +40 -0
  32. package/skills/_tests/runner.md +90 -0
  33. package/skills/_tests/specs/3d-lighting.md +19 -0
  34. package/skills/_tests/specs/asset-strategy.md +19 -0
  35. package/skills/_tests/specs/fps-controller.md +100 -0
  36. package/skills/_tests/specs/gdscript-patterns.md +19 -0
  37. package/skills/_tests/specs/make-game.md +19 -0
  38. package/skills/_tests/specs/scene-composition.md +19 -0
  39. package/skills/_tests/specs/ui-basics.md +19 -0
  40. package/skills/{asset-strategy → asset-pipeline/asset-strategy}/SKILL.md +4 -5
  41. package/skills/catalog.yaml +636 -0
  42. package/skills/{fps-controller → character-controllers/fps-controller}/SKILL.md +6 -5
  43. package/skills/{3d-lighting → rendering-and-lighting/3d-lighting}/SKILL.md +5 -5
  44. package/skills/{make-game → scene-and-project/make-game}/SKILL.md +8 -8
  45. package/skills/{scene-composition → scene-and-project/scene-composition}/SKILL.md +6 -6
  46. package/skills/{gdscript-patterns → scripting-patterns/gdscript-patterns}/SKILL.md +7 -7
  47. package/skills/{ui-basics → ui-and-ux/ui-basics}/SKILL.md +5 -5
  48. /package/skills/{gdscript-patterns → scripting-patterns/gdscript-patterns}/reference.md +0 -0
@@ -0,0 +1,143 @@
1
+ export const AGENT_CLIENTS = [
2
+ "summer",
3
+ "codex",
4
+ "claude-code",
5
+ "cursor",
6
+ "windsurf",
7
+ ];
8
+ export const SKILL_CATEGORIES = [
9
+ "_meta",
10
+ "character-controllers",
11
+ "gameplay-mechanics",
12
+ "scripting-patterns",
13
+ "scene-and-project",
14
+ "rendering-and-lighting",
15
+ "shaders",
16
+ "visual-effects",
17
+ "post-processing",
18
+ "animation",
19
+ "audio",
20
+ "physics",
21
+ "multiplayer-and-networking",
22
+ "ai-and-npcs",
23
+ "level-design",
24
+ "performance",
25
+ "ui-and-ux",
26
+ "asset-pipeline",
27
+ "input-and-controls",
28
+ "debugging",
29
+ "deployment",
30
+ ];
31
+ const ALL_CLIENTS = AGENT_CLIENTS;
32
+ /**
33
+ * Skills with `status: HAVE` in catalog.yaml. The catalog is the planning source
34
+ * of truth (~85 skills); this TS registry is the install-time list (only skills
35
+ * with shippable content). When a NEXT skill ships, mirror it here.
36
+ */
37
+ export const SKILL_REGISTRY = [
38
+ {
39
+ name: "gdscript-patterns",
40
+ category: "scripting-patterns",
41
+ public: true,
42
+ clients: ALL_CLIENTS,
43
+ recommended: true,
44
+ requiresMcpTools: [
45
+ "summer_get_script_errors",
46
+ ],
47
+ testScenario: "Write a typed GDScript with a signal, attach it to a node, connect the signal, and verify script errors.",
48
+ },
49
+ {
50
+ name: "scene-composition",
51
+ category: "scene-and-project",
52
+ public: true,
53
+ clients: ALL_CLIENTS,
54
+ recommended: true,
55
+ requiresMcpTools: [
56
+ "summer_create_scene",
57
+ "summer_open_scene",
58
+ "summer_get_scene_tree",
59
+ "summer_add_node",
60
+ "summer_instantiate_scene",
61
+ "summer_save_scene",
62
+ ],
63
+ testScenario: "Build a small reusable player scene, instantiate it in a main scene, and save both scenes through Summer tools.",
64
+ },
65
+ {
66
+ name: "fps-controller",
67
+ category: "character-controllers",
68
+ public: true,
69
+ clients: ALL_CLIENTS,
70
+ recommended: true,
71
+ requiresMcpTools: [
72
+ "summer_add_node",
73
+ "summer_set_prop",
74
+ "summer_set_resource_property",
75
+ "summer_input_map_bind",
76
+ ],
77
+ testScenario: "Create a CharacterBody3D player with collision, camera, input actions, and a host-edited controller script.",
78
+ },
79
+ {
80
+ name: "3d-lighting",
81
+ category: "rendering-and-lighting",
82
+ public: true,
83
+ clients: ALL_CLIENTS,
84
+ recommended: true,
85
+ requiresMcpTools: [
86
+ "summer_add_node",
87
+ "summer_set_prop",
88
+ "summer_set_resource_property",
89
+ ],
90
+ testScenario: "Add sun, fill lighting, WorldEnvironment, and shadow settings to a simple 3D scene.",
91
+ },
92
+ {
93
+ name: "ui-basics",
94
+ category: "ui-and-ux",
95
+ public: true,
96
+ clients: ALL_CLIENTS,
97
+ recommended: true,
98
+ requiresMcpTools: [
99
+ "summer_add_node",
100
+ "summer_set_prop",
101
+ "summer_connect_signal",
102
+ ],
103
+ testScenario: "Create a HUD CanvasLayer with labels, a ProgressBar, and a connected Button signal.",
104
+ },
105
+ {
106
+ name: "asset-strategy",
107
+ category: "asset-pipeline",
108
+ public: true,
109
+ clients: ALL_CLIENTS,
110
+ recommended: true,
111
+ requiresMcpTools: [
112
+ "summer_search_assets",
113
+ "summer_import_asset",
114
+ "summer_import_from_url",
115
+ "summer_generate_image",
116
+ "summer_generate_3d",
117
+ "summer_generate_audio",
118
+ ],
119
+ testScenario: "Choose library, generated, or primitive assets for a small level and import one asset into the project.",
120
+ },
121
+ {
122
+ name: "make-game",
123
+ category: "scene-and-project",
124
+ public: true,
125
+ clients: ALL_CLIENTS,
126
+ recommended: false,
127
+ requiresMcpTools: [
128
+ "summer_get_agent_playbook",
129
+ "summer_get_project_context",
130
+ "summer_create_scene",
131
+ "summer_open_scene",
132
+ "summer_save_scene",
133
+ "summer_add_node",
134
+ "summer_set_prop",
135
+ "summer_set_resource_property",
136
+ "summer_instantiate_scene",
137
+ "summer_play",
138
+ "summer_stop",
139
+ "summer_get_diagnostics",
140
+ ],
141
+ testScenario: "Build one narrow playable prototype from requirements through scene setup, host-edited scripts, and diagnostics.",
142
+ },
143
+ ];
@@ -1,18 +1,83 @@
1
1
  import { z } from "zod";
2
2
  import { withEngine } from "./with-engine.js";
3
- import { readFile } from "fs/promises";
4
- import { join } from "path";
5
- async function readMainSceneFromProject(projectPath) {
6
- if (!projectPath)
3
+ function asRecord(value) {
4
+ return value && typeof value === "object" && !Array.isArray(value)
5
+ ? value
6
+ : null;
7
+ }
8
+ function stringFrom(value) {
9
+ return typeof value === "string" && value.length > 0 ? value : null;
10
+ }
11
+ function pickString(record, keys) {
12
+ if (!record)
7
13
  return null;
8
- try {
9
- const text = await readFile(join(projectPath, "project.godot"), "utf-8");
10
- const match = text.match(/run\/main_scene="([^"]+)"/);
11
- return match?.[1] ?? null;
14
+ for (const key of keys) {
15
+ const value = stringFrom(record[key]);
16
+ if (value)
17
+ return value;
12
18
  }
13
- catch {
19
+ return null;
20
+ }
21
+ function getProjectData(projectState) {
22
+ return asRecord(asRecord(projectState)?.data);
23
+ }
24
+ function getProjectSetting(projectState, keys) {
25
+ const data = getProjectData(projectState);
26
+ const entries = data?.entries;
27
+ if (!Array.isArray(entries))
14
28
  return null;
29
+ for (const entry of entries) {
30
+ const item = asRecord(entry);
31
+ const key = stringFrom(item?.key);
32
+ if (key && keys.includes(key)) {
33
+ return stringFrom(item?.value);
34
+ }
15
35
  }
36
+ return null;
37
+ }
38
+ function getProjectPath(projectState) {
39
+ const root = asRecord(projectState);
40
+ const data = getProjectData(projectState);
41
+ return (pickString(data, [
42
+ "projectPath",
43
+ "project_path",
44
+ "projectRoot",
45
+ "project_root",
46
+ "rootPath",
47
+ "root_path",
48
+ ]) ??
49
+ pickString(root, ["projectPath", "project_path", "projectRoot", "project_root"]) ??
50
+ null);
51
+ }
52
+ function getProjectName(projectState) {
53
+ const root = asRecord(projectState);
54
+ const data = getProjectData(projectState);
55
+ return (pickString(data, ["projectName", "project_name", "name"]) ??
56
+ pickString(root, ["projectName", "project_name", "name"]) ??
57
+ getProjectSetting(projectState, ["application/config/name", "config/name"]));
58
+ }
59
+ function getMainScene(projectState) {
60
+ const root = asRecord(projectState);
61
+ const data = getProjectData(projectState);
62
+ return (pickString(data, ["mainScene", "main_scene", "mainScenePath", "main_scene_path"]) ??
63
+ pickString(root, ["mainScene", "main_scene", "mainScenePath", "main_scene_path"]) ??
64
+ getProjectSetting(projectState, [
65
+ "application/run/main_scene",
66
+ "run/main_scene",
67
+ ]));
68
+ }
69
+ function getCurrentScene(projectState, sceneState) {
70
+ const sceneRoot = asRecord(sceneState);
71
+ const sceneData = asRecord(sceneRoot?.data);
72
+ const sceneProvenance = asRecord(sceneRoot?.provenance);
73
+ const projectRoot = asRecord(projectState);
74
+ const projectData = getProjectData(projectState);
75
+ const projectProvenance = asRecord(projectRoot?.provenance);
76
+ return (pickString(sceneProvenance, ["scenePath", "scene_path"]) ??
77
+ pickString(sceneData, ["scenePath", "scene_path", "currentScene", "current_scene"]) ??
78
+ pickString(projectData, ["currentScene", "current_scene", "scenePath", "scene_path"]) ??
79
+ pickString(projectProvenance, ["scenePath", "scene_path"]) ??
80
+ null);
16
81
  }
17
82
  export function registerProjectTools(server) {
18
83
  server.tool("summer_get_agent_playbook", `AI-first operating guide for Summer Engine MCP.
@@ -25,7 +90,8 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
25
90
  text: JSON.stringify({
26
91
  startupChecklist: [
27
92
  "Decide first: file edit or engine operation.",
28
- "For normal code/content edits (.gd/.tscn/.tres/.ts), use host file-edit tools directly.",
93
+ "Write .gd/.cs/.json/docs/simple config with host file-edit tools directly.",
94
+ "Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, screenshots/verification.",
29
95
  "Call summer_get_project_context first.",
30
96
  "If no scene is open, call summer_open_main_scene.",
31
97
  "Call summer_get_scene_tree before structural edits.",
@@ -33,15 +99,16 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
33
99
  ],
34
100
  safeDefaults: [
35
101
  "Never guess scene filenames (main.tscn/Main.tscn).",
36
- "Do not route ordinary code edits through MCP when direct file tools are available.",
102
+ "Do not route ordinary script, JSON, docs, or simple config edits through MCP when direct file tools are available.",
37
103
  "Never remove multiple top-level nodes unless user explicitly requests destructive edits.",
38
- "Never write .tscn/.scn with file write tools; use scene ops.",
104
+ "Use MCP for scene-tree mutations and inspector-visible values instead of hand-editing live scene state.",
39
105
  ],
40
106
  preferredFlow: [
41
107
  "summer_get_project_context",
42
108
  "summer_open_main_scene (if needed)",
43
109
  "summer_get_scene_tree",
44
110
  "edit via summer_add_node / summer_set_prop / summer_set_resource_property",
111
+ "write scripts/data/docs/simple config directly with host file-edit tools",
45
112
  "summer_save_scene",
46
113
  "summer_get_diagnostics",
47
114
  ],
@@ -56,35 +123,45 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
56
123
  }));
57
124
  server.tool("summer_get_project_context", `Get essential project context before editing. Returns:
58
125
  - engine health/status
59
- - project name and path
126
+ - project name and project path when exposed by project state
60
127
  - current scene path (if available)
61
- - main scene path from project.godot
128
+ - main scene path from project settings
62
129
 
63
130
  Use this first in every fresh chat to avoid guessing scene filenames or editing the wrong scene.`, {}, async () => withEngine(async (client) => {
64
- const [health, projectState] = await Promise.all([
131
+ const [health, projectState, sceneState] = await Promise.all([
65
132
  client.health(),
66
133
  client.getProjectState(),
134
+ client.getSceneState().catch((err) => ({
135
+ ok: false,
136
+ error: err instanceof Error ? err.message : String(err),
137
+ })),
67
138
  ]);
68
- const healthObj = (health ?? {});
69
- const mainScene = await readMainSceneFromProject(typeof healthObj.project_path === "string" ? healthObj.project_path : undefined);
139
+ const projectPath = getProjectPath(projectState);
140
+ const projectName = getProjectName(projectState);
141
+ const mainScene = getMainScene(projectState);
142
+ const currentScene = getCurrentScene(projectState, sceneState);
70
143
  return {
71
144
  health,
72
145
  project: projectState,
146
+ scene: sceneState,
147
+ projectName,
148
+ projectPath,
149
+ currentScene,
73
150
  mainScene,
74
151
  guidance: mainScene
75
152
  ? "Use `summer_open_scene` with `mainScene` if no scene is open."
76
- : "Main scene not found in project.godot. Open a known scene path explicitly.",
153
+ : "Main scene not found in project state. Open a known scene path explicitly.",
154
+ fileEditingGuidance: "Write .gd/.cs/.json/docs/simple config directly with host file tools. Use MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, and visual verification.",
77
155
  };
78
156
  }));
79
- server.tool("summer_open_main_scene", `Open the project's configured main scene from project.godot.
157
+ server.tool("summer_open_main_scene", `Open the project's configured main scene from project settings.
80
158
 
81
159
  Safer than guessing scene names like main.tscn/Main.tscn.
82
160
  Call this when you get "no scene open".`, {}, async () => withEngine(async (client) => {
83
- const health = (await client.health());
84
- const projectPath = typeof health.project_path === "string" ? health.project_path : undefined;
85
- const mainScene = await readMainSceneFromProject(projectPath);
161
+ const projectState = await client.getProjectState();
162
+ const mainScene = getMainScene(projectState);
86
163
  if (!mainScene) {
87
- throw new Error("Could not resolve application/run/main_scene from project.godot. Call `summer_get_project_context` and open a scene explicitly.");
164
+ throw new Error("Could not resolve application/run/main_scene from project state. Call `summer_get_project_context` and open a scene explicitly.");
88
165
  }
89
166
  return client.executeOps([{ op: "OpenScene", path: mainScene }]);
90
167
  }));
@@ -1,10 +1,10 @@
1
1
  import { getClient, resetClient } from "../server.js";
2
- import { getAuthToken } from "../../lib/auth.js";
3
- const GATEWAY_URL = process.env.SUMMER_GATEWAY_URL || "https://www.summerengine.com";
4
2
  function extractOpError(result) {
5
3
  if (!result || typeof result !== "object")
6
4
  return null;
7
5
  const op = result;
6
+ if (op.ok === false && op.error)
7
+ return op.error;
8
8
  if (op.status === "error" && op.error)
9
9
  return op.error;
10
10
  const firstFailed = op.results?.find((r) => r.ok === false && r.error);
@@ -19,62 +19,12 @@ function buildActionHint(message) {
19
19
  return "Scene path could not be opened. Call `summer_get_project_context` to get `mainScene`, then open that exact path. Avoid guessing scene filenames.";
20
20
  }
21
21
  if (normalized.includes("writefile cannot edit .tscn/.scn")) {
22
- return "This MCP operation cannot write .tscn/.scn directly. If your host AI has normal file-edit tools, edit .gd/.tscn files there; use Summer MCP for engine/state/scene-tree operations.";
22
+ return "Write .gd/.cs/.json/docs/simple config files with normal file-edit tools. Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, and visual verification.";
23
23
  }
24
24
  return null;
25
25
  }
26
- async function checkMcpQuota() {
27
- const token = await getAuthToken();
28
- if (!token) {
29
- return {
30
- allowed: false,
31
- message: "Not signed in. The user needs to run this in their terminal:\n npx summer-engine login\nOr open: https://www.summerengine.com/login\nMCP tools require a Summer Engine account (free to create).",
32
- };
33
- }
34
- try {
35
- const res = await fetch(`${GATEWAY_URL}/api/mcp/log-local-call`, {
36
- method: "POST",
37
- headers: {
38
- Authorization: `Bearer ${token}`,
39
- "Content-Type": "application/json",
40
- },
41
- body: JSON.stringify({}),
42
- signal: AbortSignal.timeout(10000),
43
- });
44
- const data = (await res.json().catch(() => ({})));
45
- // Hard enforcement errors should block tool execution.
46
- if ([401, 402, 403, 429].includes(res.status)) {
47
- return {
48
- allowed: false,
49
- message: data.message || "Quota check failed. Try again.",
50
- };
51
- }
52
- // Soft-fail on service/gateway issues so local tools keep working.
53
- if (!res.ok) {
54
- return { allowed: true };
55
- }
56
- if (data.allowed === false) {
57
- return {
58
- allowed: false,
59
- message: data.message || "MCP call limit reached. Upgrade at https://summerengine.com/pricing",
60
- };
61
- }
62
- return { allowed: true };
63
- }
64
- catch (err) {
65
- // Network/transient errors should not brick local MCP usage.
66
- return { allowed: true };
67
- }
68
- }
69
26
  export async function withEngine(fn) {
70
27
  try {
71
- const quota = await checkMcpQuota();
72
- if (!quota.allowed) {
73
- return {
74
- content: [{ type: "text", text: quota.message || "Quota exceeded" }],
75
- isError: true,
76
- };
77
- }
78
28
  const client = await getClient();
79
29
  const result = await fn(client);
80
30
  const opError = extractOpError(result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "summer-engine",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "CLI and MCP tools for Summer Engine — the AI-native game engine",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -31,6 +31,8 @@
31
31
  "build": "tsc",
32
32
  "dev": "tsc --watch",
33
33
  "summer": "node dist/bin/summer.js",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
34
36
  "prepublishOnly": "npm run build"
35
37
  },
36
38
  "engines": {
@@ -48,6 +50,7 @@
48
50
  "devDependencies": {
49
51
  "@types/diff": "^7.0.0",
50
52
  "@types/node": "^20.0.0",
51
- "typescript": "^5.5.0"
53
+ "typescript": "^5.5.0",
54
+ "vitest": "^3.2.4"
52
55
  }
53
56
  }
@@ -0,0 +1,96 @@
1
+ # Summer Skill Library
2
+
3
+ The canonical agent-readable game-dev knowledge source for Claude Code, Cursor, Codex, and Windsurf.
4
+
5
+ When an agent is asked to make a game in Godot, this is what it reads to learn how.
6
+
7
+ ## Layout
8
+
9
+ ```
10
+ skills/
11
+ ├── README.md # this file
12
+ ├── catalog.yaml # canonical registry (name, category, status, template-id)
13
+ ├── _shared/ # cross-skill references every skill can link
14
+ │ ├── godot-version.md
15
+ │ ├── mcp-tools-reference.md
16
+ │ ├── collaborative-protocol.md
17
+ │ ├── template-registry.md
18
+ │ └── gd-style.md
19
+ ├── _meta/
20
+ │ ├── skill-test/ # static + behavioral linter
21
+ │ ├── skill-create/ # bootstrap a new skill
22
+ │ └── skill-improve/ # eval-harness iteration
23
+ ├── _tests/
24
+ │ ├── runner.md # how /skill-test works
25
+ │ └── specs/ # behavioral specs per skill
26
+ └── <category>/<skill>/
27
+ ├── SKILL.md # < 500 lines, frontmatter + body
28
+ ├── references/ # loaded on demand
29
+ ├── examples/ # working snippets
30
+ └── tests/spec.md # behavioral spec
31
+ ```
32
+
33
+ ## Categories (20)
34
+
35
+ `character-controllers`, `gameplay-mechanics`, `scripting-patterns`, `scene-and-project`, `rendering-and-lighting`, `shaders`, `visual-effects`, `post-processing`, `animation`, `audio`, `physics`, `multiplayer-and-networking`, `ai-and-npcs`, `level-design`, `performance`, `ui-and-ux`, `asset-pipeline`, `input-and-controls`, `debugging`, `deployment`.
36
+
37
+ ## Standard
38
+
39
+ Adopts the **Anthropic Agent Skills open standard** (`agentskills.io`). Frontmatter:
40
+
41
+ ```yaml
42
+ ---
43
+ name: <kebab-case>
44
+ description: <when-to-use, key trigger phrases first>
45
+ license: MIT
46
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
47
+ category: <one of the 20>
48
+ template-id: <optional, resolved against _shared/template-registry.md>
49
+ allowed-tools: Read Grep summer_add_node summer_set_prop ...
50
+ paths: ["**/*.gd", "**/*.tscn"]
51
+ ---
52
+ ```
53
+
54
+ Cross-portable. Cursor / Codex / Windsurf understand the same SKILL.md.
55
+
56
+ ## Authoring rules
57
+
58
+ 1. **SKILL.md ≤ 500 lines.** Push detail into `references/`. Progressive disclosure.
59
+ 2. **MCP-preferred + file-edit-fallback.** Every code-touching skill must show both paths so the skill works without Summer MCP installed.
60
+ 3. **Collaborative protocol.** "May I write…?" before any user-visible mutation. See `_shared/collaborative-protocol.md`.
61
+ 4. **Template cross-link.** When a working starter exists, link via `template-id` and the section "Want a working starter?".
62
+ 5. **Test spec.** Every non-`_meta` skill ships `tests/spec.md` with at least one Test Case.
63
+
64
+ ## Adding a new skill
65
+
66
+ ```
67
+ /skill-create
68
+ ```
69
+
70
+ (See `_meta/skill-create/SKILL.md`.)
71
+
72
+ ## Testing
73
+
74
+ ```
75
+ /skill-test <name> # static lint
76
+ /skill-test <name> spec # behavioral spec runner
77
+ /skill-test audit # whole-library audit
78
+ /skill-improve <name> # parallel-eval harness for iteration
79
+ ```
80
+
81
+ (See `_meta/skill-test/SKILL.md` and `_tests/runner.md`.)
82
+
83
+ ## Roadmap
84
+
85
+ Status legend in `catalog.yaml`:
86
+
87
+ - **HAVE** — ships today.
88
+ - **NEXT** — next wave, in active development.
89
+ - **LATER** — on roadmap.
90
+
91
+ Currently: **7 HAVE / ~50 NEXT / ~30 LATER ≈ 85 skills across 20 categories**. Subagents fill content category by category.
92
+
93
+ ## See also
94
+
95
+ - [Architecture spec](../../../publicsummerengine/Docs/superpowers/specs/2026-05-05-skill-library-architecture.md) (lives in publicsummerengine for spec history)
96
+ - [Research notes](../../../publicsummerengine/Docs/superpowers/research/2026-05-05-skill-library-research.md)
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: skill-create
3
+ description: Bootstrap a new Summer skill with the canonical folder structure, frontmatter, and stub sections. Use when adding a new skill to the library. Trigger on "create skill", "add skill", "new skill".
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: _meta
7
+ allowed-tools: Read Write Glob Grep
8
+ ---
9
+
10
+ # /skill-create — Bootstrap a New Skill
11
+
12
+ ## Steps
13
+
14
+ ### 1. Get the basics
15
+
16
+ Ask the user:
17
+ - **Name** (kebab-case, ≤ 64 chars). Example: `state-machine-patterns`.
18
+ - **Category** (one of the 20 in `catalog.yaml`). Example: `scripting-patterns`.
19
+ - **One-sentence description** for the frontmatter.
20
+ - **Template-id** (optional). Lookup against `_shared/template-registry.md`.
21
+
22
+ ### 2. Create the folder
23
+
24
+ May I create `skills/<category>/<name>/` with this structure?
25
+
26
+ ```
27
+ skills/<category>/<name>/
28
+ ├── SKILL.md
29
+ ├── references/ (empty, populate as needed)
30
+ ├── examples/ (empty, populate as needed)
31
+ └── tests/spec.md
32
+ ```
33
+
34
+ ### 3. Write SKILL.md (template)
35
+
36
+ ```markdown
37
+ ---
38
+ name: <name>
39
+ description: <one-sentence what + when. Lead with key trigger phrases.>
40
+ license: MIT
41
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
42
+ category: <category>
43
+ template-id: <optional template-id from _shared/template-registry.md>
44
+ allowed-tools: Read Grep <summer_* tools this skill uses>
45
+ paths: ["**/*.gd", "**/*.tscn"]
46
+ ---
47
+
48
+ # <Title> for Summer Engine
49
+
50
+ <One-paragraph context. Why this exists, who needs it.>
51
+
52
+ ## Steps
53
+
54
+ ### 1. <First step>
55
+
56
+ **Preferred (Summer MCP):**
57
+
58
+ \`\`\`
59
+ summer_<tool>(...)
60
+ \`\`\`
61
+
62
+ **Fallback (no MCP — edit `<file>` directly):**
63
+
64
+ \`\`\`
65
+ <raw text/code>
66
+ \`\`\`
67
+
68
+ May I <action>?
69
+
70
+ ### 2. <Second step>
71
+
72
+ ...
73
+
74
+ ## Common mistakes
75
+
76
+ - <mistake 1, with one-line fix>
77
+ - <mistake 2, with one-line fix>
78
+
79
+ ## Want a working starter?
80
+
81
+ → **template-id**: `<template-id>`
82
+ → **Repo**: <github URL from _shared/template-registry.md>
83
+ → **Bootstrap**: `summer template clone <template-id> my-game`
84
+
85
+ ## See also
86
+
87
+ - `_shared/godot-version.md`
88
+ - `_shared/mcp-tools-reference.md`
89
+ - `_shared/gd-style.md`
90
+ - (other relevant skills)
91
+ ```
92
+
93
+ ### 4. Write tests/spec.md (template)
94
+
95
+ ```markdown
96
+ # Skill Spec: /<name>
97
+
98
+ ## Fixture
99
+ - <starting state of the project>
100
+ - <which tools are available — Summer MCP yes/no>
101
+
102
+ ## Case 1: Happy Path
103
+ **Input:** "<typical user prompt>"
104
+ **Expected MCP tool sequence (in order):**
105
+ 1. <first tool call>
106
+ 2. <second>
107
+
108
+ **Assertions:**
109
+ - [ ] <observable outcome>
110
+ - [ ] <skill asks "May I" before any write step>
111
+
112
+ ## Case 2: Failure / Edge
113
+ **Fixture:** <something different>
114
+ **Input:** "<edge prompt>"
115
+ **Expected:** <how the skill should adapt>
116
+ ```
117
+
118
+ ### 5. Register in catalog.yaml
119
+
120
+ Append:
121
+
122
+ ```yaml
123
+ - name: <name>
124
+ category: <category>
125
+ status: NEXT # or HAVE if shipping immediately
126
+ template-id: <or omit>
127
+ public: true
128
+ recommended: false # flip to true when status: HAVE and tested
129
+ ```
130
+
131
+ ### 6. Run /skill-test in static mode
132
+
133
+ Confirm the new skill passes the 7 structural checks before commit.
134
+
135
+ ## Collaborative protocol
136
+
137
+ This skill writes files (the new skill folder + catalog update). Always ask before each write step.
138
+
139
+ ## See also
140
+
141
+ - `_shared/collaborative-protocol.md`
142
+ - `_meta/skill-test/SKILL.md`
143
+ - `catalog.yaml`