summer-engine 2.8.2 → 3.0.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 (742) hide show
  1. package/.claude-plugin/marketplace.json +27 -7
  2. package/.claude-plugin/plugin.json +99 -83
  3. package/.codex-plugin/plugin.json +112 -82
  4. package/.cursor-plugin/plugin.json +119 -83
  5. package/.factory-plugin/plugin.json +100 -3
  6. package/.mcp.json +6 -1
  7. package/.opencode/INSTALL.md +7 -4
  8. package/.opencode/plugins/summer.js +31 -18
  9. package/AGENTS.md +107 -34
  10. package/CHANGELOG.md +131 -44
  11. package/CLAUDE.md +4 -2
  12. package/GEMINI.md +7 -53
  13. package/NOTICE +31 -0
  14. package/README.md +159 -391
  15. package/assets/api-docs.json.gz +0 -0
  16. package/assets/autopilot/README.md +20 -6
  17. package/assets/autopilot/autopilot.gd +80 -26
  18. package/assets/autopilot/run.sh +132 -7
  19. package/assets/navigation/web-routes.json +719 -0
  20. package/commands/gameskill.md +10 -108
  21. package/commands/summer.md +72 -4
  22. package/dist/bin/summer.js +6 -87
  23. package/dist/cli/banner.d.ts +1 -0
  24. package/dist/cli/banner.js +33 -0
  25. package/dist/{commands → cli/commands}/config.js +1 -1
  26. package/dist/cli/commands/create.d.ts +30 -0
  27. package/dist/cli/commands/create.js +212 -0
  28. package/dist/{commands → cli/commands}/debug.js +2 -2
  29. package/dist/cli/commands/doctor.d.ts +2 -0
  30. package/dist/cli/commands/doctor.js +18 -0
  31. package/dist/cli/commands/events.d.ts +57 -0
  32. package/dist/cli/commands/events.js +158 -0
  33. package/dist/cli/commands/install.d.ts +50 -0
  34. package/dist/cli/commands/install.js +447 -0
  35. package/dist/cli/commands/list.js +72 -0
  36. package/dist/{commands → cli/commands}/login.js +33 -17
  37. package/dist/{commands → cli/commands}/logout.d.ts +1 -0
  38. package/dist/cli/commands/logout.js +19 -0
  39. package/dist/{commands → cli/commands}/memory.d.ts +1 -8
  40. package/dist/{commands → cli/commands}/memory.js +20 -11
  41. package/dist/cli/commands/open.d.ts +45 -0
  42. package/dist/cli/commands/open.js +216 -0
  43. package/dist/{commands → cli/commands}/plan.js +2 -2
  44. package/dist/{commands → cli/commands}/publish.js +1 -1
  45. package/dist/{commands → cli/commands}/releases.js +1 -1
  46. package/dist/cli/commands/run.d.ts +19 -0
  47. package/dist/cli/commands/run.js +255 -0
  48. package/dist/{commands → cli/commands}/setup.js +43 -89
  49. package/dist/{commands → cli/commands}/skills.js +85 -222
  50. package/dist/{commands → cli/commands}/status.d.ts +1 -0
  51. package/dist/cli/commands/status.js +62 -0
  52. package/dist/cli/commands/tool.d.ts +11 -0
  53. package/dist/cli/commands/tool.js +121 -0
  54. package/dist/cli/index.d.ts +6 -0
  55. package/dist/cli/index.js +89 -0
  56. package/dist/cli/root-action.d.ts +7 -0
  57. package/dist/cli/root-action.js +15 -0
  58. package/dist/{lib → core}/api-client.d.ts +106 -15
  59. package/dist/{lib → core}/api-client.js +267 -75
  60. package/dist/{lib → core}/async-op-lifecycle.d.ts +3 -4
  61. package/dist/{lib → core}/async-op-lifecycle.js +3 -4
  62. package/dist/{lib → core}/auth.d.ts +14 -6
  63. package/dist/{lib → core}/auth.js +56 -40
  64. package/dist/core/capabilities/agent-playbook.d.ts +20 -0
  65. package/dist/core/capabilities/agent-playbook.js +218 -0
  66. package/dist/core/capabilities/api-docs.d.ts +36 -0
  67. package/dist/core/capabilities/api-docs.js +118 -0
  68. package/dist/core/capabilities/asset-import.d.ts +49 -0
  69. package/dist/core/capabilities/asset-import.js +134 -0
  70. package/dist/core/capabilities/camera-view.d.ts +98 -0
  71. package/dist/core/capabilities/camera-view.js +222 -0
  72. package/dist/core/capabilities/capture.d.ts +63 -0
  73. package/dist/core/capabilities/capture.js +67 -0
  74. package/dist/core/capabilities/console-read.d.ts +17 -0
  75. package/dist/core/capabilities/console-read.js +21 -0
  76. package/dist/{lib → core/capabilities}/creator.d.ts +1 -29
  77. package/dist/{lib → core/capabilities}/creator.js +14 -33
  78. package/dist/{lib → core/capabilities}/debug-report.d.ts +1 -1
  79. package/dist/{lib → core/capabilities}/debug-report.js +12 -31
  80. package/dist/{commands → core/capabilities}/doctor.d.ts +7 -2
  81. package/dist/{commands → core/capabilities}/doctor.js +47 -73
  82. package/dist/core/capabilities/engine-fallbacks.d.ts +26 -0
  83. package/dist/core/capabilities/engine-fallbacks.js +36 -0
  84. package/dist/core/capabilities/engine-ops.d.ts +33 -0
  85. package/dist/core/capabilities/engine-ops.js +145 -0
  86. package/dist/core/capabilities/engine-receipt.d.ts +53 -0
  87. package/dist/core/capabilities/engine-receipt.js +134 -0
  88. package/dist/core/capabilities/events.d.ts +200 -0
  89. package/dist/core/capabilities/events.js +383 -0
  90. package/dist/core/capabilities/fabricate-mesh.d.ts +111 -0
  91. package/dist/core/capabilities/fabricate-mesh.js +165 -0
  92. package/dist/core/capabilities/frame-quality.d.ts +71 -0
  93. package/dist/core/capabilities/frame-quality.js +449 -0
  94. package/dist/core/capabilities/game-task-plan.d.ts +66 -0
  95. package/dist/{lib → core/capabilities}/game-task-plan.js +59 -8
  96. package/dist/core/capabilities/hdri-import.d.ts +90 -0
  97. package/dist/core/capabilities/hdri-import.js +250 -0
  98. package/dist/core/capabilities/image-generation.d.ts +24 -0
  99. package/dist/core/capabilities/image-generation.js +22 -0
  100. package/dist/core/capabilities/navigation/open.d.ts +156 -0
  101. package/dist/core/capabilities/navigation/open.js +567 -0
  102. package/dist/core/capabilities/navigation/render-product-map.d.ts +1 -0
  103. package/dist/core/capabilities/navigation/render-product-map.js +84 -0
  104. package/dist/core/capabilities/navigation/targets.d.ts +87 -0
  105. package/dist/core/capabilities/navigation/targets.js +250 -0
  106. package/dist/core/capabilities/play-determinism.d.ts +35 -0
  107. package/dist/core/capabilities/play-determinism.js +86 -0
  108. package/dist/core/capabilities/project-context.d.ts +114 -0
  109. package/dist/core/capabilities/project-context.js +249 -0
  110. package/dist/core/capabilities/runtime-control.d.ts +540 -0
  111. package/dist/core/capabilities/runtime-control.js +1011 -0
  112. package/dist/core/capabilities/scene-kind.d.ts +19 -0
  113. package/dist/core/capabilities/scene-kind.js +137 -0
  114. package/dist/core/capabilities/scene-script.d.ts +38 -0
  115. package/dist/core/capabilities/scene-script.js +45 -0
  116. package/dist/core/capabilities/tool-dispatch.d.ts +42 -0
  117. package/dist/core/capabilities/tool-dispatch.js +1490 -0
  118. package/dist/core/capabilities/ui-control.d.ts +179 -0
  119. package/dist/core/capabilities/ui-control.js +423 -0
  120. package/dist/core/capabilities/variant-types.d.ts +22 -0
  121. package/dist/core/capabilities/variant-types.js +101 -0
  122. package/dist/core/capability-skew.d.ts +205 -0
  123. package/dist/core/capability-skew.js +386 -0
  124. package/dist/{lib → core}/config.d.ts +11 -1
  125. package/dist/{lib → core}/config.js +13 -2
  126. package/dist/core/embeddings.d.ts +78 -0
  127. package/dist/core/embeddings.js +166 -0
  128. package/dist/core/engine-install.d.ts +105 -0
  129. package/dist/core/engine-install.js +190 -0
  130. package/dist/core/engine.d.ts +94 -0
  131. package/dist/{lib → core}/engine.js +74 -28
  132. package/dist/core/feedback/client.d.ts +88 -0
  133. package/dist/core/feedback/client.js +182 -0
  134. package/dist/{lib → core}/format.d.ts +0 -1
  135. package/dist/{lib → core}/format.js +1 -1
  136. package/dist/core/headless/connection.d.ts +86 -0
  137. package/dist/core/headless/connection.js +97 -0
  138. package/dist/core/headless/editor-connection.d.ts +26 -0
  139. package/dist/core/headless/editor-connection.js +81 -0
  140. package/dist/core/headless/mcp-routing.d.ts +38 -0
  141. package/dist/core/headless/mcp-routing.js +83 -0
  142. package/dist/core/headless/registry.d.ts +91 -0
  143. package/dist/core/headless/registry.js +226 -0
  144. package/dist/core/headless/resolve.d.ts +46 -0
  145. package/dist/core/headless/resolve.js +75 -0
  146. package/dist/core/headless/spawn.d.ts +46 -0
  147. package/dist/core/headless/spawn.js +205 -0
  148. package/dist/core/headless/worker-connection.d.ts +96 -0
  149. package/dist/core/headless/worker-connection.js +353 -0
  150. package/dist/core/headless/worker-engine-client.d.ts +58 -0
  151. package/dist/core/headless/worker-engine-client.js +246 -0
  152. package/dist/core/launch-posture.d.ts +170 -0
  153. package/dist/core/launch-posture.js +317 -0
  154. package/dist/core/library-read.d.ts +73 -0
  155. package/dist/core/library-read.js +279 -0
  156. package/dist/core/library-search.d.ts +143 -0
  157. package/dist/core/library-search.js +301 -0
  158. package/dist/{lib → core}/log-filters.js +4 -2
  159. package/dist/{lib → core}/mcp-log.js +2 -17
  160. package/dist/core/package-root.d.ts +9 -0
  161. package/dist/core/package-root.js +11 -0
  162. package/dist/core/redact.d.ts +20 -0
  163. package/dist/core/redact.js +64 -0
  164. package/dist/core/registry-search.d.ts +167 -0
  165. package/dist/core/registry-search.js +358 -0
  166. package/dist/core/skills-registry.d.ts +47 -0
  167. package/dist/core/skills-registry.js +83 -0
  168. package/dist/{lib → core}/store.js +11 -7
  169. package/dist/{lib → core}/summer-compatibility.js +3 -3
  170. package/dist/{lib → core}/telemetry.js +5 -2
  171. package/dist/core/templates.d.ts +98 -0
  172. package/dist/core/templates.js +241 -0
  173. package/dist/core/tool-errors.d.ts +30 -0
  174. package/dist/core/tool-errors.js +40 -0
  175. package/dist/core/trajectory.d.ts +80 -0
  176. package/dist/core/trajectory.js +307 -0
  177. package/dist/core/util/http.d.ts +13 -0
  178. package/dist/core/util/http.js +28 -0
  179. package/dist/core/util/json.d.ts +12 -0
  180. package/dist/core/util/json.js +21 -0
  181. package/dist/core/util/process.d.ts +3 -0
  182. package/dist/core/util/process.js +13 -0
  183. package/dist/core/util/sleep.d.ts +1 -0
  184. package/dist/core/util/sleep.js +3 -0
  185. package/dist/core/version.d.ts +3 -0
  186. package/dist/core/version.js +5 -0
  187. package/dist/{lib → installer}/agent-config.d.ts +20 -1
  188. package/dist/{lib → installer}/agent-config.js +138 -24
  189. package/dist/installer/legacy-skills.d.ts +26 -0
  190. package/dist/installer/legacy-skills.js +53 -0
  191. package/dist/installer/setup.d.ts +76 -0
  192. package/dist/installer/setup.js +146 -0
  193. package/dist/installer/skill-locations.d.ts +45 -0
  194. package/dist/installer/skill-locations.js +170 -0
  195. package/dist/installer/version-check.d.ts +174 -0
  196. package/dist/{lib → installer}/version-check.js +173 -42
  197. package/dist/{lib/mcp-boot-notice.d.ts → mcp/boot-notice.d.ts} +1 -1
  198. package/dist/{commands/mcp.js → mcp/command.js} +18 -6
  199. package/dist/mcp/server.d.ts +23 -3
  200. package/dist/mcp/server.js +122 -28
  201. package/dist/mcp/tools/asset-tools.js +62 -142
  202. package/dist/mcp/tools/creator-tools.js +5 -23
  203. package/dist/mcp/tools/debug-tools.js +61 -18
  204. package/dist/mcp/tools/event-tools.d.ts +14 -0
  205. package/dist/mcp/tools/event-tools.js +41 -0
  206. package/dist/mcp/tools/fabricate-tools.d.ts +2 -0
  207. package/dist/mcp/tools/fabricate-tools.js +28 -0
  208. package/dist/mcp/tools/feedback-tools.d.ts +77 -0
  209. package/dist/mcp/tools/feedback-tools.js +88 -0
  210. package/dist/mcp/tools/file-tools.js +6 -53
  211. package/dist/mcp/tools/generate-tools.js +37 -63
  212. package/dist/mcp/tools/library-tools.d.ts +11 -0
  213. package/dist/mcp/tools/library-tools.js +42 -0
  214. package/dist/mcp/tools/navigation-tools.d.ts +7 -0
  215. package/dist/mcp/tools/navigation-tools.js +37 -0
  216. package/dist/mcp/tools/perception-tools.d.ts +20 -0
  217. package/dist/mcp/tools/perception-tools.js +146 -0
  218. package/dist/mcp/tools/project-tools.d.ts +14 -0
  219. package/dist/mcp/tools/project-tools.js +72 -281
  220. package/dist/mcp/tools/runtime-tools.d.ts +2 -0
  221. package/dist/mcp/tools/runtime-tools.js +92 -0
  222. package/dist/mcp/tools/scene-tools.d.ts +3 -0
  223. package/dist/mcp/tools/scene-tools.js +68 -119
  224. package/dist/mcp/tools/script-tools.d.ts +3 -0
  225. package/dist/mcp/tools/script-tools.js +118 -0
  226. package/dist/mcp/tools/spatial-tools.d.ts +2 -0
  227. package/dist/mcp/tools/spatial-tools.js +485 -0
  228. package/dist/mcp/tools/text-json.d.ts +8 -0
  229. package/dist/mcp/tools/text-json.js +12 -0
  230. package/dist/mcp/tools/{cloud-tools.d.ts → ui-tools.d.ts} +1 -1
  231. package/dist/mcp/tools/ui-tools.js +82 -0
  232. package/dist/mcp/tools/visual-tools.d.ts +2 -7
  233. package/dist/mcp/tools/visual-tools.js +383 -101
  234. package/dist/mcp/tools/with-engine.d.ts +9 -15
  235. package/dist/mcp/tools/with-engine.js +60 -96
  236. package/dist/project-memory/project-manifest.d.ts +36 -0
  237. package/dist/project-memory/project-manifest.js +50 -0
  238. package/dist/project-memory/project-memory.d.ts +57 -0
  239. package/dist/{lib → project-memory}/project-memory.js +46 -0
  240. package/gemini-extension.json +103 -2
  241. package/hooks/hooks-cursor.json +2 -2
  242. package/hooks/hooks.json +2 -1
  243. package/hooks/pre-commit-doctor.sh +45 -39
  244. package/hooks/run-hook.cmd +30 -23
  245. package/hooks/run-hook.sh +20 -0
  246. package/hooks/session-start.sh +18 -7
  247. package/integrations/README.md +70 -0
  248. package/integrations/claude/README.md +19 -0
  249. package/integrations/claude/manifest-target.json +16 -0
  250. package/integrations/cline/README.md +13 -0
  251. package/integrations/cline/manifest-target.json +3 -0
  252. package/integrations/codex/README.md +15 -0
  253. package/integrations/codex/manifest-target.json +5 -0
  254. package/integrations/cursor/README.md +16 -0
  255. package/integrations/cursor/manifest-target.json +5 -0
  256. package/integrations/factory/README.md +26 -0
  257. package/integrations/factory/manifest-target.json +5 -0
  258. package/integrations/gemini/README.md +26 -0
  259. package/integrations/gemini/manifest-target.json +5 -0
  260. package/integrations/github-copilot/README.md +12 -0
  261. package/integrations/github-copilot/manifest-target.json +3 -0
  262. package/integrations/kilo-code/README.md +16 -0
  263. package/integrations/kilo-code/manifest-target.json +3 -0
  264. package/integrations/lm-studio/README.md +12 -0
  265. package/integrations/lm-studio/manifest-target.json +3 -0
  266. package/integrations/opencode/README.md +15 -0
  267. package/integrations/opencode/manifest-target.json +3 -0
  268. package/integrations/roo-code/README.md +13 -0
  269. package/integrations/roo-code/manifest-target.json +3 -0
  270. package/integrations/vscode-copilot/README.md +12 -0
  271. package/integrations/vscode-copilot/manifest-target.json +3 -0
  272. package/integrations/windsurf/README.md +13 -0
  273. package/integrations/windsurf/manifest-target.json +3 -0
  274. package/library/references/collaborative-protocol/resource.yaml +24 -0
  275. package/library/references/gd-style/resource.yaml +24 -0
  276. package/{references → library/references/godot-version}/godot-version.md +6 -6
  277. package/library/references/godot-version/resource.yaml +22 -0
  278. package/library/references/headless-routing/headless-routing.md +46 -0
  279. package/library/references/headless-routing/resource.yaml +24 -0
  280. package/library/references/mcp-fresh-chat-playbook/mcp-fresh-chat-playbook.md +49 -0
  281. package/library/references/mcp-fresh-chat-playbook/resource.yaml +23 -0
  282. package/library/references/mcp-tools-reference/mcp-tools-reference.md +271 -0
  283. package/library/references/mcp-tools-reference/resource.yaml +17 -0
  284. package/library/references/product-map/product-map.md +103 -0
  285. package/library/references/product-map/resource.yaml +33 -0
  286. package/{references → library/references/public-surface-language}/public-surface-language.md +5 -7
  287. package/library/references/public-surface-language/resource.yaml +17 -0
  288. package/library/references/summer-folder/resource.yaml +20 -0
  289. package/{references → library/references/summer-folder}/summer-folder.md +22 -21
  290. package/{skills/rendering-and-lighting → library/skills}/3d-lighting/SKILL.md +15 -11
  291. package/library/skills/3d-lighting/resource.yaml +27 -0
  292. package/{skills/audio → library/skills}/adaptive-music/SKILL.md +1 -1
  293. package/library/skills/adaptive-music/resource.yaml +28 -0
  294. package/library/skills/agent-playtesting/SKILL.md +148 -0
  295. package/library/skills/agent-playtesting/resource.yaml +52 -0
  296. package/library/skills/ambient-bed/resource.yaml +24 -0
  297. package/{skills/video → library/skills}/animated-loop/SKILL.md +1 -1
  298. package/library/skills/animated-loop/resource.yaml +27 -0
  299. package/{skills/animation → library/skills}/animation-tree/SKILL.md +29 -14
  300. package/library/skills/animation-tree/resource.yaml +33 -0
  301. package/{skills/rendering-and-lighting → library/skills}/art-direction/SKILL.md +3 -3
  302. package/library/skills/art-direction/resource.yaml +29 -0
  303. package/{skills/asset-pipeline → library/skills}/asset-strategy/SKILL.md +44 -44
  304. package/library/skills/asset-strategy/resource.yaml +63 -0
  305. package/{skills/audio → library/skills}/audio-direction/SKILL.md +7 -7
  306. package/library/skills/audio-direction/resource.yaml +32 -0
  307. package/library/skills/auto-fire-targeting/resource.yaml +22 -0
  308. package/{skills/scene-and-project → library/skills}/brainstorm-game/SKILL.md +8 -8
  309. package/library/skills/brainstorm-game/resource.yaml +30 -0
  310. package/{skills/workflow → library/skills}/brainstorming/SKILL.md +2 -22
  311. package/library/skills/brainstorming/resource.yaml +23 -0
  312. package/{skills/workflow → library/skills}/brainstorming/spec-document-reviewer-prompt.md +1 -1
  313. package/{skills/scene-and-project → library/skills}/browse-templates/SKILL.md +18 -18
  314. package/library/skills/browse-templates/resource.yaml +23 -0
  315. package/library/skills/celeste-momentum-platforming/SKILL.md +366 -0
  316. package/library/skills/celeste-momentum-platforming/resource.yaml +33 -0
  317. package/library/skills/character-animation-wiring/SKILL.md +249 -0
  318. package/library/skills/character-animation-wiring/resource.yaml +39 -0
  319. package/{skills/3d-assets → library/skills}/character-model/SKILL.md +25 -25
  320. package/library/skills/character-model/resource.yaml +36 -0
  321. package/{skills/2d-assets → library/skills}/character-portrait/SKILL.md +14 -14
  322. package/library/skills/character-portrait/resource.yaml +30 -0
  323. package/{skills/video → library/skills}/cinematic-cutscene/SKILL.md +2 -2
  324. package/library/skills/cinematic-cutscene/resource.yaml +29 -0
  325. package/{skills/2d-assets → library/skills}/concept-art/SKILL.md +15 -15
  326. package/library/skills/concept-art/resource.yaml +34 -0
  327. package/{skills/2d-assets → library/skills}/create-asset-sheet/SKILL.md +8 -8
  328. package/library/skills/create-asset-sheet/resource.yaml +33 -0
  329. package/{skills/debugging → library/skills}/debug/SKILL.md +3 -3
  330. package/library/skills/debug/resource.yaml +31 -0
  331. package/{skills/workflow → library/skills}/debugging-game-feel/SKILL.md +11 -11
  332. package/library/skills/debugging-game-feel/resource.yaml +25 -0
  333. package/{skills/level-design → library/skills}/design-level/SKILL.md +6 -6
  334. package/library/skills/design-level/resource.yaml +31 -0
  335. package/{skills/gameplay-mechanics → library/skills}/design-mechanic/SKILL.md +3 -3
  336. package/library/skills/design-mechanic/resource.yaml +31 -0
  337. package/{skills/ai-and-npcs → library/skills}/design-npc/SKILL.md +7 -3
  338. package/library/skills/design-npc/resource.yaml +29 -0
  339. package/{skills/workflow → library/skills}/diagnosing-perf-regressions/SKILL.md +10 -10
  340. package/library/skills/diagnosing-perf-regressions/resource.yaml +25 -0
  341. package/{skills/workflow → library/skills}/dispatching-parallel-agents/SKILL.md +8 -8
  342. package/library/skills/dispatching-parallel-agents/resource.yaml +23 -0
  343. package/library/skills/driving-the-editor-ui/SKILL.md +85 -0
  344. package/library/skills/driving-the-editor-ui/resource.yaml +46 -0
  345. package/{skills/3d-assets → library/skills}/environment-kit/SKILL.md +11 -11
  346. package/library/skills/environment-kit/resource.yaml +29 -0
  347. package/{skills/deployment → library/skills}/export-and-ship/SKILL.md +5 -5
  348. package/library/skills/export-and-ship/resource.yaml +32 -0
  349. package/library/skills/fabricating-assets/SKILL.md +195 -0
  350. package/library/skills/fabricating-assets/resource.yaml +52 -0
  351. package/{skills/animation → library/skills}/facial-and-lipsync/SKILL.md +23 -11
  352. package/library/skills/facial-and-lipsync/resource.yaml +36 -0
  353. package/{skills/character-controllers → library/skills}/fps-controller/SKILL.md +8 -4
  354. package/library/skills/fps-controller/resource.yaml +29 -0
  355. package/{skills/visual-effects → library/skills}/game-feel/SKILL.md +7 -7
  356. package/library/skills/game-feel/resource.yaml +27 -0
  357. package/library/skills/gameskill/SKILL.md +64 -0
  358. package/library/skills/gameskill/resource.yaml +22 -0
  359. package/library/skills/gdscript-patterns/resource.yaml +23 -0
  360. package/{skills/animation → library/skills}/generate-motion/SKILL.md +16 -16
  361. package/library/skills/generate-motion/resource.yaml +34 -0
  362. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +223 -0
  363. package/library/skills/godogen-visual-proof-game-generation/resource.yaml +33 -0
  364. package/{skills/workflow → library/skills}/headless-scripting/SKILL.md +14 -7
  365. package/library/skills/headless-scripting/resource.yaml +26 -0
  366. package/library/skills/host-authoritative-state/resource.yaml +28 -0
  367. package/{skills/2d-assets → library/skills}/instantiate-asset-pack/SKILL.md +10 -12
  368. package/library/skills/instantiate-asset-pack/resource.yaml +29 -0
  369. package/{skills/workflow → library/skills}/investigating-bugs/SKILL.md +4 -4
  370. package/{skills/workflow → library/skills}/investigating-bugs/condition-based-waiting.md +8 -8
  371. package/library/skills/investigating-bugs/resource.yaml +24 -0
  372. package/{skills/workflow → library/skills}/investigating-bugs/root-cause-tracing.md +1 -7
  373. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +164 -0
  374. package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +35 -0
  375. package/{skills/scene-and-project → library/skills}/make-game/SKILL.md +22 -22
  376. package/library/skills/make-game/resource.yaml +35 -0
  377. package/library/skills/music-track/resource.yaml +28 -0
  378. package/library/skills/navigate-summer/SKILL.md +61 -0
  379. package/library/skills/navigate-summer/resource.yaml +35 -0
  380. package/{skills/scene-and-project → library/skills}/new-project/SKILL.md +10 -10
  381. package/library/skills/new-project/resource.yaml +28 -0
  382. package/{skills/3d-assets → library/skills}/organic-model/SKILL.md +16 -16
  383. package/library/skills/organic-model/resource.yaml +32 -0
  384. package/{skills/multiplayer-and-networking → library/skills}/peer-to-peer-multiplayer/SKILL.md +2 -2
  385. package/library/skills/peer-to-peer-multiplayer/resource.yaml +28 -0
  386. package/{skills/2d-assets → library/skills}/pixel-art/SKILL.md +13 -13
  387. package/library/skills/pixel-art/resource.yaml +31 -0
  388. package/library/skills/play/resource.yaml +26 -0
  389. package/{skills/workflow → library/skills}/playtesting-a-feature/SKILL.md +41 -22
  390. package/library/skills/playtesting-a-feature/resource.yaml +31 -0
  391. package/{skills/animation → library/skills}/procedural-animation/SKILL.md +25 -10
  392. package/library/skills/procedural-animation/resource.yaml +33 -0
  393. package/{skills/3d-assets → library/skills}/prop-model/SKILL.md +13 -13
  394. package/library/skills/prop-model/resource.yaml +33 -0
  395. package/library/skills/psx-retro-rendering/SKILL.md +416 -0
  396. package/library/skills/psx-retro-rendering/resource.yaml +36 -0
  397. package/library/skills/realtime-wet-surfaces/SKILL.md +189 -0
  398. package/library/skills/realtime-wet-surfaces/resource.yaml +31 -0
  399. package/{skills/deployment → library/skills}/remote-deploy/SKILL.md +3 -3
  400. package/library/skills/remote-deploy/resource.yaml +23 -0
  401. package/{skills/animation → library/skills}/retarget/SKILL.md +15 -14
  402. package/library/skills/retarget/resource.yaml +33 -0
  403. package/library/skills/running-in-the-cloud/SKILL.md +103 -0
  404. package/library/skills/running-in-the-cloud/resource.yaml +33 -0
  405. package/{skills/scene-and-project → library/skills}/scene-composition/SKILL.md +8 -0
  406. package/library/skills/scene-composition/resource.yaml +27 -0
  407. package/library/skills/scene-hierarchy-design/SKILL.md +102 -0
  408. package/library/skills/scene-hierarchy-design/resource.yaml +31 -0
  409. package/library/skills/scene-scripting/SKILL.md +764 -0
  410. package/library/skills/scene-scripting/resource.yaml +58 -0
  411. package/{skills/level-design → library/skills}/scene-to-level/SKILL.md +16 -17
  412. package/library/skills/scene-to-level/resource.yaml +33 -0
  413. package/library/skills/setup-multiplayer/resource.yaml +28 -0
  414. package/library/skills/skill-create/SKILL.md +138 -0
  415. package/library/skills/skill-create/resource.yaml +25 -0
  416. package/{skills/workflow → library/skills}/skill-improve/SKILL.md +2 -2
  417. package/library/skills/skill-improve/resource.yaml +25 -0
  418. package/{skills/workflow → library/skills}/skill-test/SKILL.md +4 -4
  419. package/library/skills/skill-test/resource.yaml +26 -0
  420. package/library/skills/skintokens-auto-rigging/SKILL.md +200 -0
  421. package/library/skills/skintokens-auto-rigging/resource.yaml +38 -0
  422. package/{skills/2d-assets → library/skills}/skybox-panorama/SKILL.md +8 -8
  423. package/library/skills/skybox-panorama/resource.yaml +28 -0
  424. package/{skills/audio → library/skills}/sound-effect/SKILL.md +2 -2
  425. package/library/skills/sound-effect/resource.yaml +27 -0
  426. package/library/skills/spatial-placement/SKILL.md +74 -0
  427. package/library/skills/spatial-placement/resource.yaml +42 -0
  428. package/library/skills/spatial-placement/tests/spec.md +65 -0
  429. package/{skills/2d-assets → library/skills}/sprite-sheet/SKILL.md +12 -12
  430. package/library/skills/sprite-sheet/resource.yaml +32 -0
  431. package/{skills/2d-assets → library/skills}/tileable-texture/SKILL.md +9 -9
  432. package/library/skills/tileable-texture/resource.yaml +31 -0
  433. package/{skills/video → library/skills}/trailer-shot/SKILL.md +1 -1
  434. package/library/skills/trailer-shot/resource.yaml +27 -0
  435. package/{skills/performance → library/skills}/tune-performance/SKILL.md +6 -6
  436. package/library/skills/tune-performance/resource.yaml +31 -0
  437. package/library/skills/ui-basics/resource.yaml +25 -0
  438. package/{skills/2d-assets → library/skills}/ui-graphics/SKILL.md +11 -11
  439. package/library/skills/ui-graphics/resource.yaml +29 -0
  440. package/{skills/2d-assets → library/skills}/use-widget-asset/SKILL.md +4 -4
  441. package/library/skills/use-widget-asset/resource.yaml +29 -0
  442. package/{skills/workflow → library/skills}/using-summer/SKILL.md +12 -9
  443. package/library/skills/using-summer/resource.yaml +41 -0
  444. package/{skills/3d-assets → library/skills}/vehicle-model/SKILL.md +18 -18
  445. package/library/skills/vehicle-model/resource.yaml +34 -0
  446. package/{skills/workflow → library/skills}/verification-before-completion/SKILL.md +10 -10
  447. package/library/skills/verification-before-completion/resource.yaml +23 -0
  448. package/library/skills/verifying-scenes/SKILL.md +98 -0
  449. package/library/skills/verifying-scenes/resource.yaml +42 -0
  450. package/{skills/visual-effects/recipes/dissolve → library/skills/vfx-dissolve}/SKILL.md +31 -11
  451. package/library/skills/vfx-dissolve/resource.yaml +33 -0
  452. package/{skills/visual-effects/recipes/fire → library/skills/vfx-fire}/SKILL.md +43 -12
  453. package/library/skills/vfx-fire/_building-blocks/noise-3d-fbm.gdshaderinc +45 -0
  454. package/library/skills/vfx-fire/resource.yaml +35 -0
  455. package/{skills/visual-effects/recipes/hit-spark → library/skills/vfx-hit-spark}/SKILL.md +16 -10
  456. package/library/skills/vfx-hit-spark/_building-blocks/additive-billboard-particles.md +37 -0
  457. package/library/skills/vfx-hit-spark/resource.yaml +34 -0
  458. package/{skills/visual-effects/recipes/lightning → library/skills/vfx-lightning}/SKILL.md +24 -11
  459. package/library/skills/vfx-lightning/_building-blocks/additive-billboard-particles.md +37 -0
  460. package/library/skills/vfx-lightning/resource.yaml +33 -0
  461. package/{skills/visual-effects/recipes/magic-glow → library/skills/vfx-magic-glow}/SKILL.md +31 -11
  462. package/library/skills/vfx-magic-glow/_building-blocks/additive-billboard-particles.md +37 -0
  463. package/library/skills/vfx-magic-glow/resource.yaml +34 -0
  464. package/{skills/visual-effects/recipes/muzzle-flash → library/skills/vfx-muzzle-flash}/SKILL.md +29 -9
  465. package/library/skills/vfx-muzzle-flash/_building-blocks/additive-billboard-particles.md +37 -0
  466. package/library/skills/vfx-muzzle-flash/_building-blocks/trauma-shake-snippet.md +53 -0
  467. package/library/skills/vfx-muzzle-flash/resource.yaml +35 -0
  468. package/{skills/visual-effects/recipes/smoke → library/skills/vfx-smoke}/SKILL.md +35 -9
  469. package/library/skills/vfx-smoke/_building-blocks/noise-3d-fbm.gdshaderinc +45 -0
  470. package/library/skills/vfx-smoke/resource.yaml +32 -0
  471. package/{skills/visual-effects/recipes/water-ripple → library/skills/vfx-water-ripple}/SKILL.md +31 -9
  472. package/library/skills/vfx-water-ripple/_building-blocks/additive-billboard-particles.md +37 -0
  473. package/library/skills/vfx-water-ripple/resource.yaml +31 -0
  474. package/library/skills/voice-line/resource.yaml +27 -0
  475. package/library/skills/world-building-3d/SKILL.md +105 -0
  476. package/library/skills/world-building-3d/references/spatial-tools.md +50 -0
  477. package/library/skills/world-building-3d/resource.yaml +44 -0
  478. package/library/skills/world-building-3d/tests/spec.md +48 -0
  479. package/{skills/workflow → library/skills}/writing-plans/SKILL.md +19 -14
  480. package/library/skills/writing-plans/resource.yaml +23 -0
  481. package/{skills/workflow → library/skills}/writing-skills/SKILL.md +38 -44
  482. package/library/skills/writing-skills/resource.yaml +29 -0
  483. package/{skills/workflow → library/skills}/writing-skills/testing-skills-with-subagents.md +13 -14
  484. package/library/templates/2d-brario-platformer/resource.yaml +21 -0
  485. package/library/templates/2d-dungeon-roguelike/resource.yaml +23 -0
  486. package/library/templates/2d-grid-puzzle/resource.yaml +21 -0
  487. package/library/templates/2d-plants-and-zombies-tower-defense/resource.yaml +21 -0
  488. package/library/templates/2d-platformer/resource.yaml +24 -0
  489. package/library/templates/2d-rpg/resource.yaml +21 -0
  490. package/library/templates/2d-vampire-survivor-roguelike/resource.yaml +21 -0
  491. package/library/templates/3d-basic/resource.yaml +16 -0
  492. package/library/templates/3d-city-kit/resource.yaml +24 -0
  493. package/library/templates/3d-fps-old-school/resource.yaml +21 -0
  494. package/library/templates/3d-fps-simple-animated-npc/resource.yaml +21 -0
  495. package/library/templates/3d-lan-multiplayer-starter/resource.yaml +22 -0
  496. package/library/templates/3d-open-world-explore-tps/resource.yaml +21 -0
  497. package/library/templates/3d-procedural-road-world/resource.yaml +21 -0
  498. package/library/templates/3d-racing-game/resource.yaml +21 -0
  499. package/library/templates/3d-royale-clash-type/resource.yaml +21 -0
  500. package/library/templates/3d-third-person-controller/resource.yaml +21 -0
  501. package/library/templates/3d-voxel-sandbox/resource.yaml +24 -0
  502. package/library/templates/README.md +94 -0
  503. package/library/templates/empty/resource.yaml +16 -0
  504. package/library/tools/add-node/resource.yaml +60 -0
  505. package/library/tools/align-distribute-3d/resource.yaml +87 -0
  506. package/library/tools/api-docs/resource.yaml +57 -0
  507. package/library/tools/batch/resource.yaml +52 -0
  508. package/library/tools/camera-bookmark/resource.yaml +73 -0
  509. package/library/tools/check-job/resource.yaml +45 -0
  510. package/library/tools/clear-console/resource.yaml +40 -0
  511. package/library/tools/connect-signal/resource.yaml +63 -0
  512. package/library/tools/create-debug-report/resource.yaml +67 -0
  513. package/library/tools/create-scene/resource.yaml +59 -0
  514. package/library/tools/creator-config/resource.yaml +63 -0
  515. package/library/tools/creator-publish/resource.yaml +73 -0
  516. package/library/tools/creator-releases/resource.yaml +53 -0
  517. package/library/tools/fabricate-3d/resource.yaml +108 -0
  518. package/library/tools/game-control/resource.yaml +84 -0
  519. package/library/tools/game-input/resource.yaml +159 -0
  520. package/library/tools/game-probe/resource.yaml +90 -0
  521. package/library/tools/generate-3d/resource.yaml +112 -0
  522. package/library/tools/generate-audio/resource.yaml +80 -0
  523. package/library/tools/generate-image/resource.yaml +62 -0
  524. package/library/tools/generate-motion/resource.yaml +66 -0
  525. package/library/tools/generate-video/resource.yaml +66 -0
  526. package/library/tools/get-agent-playbook/resource.yaml +40 -0
  527. package/library/tools/get-asset/resource.yaml +42 -0
  528. package/library/tools/get-asset-download-url/resource.yaml +52 -0
  529. package/library/tools/get-console/resource.yaml +69 -0
  530. package/library/tools/get-debugger-errors/resource.yaml +56 -0
  531. package/library/tools/get-debugger-warnings/resource.yaml +52 -0
  532. package/library/tools/get-diagnostics/resource.yaml +42 -0
  533. package/library/tools/get-project-context/resource.yaml +45 -0
  534. package/library/tools/get-runtime-tree/resource.yaml +62 -0
  535. package/library/tools/get-scene-tree/resource.yaml +52 -0
  536. package/library/tools/get-script-errors/resource.yaml +45 -0
  537. package/library/tools/get-studio-workflow/resource.yaml +43 -0
  538. package/library/tools/import-asset/resource.yaml +73 -0
  539. package/library/tools/import-asset-by-id/resource.yaml +56 -0
  540. package/library/tools/import-from-url/resource.yaml +51 -0
  541. package/library/tools/import-from-url-batch/resource.yaml +60 -0
  542. package/library/tools/import-hdri/resource.yaml +69 -0
  543. package/library/tools/input-map-bind/resource.yaml +52 -0
  544. package/library/tools/inspect-node/resource.yaml +46 -0
  545. package/library/tools/inspect-resource/resource.yaml +45 -0
  546. package/library/tools/inspect-runtime-node/resource.yaml +54 -0
  547. package/library/tools/instantiate-scene/resource.yaml +62 -0
  548. package/library/tools/is-running/resource.yaml +40 -0
  549. package/library/tools/library-feedback/resource.yaml +89 -0
  550. package/library/tools/list-my-assets/resource.yaml +56 -0
  551. package/library/tools/navigation-probe/resource.yaml +85 -0
  552. package/library/tools/open/resource.yaml +80 -0
  553. package/library/tools/open-main-scene/resource.yaml +40 -0
  554. package/library/tools/open-scene/resource.yaml +46 -0
  555. package/library/tools/play/resource.yaml +91 -0
  556. package/library/tools/project-setting/resource.yaml +53 -0
  557. package/library/tools/read-file/resource.yaml +47 -0
  558. package/library/tools/read-library/resource.yaml +67 -0
  559. package/library/tools/recent-events/resource.yaml +71 -0
  560. package/library/tools/remove-node/resource.yaml +51 -0
  561. package/library/tools/replace-node/resource.yaml +57 -0
  562. package/library/tools/replace-text/resource.yaml +61 -0
  563. package/library/tools/run-editor-script/resource.yaml +61 -0
  564. package/library/tools/run-script/resource.yaml +73 -0
  565. package/library/tools/runtime-animate/resource.yaml +140 -0
  566. package/library/tools/runtime-call/resource.yaml +74 -0
  567. package/library/tools/runtime-set/resource.yaml +80 -0
  568. package/library/tools/runtime-spawn/resource.yaml +95 -0
  569. package/library/tools/save-scene/resource.yaml +50 -0
  570. package/library/tools/screenshot/resource.yaml +112 -0
  571. package/library/tools/search-assets/resource.yaml +67 -0
  572. package/library/tools/search-library/resource.yaml +81 -0
  573. package/library/tools/select-node/resource.yaml +48 -0
  574. package/library/tools/set-prop/resource.yaml +64 -0
  575. package/library/tools/set-resource-property/resource.yaml +66 -0
  576. package/library/tools/slice-asset-sheet/resource.yaml +47 -0
  577. package/library/tools/snap-to-surface/resource.yaml +90 -0
  578. package/library/tools/snapshot-diff/resource.yaml +58 -0
  579. package/library/tools/starcast/resource.yaml +120 -0
  580. package/library/tools/start-game-task/resource.yaml +91 -0
  581. package/library/tools/stop/resource.yaml +51 -0
  582. package/library/tools/test-placement/resource.yaml +109 -0
  583. package/library/tools/ui-actions/resource.yaml +77 -0
  584. package/library/tools/ui-activate/resource.yaml +88 -0
  585. package/library/tools/ui-screenshot/resource.yaml +63 -0
  586. package/library/tools/ui-tree/resource.yaml +69 -0
  587. package/library/tools/wait-for-event/resource.yaml +84 -0
  588. package/library/tools/world-snapshot/resource.yaml +60 -0
  589. package/library/tools/write-file/resource.yaml +54 -0
  590. package/package.json +22 -16
  591. package/registry/generated/aliases.json +207 -0
  592. package/registry/generated/counts.json +12 -0
  593. package/registry/generated/gemini-extension.json +122 -0
  594. package/registry/generated/index.json +7709 -0
  595. package/registry/generated/marketplace.claude.json +44 -0
  596. package/registry/generated/mcp.json +13 -0
  597. package/registry/generated/plugin.claude.json +135 -0
  598. package/registry/generated/plugin.codex.json +144 -0
  599. package/registry/generated/plugin.cursor.json +134 -0
  600. package/registry/generated/plugin.factory.json +117 -0
  601. package/registry/generated/skills-registry.json +851 -0
  602. package/registry/generated/templates-registry.json +455 -0
  603. package/registry/schemas/allowed-hosts.json +19 -0
  604. package/registry/schemas/collection.schema.json +68 -0
  605. package/registry/schemas/domains.json +96 -0
  606. package/registry/schemas/example.schema.json +11 -0
  607. package/registry/schemas/reference.schema.json +10 -0
  608. package/registry/schemas/resource.schema.json +154 -0
  609. package/registry/schemas/skill.schema.json +14 -0
  610. package/registry/schemas/template.schema.json +46 -0
  611. package/registry/schemas/tool.schema.json +61 -0
  612. package/_persona/summer/SKILL.md +0 -113
  613. package/dist/bin/postinstall.d.ts +0 -2
  614. package/dist/bin/postinstall.js +0 -3
  615. package/dist/commands/cloud.d.ts +0 -2
  616. package/dist/commands/cloud.js +0 -80
  617. package/dist/commands/create.d.ts +0 -3
  618. package/dist/commands/create.js +0 -171
  619. package/dist/commands/install.d.ts +0 -19
  620. package/dist/commands/install.js +0 -194
  621. package/dist/commands/list.js +0 -67
  622. package/dist/commands/logout.js +0 -13
  623. package/dist/commands/logs.d.ts +0 -2
  624. package/dist/commands/logs.js +0 -19
  625. package/dist/commands/open.d.ts +0 -2
  626. package/dist/commands/open.js +0 -32
  627. package/dist/commands/orchestrator.d.ts +0 -8
  628. package/dist/commands/orchestrator.js +0 -143
  629. package/dist/commands/run.d.ts +0 -2
  630. package/dist/commands/run.js +0 -167
  631. package/dist/commands/status.js +0 -46
  632. package/dist/lib/banner.d.ts +0 -4
  633. package/dist/lib/banner.js +0 -122
  634. package/dist/lib/cloud/api.d.ts +0 -79
  635. package/dist/lib/cloud/api.js +0 -107
  636. package/dist/lib/cloud/apply.d.ts +0 -32
  637. package/dist/lib/cloud/apply.js +0 -294
  638. package/dist/lib/cloud/checkpoint.d.ts +0 -26
  639. package/dist/lib/cloud/checkpoint.js +0 -142
  640. package/dist/lib/cloud/conflicts.d.ts +0 -55
  641. package/dist/lib/cloud/conflicts.js +0 -131
  642. package/dist/lib/cloud/containment.d.ts +0 -8
  643. package/dist/lib/cloud/containment.js +0 -52
  644. package/dist/lib/cloud/diff.d.ts +0 -39
  645. package/dist/lib/cloud/diff.js +0 -138
  646. package/dist/lib/cloud/engine-bridge.d.ts +0 -34
  647. package/dist/lib/cloud/engine-bridge.js +0 -139
  648. package/dist/lib/cloud/hash-cache.d.ts +0 -23
  649. package/dist/lib/cloud/hash-cache.js +0 -44
  650. package/dist/lib/cloud/hash.d.ts +0 -10
  651. package/dist/lib/cloud/hash.js +0 -111
  652. package/dist/lib/cloud/http.d.ts +0 -17
  653. package/dist/lib/cloud/http.js +0 -47
  654. package/dist/lib/cloud/lock-path.d.ts +0 -1
  655. package/dist/lib/cloud/lock-path.js +0 -5
  656. package/dist/lib/cloud/lock.d.ts +0 -14
  657. package/dist/lib/cloud/lock.js +0 -143
  658. package/dist/lib/cloud/manifest.d.ts +0 -6
  659. package/dist/lib/cloud/manifest.js +0 -41
  660. package/dist/lib/cloud/paths.d.ts +0 -20
  661. package/dist/lib/cloud/paths.js +0 -90
  662. package/dist/lib/cloud/project-godot-merge.d.ts +0 -32
  663. package/dist/lib/cloud/project-godot-merge.js +0 -185
  664. package/dist/lib/cloud/rules.d.ts +0 -39
  665. package/dist/lib/cloud/rules.js +0 -159
  666. package/dist/lib/cloud/sync.d.ts +0 -21
  667. package/dist/lib/cloud/sync.js +0 -518
  668. package/dist/lib/cloud/transfer.d.ts +0 -23
  669. package/dist/lib/cloud/transfer.js +0 -184
  670. package/dist/lib/cloud/types.d.ts +0 -62
  671. package/dist/lib/cloud/types.js +0 -1
  672. package/dist/lib/cloud/validate-path.d.ts +0 -23
  673. package/dist/lib/cloud/validate-path.js +0 -103
  674. package/dist/lib/engine.d.ts +0 -51
  675. package/dist/lib/game-task-plan.d.ts +0 -42
  676. package/dist/lib/project-memory.d.ts +0 -33
  677. package/dist/lib/public-surface-paths.d.ts +0 -13
  678. package/dist/lib/public-surface-paths.js +0 -42
  679. package/dist/lib/remote-templates.d.ts +0 -25
  680. package/dist/lib/remote-templates.js +0 -86
  681. package/dist/lib/skills-registry.d.ts +0 -572
  682. package/dist/lib/skills-registry.js +0 -1061
  683. package/dist/lib/version-check.d.ts +0 -106
  684. package/dist/mcp/tools/cloud-tools.js +0 -42
  685. package/references/mcp-tools-reference.md +0 -199
  686. package/references/template-registry.md +0 -73
  687. package/skills/README.md +0 -103
  688. package/skills/scene-and-project/summer-cloud/SKILL.md +0 -92
  689. package/skills/workflow/brainstorming/scripts/frame-template.html +0 -214
  690. package/skills/workflow/brainstorming/scripts/helper.js +0 -88
  691. package/skills/workflow/brainstorming/scripts/server.cjs +0 -354
  692. package/skills/workflow/brainstorming/scripts/start-server.sh +0 -148
  693. package/skills/workflow/brainstorming/scripts/stop-server.sh +0 -56
  694. package/skills/workflow/brainstorming/visual-companion.md +0 -287
  695. package/skills/workflow/gameskill/SKILL.md +0 -120
  696. package/skills/workflow/investigating-bugs/CREATION-LOG.md +0 -119
  697. package/skills/workflow/investigating-bugs/condition-based-waiting-example.ts +0 -158
  698. package/skills/workflow/investigating-bugs/find-polluter.sh +0 -63
  699. package/skills/workflow/investigating-bugs/test-academic.md +0 -14
  700. package/skills/workflow/investigating-bugs/test-pressure-1.md +0 -58
  701. package/skills/workflow/investigating-bugs/test-pressure-2.md +0 -68
  702. package/skills/workflow/investigating-bugs/test-pressure-3.md +0 -69
  703. package/skills/workflow/mcpupdate/SKILL.md +0 -134
  704. package/skills/workflow/skill-create/SKILL.md +0 -153
  705. package/skills/workflow/writing-skills/anthropic-best-practices.md +0 -1150
  706. package/skills/workflow/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
  707. package/skills/workflow/writing-skills/graphviz-conventions.dot +0 -172
  708. package/skills/workflow/writing-skills/persuasion-principles.md +0 -187
  709. package/skills/workflow/writing-skills/render-graphs.js +0 -168
  710. /package/dist/{commands → cli/commands}/config.d.ts +0 -0
  711. /package/dist/{commands → cli/commands}/debug.d.ts +0 -0
  712. /package/dist/{commands → cli/commands}/list.d.ts +0 -0
  713. /package/dist/{commands → cli/commands}/login.d.ts +0 -0
  714. /package/dist/{commands → cli/commands}/plan.d.ts +0 -0
  715. /package/dist/{commands → cli/commands}/publish.d.ts +0 -0
  716. /package/dist/{commands → cli/commands}/releases.d.ts +0 -0
  717. /package/dist/{commands → cli/commands}/setup.d.ts +0 -0
  718. /package/dist/{commands → cli/commands}/skills.d.ts +0 -0
  719. /package/dist/{lib → core}/log-filters.d.ts +0 -0
  720. /package/dist/{lib → core}/mcp-log.d.ts +0 -0
  721. /package/dist/{lib → core}/store.d.ts +0 -0
  722. /package/dist/{lib → core}/summer-compatibility.d.ts +0 -0
  723. /package/dist/{lib → core}/telemetry.d.ts +0 -0
  724. /package/dist/{lib/mcp-boot-notice.js → mcp/boot-notice.js} +0 -0
  725. /package/dist/{commands/mcp.d.ts → mcp/command.d.ts} +0 -0
  726. /package/{references → library/references/collaborative-protocol}/collaborative-protocol.md +0 -0
  727. /package/{references → library/references/gd-style}/gd-style.md +0 -0
  728. /package/{skills/audio → library/skills}/ambient-bed/SKILL.md +0 -0
  729. /package/{skills/gameplay-mechanics → library/skills}/auto-fire-targeting/SKILL.md +0 -0
  730. /package/{skills/scripting-patterns → library/skills}/gdscript-patterns/SKILL.md +0 -0
  731. /package/{skills/scripting-patterns → library/skills}/gdscript-patterns/reference.md +0 -0
  732. /package/{skills/multiplayer-and-networking → library/skills}/host-authoritative-state/SKILL.md +0 -0
  733. /package/{skills/workflow → library/skills}/investigating-bugs/defense-in-depth.md +0 -0
  734. /package/{skills/audio → library/skills}/music-track/SKILL.md +0 -0
  735. /package/{skills/scene-and-project → library/skills}/play/SKILL.md +0 -0
  736. /package/{skills/multiplayer-and-networking → library/skills}/setup-multiplayer/SKILL.md +0 -0
  737. /package/{skills/ui-and-ux → library/skills}/ui-basics/SKILL.md +0 -0
  738. /package/{skills/visual-effects → library/skills/vfx-dissolve}/_building-blocks/noise-3d-fbm.gdshaderinc +0 -0
  739. /package/{skills/visual-effects → library/skills/vfx-fire}/_building-blocks/additive-billboard-particles.md +0 -0
  740. /package/{skills/visual-effects → library/skills/vfx-lightning}/_building-blocks/trauma-shake-snippet.md +0 -0
  741. /package/{skills/audio → library/skills}/voice-line/SKILL.md +0 -0
  742. /package/{skills/workflow → library/skills}/writing-plans/plan-document-reviewer-prompt.md +0 -0
