summer-engine 3.7.0 → 4.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 (501) hide show
  1. package/.claude-plugin/marketplace.json +2 -13
  2. package/.claude-plugin/plugin.json +25 -22
  3. package/.codex-plugin/plugin.json +8 -14
  4. package/.cursor-plugin/plugin.json +7 -13
  5. package/.factory-plugin/plugin.json +7 -13
  6. package/AGENTS.md +19 -12
  7. package/CHANGELOG.md +112 -0
  8. package/GEMINI.md +3 -3
  9. package/README.md +111 -31
  10. package/assets/icon.png +0 -0
  11. package/commands/start.md +71 -0
  12. package/commands/summer.md +5 -3
  13. package/dist/cli/banner.js +3 -0
  14. package/dist/cli/commands/login.d.ts +27 -0
  15. package/dist/cli/commands/login.js +58 -12
  16. package/dist/cli/commands/ops.d.ts +47 -0
  17. package/dist/cli/commands/ops.js +301 -0
  18. package/dist/cli/commands/setup.d.ts +2 -0
  19. package/dist/cli/commands/setup.js +37 -12
  20. package/dist/cli/commands/skills.js +11 -7
  21. package/dist/cli/index.js +7 -0
  22. package/dist/core/agent-host.d.ts +65 -0
  23. package/dist/core/agent-host.js +193 -0
  24. package/dist/core/capabilities/agent-playbook.d.ts +3 -0
  25. package/dist/core/capabilities/agent-playbook.js +54 -2
  26. package/dist/core/capabilities/apps.d.ts +341 -0
  27. package/dist/core/capabilities/apps.js +353 -0
  28. package/dist/core/capabilities/doctor.d.ts +3 -0
  29. package/dist/core/capabilities/doctor.js +20 -15
  30. package/dist/core/capabilities/enable-plugin.d.ts +27 -0
  31. package/dist/core/capabilities/enable-plugin.js +75 -0
  32. package/dist/core/capabilities/fabricate-mesh.d.ts +2 -2
  33. package/dist/core/capabilities/game-task-plan.d.ts +12 -6
  34. package/dist/core/capabilities/game-task-plan.js +80 -5
  35. package/dist/core/capabilities/motion-generation.js +2 -2
  36. package/dist/core/capabilities/navigation/open.d.ts +6 -2
  37. package/dist/core/capabilities/navigation/open.js +3 -0
  38. package/dist/core/capabilities/publish-build.js +2 -0
  39. package/dist/core/capabilities/runtime-control.d.ts +4 -4
  40. package/dist/core/capabilities/scene-script-target.d.ts +17 -0
  41. package/dist/core/capabilities/scene-script-target.js +70 -0
  42. package/dist/core/capabilities/sfx-choices.d.ts +4 -0
  43. package/dist/core/capabilities/sfx-choices.js +13 -6
  44. package/dist/core/capabilities/tool-dispatch.d.ts +12 -3
  45. package/dist/core/capabilities/tool-dispatch.js +130 -46
  46. package/dist/core/capability-skew.js +4 -0
  47. package/dist/core/embeddings.d.ts +2 -0
  48. package/dist/core/embeddings.js +1 -1
  49. package/dist/core/error-codes.d.ts +62 -0
  50. package/dist/core/error-codes.js +97 -0
  51. package/dist/core/format.d.ts +2 -0
  52. package/dist/core/format.js +2 -1
  53. package/dist/core/hosted-mcp.d.ts +22 -0
  54. package/dist/core/hosted-mcp.js +56 -1
  55. package/dist/core/library-read.js +5 -0
  56. package/dist/core/library-search.js +2 -1
  57. package/dist/core/oauth.js +4 -0
  58. package/dist/core/ops/catalog.d.ts +41 -0
  59. package/dist/core/ops/catalog.js +145 -0
  60. package/dist/core/ops/cloud.d.ts +56 -0
  61. package/dist/core/ops/cloud.js +175 -0
  62. package/dist/core/ops/contract.d.ts +119 -0
  63. package/dist/core/ops/contract.js +142 -0
  64. package/dist/core/ops/engine-answer.d.ts +25 -0
  65. package/dist/core/ops/engine-answer.js +117 -0
  66. package/dist/core/ops/engine.d.ts +55 -0
  67. package/dist/core/ops/engine.js +649 -0
  68. package/dist/core/ops/save-files.d.ts +78 -0
  69. package/dist/core/ops/save-files.js +419 -0
  70. package/dist/core/ops/search.d.ts +9 -0
  71. package/dist/core/ops/search.js +55 -0
  72. package/dist/core/skills-registry.d.ts +16 -1
  73. package/dist/core/skills-registry.js +35 -2
  74. package/dist/core/telemetry.d.ts +32 -0
  75. package/dist/core/telemetry.js +38 -10
  76. package/dist/core/tool-profiles.d.ts +25 -0
  77. package/dist/core/tool-profiles.js +41 -0
  78. package/dist/installer/agent-config.d.ts +17 -4
  79. package/dist/installer/agent-config.js +22 -6
  80. package/dist/installer/agent-table.d.ts +3 -0
  81. package/dist/installer/agent-table.js +10 -7
  82. package/dist/installer/setup.d.ts +2 -0
  83. package/dist/installer/setup.js +9 -4
  84. package/dist/installer/version-check.d.ts +29 -9
  85. package/dist/installer/version-check.js +81 -28
  86. package/dist/mcp/command.js +7 -0
  87. package/dist/mcp/hosted-mount.d.ts +27 -2
  88. package/dist/mcp/hosted-mount.js +66 -24
  89. package/dist/mcp/legacy-toolset.d.ts +27 -0
  90. package/dist/mcp/legacy-toolset.js +43 -0
  91. package/dist/mcp/ops-surface.d.ts +59 -0
  92. package/dist/mcp/ops-surface.js +443 -0
  93. package/dist/mcp/server.d.ts +35 -11
  94. package/dist/mcp/server.js +236 -89
  95. package/dist/mcp/tool-results.d.ts +26 -0
  96. package/dist/mcp/tool-results.js +81 -0
  97. package/dist/mcp/tool-surface.d.ts +65 -0
  98. package/dist/mcp/tool-surface.js +317 -0
  99. package/dist/mcp/tools/app-tools.d.ts +2 -0
  100. package/dist/mcp/tools/app-tools.js +71 -0
  101. package/dist/mcp/tools/asset-tools.js +55 -53
  102. package/dist/mcp/tools/audit-tools.js +21 -21
  103. package/dist/mcp/tools/debug-tools.js +6 -6
  104. package/dist/mcp/tools/fabricate-tools.js +6 -6
  105. package/dist/mcp/tools/generate-tools.js +76 -70
  106. package/dist/mcp/tools/project-tools.d.ts +9 -4
  107. package/dist/mcp/tools/project-tools.js +16 -10
  108. package/dist/mcp/tools/scene-tools.js +12 -23
  109. package/dist/mcp/tools/script-tools.js +9 -5
  110. package/dist/mcp/tools/with-engine.d.ts +6 -0
  111. package/dist/mcp/tools/with-engine.js +19 -0
  112. package/gemini-extension.json +6 -12
  113. package/hooks/pre-commit-doctor.sh +5 -4
  114. package/hooks/session-start.sh +24 -28
  115. package/integrations/claude-desktop/README.md +1 -1
  116. package/integrations/copilot-jetbrains/README.md +1 -1
  117. package/integrations/junie/README.md +1 -1
  118. package/integrations/lm-studio/README.md +1 -1
  119. package/integrations/trae/README.md +1 -1
  120. package/integrations/visual-studio/README.md +1 -1
  121. package/library/references/mcp-tools-reference/mcp-tools-reference.md +85 -35
  122. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  123. package/library/references/product-map/resource.yaml +2 -2
  124. package/library/skills/3d-lighting/SKILL.md +53 -28
  125. package/library/skills/3d-lighting/resource.yaml +1 -1
  126. package/library/skills/README.md +126 -116
  127. package/library/skills/adaptive-music/SKILL.md +19 -23
  128. package/library/skills/adaptive-music/resource.yaml +1 -1
  129. package/library/skills/agent-playtesting/SKILL.md +1 -1
  130. package/library/skills/agent-playtesting/resource.yaml +1 -1
  131. package/library/skills/ai-characters-in-games/SKILL.md +1 -1
  132. package/library/skills/ai-characters-in-games/resource.yaml +1 -1
  133. package/library/skills/ambient-bed/SKILL.md +22 -13
  134. package/library/skills/ambient-bed/resource.yaml +1 -1
  135. package/library/skills/animated-loop/SKILL.md +41 -32
  136. package/library/skills/animated-loop/resource.yaml +1 -1
  137. package/library/skills/animation-tree/SKILL.md +1 -1
  138. package/library/skills/animation-tree/resource.yaml +1 -1
  139. package/library/skills/art-direction/SKILL.md +3 -3
  140. package/library/skills/art-direction/resource.yaml +1 -1
  141. package/library/skills/asset-strategy/SKILL.md +21 -4
  142. package/library/skills/asset-strategy/resource.yaml +2 -1
  143. package/library/skills/audio-direction/SKILL.md +10 -10
  144. package/library/skills/audio-direction/resource.yaml +1 -1
  145. package/library/skills/auto-fire-targeting/SKILL.md +1 -1
  146. package/library/skills/auto-fire-targeting/resource.yaml +1 -1
  147. package/library/skills/bounded-engine-runs/SKILL.md +1 -1
  148. package/library/skills/bounded-engine-runs/resource.yaml +1 -1
  149. package/library/skills/brainstorm-game/SKILL.md +1 -1
  150. package/library/skills/brainstorm-game/resource.yaml +1 -1
  151. package/library/skills/brainstorming/SKILL.md +1 -1
  152. package/library/skills/brainstorming/resource.yaml +1 -1
  153. package/library/skills/browse-templates/SKILL.md +1 -1
  154. package/library/skills/browse-templates/resource.yaml +1 -1
  155. package/library/skills/camera-rigs/SKILL.md +2 -2
  156. package/library/skills/camera-rigs/resource.yaml +1 -1
  157. package/library/skills/celeste-momentum-platforming/resource.yaml +1 -0
  158. package/library/skills/character-animation-wiring/SKILL.md +4 -4
  159. package/library/skills/character-animation-wiring/resource.yaml +1 -1
  160. package/library/skills/character-model/SKILL.md +55 -47
  161. package/library/skills/character-model/resource.yaml +1 -1
  162. package/library/skills/character-movement/SKILL.md +3 -3
  163. package/library/skills/character-movement/resource.yaml +1 -1
  164. package/library/skills/character-portrait/SKILL.md +24 -19
  165. package/library/skills/character-portrait/resource.yaml +1 -1
  166. package/library/skills/cinematic-cutscene/SKILL.md +58 -46
  167. package/library/skills/cinematic-cutscene/resource.yaml +1 -1
  168. package/library/skills/combat-basics/SKILL.md +1 -1
  169. package/library/skills/combat-basics/resource.yaml +1 -1
  170. package/library/skills/compatibility-renderer-traps/SKILL.md +1 -1
  171. package/library/skills/compatibility-renderer-traps/resource.yaml +1 -1
  172. package/library/skills/concept-art/SKILL.md +16 -12
  173. package/library/skills/concept-art/resource.yaml +1 -1
  174. package/library/skills/concept-to-level/SKILL.md +239 -0
  175. package/library/skills/concept-to-level/references/technical-rules.md +150 -0
  176. package/library/skills/concept-to-level/resource.yaml +44 -0
  177. package/library/skills/create-asset-sheet/SKILL.md +19 -13
  178. package/library/skills/create-asset-sheet/resource.yaml +1 -1
  179. package/library/skills/debug/SKILL.md +2 -2
  180. package/library/skills/debug/resource.yaml +1 -1
  181. package/library/skills/debugging-game-feel/SKILL.md +1 -1
  182. package/library/skills/debugging-game-feel/resource.yaml +1 -1
  183. package/library/skills/design-level/SKILL.md +23 -35
  184. package/library/skills/design-level/resource.yaml +1 -1
  185. package/library/skills/design-mechanic/SKILL.md +1 -1
  186. package/library/skills/design-mechanic/resource.yaml +1 -1
  187. package/library/skills/design-npc/SKILL.md +22 -24
  188. package/library/skills/design-npc/resource.yaml +1 -1
  189. package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
  190. package/library/skills/diagnosing-perf-regressions/resource.yaml +1 -1
  191. package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
  192. package/library/skills/driving-the-editor-ui/resource.yaml +1 -1
  193. package/library/skills/environment-kit/SKILL.md +20 -16
  194. package/library/skills/environment-kit/resource.yaml +1 -1
  195. package/library/skills/export-and-ship/SKILL.md +6 -6
  196. package/library/skills/export-and-ship/resource.yaml +1 -1
  197. package/library/skills/fabricating-assets/SKILL.md +7 -7
  198. package/library/skills/fabricating-assets/resource.yaml +3 -3
  199. package/library/skills/facial-and-lipsync/SKILL.md +14 -15
  200. package/library/skills/facial-and-lipsync/resource.yaml +1 -1
  201. package/library/skills/fps-controller/SKILL.md +33 -41
  202. package/library/skills/fps-controller/resource.yaml +1 -1
  203. package/library/skills/game-feel/SKILL.md +8 -8
  204. package/library/skills/game-feel/resource.yaml +1 -1
  205. package/library/skills/gameskill/SKILL.md +4 -4
  206. package/library/skills/gameskill/resource.yaml +1 -1
  207. package/library/skills/gdscript-patterns/SKILL.md +1 -1
  208. package/library/skills/gdscript-patterns/resource.yaml +1 -1
  209. package/library/skills/generate-motion/SKILL.md +30 -21
  210. package/library/skills/generate-motion/resource.yaml +1 -1
  211. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
  212. package/library/skills/godogen-visual-proof-game-generation/resource.yaml +1 -0
  213. package/library/skills/grow-analytics/SKILL.md +3 -3
  214. package/library/skills/grow-analytics/resource.yaml +1 -1
  215. package/library/skills/headless-scripting/SKILL.md +1 -1
  216. package/library/skills/headless-scripting/resource.yaml +1 -1
  217. package/library/skills/in-game-purchases/SKILL.md +1 -1
  218. package/library/skills/in-game-purchases/resource.yaml +1 -1
  219. package/library/skills/instantiate-asset-pack/SKILL.md +2 -2
  220. package/library/skills/instantiate-asset-pack/resource.yaml +1 -1
  221. package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +1 -0
  222. package/library/skills/make-game/SKILL.md +4 -4
  223. package/library/skills/make-game/references/2d-platformer.md +1 -1
  224. package/library/skills/make-game/references/3d-fps.md +1 -1
  225. package/library/skills/make-game/references/3d-horror.md +1 -1
  226. package/library/skills/make-game/references/3d-third-person.md +2 -2
  227. package/library/skills/make-game/resource.yaml +1 -1
  228. package/library/skills/multiplayer/SKILL.md +1 -1
  229. package/library/skills/multiplayer/resource.yaml +1 -1
  230. package/library/skills/multiplayer-movement/SKILL.md +1 -1
  231. package/library/skills/multiplayer-movement/resource.yaml +1 -1
  232. package/library/skills/multiplayer-project/SKILL.md +1 -1
  233. package/library/skills/multiplayer-project/resource.yaml +1 -1
  234. package/library/skills/multiplayer-publish/SKILL.md +8 -7
  235. package/library/skills/multiplayer-publish/resource.yaml +3 -3
  236. package/library/skills/multiplayer-state/SKILL.md +1 -1
  237. package/library/skills/multiplayer-state/resource.yaml +1 -1
  238. package/library/skills/multiplayer-testing/SKILL.md +1 -1
  239. package/library/skills/multiplayer-testing/resource.yaml +1 -1
  240. package/library/skills/music-track/SKILL.md +22 -13
  241. package/library/skills/music-track/resource.yaml +1 -1
  242. package/library/skills/navigate-summer/SKILL.md +11 -9
  243. package/library/skills/navigate-summer/resource.yaml +1 -1
  244. package/library/skills/new-project/SKILL.md +1 -1
  245. package/library/skills/new-project/resource.yaml +1 -1
  246. package/library/skills/organic-model/SKILL.md +24 -18
  247. package/library/skills/organic-model/resource.yaml +1 -1
  248. package/library/skills/pixel-art/SKILL.md +27 -23
  249. package/library/skills/pixel-art/resource.yaml +1 -1
  250. package/library/skills/play/SKILL.md +1 -1
  251. package/library/skills/play/resource.yaml +1 -1
  252. package/library/skills/playtesting-a-feature/SKILL.md +1 -1
  253. package/library/skills/playtesting-a-feature/resource.yaml +1 -1
  254. package/library/skills/procedural-animation/SKILL.md +20 -16
  255. package/library/skills/procedural-animation/resource.yaml +1 -1
  256. package/library/skills/prop-model/SKILL.md +25 -19
  257. package/library/skills/prop-model/resource.yaml +1 -1
  258. package/library/skills/psx-retro-rendering/resource.yaml +1 -0
  259. package/library/skills/publish-your-game/SKILL.md +16 -6
  260. package/library/skills/publish-your-game/resource.yaml +3 -3
  261. package/library/skills/realtime-wet-surfaces/resource.yaml +1 -0
  262. package/library/skills/release-readiness/SKILL.md +1 -1
  263. package/library/skills/release-readiness/resource.yaml +1 -1
  264. package/library/skills/remote-deploy/SKILL.md +1 -1
  265. package/library/skills/remote-deploy/resource.yaml +1 -1
  266. package/library/skills/retarget/SKILL.md +23 -20
  267. package/library/skills/retarget/resource.yaml +1 -1
  268. package/library/skills/running-in-the-cloud/SKILL.md +1 -1
  269. package/library/skills/running-in-the-cloud/resource.yaml +1 -1
  270. package/library/skills/save-load/SKILL.md +1 -1
  271. package/library/skills/save-load/resource.yaml +1 -1
  272. package/library/skills/scene-composition/SKILL.md +1 -1
  273. package/library/skills/scene-composition/resource.yaml +1 -1
  274. package/library/skills/scene-hierarchy-design/SKILL.md +2 -2
  275. package/library/skills/scene-hierarchy-design/resource.yaml +1 -1
  276. package/library/skills/scene-scripting/SKILL.md +66 -636
  277. package/library/skills/scene-scripting/references/ctx-api.md +184 -0
  278. package/library/skills/scene-scripting/references/recipes-2d-ui.md +193 -0
  279. package/library/skills/scene-scripting/references/recipes-3d.md +280 -0
  280. package/library/skills/scene-scripting/resource.yaml +4 -4
  281. package/library/skills/scene-to-level/SKILL.md +2 -2
  282. package/library/skills/scene-to-level/resource.yaml +1 -1
  283. package/library/skills/skill-create/resource.yaml +1 -0
  284. package/library/skills/skill-improve/resource.yaml +1 -0
  285. package/library/skills/skill-test/SKILL.md +2 -2
  286. package/library/skills/skill-test/resource.yaml +1 -0
  287. package/library/skills/skintokens-auto-rigging/resource.yaml +1 -0
  288. package/library/skills/skybox-panorama/SKILL.md +27 -21
  289. package/library/skills/skybox-panorama/resource.yaml +1 -1
  290. package/library/skills/sound-effect/SKILL.md +31 -20
  291. package/library/skills/sound-effect/resource.yaml +1 -1
  292. package/library/skills/spatial-placement/SKILL.md +14 -9
  293. package/library/skills/spatial-placement/resource.yaml +1 -1
  294. package/library/skills/sprite-sheet/SKILL.md +32 -27
  295. package/library/skills/sprite-sheet/resource.yaml +1 -1
  296. package/library/skills/store-art/SKILL.md +3 -3
  297. package/library/skills/store-art/resource.yaml +2 -2
  298. package/library/skills/store-listing/SKILL.md +3 -3
  299. package/library/skills/store-listing/resource.yaml +2 -2
  300. package/library/skills/summer-analytics/SKILL.md +7 -5
  301. package/library/skills/summer-analytics/resource.yaml +2 -2
  302. package/library/skills/summer-apps/SKILL.md +168 -0
  303. package/library/skills/summer-apps/manifest.md +57 -0
  304. package/library/skills/summer-apps/minimal-app.md +183 -0
  305. package/library/skills/summer-apps/resource.yaml +36 -0
  306. package/library/skills/summer-apps/sdk-reference.md +135 -0
  307. package/library/skills/summer-apps-design/SKILL.md +197 -0
  308. package/library/skills/summer-apps-design/resource.yaml +23 -0
  309. package/library/skills/summer-assets/SKILL.md +56 -0
  310. package/library/skills/summer-assets/resource.yaml +34 -0
  311. package/library/skills/summer-store/SKILL.md +1 -1
  312. package/library/skills/text-to-motion/SKILL.md +30 -23
  313. package/library/skills/text-to-motion/prompts-and-controls.md +5 -5
  314. package/library/skills/text-to-motion/resource.yaml +1 -1
  315. package/library/skills/text-to-motion/rigging-for-text-motion.md +2 -2
  316. package/library/skills/tileable-texture/SKILL.md +19 -15
  317. package/library/skills/tileable-texture/resource.yaml +1 -1
  318. package/library/skills/trailer-shot/SKILL.md +28 -24
  319. package/library/skills/trailer-shot/resource.yaml +1 -1
  320. package/library/skills/tune-performance/SKILL.md +6 -6
  321. package/library/skills/tune-performance/resource.yaml +1 -1
  322. package/library/skills/ui-basics/SKILL.md +41 -36
  323. package/library/skills/ui-basics/resource.yaml +1 -1
  324. package/library/skills/ui-graphics/SKILL.md +47 -38
  325. package/library/skills/ui-graphics/resource.yaml +1 -1
  326. package/library/skills/use-widget-asset/SKILL.md +1 -1
  327. package/library/skills/use-widget-asset/resource.yaml +1 -1
  328. package/library/skills/using-summer/SKILL.md +7 -5
  329. package/library/skills/using-summer/resource.yaml +1 -1
  330. package/library/skills/vehicle-model/SKILL.md +65 -57
  331. package/library/skills/vehicle-model/resource.yaml +1 -1
  332. package/library/skills/verification-before-completion/SKILL.md +1 -1
  333. package/library/skills/verification-before-completion/resource.yaml +1 -1
  334. package/library/skills/verifying-scenes/SKILL.md +2 -2
  335. package/library/skills/verifying-scenes/resource.yaml +1 -1
  336. package/library/skills/vfx-dissolve/SKILL.md +2 -2
  337. package/library/skills/vfx-dissolve/resource.yaml +1 -1
  338. package/library/skills/vfx-fire/SKILL.md +15 -41
  339. package/library/skills/vfx-fire/resource.yaml +1 -1
  340. package/library/skills/vfx-hit-spark/SKILL.md +1 -1
  341. package/library/skills/vfx-hit-spark/resource.yaml +1 -1
  342. package/library/skills/vfx-lightning/SKILL.md +7 -16
  343. package/library/skills/vfx-lightning/resource.yaml +1 -1
  344. package/library/skills/vfx-magic-glow/SKILL.md +23 -32
  345. package/library/skills/vfx-magic-glow/resource.yaml +1 -1
  346. package/library/skills/vfx-muzzle-flash/SKILL.md +20 -35
  347. package/library/skills/vfx-muzzle-flash/resource.yaml +1 -1
  348. package/library/skills/vfx-smoke/SKILL.md +13 -31
  349. package/library/skills/vfx-smoke/resource.yaml +1 -1
  350. package/library/skills/vfx-water-ripple/SKILL.md +12 -31
  351. package/library/skills/vfx-water-ripple/resource.yaml +1 -1
  352. package/library/skills/voice-line/SKILL.md +71 -40
  353. package/library/skills/voice-line/resource.yaml +1 -1
  354. package/library/skills/web-and-touch-input/SKILL.md +1 -1
  355. package/library/skills/web-and-touch-input/resource.yaml +1 -1
  356. package/library/skills/web-compatibility-look/SKILL.md +1 -1
  357. package/library/skills/web-compatibility-look/resource.yaml +1 -1
  358. package/library/skills/world-building-3d/SKILL.md +1 -1
  359. package/library/skills/world-building-3d/resource.yaml +1 -1
  360. package/library/skills/writing-skills/resource.yaml +1 -0
  361. package/library/tools/add-node/GUIDE.md +14 -0
  362. package/library/tools/align-distribute-3d/GUIDE.md +7 -0
  363. package/library/tools/app-call/resource.yaml +59 -0
  364. package/library/tools/app-create/resource.yaml +67 -0
  365. package/library/tools/app-data/resource.yaml +74 -0
  366. package/library/tools/app-dock/resource.yaml +59 -0
  367. package/library/tools/app-events/resource.yaml +68 -0
  368. package/library/tools/app-get/resource.yaml +46 -0
  369. package/library/tools/app-guide/resource.yaml +55 -0
  370. package/library/tools/app-list/resource.yaml +48 -0
  371. package/library/tools/app-page/resource.yaml +74 -0
  372. package/library/tools/app-publish/resource.yaml +90 -0
  373. package/library/tools/app-report-gap/resource.yaml +58 -0
  374. package/library/tools/app-screenshot/resource.yaml +103 -0
  375. package/library/tools/app-tools/resource.yaml +46 -0
  376. package/library/tools/app-update/resource.yaml +68 -0
  377. package/library/tools/attach-to-surface/GUIDE.md +15 -0
  378. package/library/tools/batch/GUIDE.md +27 -0
  379. package/library/tools/camera-bookmark/GUIDE.md +12 -0
  380. package/library/tools/capture-gameplay/GUIDE.md +3 -0
  381. package/library/tools/capture-gameplay/resource.yaml +3 -3
  382. package/library/tools/check-job/resource.yaml +1 -1
  383. package/library/tools/clear-console/GUIDE.md +3 -0
  384. package/library/tools/connect-ports/GUIDE.md +9 -0
  385. package/library/tools/connect-signal/GUIDE.md +14 -0
  386. package/library/tools/create-debug-report/GUIDE.md +10 -0
  387. package/library/tools/create-scene/GUIDE.md +10 -0
  388. package/library/tools/debug-views/GUIDE.md +7 -0
  389. package/library/tools/enable-plugin/GUIDE.md +3 -0
  390. package/library/tools/enable-plugin/resource.yaml +53 -0
  391. package/library/tools/export-game/GUIDE.md +3 -0
  392. package/library/tools/export-game/resource.yaml +3 -3
  393. package/library/tools/export-templates/GUIDE.md +3 -0
  394. package/library/tools/fabricate-3d/GUIDE.md +19 -0
  395. package/library/tools/frame-nodes/GUIDE.md +9 -0
  396. package/library/tools/frame-shot/GUIDE.md +9 -0
  397. package/library/tools/game-control/GUIDE.md +9 -0
  398. package/library/tools/game-input/GUIDE.md +9 -0
  399. package/library/tools/game-probe/GUIDE.md +9 -0
  400. package/library/tools/generate-3d/resource.yaml +3 -3
  401. package/library/tools/generate-motion/resource.yaml +2 -2
  402. package/library/tools/get-console/GUIDE.md +9 -0
  403. package/library/tools/get-console/resource.yaml +5 -0
  404. package/library/tools/get-debugger-errors/GUIDE.md +7 -0
  405. package/library/tools/get-debugger-errors/resource.yaml +5 -0
  406. package/library/tools/get-debugger-warnings/GUIDE.md +7 -0
  407. package/library/tools/get-debugger-warnings/resource.yaml +5 -0
  408. package/library/tools/get-diagnostics/GUIDE.md +13 -0
  409. package/library/tools/get-project-context/GUIDE.md +25 -0
  410. package/library/tools/get-runtime-tree/GUIDE.md +7 -0
  411. package/library/tools/get-runtime-tree/resource.yaml +5 -0
  412. package/library/tools/get-scene-tree/GUIDE.md +11 -0
  413. package/library/tools/get-scene-tree/resource.yaml +5 -0
  414. package/library/tools/get-script-errors/GUIDE.md +5 -0
  415. package/library/tools/grep/GUIDE.md +11 -0
  416. package/library/tools/import-asset/GUIDE.md +8 -0
  417. package/library/tools/import-asset-by-id/GUIDE.md +12 -0
  418. package/library/tools/import-from-url/GUIDE.md +10 -0
  419. package/library/tools/import-from-url-batch/GUIDE.md +3 -0
  420. package/library/tools/import-hdri/GUIDE.md +16 -0
  421. package/library/tools/input-map-bind/GUIDE.md +11 -0
  422. package/library/tools/inspect-asset/GUIDE.md +14 -0
  423. package/library/tools/inspect-asset/resource.yaml +5 -0
  424. package/library/tools/inspect-node/GUIDE.md +11 -0
  425. package/library/tools/inspect-node/resource.yaml +5 -0
  426. package/library/tools/inspect-resource/GUIDE.md +7 -0
  427. package/library/tools/inspect-resource/resource.yaml +5 -0
  428. package/library/tools/inspect-runtime-node/GUIDE.md +7 -0
  429. package/library/tools/inspect-runtime-node/resource.yaml +5 -0
  430. package/library/tools/instantiate-scene/GUIDE.md +12 -0
  431. package/library/tools/is-running/GUIDE.md +3 -0
  432. package/library/tools/measure/GUIDE.md +8 -0
  433. package/library/tools/navigation-probe/GUIDE.md +9 -0
  434. package/library/tools/open/resource.yaml +2 -2
  435. package/library/tools/open-main-scene/GUIDE.md +6 -0
  436. package/library/tools/open-scene/GUIDE.md +8 -0
  437. package/library/tools/place-adjacent/GUIDE.md +7 -0
  438. package/library/tools/play/GUIDE.md +13 -0
  439. package/library/tools/project-setting/GUIDE.md +9 -0
  440. package/library/tools/publish-build/resource.yaml +6 -6
  441. package/library/tools/raycast/GUIDE.md +7 -0
  442. package/library/tools/read-file/GUIDE.md +10 -0
  443. package/library/tools/read-library/resource.yaml +5 -0
  444. package/library/tools/recent-events/GUIDE.md +7 -0
  445. package/library/tools/remove-node/GUIDE.md +3 -0
  446. package/library/tools/repeat-along/GUIDE.md +5 -0
  447. package/library/tools/replace-node/GUIDE.md +9 -0
  448. package/library/tools/replace-text/GUIDE.md +5 -0
  449. package/library/tools/run-editor-script/GUIDE.md +16 -0
  450. package/library/tools/run-script/GUIDE.md +30 -0
  451. package/library/tools/run-script/resource.yaml +6 -3
  452. package/library/tools/runtime-animate/GUIDE.md +7 -0
  453. package/library/tools/runtime-call/GUIDE.md +7 -0
  454. package/library/tools/runtime-set/GUIDE.md +9 -0
  455. package/library/tools/runtime-spawn/GUIDE.md +9 -0
  456. package/library/tools/save-scene/GUIDE.md +3 -0
  457. package/library/tools/scene-audit/GUIDE.md +29 -0
  458. package/library/tools/screenshot/GUIDE.md +17 -0
  459. package/library/tools/select-node/GUIDE.md +3 -0
  460. package/library/tools/set-prop/GUIDE.md +23 -0
  461. package/library/tools/set-resource-property/GUIDE.md +8 -0
  462. package/library/tools/shot-sheet/GUIDE.md +7 -0
  463. package/library/tools/snap-to-surface/GUIDE.md +17 -0
  464. package/library/tools/snapshot-diff/GUIDE.md +7 -0
  465. package/library/tools/starcast/GUIDE.md +12 -0
  466. package/library/tools/stop/GUIDE.md +3 -0
  467. package/library/tools/test-placement/GUIDE.md +7 -0
  468. package/library/tools/ui-actions/GUIDE.md +12 -0
  469. package/library/tools/ui-activate/GUIDE.md +11 -0
  470. package/library/tools/ui-screenshot/GUIDE.md +9 -0
  471. package/library/tools/ui-tree/GUIDE.md +10 -0
  472. package/library/tools/wait-for-event/GUIDE.md +11 -0
  473. package/library/tools/world-snapshot/GUIDE.md +9 -0
  474. package/library/tools/write-file/GUIDE.md +5 -0
  475. package/library/tools/zoom/GUIDE.md +5 -0
  476. package/package.json +6 -4
  477. package/registry/generated/aliases.json +15 -0
  478. package/registry/generated/counts.json +3 -3
  479. package/registry/generated/gemini-extension.json +6 -12
  480. package/registry/generated/index.json +1155 -408
  481. package/registry/generated/marketplace.claude.json +2 -13
  482. package/registry/generated/plugin.claude.json +25 -22
  483. package/registry/generated/plugin.codex.json +8 -14
  484. package/registry/generated/plugin.cursor.json +7 -13
  485. package/registry/generated/plugin.factory.json +7 -13
  486. package/registry/generated/server.json +34 -0
  487. package/registry/generated/skills-index.md +126 -116
  488. package/registry/generated/skills-registry.json +178 -113
  489. package/registry/operations/fixtures/invalid-deprecated-without-replacement.json +60 -0
  490. package/registry/operations/fixtures/invalid-name.json +60 -0
  491. package/registry/operations/fixtures/invalid-publish-without-owner.json +82 -0
  492. package/registry/operations/fixtures/invalid-spend-without-estimate.json +42 -0
  493. package/registry/operations/fixtures/invalid-unknown-field.json +61 -0
  494. package/registry/operations/fixtures/valid-player-facing.json +82 -0
  495. package/registry/operations/fixtures/valid-read.json +60 -0
  496. package/registry/operations/fixtures/valid-spend.json +43 -0
  497. package/registry/operations/legacy-names.json +1206 -0
  498. package/registry/schemas/allowed-hosts.json +4 -2
  499. package/registry/schemas/operation.schema.json +318 -0
  500. package/registry/schemas/skill.schema.json +4 -0
  501. package/CLAUDE.md +0 -7
