summer-engine 1.3.0 → 1.3.1

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/README.md CHANGED
@@ -32,9 +32,14 @@ Verify:
32
32
  summer doctor
33
33
  ```
34
34
 
35
- Then open your agent and try the verification prompt:
35
+ Then open your agent and try one of the slash commands:
36
36
 
37
- > Use Summer Engine MCP. First call `summer_get_project_context` and `summer_get_agent_playbook`, then add a `DirectionalLight3D` and `Camera3D` to the main scene, save it, and check diagnostics.
37
+ | Command | What it does |
38
+ |---|---|
39
+ | `/debug` | Triage and fix a bug end-to-end (reads engine errors, locates code, proposes fix) |
40
+ | `/play` | Run the game and report state (clean run, warnings, or crash) |
41
+
42
+ Or just describe what you want — the specialist skills (FPS controller, lighting, GDScript patterns, scene composition, UI, asset strategy) auto-trigger on natural language: "make me an FPS", "add lighting", "I need a HUD".
38
43
 
39
44
  ## Commands
40
45
 
@@ -14,13 +14,18 @@ import { skillsCommand } from "../commands/skills.js";
14
14
  import { orchestratorCommand } from "../commands/orchestrator.js";
15
15
  import { setupCommand } from "../commands/setup.js";
16
16
  import { doctorCommand } from "../commands/doctor.js";
17
+ import { getBanner } from "../lib/banner.js";
18
+ import { c, sym } from "../lib/format.js";
17
19
  const require = createRequire(import.meta.url);
18
20
  const { version } = require("../../package.json");
19
21
  const program = new Command();
20
22
  program
21
23
  .name("summer")
22
24
  .description("CLI and MCP tools for Summer Engine — the AI-native game engine")
23
- .version(version);
25
+ .version(version)
26
+ .action(() => {
27
+ printIntro(version);
28
+ });
24
29
  program.addCommand(installCommand);
25
30
  program.addCommand(loginCommand);
26
31
  program.addCommand(logoutCommand);
@@ -38,3 +43,21 @@ program.parseAsync().catch((err) => {
38
43
  console.error(err instanceof Error ? err.message : String(err));
39
44
  process.exit(1);
40
45
  });
46
+ function printIntro(version) {
47
+ console.log("");
48
+ console.log(getBanner());
49
+ console.log("");
50
+ console.log(` ${sym.arrow()} ${c.bold("Summer Engine")} ${c.dim("v" + version)} ${c.dim("·")} AI-native game engine`);
51
+ console.log("");
52
+ console.log(` ${c.bold("Get started")}`);
53
+ console.log(` ${c.dim("$")} summer setup claude-code ${c.dim("Configure Claude Code (or codex/cursor/windsurf)")}`);
54
+ console.log(` ${c.dim("$")} summer create 3d-basic my-game ${c.dim("Start a project")}`);
55
+ console.log(` ${c.dim("$")} summer doctor ${c.dim("Check setup")}`);
56
+ console.log("");
57
+ console.log(` ${c.bold("Slash commands")} ${c.dim("(in your AI agent)")}`);
58
+ console.log(` ${c.brand("/debug")} ${c.dim("Triage and fix a bug end-to-end")}`);
59
+ console.log(` ${c.brand("/play")} ${c.dim("Run the game and report state")}`);
60
+ console.log("");
61
+ console.log(` ${c.dim("Docs:")} https://summerengine.com/docs`);
62
+ console.log("");
63
+ }
@@ -5,6 +5,7 @@ import { platform } from "os";
5
5
  import { Command } from "commander";
6
6
  import { getAuthToken, getUserInfo } from "../lib/auth.js";
7
7
  import { checkEngineHealth, getApiPort, getApiToken } from "../lib/engine.js";
8
+ import { brandLine, c, pad, sym, tildeify } from "../lib/format.js";
8
9
  const require = createRequire(import.meta.url);
9
10
  const { version } = require("../../package.json");
10
11
  const MAC_ENGINE_PATHS = [
@@ -17,11 +18,17 @@ const WIN_ENGINE_PATHS = [
17
18
  `${process.env.PROGRAMFILES}\\Summer Engine\\Summer.exe`,
18
19
  ];
19
20
  export const doctorCommand = new Command("doctor")
20
- .description("Run setup and connection diagnostics")
21
+ .description("Diagnose Node, login, engine, local API, and MCP boot")
21
22
  .option("--json", "Print diagnostics as JSON")
22
- .option("--human", "Print diagnostics in a human-readable format")
23
23
  .action(async (opts) => {
24
- const result = await runDoctor({ json: opts.json || !opts.human });
24
+ if (!opts.json) {
25
+ const { createRequire: req } = await import("node:module");
26
+ const ver = req(import.meta.url)("../../package.json").version;
27
+ console.log("");
28
+ console.log(brandLine(ver));
29
+ console.log("");
30
+ }
31
+ const result = await runDoctor({ json: Boolean(opts.json) });
25
32
  if (!result.ok) {
26
33
  process.exit(1);
27
34
  }
@@ -33,7 +40,7 @@ export async function runDoctor(options = {}) {
33
40
  id: "cli-version",
34
41
  label: "Summer CLI",
35
42
  status: "ok",
36
- message: `summer-engine CLI v${version}`,
43
+ message: `v${version}`,
37
44
  });
38
45
  checks.push(await checkLogin());
39
46
  checks.push(checkEngineInstall());
@@ -57,14 +64,14 @@ function checkNodeVersion() {
57
64
  id: "node-version",
58
65
  label: "Node.js",
59
66
  status: "ok",
60
- message: `Node ${process.version}`,
67
+ message: process.version,
61
68
  };
62
69
  }
63
70
  return {
64
71
  id: "node-version",
65
72
  label: "Node.js",
66
73
  status: "fail",
67
- message: `Node ${process.version} found; Summer CLI requires Node 18 or newer.`,
74
+ message: `${process.version} (need 18+)`,
68
75
  };
69
76
  }
70
77
  async function checkLogin() {
@@ -75,32 +82,34 @@ async function checkLogin() {
75
82
  id: "login",
76
83
  label: "Login",
77
84
  status: "warning",
78
- message: "Not logged in. Run: summer login",
85
+ message: "not signed in (run: summer login)",
79
86
  };
80
87
  }
