summer-engine 1.3.0 → 1.3.2

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/CHANGELOG.md +67 -0
  2. package/README.md +7 -2
  3. package/dist/bin/summer.js +24 -1
  4. package/dist/commands/doctor.js +44 -20
  5. package/dist/commands/setup.js +77 -6
  6. package/dist/commands/skills.js +5 -3
  7. package/dist/lib/format.d.ts +23 -0
  8. package/dist/lib/format.js +60 -0
  9. package/dist/lib/format.test.d.ts +1 -0
  10. package/dist/lib/format.test.js +36 -0
  11. package/dist/lib/skills-registry.d.ts +16 -0
  12. package/dist/lib/skills-registry.js +35 -0
  13. package/package.json +5 -3
  14. package/skills/_meta/skill-test/SKILL.md +1 -1
  15. package/skills/_shared/summer-folder.md +44 -0
  16. package/skills/_tests/specs/art-direction.md +115 -0
  17. package/skills/_tests/specs/audio-direction.md +123 -0
  18. package/skills/_tests/specs/brainstorm-game.md +99 -0
  19. package/skills/_tests/specs/debug.md +94 -0
  20. package/skills/_tests/specs/design-level.md +113 -0
  21. package/skills/_tests/specs/design-mechanic.md +112 -0
  22. package/skills/_tests/specs/design-npc.md +112 -0
  23. package/skills/_tests/specs/export-and-ship.md +114 -0
  24. package/skills/_tests/specs/fps-controller.md +5 -5
  25. package/skills/_tests/specs/play.md +88 -0
  26. package/skills/_tests/specs/setup-multiplayer.md +106 -0
  27. package/skills/_tests/specs/tune-performance.md +104 -0
  28. package/skills/_tests/specs/vfx.md +98 -0
  29. package/skills/ai-and-npcs/design-npc/SKILL.md +343 -0
  30. package/skills/asset-pipeline/asset-strategy/SKILL.md +1 -0
  31. package/skills/audio/audio-direction/SKILL.md +258 -0
  32. package/skills/catalog.yaml +13 -3
  33. package/skills/character-controllers/fps-controller/SKILL.md +1 -0
  34. package/skills/debugging/debug/SKILL.md +137 -0
  35. package/skills/deployment/export-and-ship/SKILL.md +300 -0
  36. package/skills/gameplay-mechanics/design-mechanic/SKILL.md +265 -0
  37. package/skills/level-design/design-level/SKILL.md +266 -0
  38. package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +328 -0
  39. package/skills/performance/tune-performance/SKILL.md +219 -0
  40. package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -0
  41. package/skills/rendering-and-lighting/art-direction/SKILL.md +284 -0
  42. package/skills/scene-and-project/brainstorm-game/SKILL.md +210 -0
  43. package/skills/scene-and-project/make-game/SKILL.md +1 -0
  44. package/skills/scene-and-project/play/SKILL.md +108 -0
  45. package/skills/scene-and-project/scene-composition/SKILL.md +1 -0
  46. package/skills/scripting-patterns/gdscript-patterns/SKILL.md +1 -0
  47. package/skills/ui-and-ux/ui-basics/SKILL.md +1 -0
  48. package/skills/visual-effects/vfx/SKILL.md +280 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ All notable changes to summer-engine will be documented here. Following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/).