@@ -1,41 +1,43 @@
1
1
  ---
2
2
  name: scene-scripting
3
- description: "One GDScript in the live editor (summer_run_script) builds scenes, 2D levels, HUDs and gameplay wiring instead of CRUD chains; verify with diff + screenshot."
3
+ description: "Build scenes, levels, HUDs and wiring with one editor GDScript (summer_run_script) instead of call chains."
4
4
  ---
5
5
 
6
6
  # Scene Scripting
7
7
 
8
8
  ## Overview
9
9
 
10
- `summer_run_script` executes a GDScript **inside the live editor, against the currently open scene**. One script replaces a chain of `summer_add_node` / `summer_set_prop` calls, and it can do what CRUD ops cannot: loops, randomness, math, procedural geometry, reading existing nodes before deciding.
10
+ `summer_run_script` runs one GDScript **inside the live editor, against the scene named by `scenePath`**. When that scene is not the edited one, it is opened first (a request of its own) and the person's tab comes back after the run; if it cannot be opened, the script does not run. One script replaces a chain of `summer_add_node` / `summer_set_prop` calls, and it does what single calls cannot: loops, randomness, math, procedural geometry, reading existing nodes before deciding.
11
11
 
12
- **The rule: 3 or more related ops, or ANY computed placement → write a script.** A single property tweak → `summer_set_prop`. A cold project-wide batch job → `summer_run_editor_script` (see below).
12
+ **The rule: 3 or more related scene edits, or ANY computed placement → one script.** A single property tweak → `summer_set_prop`. A cold project-wide batch job → `summer_run_editor_script` (below).
13
13
 