81
88
  return {
82
89
  id: "login",
83
90
  label: "Login",
84
91
  status: "ok",
85
- message: user ? `Logged in as ${user.email}` : "Auth token found.",
92
+ message: user ? user.email : "signed in",
86
93
  };
87
94
  }
88
95
  function checkEngineInstall() {
89
96
  const binary = findEngineBinary();
90
97
  if (binary) {
98
+ // Shorten /Applications/Summer.app/Contents/MacOS/Summer -> /Applications/Summer.app
99
+ const display = binary.replace(/\/Contents\/MacOS\/Summer$/, "");
91
100
  return {
92
101
  id: "engine-install",
93
- label: "Engine Install",
102
+ label: "Engine",
94
103
  status: "ok",
95
- message: `Summer Engine binary found at ${binary}`,
104
+ message: tildeify(display),
96
105
  details: { path: binary },
97
106
  };
98
107
  }
99
108
  return {
100
109
  id: "engine-install",
101
- label: "Engine Install",
110
+ label: "Engine",
102
111
  status: "warning",
103
- message: "Summer Engine binary was not found in the default install locations.",
112
+ message: "not installed (run: summer install)",
104
113
  };
105
114
  }
106
115
  async function checkLocalApi() {
@@ -111,7 +120,7 @@ async function checkLocalApi() {
111
120
  id: "local-api",
112
121
  label: "Local API",
113
122
  status: "warning",
114
- message: "Engine API token not found; Summer Engine is probably not running.",
123
+ message: "engine not running",
115
124
  details: { port },
116
125
  };
117
126
  }
@@ -121,7 +130,7 @@ async function checkLocalApi() {
121
130
  id: "local-api",
122
131
  label: "Local API",
123
132
  status: "warning",
124
- message: `API token found, but Summer Engine is not responding on port ${port}.`,
133
+ message: `not responding on :${port}`,
125
134
  details: { port },
126
135
  };
127
136
  }
@@ -129,7 +138,7 @@ async function checkLocalApi() {
129
138
  id: "local-api",
130
139
  label: "Local API",
131
140
  status: "ok",
132
- message: `Summer Engine API is responding on port ${port}.`,
141
+ message: `:${port}`,
133
142
  details: {
134
143
  port,
135
144
  version: health.version,
@@ -182,7 +191,7 @@ async function checkMcpBoot() {
182
191
  id: "mcp-boot",
183
192
  label: "MCP Server",
184
193
  status: "ok",
185
- message: "MCP stdio server starts successfully.",
194
+ message: "ready",
186
195
  });
187
196
  }
188
197
  });
@@ -225,12 +234,27 @@ function summarizeChecks(checks) {
225
234
  };
226
235
  }
227
236
  export function printDoctorResult(result) {
228
- console.log("Summer Doctor\n");
237
+ console.log(c.bold("Doctor"));
238
+ console.log("");
239
+ const labelWidth = 14;
229
240
  for (const check of result.checks) {
230
- const mark = check.status === "ok" ? "OK" : check.status === "warning" ? "WARN" : "FAIL";
231
- console.log(` ${mark.padEnd(4)} ${check.label}: ${check.message}`);
241
+ const mark = check.status === "ok"
242
+ ? sym.ok()
243
+ : check.status === "warning"
244
+ ? sym.warn()
245
+ : sym.fail();
246
+ console.log(` ${mark} ${pad(c.bold(check.label), labelWidth)}${c.dim(check.message)}`);
247
+ }
248
+ console.log("");
249
+ if (result.summary.failures > 0) {
250
+ console.log(c.red(`${result.summary.failures} ${result.summary.failures === 1 ? "issue" : "issues"} to fix.`));
251
+ }
252
+ else if (result.summary.warnings > 0) {
253
+ console.log(c.yellow(`${result.summary.warnings} ${result.summary.warnings === 1 ? "warning" : "warnings"}, ${result.summary.ok} OK.`));
254
+ }
255
+ else {
256
+ console.log(c.green("Everything's wired up."));
232
257
  }
233
- console.log(`\n${result.summary.ok} ok, ${result.summary.warnings} warnings, ${result.summary.failures} failures`);
234
258
  }
