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.
- package/CHANGELOG.md +67 -0
- package/README.md +7 -2
- package/dist/bin/summer.js +24 -1
- package/dist/commands/doctor.js +44 -20
- package/dist/commands/setup.js +77 -6
- package/dist/commands/skills.js +5 -3
- package/dist/lib/format.d.ts +23 -0
- package/dist/lib/format.js +60 -0
- package/dist/lib/format.test.d.ts +1 -0
- package/dist/lib/format.test.js +36 -0
- package/dist/lib/skills-registry.d.ts +16 -0
- package/dist/lib/skills-registry.js +35 -0
- package/package.json +5 -3
- package/skills/_meta/skill-test/SKILL.md +1 -1
- package/skills/_shared/summer-folder.md +44 -0
- package/skills/_tests/specs/art-direction.md +115 -0
- package/skills/_tests/specs/audio-direction.md +123 -0
- package/skills/_tests/specs/brainstorm-game.md +99 -0
- package/skills/_tests/specs/debug.md +94 -0
- package/skills/_tests/specs/design-level.md +113 -0
- package/skills/_tests/specs/design-mechanic.md +112 -0
- package/skills/_tests/specs/design-npc.md +112 -0
- package/skills/_tests/specs/export-and-ship.md +114 -0
- package/skills/_tests/specs/fps-controller.md +5 -5
- package/skills/_tests/specs/play.md +88 -0
- package/skills/_tests/specs/setup-multiplayer.md +106 -0
- package/skills/_tests/specs/tune-performance.md +104 -0
- package/skills/_tests/specs/vfx.md +98 -0
- package/skills/ai-and-npcs/design-npc/SKILL.md +343 -0
- package/skills/asset-pipeline/asset-strategy/SKILL.md +1 -0
- package/skills/audio/audio-direction/SKILL.md +258 -0
- package/skills/catalog.yaml +13 -3
- package/skills/character-controllers/fps-controller/SKILL.md +1 -0
- package/skills/debugging/debug/SKILL.md +137 -0
- package/skills/deployment/export-and-ship/SKILL.md +300 -0
- package/skills/gameplay-mechanics/design-mechanic/SKILL.md +265 -0
- package/skills/level-design/design-level/SKILL.md +266 -0
- package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +328 -0
- package/skills/performance/tune-performance/SKILL.md +219 -0
- package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -0
- package/skills/rendering-and-lighting/art-direction/SKILL.md +284 -0
- package/skills/scene-and-project/brainstorm-game/SKILL.md +210 -0
- package/skills/scene-and-project/make-game/SKILL.md +1 -0
- package/skills/scene-and-project/play/SKILL.md +108 -0
- package/skills/scene-and-project/scene-composition/SKILL.md +1 -0
- package/skills/scripting-patterns/gdscript-patterns/SKILL.md +1 -0
- package/skills/ui-and-ux/ui-basics/SKILL.md +1 -0
- 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
|
|
35
|
+
Then open your agent and try one of the slash commands:
|
|
36
36
|
|
|
37
|
-
|
|
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
|
|
package/dist/bin/summer.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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("
|
|
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
|
-
|
|
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: `
|
|
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:
|
|
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:
|
|
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: "
|
|
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 ?
|
|
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
|
|
102
|
+
label: "Engine",
|
|
94
103
|
status: "ok",
|
|
95
|
-
message:
|
|
104
|
+
message: tildeify(display),
|
|
96
105
|
details: { path: binary },
|
|
97
106
|
};
|
|
98
107
|
}
|
|
99
108
|
return {
|
|
100
109
|
id: "engine-install",
|
|
101
|
-
label: "Engine
|
|
110
|
+
label: "Engine",
|
|
102
111
|
status: "warning",
|
|
103
|
-
message: "
|
|
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: "
|
|
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: `
|
|
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:
|
|
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: "
|
|
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("
|
|
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"
|
|
231
|
-
|
|
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;
|
package/dist/commands/setup.js
CHANGED
|
@@ -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
|
-
|
|
114
|
-
|
|
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(`
|
|
132
|
+
console.log(` ${sym.ok()} Linked to ${c.bold(agentLabel)} ${c.dim(tildeify(config.path))}`);
|
|
118
133
|
}
|
|
119
134
|
else {
|
|
120
|
-
console.log(`
|
|
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.
|
|
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
|
}
|
package/dist/commands/skills.js
CHANGED
|
@@ -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 ${
|
|
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 ${
|
|
252
|
+
console.log(`Cursor rules are in ${tildeified}/summer-<skill>.mdc`);
|
|
251
253
|
}
|
|
252
254
|
else {
|
|
253
|
-
console.log(`Windsurf rules are in ${
|
|
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.
|
|
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. **
|
|
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
|
|