14
14
  ## The contract
15
15
 
16
- Your source is just the function — no `extends`, no `@tool`:
17
-
18
- ```gdscript
16
+ ```
17
+ summer_run_script(scenePath="res://levels/level_01.tscn", source="""
19
18
  func run(ctx):
20
19
  var root = ctx.get_scene_root()
21
20
  var node := MeshInstance3D.new()
22
21
  node.mesh = BoxMesh.new()
23
22
  root.add_child(node)
24
- ctx.set_owner_recursive(node) # REQUIRED — see owner rules
23
+ ctx.set_owner_recursive(node) # REQUIRED: see owner rules
25
24
  ctx.report("added", node.name)
25
+ ctx.save_scene()
26
+ """)
26
27
  ```
27
28
 
28
- - `ctx.get_scene_root()` — root node of the open scene.
29
- - `ctx.set_owner_recursive(node)` — stamp a created subtree with the scene-root owner.
30
- - `ctx.report(key, value)` — structured results back to you (`reports` in the result).
31
- - `print(...)` — captured into `output`.
29
+ - `scenePath` — the exact `res://` scene the script changes (from `summer_get_project_context` or the scene tree). Always pass it; without it the script runs against whichever tab happens to be open. The result's `scene_target` says whether the scene was opened and which tab came back.
30
+ - `source` — just the function: no `extends`, no `@tool` (missing ones are added and reported in `normalizations`).
31
+ - `ctx.get_scene_root()` — root node of that scene. `ctx.set_owner_recursive(node)` — stamp a created subtree with the scene-root owner. `ctx.report(key, value)` — structured results back (`reports`). `print(...)` — captured into `output`.
32
32
  - Values are real GDScript: `Vector3(0, 10, 0)`, `Color(1, 0, 0, 1)`. The quoted variant-string convention (`"Vector3(0, 10, 0)"`) belongs to `summer_set_prop`, not to script code.
