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.
Files changed (216) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +3 -2
  3. package/.codex-plugin/plugin.json +15 -11
  4. package/.cursor-plugin/plugin.json +5 -3
  5. package/.factory-plugin/plugin.json +3 -2
  6. package/AGENTS.md +2 -2
  7. package/CHANGELOG.md +48 -0
  8. package/README.md +21 -10
  9. package/dist/cli/command-usage.d.ts +8 -0
  10. package/dist/cli/command-usage.js +22 -0
  11. package/dist/cli/commands/config.js +3 -2
  12. package/dist/cli/commands/create.js +12 -3
  13. package/dist/cli/commands/list.d.ts +5 -0
  14. package/dist/cli/commands/list.js +30 -13
  15. package/dist/cli/commands/login.js +1 -1
  16. package/dist/cli/commands/memory.js +6 -2
  17. package/dist/cli/commands/ops.d.ts +20 -3
  18. package/dist/cli/commands/ops.js +60 -27
  19. package/dist/cli/commands/setup.d.ts +2 -0
  20. package/dist/cli/commands/setup.js +219 -16
  21. package/dist/cli/commands/skills.js +20 -7
  22. package/dist/cli/commands/status.d.ts +26 -0
  23. package/dist/cli/commands/status.js +36 -21
  24. package/dist/cli/commands/tool.js +4 -0
  25. package/dist/cli/index.js +2 -0
  26. package/dist/core/agent-host.d.ts +3 -0
  27. package/dist/core/agent-host.js +8 -2
  28. package/dist/core/capabilities/apps.d.ts +10 -10
  29. package/dist/core/capabilities/audit/args.d.ts +6 -6
  30. package/dist/core/capabilities/fabricate-mesh.d.ts +2 -2
  31. package/dist/core/capabilities/game-task-plan.d.ts +2 -2
  32. package/dist/core/capabilities/hdri-import.d.ts +2 -2
  33. package/dist/core/capabilities/motion-generation.d.ts +2 -2
  34. package/dist/core/capabilities/placement.d.ts +23 -23
  35. package/dist/core/capabilities/runtime-control.d.ts +38 -38
  36. package/dist/core/capabilities/seeing/args.d.ts +16 -16
  37. package/dist/core/capabilities/ui-control.d.ts +2 -2
  38. package/dist/core/capabilities/world-snapshot.d.ts +4 -4
  39. package/dist/core/cli-usage-beacon.d.ts +1 -0
  40. package/dist/core/cli-usage-beacon.js +18 -0
  41. package/dist/core/cli-usage.d.ts +49 -0
  42. package/dist/core/cli-usage.js +108 -0
  43. package/dist/core/feedback/client.d.ts +1 -1
  44. package/dist/core/feedback/client.js +3 -2
  45. package/dist/core/hosted-mcp.d.ts +7 -1
  46. package/dist/core/hosted-mcp.js +6 -4
  47. package/dist/core/library-search.d.ts +2 -2
  48. package/dist/core/ops/areas.d.ts +11 -0
  49. package/dist/core/ops/areas.js +24 -0
  50. package/dist/core/ops/catalog.d.ts +11 -0
  51. package/dist/core/ops/catalog.js +24 -1
  52. package/dist/core/ops/cloud.d.ts +10 -2
  53. package/dist/core/ops/cloud.js +51 -28
  54. package/dist/core/ops/contract.d.ts +2 -1
  55. package/dist/core/ops/contract.js +2 -10
  56. package/dist/core/ops/public-catalog.d.ts +48 -0
  57. package/dist/core/ops/public-catalog.js +56 -0
  58. package/dist/core/ops/search.d.ts +0 -1
  59. package/dist/core/ops/search.js +169 -37
  60. package/dist/core/telemetry.d.ts +10 -0
  61. package/dist/core/telemetry.js +58 -29
  62. package/dist/installer/agent-config.js +5 -0
  63. package/dist/installer/agent-table.d.ts +9 -0
  64. package/dist/installer/agent-table.js +23 -2
  65. package/dist/installer/cli-permissions.d.ts +65 -0
  66. package/dist/installer/cli-permissions.js +182 -0
  67. package/dist/installer/setup.d.ts +2 -0
  68. package/dist/installer/setup.js +3 -0
  69. package/dist/installer/skills-refresh.d.ts +29 -0
  70. package/dist/installer/skills-refresh.js +261 -0
  71. package/dist/installer/version-check.d.ts +22 -2
  72. package/dist/installer/version-check.js +66 -9
  73. package/dist/mcp/ops-surface.js +16 -6
  74. package/dist/mcp/server.d.ts +10 -0
  75. package/dist/mcp/server.js +38 -8
  76. package/dist/project-memory/agent-instructions.d.ts +47 -0
  77. package/dist/project-memory/agent-instructions.js +226 -0
  78. package/gemini-extension.json +3 -2
  79. package/integrations/README.md +1 -1
  80. package/integrations/claude/README.md +6 -0
  81. package/integrations/claude/manifest-target.json +4 -0
  82. package/integrations/gemini/README.md +11 -0
  83. package/integrations/windsurf/README.md +1 -1
  84. package/library/skills/3d-lighting/SKILL.md +16 -0
  85. package/library/skills/README.md +5 -4
  86. package/library/skills/adaptive-music/SKILL.md +21 -0
  87. package/library/skills/agent-playtesting/SKILL.md +22 -0
  88. package/library/skills/ai-characters-in-games/SKILL.md +15 -0
  89. package/library/skills/ambient-bed/SKILL.md +16 -0
  90. package/library/skills/animated-loop/SKILL.md +19 -0
  91. package/library/skills/animation-tree/SKILL.md +26 -0
  92. package/library/skills/art-direction/SKILL.md +14 -0
  93. package/library/skills/asset-strategy/SKILL.md +30 -0
  94. package/library/skills/audio-direction/SKILL.md +16 -0
  95. package/library/skills/auto-fire-targeting/SKILL.md +14 -0
  96. package/library/skills/brainstorm-game/SKILL.md +13 -0
  97. package/library/skills/browse-templates/SKILL.md +10 -0
  98. package/library/skills/camera-rigs/SKILL.md +20 -0
  99. package/library/skills/character-animation-wiring/SKILL.md +24 -0
  100. package/library/skills/character-model/SKILL.md +28 -0
  101. package/library/skills/character-movement/SKILL.md +24 -0
  102. package/library/skills/character-portrait/SKILL.md +15 -0
  103. package/library/skills/cinematic-cutscene/SKILL.md +21 -0
  104. package/library/skills/combat-basics/SKILL.md +19 -0
  105. package/library/skills/compatibility-renderer-traps/SKILL.md +11 -0
  106. package/library/skills/concept-art/SKILL.md +13 -0
  107. package/library/skills/concept-to-level/SKILL.md +20 -1
  108. package/library/skills/concept-to-level/resource.yaml +2 -2
  109. package/library/skills/create-asset-sheet/SKILL.md +18 -0
  110. package/library/skills/debug/SKILL.md +32 -0
  111. package/library/skills/debugging-game-feel/SKILL.md +12 -0
  112. package/library/skills/design-level/SKILL.md +17 -0
  113. package/library/skills/design-mechanic/SKILL.md +17 -0
  114. package/library/skills/design-npc/SKILL.md +21 -0
  115. package/library/skills/diagnosing-perf-regressions/SKILL.md +12 -0
  116. package/library/skills/driving-the-editor-ui/SKILL.md +23 -0
  117. package/library/skills/environment-kit/SKILL.md +23 -0
  118. package/library/skills/export-and-ship/SKILL.md +23 -0
  119. package/library/skills/fabricating-assets/SKILL.md +23 -0
  120. package/library/skills/facial-and-lipsync/SKILL.md +22 -0
  121. package/library/skills/fps-controller/SKILL.md +17 -0
  122. package/library/skills/game-feel/SKILL.md +26 -0
  123. package/library/skills/gameskill/SKILL.md +11 -0
  124. package/library/skills/gdscript-patterns/SKILL.md +15 -0
  125. package/library/skills/generate-motion/SKILL.md +21 -0
  126. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +17 -0
  127. package/library/skills/grow-analytics/SKILL.md +10 -0
  128. package/library/skills/headless-scripting/SKILL.md +13 -0
  129. package/library/skills/in-game-purchases/SKILL.md +11 -0
  130. package/library/skills/instantiate-asset-pack/SKILL.md +17 -0
  131. package/library/skills/make-game/SKILL.md +29 -0
  132. package/library/skills/multiplayer/SKILL.md +12 -0
  133. package/library/skills/multiplayer-movement/SKILL.md +18 -0
  134. package/library/skills/multiplayer-project/SKILL.md +17 -0
  135. package/library/skills/multiplayer-publish/SKILL.md +17 -5
  136. package/library/skills/multiplayer-state/SKILL.md +16 -0
  137. package/library/skills/multiplayer-testing/SKILL.md +15 -0
  138. package/library/skills/music-track/SKILL.md +16 -0
  139. package/library/skills/navigate-summer/SKILL.md +15 -0
  140. package/library/skills/new-project/SKILL.md +11 -0
  141. package/library/skills/organic-model/SKILL.md +24 -0
  142. package/library/skills/pixel-art/SKILL.md +15 -0
  143. package/library/skills/play/SKILL.md +18 -0
  144. package/library/skills/playtesting-a-feature/SKILL.md +23 -0
  145. package/library/skills/procedural-animation/SKILL.md +18 -0
  146. package/library/skills/prop-model/SKILL.md +23 -0
  147. package/library/skills/publish-your-game/SKILL.md +27 -0
  148. package/library/skills/release-readiness/SKILL.md +12 -0
  149. package/library/skills/remote-deploy/SKILL.md +14 -0
  150. package/library/skills/retarget/SKILL.md +20 -0
  151. package/library/skills/running-in-the-cloud/SKILL.md +10 -0
  152. package/library/skills/save-load/SKILL.md +15 -0
  153. package/library/skills/scene-composition/SKILL.md +17 -0
  154. package/library/skills/scene-hierarchy-design/SKILL.md +24 -0
  155. package/library/skills/scene-scripting/SKILL.md +30 -0
  156. package/library/skills/scene-to-level/SKILL.md +22 -0
  157. package/library/skills/skill-test/SKILL.md +10 -0
  158. package/library/skills/skybox-panorama/SKILL.md +20 -0
  159. package/library/skills/sound-effect/SKILL.md +23 -0
  160. package/library/skills/spatial-placement/SKILL.md +30 -0
  161. package/library/skills/sprite-sheet/SKILL.md +23 -0
  162. package/library/skills/store-art/SKILL.md +11 -0
  163. package/library/skills/store-listing/SKILL.md +10 -0
  164. package/library/skills/summer-analytics/SKILL.md +15 -0
  165. package/library/skills/summer-apps/SKILL.md +29 -0
  166. package/library/skills/summer-assets/SKILL.md +31 -1
  167. package/library/skills/summer-assets/resource.yaml +2 -2
  168. package/library/skills/summer-cli/SKILL.md +133 -0
  169. package/library/skills/summer-cli/resource.yaml +31 -0
  170. package/library/skills/summer-friends/SKILL.md +14 -0
  171. package/library/skills/summer-leaderboards/SKILL.md +16 -0
  172. package/library/skills/summer-match-results/SKILL.md +17 -0
  173. package/library/skills/summer-matchmaking/SKILL.md +17 -0
  174. package/library/skills/summer-parties/SKILL.md +14 -0
  175. package/library/skills/summer-player-data/SKILL.md +14 -0
  176. package/library/skills/summer-store/SKILL.md +15 -0
  177. package/library/skills/summer-world-chat/SKILL.md +14 -0
  178. package/library/skills/summer-world-saves/SKILL.md +14 -0
  179. package/library/skills/text-to-motion/SKILL.md +15 -0
  180. package/library/skills/tileable-texture/SKILL.md +18 -0
  181. package/library/skills/trailer-shot/SKILL.md +18 -0
  182. package/library/skills/tune-performance/SKILL.md +26 -0
  183. package/library/skills/ui-basics/SKILL.md +14 -0
  184. package/library/skills/ui-graphics/SKILL.md +17 -0
  185. package/library/skills/use-widget-asset/SKILL.md +14 -0
  186. package/library/skills/using-summer/SKILL.md +26 -0
  187. package/library/skills/using-summer/resource.yaml +2 -1
  188. package/library/skills/vehicle-model/SKILL.md +27 -0
  189. package/library/skills/verifying-scenes/SKILL.md +29 -0
  190. package/library/skills/vfx-dissolve/SKILL.md +19 -0
  191. package/library/skills/vfx-fire/SKILL.md +26 -0
  192. package/library/skills/vfx-hit-spark/SKILL.md +23 -0
  193. package/library/skills/vfx-lightning/SKILL.md +23 -0
  194. package/library/skills/vfx-magic-glow/SKILL.md +23 -0
  195. package/library/skills/vfx-muzzle-flash/SKILL.md +23 -0
  196. package/library/skills/vfx-smoke/SKILL.md +26 -0
  197. package/library/skills/vfx-water-ripple/SKILL.md +23 -0
  198. package/library/skills/voice-line/SKILL.md +16 -0
  199. package/library/skills/web-and-touch-input/SKILL.md +19 -0
  200. package/library/skills/web-compatibility-look/SKILL.md +20 -0
  201. package/library/skills/world-building-3d/SKILL.md +28 -0
  202. package/package.json +3 -2
  203. package/registry/generated/counts.json +2 -2
  204. package/registry/generated/gemini-extension.json +3 -2
  205. package/registry/generated/index.json +157 -118
  206. package/registry/generated/marketplace.claude.json +2 -2
  207. package/registry/generated/plugin.claude-directory.json +168 -0
  208. package/registry/generated/plugin.claude.json +3 -2
  209. package/registry/generated/plugin.codex.json +15 -11
  210. package/registry/generated/plugin.cursor.json +5 -3
  211. package/registry/generated/plugin.factory.json +3 -2
  212. package/registry/generated/server.json +11 -2
  213. package/registry/generated/skills-index.md +5 -4
  214. package/registry/generated/skills-registry.json +16 -2
  215. package/registry/operations/legacy-names.json +35 -0
  216. package/registry/operations/public-catalog.json +33160 -0
@@ -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
- /** Areas with operations today; CLI command words stay out of this list. */
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
- const args = split ? splitToolArgs(split, call.input) : call.input;
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), call.input);
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
- const { items, warning } = await searchEverywhere(wordsArg.join(" "), { area: options.area, limit: Number(options.limit) || 8 }, defaultOpsCliDeps.hosted);
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
- .action(async (op) => {
293
- const local = localWireForm(op);
294
- if (local) {
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" && config.dryRun) {
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, so the asset
348
- // skills (--assets, setup --assets-only) leave them out.
349
- if (agent === "claude-code" && !opts.assets) {
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
- writeSkillMarker(location.path, cliVersion);
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
- .action((name) => {
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>;