4
+
5
+ ## [1.3.2] — 2026-05-05
6
+
7
+ ### Fixed
8
+ - `summer_input_map_bind` syntax aligned across `fps-controller` SKILL.md and its behavioral test spec.
9
+ - `_meta/skill-test` static linter relaxed to allow forward-reference `See also` links to other SKILL.md files (warn instead of fail).
10
+
11
+ ### Added
12
+ - `_shared/summer-folder.md` — canonical `.summer/` folder convention (documents files written by `/summer:brainstorm-game`, `/summer:art-direction`, etc.).
13
+ - `CHANGELOG.md` — retroactive v1.0.0 → v1.3.1 history.
14
+
15
+ ## [1.3.1] — 2026-05-05
16
+
17
+ ### Added
18
+ - ASCII banner displays on bare `summer` command (`npx summer-engine`).
19
+ - ANSI colors throughout: green ✓ for OK, yellow ⚠ for warnings, red ✗ for failures.
20
+ - Brand line + colored slash command list in setup output.
21
+ - `/debug` workflow skill — triage and fix a bug end-to-end via Summer MCP diagnostics.
22
+ - `/play` workflow skill — run the game and report state.
23
+ - 7 specialist skills marked `user-invocable: false` (auto-trigger only).
24
+
25
+ ### Fixed
26
+ - `summer doctor` defaults to human-readable output instead of JSON.
27
+ - Engine path display shortened (`/Applications/Summer.app/Contents/MacOS/Summer` → `/Applications/Summer.app`).
28
+ - Home-relative paths now display tildeified.
29
+ - MCP server status no longer leaks "stdio" implementation detail (now reads "ready").
30
+ - `tools/summer-cli/src/bin/` finally tracked in git (root `.gitignore`'s `[Bb]in/` was silently excluding the npm entrypoint).
31
+ - `LICENSE` now bundled in the npm tarball.
32
+
33
+ ## [1.3.0] — 2026-05-05
34
+
35
+ ### Added
36
+ - 20-category skill library scaffold with descriptive folder names (`character-controllers`, `gameplay-mechanics`, `scripting-patterns`, etc.).
37
+ - `_shared/` directory with 5 canonical references (godot-version, mcp-tools-reference, collaborative-protocol, template-registry, gd-style).
38
+ - `_meta/` directory with 3 meta-skills (skill-test linter, skill-create bootstrap, skill-improve eval harness).
39
+ - `_tests/specs/` directory with per-skill behavioral test specs (fps-controller as canonical format).
40
+ - `catalog.yaml` — 85-skill roadmap with HAVE / NEXT / LATER status.
41
+
42
+ ### Changed
43
+ - 7 existing skills migrated into category folders with Anthropic-spec frontmatter (`category`, `template-id`, `allowed-tools`, `paths`).
44
+ - CLI `skills install` walks `<skillsDir>/<category>/<name>/` paths.
45
+
46
+ ## [1.2.1] — 2026-05-05
47
+
48
+ ### Added
49
+ - `summer setup <agent>` — one-shot MCP config + recommended skills install + doctor.
50
+ - `summer doctor` — node, login, engine, local API, MCP boot diagnostics.
51
+ - `summer mcp setup <agent>` — idempotent JSON/TOML config writer.
52
+ - Multi-agent skills install (codex, claude-code, cursor, windsurf with user/project scopes; Cursor `.mdc` rule generation; Windsurf rule blocks).
53
+ - Skill registry (`src/lib/skills-registry.ts`) with category metadata.
54
+ - Per-agent docs (AGENT_KIT, CLAUDE_CODE, CODEX, CURSOR, SKILLS, TEMPLATES).
55
+
56
+ ### Changed
57
+ - `/api/mcp/assets`: removed Pro gate; public/community asset search now free for all signed-in users (deployed on summerengine.com).
58
+ - `/api/mcp/log-local-call`: removed visible 100/week quota; auth-gated telemetry only.
59
+
60
+ ## [1.2.0] — 2026-04-23
61
+
62
+ ### Added
63
+ - Initial public release on npm.
64
+ - MCP server with 36 `summer_*` tools.
65
+ - 7 specialist skills (`fps-controller`, `gdscript-patterns`, `scene-composition`, `3d-lighting`, `ui-basics`, `asset-strategy`, `make-game`).
66
+ - CLI commands (`install`, `login`, `logout`, `status`, `run`, `open`, `create`, `list`, `skills`, `mcp`).
67
+ - MIT license.
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.2",
4
4
  "description": "CLI and MCP tools for Summer Engine — the AI-native game engine",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -25,7 +25,9 @@
25
25
  "files": [
26
26
  "dist/",
27
27
  "skills/",
28
- "README.md"
28
+ "README.md",
29
+ "LICENSE",
30
+ "CHANGELOG.md"
29
31
  ],
30
32
  "scripts": {
31
33
  "build": "tsc",
@@ -33,7 +35,7 @@
33
35
  "summer": "node dist/bin/summer.js",
34
36
  "test": "vitest run",
35
37
  "test:watch": "vitest",
36
- "prepublishOnly": "npm run build"
38
+ "prepublishOnly": "npm run build && npm test"
37
39
  },
38
40
  "engines": {
39
41
  "node": ">=18"
@@ -23,7 +23,7 @@ For a single skill at `skills/<category>/<name>/`, run these checks:
23
23
  4. **Collaborative protocol.** If the skill triggers user-visible writes, body must contain at least one of: `May I`, `I'm about to`, `Continue?`, `OK?`, `Proceed?`. Skills that only inspect/read are exempt.
24
24
  5. **Template-id resolved.** If the skill's frontmatter declares `template-id: <id>`, that id must exist in `_shared/template-registry.md`.
25
25
  6. **Tests/spec.md present.** Every skill in a non-`_meta` category must have `tests/spec.md` with at least one `## Case` heading.
26
- 7. **No broken relative links.** Every `](../...)` and `](./...)` in the body must point at an existing file.
26
+ 7. **Relative links resolve.** Every `](../_shared/...)`, `](../../_shared/...)`, `](./references/...)`, and `](./examples/...)` link must point at an existing file (FAIL if broken). Forward references in `## See also` to other SKILL.md files (e.g., `[design-mechanic](../../gameplay-mechanics/design-mechanic/SKILL.md)`) are allowed to dangle and only WARN if the target is missing — the catalog evolves and skills should be free to point at planned skills.
27
27
 
28
28
  Output:
29
29