@@ -0,0 +1,1490 @@
1
+ /**
2
+ * tool-dispatch — the shared per-tool dispatch registry (CONTRACT.md §2, §5).
3
+ *
4
+ * One entry per library `tool` resource (library/tools/<slug>/resource.yaml),
5
+ * dispatching through the SAME underlying implementations the MCP server uses:
6
+ * core capabilities (creator, debug-report, game-task-plan,
7
+ * library feedback), the core EngineApiClient for engine ops, and the same
8
+ * gateway endpoints for Studio generation / asset APIs.
9
+ *
10
+ * Consumed today by `summer tool <name>` (src/cli/commands/tool.ts).
11
+ *
12
+ * The helpers both faces need (scene-mutation chunking, guarded file writes,
13
+ * Kenney import pairing, engine receipt failure semantics) live in ONE copy
14
+ * each — ./engine-ops.ts, ./asset-import.ts, ./engine-receipt.ts — imported
15
+ * here and by src/mcp/tools/*. v3-followup: src/mcp should adopt this registry
16
+ * as the single per-tool dispatch table so the handler bodies stop being two
17
+ * mirrors as well.
18
+ */
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+ import { mkdir, writeFile } from "node:fs/promises";
22
+ import { EngineApiClient } from "../api-client.js";
23
+ import { missingEngineEventsResult, missingEngineOpResult, resolveSingleOnlyOps, } from "../capability-skew.js";
24
+ import { buildAgentPlaybook } from "./agent-playbook.js";
25
+ import { recentEvents, recentEventsArgsSchema, waitForEvent, waitForEventArgsSchema, } from "./events.js";
26
+ import { importResolvedAsset } from "./asset-import.js";
27
+ import { executeOpsChunked, executeSceneMutation, occurrenceCount, readTextPayload, safeProjectPath, validSha256, } from "./engine-ops.js";
28
+ import { extractOpError, withOldEngineHint } from "./engine-receipt.js";
29
+ import { lookupApiDocs } from "./api-docs.js";
30
+ import { imageGenerationArgsSchema } from "./image-generation.js";
31
+ import { ImportHdriError, importHdriArgsSchema, importPolyHavenHdri } from "./hdri-import.js";
32
+ import { FABRICATE_FALLBACK, buildFabricateMeshOp, fabricateArgsSchema } from "./fabricate-mesh.js";
33
+ import { buildUiActionsOp, buildUiActivateOp, buildUiScreenshotOp, buildUiTreeOp, executeUiOp, uiActionsArgsSchema, uiActivateArgsSchema, uiScreenshotArgsSchema, uiScreenshotCaption, uiScreenshotImage, uiTreeArgsSchema, withUiFailureDetails, } from "./ui-control.js";
34
+ import { PLAY_INSTANCE_FALLBACK, RUNTIME_FALLBACKS, buildGameControlOp, buildGameInputOp, buildGameProbeOp, buildRuntimeAnimateOp, buildRuntimeCallOp, buildRuntimeSetOp, buildRuntimeSpawnOp, buildStopGameOp, findProbePayload, gameControlArgsSchema, gameInputArgsSchema, gameProbeArgsSchema, playGame, probeFrameStamp, runtimeAnimateArgsSchema, runtimeCallArgsSchema, runtimeSetArgsSchema, runtimeSpawnArgsSchema, stripProbeImage, withRuntimeFailureHints, } from "./runtime-control.js";
35
+ import { RUN_EDITOR_SCRIPT_FALLBACK, RUN_SCRIPT_FALLBACK, buildRunEditorScriptOp, buildRunSceneScriptOp, } from "./scene-script.js";
36
+ import { CAMERA_BOOKMARK_ACTIONS, CAMERA_BOOKMARK_FALLBACK, SCREENSHOT_FRAMINGS, buildCameraBookmarkOp, buildScenePreviewInput, } from "./camera-view.js";
37
+ import { PLAY_DETERMINISM_NOT_SUPPORTED, pickPlayDeterminism, readPlayDeterminism } from "./play-determinism.js";
38
+ import { ToolInputError } from "../tool-errors.js";
39
+ import { getAuthToken } from "../auth.js";
40
+ import open from "open";
41
+ import { openArgsSchema, runOpen } from "./navigation/open.js";
42
+ import { shapeEngineLogResponse } from "../log-filters.js";
43
+ import { listCreatorReleases, publishCreator, } from "./creator.js";
44
+ import { CONFIG_KEYS, getConfigValue, isConfigKey, readSummerConfig, resolveGatewayUrl, setConfigValue, unsetConfigValue, } from "../config.js";
45
+ import { createDebugReportArtifact } from "./debug-report.js";
46
+ import { buildGameTaskPlan, gameTaskPlanInputSchema } from "./game-task-plan.js";
47
+ import { buildProjectContext, projectContextInputSchema, projectSettingValue, } from "./project-context.js";
48
+ import { annotateVariantTypes } from "./variant-types.js";
49
+ import { withConsoleScope } from "./console-read.js";
50
+ import { captureGame, captureScene, captureViewport } from "./capture.js";
51
+ // engine_lacks_op fallbacks: ONE copy for every face (E2E 2026-09-03 F-16);
52
+ // the scripting ones come with their op builders from ./scene-script.js.
53
+ import { ALIGN_DISTRIBUTE_FALLBACK, NAVIGATION_PROBE_FALLBACK, RUNTIME_NODE_FALLBACK, RUNTIME_TREE_FALLBACK, SNAP_TO_SURFACE_FALLBACK, SNAPSHOT_DIFF_FALLBACK, STARCAST_FALLBACK, TEST_PLACEMENT_FALLBACK, WORLD_SNAPSHOT_FALLBACK, } from "./engine-fallbacks.js";
54
+ import { sendLibraryFeedback, } from "../feedback/client.js";
55
+ import { readLibraryEntry, readLibraryInputSchema } from "../library-read.js";
56
+ import { runSearchLibrary, searchLibraryInputSchema } from "../library-search.js";
57
+ import { TOOLKIT_VERSION as CLI_VERSION } from "../version.js";
58
+ /** Dispatch-level failure with a user-facing message (no stack needed). */
59
+ export class ToolDispatchError extends Error {
60
+ }
61
+ /** The local engine is not reachable — a clean state, not a crash. */
62
+ export class EngineUnavailableError extends ToolDispatchError {
63
+ }
64
+ /** A tool failed with a structured receipt that `summer tool` prints whole
65
+ * (JSON on stdout, exit 1) instead of flattening to `message`: every engine
66
+ * envelope extractOpError reads as a failure (the same gate the MCP face
67
+ * applies in withEngine before setting isError), the engine_lacks_op result
68
+ * from the capability pre-flight (nothing sent) and from the post-hoc
69
+ * unknown-op rewrite (requireSupportedOp), and the not-found/ambiguous
70
+ * results of the library and navigation tools. */
71
+ export class ToolResultError extends ToolDispatchError {
72
+ result;
73
+ constructor(result, message) {
74
+ super(message);
75
+ this.result = result;
76
+ }
77
+ }
78
+ export function createDefaultDispatchContext() {
79
+ let cached = null;
80
+ return {
81
+ async engine() {
82
+ if (cached)
83
+ return cached;
84
+ try {
85
+ cached = await EngineApiClient.connect();
86
+ return cached;
87
+ }
88
+ catch (error) {
89
+ const reason = error instanceof Error ? error.message : String(error);
90
+ throw new EngineUnavailableError("Summer Engine is not running (or no project is open).\n" +
91
+ `${reason}\n` +
92
+ "Start it with 'summer run' or open the project in the Summer desktop app, then retry.\n" +
93
+ "Engine-free tools (generate-*, asset search/list/get, creator, plan) work without it.");
94
+ }
95
+ },
96
+ };
97
+ }
98
+ /**
99
+ * Engine receipt → the receipt, or a ToolResultError carrying the failing
100
+ * envelope. Exactly the decision the MCP face makes in withEngine
101
+ * (extractOpError → isError); `summer tool` prints the envelope as JSON and
102
+ * exits 1, so scripts read `ok`/`error`/`failure_reason` instead of scraping
103
+ * a sentence. dispatchTool applies it to EVERY handler result, so a handler
104
+ * that returns receipts directly (scene mutations, batches, snapshots) cannot
105
+ * exit 0 on a failed op (E2E 2026-09-03 F-06).
106
+ */
107
+ function requireEngineSuccess(result) {
108
+ const failure = extractOpError(result);
109
+ if (failure)
110
+ throw new ToolResultError(failedEnvelope(result, failure), failure);
111
+ return result;
112
+ }
113
+ /**
114
+ * The envelope `summer tool` prints for a failed engine result: the engine's
115
+ * own fields, `ok: false` stamped (a failure terminalState can arrive with
116
+ * ok absent), and a plain-text top-level `error` when the engine put its
117
+ * message only inside results[] or only a terminalState. extractOpError
118
+ * renders CLASSIFIED failures as a JSON string; that rendering is for the MCP
119
+ * text face — here the fields are already structured, so only its plain
120
+ * message is reused.
121
+ */
122
+ function failedEnvelope(result, failure) {
123
+ const envelope = result && typeof result === "object" && !Array.isArray(result)
124
+ ? { ...result }
125
+ : { result };
126
+ if (typeof envelope.error !== "string" || envelope.error.length === 0) {
127
+ envelope.error = plainFailureMessage(envelope, failure);
128
+ }
129
+ return { ...envelope, ok: false };
130
+ }
131
+ function plainFailureMessage(envelope, failure) {
132
+ const results = Array.isArray(envelope.results) ? envelope.results : [];
133
+ const failed = results.find((entry) => entry && entry.ok === false && typeof entry.error === "string" && entry.error.length > 0);
134
+ if (failed)
135
+ return failed.error;
136
+ if (failure.startsWith("{")) {
137
+ try {
138
+ const parsed = JSON.parse(failure);
139
+ if (typeof parsed.error === "string" && parsed.error.length > 0)
140
+ return parsed.error;
141
+ }
142
+ catch {
143
+ // Not the classified JSON rendering after all; fall through to the text.
144
+ }
145
+ }
146
+ return failure;
147
+ }
148
+ /** Post-hoc twin of the missingEngineOpResult pre-flight, for engines that
149
+ * advertise no opKinds (0.5.65 ships `singleOnlyOps` only, so the pre-flight
150
+ * cannot refuse): such an engine answers with a per-op "unknown op: <Kind>".
151
+ * withOldEngineHint — the same helper the MCP face applies — rewrites that
152
+ * into the upgrade path + fallback and stamps failure_reason engine_lacks_op;
153
+ * it is thrown as a ToolResultError so `summer tool` prints the structured
154
+ * result rather than the bare engine string. */
155
+ function requireSupportedOp(result, op, fallback) {
156
+ const hinted = withOldEngineHint(result, op, fallback);
157
+ if (hinted?.failure_reason === "engine_lacks_op") {
158
+ throw new ToolResultError(hinted, String(hinted.error));
159
+ }
160
+ return requireEngineSuccess(result);
161
+ }
162
+ /** Pre-flight refusal as a structured result (nothing was sent). */
163
+ function refuseMissingOp(missing) {
164
+ throw new ToolResultError({ ...missing }, missing.error);
165
+ }
166
+ /** Events-channel pre-flight (a capability, not an op): refuse before sending
167
+ * when /api/health lacks capabilities.events — the engine_lacks_events twin
168
+ * of refuseMissingOp, printed whole by `summer tool`. */
169
+ async function requireEventsChannel(ctx) {
170
+ const client = await ctx.engine();
171
+ const missing = missingEngineEventsResult(client);
172
+ if (missing)
173
+ throw new ToolResultError({ ...missing }, missing.error);
174
+ return client;
175
+ }
176
+ /** A structured events failure (engine_lacks_events post-hoc, unknown_event_kind,
177
+ * an identity_mismatch terminalState, a 503) is thrown as a ToolResultError so
178
+ * `summer tool` prints the receipt rather than a flattened sentence. */
179
+ function requireEventsSuccess(result) {
180
+ if (result.ok !== false)
181
+ return result;
182
+ const record = result;
183
+ const message = extractOpError(record) ??
184
+ (typeof record.error === "string" ? record.error : "Engine events request failed.");
185
+ throw new ToolResultError(record, message);
186
+ }
187
+ /** Run a shared core op builder; its ToolInputError (nothing sent) becomes the
188
+ * CLI's readable ToolDispatchError instead of a stack trace. */
189
+ function buildOrRefuse(build) {
190
+ try {
191
+ return build();
192
+ }
193
+ catch (err) {
194
+ if (err instanceof ToolInputError)
195
+ throw new ToolDispatchError(err.message);
196
+ throw err;
197
+ }
198
+ }
199
+ /** buildOrRefuse for a shared async implementation that validates inline. */
200
+ async function buildOrRefuseAsync(run) {
201
+ try {
202
+ return await run();
203
+ }
204
+ catch (err) {
205
+ if (err instanceof ToolInputError)
206
+ throw new ToolDispatchError(err.message);
207
+ throw err;
208
+ }
209
+ }
210
+ /** A capability pre-flight refusal returned (not thrown) by a shared implementation. */
211
+ function isMissingOpResult(value) {
212
+ return (!!value &&
213
+ typeof value === "object" &&
214
+ value.failure_reason === "engine_lacks_op" &&
215
+ value.ok === false &&
216
+ typeof value.hint === "string");
217
+ }
218
+ function optNumberOrUndefined(args, key) {
219
+ const value = args[key];
220
+ if (value === undefined)
221
+ return undefined;
222
+ if (typeof value !== "number" || !Number.isFinite(value)) {
223
+ throw new ToolDispatchError(`${key} must be a finite number`);
224
+ }
225
+ return value;
226
+ }
227
+ /** Editor UI control (editor UI control): pre-flight the op kind the arguments resolved
228
+ * to (summer_ui_actions is UiListActions OR UiInvoke, summer_ui_tree is
229
+ * UiTree OR UiDialogs, ...), send it — mutating kinds identity-bound — and
230
+ * render the engine's failure details the same way the MCP face does. */
231
+ async function dispatchUiOp(ctx, built) {
232
+ const client = await ctx.engine();
233
+ const missing = missingEngineOpResult(client, built.kind, built.fallback);
234
+ if (missing)
235
+ refuseMissingOp(missing);
236
+ return requireSupportedOp(withUiFailureDetails(await executeUiOp(client, built)), built.kind, built.fallback);
237
+ }
238
+ // ---------------------------------------------------------------------------
239
+ // Gateway helpers (Studio generation + asset APIs). Same endpoints, bodies,
240
+ // and auth the MCP tools use (src/mcp/tools/generate-tools.ts,
241
+ // asset-tools.ts) with surface header "cli".
242
+ // ---------------------------------------------------------------------------
243
+ async function requireToken() {
244
+ const token = await getAuthToken();
245
+ if (!token) {
246
+ throw new ToolDispatchError("Not signed in. Run 'summer login' first — these tools require a Summer Engine account.");
247
+ }
248
+ return token;
249
+ }
250
+ function gatewayHeaders(token, endpoint) {
251
+ return {
252
+ Authorization: `Bearer ${token}`,
253
+ "X-Summer-Client": "summer-cli",
254
+ "X-Summer-Client-Version": CLI_VERSION,
255
+ "X-Summer-Client-Surface": "cli",
256
+ "X-Summer-MCP-Endpoint": endpoint,
257
+ };
258
+ }
259
+ async function readJsonBody(res) {
260
+ const text = await res.text().catch(() => "");
261
+ if (!text)
262
+ return {};
263
+ try {
264
+ return JSON.parse(text);
265
+ }
266
+ catch {
267
+ return { message: text.slice(0, 1000) };
268
+ }
269
+ }
270
+ async function gatewayGet(endpoint, params, timeoutMs = 30_000) {
271
+ const token = await requireToken();
272
+ const gatewayUrl = await resolveGatewayUrl();
273
+ // URLSearchParams.size is undefined on Node < 18.16 (falsy -> the whole
274
+ // query used to be dropped silently); stringify instead.
275
+ const query = params?.toString() ?? "";
276
+ const suffix = query ? `?${query}` : "";
277
+ let res;
278
+ try {
279
+ res = await fetch(`${gatewayUrl}${endpoint}${suffix}`, {
280
+ headers: gatewayHeaders(token, endpoint),
281
+ signal: AbortSignal.timeout(timeoutMs),
282
+ });
283
+ }
284
+ catch (err) {
285
+ throw new ToolDispatchError(`Request failed: ${err instanceof Error ? err.message : String(err)}`);
286
+ }
287
+ const data = await readJsonBody(res);
288
+ if (!res.ok) {
289
+ throw new ToolDispatchError(String(data.message || data.error || `Request failed (${res.status})`));
290
+ }
291
+ return data;
292
+ }
293
+ async function gatewayPost(endpoint, body, timeoutMs = 120_000) {
294
+ const token = await requireToken();
295
+ const gatewayUrl = await resolveGatewayUrl();
296
+ let res;
297
+ try {
298
+ res = await fetch(`${gatewayUrl}${endpoint}`, {
299
+ method: "POST",
300
+ headers: {
301
+ ...gatewayHeaders(token, endpoint),
302
+ "Content-Type": "application/json",
303
+ },
304
+ body: JSON.stringify(body),
305
+ signal: AbortSignal.timeout(timeoutMs),
306
+ });
307
+ }
308
+ catch (err) {
309
+ throw new ToolDispatchError(`Generation request failed: ${err instanceof Error ? err.message : String(err)}`);
310
+ }
311
+ const data = await readJsonBody(res);
312
+ if (!res.ok) {
313
+ let message = String(data.message || data.error || `Request failed (${res.status})`);
314
+ if (res.status === 402) {
315
+ message = `${message} — insufficient credits; top up or upgrade on the Summer dashboard.`;
316
+ }
317
+ if (res.status === 401) {
318
+ message = `${message} — auth token expired; run 'summer login' again.`;
319
+ }
320
+ throw new ToolDispatchError(message);
321
+ }
322
+ return data;
323
+ }
324
+ async function pollGenerationJob(jobId, maxWaitMs = 600_000, intervalMs = 5_000) {
325
+ const token = await requireToken();
326
+ const gatewayUrl = await resolveGatewayUrl();
327
+ const deadline = Date.now() + maxWaitMs;
328
+ let interval = intervalMs;
329
+ while (Date.now() < deadline) {
330
+ const res = await fetch(`${gatewayUrl}/api/mcp/jobs/${encodeURIComponent(jobId)}`, {
331
+ headers: gatewayHeaders(token, "/api/mcp/jobs"),
332
+ signal: AbortSignal.timeout(15_000),
333
+ });
334
+ const data = await readJsonBody(res);
335
+ if (!res.ok) {
336
+ throw new ToolDispatchError(String(data.message || "Job poll failed"));
337
+ }
338
+ if (data.status === "completed")
339
+ return data;
340
+ if (data.status === "failed") {
341
+ throw new ToolDispatchError(String(data.error || "Job failed"));
342
+ }
343
+ await new Promise((resolve) => setTimeout(resolve, interval));
344
+ interval = Math.min(interval * 1.2, 15_000);
345
+ }
346
+ throw new ToolDispatchError(`Timed out after ${maxWaitMs / 1000}s. Re-check later with: summer tool check-job --json '{"jobId":"${jobId}"}'`);
347
+ }
348
+ async function searchAssetsGateway(params) {
349
+ const search = new URLSearchParams();
350
+ search.set("query", params.query);
351
+ if (params.assetType && params.assetType !== "all")
352
+ search.set("assetType", params.assetType);
353
+ if (params.limit)
354
+ search.set("limit", String(Math.min(params.limit, 20)));
355
+ if (params.source)
356
+ search.set("source", params.source);
357
+ return gatewayGet("/api/mcp/assets", search, 15_000);
358
+ }
359
+ // ---------------------------------------------------------------------------
360
+ // Small engine helpers
361
+ // ---------------------------------------------------------------------------
362
+ function str(args, key) {
363
+ const value = args[key];
364
+ if (typeof value !== "string" || value.length === 0) {
365
+ throw new ToolDispatchError(`Missing required string argument: ${key}`);
366
+ }
367
+ return value;
368
+ }
369
+ function optStr(args, key) {
370
+ const value = args[key];
371
+ return typeof value === "string" && value.length > 0 ? value : undefined;
372
+ }
373
+ /** Validate CLI args against the SAME zod schema the MCP face uses, so the
374
+ * two surfaces reject the same inputs with a readable message. */
375
+ function parseToolArgs(schema, args, tool) {
376
+ const parsed = schema.safeParse(args);
377
+ if (parsed.success)
378
+ return parsed.data;
379
+ const issues = parsed.error.issues
380
+ .map((issue) => `${issue.path.join(".") || "args"}: ${issue.message}`)
381
+ .join("; ");
382
+ throw new ToolDispatchError(`Invalid arguments for ${tool}: ${issues}`);
383
+ }
384
+ async function snapshotResult(snap, target) {
385
+ requireEngineSuccess(snap);
386
+ if (target === "game" && snap.failureReason === "bridge_required") {
387
+ throw new ToolDispatchError("Game capture is not available over this connection (requires the Summer desktop app bridge). " +
388
+ "Use target 'viewport' or 'scene' instead.");
389
+ }
390
+ let localPath = snap.localPath;
391
+ if (!localPath && snap.base64) {
392
+ const dir = join(tmpdir(), "summer-cli");
393
+ await mkdir(dir, { recursive: true });
394
+ const ext = snap.mime?.includes("png") ? "png" : "jpg";
395
+ localPath = join(dir, `screenshot-${Date.now()}.${ext}`);
396
+ await writeFile(localPath, Buffer.from(snap.base64, "base64"));
397
+ }
398
+ const { base64: _dropped, ...rest } = snap;
399
+ return { ...rest, localPath };
400
+ }
401
+ // ---------------------------------------------------------------------------
402
+ // The registry
403
+ // ---------------------------------------------------------------------------
404
+ function entry(name, summary, engineRequired, handler) {
405
+ return {
406
+ name,
407
+ slug: name.replace(/^summer_/, "").replace(/_/g, "-"),
408
+ summary,
409
+ engineRequired,
410
+ handler,
411
+ };
412
+ }
413
+ const SCENE_MUTATION_OPS = new Set([
414
+ "AddNode", "RemoveNode", "MoveNode", "ReparentNode", "ReplaceNode",
415
+ "SetProp", "SetResourceProperty", "ConnectSignal", "DisconnectSignal",
416
+ "InstantiateScene", "SaveScene", "SnapToSurface", "AlignDistribute3D", "Undo",
417
+ ]);
418
+ /** Read-only spatial queries: identity-bound to an exact scene, never saved. */
419
+ const SCENE_QUERY_OPS = new Set(["TestPlacement3D", "NavigationProbe3D", "Starcast3D"]);
420
+ // ---------------------------------------------------------------------------
421
+ // Spatial tool argument helpers. Mirror of the bounds in
422
+ // src/mcp/tools/spatial-tools.ts (exact paths, UTF-8 byte limits, subject
423
+ // counts). v3-followup: fold into one copy when mcp adopts this registry.
424
+ // ---------------------------------------------------------------------------
425
+ const SPATIAL_SCENE_PATH_LIMIT_BYTES = 512;
426
+ const SPATIAL_NODE_PATH_LIMIT_BYTES = 256;
427
+ function exactPath(args, key, limitBytes) {
428
+ const value = str(args, key).trim();
429
+ if (!value)
430
+ throw new ToolDispatchError(`${key} must name one exact path.`);
431
+ if (Buffer.byteLength(value, "utf8") > limitBytes) {
432
+ throw new ToolDispatchError(`${key} must be at most ${limitBytes} UTF-8 bytes after trimming.`);
433
+ }
434
+ return value;
435
+ }
436
+ function exactSubjectPaths(args, min, max) {
437
+ const raw = args.subjectPaths;
438
+ if (!Array.isArray(raw) || raw.some((entry) => typeof entry !== "string")) {
439
+ throw new ToolDispatchError("subjectPaths must be an array of exact node path strings");
440
+ }
441
+ const subjects = raw.map((entry, index) => {
442
+ const value = entry.trim();
443
+ if (!value)
444
+ throw new ToolDispatchError(`subjectPaths[${index}] must name one exact path.`);
445
+ if (Buffer.byteLength(value, "utf8") > SPATIAL_NODE_PATH_LIMIT_BYTES) {
446
+ throw new ToolDispatchError(`subjectPaths[${index}] must be at most ${SPATIAL_NODE_PATH_LIMIT_BYTES} UTF-8 bytes after trimming.`);
447
+ }
448
+ return value;
449
+ });
450
+ if (subjects.length < min || subjects.length > max) {
451
+ throw new ToolDispatchError(`subjectPaths must contain ${min}..${max} exact paths.`);
452
+ }
453
+ if (new Set(subjects).size !== subjects.length) {
454
+ throw new ToolDispatchError("subjectPaths must not contain duplicates.");
455
+ }
456
+ return subjects;
457
+ }
458
+ function finiteVector3(args, key, fallback) {
459
+ const value = args[key];
460
+ if (value === undefined && fallback)
461
+ return fallback;
462
+ if (!Array.isArray(value) ||
463
+ value.length !== 3 ||
464
+ !value.every((n) => typeof n === "number" && Number.isFinite(n))) {
465
+ throw new ToolDispatchError(`${key} must be an array of exactly three finite numbers`);
466
+ }
467
+ return value;
468
+ }
469
+ function optNumber(args, key, fallback) {
470
+ const value = args[key];
471
+ if (value === undefined)
472
+ return fallback;
473
+ if (typeof value !== "number" || !Number.isFinite(value)) {
474
+ throw new ToolDispatchError(`${key} must be a finite number`);
475
+ }
476
+ return value;
477
+ }
478
+ function optBoolean(args, key, fallback) {
479
+ const value = args[key];
480
+ if (value === undefined)
481
+ return fallback;
482
+ if (typeof value !== "boolean")
483
+ throw new ToolDispatchError(`${key} must be a boolean`);
484
+ return value;
485
+ }
486
+ /** runtime op: pre-flight on the resolved kind, send it alone with the
487
+ * op's own budget, and teach both failure classes (engine_lacks_op via the
488
+ * post-hoc rewrite; the runtime gates via the shared hints). */
489
+ async function runRuntimeOp(ctx, built) {
490
+ const fallback = RUNTIME_FALLBACKS[built.kind] ?? "use a RunVerification probe";
491
+ const client = await ctx.engine();
492
+ const missing = missingEngineOpResult(client, built.kind, fallback);
493
+ if (missing)
494
+ refuseMissingOp(missing);
495
+ const result = withRuntimeFailureHints(withOldEngineHint(await client.executeOps([built.op], undefined, built.timeoutMs), built.kind, fallback));
496
+ return requireSupportedOp(result, built.kind, fallback);
497
+ }
498
+ /** Capability pre-flight shared by the six spatial tools: refuse before
499
+ * sending when the engine advert PROVES the op is missing. */
500
+ async function requireSpatialOp(ctx, op, fallback) {
501
+ const client = await ctx.engine();
502
+ const missing = missingEngineOpResult(client, op, fallback);
503
+ if (missing)
504
+ refuseMissingOp(missing);
505
+ return client;
506
+ }
507
+ export const TOOL_DISPATCH = [
508
+ // --- asset ---
509
+ entry("summer_search_assets", "Search the Summer asset library and your own assets; returns import-ready URLs", false, (args) => searchAssetsGateway({
510
+ query: String(args.query ?? ""),
511
+ assetType: optStr(args, "assetType"),
512
+ limit: typeof args.limit === "number" ? args.limit : 10,
513
+ source: optStr(args, "source") ?? "library",
514
+ })),
515
+ entry("summer_list_my_assets", "List or search the signed-in user's generated and uploaded assets", false, (args) => searchAssetsGateway({
516
+ query: String(args.query ?? ""),
517
+ assetType: optStr(args, "assetType"),
518
+ limit: typeof args.limit === "number" ? args.limit : 10,
519
+ source: "my_assets",
520
+ })),
521
+ entry("summer_get_asset", "Fetch one asset by exact Summer asset id (file URL, metadata, license)", false, (args) => gatewayGet(`/api/mcp/assets/${encodeURIComponent(str(args, "assetId"))}`)),
522
+ entry("summer_get_asset_download_url", "Get a downloadable URL for an asset file (primary or thumbnail)", false, (args) => gatewayGet(`/api/mcp/assets/${encodeURIComponent(str(args, "assetId"))}/download-url`, new URLSearchParams({ role: optStr(args, "role") ?? "primary" }))),
523
+ entry("summer_import_asset_by_id", "Import an exact Summer asset id into the open project (optionally into a scene)", true, async (args, ctx) => {
524
+ const fetched = await gatewayGet(`/api/mcp/assets/${encodeURIComponent(str(args, "assetId"))}`);
525
+ const asset = fetched.asset;
526
+ if (!asset)
527
+ throw new ToolDispatchError("Asset not found.");
528
+ return importResolvedAsset(await ctx.engine(), {
529
+ asset,
530
+ parent: optStr(args, "parent"),
531
+ scenePath: optStr(args, "scenePath"),
532
+ path: optStr(args, "path"),
533
+ name: optStr(args, "name"),
534
+ });
535
+ }),
536
+ entry("summer_import_asset", "Search the asset library and import the best match in one step", true, async (args, ctx) => {
537
+ const result = await searchAssetsGateway({
538
+ query: str(args, "query"),
539
+ assetType: optStr(args, "assetType") ?? "3d_model",
540
+ limit: 5,
541
+ source: optStr(args, "source") ?? "all",
542
+ });
543
+ const assets = (result.assets ?? []);
544
+ if (assets.length === 0) {
545
+ throw new ToolDispatchError(`No assets found for "${String(args.query)}". Try different keywords.`);
546
+ }
547
+ return importResolvedAsset(await ctx.engine(), {
548
+ asset: assets[0],
549
+ parent: optStr(args, "parent"),
550
+ scenePath: optStr(args, "scenePath"),
551
+ });
552
+ }),
553
+ // --- creator (shared core capability, face: cli) ---
554
+ entry("summer_import_hdri", "Search Poly Haven's CC0 HDRIs and import one as environment lighting", true, async (args, ctx) => {
555
+ const parsed = parseToolArgs(importHdriArgsSchema, args, "import-hdri");
556
+ try {
557
+ return await importPolyHavenHdri(parsed, () => ctx.engine());
558
+ }
559
+ catch (err) {
560
+ if (err instanceof ImportHdriError) {
561
+ throw new ToolDispatchError(`${err.message}${err.hint ? ` ${err.hint}` : ""}`);
562
+ }
563
+ throw err;
564
+ }
565
+ }),
566
+ entry("summer_creator_publish", "Publish an exported .pck through the creator API (confirm-gated)", false, (args) => publishCreator({
567
+ project: optStr(args, "project"),
568
+ artifact: optStr(args, "artifact"),
569
+ version: optStr(args, "version"),
570
+ manifest: optStr(args, "manifest"),
571
+ projectId: optStr(args, "projectId"),
572
+ channel: optStr(args, "channel"),
573
+ notes: optStr(args, "notes"),
574
+ confirm: args.confirm === true,
575
+ face: "cli",
576
+ })),
577
+ entry("summer_creator_releases", "List creator-owned releases", false, (args) => listCreatorReleases({
578
+ projectId: optStr(args, "projectId"),
579
+ limit: typeof args.limit === "number" ? args.limit : 20,
580
+ cursor: optStr(args, "cursor"),
581
+ face: "cli",
582
+ })),
583
+ entry("summer_creator_config", "Read or update the shared non-secret Summer configuration", false, async (args) => {
584
+ const action = str(args, "action");
585
+ if (action === "list") {
586
+ const config = await readSummerConfig();
587
+ return {
588
+ ok: true,
589
+ values: Object.fromEntries(CONFIG_KEYS.map((name) => [name, getConfigValue(config, name) ?? null])),
590
+ };
591
+ }
592
+ const key = optStr(args, "key");
593
+ if (!key || !isConfigKey(key)) {
594
+ throw new ToolDispatchError(`A valid key is required. Use one of ${CONFIG_KEYS.join(", ")}.`);
595
+ }
596
+ if (action === "get") {
597
+ return { ok: true, key, value: getConfigValue(await readSummerConfig(), key) ?? null };
598
+ }
599
+ if (args.confirm !== true) {
600
+ throw new ToolDispatchError(`Changing ${key} requires confirmation. Retry with "confirm": true after the user approves the exact change.`);
601
+ }
602
+ if (action === "set") {
603
+ const value = optStr(args, "value");
604
+ if (value === undefined) {
605
+ throw new ToolDispatchError(`A value is required for ${key}.`);
606
+ }
607
+ const config = await setConfigValue(key, value);
608
+ return { ok: true, key, value: getConfigValue(config, key) };
609
+ }
610
+ if (action === "unset") {
611
+ const config = await unsetConfigValue(key);
612
+ return { ok: true, key, value: getConfigValue(config, key) ?? null };
613
+ }
614
+ throw new ToolDispatchError(`Unknown action "${action}". Use list, get, set, or unset.`);
615
+ }),
616
+ // --- debug ---
617
+ entry("summer_get_diagnostics", "Overview of editor console and runtime debugger errors/warnings", true, async (args, ctx) => requireEngineSuccess(await (await ctx.engine()).getDiagnostics())),
618
+ entry("summer_get_console", "Read recent editor Output panel messages (deduplicated)", true, async (args, ctx) => {
619
+ const op = {
620
+ op: "GetConsoleOutput",
621
+ max_lines: typeof args.max_lines === "number" ? args.max_lines : 100,
622
+ };
623
+ if (optStr(args, "filter"))
624
+ op.filter = args.filter;
625
+ if (optStr(args, "type"))
626
+ op.type = args.type;
627
+ const engineResult = requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
628
+ if (args.raw === true)
629
+ return engineResult;
630
+ const { result } = shapeEngineLogResponse(engineResult, {
631
+ errorsOnly: args.errors_only !== false,
632
+ errorsOnlyStrict: args.strict_errors === true,
633
+ maxEntries: typeof args.max_lines === "number" ? args.max_lines : 100,
634
+ });
635
+ // E2E 2026-09-03 F-07: the console is not where a played game's runtime
636
+ // errors go — the same _scope note the MCP face stamps.
637
+ return withConsoleScope(result);
638
+ }),
639
+ entry("summer_clear_console", "Clear the editor's Output panel", true, async (args, ctx) => requireEngineSuccess(await (await ctx.engine()).executeOps([{ op: "ClearConsoleOutput" }]))),
640
+ entry("summer_get_debugger_errors", "Read deduplicated runtime errors from the debugger", true, async (args, ctx) => {
641
+ const maxErrors = typeof args.max_errors === "number" ? args.max_errors : 50;
642
+ const op = { op: "GetDebuggerErrors", max_errors: maxErrors };
643
+ if (args.include_stack !== undefined)
644
+ op.include_stack = args.include_stack === true;
645
+ if (args.include_warnings === true)
646
+ op.include_warnings = true;
647
+ const engineResult = requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
648
+ if (args.raw === true)
649
+ return engineResult;
650
+ return shapeEngineLogResponse(engineResult, { maxEntries: maxErrors }).result;
651
+ }),
652
+ entry("summer_get_debugger_warnings", "Read runtime warnings from the debugger panel", true, async (args, ctx) => {
653
+ const maxWarnings = typeof args.max_warnings === "number" ? args.max_warnings : 50;
654
+ const op = {
655
+ op: "GetDebuggerErrors",
656
+ max_errors: maxWarnings,
657
+ type: "warning",
658
+ include_stack: args.include_stack !== false,
659
+ };
660
+ const engineResult = requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
661
+ if (args.raw === true)
662
+ return engineResult;
663
+ return shapeEngineLogResponse(engineResult, { maxEntries: maxWarnings }).result;
664
+ }),
665
+ entry("summer_play", "Start the game quietly (no Game-tab switch or focus grab; focus:true for the toolbar-style launch) — main scene or a specific scene; seed/fixed_fps/time_scale pins, instance/mode for playtests", true, async (args, ctx) => {
666
+ const seed = optNumberOrUndefined(args, "seed");
667
+ const fixedFps = optNumberOrUndefined(args, "fixed_fps");
668
+ const timeScale = optNumberOrUndefined(args, "time_scale");
669
+ const speed = optNumberOrUndefined(args, "speed");
670
+ if (seed !== undefined && !Number.isInteger(seed))
671
+ throw new ToolDispatchError("seed must be an integer");
672
+ if (fixedFps !== undefined && (!Number.isInteger(fixedFps) || fixedFps <= 0)) {
673
+ throw new ToolDispatchError("fixed_fps must be an integer > 0");
674
+ }
675
+ if (timeScale !== undefined && timeScale <= 0)
676
+ throw new ToolDispatchError("time_scale must be > 0");
677
+ const playArgs = {
678
+ scene: optStr(args, "scene"),
679
+ instance: optStr(args, "instance"),
680
+ mode: optStr(args, "mode"),
681
+ deterministic: typeof args.deterministic === "boolean" ? args.deterministic : undefined,
682
+ seed,
683
+ fixed_fps: fixedFps,
684
+ time_scale: timeScale,
685
+ speed,
686
+ focus: typeof args.focus === "boolean" ? args.focus : undefined,
687
+ };
688
+ if (args.focus !== undefined && typeof args.focus !== "boolean")
689
+ throw new ToolDispatchError("focus must be a boolean");
690
+ const requested = pickPlayDeterminism({ seed, fixed_fps: fixedFps, time_scale: timeScale });
691
+ const client = await ctx.engine();
692
+ // ONE implementation with the MCP face (runtime-control.ts playGame); this
693
+ // face only turns its outcomes into the CLI's thrown, structured failures.
694
+ const result = await buildOrRefuseAsync(() => playGame(client, playArgs));
695
+ if (isMissingOpResult(result))
696
+ refuseMissingOp(result);
697
+ const echoed = requireSupportedOp(result, "PlayGame", PLAY_INSTANCE_FALLBACK);
698
+ // Pins sent, no determinism block back: the engine predates the params and
699
+ // ignored them. Say so in the receipt instead of letting the v1 result pass
700
+ // as a pinned run (the engine's own block, when present, is authoritative).
701
+ if (requested && readPlayDeterminism(echoed) === null && echoed && typeof echoed === "object") {
702
+ return { ...echoed, determinism_note: PLAY_DETERMINISM_NOT_SUPPORTED };
703
+ }
704
+ return echoed;
705
+ }),
706
+ entry("summer_stop", "Stop the running game (or one offscreen instance)", true, async (args, ctx) => {
707
+ const client = await ctx.engine();
708
+ const instance = optStr(args, "instance");
709
+ if (!instance || instance.trim() === "main")
710
+ return requireEngineSuccess(await client.stop());
711
+ const missing = missingEngineOpResult(client, "ListGameInstances", RUNTIME_FALLBACKS.ListGameInstances);
712
+ if (missing)
713
+ refuseMissingOp(missing);
714
+ const { op, timeoutMs } = buildStopGameOp(instance);
715
+ return requireSupportedOp(withRuntimeFailureHints(withOldEngineHint(await client.executeOps([op], undefined, timeoutMs), "StopGame", RUNTIME_FALLBACKS.ListGameInstances)), "StopGame", RUNTIME_FALLBACKS.ListGameInstances);
716
+ }),
717
+ entry("summer_is_running", "Check whether the game is running", true, async (_args, ctx) => requireEngineSuccess(await (await ctx.engine()).executeOps([{ op: "IsGameRunning" }]))),
718
+ entry("summer_get_script_errors", "Check a GDScript file for parse/compile errors", true, async (args, ctx) => requireEngineSuccess(await (await ctx.engine()).getScriptErrors(str(args, "path")))),
719
+ entry("summer_create_debug_report", "Create a support-ready Markdown debug report", false, async (args) => {
720
+ const artifact = await createDebugReportArtifact({
721
+ issue: optStr(args, "issue"),
722
+ outputPath: optStr(args, "output_path"),
723
+ includePlaySession: args.include_play_session === true,
724
+ playWaitMs: typeof args.play_wait_ms === "number" ? args.play_wait_ms : 2500,
725
+ maxConsoleLines: typeof args.max_console_lines === "number" ? args.max_console_lines : 200,
726
+ maxDebuggerEntries: typeof args.max_debugger_entries === "number" ? args.max_debugger_entries : 100,
727
+ includeDoctor: args.include_doctor !== false,
728
+ });
729
+ return {
730
+ ok: true,
731
+ path: artifact.path,
732
+ engineConnected: artifact.report.engine.connected,
733
+ generatedAt: artifact.report.generatedAt,
734
+ issue: artifact.report.issue,
735
+ reviewNote: "Report omits auth tokens, but review local paths and stack traces before sending.",
736
+ };
737
+ }),
738
+ // --- file ---
739
+ entry("summer_read_file", "Read a project text file and its sha256 receipt", true, async (args, ctx) => requireEngineSuccess(await (await ctx.engine()).readProjectFile(safeProjectPath(str(args, "path")), typeof args.max_bytes === "number" ? args.max_bytes : 200_000))),
740
+ entry("summer_write_file", "Create or safely overwrite one complete project text file", true, async (args, ctx) => {
741
+ const safePath = safeProjectPath(str(args, "path"));
742
+ const content = args.content;
743
+ if (typeof content !== "string") {
744
+ throw new ToolDispatchError("Missing required string argument: content");
745
+ }
746
+ const guardedCreate = args.create_only === true;
747
+ const guardedOverwrite = validSha256(args.expected_sha256);
748
+ if (args.expected_sha256 !== undefined && !guardedOverwrite) {
749
+ throw new ToolDispatchError("Safe write refused: expected_sha256 must be a 64-character hexadecimal sha256 from read-file. Nothing was written.");
750
+ }
751
+ if (guardedCreate === guardedOverwrite) {
752
+ throw new ToolDispatchError("Safe write requires exactly one guard: create_only:true for a new file, or expected_sha256 from read-file for an existing file. Nothing was written.");
753
+ }
754
+ const op = { op: "WriteFile", path: safePath, content };
755
+ if (guardedCreate)
756
+ op.mustNotExist = true;
757
+ if (guardedOverwrite)
758
+ op.expectedSha256 = args.expected_sha256.toLowerCase();
759
+ return requireEngineSuccess(await (await ctx.engine()).executeIdentityBoundOps([op]));
760
+ }),
761
+ entry("summer_replace_text", "Safely replace text in an existing project file", true, async (args, ctx) => {
762
+ const safePath = safeProjectPath(str(args, "path"));
763
+ const oldText = str(args, "old_text");
764
+ const newText = typeof args.new_text === "string" ? args.new_text : "";
765
+ const replaceAll = args.replace_all === true;
766
+ const client = await ctx.engine();
767
+ const current = readTextPayload(await client.readProjectFile(safePath, 1_000_000));
768
+ const matches = occurrenceCount(current.content, oldText);
769
+ if (matches === 0) {
770
+ throw new ToolDispatchError("Safe replace refused: old_text was not found. Nothing was written.");
771
+ }
772
+ if (!replaceAll && matches !== 1) {
773
+ throw new ToolDispatchError(`Safe replace refused: old_text matched ${matches} times. Provide a unique span or set replace_all:true. Nothing was written.`);
774
+ }
775
+ const content = replaceAll
776
+ ? current.content.split(oldText).join(newText)
777
+ : current.content.replace(oldText, newText);
778
+ if (content === current.content) {
779
+ return { ok: true, noOp: true, path: safePath, sha256: current.sha256, matches };
780
+ }
781
+ return requireEngineSuccess(await client.executeIdentityBoundOps([
782
+ { op: "WriteFile", path: safePath, content, expectedSha256: current.sha256 },
783
+ ]));
784
+ }),
785
+ // --- generate ---
786
+ entry("summer_get_studio_workflow", "Discover Summer Studio guided workflow recipes", false, (args) => {
787
+ const params = new URLSearchParams();
788
+ const workflowId = optStr(args, "workflowId");
789
+ if (workflowId)
790
+ params.set("id", workflowId);
791
+ return gatewayGet("/api/mcp/workflows", params);
792
+ }),
793
+ entry("summer_generate_image", "Generate or edit an image via Summer Studio", false, (args) => gatewayPost("/api/mcp/generate/image", parseToolArgs(imageGenerationArgsSchema, args, "generate-image"))),
794
+ entry("summer_slice_asset_sheet", "Detect and crop every asset from a generated sheet image", false, (args) => gatewayPost("/api/mcp/generate/slice-asset-sheet", { assetId: str(args, "assetId") }, 300_000)),
795
+ entry("summer_generate_audio", "Generate speech, sound effects, music, or dialogue", false, (args) => {
796
+ const body = { capability: str(args, "capability") };
797
+ for (const key of ["text", "prompt", "voiceId", "modelId"]) {
798
+ if (optStr(args, key))
799
+ body[key] = args[key];
800
+ }
801
+ if (typeof args.durationSeconds === "number")
802
+ body.durationSeconds = args.durationSeconds;
803
+ if (Array.isArray(args.inputs))
804
+ body.inputs = args.inputs;
805
+ if (args.options)
806
+ body.options = args.options;
807
+ return gatewayPost("/api/mcp/generate/audio", body);
808
+ }),
809
+ entry("summer_generate_3d", "Generate a 3D model (optional auto-rig + animations)", false, async (args) => {
810
+ const options = {
811
+ ...(args.options ?? {}),
812
+ ...(Array.isArray(args.imageUrls) ? { imageUrls: args.imageUrls } : {}),
813
+ ...(optStr(args, "assetIntent") ? { assetIntent: args.assetIntent } : {}),
814
+ referencePreparation: optStr(args, "referencePreparation") ?? "auto",
815
+ ...(args.rig === true ? { rig: true } : {}),
816
+ ...(Array.isArray(args.animationNames) ? { animationNames: args.animationNames } : {}),
817
+ ...(Array.isArray(args.actionIds) ? { actionIds: args.actionIds } : {}),
818
+ ...(typeof args.riggingHeightMeters === "number"
819
+ ? { riggingHeightMeters: args.riggingHeightMeters }
820
+ : {}),
821
+ };
822
+ const result = await gatewayPost("/api/mcp/generate/3d", {
823
+ prompt: optStr(args, "prompt"),
824
+ kind: optStr(args, "kind") ?? "text-to-3d",
825
+ model: optStr(args, "model") ?? "hunyuan",
826
+ imageUrl: optStr(args, "imageUrl"),
827
+ title: optStr(args, "title"),
828
+ idempotencyKey: optStr(args, "idempotencyKey"),
829
+ options,
830
+ });
831
+ const jobId = typeof result.jobId === "string" ? result.jobId : undefined;
832
+ if (args.wait === false || !jobId)
833
+ return result;
834
+ const final = await pollGenerationJob(jobId);
835
+ return { ...final, jobId };
836
+ }),
837
+ entry("summer_generate_video", "Generate a video from text or an image", false, (args) => gatewayPost("/api/mcp/generate/video", {
838
+ prompt: str(args, "prompt"),
839
+ model: optStr(args, "model") ?? "ltx",
840
+ imageUrl: optStr(args, "imageUrl"),
841
+ duration: typeof args.duration === "number" ? args.duration : 5,
842
+ aspectRatio: optStr(args, "aspectRatio") ?? "16:9",
843
+ options: args.options,
844
+ })),
845
+ entry("summer_check_job", "Check the status of an async generation job", false, (args) => gatewayGet(`/api/mcp/jobs/${encodeURIComponent(str(args, "jobId"))}`, undefined, 15_000)),
846
+ entry("summer_generate_motion", "Generate a curated mocap clip for a rigged humanoid", false, async (args) => {
847
+ const result = await gatewayPost("/api/mcp/generate/motion", {
848
+ rigAssetId: str(args, "rigAssetId"),
849
+ backend: optStr(args, "backend") ?? "meshy-library",
850
+ motionName: str(args, "motionName"),
851
+ options: args.options,
852
+ });
853
+ const jobId = typeof result.jobId === "string" ? result.jobId : undefined;
854
+ if (args.wait === false || !jobId)
855
+ return result;
856
+ const final = await pollGenerationJob(jobId, 300_000);
857
+ return { ...final, jobId };
858
+ }),
859
+ // --- project ---
860
+ entry("summer_start_game_task", "Plan the right Summer workflow for a game-building task", false, async (args) => buildGameTaskPlan(parseToolArgs(gameTaskPlanInputSchema, args, "start-game-task"))),
861
+ entry("summer_get_agent_playbook", "AI-first operating guide for Summer MCP", false, async () =>
862
+ // Same content the MCP tool/prompt serve (core/capabilities/agent-playbook.ts);
863
+ // the boot drift notice is an MCP-surface extra and is null here.
864
+ buildAgentPlaybook()),
865
+ entry("summer_get_project_context", "Engine health, project/scene state, template pin, memory summary, and session rebind", true, async (args, ctx) => {
866
+ // Validate before connecting, as the MCP SDK does — a bad argument is a
867
+ // bad argument even with no engine running.
868
+ const parsed = parseToolArgs(projectContextInputSchema, args, "get-project-context");
869
+ // ONE builder for both faces (core/capabilities/project-context.ts) so the
870
+ // payload is byte-identical; the boot drift notice is an MCP-surface extra
871
+ // and is null here.
872
+ return buildProjectContext(await ctx.engine(), parsed);
873
+ }),
874
+ entry("summer_open", "Open a summerengine.com page or an editor surface by intent name, or print the URL/op", false, async (args, ctx) => {
875
+ // Same behavior as the MCP face (navigation-tools.ts) and the dedicated
876
+ // `summer open <target>` command: core/capabilities/navigation/open.ts.
877
+ const parsed = openArgsSchema.safeParse(args);
878
+ if (!parsed.success) {
879
+ throw new ToolDispatchError(`Invalid arguments for summer_open: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ")}`);
880
+ }
881
+ const result = await runOpen(parsed.data, {
882
+ engine: () => ctx.engine(),
883
+ openUrl: (url) => open(url),
884
+ isLoggedIn: async () => (await getAuthToken()) !== null,
885
+ gatewayUrl: resolveGatewayUrl,
886
+ });
887
+ if (!result.ok) {
888
+ throw new ToolResultError(result, result.hint ?? result.action);
889
+ }
890
+ return result;
891
+ }),
892
+ entry("summer_open_main_scene", "Open the project's configured main scene", true, async (_args, ctx) => {
893
+ const client = await ctx.engine();
894
+ const projectState = await client.getProjectState();
895
+ const mainScene = projectSettingValue(projectState, [
896
+ "application/run/main_scene",
897
+ "run/main_scene",
898
+ ]);
899
+ if (!mainScene) {
900
+ throw new ToolDispatchError("Could not resolve application/run/main_scene from project state. Open a scene explicitly with open-scene.");
901
+ }
902
+ return requireEngineSuccess(await client.executeOps([{ op: "OpenScene", path: mainScene }]));
903
+ }),
904
+ entry("summer_project_setting", "Set one project.godot setting", true, async (args, ctx) => {
905
+ const value = args.value;
906
+ if (typeof value !== "string" &&
907
+ typeof value !== "number" &&
908
+ typeof value !== "boolean") {
909
+ throw new ToolDispatchError("value must be a string, number, or boolean");
910
+ }
911
+ return requireEngineSuccess(await (await ctx.engine()).executeOps([
912
+ { op: "ProjectSetting", key: str(args, "key"), value },
913
+ ]));
914
+ }),
915
+ entry("summer_input_map_bind", "Create an input action and bind events to it", true, async (args, ctx) => {
916
+ if (!Array.isArray(args.events)) {
917
+ throw new ToolDispatchError("events must be an array of input event objects");
918
+ }
919
+ return requireEngineSuccess(await (await ctx.engine()).executeOps([
920
+ { op: "InputMapAddAction", name: str(args, "name") },
921
+ { op: "InputMapBind", name: str(args, "name"), events: args.events },
922
+ ]));
923
+ }),
924
+ entry("summer_get_scene_tree", "Read a scene's node tree (explicit depth/limit honored)", true, async (args, ctx) => {
925
+ const client = await ctx.engine();
926
+ const scenePath = optStr(args, "scenePath");
927
+ const depth = typeof args.depth === "number" ? args.depth : undefined;
928
+ const limit = typeof args.limit === "number" ? args.limit : undefined;
929
+ if (depth === undefined && limit === undefined) {
930
+ return requireEngineSuccess(await client.getSceneState(scenePath));
931
+ }
932
+ let target = scenePath;
933
+ if (!target) {
934
+ const snapshot = (await client.getSceneState());
935
+ const provenance = (snapshot?.provenance ?? {});
936
+ const data = (snapshot?.data ?? {});
937
+ target =
938
+ (typeof provenance.scenePath === "string" && provenance.scenePath) ||
939
+ (typeof data.scenePath === "string" && data.scenePath) ||
940
+ undefined;
941
+ if (!target) {
942
+ return {
943
+ ...snapshot,
944
+ depthLimitApplied: false,
945
+ note: "depth/limit were IGNORED: the current scene path could not be resolved. Pass scenePath explicitly to apply them.",
946
+ };
947
+ }
948
+ }
949
+ return requireEngineSuccess(await client.getSceneState(target, { depth, limit }));
950
+ }),
951
+ entry("summer_import_from_url", "Download one file by URL through Godot's import pipeline", true, async (args, ctx) => {
952
+ const op = { op: "ImportFromUrl", url: str(args, "url") };
953
+ if (optStr(args, "path"))
954
+ op.path = args.path;
955
+ return requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
956
+ }),
957
+ entry("summer_import_from_url_batch", "Download multiple files by URL in one operation", true, async (args, ctx) => {
958
+ if (!Array.isArray(args.imports)) {
959
+ throw new ToolDispatchError("imports must be an array of {url, path} objects");
960
+ }
961
+ return requireEngineSuccess(await (await ctx.engine()).executeOps([{ op: "ImportFromUrlBatch", imports: args.imports }]));
962
+ }),
963
+ // --- scene ---
964
+ entry("summer_create_scene", "Create a new minimal .tscn with a create-only guard", true, async (args, ctx) => {
965
+ const safePath = str(args, "path").trim().replace(/\\/g, "/");
966
+ const rootName = optStr(args, "rootName") ?? "Main";
967
+ const rootType = optStr(args, "rootType") ?? "Node3D";
968
+ if (!safePath.startsWith("res://") || safePath.includes("..")) {
969
+ throw new ToolDispatchError("Scene path must be a traversal-free res:// project path.");
970
+ }
971
+ if (!safePath.endsWith(".tscn")) {
972
+ throw new ToolDispatchError("New scenes must use the text format: the path must end in .tscn.");
973
+ }
974
+ if (!/^[A-Za-z_][A-Za-z0-9_\- ]*$/.test(rootName)) {
975
+ throw new ToolDispatchError(`Invalid rootName "${rootName}".`);
976
+ }
977
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(rootType)) {
978
+ throw new ToolDispatchError(`Invalid rootType "${rootType}".`);
979
+ }
980
+ const client = await ctx.engine();
981
+ const receipt = requireEngineSuccess(await client.executeIdentityBoundOps([
982
+ {
983
+ op: "WriteFile",
984
+ path: safePath,
985
+ content: `[gd_scene format=3]\n\n[node name="${rootName}" type="${rootType}"]\n`,
986
+ mustNotExist: true,
987
+ },
988
+ ]));
989
+ return {
990
+ ok: true,
991
+ created: safePath,
992
+ rootName,
993
+ rootType,
994
+ receipt,
995
+ hint: "The new scene is on disk but not open. Use open-scene to start editing it.",
996
+ };
997
+ }),
998
+ entry("summer_add_node", "Add a node to an exact scene", true, async (args, ctx) => executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [
999
+ { op: "AddNode", parent: str(args, "parent"), type: str(args, "type"), name: str(args, "name") },
1000
+ ])),
1001
+ entry("summer_set_prop", "Set a node property (Godot string syntax for complex types)", true, async (args, ctx) => executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [
1002
+ { op: "SetProp", path: str(args, "path"), key: str(args, "key"), value: args.value },
1003
+ ])),
1004
+ entry("summer_set_resource_property", "Set a nested property on a node's resource", true, async (args, ctx) => executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [
1005
+ {
1006
+ op: "SetResourceProperty",
1007
+ nodePath: str(args, "nodePath"),
1008
+ resourceProperty: str(args, "resourceProperty"),
1009
+ subProperty: str(args, "subProperty"),
1010
+ value: args.value,
1011
+ },
1012
+ ])),
1013
+ entry("summer_remove_node", "Remove a node (and children) from a scene", true, async (args, ctx) => executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [
1014
+ { op: "RemoveNode", path: str(args, "path") },
1015
+ ])),
1016
+ entry("summer_save_scene", "Save an explicit scene to disk (or save-as)", true, async (args, ctx) => {
1017
+ const op = { op: "SaveScene" };
1018
+ if (optStr(args, "path"))
1019
+ op.path = args.path;
1020
+ return executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [op]);
1021
+ }),
1022
+ entry("summer_open_scene", "Open a scene file in the editor", true, async (args, ctx) => requireEngineSuccess(await (await ctx.engine()).executeOps([{ op: "OpenScene", path: str(args, "path") }]))),
1023
+ entry("summer_instantiate_scene", "Add an existing scene or 3D model as a child node", true, async (args, ctx) => {
1024
+ const op = {
1025
+ op: "InstantiateScene",
1026
+ parent: str(args, "parent"),
1027
+ scene: str(args, "scene"),
1028
+ };
1029
+ if (optStr(args, "name"))
1030
+ op.name = args.name;
1031
+ return executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [op]);
1032
+ }),
1033
+ entry("summer_connect_signal", "Connect a signal between two nodes", true, async (args, ctx) => executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [
1034
+ {
1035
+ op: "ConnectSignal",
1036
+ emitter: str(args, "emitter"),
1037
+ signal: str(args, "signal"),
1038
+ receiver: str(args, "receiver"),
1039
+ method: str(args, "method"),
1040
+ },
1041
+ ])),
1042
+ entry("summer_select_node", "Select a node in the editor scene tree", true, async (args, ctx) => {
1043
+ const op = { op: "SelectNode", nodePath: str(args, "nodePath") };
1044
+ if (optStr(args, "scenePath"))
1045
+ op.scenePath = args.scenePath;
1046
+ return requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
1047
+ }),
1048
+ entry("summer_replace_node", "Replace a node with a different type or scene", true, async (args, ctx) => {
1049
+ const op = { op: "ReplaceNode", path: str(args, "path") };
1050
+ if (optStr(args, "type"))
1051
+ op.type = args.type;
1052
+ if (optStr(args, "scene"))
1053
+ op.scene = args.scene;
1054
+ return executeSceneMutation(await ctx.engine(), str(args, "scenePath"), [op]);
1055
+ }),
1056
+ entry("summer_inspect_node", "Get all editable properties of a node", true, async (args, ctx) =>
1057
+ // E2E 2026-09-03 F-14: the engine returns Variant.Type as a bare int; both
1058
+ // faces add type_name (core/capabilities/variant-types.ts).
1059
+ annotateVariantTypes(requireEngineSuccess(await (await ctx.engine()).inspectNode(str(args, "path"))))),
1060
+ entry("summer_inspect_resource", "Get all properties of a resource", true, async (args, ctx) => requireEngineSuccess(await (await ctx.engine()).inspectResource(str(args, "path")))),
1061
+ entry("summer_batch", "Run multiple engine ops as one undo group (verbatim passthrough)", true, async (args, ctx) => {
1062
+ if (!Array.isArray(args.ops)) {
1063
+ throw new ToolDispatchError("ops must be an array of operation objects");
1064
+ }
1065
+ const ops = args.ops;
1066
+ const rawFileMutation = ops.find((op) => {
1067
+ const kind = String(op.op ?? "");
1068
+ return kind === "WriteFile" || kind === "ReplaceText";
1069
+ });
1070
+ if (rawFileMutation) {
1071
+ throw new ToolDispatchError(`batch does not accept raw ${String(rawFileMutation.op)} operations. ` +
1072
+ "Use write-file or replace-text so content guards are enforced.");
1073
+ }
1074
+ const scenePath = optStr(args, "scenePath");
1075
+ const containsMutation = ops.some((op) => SCENE_MUTATION_OPS.has(String(op.op ?? "")));
1076
+ const needsScenePath = containsMutation || ops.some((op) => SCENE_QUERY_OPS.has(String(op.op ?? "")));
1077
+ if (needsScenePath && !scenePath) {
1078
+ throw new ToolDispatchError("batch requires scenePath when ops targets a scene");
1079
+ }
1080
+ const client = await ctx.engine();
1081
+ const options = { groupUndo: true, ...(scenePath ? { scenePath } : {}) };
1082
+ if (containsMutation)
1083
+ return executeSceneMutation(client, scenePath, ops, options);
1084
+ return executeOpsChunked((chunk) => needsScenePath
1085
+ ? client.executeIdentityBoundOps(chunk, options)
1086
+ : client.executeOps(chunk, options), ops, resolveSingleOnlyOps(client));
1087
+ }),
1088
+ // --- scene scripting ---
1089
+ entry("summer_run_script", "Run a GDScript func run(ctx) inside the live editor against the open scene", true, async (args, ctx) => {
1090
+ const client = await ctx.engine();
1091
+ const missing = missingEngineOpResult(client, "RunSceneScript", RUN_SCRIPT_FALLBACK);
1092
+ if (missing)
1093
+ refuseMissingOp(missing);
1094
+ const undo = optStr(args, "undo");
1095
+ const { op, timeoutMs } = buildRunSceneScriptOp({
1096
+ source: str(args, "source"),
1097
+ max_seconds: typeof args.max_seconds === "number" ? args.max_seconds : undefined,
1098
+ checkpoint: typeof args.checkpoint === "boolean" ? args.checkpoint : undefined,
1099
+ undo: undo === "action" || undo === "none" ? undo : undefined,
1100
+ });
1101
+ return requireSupportedOp(await client.executeIdentityBoundOps([op], undefined, timeoutMs), "RunSceneScript", RUN_SCRIPT_FALLBACK);
1102
+ }),
1103
+ entry("summer_run_editor_script", "Run an EditorScript in a fresh headless child editor against the on-disk project", true, async (args, ctx) => {
1104
+ const client = await ctx.engine();
1105
+ const missing = missingEngineOpResult(client, "RunEditorScript", RUN_EDITOR_SCRIPT_FALLBACK);
1106
+ if (missing)
1107
+ refuseMissingOp(missing);
1108
+ const { op, timeoutMs } = buildRunEditorScriptOp({
1109
+ source: str(args, "source"),
1110
+ max_seconds: typeof args.max_seconds === "number" ? args.max_seconds : undefined,
1111
+ checkpoint: typeof args.checkpoint === "boolean" ? args.checkpoint : undefined,
1112
+ });
1113
+ return requireSupportedOp(await client.executeIdentityBoundOps([op], undefined, timeoutMs), "RunEditorScript", RUN_EDITOR_SCRIPT_FALLBACK);
1114
+ }),
1115
+ entry("summer_api_docs", "Offline engine class-reference lookup (properties, methods, signals, constants)", false, async (args) => lookupApiDocs(str(args, "class_name"), optStr(args, "member"))),
1116
+ // --- events (engine events channel; a capability, not an op) ---
1117
+ entry("summer_wait_for_event", "Block until a matching engine event (play.started, op.applied by requestId, script.error, ...) arrives or the timeout elapses", true, async (args, ctx) => {
1118
+ const parsed = parseToolArgs(waitForEventArgsSchema, args, "wait-for-event");
1119
+ const client = await requireEventsChannel(ctx);
1120
+ return requireEventsSuccess(await waitForEvent(client, parsed));
1121
+ }),
1122
+ entry("summer_recent_events", "Read the newest engine events in one zero-wait poll (and the next_seq cursor to wait from)", true, async (args, ctx) => {
1123
+ const parsed = parseToolArgs(recentEventsArgsSchema, args, "recent-events");
1124
+ const client = await requireEventsChannel(ctx);
1125
+ return requireEventsSuccess(await recentEvents(client, parsed));
1126
+ }),
1127
+ // --- mesh fabrication ---
1128
+ entry("summer_fabricate_3d", "Fabricate a mesh with a bpy script in the user's own Blender (headless, supervised), import the .glb, optionally instantiate it", true, async (args, ctx) => {
1129
+ // Validate with the SAME zod contract the MCP face registers, before any
1130
+ // engine connection — a bad destination never needs a running editor.
1131
+ const parsed = parseToolArgs(fabricateArgsSchema, args, "fabricate-3d");
1132
+ const client = await ctx.engine();
1133
+ const missing = missingEngineOpResult(client, "FabricateMesh", FABRICATE_FALLBACK);
1134
+ if (missing)
1135
+ refuseMissingOp(missing);
1136
+ const { op, timeoutMs } = buildFabricateMeshOp(parsed);
1137
+ return requireSupportedOp(await client.executeIdentityBoundOps([op], undefined, timeoutMs), "FabricateMesh", FABRICATE_FALLBACK);
1138
+ }),
1139
+ // --- editor UI control (editor UI control) ---
1140
+ entry("summer_ui_actions", "List the editor's named actions (mode list) or invoke one by name exactly as its shortcut would (mode invoke)", true, async (args, ctx) => {
1141
+ // Validate with the SAME zod contract the MCP face registers, before any
1142
+ // engine connection — a malformed mode/action_name never needs an editor.
1143
+ const parsed = parseToolArgs(uiActionsArgsSchema, args, "ui-actions");
1144
+ return dispatchUiOp(ctx, buildUiActionsOp(parsed));
1145
+ }),
1146
+ entry("summer_ui_tree", "Structured Control tree of the live editor UI, or every visible dialog with its blocking flag (root dialogs)", true, async (args, ctx) => {
1147
+ const parsed = parseToolArgs(uiTreeArgsSchema, args, "ui-tree");
1148
+ return dispatchUiOp(ctx, buildUiTreeOp(parsed));
1149
+ }),
1150
+ entry("summer_ui_activate", "Activate one editor control by tree path (press/toggle/focus/select_tab/set_text/set_value) or dismiss a dialog", true, async (args, ctx) => {
1151
+ const parsed = parseToolArgs(uiActivateArgsSchema, args, "ui-activate");
1152
+ return dispatchUiOp(ctx, buildUiActivateOp(parsed));
1153
+ }),
1154
+ entry("summer_ui_screenshot", "PNG of the editor window or one dock/dialog/control, saved to a temp file (pixels-last fallback)", true, async (args, ctx) => {
1155
+ const parsed = parseToolArgs(uiScreenshotArgsSchema, args, "ui-screenshot");
1156
+ const result = await dispatchUiOp(ctx, buildUiScreenshotOp(parsed));
1157
+ const image = uiScreenshotImage(result);
1158
+ if (!image) {
1159
+ // A "success" with no bytes: hand the receipt back rather than a broken
1160
+ // image; the caller reads the UI structurally with ui-tree instead.
1161
+ return {
1162
+ ...result,
1163
+ note: "UiScreenshot succeeded but returned no image data. Retry once; if it persists, read the UI structurally with ui-tree.",
1164
+ };
1165
+ }
1166
+ // Same temp-file convention as `summer tool screenshot`: the shell face
1167
+ // cannot show an image, so the PNG lands next to the other captures and
1168
+ // the receipt names the path. Never under the project or ~/.summer.
1169
+ const dir = join(tmpdir(), "summer-cli");
1170
+ await mkdir(dir, { recursive: true });
1171
+ const localPath = join(dir, `ui-screenshot-${Date.now()}.png`);
1172
+ await writeFile(localPath, Buffer.from(image.base64, "base64"));
1173
+ return { ...image.receipt, local_path: localPath, caption: uiScreenshotCaption(image) };
1174
+ }),
1175
+ // --- perception ---
1176
+ entry("summer_world_snapshot", "Structured snapshot of the edited scene (transforms, AABBs, fingerprints, counts)", true, async (args, ctx) => {
1177
+ const client = await ctx.engine();
1178
+ const missing = missingEngineOpResult(client, "GetWorldSnapshot", WORLD_SNAPSHOT_FALLBACK);
1179
+ if (missing)
1180
+ refuseMissingOp(missing);
1181
+ const op = { op: "GetWorldSnapshot" };
1182
+ if (optStr(args, "scene_path"))
1183
+ op.scene_path = args.scene_path;
1184
+ if (typeof args.max_nodes === "number")
1185
+ op.max_nodes = args.max_nodes;
1186
+ return requireSupportedOp(await client.executeOps([op]), "GetWorldSnapshot", WORLD_SNAPSHOT_FALLBACK);
1187
+ }),
1188
+ entry("summer_snapshot_diff", "Diff two world snapshots into added/removed/changed nodes and count deltas", true, async (args, ctx) => {
1189
+ const client = await ctx.engine();
1190
+ const missing = missingEngineOpResult(client, "DiffWorldSnapshot", SNAPSHOT_DIFF_FALLBACK);
1191
+ if (missing)
1192
+ refuseMissingOp(missing);
1193
+ const op = { op: "DiffWorldSnapshot", from_id: str(args, "from_id") };
1194
+ if (optStr(args, "to_id"))
1195
+ op.to_id = args.to_id;
1196
+ return requireSupportedOp(await client.executeOps([op]), "DiffWorldSnapshot", SNAPSHOT_DIFF_FALLBACK);
1197
+ }),
1198
+ entry("summer_get_runtime_tree", "Scene tree of the RUNNING game (live runtime state)", true, async (args, ctx) => {
1199
+ const client = await ctx.engine();
1200
+ const missing = missingEngineOpResult(client, "GetRuntimeSceneTree", RUNTIME_TREE_FALLBACK);
1201
+ if (missing)
1202
+ refuseMissingOp(missing);
1203
+ const op = { op: "GetRuntimeSceneTree" };
1204
+ if (optStr(args, "path"))
1205
+ op.path = args.path;
1206
+ if (typeof args.depth === "number")
1207
+ op.depth = args.depth;
1208
+ if (typeof args.limit === "number")
1209
+ op.limit = args.limit;
1210
+ return requireSupportedOp(await client.executeOps([op]), "GetRuntimeSceneTree", RUNTIME_TREE_FALLBACK);
1211
+ }),
1212
+ entry("summer_inspect_runtime_node", "Live properties of one node in the RUNNING game", true, async (args, ctx) => {
1213
+ const client = await ctx.engine();
1214
+ const missing = missingEngineOpResult(client, "GetRuntimeNode", RUNTIME_NODE_FALLBACK);
1215
+ if (missing)
1216
+ refuseMissingOp(missing);
1217
+ return requireSupportedOp(await client.executeOps([{ op: "GetRuntimeNode", path: str(args, "path") }]), "GetRuntimeNode", RUNTIME_NODE_FALLBACK);
1218
+ }),
1219
+ // --- runtime control & playtest (runtime control) ---
1220
+ entry("summer_runtime_set", "Set one property on a node in the RUNNING game (never the scene file)", true, async (args, ctx) => runRuntimeOp(ctx, buildRuntimeSetOp(parseToolArgs(runtimeSetArgsSchema, args, "runtime-set")))),
1221
+ entry("summer_runtime_call", "Call one method on a node in the RUNNING game and return its result", true, async (args, ctx) => runRuntimeOp(ctx, buildRuntimeCallOp(parseToolArgs(runtimeCallArgsSchema, args, "runtime-call")))),
1222
+ entry("summer_runtime_spawn", "Spawn a PackedScene into, or free a node from, the RUNNING game", true, async (args, ctx) => runRuntimeOp(ctx, buildRuntimeSpawnOp(parseToolArgs(runtimeSpawnArgsSchema, args, "runtime-spawn")))),
1223
+ entry("summer_runtime_animate", "Drive/read an AnimationPlayer, AnimationTree state machine, or Skeleton3D bones in the RUNNING game", true, async (args, ctx) => runRuntimeOp(ctx, buildRuntimeAnimateOp(parseToolArgs(runtimeAnimateArgsSchema, args, "runtime-animate")))),
1224
+ entry("summer_game_control", "Pause, resume, step exact frames, set speed, or list instances of the RUNNING game", true, async (args, ctx) => runRuntimeOp(ctx, buildGameControlOp(parseToolArgs(gameControlArgsSchema, args, "game-control")))),
1225
+ entry("summer_game_input", "Script timed synthetic input, or record/replay real input, in the RUNNING game", true, async (args, ctx) => runRuntimeOp(ctx, buildGameInputOp(parseToolArgs(gameInputArgsSchema, args, "game-input")))),
1226
+ entry("summer_game_probe", "State + screenshot of ONE frame of the RUNNING game, frame-stamped (image saved to a file)", true, async (args, ctx) => {
1227
+ const result = await runRuntimeOp(ctx, buildGameProbeOp(parseToolArgs(gameProbeArgsSchema, args, "game-probe")));
1228
+ // The CLI face cannot return an image block: write the JPEG beside the
1229
+ // screenshot tool's files and hand back the path instead of the base64.
1230
+ const payload = findProbePayload(result);
1231
+ const image = payload && typeof payload.image_base64 === "string" ? payload.image_base64 : null;
1232
+ const stripped = stripProbeImage(result);
1233
+ if (!payload || !image)
1234
+ return stripped;
1235
+ const dir = join(tmpdir(), "summer-cli");
1236
+ await mkdir(dir, { recursive: true });
1237
+ const ext = typeof payload.mime === "string" && payload.mime.includes("png") ? "png" : "jpg";
1238
+ const imagePath = join(dir, `game-probe-${Date.now()}.${ext}`);
1239
+ await writeFile(imagePath, Buffer.from(image, "base64"));
1240
+ return { ...stripped, image_path: imagePath, frame_stamp: probeFrameStamp(payload) };
1241
+ }),
1242
+ // --- spatial / world building ---
1243
+ entry("summer_test_placement", "Ghost-test one node at a candidate global pose (read-only, never saves)", true, async (args, ctx) => {
1244
+ const client = await requireSpatialOp(ctx, "TestPlacement3D", TEST_PLACEMENT_FALLBACK);
1245
+ const scenePath = exactPath(args, "scenePath", SPATIAL_SCENE_PATH_LIMIT_BYTES);
1246
+ const maxFloorDistance = optNumber(args, "maxFloorDistance", 5);
1247
+ if (maxFloorDistance < 0.001)
1248
+ throw new ToolDispatchError("maxFloorDistance must be at least 0.001.");
1249
+ return requireSupportedOp(await client.executeIdentityBoundOps([
1250
+ {
1251
+ op: "TestPlacement3D",
1252
+ subject_path: exactPath(args, "subjectPath", SPATIAL_NODE_PATH_LIMIT_BYTES),
1253
+ candidate_global_position: finiteVector3(args, "candidateGlobalPosition"),
1254
+ candidate_global_rotation_degrees: finiteVector3(args, "candidateGlobalRotationDegrees"),
1255
+ collision_mask: optNumber(args, "collisionMask", 0xffffffff),
1256
+ collide_with_areas: optBoolean(args, "collideWithAreas", true),
1257
+ max_floor_distance: maxFloorDistance,
1258
+ ground_tolerance: optNumber(args, "groundTolerance", 0.05),
1259
+ margin: optNumber(args, "margin", 0.001),
1260
+ },
1261
+ ], { scenePath }), "TestPlacement3D", TEST_PLACEMENT_FALLBACK);
1262
+ }),
1263
+ entry("summer_snap_to_surface", "Seat one subject on the first surface along a world ray (mutation + save)", true, async (args, ctx) => {
1264
+ const client = await requireSpatialOp(ctx, "SnapToSurface", SNAP_TO_SURFACE_FALLBACK);
1265
+ const scenePath = exactPath(args, "scenePath", SPATIAL_SCENE_PATH_LIMIT_BYTES);
1266
+ const direction = finiteVector3(args, "direction", [0, -1, 0]);
1267
+ if (direction.reduce((sum, n) => sum + n * n, 0) <= 0.00001) {
1268
+ throw new ToolDispatchError("direction squared length must exceed 0.00001.");
1269
+ }
1270
+ const maxDistance = optNumber(args, "maxDistance", 20);
1271
+ const gap = optNumber(args, "gap", 0);
1272
+ if (maxDistance <= 0)
1273
+ throw new ToolDispatchError("maxDistance must be positive.");
1274
+ if (gap < 0 || gap > maxDistance)
1275
+ throw new ToolDispatchError("gap must be >= 0 and must not exceed maxDistance.");
1276
+ return requireSupportedOp(await executeSceneMutation(client, scenePath, [
1277
+ {
1278
+ op: "SnapToSurface",
1279
+ subject_path: exactPath(args, "subjectPath", SPATIAL_NODE_PATH_LIMIT_BYTES),
1280
+ direction,
1281
+ max_distance: maxDistance,
1282
+ gap,
1283
+ align_up: optBoolean(args, "alignUp", false),
1284
+ },
1285
+ ]), "SnapToSurface", SNAP_TO_SURFACE_FALLBACK);
1286
+ }),
1287
+ entry("summer_align_distribute_3d", "Align or equal-space 2-16 ordered subjects along one world axis (mutation + save)", true, async (args, ctx) => {
1288
+ const client = await requireSpatialOp(ctx, "AlignDistribute3D", ALIGN_DISTRIBUTE_FALLBACK);
1289
+ const scenePath = exactPath(args, "scenePath", SPATIAL_SCENE_PATH_LIMIT_BYTES);
1290
+ const axis = finiteVector3(args, "axis");
1291
+ if (Math.hypot(...axis) <= 1e-6)
1292
+ throw new ToolDispatchError("axis must be non-zero.");
1293
+ const mode = str(args, "mode");
1294
+ if (!["align_min", "align_center", "align_max", "distribute_centers", "distribute_gaps"].includes(mode)) {
1295
+ throw new ToolDispatchError("mode must be one of align_min, align_center, align_max, distribute_centers, distribute_gaps.");
1296
+ }
1297
+ return requireSupportedOp(await executeSceneMutation(client, scenePath, [
1298
+ { op: "AlignDistribute3D", subject_paths: exactSubjectPaths(args, 2, 16), axis, mode },
1299
+ ]), "AlignDistribute3D", ALIGN_DISTRIBUTE_FALLBACK);
1300
+ }),
1301
+ entry("summer_navigation_probe", "Read-only navigation reachability between two world points on the scene's nav map", true, async (args, ctx) => {
1302
+ const client = await requireSpatialOp(ctx, "NavigationProbe3D", NAVIGATION_PROBE_FALLBACK);
1303
+ const scenePath = exactPath(args, "scenePath", SPATIAL_SCENE_PATH_LIMIT_BYTES);
1304
+ const navigationLayers = optNumber(args, "navigationLayers", 1);
1305
+ if (!Number.isInteger(navigationLayers) || navigationLayers < 1 || navigationLayers > 0xffffffff) {
1306
+ throw new ToolDispatchError("navigationLayers must be an integer from 1 through 4294967295.");
1307
+ }
1308
+ return requireSupportedOp(await client.executeIdentityBoundOps([
1309
+ {
1310
+ op: "NavigationProbe3D",
1311
+ start: finiteVector3(args, "start"),
1312
+ end: finiteVector3(args, "end"),
1313
+ navigation_layers: navigationLayers,
1314
+ optimize: optBoolean(args, "optimize", true),
1315
+ },
1316
+ ], { scenePath }), "NavigationProbe3D", NAVIGATION_PROBE_FALLBACK);
1317
+ }),
1318
+ entry("summer_starcast", "Read-only 26-direction spatial rundown around one exact node: clearance, contacts, grounding (never saves)", true, async (args, ctx) => {
1319
+ const client = await requireSpatialOp(ctx, "Starcast3D", STARCAST_FALLBACK);
1320
+ const scenePath = exactPath(args, "scenePath", SPATIAL_SCENE_PATH_LIMIT_BYTES);
1321
+ const detail = optStr(args, "detail") ?? "summary";
1322
+ if (detail !== "summary" && detail !== "full") {
1323
+ throw new ToolDispatchError("detail must be one of summary, full.");
1324
+ }
1325
+ const directionSpace = optStr(args, "directionSpace") ?? "world";
1326
+ if (directionSpace !== "world" && directionSpace !== "local") {
1327
+ throw new ToolDispatchError("directionSpace must be one of world, local.");
1328
+ }
1329
+ const maxDistance = optNumber(args, "maxDistance", 20);
1330
+ if (maxDistance <= 0 || maxDistance > 10000)
1331
+ throw new ToolDispatchError("maxDistance must be positive and at most 10000.");
1332
+ const nearbyRadius = optNumber(args, "nearbyRadius", 10);
1333
+ if (nearbyRadius < 0 || nearbyRadius > 10000)
1334
+ throw new ToolDispatchError("nearbyRadius must be from 0 through 10000.");
1335
+ const collisionMask = optNumber(args, "collisionMask", 0xffffffff);
1336
+ if (!Number.isInteger(collisionMask) || collisionMask < 0 || collisionMask > 0xffffffff) {
1337
+ throw new ToolDispatchError("collisionMask must be an integer from 0 through 4294967295.");
1338
+ }
1339
+ const maxHitsPerDirection = optNumber(args, "maxHitsPerDirection", 3);
1340
+ if (!Number.isInteger(maxHitsPerDirection) || maxHitsPerDirection < 1 || maxHitsPerDirection > 8) {
1341
+ throw new ToolDispatchError("maxHitsPerDirection must be an integer from 1 through 8.");
1342
+ }
1343
+ const maxResults = optNumber(args, "maxResults", 64);
1344
+ if (!Number.isInteger(maxResults) || maxResults < 1 || maxResults > 128) {
1345
+ throw new ToolDispatchError("maxResults must be an integer from 1 through 128.");
1346
+ }
1347
+ const margin = optNumber(args, "margin", 0.001);
1348
+ if (margin < 0 || margin > 1)
1349
+ throw new ToolDispatchError("margin must be from 0 through 1.");
1350
+ return requireSupportedOp(await client.executeIdentityBoundOps([
1351
+ {
1352
+ op: "Starcast3D",
1353
+ path: exactPath(args, "path", SPATIAL_NODE_PATH_LIMIT_BYTES),
1354
+ detail,
1355
+ max_distance: maxDistance,
1356
+ nearby_radius: nearbyRadius,
1357
+ direction_space: directionSpace,
1358
+ collision_mask: collisionMask,
1359
+ collide_with_areas: optBoolean(args, "collideWithAreas", true),
1360
+ max_hits_per_direction: maxHitsPerDirection,
1361
+ max_results: maxResults,
1362
+ margin,
1363
+ },
1364
+ ], { scenePath }), "Starcast3D", STARCAST_FALLBACK);
1365
+ }),
1366
+ // --- visual ---
1367
+ entry("summer_screenshot", "Capture an editor viewport, scene render, or game frame to a file", true, async (args, ctx) => {
1368
+ const client = await ctx.engine();
1369
+ const target = optStr(args, "target") ?? "viewport";
1370
+ // Same capture path as the MCP face (core/capabilities/capture.ts): every
1371
+ // frame is content-checked, a flat viewport frame is recaptured once, and a
1372
+ // camera-less scene render learns its 2D/3D kind — the receipt carries
1373
+ // frameQuality / recapture / sceneKind identically (E2E 2026-09-03 F-01, F-05).
1374
+ let snap;
1375
+ if (target === "game") {
1376
+ snap = await captureGame(client);
1377
+ }
1378
+ else if (target === "scene") {
1379
+ const framing = optStr(args, "framing");
1380
+ if (framing !== undefined && !SCREENSHOT_FRAMINGS.includes(framing)) {
1381
+ throw new ToolDispatchError(`framing must be one of ${SCREENSHOT_FRAMINGS.join(", ")}`);
1382
+ }
1383
+ const preview = buildOrRefuse(() => buildScenePreviewInput({
1384
+ scenePath: optStr(args, "scenePath"),
1385
+ framing: framing,
1386
+ bookmark_name: optStr(args, "bookmark_name"),
1387
+ size: Array.isArray(args.size) ? args.size : undefined,
1388
+ nodePath: optStr(args, "nodePath"),
1389
+ camera_path: optStr(args, "camera_path"),
1390
+ camera_position: optStr(args, "camera_position"),
1391
+ camera_look_at: optStr(args, "camera_look_at"),
1392
+ fov: optNumberOrUndefined(args, "fov"),
1393
+ marks: args.marks === undefined ? undefined : optBoolean(args, "marks", false),
1394
+ max_marks: optNumberOrUndefined(args, "max_marks"),
1395
+ }));
1396
+ snap = await captureScene(client, preview);
1397
+ }
1398
+ else {
1399
+ snap = await captureViewport(client);
1400
+ }
1401
+ return snapshotResult(snap, target);
1402
+ }),
1403
+ entry("summer_camera_bookmark", "Save, list, or delete named camera viewpoints (res://.summer/camera_bookmarks.json) for pose-stable screenshots", true, async (args, ctx) => {
1404
+ const action = str(args, "action");
1405
+ if (!CAMERA_BOOKMARK_ACTIONS.includes(action)) {
1406
+ throw new ToolDispatchError(`action must be one of ${CAMERA_BOOKMARK_ACTIONS.join(", ")}`);
1407
+ }
1408
+ const op = buildOrRefuse(() => buildCameraBookmarkOp({
1409
+ action: action,
1410
+ name: optStr(args, "name"),
1411
+ position: optStr(args, "position"),
1412
+ look_at: optStr(args, "look_at"),
1413
+ fov: optNumberOrUndefined(args, "fov"),
1414
+ }));
1415
+ const kind = String(op.op);
1416
+ const client = await ctx.engine();
1417
+ const missing = missingEngineOpResult(client, kind, CAMERA_BOOKMARK_FALLBACK);
1418
+ if (missing)
1419
+ refuseMissingOp(missing);
1420
+ return requireSupportedOp(await client.executeOps([op]), kind, CAMERA_BOOKMARK_FALLBACK);
1421
+ }),
1422
+ // --- library (the runtime librarian; engine-free) ---
1423
+ entry("summer_search_library", "Search the Summer library by describing the task in plain words", false, async (args) => runSearchLibrary(parseToolArgs(searchLibraryInputSchema, args, "search-library"))),
1424
+ entry("summer_read_library", "Load one library entry by id — body, metadata, how to call or install it", false, async (args) => {
1425
+ const parsed = parseToolArgs(readLibraryInputSchema, args, "read-library");
1426
+ const result = await readLibraryEntry(parsed.id, parsed.part ?? "all");
1427
+ // Same shape on both faces: the MCP face returns the not_found result as
1428
+ // an error payload; here it prints whole (JSON, exit 1) via ToolResultError.
1429
+ if (!result.ok)
1430
+ throw new ToolResultError(result, result.hint);
1431
+ return result;
1432
+ }),
1433
+ // --- feedback ---
1434
+ entry("summer_library_feedback", "Report library entry outcomes so Summer can fix and re-rank them", false, async (args) => {
1435
+ if (!Array.isArray(args.reports) || args.reports.length === 0) {
1436
+ throw new ToolDispatchError("reports must be a non-empty array of outcome reports");
1437
+ }
1438
+ const engineVersion = str(args, "engine_version");
1439
+ return sendLibraryFeedback({
1440
+ reports: args.reports,
1441
+ engine_version: engineVersion,
1442
+ // Self-reported model id; "unknown" is the documented CLI default when
1443
+ // the calling agent does not identify itself.
1444
+ agent_model: optStr(args, "agent_model") ?? "unknown",
1445
+ });
1446
+ }),
1447
+ ];
1448
+ // ---------------------------------------------------------------------------
1449
+ // Lookup API
1450
+ // ---------------------------------------------------------------------------
1451
+ const BY_KEY = new Map();
1452
+ for (const dispatchEntry of TOOL_DISPATCH) {
1453
+ for (const key of [dispatchEntry.slug, dispatchEntry.name]) {
1454
+ if (BY_KEY.has(key)) {
1455
+ throw new Error(`Duplicate tool dispatch key: ${key}`);
1456
+ }
1457
+ BY_KEY.set(key, dispatchEntry);
1458
+ }
1459
+ }
1460
+ export function listToolDispatches() {
1461
+ return TOOL_DISPATCH;
1462
+ }
1463
+ /** Resolve by library slug ("add-node"), MCP name ("summer_add_node"), or the
1464
+ * underscore-less mixed forms users type ("add_node", "summer-add-node"). */
1465
+ export function resolveToolDispatch(nameOrSlug) {
1466
+ const raw = nameOrSlug.trim();
1467
+ const candidates = [
1468
+ raw,
1469
+ raw.replace(/_/g, "-"),
1470
+ raw.replace(/-/g, "_"),
1471
+ raw.replace(/^summer[-_]/, "").replace(/_/g, "-"),
1472
+ raw.replace(/^tool\//, ""),
1473
+ ];
1474
+ for (const candidate of candidates) {
1475
+ const hit = BY_KEY.get(candidate);
1476
+ if (hit)
1477
+ return hit;
1478
+ }
1479
+ return null;
1480
+ }
1481
+ export async function dispatchTool(nameOrSlug, args, ctx = createDefaultDispatchContext()) {
1482
+ const dispatchEntry = resolveToolDispatch(nameOrSlug);
1483
+ if (!dispatchEntry) {
1484
+ throw new ToolDispatchError(`Unknown tool "${nameOrSlug}". Run 'summer tool --list' to see all ${TOOL_DISPATCH.length} tools.`);
1485
+ }
1486
+ // The one gate every result passes, mirroring withEngine on the MCP face:
1487
+ // whatever the MCP face would mark isError is a ToolResultError here, so the
1488
+ // CLI exit code says what the receipt says — for every tool, not per handler.
1489
+ return requireEngineSuccess(await dispatchEntry.handler(args, ctx));
1490
+ }