235
259
  function findEngineBinary() {
236
260
  const paths = platform() === "darwin" ? MAC_ENGINE_PATHS : WIN_ENGINE_PATHS;
@@ -1,7 +1,17 @@
1
1
  import { spawnSync } from "child_process";
2
+ import { createRequire } from "node:module";
2
3
  import { Command } from "commander";
3
4
  import { configureAgentMcp, parseAgent, parseScope, supportedAgents, } from "../lib/agent-config.js";
4
5
  import { printDoctorResult, runDoctor } from "./doctor.js";
6
+ import { brandLine, c, sym, tildeify } from "../lib/format.js";
7
+ const requireFromHere = createRequire(import.meta.url);
8
+ const { version: cliVersion } = requireFromHere("../../package.json");
9
+ const AGENT_LABEL = {
10
+ "claude-code": "Claude Code",
11
+ codex: "Codex",
12
+ cursor: "Cursor",
13
+ windsurf: "Windsurf",
14
+ };
5
15
  export const setupCommand = new Command("setup")
6
16
  .description("Configure Summer Engine for an AI agent and run diagnostics")
7
17
  .argument("[agent]", `Agent to configure: ${supportedAgents.join(", ")}`)
@@ -109,20 +119,81 @@ function skillInstallInvocation(agent) {
109
119
  function printSetupResult(config, skills, doctor) {
110
120
  if (config.print) {
111
121
  console.log(config.snippet.trimEnd());
122
+ return;
112
123
  }
113
- else if (config.dryRun) {
114
- console.log(`Would update ${config.path} for ${config.agent} (${config.scope}).`);
124
+ console.log("");
125
+ console.log(brandLine(cliVersion));
126
+ console.log("");
127
+ const agentLabel = AGENT_LABEL[config.agent] ?? config.agent;
128
+ if (config.dryRun) {
129
+ console.log(` ${c.dim("(dry run)")} Would link to ${agentLabel} ${c.dim(tildeify(config.path))}`);
115
130
  }
116
131
  else if (config.wrote) {
117
- console.log(`Configured Summer Engine MCP for ${config.agent} (${config.scope}) at ${config.path}.`);
132
+ console.log(` ${sym.ok()} Linked to ${c.bold(agentLabel)} ${c.dim(tildeify(config.path))}`);
118
133
  }
119
134
  else {
120
- console.log(`Summer Engine MCP already configured for ${config.agent} (${config.scope}) at ${config.path}.`);
135
+ console.log(` ${sym.ok()} Already linked to ${c.bold(agentLabel)} ${c.dim(tildeify(config.path))}`);
136
+ }
137
+ if (skills.status === "installed") {
138
+ const installed = parseInstalledSkills(skills.stdout);
139
+ if (installed.length > 0) {
140
+ const where = parseSkillTargetDir(skills.stdout);
141
+ console.log(` ${sym.ok()} Installed ${c.bold(String(installed.length) + " skills")} ${where ? c.dim(tildeify(where)) : ""}`);
142
+ const grouped = installed.reduce((rows, name, i) => {
143
+ const row = Math.floor(i / 3);
144
+ rows[row] = rows[row] ?? [];
145
+ rows[row].push(name);
146
+ return rows;
147
+ }, []);
148
+ for (const row of grouped) {
149
+ console.log(` ${c.dim(row.join(" · "))}`);
150
+ }
151
+ }
152
+ else {
153
+ console.log(` ${sym.ok()} Installed recommended skills`);
154
+ }
155
+ }
156
+ else if (skills.status === "planned" || skills.status === "skipped") {
157
+ console.log(` ${sym.warn()} Skills: ${c.dim(skills.message)}`);
158
+ }
159
+ else if (skills.status === "failed") {
160
+ console.log(` ${sym.fail()} Skills install failed`);
161
+ if (skills.stderr)
162
+ console.log(` ${c.dim(skills.stderr)}`);
121
163
  }
122
164
  for (const warning of config.warnings) {
123
- console.warn(`Warning: ${warning}`);
165
+ console.log(` ${sym.warn()} ${c.yellow(warning)}`);
124
166
  }
125
- console.log(`Skills: ${skills.message}`);
126
167
  console.log("");
127
168
  printDoctorResult(doctor);
169
+ if (doctor.ok && skills.status !== "failed" && !config.dryRun) {
170
+ console.log("");
171
+ console.log(`${c.dim("Try it:")} open ${AGENT_LABEL[config.agent] ?? config.agent} and ask: ${c.bold("\"add a DirectionalLight3D and Camera3D to my scene\"")}`);
172
+ }
173
+ }
174
+ function parseInstalledSkills(stdout) {
175
+ if (!stdout)
176
+ return [];
177
+ // Lines look like: " Installed fps-controller -> /path/.../fps-controller"
178
+ const names = [];
179
+ for (const line of stdout.split("\n")) {
180
+ const m = line.match(/^\s*Installed\s+([a-z0-9-]+)\s+->/i);
181
+ if (m)
182
+ names.push(m[1]);
183
+ }
184
+ return names;
185
+ }
186
+ function parseSkillTargetDir(stdout) {
187
+ if (!stdout)
188
+ return null;
189
+ for (const line of stdout.split("\n")) {
190
+ const m = line.match(/^\s*Installed\s+[a-z0-9-]+\s+->\s+(.+)$/i);
191
+ if (m) {
192
+ const path = m[1].trim();
193
+ // Strip trailing /<skill-name> to get the parent dir
194
+ const parent = path.replace(/\/[a-z0-9-]+$/i, "/");
195
+ return parent;
196
+ }
197
+ }
198
+ return null;
128
199
  }
@@ -4,6 +4,7 @@ import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { homedir } from "os";
6
6
  import { AGENT_CLIENTS, SKILL_REGISTRY, } from "../lib/skills-registry.js";
7
+ import { tildeify } from "../lib/format.js";
7
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
9
  // Resolve skills dir: from dist/commands/skills.js -> ../../skills
9
10
  const skillsDir = join(__dirname, "..", "..", "skills");
@@ -242,15 +243,16 @@ function escapeRegExp(value) {
242
243
  }
243
244
  function printInstallSummary(count, agent, scope, location) {
244
245
  const label = agentLabel(agent);
246
+ const tildeified = tildeify(location.path);
245
247
  console.log(`\n${count} skill${count === 1 ? "" : "s"} ready for ${label} (${scope} scope).`);
246
248
  if (location.kind === "skill-dir") {
247
- console.log(`${label} can read skills from ${location.path}/<skill>/SKILL.md`);
249
+ console.log(`${label} can read skills from ${tildeified}/<skill>/SKILL.md`);
248
250
  }
249
251
  else if (location.kind === "cursor-rule-dir") {
250
- console.log(`Cursor rules are in ${location.path}/summer-<skill>.mdc`);
252
+ console.log(`Cursor rules are in ${tildeified}/summer-<skill>.mdc`);
251
253
  }
252
254
  else {
253
- console.log(`Windsurf rules are in ${location.path}`);
255
+ console.log(`Windsurf rules are in ${tildeified}`);
254
256
  }
255
257
  }
256
258
  export const skillsCommand = new Command("skills")
@@ -0,0 +1,23 @@
1
+ export declare function useColor(): boolean;
2
+ export declare const c: {
3
+ bold: (s: string) => string;
4
+ dim: (s: string) => string;
5
+ red: (s: string) => string;
6
+ green: (s: string) => string;
7
+ yellow: (s: string) => string;
8
+ cyan: (s: string) => string;
9
+ brand: (s: string) => string;
10
+ };
11
+ export declare const sym: {
12
+ ok: () => string;
13
+ warn: () => string;
14
+ fail: () => string;
15
+ arrow: () => string;
16
+ };
17
+ export declare function tildeify(path: string | undefined | null): string;
18
+ export declare function brandLine(version: string): string;
19
+ export declare function stripAnsi(s: string): string;
20
+ /**
21
+ * Pad a string to a visible width, accounting for ANSI codes.
22
+ */
23
+ export declare function pad(s: string, width: number): string;
@@ -0,0 +1,60 @@
1
+ import { homedir } from "os";
2
+ const ANSI = {
3
+ reset: "\x1b[0m",
4
+ bold: "\x1b[1m",
5
+ dim: "\x1b[2m",
6
+ red: "\x1b[31m",
7
+ green: "\x1b[32m",
8
+ yellow: "\x1b[33m",
9
+ cyan: "\x1b[36m",
10
+ // Summer brand orange (matches banner.ts mid gradient)
11
+ brand: "\x1b[38;2;255;143;23m",
12
+ };
13
+ export function useColor() {
14
+ if (process.env.NO_COLOR)
15
+ return false;
16
+ if (process.env.FORCE_COLOR)
17
+ return true;
18
+ return Boolean(process.stdout.isTTY);
19
+ }
20
+ function paint(code, s) {
21
+ return useColor() ? `${code}${s}${ANSI.reset}` : s;
22
+ }
23
+ export const c = {
24
+ bold: (s) => paint(ANSI.bold, s),
25
+ dim: (s) => paint(ANSI.dim, s),
26
+ red: (s) => paint(ANSI.red, s),
27
+ green: (s) => paint(ANSI.green, s),
28
+ yellow: (s) => paint(ANSI.yellow, s),
29
+ cyan: (s) => paint(ANSI.cyan, s),
30
+ brand: (s) => paint(ANSI.brand, s),
31
+ };
32
+ export const sym = {
33
+ ok: () => c.green("✓"),
34
+ warn: () => c.yellow("⚠"),
35
+ fail: () => c.red("✗"),
36
+ arrow: () => c.brand("▶"),
37
+ };
38
+ export function tildeify(path) {
39
+ if (!path)
40
+ return "";
41
+ const home = homedir();
42
+ if (path === home)
43
+ return "~";
44
+ if (path.startsWith(home + "/"))
45
+ return "~" + path.slice(home.length);
46
+ return path;
47
+ }
48
+ export function brandLine(version) {
49
+ return `${sym.arrow()} ${c.bold("Summer Engine")} ${c.dim("v" + version)}`;
50
+ }
51
+ export function stripAnsi(s) {
52
+ return s.replace(/\x1b\[[0-9;]*m/g, "");
53
+ }
54
+ /**
55
+ * Pad a string to a visible width, accounting for ANSI codes.
56
+ */
57
+ export function pad(s, width) {
58
+ const visLen = stripAnsi(s).length;
59
+ return s + " ".repeat(Math.max(0, width - visLen));
60
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { homedir } from "os";
2
+ import { describe, expect, it } from "vitest";
3
+ import { stripAnsi, tildeify } from "./format.js";
4
+ describe("tildeify", () => {
5
+ it("replaces home directory prefix with ~", () => {
6
+ const home = homedir();
7
+ expect(tildeify(home + "/.claude.json")).toBe("~/.claude.json");
8
+ expect(tildeify(home + "/projects/foo")).toBe("~/projects/foo");
9
+ });
10
+ it("returns ~ when path is the home directory exactly", () => {
11
+ expect(tildeify(homedir())).toBe("~");
12
+ });
13
+ it("does not touch non-home paths", () => {
14
+ expect(tildeify("/Applications/Summer.app")).toBe("/Applications/Summer.app");
15
+ expect(tildeify("/tmp/foo")).toBe("/tmp/foo");
16
+ });
17
+ it("does not partial-match (e.g. /Users/Mat vs /Users/MathiasWork)", () => {
18
+ const home = homedir();
19
+ const sibling = home.slice(0, -2) + "Other";
20
+ expect(tildeify(sibling + "/file")).toBe(sibling + "/file");
21
+ });
22
+ it("handles empty/null/undefined input", () => {
23
+ expect(tildeify(undefined)).toBe("");
24
+ expect(tildeify(null)).toBe("");
25
+ expect(tildeify("")).toBe("");
26
+ });
27
+ });
28
+ describe("stripAnsi", () => {
29
+ it("removes ANSI color codes", () => {
30
+ expect(stripAnsi("\x1b[31mhello\x1b[0m")).toBe("hello");
31
+ expect(stripAnsi("\x1b[1;32mbold green\x1b[0m text")).toBe("bold green text");
32
+ });
33
+ it("leaves clean strings untouched", () => {
34
+ expect(stripAnsi("hello")).toBe("hello");
35
+ });
36
+ });
@@ -17,6 +17,22 @@ export interface SkillRegistryEntry {
17
17
  * with shippable content). When a NEXT skill ships, mirror it here.
18
18
  */
19
19
  export declare const SKILL_REGISTRY: readonly [{
20
+ readonly name: "debug";
21
+ readonly category: "debugging";
22
+ readonly public: true;
23
+ readonly clients: readonly ["summer", "codex", "claude-code", "cursor", "windsurf"];
24
+ readonly recommended: true;
25
+ readonly requiresMcpTools: readonly ["summer_get_console", "summer_get_debugger_errors", "summer_get_script_errors", "summer_get_diagnostics", "summer_clear_console", "summer_play", "summer_stop", "summer_inspect_node"];
26
+ readonly testScenario: "Triage a runtime crash end-to-end: read errors, locate code, propose fix, apply on approval, verify.";
27
+ }, {
28
+ readonly name: "play";
29
+ readonly category: "scene-and-project";
30
+ readonly public: true;
31
+ readonly clients: readonly ["summer", "codex", "claude-code", "cursor", "windsurf"];
32
+ readonly recommended: true;
33
+ readonly requiresMcpTools: readonly ["summer_is_running", "summer_play", "summer_stop", "summer_clear_console", "summer_get_diagnostics", "summer_get_debugger_errors", "summer_get_script_errors"];
34
+ readonly testScenario: "Run the game from a clean state, wait for first frame, report whether it runs cleanly or has errors.";
35
+ }, {
20
36
  readonly name: "gdscript-patterns";
21
37
  readonly category: "scripting-patterns";
22
38
  readonly public: true;
@@ -35,6 +35,41 @@ const ALL_CLIENTS = AGENT_CLIENTS;
35
35
  * with shippable content). When a NEXT skill ships, mirror it here.
36
36
  */
37
37
  export const SKILL_REGISTRY = [
38
+ {
39
+ name: "debug",
40
+ category: "debugging",
41
+ public: true,
42
+ clients: ALL_CLIENTS,
43
+ recommended: true,
44
+ requiresMcpTools: [
45
+ "summer_get_console",
46
+ "summer_get_debugger_errors",
47
+ "summer_get_script_errors",
48
+ "summer_get_diagnostics",
49
+ "summer_clear_console",
50
+ "summer_play",
51
+ "summer_stop",
52
+ "summer_inspect_node",
53
+ ],
54
+ testScenario: "Triage a runtime crash end-to-end: read errors, locate code, propose fix, apply on approval, verify.",
55
+ },
56
+ {
57
+ name: "play",
58
+ category: "scene-and-project",
59
+ public: true,
60
+ clients: ALL_CLIENTS,
61
+ recommended: true,
62
+ requiresMcpTools: [
63
+ "summer_is_running",
64
+ "summer_play",
65
+ "summer_stop",
66
+ "summer_clear_console",
67
+ "summer_get_diagnostics",
68
+ "summer_get_debugger_errors",
69
+ "summer_get_script_errors",
70
+ ],
71
+ testScenario: "Run the game from a clean state, wait for first frame, report whether it runs cleanly or has errors.",
72
+ },
38
73
  {
39
74
  name: "gdscript-patterns",
40
75
  category: "scripting-patterns",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "summer-engine",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "CLI and MCP tools for Summer Engine — the AI-native game engine",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -0,0 +1,94 @@
1
+ # Skill Spec: /debug
2
+
3
+ ## Fixture
4
+
5
+ - A Godot/Summer project with one or more reproducible bugs.
6
+ - Summer Engine running, MCP available.
7
+ - Host file tools available (Read, Edit, Write).
8
+
9
+ ## Case 1: Happy Path — runtime error with clear stack trace
10
+
11
+ **Fixture:** `scripts/player.gd` has `velocity.y -= GRAVTY * delta` (typo: GRAVTY instead of GRAVITY). Player crashes on `_physics_process` first frame.
12
+
13
+ **Input:** "Debug my game. The player crashes the moment I press play."
14
+
15
+ **Expected MCP tool sequence (in order):**
16
+
17
+ 1. (Skill asks the user one clarifying question and waits.)
18
+ 2. `summer_get_script_errors` — finds `Identifier "GRAVTY" not declared` at scripts/player.gd:14
19
+ 3. (Optional: `summer_get_console` to confirm no other errors hiding)
20
+ 4. `Read scripts/player.gd` — read 20–40 lines around line 14
21
+ 5. (Skill states hypothesis: typo, GRAVTY → GRAVITY)
22
+ 6. (Skill asks: "May I edit `scripts/player.gd` to rename GRAVTY → GRAVITY?")
23
+ 7. `Edit scripts/player.gd` — apply fix on user OK
24
+ 8. `summer_get_script_errors` — verify clean
25
+
26
+ **Assertions:**
27
+
28
+ - [ ] Skill asks for the user's description before any tool call.
29
+ - [ ] First diagnostic tool called is `summer_get_script_errors` (cheapest).
30
+ - [ ] Skill does NOT grep the whole codebase before reading the error.
31
+ - [ ] Skill reads only the file + lines indicated by the error, not the whole file.
32
+ - [ ] Skill states a single specific hypothesis before proposing a fix.
33
+ - [ ] Skill asks "May I edit …" before writing.
34
+ - [ ] After the fix, skill re-runs `summer_get_script_errors` to verify.
35
+ - [ ] Skill never calls `summer_set_resource_property` on an inline sub-resource (silent-fail trap).
36
+
37
+ ## Case 2: Failure Path — bug only reproduces at runtime
38
+
39
+ **Fixture:** Game compiles fine but crashes when player picks up a coin: `Invalid call. Nonexistent function 'play' in base 'Nil'.` AudioStreamPlayer node was deleted in editor but pickup script still references it.
40
+
41
+ **Input:** "When I pick up a coin, the game crashes."
42
+
43
+ **Expected behavior:**
44
+
45
+ - Skill calls `summer_get_script_errors` first → clean.
46
+ - Skill realizes it's runtime: `summer_clear_console`, `summer_play`.
47
+ - Skill asks user: "Reproduce the bug now — pick up a coin."
48
+ - After confirmation, calls `summer_get_debugger_errors`.
49
+ - Stack trace points to `coin.gd:32`, function `_on_body_entered`, calling `audio.play()` where `audio` is `null`.
50
+ - Skill calls `summer_inspect_node "./Coin/AudioStreamPlayer"` to confirm the node is missing.
51
+ - Skill proposes: re-add the AudioStreamPlayer (via `summer_add_node`) OR null-check in code.
52
+ - Asks the user which.
53
+
54
+ **Assertions:**
55
+
56
+ - [ ] Skill recognizes that script-errors-clean + crash-on-runtime = runtime-only bug.
57
+ - [ ] Skill explicitly invokes the play→reproduce→read-debugger loop, not just one shot.
58
+ - [ ] Skill verifies the missing-node hypothesis with `summer_inspect_node` BEFORE editing code.
59
+ - [ ] Skill offers BOTH options (fix scene OR fix code) instead of unilaterally picking.
60
+
61
+ ## Case 3: Edge Case — user gives a vague description
62
+
63
+ **Fixture:** Same as Case 1.
64
+
65
+ **Input:** "Something's broken."
66
+
67
+ **Expected behavior:**
68
+
69
+ - Skill asks one focused follow-up: "What's happening, and when?"
70
+ - Does NOT start grepping code or running tools blindly.
71
+
72
+ **Assertions:**
73
+
74
+ - [ ] No tool calls happen until the user gives a concrete symptom.
75
+ - [ ] Skill's follow-up is one short question, not a checklist.
76
+
77
+ ## Case 4: No Summer MCP — fallback path
78
+
79
+ **Fixture:** Same as Case 1, but Summer MCP is unavailable (engine not connected).
80
+
81
+ **Input:** "Debug my game."
82
+
83
+ **Expected behavior:**
84
+
85
+ - Skill detects MCP unavailable.
86
+ - Asks the user to copy-paste from Godot's Output and Debugger panels.
87
+ - Reasons over the pasted text the same way it would over MCP output.
88
+ - Continues with the rest of the workflow (read code, hypothesize, propose fix).
89
+
90
+ **Assertions:**
91
+
92
+ - [ ] Skill does not blindly call `summer_*` tools and fail.
93
+ - [ ] Skill explicitly asks for the panel output instead of guessing.
94
+ - [ ] The rest of the loop (hypothesis → "May I edit?" → verify) still runs.
@@ -0,0 +1,88 @@
1
+ # Skill Spec: /play
2
+
3
+ ## Fixture
4
+
5
+ - A Godot/Summer project with a main scene set in `project.godot`.
6
+ - Summer Engine running, MCP available.
7
+
8
+ ## Case 1: Happy Path — clean run
9
+
10
+ **Fixture:** Project compiles cleanly. Game is not currently running.
11
+
12
+ **Input:** "Play."
13
+
14
+ **Expected MCP tool sequence (in order):**
15
+
16
+ 1. `summer_is_running` — false
17
+ 2. `summer_get_script_errors` — clean
18
+ 3. `summer_clear_console`
19
+ 4. `summer_play`
20
+ 5. (~2 second wait)
21
+ 6. `summer_get_diagnostics`
22
+ 7. `summer_get_debugger_errors`
23
+ 8. (Skill reports "Running. No errors or warnings.")
24
+
25
+ **Assertions:**
26
+
27
+ - [ ] First call is `summer_is_running` to avoid double-launch.
28
+ - [ ] `summer_get_script_errors` runs BEFORE `summer_play` (avoid noisy fail).
29
+ - [ ] `summer_clear_console` runs before `summer_play` (so post-run output is clean).
30
+ - [ ] Skill waits before reading post-play state (≥1 second).
31
+ - [ ] Skill does NOT call `summer_stop` on its own.
32
+ - [ ] Skill reports state in one short sentence, doesn't dump raw tool output.
33
+
34
+ ## Case 2: Failure — game is already running
35
+
36
+ **Fixture:** Game already running.
37
+
38
+ **Input:** "Play."
39
+
40
+ **Expected behavior:**
41
+
42
+ - `summer_is_running` returns true.
43
+ - Skill asks: "It's already playing. Stop and restart, or leave it?"
44
+ - Waits for user.
45
+ - On "leave it": no further calls. On "stop and restart": `summer_stop`, then continues from step 2.
46
+
47
+ **Assertions:**
48
+
49
+ - [ ] Skill does not unilaterally restart.
50
+ - [ ] Skill does not unilaterally `summer_stop` without asking.
51
+
52
+ ## Case 3: Compile error before play
53
+
54
+ **Fixture:** Script error in `scripts/player.gd:14` (typo). Game not running.
55
+
56
+ **Input:** "Play."
57
+
58
+ **Expected behavior:**
59
+
60
+ - `summer_is_running` → false.
61
+ - `summer_get_script_errors` → returns error.
62
+ - Skill stops, surfaces the error, suggests `/debug`.
63
+ - Does NOT call `summer_play` (would just produce more noise).
64
+
65
+ **Assertions:**
66
+
67
+ - [ ] No `summer_play` call when script errors are present.
68
+ - [ ] Error message is one line, not raw tool output.
69
+ - [ ] Skill explicitly mentions `/debug` as the next move.
70
+
71
+ ## Case 4: Runtime crash
72
+
73
+ **Fixture:** Compiles fine. Game crashes on first frame (e.g. null pointer in `_ready`).
74
+
75
+ **Input:** "Play."
76
+
77
+ **Expected behavior:**
78
+
79
+ - Steps 1–5 run normally.
80
+ - `summer_get_debugger_errors` returns a stack trace.
81
+ - Skill reports the crash in one line and offers `/debug`.
82
+ - Does NOT auto-`summer_stop` — the user might want to inspect.
83
+
84
+ **Assertions:**
85
+
86
+ - [ ] Skill detects the crash via `summer_get_debugger_errors`, not by absence of output.
87
+ - [ ] Crash report is concise: error class, location, message.
88
+ - [ ] Skill explicitly mentions `/debug` as the next move.
@@ -4,6 +4,7 @@ description: Decision guide for how to create different game asset types — gen
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: asset-pipeline
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep summer_search_assets summer_import_asset summer_generate_image summer_generate_3d summer_generate_audio summer_import_from_url
8
9
  ---
9
10
 
@@ -138,6 +138,13 @@ skills:
138
138
  public: true
139
139
  recommended: true
140
140
  last-tested: 2026-05-05
141
+ - name: play
142
+ category: scene-and-project
143
+ status: HAVE
144
+ public: true
145
+ recommended: true
146
+ user-invocable: true
147
+ last-tested: 2026-05-05
141
148
  - name: make-game
142
149
  category: scene-and-project
143
150
  status: HAVE
@@ -600,11 +607,14 @@ skills:
600
607
  public: true
601
608
 
602
609
  # ── debugging ──
603
- - name: debugger-basics
610
+ - name: debug
604
611
  category: debugging
605
- status: NEXT
612
+ status: HAVE
606
613
  public: true
607
- - name: error-triage-via-summer-mcp
614
+ recommended: true
615
+ user-invocable: true
616
+ last-tested: 2026-05-05
617
+ - name: debugger-basics
608
618
  category: debugging
609
619
  status: NEXT
610
620
  public: true
@@ -4,6 +4,7 @@ description: First-person character controller with WASD movement, mouse look, j
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: character-controllers
7
+ user-invocable: false
7
8
  template-id: template-3d-fps
8
9
  allowed-tools: Read Grep summer_add_node summer_set_prop summer_set_resource_property summer_input_map_bind summer_save_scene summer_get_script_errors
9
10
  paths: ["**/*.gd", "**/*.tscn"]
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: debug
3
+ description: Use when the user reports their game is broken, crashing, behaving unexpectedly, or says "debug", "fix", "something's wrong", or "it's not working". Triages the bug end-to-end via Summer Engine's diagnostic tools (console, debugger errors, script errors), locates the offending code, proposes a fix, and verifies the fix.
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: debugging
7
+ user-invocable: true
8
+ allowed-tools: Read Grep Glob Edit summer_get_console summer_get_debugger_errors summer_get_script_errors summer_get_diagnostics summer_clear_console summer_play summer_stop summer_is_running summer_get_scene_tree summer_inspect_node
9
+ paths: ["**/*.gd", "**/*.tscn", "**/*.cs"]
10
+ ---
11
+
12
+ # /debug — Triage and Fix a Bug
13
+
14
+ ## Overview
15
+
16
+ A bug in a Godot/Summer game can hide in three places: scripts (GDScript/C# compile or runtime errors), the engine console (printed messages, warnings), or the scene/resource state (wrong property, missing collider, broken signal connection). This skill walks all three before guessing.
17
+
18
+ **Core principle:** read what the engine actually saw, not what the code looks like it does.
19
+
20
+ ## Steps
21
+
22
+ ### 1. Get the user's description first
23
+
24
+ Ask one short question:
25
+
26
+ > What's happening? Briefly: when does it go wrong, and what do you expect vs. what you see?
27
+
28
+ Wait for the answer before doing anything else. Don't start grepping code yet — the user's words narrow the search 10x.
29
+
30
+ ### 2. Read the engine's actual error state
31
+
32
+ Always do this in order. Each tool answers a different question.
33
+
34
+ **Preferred (Summer MCP):**
35
+
36
+ ```
37
+ summer_get_script_errors # Compile errors? (cheapest, do first)
38
+ summer_get_console # Print() output, warnings, info
39
+ summer_get_debugger_errors # Runtime errors with stack traces
40
+ summer_get_diagnostics # Aggregated count + summary
41
+ ```
42
+
43
+ If the bug is reproducible only at runtime and the engine isn't currently playing:
44
+
45
+ ```
46
+ summer_clear_console # Clean slate
47
+ summer_play # Run it
48
+ # (let the user reproduce the bug)
49
+ summer_get_debugger_errors # Now read what blew up
50
+ summer_stop # When done
51
+ ```
52
+
53
+ **Fallback (no MCP — engine isn't connected):**
54
+
55
+ Ask the user to copy-paste the engine's Output panel and Debugger errors. Inspect manually.
56
+
57
+ ### 3. Locate the offending code
58
+
59
+ For each error, follow the stack trace to the file + line. Use host file tools:
60
+
61
+ ```
62
+ Read <file>:<line-near-error>
63
+ ```
64
+
65
+ Read 20–40 lines around the error site. Don't open the whole file unless you need to — context budget matters.
66
+
67
+ If the error mentions a node path (e.g. `./World/Player/Camera`), inspect it:
68
+
69
+ ```
70
+ summer_inspect_node "./World/Player/Camera"
71
+ ```
72
+
73
+ ### 4. Form a hypothesis and propose the fix
74
+
75
+ State it in one sentence:
76
+
77
+ > The error happens because <root cause>. The fix is <one-line summary>.
78
+
79
+ Then ask:
80
+
81
+ > May I edit `<file>` to <do exactly this>?
82
+
83
+ Don't guess at multiple fixes at once. Pick one. If wrong, the next iteration is cheap.
84
+
85
+ ### 5. Apply the fix
86
+
87
+ On user yes, edit the file with host tools (`Edit`/`Write`). Single focused change.
88
+
89
+ ### 6. Verify
90
+
91
+ Re-run the relevant check:
92
+
93
+ ```
94
+ summer_get_script_errors # If it was a compile error
95
+ summer_play → summer_get_debugger_errors → summer_stop # If runtime
96
+ ```
97
+
98
+ Report:
99
+
100
+ > Fixed. <One-line description of what changed.>
101
+
102
+ If still broken: go back to step 4 with the new error state. State explicitly that the first hypothesis was wrong.
103
+
104
+ ## Common bug patterns and how to spot them
105
+
106
+ | Symptom | Likely cause | First check |
107
+ |---|---|---|
108
+ | `Invalid set index 'X' (on base: Nil)` | Node path resolved to null | `summer_inspect_node` the path |
109
+ | `Identifier not declared` (script error) | Missing `class_name` or wrong scope | `summer_get_script_errors` |
110
+ | Game runs but sprite doesn't appear | Node added but not visible (transform / parent / layer) | `summer_get_scene_tree` + inspect parent |
111
+ | Signal handler never fires | Connection not made, or connected to wrong instance | `summer_inspect_node` of emitter, check connections |
112
+ | Player falls through floor | CollisionShape3D has no shape, or shape is inline sub_resource | `summer_inspect_resource` on the CollisionShape |
113
+ | Mouse capture stuck | `Input.set_mouse_mode` not paired with release on focus loss | `summer_get_console` for warnings |
114
+ | Game hangs on start | Infinite loop in `_ready()` or `_process()` | `summer_get_debugger_errors` after a forced stop |
115
+
116
+ ## Anti-patterns (don't do these)
117
+
118
+ - **Don't** start by reading the user's whole codebase. The error tells you where to look.
119
+ - **Don't** propose a sweeping refactor for a single bug. One file, one change, verify.
120
+ - **Don't** call `summer_set_resource_property` on an inline `sub_resource` — it silently fails. See `_shared/mcp-tools-reference.md` § "Trap".
121
+ - **Don't** call `summer_play` without `summer_clear_console` first if you want clean output to read.
122
+ - **Don't** skip the user's description in step 1. "What were you trying to do" beats "what does the code do" 9/10 times.
123
+
124
+ ## Collaborative protocol
125
+
126
+ This skill writes files. Always ask before each edit step. See `_shared/collaborative-protocol.md`.
127
+
128
+ ## Want a working starter?
129
+
130
+ No template — this is a workflow, not a project scaffold. It works against any Godot/Summer project that has Summer MCP wired up.
131
+
132
+ ## See also
133
+
134
+ - `_shared/mcp-tools-reference.md` — full tool list
135
+ - `_shared/godot-version.md` — engine version + LLM cutoff risks
136
+ - `scripting-patterns/gdscript-patterns/SKILL.md` — what good GDScript looks like
137
+ - `scene-and-project/play/SKILL.md` — running the game
@@ -4,6 +4,7 @@ description: Lighting setup, WorldEnvironment, sky, and shadows for 3D scenes. U
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: rendering-and-lighting
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep summer_add_node summer_set_prop summer_set_resource_property summer_save_scene
8
9
  paths: ["**/*.tscn", "**/*.tres"]
9
10
  ---
@@ -4,6 +4,7 @@ description: End-to-end game creation workflow that orchestrates project setup,
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: scene-and-project
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep Glob Write Edit summer_get_project_context summer_get_agent_playbook summer_create_scene summer_open_scene summer_save_scene summer_add_node summer_set_prop summer_set_resource_property summer_instantiate_scene summer_play summer_stop summer_get_diagnostics
8
9
  paths: ["**/*.gd", "**/*.tscn", "**/project.godot"]
9
10
  ---
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: play
3
+ description: Use when the user says "play", "run it", "test it", "let me see it", or "start the game". Runs the project in Summer Engine, briefly waits, then reports what's happening — clean run, errors, or warnings.
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: scene-and-project
7
+ user-invocable: true
8
+ allowed-tools: summer_is_running summer_play summer_stop summer_clear_console summer_get_console summer_get_debugger_errors summer_get_script_errors summer_get_diagnostics
9
+ paths: ["**/project.godot", "**/*.tscn"]
10
+ ---
11
+
12
+ # /play — Run the Game
13
+
14
+ ## Overview
15
+
16
+ Press play. Watch what happens. Report it back.
17
+
18
+ ## Steps
19
+
20
+ ### 1. Check if it's already running
21
+
22
+ ```
23
+ summer_is_running
24
+ ```
25
+
26
+ If running:
27
+ > The game is already playing. Stop and restart, or leave it?
28
+
29
+ Wait for the user. Don't unilaterally stop.
30
+
31
+ If not running, continue.
32
+
33
+ ### 2. Pre-check for compile errors
34
+
35
+ Cheapest call. If the script won't compile, play will fail with a noisy error — better to surface that clearly first.
36
+
37
+ ```
38
+ summer_get_script_errors
39
+ ```
40
+
41
+ If errors found:
42
+ > Won't run yet — there's a script error: `<one-line summary>`. Want me to /debug it?
43
+
44
+ Stop here on user's no.
45
+
46
+ ### 3. Clear and play
47
+
48
+ ```
49
+ summer_clear_console
50
+ summer_play
51
+ ```
52
+
53
+ ### 4. Brief wait, then read state
54
+
55
+ Wait ~2 seconds (so the engine has time to process at least one frame). Then:
56
+
57
+ ```
58
+ summer_get_diagnostics
59
+ summer_get_debugger_errors
60
+ ```
61
+
62
+ ### 5. Report
63
+
64
+ Three outcomes:
65
+
66
+ **Clean:** `summer_get_diagnostics` shows zero errors and zero warnings.
67
+
68
+ > Running. No errors or warnings. Tell me what you see and I'll dig in if needed.
69
+
70
+ **Warnings only:**
71
+
72
+ > Running. <N> warnings — `<one-line summary of the most important one>`. Want me to look at them?
73
+
74
+ **Runtime error:**
75
+
76
+ > Crashed: `<error class>` at `<file>:<line>` — `<message>`. Want me to /debug?
77
+
78
+ ### 6. Don't auto-stop
79
+
80
+ Leave the game running. The user wants to play it. Stop only on explicit ask.
81
+
82
+ ## Common patterns
83
+
84
+ | Situation | Right move |
85
+ |---|---|
86
+ | User says "play" with no context | Steps 1–5 above |
87
+ | User says "play and watch it for me" | After step 5, set a timer / poll `summer_get_debugger_errors` every few seconds for the duration the user specifies |
88
+ | User says "play the boss fight scene" | Open the specific scene first (`summer_open_scene "scenes/boss_fight.tscn"`), then steps 1–5 |
89
+ | Game is already running and user says "play" again | Step 1's check covers it — ask before restarting |
90
+
91
+ ## Anti-patterns
92
+
93
+ - **Don't** auto-stop a running game just because the user said "play". They probably mean "make sure it's playing", not "stop and restart".
94
+ - **Don't** call `summer_get_console` instead of `summer_get_debugger_errors` for runtime errors. Console has print() output and warnings; debugger errors has the stack traces.
95
+ - **Don't** report "no errors" before waiting for the engine to actually run a frame. A 0-frame snapshot is meaningless.
96
+
97
+ ## Collaborative protocol
98
+
99
+ This skill mutates engine state (clearing console, starting play). It's a read-side workflow with one safe write (`summer_play`). No "May I" needed — this is what the user asked for. Do confirm before stopping a running game.
100
+
101
+ ## Want a working starter?
102
+
103
+ No template — works against any Godot/Summer project that has a main scene set in `project.godot`.
104
+
105
+ ## See also
106
+
107
+ - `_shared/mcp-tools-reference.md` — full tool list
108
+ - `debugging/debug/SKILL.md` — what to do when /play surfaces an error
@@ -4,6 +4,7 @@ description: How to structure scenes, when to use sub-scenes, node hierarchy con
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: scene-and-project
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep summer_create_scene summer_open_scene summer_get_scene_tree summer_add_node summer_instantiate_scene summer_save_scene
8
9
  paths: ["**/*.tscn"]
9
10
  ---
@@ -4,6 +4,7 @@ description: Common GDScript idioms — type hints, signals, exports, onready, l
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: scripting-patterns
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep summer_get_script_errors
8
9
  paths: ["**/*.gd"]
9
10
  ---
@@ -4,6 +4,7 @@ description: HUD, menus, health bars, progress bars, and Control-based UI layout
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: ui-and-ux
7
+ user-invocable: false
7
8
  allowed-tools: Read Grep summer_add_node summer_set_prop summer_connect_signal summer_save_scene
8
9
  paths: ["**/*.tscn", "**/*.gd"]
9
10
  ---