summer-engine 4.0.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +3 -2
- package/.codex-plugin/plugin.json +15 -11
- package/.cursor-plugin/plugin.json +5 -3
- package/.factory-plugin/plugin.json +3 -2
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +48 -0
- package/README.md +21 -10
- package/dist/cli/command-usage.d.ts +8 -0
- package/dist/cli/command-usage.js +22 -0
- package/dist/cli/commands/config.js +3 -2
- package/dist/cli/commands/create.js +12 -3
- package/dist/cli/commands/list.d.ts +5 -0
- package/dist/cli/commands/list.js +30 -13
- package/dist/cli/commands/login.js +1 -1
- package/dist/cli/commands/memory.js +6 -2
- package/dist/cli/commands/ops.d.ts +20 -3
- package/dist/cli/commands/ops.js +60 -27
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.js +219 -16
- package/dist/cli/commands/skills.js +20 -7
- package/dist/cli/commands/status.d.ts +26 -0
- package/dist/cli/commands/status.js +36 -21
- package/dist/cli/commands/tool.js +4 -0
- package/dist/cli/index.js +2 -0
- package/dist/core/agent-host.d.ts +3 -0
- package/dist/core/agent-host.js +8 -2
- package/dist/core/capabilities/apps.d.ts +10 -10
- package/dist/core/capabilities/audit/args.d.ts +6 -6
- package/dist/core/capabilities/fabricate-mesh.d.ts +2 -2
- package/dist/core/capabilities/game-task-plan.d.ts +2 -2
- package/dist/core/capabilities/hdri-import.d.ts +2 -2
- package/dist/core/capabilities/motion-generation.d.ts +2 -2
- package/dist/core/capabilities/placement.d.ts +23 -23
- package/dist/core/capabilities/runtime-control.d.ts +38 -38
- package/dist/core/capabilities/seeing/args.d.ts +16 -16
- package/dist/core/capabilities/ui-control.d.ts +2 -2
- package/dist/core/capabilities/world-snapshot.d.ts +4 -4
- package/dist/core/cli-usage-beacon.d.ts +1 -0
- package/dist/core/cli-usage-beacon.js +18 -0
- package/dist/core/cli-usage.d.ts +49 -0
- package/dist/core/cli-usage.js +108 -0
- package/dist/core/feedback/client.d.ts +1 -1
- package/dist/core/feedback/client.js +3 -2
- package/dist/core/hosted-mcp.d.ts +7 -1
- package/dist/core/hosted-mcp.js +6 -4
- package/dist/core/library-search.d.ts +2 -2
- package/dist/core/ops/areas.d.ts +11 -0
- package/dist/core/ops/areas.js +24 -0
- package/dist/core/ops/catalog.d.ts +11 -0
- package/dist/core/ops/catalog.js +24 -1
- package/dist/core/ops/cloud.d.ts +10 -2
- package/dist/core/ops/cloud.js +51 -28
- package/dist/core/ops/contract.d.ts +2 -1
- package/dist/core/ops/contract.js +2 -10
- package/dist/core/ops/public-catalog.d.ts +48 -0
- package/dist/core/ops/public-catalog.js +56 -0
- package/dist/core/ops/search.d.ts +0 -1
- package/dist/core/ops/search.js +169 -37
- package/dist/core/telemetry.d.ts +10 -0
- package/dist/core/telemetry.js +58 -29
- package/dist/installer/agent-config.js +5 -0
- package/dist/installer/agent-table.d.ts +9 -0
- package/dist/installer/agent-table.js +23 -2
- package/dist/installer/cli-permissions.d.ts +65 -0
- package/dist/installer/cli-permissions.js +182 -0
- package/dist/installer/setup.d.ts +2 -0
- package/dist/installer/setup.js +3 -0
- package/dist/installer/skills-refresh.d.ts +29 -0
- package/dist/installer/skills-refresh.js +261 -0
- package/dist/installer/version-check.d.ts +22 -2
- package/dist/installer/version-check.js +66 -9
- package/dist/mcp/ops-surface.js +16 -6
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +38 -8
- package/dist/project-memory/agent-instructions.d.ts +47 -0
- package/dist/project-memory/agent-instructions.js +226 -0
- package/gemini-extension.json +3 -2
- package/integrations/README.md +1 -1
- package/integrations/claude/README.md +6 -0
- package/integrations/claude/manifest-target.json +4 -0
- package/integrations/gemini/README.md +11 -0
- package/integrations/windsurf/README.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +16 -0
- package/library/skills/README.md +5 -4
- package/library/skills/adaptive-music/SKILL.md +21 -0
- package/library/skills/agent-playtesting/SKILL.md +22 -0
- package/library/skills/ai-characters-in-games/SKILL.md +15 -0
- package/library/skills/ambient-bed/SKILL.md +16 -0
- package/library/skills/animated-loop/SKILL.md +19 -0
- package/library/skills/animation-tree/SKILL.md +26 -0
- package/library/skills/art-direction/SKILL.md +14 -0
- package/library/skills/asset-strategy/SKILL.md +30 -0
- package/library/skills/audio-direction/SKILL.md +16 -0
- package/library/skills/auto-fire-targeting/SKILL.md +14 -0
- package/library/skills/brainstorm-game/SKILL.md +13 -0
- package/library/skills/browse-templates/SKILL.md +10 -0
- package/library/skills/camera-rigs/SKILL.md +20 -0
- package/library/skills/character-animation-wiring/SKILL.md +24 -0
- package/library/skills/character-model/SKILL.md +28 -0
- package/library/skills/character-movement/SKILL.md +24 -0
- package/library/skills/character-portrait/SKILL.md +15 -0
- package/library/skills/cinematic-cutscene/SKILL.md +21 -0
- package/library/skills/combat-basics/SKILL.md +19 -0
- package/library/skills/compatibility-renderer-traps/SKILL.md +11 -0
- package/library/skills/concept-art/SKILL.md +13 -0
- package/library/skills/concept-to-level/SKILL.md +20 -1
- package/library/skills/concept-to-level/resource.yaml +2 -2
- package/library/skills/create-asset-sheet/SKILL.md +18 -0
- package/library/skills/debug/SKILL.md +32 -0
- package/library/skills/debugging-game-feel/SKILL.md +12 -0
- package/library/skills/design-level/SKILL.md +17 -0
- package/library/skills/design-mechanic/SKILL.md +17 -0
- package/library/skills/design-npc/SKILL.md +21 -0
- package/library/skills/diagnosing-perf-regressions/SKILL.md +12 -0
- package/library/skills/driving-the-editor-ui/SKILL.md +23 -0
- package/library/skills/environment-kit/SKILL.md +23 -0
- package/library/skills/export-and-ship/SKILL.md +23 -0
- package/library/skills/fabricating-assets/SKILL.md +23 -0
- package/library/skills/facial-and-lipsync/SKILL.md +22 -0
- package/library/skills/fps-controller/SKILL.md +17 -0
- package/library/skills/game-feel/SKILL.md +26 -0
- package/library/skills/gameskill/SKILL.md +11 -0
- package/library/skills/gdscript-patterns/SKILL.md +15 -0
- package/library/skills/generate-motion/SKILL.md +21 -0
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +17 -0
- package/library/skills/grow-analytics/SKILL.md +10 -0
- package/library/skills/headless-scripting/SKILL.md +13 -0
- package/library/skills/in-game-purchases/SKILL.md +11 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +17 -0
- package/library/skills/make-game/SKILL.md +29 -0
- package/library/skills/multiplayer/SKILL.md +12 -0
- package/library/skills/multiplayer-movement/SKILL.md +18 -0
- package/library/skills/multiplayer-project/SKILL.md +17 -0
- package/library/skills/multiplayer-publish/SKILL.md +17 -5
- package/library/skills/multiplayer-state/SKILL.md +16 -0
- package/library/skills/multiplayer-testing/SKILL.md +15 -0
- package/library/skills/music-track/SKILL.md +16 -0
- package/library/skills/navigate-summer/SKILL.md +15 -0
- package/library/skills/new-project/SKILL.md +11 -0
- package/library/skills/organic-model/SKILL.md +24 -0
- package/library/skills/pixel-art/SKILL.md +15 -0
- package/library/skills/play/SKILL.md +18 -0
- package/library/skills/playtesting-a-feature/SKILL.md +23 -0
- package/library/skills/procedural-animation/SKILL.md +18 -0
- package/library/skills/prop-model/SKILL.md +23 -0
- package/library/skills/publish-your-game/SKILL.md +27 -0
- package/library/skills/release-readiness/SKILL.md +12 -0
- package/library/skills/remote-deploy/SKILL.md +14 -0
- package/library/skills/retarget/SKILL.md +20 -0
- package/library/skills/running-in-the-cloud/SKILL.md +10 -0
- package/library/skills/save-load/SKILL.md +15 -0
- package/library/skills/scene-composition/SKILL.md +17 -0
- package/library/skills/scene-hierarchy-design/SKILL.md +24 -0
- package/library/skills/scene-scripting/SKILL.md +30 -0
- package/library/skills/scene-to-level/SKILL.md +22 -0
- package/library/skills/skill-test/SKILL.md +10 -0
- package/library/skills/skybox-panorama/SKILL.md +20 -0
- package/library/skills/sound-effect/SKILL.md +23 -0
- package/library/skills/spatial-placement/SKILL.md +30 -0
- package/library/skills/sprite-sheet/SKILL.md +23 -0
- package/library/skills/store-art/SKILL.md +11 -0
- package/library/skills/store-listing/SKILL.md +10 -0
- package/library/skills/summer-analytics/SKILL.md +15 -0
- package/library/skills/summer-apps/SKILL.md +29 -0
- package/library/skills/summer-assets/SKILL.md +31 -1
- package/library/skills/summer-assets/resource.yaml +2 -2
- package/library/skills/summer-cli/SKILL.md +133 -0
- package/library/skills/summer-cli/resource.yaml +31 -0
- package/library/skills/summer-friends/SKILL.md +14 -0
- package/library/skills/summer-leaderboards/SKILL.md +16 -0
- package/library/skills/summer-match-results/SKILL.md +17 -0
- package/library/skills/summer-matchmaking/SKILL.md +17 -0
- package/library/skills/summer-parties/SKILL.md +14 -0
- package/library/skills/summer-player-data/SKILL.md +14 -0
- package/library/skills/summer-store/SKILL.md +15 -0
- package/library/skills/summer-world-chat/SKILL.md +14 -0
- package/library/skills/summer-world-saves/SKILL.md +14 -0
- package/library/skills/text-to-motion/SKILL.md +15 -0
- package/library/skills/tileable-texture/SKILL.md +18 -0
- package/library/skills/trailer-shot/SKILL.md +18 -0
- package/library/skills/tune-performance/SKILL.md +26 -0
- package/library/skills/ui-basics/SKILL.md +14 -0
- package/library/skills/ui-graphics/SKILL.md +17 -0
- package/library/skills/use-widget-asset/SKILL.md +14 -0
- package/library/skills/using-summer/SKILL.md +26 -0
- package/library/skills/using-summer/resource.yaml +2 -1
- package/library/skills/vehicle-model/SKILL.md +27 -0
- package/library/skills/verifying-scenes/SKILL.md +29 -0
- package/library/skills/vfx-dissolve/SKILL.md +19 -0
- package/library/skills/vfx-fire/SKILL.md +26 -0
- package/library/skills/vfx-hit-spark/SKILL.md +23 -0
- package/library/skills/vfx-lightning/SKILL.md +23 -0
- package/library/skills/vfx-magic-glow/SKILL.md +23 -0
- package/library/skills/vfx-muzzle-flash/SKILL.md +23 -0
- package/library/skills/vfx-smoke/SKILL.md +26 -0
- package/library/skills/vfx-water-ripple/SKILL.md +23 -0
- package/library/skills/voice-line/SKILL.md +16 -0
- package/library/skills/web-and-touch-input/SKILL.md +19 -0
- package/library/skills/web-compatibility-look/SKILL.md +20 -0
- package/library/skills/world-building-3d/SKILL.md +28 -0
- package/package.json +3 -2
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +3 -2
- package/registry/generated/index.json +157 -118
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude-directory.json +168 -0
- package/registry/generated/plugin.claude.json +3 -2
- package/registry/generated/plugin.codex.json +15 -11
- package/registry/generated/plugin.cursor.json +5 -3
- package/registry/generated/plugin.factory.json +3 -2
- package/registry/generated/server.json +11 -2
- package/registry/generated/skills-index.md +5 -4
- package/registry/generated/skills-registry.json +16 -2
- package/registry/operations/legacy-names.json +35 -0
- package/registry/operations/public-catalog.json +33160 -0
package/dist/cli/commands/ops.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import { CLI_AREAS } from "../../core/ops/areas.js";
|
|
2
3
|
import { localEntries, localInputSchema, localWireForm, missingInput } from "../../core/ops/catalog.js";
|
|
3
|
-
import { answerOfHosted, callHostedV4, hostedCallFailure, searchEverywhere } from "../../core/ops/cloud.js";
|
|
4
|
-
import { errorAnswer, isLocalOp, localToolsFor, opFromCli, OP_NAME } from "../../core/ops/contract.js";
|
|
4
|
+
import { answerOfHosted, callHostedV4, describeFromShipped, hostedCallFailure, searchEverywhere } from "../../core/ops/cloud.js";
|
|
5
|
+
import { errorAnswer, isLocalOp, localToolsFor, opFromCli, OP_NAME, resolveOpName } from "../../core/ops/contract.js";
|
|
5
6
|
import { ENGINE_SPLIT_OPS, engineOpDef, splitToolArgs } from "../../core/ops/engine.js";
|
|
6
7
|
import { engineFailure, engineNotRunning } from "../../core/ops/engine-answer.js";
|
|
7
8
|
import { loadLibraryIndex } from "../../core/library-search.js";
|
|
8
9
|
import { FileSaver, withOutField } from "../../core/ops/save-files.js";
|
|
9
10
|
import { dispatchTool, EngineUnavailableError, ToolResultError } from "../../core/capabilities/tool-dispatch.js";
|
|
11
|
+
import { noteCliError, noteCliHosted } from "../../core/cli-usage.js";
|
|
10
12
|
/**
|
|
11
13
|
* The CLI view of the operation catalog (on by default; SUMMER_SURFACE=v3 leaves it out):
|
|
12
14
|
*
|
|
@@ -18,12 +20,7 @@ import { dispatchTool, EngineUnavailableError, ToolResultError } from "../../cor
|
|
|
18
20
|
* `summer tool` runs); cloud operations run on the hosted Summer server
|
|
19
21
|
* through its summer_call, with the person's sign-in.
|
|
20
22
|
*/
|
|
21
|
-
|
|
22
|
-
export const CLI_AREAS = [
|
|
23
|
-
"team", "profile", "docs", "assets", "makers", "jobs", "projects", "plans", "games", "store", "jams", "pricing",
|
|
24
|
-
"analytics", "experiments", "marketing", "players", "economy", "inbox", "apps", "studio", "chats", "stories",
|
|
25
|
-
"plugins", "engine", "account",
|
|
26
|
-
];
|
|
23
|
+
export { CLI_AREAS };
|
|
27
24
|
const RESERVED_FLAGS = new Set(["json", "dry-run", "wait", "idempotency-key", "input"]);
|
|
28
25
|
const camel = (flag) => flag.replace(/-([a-z0-9])/g, (_match, letter) => letter.toUpperCase());
|
|
29
26
|
/** A flag's field: camelCase (`--scene-path` is scenePath), or snake_case when the schema names it so (`--look-at` is look_at). */
|
|
@@ -95,6 +92,9 @@ export function parseCallFlags(argv, schema) {
|
|
|
95
92
|
}
|
|
96
93
|
}
|
|
97
94
|
out.input = { ...out.input, ...fields };
|
|
95
|
+
// --dry-run anywhere makes a dry run, whatever the input says: allow rules for dry runs rely on it (installer/cli-permissions.ts).
|
|
96
|
+
if (out.dryRun)
|
|
97
|
+
delete out.input.dryRun;
|
|
98
98
|
return out;
|
|
99
99
|
}
|
|
100
100
|
export const defaultOpsCliDeps = {
|
|
@@ -115,18 +115,21 @@ async function runLocal(op, call, deps) {
|
|
|
115
115
|
const tool = split?.tool ?? entry?.mcpTool ?? localToolsFor(op)[0];
|
|
116
116
|
if (!tool)
|
|
117
117
|
return errorAnswer("not_found", `No operation is named "${op}".`, { next: { op: "ops.search", input: { query: op } } });
|
|
118
|
-
|
|
118
|
+
// maxCharacters cuts the text an MCP client gets; the CLI prints the whole answer.
|
|
119
|
+
const { maxCharacters, ...input } = call.input;
|
|
120
|
+
const uncut = maxCharacters === undefined ? undefined : ["maxCharacters: the CLI prints the whole answer; cut it in the shell (for example | head -c 8000)."];
|
|
121
|
+
const args = split ? splitToolArgs(split, input) : input;
|
|
119
122
|
const def = engineOpDef(op);
|
|
120
123
|
const guide = guideOf(tool);
|
|
121
124
|
const describe = guide && def ? { op: "docs.read", input: { id: guide } } : { op: "ops.describe", input: { name: op } };
|
|
122
|
-
const missing = missingInput(localInputSchema(op),
|
|
125
|
+
const missing = missingInput(localInputSchema(op), input);
|
|
123
126
|
if (missing)
|
|
124
127
|
return errorAnswer("invalid_input", missing, { next: describe });
|
|
125
128
|
if (call.dryRun) {
|
|
126
129
|
return { ok: true, status: "dry_run", preview: { summary: `${entry?.title ?? op}: the input is valid. Nothing was done.`, changes: [], approval: entry?.approval ?? "none", estimate: null, valid: true } };
|
|
127
130
|
}
|
|
128
131
|
try {
|
|
129
|
-
return { ok: true, status: "done", result: await deps.dispatch(tool, args) };
|
|
132
|
+
return { ok: true, status: "done", result: await deps.dispatch(tool, args), ...(uncut ? { warnings: uncut } : {}) };
|
|
130
133
|
}
|
|
131
134
|
catch (error) {
|
|
132
135
|
if (error instanceof EngineUnavailableError)
|
|
@@ -234,6 +237,8 @@ export async function runOpFromCli(op, argv, deps = defaultOpsCliDeps) {
|
|
|
234
237
|
answer = await waitFor(answer, deps);
|
|
235
238
|
}
|
|
236
239
|
else {
|
|
240
|
+
// Summer's server records a cloud operation itself (X-Summer-Client-Surface: cli).
|
|
241
|
+
noteCliHosted();
|
|
237
242
|
// --out stays here: the files save on this machine once the answer (or the waited job) names them.
|
|
238
243
|
const files = deps.files ?? new FileSaver({ saveByDefault: true });
|
|
239
244
|
const taken = files.take(op, call.input);
|
|
@@ -243,12 +248,52 @@ export async function runOpFromCli(op, argv, deps = defaultOpsCliDeps) {
|
|
|
243
248
|
answer = await waitFor(answer, deps);
|
|
244
249
|
answer = await files.finish(op, answer, out);
|
|
245
250
|
}
|
|
251
|
+
if (!answer.ok)
|
|
252
|
+
noteCliError(answer.error.code);
|
|
246
253
|
(answer.ok ? deps.print : deps.printError)(call.json ? JSON.stringify(answer) : formatAnswer(answer, out));
|
|
247
254
|
if (!call.json && answer.ok)
|
|
248
255
|
for (const warning of answer.warnings ?? [])
|
|
249
256
|
deps.printError(`Warning: ${warning}`);
|
|
250
257
|
return answer.ok ? 0 : 1;
|
|
251
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* `summer ops search <words...>`: every operation, local and cloud, signed in
|
|
261
|
+
* or not; a cloud one that needs a sign-in says so. A how-to (docs.search
|
|
262
|
+
* first) points at the library.
|
|
263
|
+
*/
|
|
264
|
+
export async function searchFromCli(query, options, deps = defaultOpsCliDeps) {
|
|
265
|
+
const { items, warning } = await searchEverywhere(query, { area: options.area, limit: options.limit }, deps.hosted);
|
|
266
|
+
if (options.json) {
|
|
267
|
+
deps.print(JSON.stringify({ ok: true, status: "done", result: { items }, ...(warning ? { warnings: [warning] } : {}) }));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
for (const item of items)
|
|
271
|
+
deps.print(`${item.name.padEnd(32)} ${item.effect.padEnd(13)} ${item.title}${item.needsSignIn ? " (needs sign-in)" : ""}`);
|
|
272
|
+
if (items[0]?.name === "docs.search")
|
|
273
|
+
deps.print(`How-to guides for this: summer docs search --query ${shellWord(query)}`);
|
|
274
|
+
if (warning)
|
|
275
|
+
deps.printError(warning);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* `summer ops describe <op>`: a local operation from this package; a cloud
|
|
279
|
+
* one from Summer's server, or from the shipped catalog when the server
|
|
280
|
+
* cannot answer (signed out, offline).
|
|
281
|
+
*/
|
|
282
|
+
export async function describeFromCli(op, deps = defaultOpsCliDeps, options = {}) {
|
|
283
|
+
const local = localWireForm(resolveOpName(op));
|
|
284
|
+
if (local) {
|
|
285
|
+
// --json: the same answer shape as the cloud describe ({operation}).
|
|
286
|
+
deps.print(options.json ? JSON.stringify({ ok: true, status: "done", result: { operation: local } }) : JSON.stringify(local, null, 2));
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
289
|
+
const asked = await runCloud("ops.describe", { input: { name: op }, json: options.json === true, dryRun: false, wait: false }, deps);
|
|
290
|
+
const answer = describeFromShipped(op, asked) ?? asked;
|
|
291
|
+
(answer.ok ? deps.print : deps.printError)(options.json ? JSON.stringify(answer) : formatAnswer(answer));
|
|
292
|
+
if (!options.json && answer.ok)
|
|
293
|
+
for (const warning of answer.warnings ?? [])
|
|
294
|
+
deps.printError(`Warning: ${warning}`);
|
|
295
|
+
return answer.ok ? 0 : 1;
|
|
296
|
+
}
|
|
252
297
|
function areaCommand(area) {
|
|
253
298
|
return new Command(area)
|
|
254
299
|
.description(`Summer ${area} operations: summer ${area} <action> --field value. Find them: summer ops search ${area}`)
|
|
@@ -276,26 +321,14 @@ export function opsCliCommands() {
|
|
|
276
321
|
.option("--limit <n>", "Most results", "8")
|
|
277
322
|
.option("--json", "Print the answer as JSON")
|
|
278
323
|
.action(async (wordsArg, options) => {
|
|
279
|
-
|
|
280
|
-
if (options.json) {
|
|
281
|
-
defaultOpsCliDeps.print(JSON.stringify({ ok: true, status: "done", result: { items }, ...(warning ? { warnings: [warning] } : {}) }));
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
for (const item of items)
|
|
285
|
-
defaultOpsCliDeps.print(`${item.name.padEnd(32)} ${item.effect.padEnd(13)} ${item.title}`);
|
|
286
|
-
if (warning)
|
|
287
|
-
defaultOpsCliDeps.printError(warning);
|
|
324
|
+
await searchFromCli(wordsArg.join(" "), { area: options.area, limit: Number(options.limit) || 8, json: options.json === true });
|
|
288
325
|
});
|
|
289
326
|
ops
|
|
290
327
|
.command("describe <op>")
|
|
291
328
|
.description("Show one operation with its exact input")
|
|
292
|
-
.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
defaultOpsCliDeps.print(JSON.stringify(local, null, 2));
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
process.exitCode = await runOpFromCli("ops.describe", ["--name", op]);
|
|
329
|
+
.option("--json", "Print the answer as JSON")
|
|
330
|
+
.action(async (op, options) => {
|
|
331
|
+
process.exitCode = await describeFromCli(op, defaultOpsCliDeps, { json: options.json === true });
|
|
299
332
|
});
|
|
300
333
|
return [whoami, ops, ...CLI_AREAS.map(areaCommand)];
|
|
301
334
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
export declare const setupCommand: Command;
|
|
3
|
+
/** True when `path` sits in a git repository that does not ignore it (false outside git, or without git). */
|
|
4
|
+
export declare function notGitIgnored(path: string, cwd?: string): boolean;
|
|
3
5
|
/** One line instead of a row per three skills: the first few names and how many more. `--json` keeps the full list. */
|
|
4
6
|
export declare function skillNamesSample(names: readonly string[], max?: number): string;
|
|
@@ -1,19 +1,31 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { relative } from "path";
|
|
1
4
|
import { Command, Option } from "commander";
|
|
2
5
|
import { configureAgentMcp, configureSummerGamesMcp, resolveAgentSelection, resolveConfigScope, supportedAgents, } from "../../installer/agent-config.js";
|
|
3
6
|
import { previewNote, setupSkills } from "../../installer/setup.js";
|
|
4
7
|
import { printDoctorResult, runDoctor } from "../../core/capabilities/doctor.js";
|
|
5
8
|
import { brandLine, c, sym, tildeify } from "../../core/format.js";
|
|
6
9
|
import { TOOLKIT_VERSION as cliVersion } from "../../core/version.js";
|
|
7
|
-
import { resolveDefaultChannel } from "../../installer/version-check.js";
|
|
8
|
-
import { agentLabel } from "../../installer/agent-table.js";
|
|
10
|
+
import { listsSummerEngine, resolveDefaultChannel } from "../../installer/version-check.js";
|
|
11
|
+
import { agentLabel, agentSpec, cliSetupRefusal, defaultPathContext } from "../../installer/agent-table.js";
|
|
12
|
+
import { addAllowRules, allowRulesPath, removeAllowRules, supportsAllowRules } from "../../installer/cli-permissions.js";
|
|
9
13
|
import { PROFILES, parseProfile } from "../../core/tool-profiles.js";
|
|
14
|
+
import { findSetupProjectRoot, writeAgentInstructions } from "../../project-memory/agent-instructions.js";
|
|
10
15
|
const PROFILE_NOTES = {
|
|
11
16
|
full: "Every Summer tool.",
|
|
12
17
|
engine: "Engine, generation and store tools. Every tool: summer setup <agent> --profile full",
|
|
13
18
|
assets: "Generation and asset tools, no engine tools. Every tool: summer setup <agent> --profile full",
|
|
14
19
|
};
|
|
20
|
+
/** The options that only shape the MCP entry, which --cli does not write. */
|
|
21
|
+
const MCP_ENTRY_FLAGS = [
|
|
22
|
+
["print", "--print"],
|
|
23
|
+
["localDev", "--local-dev"],
|
|
24
|
+
["channel", "--channel"],
|
|
25
|
+
["profile", "--profile"],
|
|
26
|
+
];
|
|
15
27
|
export const setupCommand = new Command("setup")
|
|
16
|
-
.description("Configure Summer Engine for an AI agent and run diagnostics. For agents that can sign in to it, it also adds Summer Games (summer-games).")
|
|
28
|
+
.description("Configure Summer Engine for an AI agent and run diagnostics. For agents that can sign in to it, it also adds Summer Games (summer-games). --cli installs the skills only: the agent runs summer commands in its shell, with no MCP server.")
|
|
17
29
|
.argument("[agent]", `Agent to configure: ${supportedAgents.join(", ")}`)
|
|
18
30
|
.option("--agent <agent>", `Agent to configure: ${supportedAgents.join(", ")}`)
|
|
19
31
|
.option("--scope <scope>", "Configuration scope: user or project", "user")
|
|
@@ -25,6 +37,11 @@ export const setupCommand = new Command("setup")
|
|
|
25
37
|
.option("--no-games", "Do not add the Summer Games MCP (summer-games: friends, parties and messages as you, signed in through summer.games)")
|
|
26
38
|
.option("--profile <profile>", "MCP tool profile: full (every tool, the default), engine (engine, generation and the store; under 128 tools; the default for VS Code Copilot), assets (generation and the asset library, no engine tools)")
|
|
27
39
|
.option("--assets-only", "Set up for making assets without Summer Engine: the assets profile, only the asset skills, and no Summer Games")
|
|
40
|
+
.option("--cli", "CLI only: install the skills and no MCP server (and no Summer Games). The agent runs Summer as summer <area> <action> commands in its shell; the summer-cli skill teaches it")
|
|
41
|
+
.option("--no-mcp", "Same as --cli")
|
|
42
|
+
.option("--allow-rules", "With --cli, for Claude Code and Codex: let the agent run read-only summer commands, and dry runs of spends, without asking. Prints every rule; written only with --yes")
|
|
43
|
+
.option("--remove-allow-rules", "Remove the allow rules --allow-rules added (Claude Code, Codex), and nothing else")
|
|
44
|
+
.option("--no-project-files", "Inside a Summer project, do not add the Summer instructions to its AGENTS.md and CLAUDE.md (added with --yes by default)")
|
|
28
45
|
.option("--json", "Print setup result as JSON")
|
|
29
46
|
.option("--force", "Overwrite existing skill content (passes --force through to skills install)")
|
|
30
47
|
.option("--recommended", "Install only the recommended skill subset instead of the whole library")
|
|
@@ -34,6 +51,17 @@ export const setupCommand = new Command("setup")
|
|
|
34
51
|
.action(async (agentArg, opts) => {
|
|
35
52
|
const agent = resolveAgentSelection(agentArg, opts.agent);
|
|
36
53
|
const scope = resolveConfigScope(opts.scope);
|
|
54
|
+
if (opts.removeAllowRules) {
|
|
55
|
+
await removeCliAllowRules(agent, scope, opts);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (opts.cli || opts.mcp === false) {
|
|
59
|
+
await setupCliOnly(agent, scope, opts);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// On stderr, so --json output stays one JSON document.
|
|
63
|
+
if (opts.allowRules)
|
|
64
|
+
console.error(` ${sym.warn()} ${c.yellow("--allow-rules works with --cli only (the MCP server asks through the agent's own tool approvals); no rules were added.")}`);
|
|
37
65
|
const profile = resolveSetupProfile(opts);
|
|
38
66
|
const defaultChannel = await resolveDefaultChannel(cliVersion);
|
|
39
67
|
const config = await configureAgentMcp({
|
|
@@ -60,6 +88,7 @@ export const setupCommand = new Command("setup")
|
|
|
60
88
|
stableOnly: Boolean(opts.stableOnly),
|
|
61
89
|
assetsOnly: Boolean(opts.assetsOnly),
|
|
62
90
|
});
|
|
91
|
+
const projectFiles = setupProjectInstructions(opts);
|
|
63
92
|
const doctor = await runDoctor({ quiet: true });
|
|
64
93
|
if (opts.json) {
|
|
65
94
|
console.log(JSON.stringify({
|
|
@@ -67,16 +96,184 @@ export const setupCommand = new Command("setup")
|
|
|
67
96
|
config,
|
|
68
97
|
games,
|
|
69
98
|
skills,
|
|
99
|
+
projectFiles,
|
|
70
100
|
doctor,
|
|
71
101
|
}, null, 2));
|
|
72
102
|
}
|
|
73
103
|
else {
|
|
74
|
-
printSetupResult(config, games, skills, doctor);
|
|
104
|
+
printSetupResult(config, games, skills, doctor, projectFiles);
|
|
75
105
|
}
|
|
76
106
|
if (!doctor.ok || skills.status === "failed") {
|
|
77
107
|
process.exit(1);
|
|
78
108
|
}
|
|
79
109
|
});
|
|
110
|
+
/**
|
|
111
|
+
* `summer setup <agent> --cli`: the skills (the asset skills with --assets-only)
|
|
112
|
+
* and no MCP server, so the agent runs Summer as `summer <area> <action>` in its
|
|
113
|
+
* shell. Writes no agent config file; an existing summer-engine entry from an
|
|
114
|
+
* earlier setup is left as it is and named in a warning.
|
|
115
|
+
*/
|
|
116
|
+
async function setupCliOnly(agent, scope, opts) {
|
|
117
|
+
const spec = agentSpec(agent);
|
|
118
|
+
const mcpFlags = MCP_ENTRY_FLAGS.filter(([key]) => opts[key] !== undefined && opts[key] !== false).map(([, flag]) => flag);
|
|
119
|
+
if (mcpFlags.length > 0) {
|
|
120
|
+
throw new Error(`${mcpFlags.join(", ")} sets up the MCP entry, and --cli writes none. Leave ${mcpFlags.length === 1 ? "it" : "them"} out, or leave out --cli.`);
|
|
121
|
+
}
|
|
122
|
+
const refusal = cliSetupRefusal(spec);
|
|
123
|
+
if (refusal)
|
|
124
|
+
throw new Error(refusal);
|
|
125
|
+
const warnings = spec.status === "legacy" && spec.legacyNote ? [spec.legacyNote] : [];
|
|
126
|
+
const existing = await recordedMcpEntry(agent, scope);
|
|
127
|
+
if (existing) {
|
|
128
|
+
warnings.push(`${tildeify(existing)} still lists the summer-engine MCP server from an earlier setup. Remove that entry to use the CLI only.`);
|
|
129
|
+
}
|
|
130
|
+
const dryRun = Boolean(opts.dryRun);
|
|
131
|
+
const skills = setupSkills(agent, {
|
|
132
|
+
dryRun,
|
|
133
|
+
yes: Boolean(opts.yes),
|
|
134
|
+
force: Boolean(opts.force),
|
|
135
|
+
scope,
|
|
136
|
+
recommended: Boolean(opts.recommended),
|
|
137
|
+
stableOnly: Boolean(opts.stableOnly),
|
|
138
|
+
assetsOnly: Boolean(opts.assetsOnly),
|
|
139
|
+
commands: false,
|
|
140
|
+
});
|
|
141
|
+
// Read-only and dry-run summer commands run without asking, where the agent has clean allow rules.
|
|
142
|
+
// Opt-in: the flag is in the command the person approves.
|
|
143
|
+
let allowRules = null;
|
|
144
|
+
if (opts.allowRules && !supportsAllowRules(agent)) {
|
|
145
|
+
warnings.push(`--allow-rules is for Claude Code and Codex; ${agentLabel(agent)} manages command approvals in its own settings.`);
|
|
146
|
+
}
|
|
147
|
+
else if (opts.allowRules) {
|
|
148
|
+
try {
|
|
149
|
+
allowRules = await addAllowRules(agent, allowRulesPath(agent, scope, defaultPathContext()), Boolean(opts.yes) && !dryRun);
|
|
150
|
+
const shared = agent === "claude-code" && scope === "project" ? notGitIgnored(allowRules.path) : false;
|
|
151
|
+
if (shared)
|
|
152
|
+
warnings.push(`${tildeify(allowRules.path)} is not in .gitignore, so git would share these personal allow rules. Add .claude/settings.local.json to .gitignore.`);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
warnings.push(error instanceof Error ? error.message : String(error));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const projectFiles = setupProjectInstructions(opts);
|
|
159
|
+
const doctor = await runDoctor({ quiet: true });
|
|
160
|
+
const ok = doctor.ok && skills.status !== "failed";
|
|
161
|
+
if (opts.json) {
|
|
162
|
+
console.log(JSON.stringify({ ok, mcp: false, agent, scope, config: null, games: null, existingMcpEntry: existing, skills, allowRules, projectFiles, doctor, warnings }, null, 2));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const label = agentLabel(agent);
|
|
166
|
+
console.log("");
|
|
167
|
+
console.log(brandLine(cliVersion));
|
|
168
|
+
console.log("");
|
|
169
|
+
console.log(` ${dryRun ? c.dim("(dry run)") : sym.ok()} ${c.bold(`CLI only for ${label}`)}: no MCP server`);
|
|
170
|
+
console.log(` ${c.dim("The agent runs Summer in its shell: summer <area> <action>, or npx -y summer-engine@latest <area> <action>. The summer-cli skill teaches it.")}`);
|
|
171
|
+
printSkillsLine(skills, dryRun);
|
|
172
|
+
if (allowRules)
|
|
173
|
+
printAllowRules(allowRules, label, Boolean(opts.yes) && !dryRun);
|
|
174
|
+
else if (supportsAllowRules(agent) && !opts.allowRules) {
|
|
175
|
+
console.log(` ${sym.arrow()} ${c.dim(`${label} asks before each summer command. To run read-only and dry-run ones without asking: summer setup ${agent} --cli --allow-rules --yes (it prints every rule).`)}`);
|
|
176
|
+
}
|
|
177
|
+
printProjectFiles(projectFiles);
|
|
178
|
+
for (const warning of warnings)
|
|
179
|
+
console.log(` ${sym.warn()} ${c.yellow(warning)}`);
|
|
180
|
+
console.log("");
|
|
181
|
+
printDoctorResult(doctor);
|
|
182
|
+
if (ok && !dryRun)
|
|
183
|
+
printTryIt(label, Boolean(opts.assetsOnly));
|
|
184
|
+
}
|
|
185
|
+
if (!ok)
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
/** The allow rules, each on its line: what the agent now runs without asking, or would with --yes. */
|
|
189
|
+
function printAllowRules(result, label, write) {
|
|
190
|
+
const where = c.dim(tildeify(result.path));
|
|
191
|
+
if (!write) {
|
|
192
|
+
console.log(` ${sym.arrow()} ${label} would run these summer commands without asking. Add them: run the same command with --yes. ${where}`);
|
|
193
|
+
}
|
|
194
|
+
else if (result.changed.length === 0) {
|
|
195
|
+
console.log(` ${sym.ok()} ${label} already runs read-only and dry-run summer commands without asking ${where}`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
console.log(` ${sym.ok()} ${label} runs these summer commands without asking (${result.changed.length} rules added) ${where}`);
|
|
200
|
+
}
|
|
201
|
+
for (const rule of result.rules)
|
|
202
|
+
console.log(` ${c.dim(rule)}`);
|
|
203
|
+
console.log(` ${c.dim("Changes, publishing, prices, deletes and messages still ask. jobs wait and assets get save the files they name. Put --dry-run right after the action: summer assets generate-image --dry-run --prompt ... Undo: --remove-allow-rules")}`);
|
|
204
|
+
}
|
|
205
|
+
/** True when `path` sits in a git repository that does not ignore it (false outside git, or without git). */
|
|
206
|
+
export function notGitIgnored(path, cwd = process.cwd()) {
|
|
207
|
+
const run = (args) => spawnSync("git", args, { cwd, stdio: "ignore" }).status;
|
|
208
|
+
if (run(["rev-parse", "--is-inside-work-tree"]) !== 0)
|
|
209
|
+
return false;
|
|
210
|
+
return run(["check-ignore", "-q", relative(cwd, path)]) === 1;
|
|
211
|
+
}
|
|
212
|
+
/** `summer setup <agent> --remove-allow-rules`: take out exactly the rules --allow-rules added. */
|
|
213
|
+
async function removeCliAllowRules(agent, scope, opts) {
|
|
214
|
+
if (!supportsAllowRules(agent))
|
|
215
|
+
throw new Error(`--remove-allow-rules is for Claude Code and Codex; Summer adds no allow rules for ${agentLabel(agent)}.`);
|
|
216
|
+
const result = await removeAllowRules(agent, allowRulesPath(agent, scope, defaultPathContext()), !opts.dryRun);
|
|
217
|
+
if (opts.json) {
|
|
218
|
+
console.log(JSON.stringify({ ok: true, agent, scope, allowRules: result }, null, 2));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const where = c.dim(tildeify(result.path));
|
|
222
|
+
if (result.changed.length === 0)
|
|
223
|
+
console.log(` ${sym.ok()} No Summer allow rules for ${agentLabel(agent)} ${where}`);
|
|
224
|
+
else
|
|
225
|
+
console.log(` ${opts.dryRun ? c.dim("(dry run)") : sym.ok()} ${opts.dryRun ? "Would remove" : "Removed"} ${result.changed.length} Summer allow rules for ${agentLabel(agent)} ${where}`);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Inside a Summer project (findSetupProjectRoot: project.godot at or above the
|
|
229
|
+
* working directory, no higher than the git top level, never home): its
|
|
230
|
+
* AGENTS.md and CLAUDE.md, added when missing and never overwritten
|
|
231
|
+
* (project-memory/agent-instructions.ts). Like the skills, they are only
|
|
232
|
+
* planned without --yes. Null outside a project, with --no-project-files,
|
|
233
|
+
* --print, and --assets-only (the block is about building in Summer Engine).
|
|
234
|
+
*/
|
|
235
|
+
function setupProjectInstructions(opts) {
|
|
236
|
+
if (opts.projectFiles === false || opts.print || opts.assetsOnly)
|
|
237
|
+
return null;
|
|
238
|
+
const root = findSetupProjectRoot();
|
|
239
|
+
if (!root)
|
|
240
|
+
return null;
|
|
241
|
+
const dryRun = Boolean(opts.dryRun);
|
|
242
|
+
const written = Boolean(opts.yes) && !dryRun;
|
|
243
|
+
return { root, written, dryRun, files: writeAgentInstructions(root, { dryRun: !written }) };
|
|
244
|
+
}
|
|
245
|
+
function printProjectFiles(project) {
|
|
246
|
+
if (!project)
|
|
247
|
+
return;
|
|
248
|
+
const changed = project.files.filter((file) => file.action === "created" || file.action === "added" || file.action === "updated");
|
|
249
|
+
if (changed.length) {
|
|
250
|
+
const where = c.dim(changed.map((file) => tildeify(file.path)).join(", "));
|
|
251
|
+
if (project.written)
|
|
252
|
+
console.log(` ${sym.ok()} Added the Summer instructions for agents ${where}`);
|
|
253
|
+
else if (project.dryRun)
|
|
254
|
+
console.log(` ${c.dim("(dry run)")} Would add the Summer instructions for agents ${where}`);
|
|
255
|
+
else
|
|
256
|
+
console.log(` ${sym.warn()} Instructions for agents not added. Run the same setup command again with --yes to add them ${where}`);
|
|
257
|
+
}
|
|
258
|
+
for (const file of project.files) {
|
|
259
|
+
if (!file.reason)
|
|
260
|
+
continue;
|
|
261
|
+
const what = file.action === "skipped" ? "not written" : "left as it is";
|
|
262
|
+
console.log(` ${sym.warn()} ${c.yellow(`${tildeify(file.path)} ${what}: ${file.reason}`)}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** The config file that already lists summer-engine for this agent and scope, or null. */
|
|
266
|
+
async function recordedMcpEntry(agent, scope) {
|
|
267
|
+
try {
|
|
268
|
+
const target = await configureAgentMcp({ agent, scope, print: true });
|
|
269
|
+
if (!existsSync(target.path))
|
|
270
|
+
return null;
|
|
271
|
+
return listsSummerEngine(readFileSync(target.path, "utf-8"), target.format) ? target.path : null;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
80
277
|
/** --assets-only means the assets profile; --profile must name a profile and agree with it. */
|
|
81
278
|
function resolveSetupProfile(opts) {
|
|
82
279
|
const profile = opts.profile === undefined ? undefined : parseProfile(opts.profile);
|
|
@@ -90,7 +287,7 @@ function resolveSetupProfile(opts) {
|
|
|
90
287
|
}
|
|
91
288
|
return profile;
|
|
92
289
|
}
|
|
93
|
-
function printSetupResult(config, games, skills, doctor) {
|
|
290
|
+
function printSetupResult(config, games, skills, doctor, projectFiles) {
|
|
94
291
|
if (config.print) {
|
|
95
292
|
console.log(config.snippet.trimEnd());
|
|
96
293
|
return;
|
|
@@ -132,6 +329,22 @@ function printSetupResult(config, games, skills, doctor) {
|
|
|
132
329
|
if (!config.dryRun)
|
|
133
330
|
console.log(` ${c.dim(`Sign in once: ${games.signIn}`)}`);
|
|
134
331
|
}
|
|
332
|
+
printSkillsLine(skills, config.dryRun);
|
|
333
|
+
printProjectFiles(projectFiles);
|
|
334
|
+
for (const warning of config.warnings) {
|
|
335
|
+
console.log(` ${sym.warn()} ${c.yellow(warning)}`);
|
|
336
|
+
}
|
|
337
|
+
console.log("");
|
|
338
|
+
printDoctorResult(doctor);
|
|
339
|
+
if (doctor.ok && skills.status !== "failed" && !config.dryRun)
|
|
340
|
+
printTryIt(label, config.profile === "assets");
|
|
341
|
+
}
|
|
342
|
+
function printTryIt(label, assets) {
|
|
343
|
+
console.log("");
|
|
344
|
+
const ask = assets ? "make a sword clash sound effect" : "add a DirectionalLight3D and Camera3D to my scene";
|
|
345
|
+
console.log(`${c.dim("Try it:")} open ${label} and ask: ${c.bold(`"${ask}"`)}`);
|
|
346
|
+
}
|
|
347
|
+
function printSkillsLine(skills, dryRun) {
|
|
135
348
|
if (skills.status === "installed") {
|
|
136
349
|
const installed = parseInstalledSkills(skills.stdout);
|
|
137
350
|
if (installed.length > 0) {
|
|
@@ -147,7 +360,7 @@ function printSetupResult(config, games, skills, doctor) {
|
|
|
147
360
|
console.log(` ${sym.ok()} ${skills.message}`);
|
|
148
361
|
}
|
|
149
362
|
}
|
|
150
|
-
else if (skills.status === "planned" &&
|
|
363
|
+
else if (skills.status === "planned" && dryRun) {
|
|
151
364
|
const note = previewNote(skills);
|
|
152
365
|
console.log(` ${c.dim("(dry run)")} Would install ${c.bold(String(skills.count ?? "?") + " skills")}${note ? c.dim(` (${note})`) : ""} ${c.dim(skills.destination ?? "")}`);
|
|
153
366
|
}
|
|
@@ -159,16 +372,6 @@ function printSetupResult(config, games, skills, doctor) {
|
|
|
159
372
|
if (skills.stderr)
|
|
160
373
|
console.log(` ${c.dim(skills.stderr)}`);
|
|
161
374
|
}
|
|
162
|
-
for (const warning of config.warnings) {
|
|
163
|
-
console.log(` ${sym.warn()} ${c.yellow(warning)}`);
|
|
164
|
-
}
|
|
165
|
-
console.log("");
|
|
166
|
-
printDoctorResult(doctor);
|
|
167
|
-
if (doctor.ok && skills.status !== "failed" && !config.dryRun) {
|
|
168
|
-
console.log("");
|
|
169
|
-
const ask = config.profile === "assets" ? "make a sword clash sound effect" : "add a DirectionalLight3D and Camera3D to my scene";
|
|
170
|
-
console.log(`${c.dim("Try it:")} open ${label} and ask: ${c.bold(`"${ask}"`)}`);
|
|
171
|
-
}
|
|
172
375
|
}
|
|
173
376
|
/** One line instead of a row per three skills: the first few names and how many more. `--json` keeps the full list. */
|
|
174
377
|
export function skillNamesSample(names, max = 8) {
|
|
@@ -234,8 +234,13 @@ skillsCommand
|
|
|
234
234
|
.command("list")
|
|
235
235
|
.description("List available skills")
|
|
236
236
|
.option("--by-domain", "Group skills by their primary domain (same grouping as library/skills/README.md)")
|
|
237
|
+
.option("--json", "Print the skills as JSON")
|
|
237
238
|
.action((opts) => {
|
|
238
239
|
const skills = getBuiltinSkills();
|
|
240
|
+
if (opts.json) {
|
|
241
|
+
console.log(JSON.stringify({ skills }));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
239
244
|
if (skills.length === 0) {
|
|
240
245
|
console.log("No skills found.");
|
|
241
246
|
return;
|
|
@@ -287,6 +292,7 @@ skillsCommand
|
|
|
287
292
|
// Pre-reversal opt-in, kept one release as a hidden no-op so old scripts still parse.
|
|
288
293
|
.addOption(new Option("--include-preview", "No-op: preview skills install by default").hideHelp())
|
|
289
294
|
.option("--force", "Overwrite existing skill files (wipe stale skill dirs before copying)")
|
|
295
|
+
.option("--no-commands", "Claude Code: skip the slash commands (/summer, /gameskill, /start), which start work through the MCP tools")
|
|
290
296
|
.action((name, opts) => {
|
|
291
297
|
const agent = orDie(() => resolveSkillAgent(opts));
|
|
292
298
|
const scope = orDie(() => resolveSkillScope(agent, opts));
|
|
@@ -344,15 +350,16 @@ skillsCommand
|
|
|
344
350
|
console.log(previewSkippedLine(previewSkipped));
|
|
345
351
|
// Claude Code: also install slash commands (`commands/*.md`)
|
|
346
352
|
// into `~/.claude/commands/`. Other agents don't have an equivalent today.
|
|
347
|
-
// The commands start and route game work in the engine
|
|
348
|
-
// skills (--assets, setup --assets-only)
|
|
349
|
-
|
|
353
|
+
// The commands start and route game work in the engine through the MCP
|
|
354
|
+
// tools, so the asset skills (--assets, setup --assets-only) and a setup
|
|
355
|
+
// without MCP (--no-commands, setup --cli) leave them out.
|
|
356
|
+
if (agent === "claude-code" && !opts.assets && opts.commands !== false) {
|
|
350
357
|
const commandResults = installClaudeCommands(scope, Boolean(opts.force));
|
|
351
358
|
for (const r of commandResults) {
|
|
352
359
|
console.log(` ${r.action} command ${r.name} -> ${r.path}`);
|
|
353
360
|
}
|
|
354
361
|
}
|
|
355
|
-
writeMarkerForLocation(location);
|
|
362
|
+
writeMarkerForLocation(location, skills.map((skill) => skill.name));
|
|
356
363
|
printInstallSummary(skills.length, agent, scope, location);
|
|
357
364
|
});
|
|
358
365
|
/**
|
|
@@ -390,12 +397,13 @@ function installClaudeCommands(scope, force) {
|
|
|
390
397
|
* `skills-version-stale` check can detect drift later. Devin Desktop (formerly Windsurf) writes a
|
|
391
398
|
* single rules file with no surrounding dir, so we skip it there.
|
|
392
399
|
*/
|
|
393
|
-
function writeMarkerForLocation(location) {
|
|
400
|
+
function writeMarkerForLocation(location, installed) {
|
|
394
401
|
if (location.kind === "windsurf-rule-file")
|
|
395
402
|
return;
|
|
396
403
|
try {
|
|
397
404
|
mkdirSync(location.path, { recursive: true });
|
|
398
|
-
|
|
405
|
+
// Skills the upgrade refresh left stay protected, unless this install just replaced them.
|
|
406
|
+
writeSkillMarker(location.path, cliVersion, new Date(), installed);
|
|
399
407
|
}
|
|
400
408
|
catch {
|
|
401
409
|
// Marker is purely informational. If the FS rejects it (e.g. permission
|
|
@@ -405,7 +413,8 @@ function writeMarkerForLocation(location) {
|
|
|
405
413
|
skillsCommand
|
|
406
414
|
.command("info <name>")
|
|
407
415
|
.description("Show skill description and preview")
|
|
408
|
-
.
|
|
416
|
+
.option("--json", "Print the skill and its full SKILL.md body as JSON")
|
|
417
|
+
.action((name, opts) => {
|
|
409
418
|
const meta = getSkillMeta(name);
|
|
410
419
|
if (!meta) {
|
|
411
420
|
console.error(`Unknown skill: ${name}`);
|
|
@@ -416,6 +425,10 @@ skillsCommand
|
|
|
416
425
|
die(`Skill files missing: ${name}`);
|
|
417
426
|
const skillPath = join(src, "SKILL.md");
|
|
418
427
|
const content = readFileSync(skillPath, "utf-8");
|
|
428
|
+
if (opts.json) {
|
|
429
|
+
console.log(JSON.stringify({ ...meta, file: skillPath, body: content.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "") }));
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
419
432
|
console.log(`\n${meta.name}`);
|
|
420
433
|
console.log("-".repeat(40));
|
|
421
434
|
console.log(meta.description);
|
|
@@ -1,3 +1,29 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
export declare const statusCommand: Command;
|
|
3
|
+
/** What `summer status` reports; `--json` prints exactly this. */
|
|
4
|
+
export interface StatusReport {
|
|
5
|
+
auth: {
|
|
6
|
+
signedIn: false;
|
|
7
|
+
loginUrl: string;
|
|
8
|
+
} | {
|
|
9
|
+
signedIn: true;
|
|
10
|
+
source: "env";
|
|
11
|
+
} | {
|
|
12
|
+
signedIn: true;
|
|
13
|
+
source: "stored";
|
|
14
|
+
email?: string;
|
|
15
|
+
identityError?: string;
|
|
16
|
+
};
|
|
17
|
+
engine: {
|
|
18
|
+
state: "not_running";
|
|
19
|
+
} | {
|
|
20
|
+
state: "not_responding";
|
|
21
|
+
port: number;
|
|
22
|
+
} | {
|
|
23
|
+
state: "running";
|
|
24
|
+
port: number;
|
|
25
|
+
version: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export declare function readStatus(): Promise<StatusReport>;
|
|
3
29
|
export declare function runStatus(log: (message: string) => void): Promise<void>;
|