33
- - Budget: `max_seconds` default 20, clamp 5–120. The script blocks the editor between frames — keep it fast; move heavy batch work to `summer_run_editor_script`. On newer engines the budget is a HARD deadline: overrun raises the script error `"Summer script budget exceeded (Ns)"` (result `budget_enforced: true`). Split the work into smaller scripts — never resubmit the same oversized one.
34
- - Transactions: newer engines wrap the run in ONE named undo action (`undo: "action"`, the default) and roll it back on a mid-script runtime error — the result then carries `rolled_back: true` and the scene is untouched. Pass `undo: "none"` for checkpoint-only behavior (partial mutations survive an error). Older engines ignore the param; treat any `errors` there as a possible half-applied mutation.
33
+ - `ctx` is untyped, so its results are untyped: write `var wall = ctx.add_mesh(...)`. `var wall := ctx.add_mesh(...)` does not compile ("Cannot infer the type").
34
+ - Saving: `ctx.save_scene()` writes the scene (after an ownership audit). Without it the change stays in the editor, unsaved.
35
+ - Budget: `max_seconds` default 20, clamp 5–120. The script blocks the editor between frames; on newer engines the budget is a HARD deadline (`"Summer script budget exceeded (Ns)"`, `budget_enforced: true`). Split the work into smaller scripts — never resubmit the same oversized one.
36
+ - Transactions: newer engines wrap the run in ONE named undo action (`undo: "action"`, the default) and roll it back on a mid-script runtime error — the result carries `rolled_back: true` and the scene is untouched. `undo: "none"` keeps partial mutations. Older engines ignore the param; treat any `errors` there as a possible half-applied mutation.
35
37
 
36
- ## The ctx stdlib (newer engines)
38
+ ## The core ctx API
37
39
 
38
- Creation helpers that **set the owner for you** (to the edited scene root) and return the created node — prefer them over the manual `new()` + `add_child` + `set_owner_recursive` steps. Frozen signatures:
40
+ Creation helpers **set the owner for you** and return the created node — prefer them over `new()` + `add_child` + `set_owner_recursive`. Frozen signatures:
39
41
 
40
42
  ```gdscript
41
43
  add_node(type: String, name: String, parent: Node = null, props: Dictionary = {}) -> Node
@@ -63,183 +65,18 @@ save_scene(path: String = "") -> bool # ownership audit first; false +
63
65
  frames_budget_exceeded() -> bool
64
66
  ```
65
67
 
66
- `props` dictionaries apply via `set()` per key; unknown keys are collected into a `prop_warnings` report entry, never silently dropped — read it. On an **older engine** a missing helper is a plain GDScript error (`Invalid call to method 'add_mesh'…`): fall back to the manual form, which works everywhere.
67
-
68
- ## The geometry & authoring stdlib (geometry tier)
69
-
70
- Newer engines extend ctx with a second tier: real geometry (booleans, lathe, sweep, terrain), mesh post-processing (smooth, decimate, UVs, mirror, collision), keyframe animation, and text shaders. Frozen signatures:
71
-
72
- ```gdscript
73
- boolean(a: Node, b: Node, op: String = "union") -> MeshInstance3D
74
- # op: union|difference|intersection. Manifold-exact CSG, baked to a plain
75
- # ArrayMesh — no live CSG nodes remain. a and b are CONSUMED (freed) by
76
- # default; pass keep_inputs: bool = true as the final param to keep them.
77
- extrude_polygon(points: PackedVector2Array, depth: float, name: String,
78
- parent: Node = null) -> MeshInstance3D # 2D footprint -> prism, baked
79
- lathe(points: PackedVector2Array, name: String, parent: Node = null,
80
- spin_degrees: float = 360.0, sides: int = 32) -> MeshInstance3D # revolve a profile, baked
81
- sweep(points: PackedVector2Array, path_points: PackedVector3Array,
82
- name: String, parent: Node = null) -> MeshInstance3D # profile swept along a 3D path, baked
83
- # add_mesh shapes extended: torus | text (props.text: String, TextMesh)
84
- set_smooth(node: Node, angle_deg: float = 30.0) -> bool # shade smooth by angle
85
- terrain(size: Vector2, height: float, seed: int, name: String,
86
- parent: Node = null, image_path: String = "") -> MeshInstance3D
87
- # noise heightfield (or heightmap image via image_path); adds
88
- # HeightMapShape3D collision under a StaticBody3D child automatically
89
- decimate(node: Node, ratio: float) -> bool # simplify mesh to ~ratio of triangles
90
- convex_collision(node: Node, decompose: bool = false) -> Node
91
- # single convex hull, or multi-shape convex decomposition when decompose;
92
- # ALL created bodies are owned — they survive the save
93
- uv_planar(node: Node, axis: String = "y", scale: float = 1.0) -> bool
94
- uv_box(node: Node, scale: float = 1.0) -> bool # make textures land on generated geometry
95
- mirror(node: Node, axis: String = "x") -> Node # mirrored duplicate
96
- animate(node: Node, property: String, keys: Array, anim_name: String = "",
97
- loop: bool = false, player: AnimationPlayer = null) -> AnimationPlayer
98
- # keys: [[time_s, value], ...]; gets-or-creates the AnimationPlayer and
99
- # library; position/rotation/scale get dedicated 3D tracks (rotation keys
100
- # are converted for you — never hand-build quaternion tracks)
101
- make_shader(code: String, params: Dictionary = {}) -> ShaderMaterial
102
- # builds Shader + ShaderMaterial from source and sets shader parameters;
103
- # compile errors come back VERBATIM in the result (the `make_shader_errors`
104
- # report entry, plus errors[]) — read them
105
- ```
106
-
107
- Conventions, same as the rest of the stdlib: creation helpers set the owner and return the created node; bad input produces a per-helper `report` entry plus a `null`/`false` return, never a crash; every baked mesh carries generated normals (and tangents where UVs exist). On an older engine these helpers are missing — the CSG-node fallback recipe below still works everywhere.
108
-
109
- ## The animation stdlib (animation tier)
110
-
111
- Character-animation USAGE on top of `animate()` — state machines, method tracks, bone poses, head tracking. Same conventions. Frozen signatures:
112
-
113
- ```gdscript
114
- animate(...) v2 — additive extensions, signature unchanged:
115
- # keys entries also accept {time, value, interpolation: "nearest"|"linear"|"cubic"}
116
- # property may target bones: "<skeleton_node>:<bone_name>/position|rotation|scale"
117
- # -> creates bone position_3d/rotation_3d/scale_3d tracks (path "Skeleton:bone")
118
- animate_method(node: Node, calls: Array, anim_name: String = "",
119
- loop: bool = false, player: AnimationPlayer = null) -> AnimationPlayer
120
- # calls: [[time_s, method_name, args_array], ...] -> one method-call track
121
- anim_state_machine(target: Node, spec: Dictionary,
122
- player: AnimationPlayer = null) -> AnimationTree
123
- # spec: { states: {name: clip_name}, transitions: [[from, to, {auto?: bool,
124
- # blend_s?: float}], ...], start: name }
125
- # get-or-create AnimationTree wired to the (found or given) player,
126
- # AnimationNodeStateMachine, active=true; unknown clip names -> report
127
- # entry listing the player's actual clips (never silent)
128
- bone_pose(skeleton: Node, bone: String, pose: Dictionary) -> bool
129
- # pose keys position/rotation/scale -> set_bone_pose_* on Skeleton3D;
130
- # unknown bone -> false + report listing bone names (capped 64)
131
- look_at_modifier(node: Node, target: Node, props: Dictionary = {}) -> Node
132
- # LookAtModifier3D under the skeleton/node, owned
133
- ```
134
-
135
- Blend shapes key through plain `animate()` with property `"blend_shapes/<name>"` (value tracks) — no separate helper. The end-to-end recipe (inspect an imported rig's real clips/bones, locomotion wiring, footstep method tracks, root motion, the raw-GDScript fallback for engines without these helpers) lives in `character-animation-wiring` — one hop, not duplicated here.
136
-
137
- ## The game-completeness stdlib (game-completeness tier: 2D, UI, gameplay code)
138
-
139
- Everything a script needs to produce a GAME, not just a 3D scene: 2D levels, Control trees, persisted signal connections, attached scripts, prefabs, project settings. Same conventions (owner-by-default, return the node, `prop_warnings`, structured report entries on failure — never silent). 2D helpers work in any edited scene (root may be `Node2D` or `Node`); Control helpers parent under the edited root unless a parent is given. Frozen signatures:
140
-
141
- ```gdscript
142
- # 2D
143
- add_sprite(texture_path: String, name: String, parent: Node = null,
144
- props: Dictionary = {}) -> Sprite2D # missing texture -> report + null
145
- add_animated_sprite(frames: Dictionary, name: String, parent: Node = null,
146
- autoplay: String = "") -> AnimatedSprite2D
147
- # frames: { anim_name: { frames: [texture_paths], fps: float, loop: bool } } -> SpriteFrames
148
- add_tilemap(tileset_path: String, name: String, parent: Node = null) -> TileMapLayer
149
- paint_tiles(layer: TileMapLayer, cells: Array, source_id: int,
150
- atlas_coords: Vector2i = Vector2i.ZERO) -> int # cells: [Vector2i]; returns count
151
- paint_rect(layer: TileMapLayer, rect: Rect2i, source_id: int,
152
- atlas_coords: Vector2i = Vector2i.ZERO) -> int
153
- add_body_2d(kind: String, name: String, parent: Node = null,
154
- shape: Dictionary = {}, props: Dictionary = {}) -> Node
155
- # kind: static|rigid|character|area ; shape: {type: rect|circle|capsule, size|radius|height}
156
- # -> body + owned CollisionShape2D child. Empty shape = a 32x32 rect (confessed in
157
- # prop_warnings); size may be a float (square); props try the body, then the shape
158
- add_camera_2d(position: Vector2, zoom: Vector2 = Vector2.ONE, make_current: bool = true,
159
- limits: Rect2i = Rect2i()) -> Camera2D # empty limits = unlimited
160
- # Camera2D has no saved "current" flag: make_current persists as `enabled` (+ a live
161
- # make_current()); make_current: false saves the camera disabled
162
- add_parallax(layers: Array, name: String, parent: Node = null) -> Node
163
- # layers: [{texture: path, motion_scale: Vector2, repeat: Vector2i}] -> one Parallax2D per
164
- # layer under one Node2D group; repeat -> repeat_size. Parallax2D is in the engine tree
165
- # (no ParallaxBackground fallback); a layer whose texture fails to load is skipped + reported
166
-
167
- # UI (Control)
168
- add_ui(kind: String, name: String, parent: Node = null, props: Dictionary = {}) -> Control
169
- # kind: label|button|panel|vbox|hbox|grid|margin|texture_rect|progress_bar|line_edit|
170
- # rich_text|color_rect|center ; panel -> PanelContainer (container semantics for HUD
171
- # layouts; a plain Panel is add_node("Panel", ...)). props.anchor: full_rect|center|
172
- # top_left|top_right|bottom_left|bottom_right|top_wide|bottom_wide (+ the other
173
- # Control::PRESET_* names) -> set_anchors_and_offsets_preset, applied BEFORE the other
174
- # props so an explicit size/position wins; props.text/custom_minimum_size/etc via set();
175
- # props.texture as a "res://" path is loaded for you. A Control parented under a Node2D
176
- # gets a prop_warning (it scrolls with the camera — HUDs go under add_canvas_layer)
177
- add_canvas_layer(name: String, layer: int = 1, parent: Node = null) -> CanvasLayer
178
- set_theme_overrides(control: Control, overrides: Dictionary) -> int
179
- # {font_size: int, font_color: Color, <theme_item>: value} -> add_theme_*_override; count applied
180
- # dispatch by value: Color or "#hex" -> color; int/float -> font_size when the key is/ends with
181
- # font_size, else constant; StyleBox/Font/Texture2D (or a "res://" path) -> style/font/icon.
182
- # An item the control's theme type does not define is stored but confessed in prop_warnings
183
- connect_signal(emitter: Node, signal_name: String, target: Node, method: String,
184
- binds: Array = []) -> bool # PERSIST flag so it saves in .tscn;
185
- # unknown signal/method -> report, false
186
- # NOT `connect` — a ctx method of that name would shadow Object.connect on the ctx object,
187
- # so nothing named `connect` is bound. Method gate: target.has_method, else its script
188
- # has_method, else the script SOURCE declares `func <method>(` (attached this run, not yet
189
- # compiled). Already connected -> true (+ already_connected in the report). Both ends must
190
- # be owned by the edited root for the .tscn to carry the connection (prop_warning otherwise)
191
-
192
- # Gameplay-code lane
193
- attach_script(node: Node, source: String, path: String = "") -> Script
194
- # compile-validate first (GDScriptLanguage::validate; parse errors -> `attach_script_errors`
195
- # report entry {node, path, errors: [{line, column, message}]}, null, nothing written);
196
- # path empty -> res://scripts/<SceneName>/<NodeName>.gd (dirs created; the root node's name
197
- # when the scene was never saved, prop_warning); writes the file, FS rescan, set_script.
198
- # An existing file at path IS overwritten; its previous sha is recorded in the report (the
199
- # pre-run checkpoint covers rollback). Gate: the script's native base (`extends`) must be an
200
- # ancestor of the node's class — else the file is written but NOT attached (report, null)
201
- make_prefab(node: Node, path: String, replace_with_instance: bool = true) -> Node
202
- # pack the subtree to res:// path as a PackedScene (owner fix-up first), save; when
203
- # replace_with_instance the node is swapped for an instance of the new file (same
204
- # name/transform/parent; outgoing persisted connections re-established) and the instance
205
- # is returned; else returns node. REFUSES the scene root and a path open in an editor tab.
206
- # The node's own position is zeroed for the pack (prefab authored at its origin, like the
207
- # editor's Save Branch) and restored / copied onto the instance; rotation and scale stay in
208
- # the prefab. A failure AFTER the file was written keeps + returns the original node (report)
209
- add_autoload(name: String, path: String) -> bool # ProjectSettings autoload/<name>="*res://…"
210
- # same gates as the editor's autoload dock: valid identifier, no engine/global-class
211
- # collision, existing res:// path to a Script or PackedScene
212
- add_input_action(action: String, events: Array) -> bool
213
- # events: [{type: key, keycode: String|int} | {type: mouse_button, button_index: int} |
214
- # {type: joypad_button, button_index: int} | {type: joypad_axis, axis:int, value:float}]
215
- # writes input/<action> = {deadzone, events} in ProjectSettings + saves; every event is
216
- # built BEFORE anything is written — unknown shape -> report, false, nothing half-written.
217
- # New action: deadzone = the engine default 0.2. Existing action: keeps its deadzone and
218
- # gains only the events it does not already match. Keycode strings go through
219
- # find_keycode ("Space", "Left", "Ctrl+S" — modifiers become the event's modifier flags)
220
- set_main_scene(path: String) -> bool # application/run/main_scene + save
221
- # path must exist and be a PackedScene
222
- # Project settings are written in the res:// PATH form ("*res://…", "res://…"); the editor UI
223
- # writes uid:// forms — both resolve. Do not "fix" one into the other.
224
- ```
68
+ `props` apply via `set()` per key; unknown keys are collected into a `prop_warnings` report entry, never silently dropped — read it. On an **older engine** a missing helper is a plain GDScript error (`Invalid call to method 'add_mesh'…`): fall back to the manual form, which works everywhere.
225
69
 
226
- **Engine posture. Read before relying on any of this.** The game-completeness tier is a frozen contract that not every engine build exposes yet; treat it with the same preview posture as `summer_run_script` itself (which returns `engine_lacks_op` on engines without RunSceneScript). On an engine that has RunSceneScript but predates this tier, a missing helper is a plain GDScript error (`Invalid call to method 'add_ui'…`) and, under the default `undo: "action"`, the whole run rolls back. Fall back to what works everywhere:
70
+ Newer engines add three more tiers. Their frozen signatures, conventions and fallbacks are in `references/ctx-api.md` (read it with `summer_docs_read` id `skill/scene-scripting/references/ctx-api.md`):
227
71
 
228
- | Game-completeness helper | Works on every engine |
229
- |---|---|
230
- | `add_tilemap` / `paint_tiles` / `paint_rect` | `TileMapLayer.new()` + `layer.tile_set = load(path)`, then `layer.set_cell(coords, source_id, atlas_coords)` in a loop — the GridMap recipe, in 2D |
231
- | `add_sprite` / `add_animated_sprite` / `add_body_2d` / `add_camera_2d` / `add_parallax` | manual `.new()` + `add_child` + `ctx.set_owner_recursive` |
232
- | `add_ui` / `add_canvas_layer` / `set_theme_overrides` | `.new()` + `set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)` + `add_theme_constant_override(...)`, owned as above — or the `ui-basics` skill's `summer_add_node` / `summer_set_prop` path |
233
- | `connect_signal` | `summer_connect_signal` (persists in the `.tscn`); a raw `emitter.pressed.connect(...)` inside the run script does NOT persist |
234
- | `attach_script` | `summer_write_file` the source, `summer_get_script_errors` on it, then `node.set_script(load(path))` in a script |
235
- | `make_prefab` | `PackedScene.new().pack(node)` + `ResourceSaver.save(...)` (pack includes only OWNED descendants — owner rules again), then `summer_instantiate_scene` |
236
- | `add_input_action` / `add_autoload` / `set_main_scene` | `summer_input_map_bind`; `summer_project_setting` (`autoload/<Name>`, `application/run/main_scene`) |
72
+ - **Geometry:** `boolean`, `extrude_polygon`, `lathe`, `sweep`, `terrain`, `set_smooth`, `decimate`, `convex_collision`, `uv_planar` / `uv_box`, `mirror`, `animate`, `make_shader`; `add_mesh` also takes `torus` and `text`.
73
+ - **Animation:** `animate_method`, `anim_state_machine`, `bone_pose`, `look_at_modifier` (the full recipe lives in `character-animation-wiring`).
74
+ - **Game completeness (2D, UI, gameplay code):** `add_sprite`, `add_animated_sprite`, `add_tilemap`, `paint_tiles` / `paint_rect`, `add_body_2d`, `add_camera_2d`, `add_parallax`, `add_ui`, `add_canvas_layer`, `set_theme_overrides`, `connect_signal`, `attach_script`, `make_prefab`, `add_autoload`, `add_input_action`, `set_main_scene`.
237
75
 
238
- **Undo posture.** File-writing helpers (`attach_script`, `make_prefab`) and project-settings helpers (`add_autoload`, `add_input_action`, `set_main_scene`) are covered by the pre-run checkpoint, NOT by the packed-scene undo action — a `rolled_back: true` result has reverted the scene but not those files or settings. The result's `undo_action` note says so whenever any of them ran (`files_written: [paths]`, `project_settings_changed: [keys]`): read it, and surface `no_rewind_point: true` to the user before more work of this kind.
76
+ **Engine posture.** Not every engine build exposes every tier. On an engine with RunSceneScript that predates a tier, a missing helper is a plain GDScript error and, under the default `undo: "action"`, the whole run rolls back. `references/ctx-api.md` has the table of what works on every engine.
239
77
 
240
78
  ## Owner rules
241
79
 
242
- <EXTREMELY-IMPORTANT>
243
80
  **Every node you create must be owned by the scene root, or it silently vanishes when the scene saves.** Descendants too — being under an owned node is NOT enough. `add_child` succeeds, the screenshot even shows it, and the saved `.tscn` is missing it.
244
81
 
245
82
  ```gdscript
@@ -247,474 +84,78 @@ root.add_child(node)
247
84
  ctx.set_owner_recursive(node) # node + every descendant
248
85
  ```
249
86
 
250
- `ctx.set_owner_recursive(node)` is the ctx helper for exactly this; the manual form is `node.owner = root` on the node AND each descendant. Either way, set owners AFTER `add_child` — owner assignment fails on a node not yet in the tree.
251
- </EXTREMELY-IMPORTANT>
87
+ The ctx creation helpers own what they create. For anything you build with `new()`, call `ctx.set_owner_recursive(node)` (or set `node.owner = root` on the node AND each descendant) AFTER `add_child` — owner assignment fails on a node not yet in the tree. Re-stamp the owner after any `reparent`.
252
88
 
253
89
  ## The loop
254
90
 
255
91
  1. `summer_world_snapshot` — the structured BEFORE baseline; keep its `snapshot_id`.
256
92
  2. `summer_api_docs` — verify every property/method name you are not certain of.
257
- 3. `summer_run_script` — run the script.
258
- 4. Read `errors`, `reports`, and `rolled_back` in the result — `rolled_back: true` means a runtime error undid everything (fix and re-run); on older engines / `undo: "none"`, an error can leave a partial mutation.
93
+ 3. `summer_run_script` with `scenePath` — run the script.
94
+ 4. Read `errors`, `reports`, `rolled_back` and `scene_target` in the result — `rolled_back: true` means a runtime error undid everything (fix and re-run); on older engines / `undo: "none"`, an error can leave a partial mutation.
259
95
  5. `summer_snapshot_diff from_id:<the id>` — the structural receipt: exactly the nodes you meant to add were added, nothing else changed, nothing vanished (an unowned node dropping on save shows up HERE).
260
96
  6. `summer_screenshot` — LOOK at it. Use `target:"scene" framing:"camera"` when the change touched lighting, environment, or emissive materials — the preset framings render a flat substitute environment and cannot show those.
261
- 7. Iterate. Never claim visual success without steps 5–6 (honesty rule: a capture is a fact; describing an unseen frame is fabrication). The full perception discipline lives in `verifying-scenes`.
97
+ 7. Iterate. Never claim visual success without steps 5–6 (a capture is a fact; describing an unseen frame is fabrication). The full perception discipline lives in `verifying-scenes`.
262
98
 
263
99
  ## Verify names with summer_api_docs — never guess
264
100
 
265
101
  Wrong property names fail silently or throw mid-script after half the mutation applied. Before using an unfamiliar class:
266
102
 
267
103
  ```
268
- summer_api_docs class_name:"CylinderMesh" → all properties/methods
269
- summer_api_docs class_name:"BoxShape3D" member:"size" → {type:"Vector3", default:"Vector3(1, 1, 1)"}
104
+ summer_api_docs(class_name="CylinderMesh") # all properties/methods
105
+ summer_api_docs(class_name="BoxShape3D", member="size") # {type:"Vector3", default:"Vector3(1, 1, 1)"}
270
106
  ```
271
107
 
272
108
  Entries list only members **declared on that class** — `position` lives on `Node3D`, not `MeshInstance3D`. Walk `inherits` upward when a member seems missing.
273
109
 
274
- ## Recipes
110
+ ## Two examples
275
111
 
276
- ### Scatter N instances with randomized transforms
112
+ Scatter 40 trees with deterministic randomness:
277
113
 
278
- ```gdscript
114
+ ```
115
+ summer_run_script(scenePath="res://levels/forest.tscn", source="""
279
116
  func run(ctx):
280
- var root = ctx.get_scene_root()
281
117
  var rng := RandomNumberGenerator.new()
282
- rng.seed = 12345 # deterministic re-runs
283
- var source: PackedScene = load("res://props/tree.tscn")
118
+ rng.seed = 12345 # same seed, same forest on a re-run
284
119
  for i in range(40):
285
- var tree := source.instantiate()
286
- root.add_child(tree)
287
- ctx.set_owner_recursive(tree) # owns the whole instanced subtree
120
+ var tree = ctx.instance_scene("res://props/tree.tscn", null, "Tree%d" % i)
288
121
  tree.position = Vector3(rng.randf_range(-20, 20), 0, rng.randf_range(-20, 20))
289
122
  tree.rotation.y = rng.randf_range(0, TAU)
290
123
  var s := rng.randf_range(0.8, 1.3)
291
124
  tree.scale = Vector3(s, s, s)
292
125
  ctx.report("scattered", 40)
126
+ ctx.save_scene()
127
+ """)
293
128
  ```
294
129
 
295
- ### Procedural mesh via SurfaceTool
296
-
297
- ```gdscript
298
- func run(ctx):
299
- var root = ctx.get_scene_root()
300
- var st := SurfaceTool.new()
301
- st.begin(Mesh.PRIMITIVE_TRIANGLES)
302
- st.set_normal(Vector3.UP)
303
- st.add_vertex(Vector3(0, 0, 0))
304
- st.add_vertex(Vector3(1, 0, 0))
305
- st.add_vertex(Vector3(0, 0, 1))
306
- var mesh := st.commit() # ArrayMesh
307
- var mi := MeshInstance3D.new()
308
- mi.mesh = mesh
309
- mi.name = "GeneratedMesh"
310
- root.add_child(mi)
311
- ctx.set_owner_recursive(mi)
312
- ctx.report("surfaces", mesh.get_surface_count())
313
- ```
314
-
315
- For grids/heightfields, build `PackedVector3Array`s and feed `ArrayMesh.add_surface_from_arrays` directly. Call `st.generate_normals()` before `commit()` when you did not set normals by hand.
316
-
317
- ### CSG primitives (fast blockouts)
318
-
319
- ```gdscript
320
- func run(ctx):
321
- var root = ctx.get_scene_root()
322
- var body := CSGCombiner3D.new()
323
- body.name = "Blockout"
324
- root.add_child(body)
325
- var box := CSGBox3D.new()
326
- box.size = Vector3(6, 3, 6)
327
- body.add_child(box)
328
- var hole := CSGCylinder3D.new()
329
- hole.operation = CSGShape3D.OPERATION_SUBTRACTION
330
- hole.radius = 1.0
331
- hole.height = 7.0
332
- body.add_child(hole)
333
- ctx.set_owner_recursive(body) # owner is the SCENE root, not the parent
334
- ```
335
-
336
- Live CSG nodes are the **older-engine fallback** — they re-evaluate every frame and should be converted before shipping (`scene-composition`). On a geometry-tier engine prefer `ctx.boolean(...)`, which bakes to a plain ArrayMesh and leaves no CSG nodes behind.
337
-
338
- ### Boolean blockout — carve a doorway (geometry tier)
339
-
340
- Box minus box, baked. The inputs are consumed; only the result remains.
341
-
342
- ```gdscript
343
- func run(ctx):
344
- var wall := ctx.add_mesh("box", "Wall", null, {"size": Vector3(6, 3, 0.3)})
345
- var hole := ctx.add_mesh("box", "DoorHole", null, {"size": Vector3(1.0, 2.1, 0.5)})
346
- hole.position = Vector3(0, -0.45, 0) # sink the opening to floor level
347
- var carved := ctx.boolean(wall, hole, "difference")
348
- if carved == null:
349
- return # reports carry the reason — read them
350
- carved.name = "WallWithDoorway"
351
- ctx.uv_box(carved) # boolean output needs UVs to take a texture
352
- ctx.report("doorway", str(carved.get_path()))
353
- ```
354
-
355
- Then `summer_snapshot_diff` must show `Wall` and `DoorHole` GONE and `WallWithDoorway` added — leftover inputs mean the boolean failed and returned null. Screenshot to check the opening is where you meant.
356
-
357
- ### Lathe a goblet / pillar (geometry tier)
358
-
359
- `lathe` revolves a 2D profile (x = radius, y = height) around Y. Design the profile from the axis outward.
360
-
361
- ```gdscript
362
- func run(ctx):
363
- var profile := PackedVector2Array([
364
- Vector2(0.0, 0.0), Vector2(0.45, 0.0), # foot
365
- Vector2(0.08, 0.1), Vector2(0.08, 0.55), # stem
366
- Vector2(0.35, 0.7), Vector2(0.4, 1.1), # bowl
367
- Vector2(0.0, 1.15),
368
- ])
369
- var goblet := ctx.lathe(profile, "Goblet", null, 360.0, 48)
370
- ctx.set_smooth(goblet, 40.0) # lathes look faceted without it
371
- ctx.apply_material(goblet, ctx.make_material({"albedo_color": Color(0.85, 0.7, 0.2), "metallic": 0.8, "roughness": 0.25}))
372
- ```
373
-
374
- A square-ish profile with `sides: 8` and `spin_degrees: 360` makes a chamfered pillar; `spin_degrees: 180` makes an apse/half-dome.
375
-
376
- ### Sweep a rail / pipe (geometry tier)
377
-
378
- `sweep` extrudes a 2D cross-section along a 3D polyline.
379
-
380
- ```gdscript
381
- func run(ctx):
382
- var section := PackedVector2Array([ # small circle-ish octagon, the pipe wall
383
- Vector2(0.05, 0), Vector2(0.035, 0.035), Vector2(0, 0.05), Vector2(-0.035, 0.035),
384
- Vector2(-0.05, 0), Vector2(-0.035, -0.035), Vector2(0, -0.05), Vector2(0.035, -0.035),
385
- ])
386
- var path := PackedVector3Array([
387
- Vector3(0, 1, 0), Vector3(4, 1, 0), Vector3(6, 1, 2), Vector3(6, 3, 6),
388
- ])
389
- var pipe := ctx.sweep(section, path, "SteamPipe")
390
- ctx.set_smooth(pipe, 45.0)
391
- ```
392
-
393
- Rails, roads, cables, gutters — same recipe, different cross-section. Keep path points a reasonable distance apart; hairpin corners self-intersect.
394
-
395
- ### Terrain with collision (geometry tier)
396
-
397
- ```gdscript
398
- func run(ctx):
399
- var ground := ctx.terrain(Vector2(64, 64), 6.0, 1337, "Terrain")
400
- ctx.apply_material(ground, ctx.make_material({"albedo_color": Color(0.35, 0.5, 0.25), "roughness": 1.0}))
401
- ctx.report("terrain", ctx.summary())
402
- ```
403
-
404
- Collision arrives automatically (a `StaticBody3D` + `HeightMapShape3D` child), already owned. Same seed → same terrain, so re-runs are deterministic. Pass `image_path` to drive heights from a grayscale heightmap instead of noise. Verify with `summer_screenshot framing:"camera"` — terrain reads wrong from preset framings' top-down angles.
405
-
406
- ### Text signage (geometry tier)
407
-
408
- `add_mesh` accepts `torus` and `text` on geometry-tier engines:
409
-
410
- ```gdscript
411
- func run(ctx):
412
- var sign := ctx.add_mesh("text", "ExitSign", null, {"text": "EXIT", "depth": 0.08})
413
- sign.position = Vector3(0, 2.6, -4)
414
- ctx.apply_material(sign, ctx.make_material({"albedo_color": Color(1, 0.2, 0.2), "emission_enabled": true, "emission": Color(1, 0.1, 0.1), "emission_energy_multiplier": 3.0}))
415
- ```
416
-
417
- Emissive text only proves itself in a `framing:"camera"` screenshot — preset framings substitute the environment and mute emission.
418
-
419
- ### Decimate for LOD, convex collision for props (geometry tier)
420
-
421
- Dense generated/imported meshes (Meshy-class output) want both before they ship:
422
-
423
- ```gdscript
424
- func run(ctx):
425
- var prop := ctx.find("AncientStatue") # e.g. a generated import
426
- if prop == null:
427
- ctx.report("error", "AncientStatue not found")
428
- return
429
- ctx.decimate(prop, 0.35) # keep ~35% of the triangles
430
- var body := ctx.convex_collision(prop, true) # V-HACD multi-shape for concave props
431
- ctx.report("collision_body", str(body.get_path()))
432
- ```
433
-
434
- `decimate` returns `false` (with a report entry) instead of ruining the mesh when the ratio is out of range. `convex_collision` owns every body it creates — the diff after `save_scene` is your receipt that nothing was silently dropped. Simple convex props (crates, rocks): leave `decompose` false, one hull is cheaper.
435
-
436
- ### Mirror symmetry (geometry tier)
437
-
438
- ```gdscript
439
- func run(ctx):
440
- var left_tower := ctx.find("Tower")
441
- var right_tower := ctx.mirror(left_tower, "x") # mirrored duplicate, owned
442
- right_tower.name = "TowerMirrored"
443
- ```
444
-
445
- The mirror is a real duplicate with flipped winding/normals — edit either side independently afterward.
446
-
447
- ### Animate — camera flythrough and a door-open (geometry tier)
448
-
449
- `animate` collapses the AnimationPlayer/library/track boilerplate into one call per property. Multiple calls with the same `anim_name` append tracks to the same clip.
450
-
451
- ```gdscript
452
- func run(ctx):
453
- var cam := ctx.add_camera(Vector3(0, 4, 12), Vector3.ZERO, true)
454
- ctx.animate(cam, "position", [
455
- [0.0, Vector3(0, 4, 12)],
456
- [4.0, Vector3(8, 5, 4)],
457
- [8.0, Vector3(0, 6, -10)],
458
- ], "flythrough")
459
- ctx.animate(cam, "rotation_degrees", [
460
- [0.0, Vector3(-10, 0, 0)],
461
- [4.0, Vector3(-12, 60, 0)],
462
- [8.0, Vector3(-15, 175, 0)],
463
- ], "flythrough") # same clip — second track appended
464
-
465
- var door := ctx.find("Door")
466
- ctx.animate(door, "rotation_degrees", [
467
- [0.0, Vector3(0, 0, 0)],
468
- [0.8, Vector3(0, 110, 0)],
469
- ], "open")
470
- ```
471
-
472
- Pass plain `position` / `rotation_degrees` values — the helper picks the right track type and handles the quaternion conversion for rotation. Verify by playing: `summer_play`, trigger the clip (or set autoplay), `summer_screenshot target:"game"`, `summer_stop`.
473
-
474
- ### make_shader — dissolve / glow FX with the compile-error loop (geometry tier)
475
-
476
- `make_shader` is the safe lane for text shaders because compile errors come back **verbatim** in the result (the `make_shader_errors` report entry, with the line number) instead of failing silently to a magenta material:
477
-
478
- ```gdscript
479
- func run(ctx):
480
- var mat := ctx.make_shader("""
481
- shader_type spatial;
482
- uniform float threshold : hint_range(0.0, 1.0) = 0.3;
483
- uniform vec4 edge_color : source_color = vec4(1.0, 0.55, 0.1, 1.0);
484
- void fragment() {
485
- float n = fract(sin(dot(UV * 12.0, vec2(12.9898, 78.233))) * 43758.5453);
486
- if (n < threshold) { discard; }
487
- ALBEDO = vec3(0.8);
488
- EMISSION = edge_color.rgb * smoothstep(threshold + 0.08, threshold, n) * 4.0;
489
- }
490
- """, {"threshold": 0.35})
491
- ctx.apply_material(ctx.find("Crystal"), mat)
492
- ```
493
-
494
- The loop when it fails: read the exact compile error (line number included) from the `make_shader_errors` report entry → fix that line → re-run. Do not guess-and-mutate; the error text names the line and identifier. For production-grade dissolve/fire/glow (FBM noise include, controllers, cookbook variants) use the `vfx-<effect>` recipes — `make_shader` is the fast lane for one-off FX and for iterating on a shader before writing it to a file.
495
-
496
- ### GridMap fills
497
-
498
- ```gdscript
499
- func run(ctx):
500
- var root = ctx.get_scene_root()
501
- var grid: GridMap = root.get_node("GridMap") # needs mesh_library assigned
502
- for x in range(16):
503
- for z in range(16):
504
- grid.set_cell_item(Vector3i(x, 0, z), 0) # item = MeshLibrary index
505
- ctx.report("cells", 256)
506
- ```
507
-
508
- Cells serialize into packed data — scripting is the ONLY sane way to fill them; never hand-edit `tile_map_data`-style fields as text.
509
-
510
- ### 3-point lighting rig
511
-
512
- ```gdscript
513
- func run(ctx):
514
- var root = ctx.get_scene_root()
515
- var rig := Node3D.new()
516
- rig.name = "LightRig"
517
- root.add_child(rig)
518
-
519
- var key := DirectionalLight3D.new()
520
- key.name = "KeyLight"
521
- key.light_energy = 1.2
522
- key.shadow_enabled = true
523
- key.rotation_degrees = Vector3(-45, -30, 0)
524
-
525
- var fill := OmniLight3D.new()
526
- fill.name = "FillLight"
527
- fill.light_energy = 0.4
528
- fill.position = Vector3(-4, 2, 4)
529
-
530
- var rim := SpotLight3D.new()
531
- rim.name = "RimLight"
532
- rim.light_energy = 0.8
533
- rim.position = Vector3(0, 3, -5)
534
- rim.rotation_degrees = Vector3(-20, 180, 0)
535
-
536
- for light in [key, fill, rim]:
537
- rig.add_child(light)
538
- ctx.set_owner_recursive(rig)
539
- ```
540
-
541
- Then `summer_screenshot` — lighting is exactly the kind of change you cannot judge without pixels.
542
-
543
- ### 2D platformer level — tilemap paint, character body, camera limits, parallax (game-completeness tier)
544
-
545
- One script lays down the level: paint the ground, drop a character body with collision, bound the camera to the level, stack parallax behind it. Tile coordinates are cells; camera limits are pixels (cells × tile size — 32 here).
546
-
547
- ```gdscript
548
- func run(ctx):
549
- const TILE := 32
550
- var ground := ctx.add_tilemap("res://tilesets/grass.tres", "Ground")
551
- var painted := ctx.paint_rect(ground, Rect2i(0, 12, 64, 4), 0, Vector2i(0, 0)) # floor: 64 wide, 4 deep
552
- painted += ctx.paint_rect(ground, Rect2i(20, 8, 6, 1), 0, Vector2i(1, 0)) # a floating platform
553
- painted += ctx.paint_tiles(ground, [Vector2i(10, 11), Vector2i(11, 11), Vector2i(12, 11)], 0, Vector2i(2, 0)) # a step
554
- ctx.report("tiles_painted", painted)
555
-
556
- var player := ctx.add_body_2d("character", "Player", null,
557
- {"type": "capsule", "radius": 14.0, "height": 60.0},
558
- {"position": Vector2(3 * TILE, 11 * TILE)})
559
- ctx.add_sprite("res://sprites/player_idle.png", "Sprite", player)
560
-
561
- var cam := ctx.add_camera_2d(player.position, Vector2(2, 2), true, Rect2i(0, 0, 64 * TILE, 16 * TILE))
562
- cam.reparent(player, false) # follow the body...
563
- cam.position = Vector2.ZERO
564
- ctx.set_owner_recursive(cam) # ...and re-stamp the owner after ANY reparent
565
-
566
- ctx.add_parallax([
567
- {"texture": "res://backgrounds/sky.png", "motion_scale": Vector2(0.1, 0.1), "repeat": Vector2i(1024, 0)},
568
- {"texture": "res://backgrounds/hills.png", "motion_scale": Vector2(0.4, 0.4), "repeat": Vector2i(1024, 0)},
569
- ], "Background")
570
- ctx.report("level", ctx.summary())
571
- ```
572
-
573
- Compare `tiles_painted` with the cell areas you asked for (256 + 6 + 3 here) — a short count means cells were skipped. Then `summer_snapshot_diff`: `added` must list `Ground`, `Player` **with its `CollisionShape2D` child**, the camera under `Player`, and the `Background` group with one `Parallax2D` per layer. Screenshot `target:"scene" nodePath:"Player"` to see the body standing on the floor row, then the whole scene to see the platform is reachable. Movement needs the controller script and the input actions — two recipes below. Want a working baseline instead of a blank scene? The `2d-platformer` template already has tilemap + `CharacterBody2D` + parallax wired.
574
-
575
- ### Animated sprite (game-completeness tier)
576
-
577
- `add_animated_sprite` builds the `SpriteFrames` resource from per-frame texture paths — one entry per animation, `fps` and `loop` per clip — and optionally autoplays one. Cut a sheet into frames first (`summer_slice_asset_sheet`, or the `sprite-sheet` skill when generating them).
578
-
579
- ```gdscript
580
- func run(ctx):
581
- var player := ctx.find("Player")
582
- if player == null:
583
- ctx.report("error", "Player not found")
584
- return
585
- var anim := ctx.add_animated_sprite({
586
- "idle": {"frames": ["res://sprites/player/idle_0.png", "res://sprites/player/idle_1.png"], "fps": 4.0, "loop": true},
587
- "run": {"frames": ["res://sprites/player/run_0.png", "res://sprites/player/run_1.png",
588
- "res://sprites/player/run_2.png", "res://sprites/player/run_3.png"], "fps": 12.0, "loop": true},
589
- "jump": {"frames": ["res://sprites/player/jump_0.png"], "fps": 1.0, "loop": false},
590
- }, "Anim", player, "idle")
591
- ctx.report("animations", anim.sprite_frames.get_animation_names())
592
- ```
593
-
594
- A missing texture path comes back as a report entry — read it before wondering why a clip is short. Which clip plays when is gameplay code (`anim.play("run")` from the controller's `_physics_process`), not scene setup. Verify by playing: `summer_play`, move, `summer_screenshot target:"game"` — an edit-time preview shows frame 0 only.
595
-
596
- ### HUD — canvas layer, margin, vbox, label/progress/button, anchors, theme overrides (game-completeness tier)
130
+ A key/fill/rim lighting rig:
597
131
 
598
- ```gdscript
599
- func run(ctx):
600
- var hud := ctx.add_canvas_layer("HUD", 1)
601
- var margin := ctx.add_ui("margin", "Margin", hud, {"anchor": "full_rect"})
602
- ctx.set_theme_overrides(margin, {"margin_left": 24, "margin_top": 24, "margin_right": 24, "margin_bottom": 24})
603
-
604
- var column := ctx.add_ui("vbox", "TopLeft", margin)
605
- var score := ctx.add_ui("label", "Score", column, {"text": "Score: 0"})
606
- ctx.set_theme_overrides(score, {"font_size": 28, "font_color": Color(1, 1, 1)})
607
- var health := ctx.add_ui("progress_bar", "Health", column,
608
- {"min_value": 0, "max_value": 100, "value": 100, "show_percentage": false,
609
- "custom_minimum_size": Vector2(240, 18)})
610
-
611
- var pause := ctx.add_ui("button", "PauseButton", hud, {"text": "Pause", "anchor": "top_right"})
612
- pause.process_mode = Node.PROCESS_MODE_ALWAYS # still clickable while the tree is paused
613
- ctx.report("hud", ctx.summary())
614
132
  ```
615
-
616
- `props.anchor` runs `set_anchors_and_offsets_preset` — the right way to pin a Control; hand-set `anchor_*`/`offset_*` values drift the moment the window resizes. The preset is applied before the other props, so an explicit `size`/`position` in the same dict wins. `add_ui("panel", …)` is a **PanelContainer** (it sizes to its child — a backdrop for a vbox/hbox); a plain `Panel` is `add_node("Panel", …)`. `set_theme_overrides` returns how many overrides it applied — compare with what you passed (an `int` on a key that is not `font_size` becomes a *constant* override, as with the margins above). Unknown `props` keys land in `prop_warnings` as always (use real property names: `custom_minimum_size`, not `min_size` or a guess). HUD Controls belong under a `CanvasLayer` — a Control parented under a `Node2D` scrolls with the camera and gets a `prop_warning`. For the layout and theme *design* — which container for which job, theme vs inline styling — use `ui-basics`; this is its one-script form.
617
-
618
- ### Wire a button with connect_signal() (game-completeness tier)
619
-
620
- `ctx.connect_signal` makes a **persisted** connection (`CONNECT_PERSIST`) — it survives in the `.tscn`, exactly what the editor's ConnectionsDock writes. The target needs a method to call, so attach the script first; a script attached in the same run has not compiled yet, and `connect_signal` accepts it because the source declares `func _on_pause_pressed(` (the method gate):
621
-
622
- ```gdscript
133
+ summer_run_script(scenePath="res://main.tscn", source="""
623
134
  func run(ctx):
624
- var hud := ctx.find("HUD")
625
- var button := ctx.find("PauseButton")
626
- if ctx.attach_script(hud, """
627
- extends CanvasLayer
628
-
629
- func _on_pause_pressed() -> void:
630
- get_tree().paused = not get_tree().paused
631
- """) == null:
632
- return # parse errors are in the report — fix, re-run
633
- if not ctx.connect_signal(button, "pressed", hud, "_on_pause_pressed"):
634
- return # the report names the unknown signal or method
635
- ctx.report("wired", "PauseButton.pressed -> HUD._on_pause_pressed")
636
- ```
637
-
638
- Never wire with a bare `button.pressed.connect(...)` inside the run script: that connection lives only in the editor process and is not saved. And the helper is `connect_signal`, not `connect` — `ctx.connect(...)` is `Object.connect` on the ctx object itself and fails with a wrong-signature error. `true` from `ctx.connect_signal` says the connection was made and flagged persistent (an already-connected pair also returns `true`, with `already_connected` in the report); both ends must be owned by the edited root or the `.tscn` cannot carry it. The proof is `summer_play`, click, `summer_screenshot target:"game"`.
639
-
640
- ### attach_script — behavior with the parse-error loop (game-completeness tier)
641
-
642
- `attach_script` validates the source **before** writing anything: a parse error comes back as a report entry (with the line) and the return is `null` — no half-written file, no broken node. Empty `path` → `res://scripts/<SceneName>/<NodeName>.gd`.
643
-
644
- ```gdscript
645
- func run(ctx):
646
- var player := ctx.find("Player")
647
- var script := ctx.attach_script(player, """
648
- extends CharacterBody2D
649
-
650
- const SPEED := 220.0
651
- const JUMP_VELOCITY := -420.0
652
-
653
- @onready var anim: AnimatedSprite2D = $Anim
654
-
655
- func _physics_process(delta: float) -> void:
656
- if not is_on_floor():
657
- velocity += get_gravity() * delta
658
- if Input.is_action_just_pressed("jump") and is_on_floor():
659
- velocity.y = JUMP_VELOCITY
660
- var dir := Input.get_axis("move_left", "move_right")
661
- velocity.x = dir * SPEED
662
- if dir != 0.0:
663
- anim.flip_h = dir < 0.0
664
- anim.play("jump" if not is_on_floor() else ("run" if dir != 0.0 else "idle"))
665
- move_and_slide()
135
+ var rig = ctx.add_node("Node3D", "LightRig")
136
+ ctx.add_node("DirectionalLight3D", "KeyLight", rig, {"light_energy": 1.2, "shadow_enabled": true, "rotation_degrees": Vector3(-45, -30, 0)})
137
+ ctx.add_node("OmniLight3D", "FillLight", rig, {"light_energy": 0.4, "position": Vector3(-4, 2, 4)})
138
+ ctx.add_node("SpotLight3D", "RimLight", rig, {"light_energy": 0.8, "position": Vector3(0, 3, -5), "rotation_degrees": Vector3(-20, 180, 0)})
139
+ ctx.save_scene()
666
140
  """)
667
- if script == null:
668
- return # read the report: line + message; fix THAT line; re-run
669
- ctx.report("script", script.resource_path)
670
141
  ```
671
142
 
672
- The loop: read the exact parse error (line and identifier) from the report → fix that line → re-run. Do not guess-and-mutate. An existing file at `path` is overwritten and its previous sha recorded in the report — the pre-run checkpoint is the rollback for that, not the undo action. Idioms (typed declarations, signals, `_ready` vs `_process`): `gdscript-patterns`. Later edits to a script already on disk: `summer_write_file` + `summer_get_script_errors`.
673
-
674
- ### make_prefab — turn a built subtree into a reusable .tscn (game-completeness tier)
675
-
676
- Build the thing once in-scene, then pack it. With `replace_with_instance` (the default) the inline subtree is swapped for an instance of the new file — same name, transform, parent — and the instance is returned.
677
-
678
- ```gdscript
679
- func run(ctx):
680
- var torch := ctx.find("Torch") # Sprite2D + PointLight2D + Area2D you built above
681
- if torch == null:
682
- return
683
- var first := ctx.make_prefab(torch, "res://prefabs/torch.tscn")
684
- for i in range(3):
685
- var t := ctx.instance_scene("res://prefabs/torch.tscn", null, "Torch%d" % (i + 2))
686
- t.position = first.position + Vector2(160 * (i + 1), 0)
687
- ctx.report("prefab", first.scene_file_path)
688
- ```
689
-
690
- The owner fix-up runs before packing, so a child you forgot to own is included instead of silently dropped. The prefab is authored at its origin — the node's position is zeroed for the pack (the editor's Save Branch default) and copied back onto the instance, so `first.position` above is still where the torch stood; rotation and scale stay in the prefab. `make_prefab` refuses the scene root (pack a child subtree, never `ctx.get_scene_root()`) and a path that is open in an editor tab. The receipt: `res://prefabs/torch.tscn` in the result's `files_written`, `Torch` now reporting a `scene_file_path`, and the copies in the diff's `added`. From here on, edit the prefab file, not the instances.
691
-
692
- ### Input actions, autoload, main scene (game-completeness tier)
693
-
694
- Project-level wiring in one run — the platformer controller above already reads these actions:
695
-
696
- ```gdscript
697
- func run(ctx):
698
- ctx.add_input_action("move_left", [{"type": "key", "keycode": "A"}, {"type": "key", "keycode": "Left"},
699
- {"type": "joypad_axis", "axis": 0, "value": -1.0}])
700
- ctx.add_input_action("move_right", [{"type": "key", "keycode": "D"}, {"type": "key", "keycode": "Right"},
701
- {"type": "joypad_axis", "axis": 0, "value": 1.0}])
702
- ctx.add_input_action("jump", [{"type": "key", "keycode": "Space"}, {"type": "joypad_button", "button_index": 0}])
703
- ctx.add_autoload("GameState", "res://scripts/autoload/game_state.gd") # the file must exist — write it first
704
- if ctx.save_scene("res://levels/level_01.tscn"):
705
- ctx.set_main_scene("res://levels/level_01.tscn")
706
- ```
143
+ Then `summer_screenshot` — lighting is exactly the kind of change you cannot judge without pixels.
707
144
 
708
- An unknown event shape → report + `false`, never a half-written action (every event is built before anything is written). A new action gets the engine's default deadzone, 0.2; re-running on an existing action keeps its deadzone and appends only the events it does not already match, so the recipe is safe to re-run. Keycode strings are `find_keycode` names (`"Space"`, `"Left"`, `"Ctrl+S"` — modifiers become the event's modifier flags). Write the autoload's script before registering it (`summer_write_file`, or `attach_script` on a node you then `make_prefab`) — `add_autoload` applies the editor dock's gates (valid identifier, no class-name collision, an existing `res://` Script or PackedScene). These helpers write the `res://` path form (`"*res://…"`, `"res://…"`); the editor UI writes `uid://` forms and both resolve — leave them be. They write `project.godot`, not the scene: the result's `undo_action` note lists them under `project_settings_changed`, outside the scene undo — checkpoint territory. Proof: `summer_play` → `summer_get_runtime_tree` shows `/root/GameState`; the keys move the body in a `target:"game"` capture.
145
+ More recipes, each the `source` of one call:
709
146
 
710
- ### Verifying 2D and UI work
147
+ - `references/recipes-3d.md` (`skill/scene-scripting/references/recipes-3d.md`): procedural meshes, CSG and boolean blockouts, lathe, sweep, terrain, text signage, decimate and convex collision, mirror, keyframe animation, `make_shader`, GridMap fills.
148
+ - `references/recipes-2d-ui.md` (`skill/scene-scripting/references/recipes-2d-ui.md`): a 2D platformer level, animated sprites, a HUD, a persisted button connection, `attach_script`, `make_prefab`, input actions, autoloads and the main scene, and how to verify 2D and UI work.
711
149
 
712
- Same loop, different framing:
150
+ ## Which tool
713
151
 
714
- - **Diff first.** `added` must carry the bodies' `CollisionShape2D` children, the `CanvasLayer` with its Controls, one `Parallax2D` per layer. Anchors and theme overrides show up as `changed` properties on the Controls.
715
- - **2D scene captures.** `summer_screenshot target:"scene"` on a 2D scene synthesizes a `Camera2D` and auto-fits the `CanvasItem` bounds — the 3D presets do not apply, and `framing:"camera"` errors on a scene with no 3D content. `nodePath:"Player"` frames one node (its 2D rect, children included); `size:[1280, 720]` sets the resolution that anchors resolve against.
716
- - **UI and anything input-driven** is only proven in the running game: `summer_play`, then `summer_screenshot target:"game"`. An edit-time preview of a HUD approximates a viewport the editor does not have.
717
- - Read the confession fields in every capture (synthetic camera, no camera, project mismatch). A capture you did not receive is not evidence — `verifying-scenes` has the full discipline.
152
+ | Job | Tool |
153
+ |---|---|
154
+ | One property on one node | `summer_set_prop` |
155
+ | One signal, one input binding, one project setting | `summer_connect_signal`, `summer_input_map_bind`, `summer_project_setting` |
156
+ | 3+ related scene edits, or anything computed | `summer_run_script` with `scenePath` |
157
+ | Many kit pieces at exact poses, read back as one receipt | `summer_batch` with placed `InstantiateScene` ops (`spatial-placement`) |
158
+ | Project-wide batch work on files on disk | `summer_run_editor_script` |
718
159
 
719
160
  ## summer_run_editor_script — the cold path
720
161
 
@@ -724,41 +165,30 @@ A different tool for a different job: it boots a **fresh headless child editor a
724
165
  - Use for batch/project-wide work: re-saving every scene, sweeping resources, mass fixes, generating `.tres` assets.
725
166
  - Budget default 120s, clamp 15–600 — include boot time (30s+ on large projects).
726
167
  - It confesses `no_rewind_point:true` when no pre-run checkpoint could be taken — surface that to the user before more destructive work.
727
- - No renderer: it can never screenshot. Judge it by artifacts on disk (see `headless-scripting` for the full discipline).
168
+ - No renderer: it can never screenshot. Judge it by artifacts on disk (see `headless-scripting`).
728
169
 
729
- If `summer_run_script` fails with "doesn't support RunSceneScript yet", the engine build is too old — fall back to `summer_run_editor_script` or tell the user to update Summer Engine.
170
+ If `summer_run_script` answers `engine_lacks_op`, the engine build predates RunSceneScript (Summer Engine 0.5.66 or newer has it): update Summer Engine, or use `summer_run_editor_script` for the same work.
730
171
 
731
- ## Red Flags — STOP
172
+ ## Pitfalls
732
173
 
733
- | Red flag | Reality |
174
+ | Pitfall | Reality |
734
175
  |---|---|
735
176
  | Ten `summer_add_node`/`summer_set_prop` calls in a row | That is one script. Write the script. |
736
- | `add_child` without `ctx.set_owner_recursive(node)` (or manual `.owner`) | Saved scene silently loses the node. |
737
- | Guessing a property name "it's probably `color`" | `summer_api_docs` answers in one call. Wrong names fail after half the mutation applied. |
177
+ | A script without `scenePath` | It runs against whatever tab is open. Name the scene. |
178
+ | `add_child` without `ctx.set_owner_recursive(node)` (or manual `.owner`) | The saved scene silently loses the node. |
179
+ | `var node := ctx.add_node(...)` | Does not compile: `ctx` results are untyped. `var node = ctx.add_node(...)`. |
180
+ | Guessing a property name ("it's probably `color`") | `summer_api_docs` answers in one call. Wrong names fail after half the mutation applied. |
738
181
  | Claiming "the forest looks great" without a screenshot | Describe only frames you received. Run `summer_screenshot`. |
739
182
  | Heavy batch loop in `summer_run_script` | It blocks the live editor. Move it to `summer_run_editor_script`. |
740
183
  | Using `summer_run_editor_script` to edit the OPEN scene | It sees only the on-disk file; live edits are invisible and collisions likely. Use `summer_run_script`. |
741
184
  | `"Vector3(0,10,0)"` (quoted) inside script source | That is the `summer_set_prop` wire convention. In GDScript write `Vector3(0, 10, 0)`. |
742
185
  | Ignoring `errors` because `ok` was true | A partially-failed script may have half-mutated the scene. Read them. |
743
- | Hand-building CSG node trees on a geometry-tier engine | `ctx.boolean/lathe/sweep/extrude_polygon` bake clean ArrayMeshes with no live CSG left behind. |
744
- | Re-running a failed `make_shader` with a guessed fix | The compile error is in the `make_shader_errors` report verbatim, with the line. Read it, fix that line. |
745
- | Hand-writing AnimationPlayer/library/track plumbing | `ctx.animate` is one call per property and dodges the quaternion trap. |
746
- | Painting tiles with `summer_set_prop`, or hand-editing `tile_map_data` | `ctx.paint_rect` / `paint_tiles` (or `set_cell` in a loop) — cells are packed data. |
747
- | Building a HUD with a dozen `summer_add_node`/`summer_set_prop` calls | One script: `add_canvas_layer` → `add_ui` → `set_theme_overrides`. |
748
- | `button.pressed.connect(...)` inside the run script | Not persisted — gone when the script ends. `ctx.connect_signal` (or `summer_connect_signal`) saves it in the `.tscn`. |
749
- | `ctx.connect(button, "pressed", hud, "_on_pressed")` | That is `Object.connect` on the ctx object, not the helper — wrong-signature error. The helper is `ctx.connect_signal(...)`. |
750
- | `{"min_size": ...}` in `add_ui` props | Not a Control property — lands in `prop_warnings` and does nothing. The property is `custom_minimum_size`. |
751
- | `ctx.make_prefab(ctx.get_scene_root(), ...)` | Refused. Pack a child subtree; the scene itself is `save_scene`. |
752
- | Re-running a failed `attach_script` with a guessed fix | The parse error is in the report with the line. Read it, fix that line. |
753
- | Judging a HUD from an edit-time scene preview | Anchors resolve against the real viewport. `summer_play` + `target:"game"`. |
754
- | Calling game-completeness helpers on an engine that predates them | `Invalid call to method` — the run rolls back. Use the fallback table above. |
755
- | Reading `rolled_back: true` as "nothing happened" after `attach_script` / `add_autoload` / `set_main_scene` | Files and `project.godot` sit outside the undo action. Read `files_written` / `project_settings_changed`; the checkpoint is the rollback. |
756
186
 
757
187
  **Related skills:**
758
188
  - `verifying-scenes` — the perception discipline: snapshot/diff/screenshot before-and-after, runtime reads, honest claims.
759
189
  - `headless-scripting` — shell-launched engine scripts, imports, exports.
760
190
  - `scene-composition` — what a well-structured scene looks like before you generate one.
761
- - `ui-basics` — Control hierarchy, containers, anchors, theme vs inline styling: the design behind the HUD recipe.
191
+ - `ui-basics` — Control hierarchy, containers, anchors, theme vs inline styling.
192
+ - `character-animation-wiring` — the animation tier end to end.
762
193
  - `gdscript-patterns` — idioms for the scripts `attach_script` writes.
763
- - `sprite-sheet` — generating the frames `add_animated_sprite` consumes.
764
194
  - `verification-before-completion` — proving the result before claiming done.