summer-engine 3.1.0 → 3.2.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 (366) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +27 -5
  3. package/.codex-plugin/plugin.json +28 -6
  4. package/.cursor-plugin/plugin.json +27 -5
  5. package/.factory-plugin/plugin.json +27 -5
  6. package/.opencode/plugins/summer.js +1 -1
  7. package/CHANGELOG.md +102 -0
  8. package/README.md +18 -3
  9. package/assets/audit/scene_audit.gd +4136 -0
  10. package/assets/seeing/seeing_probe.gd +1873 -0
  11. package/dist/cli/commands/skills.js +20 -2
  12. package/dist/core/api-client.d.ts +9 -1
  13. package/dist/core/api-client.js +21 -4
  14. package/dist/core/capabilities/agent-playbook.js +46 -0
  15. package/dist/core/capabilities/audit/accept.d.ts +55 -0
  16. package/dist/core/capabilities/audit/accept.js +147 -0
  17. package/dist/core/capabilities/audit/args.d.ts +95 -0
  18. package/dist/core/capabilities/audit/args.js +89 -0
  19. package/dist/core/capabilities/audit/audit.d.ts +56 -0
  20. package/dist/core/capabilities/audit/audit.js +287 -0
  21. package/dist/core/capabilities/audit/frame.d.ts +14 -0
  22. package/dist/core/capabilities/audit/frame.js +35 -0
  23. package/dist/core/capabilities/audit/gaps.d.ts +96 -0
  24. package/dist/core/capabilities/audit/gaps.js +257 -0
  25. package/dist/core/capabilities/audit/judge.d.ts +162 -0
  26. package/dist/core/capabilities/audit/judge.js +1134 -0
  27. package/dist/core/capabilities/audit/math.d.ts +110 -0
  28. package/dist/core/capabilities/audit/math.js +263 -0
  29. package/dist/core/capabilities/audit/summary.d.ts +74 -0
  30. package/dist/core/capabilities/audit/summary.js +212 -0
  31. package/dist/core/capabilities/board.d.ts +31 -0
  32. package/dist/core/capabilities/board.js +43 -0
  33. package/dist/core/capabilities/camera-view.d.ts +4 -2
  34. package/dist/core/capabilities/camera-view.js +6 -3
  35. package/dist/core/capabilities/connect-signal.d.ts +85 -0
  36. package/dist/core/capabilities/connect-signal.js +285 -0
  37. package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
  38. package/dist/core/capabilities/engine-fallbacks.js +5 -0
  39. package/dist/core/capabilities/engine-ops.d.ts +5 -1
  40. package/dist/core/capabilities/engine-ops.js +0 -3
  41. package/dist/core/capabilities/engine-receipt.d.ts +12 -0
  42. package/dist/core/capabilities/engine-receipt.js +26 -0
  43. package/dist/core/capabilities/file-read.d.ts +64 -0
  44. package/dist/core/capabilities/file-read.js +325 -0
  45. package/dist/core/capabilities/game-task-plan.js +4 -2
  46. package/dist/core/capabilities/grep.d.ts +60 -0
  47. package/dist/core/capabilities/grep.js +190 -0
  48. package/dist/core/capabilities/inspect-node.d.ts +56 -0
  49. package/dist/core/capabilities/inspect-node.js +225 -0
  50. package/dist/core/capabilities/inspect-resource.d.ts +61 -0
  51. package/dist/core/capabilities/inspect-resource.js +376 -0
  52. package/dist/core/capabilities/math3d.d.ts +28 -0
  53. package/dist/core/capabilities/math3d.js +88 -0
  54. package/dist/core/capabilities/motion-generation.d.ts +62 -0
  55. package/dist/core/capabilities/motion-generation.js +138 -0
  56. package/dist/core/capabilities/placement-batch.d.ts +102 -0
  57. package/dist/core/capabilities/placement-batch.js +483 -0
  58. package/dist/core/capabilities/placement-math.d.ts +70 -0
  59. package/dist/core/capabilities/placement-math.js +209 -0
  60. package/dist/core/capabilities/placement-script.d.ts +42 -0
  61. package/dist/core/capabilities/placement-script.js +1096 -0
  62. package/dist/core/capabilities/placement.d.ts +485 -0
  63. package/dist/core/capabilities/placement.js +1731 -0
  64. package/dist/core/capabilities/project-context.d.ts +33 -2
  65. package/dist/core/capabilities/project-context.js +87 -4
  66. package/dist/core/capabilities/replace-node.d.ts +91 -0
  67. package/dist/core/capabilities/replace-node.js +423 -0
  68. package/dist/core/capabilities/runtime-control.d.ts +18 -2
  69. package/dist/core/capabilities/runtime-control.js +67 -2
  70. package/dist/core/capabilities/scene-batch.d.ts +63 -0
  71. package/dist/core/capabilities/scene-batch.js +394 -0
  72. package/dist/core/capabilities/scene-readback.d.ts +36 -0
  73. package/dist/core/capabilities/scene-readback.js +54 -0
  74. package/dist/core/capabilities/seeing/args.d.ts +348 -0
  75. package/dist/core/capabilities/seeing/args.js +178 -0
  76. package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
  77. package/dist/core/capabilities/seeing/candidates.js +282 -0
  78. package/dist/core/capabilities/seeing/math.d.ts +134 -0
  79. package/dist/core/capabilities/seeing/math.js +269 -0
  80. package/dist/core/capabilities/seeing/probe.d.ts +62 -0
  81. package/dist/core/capabilities/seeing/probe.js +188 -0
  82. package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
  83. package/dist/core/capabilities/seeing/scoring.js +639 -0
  84. package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
  85. package/dist/core/capabilities/seeing/seeing.js +1341 -0
  86. package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
  87. package/dist/core/capabilities/seeing/shot-store.js +204 -0
  88. package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
  89. package/dist/core/capabilities/surface-snap-mesh.js +425 -0
  90. package/dist/core/capabilities/surface-snap.d.ts +77 -0
  91. package/dist/core/capabilities/surface-snap.js +461 -0
  92. package/dist/core/capabilities/tool-dispatch.js +231 -72
  93. package/dist/core/capabilities/tscn.d.ts +85 -0
  94. package/dist/core/capabilities/tscn.js +391 -0
  95. package/dist/core/capabilities/world-snapshot.d.ts +58 -0
  96. package/dist/core/capabilities/world-snapshot.js +150 -0
  97. package/dist/core/capability-skew.d.ts +5 -5
  98. package/dist/core/capability-skew.js +14 -6
  99. package/dist/core/headless/worker-engine-client.d.ts +1 -1
  100. package/dist/core/headless/worker-engine-client.js +2 -2
  101. package/dist/core/library-read.d.ts +37 -5
  102. package/dist/core/library-read.js +264 -33
  103. package/dist/core/library-search.d.ts +2 -2
  104. package/dist/installer/agent-config.js +5 -3
  105. package/dist/installer/agent-table.d.ts +16 -0
  106. package/dist/installer/agent-table.js +52 -17
  107. package/dist/mcp/server.js +9 -3
  108. package/dist/mcp/tools/asset-tools.js +21 -3
  109. package/dist/mcp/tools/audit-tools.d.ts +23 -0
  110. package/dist/mcp/tools/audit-tools.js +60 -0
  111. package/dist/mcp/tools/debug-tools.js +3 -1
  112. package/dist/mcp/tools/file-tools.js +17 -4
  113. package/dist/mcp/tools/generate-tools.js +74 -66
  114. package/dist/mcp/tools/library-tools.js +2 -0
  115. package/dist/mcp/tools/perception-tools.js +6 -19
  116. package/dist/mcp/tools/placement-tools.d.ts +24 -0
  117. package/dist/mcp/tools/placement-tools.js +91 -0
  118. package/dist/mcp/tools/project-tools.js +18 -15
  119. package/dist/mcp/tools/scene-tools.js +71 -83
  120. package/dist/mcp/tools/seeing-tools.d.ts +24 -0
  121. package/dist/mcp/tools/seeing-tools.js +64 -0
  122. package/dist/mcp/tools/spatial-tools.js +17 -9
  123. package/dist/mcp/tools/visual-tools.js +59 -7
  124. package/gemini-extension.json +26 -4
  125. package/integrations/amp/README.md +1 -1
  126. package/integrations/crush/README.md +1 -1
  127. package/integrations/grok-build/README.md +1 -1
  128. package/integrations/hermes/README.md +1 -1
  129. package/integrations/kimi-code/README.md +1 -1
  130. package/integrations/mistral-vibe/README.md +1 -1
  131. package/integrations/opencode/README.md +1 -1
  132. package/integrations/rovo-dev/README.md +1 -1
  133. package/integrations/vscode-copilot/README.md +1 -1
  134. package/integrations/warp/README.md +1 -1
  135. package/integrations/windsurf/README.md +1 -1
  136. package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
  137. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  138. package/library/references/summer-folder/resource.yaml +1 -1
  139. package/library/references/summer-folder/summer-folder.md +1 -1
  140. package/library/skills/3d-lighting/SKILL.md +1 -1
  141. package/library/skills/README.md +36 -14
  142. package/library/skills/adaptive-music/SKILL.md +1 -1
  143. package/library/skills/agent-playtesting/SKILL.md +1 -1
  144. package/library/skills/ai-characters-in-games/SKILL.md +220 -0
  145. package/library/skills/ai-characters-in-games/resource.yaml +30 -0
  146. package/library/skills/ambient-bed/SKILL.md +1 -1
  147. package/library/skills/animated-loop/SKILL.md +1 -1
  148. package/library/skills/animation-tree/SKILL.md +1 -1
  149. package/library/skills/art-direction/SKILL.md +1 -1
  150. package/library/skills/asset-strategy/SKILL.md +2 -1
  151. package/library/skills/asset-strategy/resource.yaml +2 -1
  152. package/library/skills/audio-direction/SKILL.md +1 -1
  153. package/library/skills/auto-fire-targeting/SKILL.md +1 -1
  154. package/library/skills/brainstorm-game/SKILL.md +1 -1
  155. package/library/skills/brainstorming/SKILL.md +1 -1
  156. package/library/skills/browse-templates/SKILL.md +3 -3
  157. package/library/skills/browse-templates/resource.yaml +1 -1
  158. package/library/skills/camera-rigs/SKILL.md +128 -0
  159. package/library/skills/camera-rigs/resource.yaml +29 -0
  160. package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
  161. package/library/skills/character-animation-wiring/SKILL.md +1 -1
  162. package/library/skills/character-model/SKILL.md +1 -1
  163. package/library/skills/character-movement/SKILL.md +326 -0
  164. package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
  165. package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
  166. package/library/skills/character-movement/references/movement-3d.md +136 -0
  167. package/library/skills/character-movement/references/platformer-feel.md +83 -0
  168. package/library/skills/character-movement/resource.yaml +40 -0
  169. package/library/skills/character-portrait/SKILL.md +1 -1
  170. package/library/skills/cinematic-cutscene/SKILL.md +1 -1
  171. package/library/skills/combat-basics/SKILL.md +159 -0
  172. package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
  173. package/library/skills/combat-basics/resource.yaml +34 -0
  174. package/library/skills/concept-art/SKILL.md +1 -1
  175. package/library/skills/create-asset-sheet/SKILL.md +1 -1
  176. package/library/skills/debug/SKILL.md +1 -1
  177. package/library/skills/debugging-game-feel/SKILL.md +1 -1
  178. package/library/skills/design-level/SKILL.md +1 -1
  179. package/library/skills/design-mechanic/SKILL.md +1 -1
  180. package/library/skills/design-npc/SKILL.md +1 -1
  181. package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
  182. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
  183. package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
  184. package/library/skills/environment-kit/SKILL.md +1 -1
  185. package/library/skills/export-and-ship/SKILL.md +1 -1
  186. package/library/skills/fabricating-assets/SKILL.md +1 -1
  187. package/library/skills/facial-and-lipsync/SKILL.md +1 -1
  188. package/library/skills/fps-controller/SKILL.md +9 -7
  189. package/library/skills/fps-controller/resource.yaml +4 -1
  190. package/library/skills/game-feel/SKILL.md +1 -1
  191. package/library/skills/gameskill/SKILL.md +1 -1
  192. package/library/skills/gdscript-patterns/SKILL.md +1 -1
  193. package/library/skills/generate-motion/SKILL.md +9 -9
  194. package/library/skills/generate-motion/resource.yaml +2 -1
  195. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
  196. package/library/skills/headless-scripting/SKILL.md +1 -1
  197. package/library/skills/in-game-purchases/SKILL.md +254 -0
  198. package/library/skills/in-game-purchases/resource.yaml +29 -0
  199. package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
  200. package/library/skills/investigating-bugs/SKILL.md +1 -1
  201. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
  202. package/library/skills/make-game/SKILL.md +25 -3
  203. package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
  204. package/library/skills/make-game/references/2d-platformer.md +117 -0
  205. package/library/skills/make-game/references/2d-rpg-content.md +234 -0
  206. package/library/skills/make-game/references/2d-rpg.md +241 -0
  207. package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
  208. package/library/skills/make-game/references/2d-survivors.md +237 -0
  209. package/library/skills/make-game/references/3d-fps-systems.md +216 -0
  210. package/library/skills/make-game/references/3d-fps.md +177 -0
  211. package/library/skills/make-game/references/3d-horror-systems.md +186 -0
  212. package/library/skills/make-game/references/3d-horror.md +201 -0
  213. package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
  214. package/library/skills/make-game/references/3d-third-person.md +215 -0
  215. package/library/skills/make-game/resource.yaml +6 -1
  216. package/library/skills/multiplayer/SKILL.md +150 -0
  217. package/library/skills/multiplayer/resource.yaml +33 -0
  218. package/library/skills/multiplayer-movement/SKILL.md +510 -0
  219. package/library/skills/multiplayer-movement/files.md +501 -0
  220. package/library/skills/multiplayer-movement/resource.yaml +30 -0
  221. package/library/skills/multiplayer-project/SKILL.md +521 -0
  222. package/library/skills/multiplayer-project/files.md +448 -0
  223. package/library/skills/multiplayer-project/resource.yaml +36 -0
  224. package/library/skills/multiplayer-publish/SKILL.md +229 -0
  225. package/library/skills/multiplayer-publish/export-errors.md +89 -0
  226. package/library/skills/multiplayer-publish/resource.yaml +28 -0
  227. package/library/skills/multiplayer-state/SKILL.md +438 -0
  228. package/library/skills/multiplayer-state/files.md +592 -0
  229. package/library/skills/multiplayer-state/resource.yaml +35 -0
  230. package/library/skills/multiplayer-testing/SKILL.md +236 -0
  231. package/library/skills/multiplayer-testing/resource.yaml +39 -0
  232. package/library/skills/music-track/SKILL.md +1 -1
  233. package/library/skills/navigate-summer/SKILL.md +1 -1
  234. package/library/skills/new-project/SKILL.md +1 -1
  235. package/library/skills/organic-model/SKILL.md +1 -1
  236. package/library/skills/pixel-art/SKILL.md +1 -1
  237. package/library/skills/play/SKILL.md +1 -1
  238. package/library/skills/playtesting-a-feature/SKILL.md +1 -1
  239. package/library/skills/procedural-animation/SKILL.md +2 -2
  240. package/library/skills/procedural-animation/resource.yaml +1 -1
  241. package/library/skills/prop-model/SKILL.md +1 -1
  242. package/library/skills/psx-retro-rendering/SKILL.md +1 -1
  243. package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
  244. package/library/skills/remote-deploy/SKILL.md +1 -1
  245. package/library/skills/retarget/SKILL.md +1 -1
  246. package/library/skills/running-in-the-cloud/SKILL.md +1 -1
  247. package/library/skills/save-load/SKILL.md +156 -0
  248. package/library/skills/save-load/resource.yaml +27 -0
  249. package/library/skills/scene-composition/SKILL.md +1 -1
  250. package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
  251. package/library/skills/scene-scripting/SKILL.md +1 -1
  252. package/library/skills/scene-to-level/SKILL.md +1 -1
  253. package/library/skills/skill-create/SKILL.md +1 -1
  254. package/library/skills/skill-improve/SKILL.md +1 -1
  255. package/library/skills/skill-test/SKILL.md +1 -1
  256. package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
  257. package/library/skills/skybox-panorama/SKILL.md +1 -1
  258. package/library/skills/sound-effect/SKILL.md +1 -1
  259. package/library/skills/spatial-placement/SKILL.md +134 -3
  260. package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
  261. package/library/skills/spatial-placement/resource.yaml +16 -2
  262. package/library/skills/sprite-sheet/SKILL.md +1 -1
  263. package/library/skills/summer-analytics/SKILL.md +147 -0
  264. package/library/skills/summer-analytics/resource.yaml +28 -0
  265. package/library/skills/summer-friends/SKILL.md +226 -0
  266. package/library/skills/summer-friends/resource.yaml +28 -0
  267. package/library/skills/summer-leaderboards/SKILL.md +231 -0
  268. package/library/skills/summer-leaderboards/resource.yaml +30 -0
  269. package/library/skills/summer-match-results/SKILL.md +286 -0
  270. package/library/skills/summer-match-results/resource.yaml +32 -0
  271. package/library/skills/summer-matchmaking/SKILL.md +301 -0
  272. package/library/skills/summer-matchmaking/resource.yaml +32 -0
  273. package/library/skills/summer-parties/SKILL.md +253 -0
  274. package/library/skills/summer-parties/resource.yaml +30 -0
  275. package/library/skills/summer-player-data/SKILL.md +322 -0
  276. package/library/skills/summer-player-data/resource.yaml +30 -0
  277. package/library/skills/summer-store/SKILL.md +271 -0
  278. package/library/skills/summer-store/resource.yaml +29 -0
  279. package/library/skills/summer-world-chat/SKILL.md +195 -0
  280. package/library/skills/summer-world-chat/resource.yaml +30 -0
  281. package/library/skills/summer-world-saves/SKILL.md +284 -0
  282. package/library/skills/summer-world-saves/resource.yaml +29 -0
  283. package/library/skills/text-to-motion/SKILL.md +99 -0
  284. package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
  285. package/library/skills/text-to-motion/resource.yaml +41 -0
  286. package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
  287. package/library/skills/tileable-texture/SKILL.md +1 -1
  288. package/library/skills/trailer-shot/SKILL.md +1 -1
  289. package/library/skills/tune-performance/SKILL.md +1 -1
  290. package/library/skills/ui-basics/SKILL.md +10 -1
  291. package/library/skills/ui-graphics/SKILL.md +1 -1
  292. package/library/skills/use-widget-asset/SKILL.md +1 -1
  293. package/library/skills/using-summer/SKILL.md +2 -2
  294. package/library/skills/using-summer/resource.yaml +2 -2
  295. package/library/skills/vehicle-model/SKILL.md +1 -1
  296. package/library/skills/verification-before-completion/SKILL.md +1 -1
  297. package/library/skills/verifying-scenes/SKILL.md +76 -2
  298. package/library/skills/verifying-scenes/resource.yaml +13 -2
  299. package/library/skills/vfx-dissolve/SKILL.md +1 -1
  300. package/library/skills/vfx-fire/SKILL.md +1 -1
  301. package/library/skills/vfx-hit-spark/SKILL.md +1 -1
  302. package/library/skills/vfx-lightning/SKILL.md +1 -1
  303. package/library/skills/vfx-magic-glow/SKILL.md +1 -1
  304. package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
  305. package/library/skills/vfx-smoke/SKILL.md +1 -1
  306. package/library/skills/vfx-water-ripple/SKILL.md +1 -1
  307. package/library/skills/voice-line/SKILL.md +1 -1
  308. package/library/skills/web-and-touch-input/SKILL.md +38 -0
  309. package/library/skills/web-and-touch-input/resource.yaml +28 -0
  310. package/library/skills/web-compatibility-look/SKILL.md +62 -0
  311. package/library/skills/web-compatibility-look/resource.yaml +30 -0
  312. package/library/skills/world-building-3d/SKILL.md +8 -1
  313. package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
  314. package/library/skills/world-building-3d/resource.yaml +2 -1
  315. package/library/skills/writing-plans/SKILL.md +1 -1
  316. package/library/skills/writing-skills/SKILL.md +1 -1
  317. package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
  318. package/library/tools/attach-to-surface/resource.yaml +142 -0
  319. package/library/tools/batch/resource.yaml +9 -3
  320. package/library/tools/connect-ports/resource.yaml +120 -0
  321. package/library/tools/connect-signal/resource.yaml +3 -2
  322. package/library/tools/debug-views/resource.yaml +94 -0
  323. package/library/tools/frame-nodes/resource.yaml +104 -0
  324. package/library/tools/frame-shot/resource.yaml +134 -0
  325. package/library/tools/generate-motion/resource.yaml +44 -9
  326. package/library/tools/get-board/resource.yaml +50 -0
  327. package/library/tools/get-project-context/resource.yaml +13 -2
  328. package/library/tools/grep/resource.yaml +77 -0
  329. package/library/tools/inspect-asset/resource.yaml +71 -0
  330. package/library/tools/inspect-node/resource.yaml +9 -2
  331. package/library/tools/inspect-resource/resource.yaml +16 -5
  332. package/library/tools/instantiate-scene/resource.yaml +27 -2
  333. package/library/tools/measure/resource.yaml +96 -0
  334. package/library/tools/place-adjacent/resource.yaml +113 -0
  335. package/library/tools/play/resource.yaml +15 -2
  336. package/library/tools/raycast/resource.yaml +101 -0
  337. package/library/tools/read-file/resource.yaml +36 -2
  338. package/library/tools/read-library/resource.yaml +2 -2
  339. package/library/tools/repeat-along/resource.yaml +129 -0
  340. package/library/tools/replace-node/resource.yaml +11 -5
  341. package/library/tools/scene-audit/resource.yaml +144 -0
  342. package/library/tools/screenshot/resource.yaml +13 -2
  343. package/library/tools/search-assets/resource.yaml +18 -0
  344. package/library/tools/shot-sheet/resource.yaml +110 -0
  345. package/library/tools/snap-to-surface/resource.yaml +4 -1
  346. package/library/tools/world-snapshot/resource.yaml +24 -3
  347. package/library/tools/zoom/resource.yaml +105 -0
  348. package/package.json +1 -1
  349. package/registry/generated/aliases.json +17 -3
  350. package/registry/generated/counts.json +3 -3
  351. package/registry/generated/gemini-extension.json +26 -4
  352. package/registry/generated/index.json +2154 -469
  353. package/registry/generated/marketplace.claude.json +2 -2
  354. package/registry/generated/plugin.claude.json +27 -5
  355. package/registry/generated/plugin.codex.json +28 -6
  356. package/registry/generated/plugin.cursor.json +27 -5
  357. package/registry/generated/plugin.factory.json +27 -5
  358. package/registry/generated/skills-index.md +36 -14
  359. package/registry/generated/skills-registry.json +421 -124
  360. package/registry/generated/templates-registry.json +2 -2
  361. package/library/skills/host-authoritative-state/SKILL.md +0 -332
  362. package/library/skills/host-authoritative-state/resource.yaml +0 -28
  363. package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
  364. package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
  365. package/library/skills/setup-multiplayer/SKILL.md +0 -336
  366. package/library/skills/setup-multiplayer/resource.yaml +0 -28
@@ -8,7 +8,7 @@ import { writeSkillMarker } from "../../installer/version-check.js";
8
8
  import { pruneRetiredSkills } from "../../installer/legacy-skills.js";
9
9
  import { resolveInstallLocation, resolveSkillAgent, resolveSkillScope, } from "../../installer/skill-locations.js";
10
10
  import { PACKAGE_ROOT } from "../../core/package-root.js";
11
- import { agentLabel as tableAgentLabel, agentSpec, defaultPathContext, legacyRuleFiles } from "../../installer/agent-table.js";
11
+ import { agentLabel as tableAgentLabel, agentSpec, agentsSharingSkills, defaultPathContext, isSharedSkillsPath, legacyRuleFiles, legacySkillDirs, } from "../../installer/agent-table.js";
12
12
  import { TOOLKIT_VERSION as cliVersion } from "../../core/version.js";
13
13
  // Skill files live in library/skills/<slug>/ and are resolved through the
14
14
  // generated registry (registry/generated/skills-registry.json).
@@ -198,6 +198,14 @@ function printInstallSummary(count, agent, scope, location) {
198
198
  const reloadHint = agent === "summer" ? undefined : agentSpec(agent).skills?.reloadHint;
199
199
  if (location.kind === "skill-dir") {
200
200
  console.log(`${label} can read skills from ${tildeified}/<skill>/SKILL.md`);
201
+ if (isSharedSkillsPath(location.path)) {
202
+ const others = agentsSharingSkills(scope, defaultPathContext())
203
+ .map((spec) => spec.label)
204
+ .filter((name) => name !== label);
205
+ if (others.length > 0) {
206
+ console.log(`This is the shared agentskills.io folder; ${others.join(", ")} read it too, so they are covered by this install.`);
207
+ }
208
+ }
201
209
  if (reloadHint)
202
210
  console.log(reloadHint);
203
211
  }
@@ -306,13 +314,23 @@ skillsCommand
306
314
  // (Cursor, OpenCode): drop the rule files 3.0 wrote so the agent does not
307
315
  // load both for the same slug. Only files with Summer's own naming go.
308
316
  if (agent !== "summer" && !process.env.SUMMER_SKILLS_DIR) {
309
- const legacy = legacyRuleFiles(agentSpec(agent), scope, defaultPathContext(), skills.map((skill) => skill.name)) ?? [];
317
+ const names = skills.map((skill) => skill.name);
318
+ const legacy = legacyRuleFiles(agentSpec(agent), scope, defaultPathContext(), names) ?? [];
310
319
  for (const file of legacy) {
311
320
  if (!existsSync(file))
312
321
  continue;
313
322
  rmSync(file, { force: true });
314
323
  console.log(` Removed ${tildeify(file)} (rule file from Summer 3.0; now a skill)`);
315
324
  }
325
+ // 3.1.0 wrote skills into each agent's own folder; agents that read
326
+ // the shared ~/.agents/skills now install there. Drop Summer's copies
327
+ // (dirs with a SKILL.md and a library name) from the old folder.
328
+ for (const dir of legacySkillDirs(agentSpec(agent), scope, defaultPathContext(), names) ?? []) {
329
+ if (!existsSync(join(dir, "SKILL.md")))
330
+ continue;
331
+ rmSync(dir, { recursive: true, force: true });
332
+ console.log(` Removed ${tildeify(dir)} (moved to the shared skills folder)`);
333
+ }
316
334
  }
317
335
  }
318
336
  if (name && skills[0]?.status === "preview") {
@@ -96,6 +96,11 @@ export declare class EngineApiClient {
96
96
  getEngineCapabilities(): EngineCapabilities | undefined;
97
97
  /** Engine version string from the last health read, for skew messages. */
98
98
  getEngineVersion(): string | undefined;
99
+ /** Absolute project directory: the engine's own report from the last health
100
+ * read (newer engines publish project_path), else the explicit --project
101
+ * selection. Undefined when neither is known. Used for the bounded
102
+ * res://.summer/shots/ folder the seeing tools keep on the local disk. */
103
+ getProjectRoot(): string | undefined;
99
104
  /** The projectIdHash this session is bound to (undefined if none was reported
100
105
  * at connect — e.g. no project open yet). */
101
106
  getBoundProjectIdHash(): string | undefined;
@@ -168,11 +173,14 @@ export declare class EngineApiClient {
168
173
  getSceneState(scenePath?: string, options?: {
169
174
  depth?: number;
170
175
  limit?: number;
176
+ root?: string;
171
177
  }): Promise<unknown>;
172
178
  getProjectState(prefix?: string): Promise<unknown>;
173
179
  getDiagnostics(): Promise<unknown>;
174
180
  inspectNode(path: string): Promise<unknown>;
175
- inspectResource(path: string): Promise<unknown>;
181
+ /** A resource a node of the edited scene holds (state:resource takes
182
+ * nodePath + property; resource FILES go through the inspect-resource probe). */
183
+ inspectNodeResource(nodePath: string, property: string): Promise<unknown>;
176
184
  getScriptErrors(path: string): Promise<unknown>;
177
185
  play(scene?: string, determinism?: PlayDeterminism): Promise<unknown>;
178
186
  stop(): Promise<unknown>;
@@ -6,6 +6,7 @@ import { checkEngineHealth, discoverRegistryConnection, engineNotRunningError, e
6
6
  import { asRecord, boolFrom, numberFrom, stringFrom } from "./util/json.js";
7
7
  import { classifyOpsResponse, pollOpToTerminal, } from "./async-op-lifecycle.js";
8
8
  import { pickPlayDeterminism } from "./capabilities/play-determinism.js";
9
+ import { honestSceneReceipt } from "./capabilities/engine-receipt.js";
9
10
  function findSnapshotPayload(value) {
10
11
  const record = asRecord(value);
11
12
  if (!record)
@@ -180,6 +181,13 @@ export class EngineApiClient {
180
181
  getEngineVersion() {
181
182
  return this.lastHealth?.version;
182
183
  }
184
+ /** Absolute project directory: the engine's own report from the last health
185
+ * read (newer engines publish project_path), else the explicit --project
186
+ * selection. Undefined when neither is known. Used for the bounded
187
+ * res://.summer/shots/ folder the seeing tools keep on the local disk. */
188
+ getProjectRoot() {
189
+ return this.lastHealth?.project_path ?? this.selection?.projectPath;
190
+ }
183
191
  /** The projectIdHash this session is bound to (undefined if none was reported
184
192
  * at connect — e.g. no project open yet). */
185
193
  getBoundProjectIdHash() {
@@ -461,7 +469,9 @@ export class EngineApiClient {
461
469
  // different project (identity_mismatch, atomic — before any op applies).
462
470
  const merged = { ...(options ?? {}), ...this.identityOptions() };
463
471
  const body = Object.keys(merged).length ? { ops, options: merged } : { ops };
464
- return this._requestQueued("POST", "/api/ops", body, timeoutMs);
472
+ // scenePersistence.persisted only means SaveScene ran; hand it out as
473
+ // `saved` (engine-receipt.ts honestSceneReceipt).
474
+ return honestSceneReceipt(await this._requestQueued("POST", "/api/ops", body, timeoutMs));
465
475
  }
466
476
  /**
467
477
  * Mutation path for MCP file tools. Unlike legacy executeOps callers, this
@@ -480,13 +490,16 @@ export class EngineApiClient {
480
490
  return this.request("GET", `/api/state/read-file?${query.toString()}`);
481
491
  }
482
492
  async getSceneState(scenePath, options) {
483
- // depth/limit only take effect on targeted (scene=) reads: the engine
493
+ // depth/limit/root only take effect on targeted (scene=) reads: the engine
484
494
  // routes those through the live command queue and forwards every query
485
495
  // param into StateProvider::scene_state (tool_net_thread.cpp
486
496
  // _parse_state_args). An UNtargeted read is answered from a pre-published
487
497
  // snapshot built with the defaults (depth 2, limit 200) and its query
488
498
  // params are ignored — callers who need depth/limit must pass scenePath
489
499
  // (summer_get_scene_tree resolves the current scene path for this).
500
+ // `root` starts the walk at a scene-relative node path; a path the engine
501
+ // cannot resolve falls back to the scene root, so callers compare the
502
+ // returned data.path before trusting a rooted read.
490
503
  const parts = [];
491
504
  if (scenePath)
492
505
  parts.push(`scene=${encodeURIComponent(scenePath)}`);
@@ -494,6 +507,8 @@ export class EngineApiClient {
494
507
  parts.push(`depth=${options.depth}`);
495
508
  if (options?.limit !== undefined)
496
509
  parts.push(`limit=${options.limit}`);
510
+ if (options?.root !== undefined)
511
+ parts.push(`root=${encodeURIComponent(options.root)}`);
497
512
  const query = parts.length ? `?${parts.join("&")}` : "";
498
513
  return this.request("GET", `/api/state/scene${query}`);
499
514
  }
@@ -513,8 +528,10 @@ export class EngineApiClient {
513
528
  async inspectNode(path) {
514
529
  return this.request("GET", `/api/state/inspector?path=${encodeURIComponent(path)}`);
515
530
  }
516
- async inspectResource(path) {
517
- return this.request("GET", `/api/state/resource?path=${encodeURIComponent(path)}`);
531
+ /** A resource a node of the edited scene holds (state:resource takes
532
+ * nodePath + property; resource FILES go through the inspect-resource probe). */
533
+ async inspectNodeResource(nodePath, property) {
534
+ return this.request("GET", `/api/state/resource?nodePath=${encodeURIComponent(nodePath)}&property=${encodeURIComponent(property)}`);
518
535
  }
519
536
  async getScriptErrors(path) {
520
537
  return this.request("GET", `/api/state/script-errors?path=${encodeURIComponent(path)}`);
@@ -19,6 +19,14 @@ export function buildAgentPlaybook(options = {}) {
19
19
  "For lighting, mood, environment, or emissive materials, use summer_screenshot target:'scene' framing:'camera' — it renders through the scene's OWN camera with its REAL WorldEnvironment. The preset framings (iso/top/...) substitute a flat preview environment and CANNOT verify lighting.",
20
20
  "When executing multiple batches, screenshot between them — catch the wrong turn at batch 2, not batch 7.",
21
21
  "If something looks wrong in the screenshot or the diff, investigate and fix before proceeding. Do not stack more work on a broken base.",
22
+ "A game made from a planning board (the brief gives its project id): read the board with summer_get_board at the start of each build step and compare your screenshot against its images (palette, shapes and proportions, camera angle, composition); fix the biggest difference.",
23
+ ],
24
+ // ------------------------------------------------------------------
25
+ // SEEING — judge a 3D environment the way a player and an artist do.
26
+ // ------------------------------------------------------------------
27
+ seeing: [
28
+ "Environment work ('make it beautiful', level dressing, lighting passes): bookmark hero views with summer_frame_shot (establishing / eye_level / low_angle / detail / corridor; walls, or a camera behind a one-sided wall, reject a pose; props frame it), then after every change one summer_shot_sheet of those bookmarks (compare_previous:true for previous / now / difference; only it or update_previous:true replaces a bookmark's baseline), summer_debug_views on the weakest shot (lighting, unshaded, normals, overdraw, wireframe) and summer_zoom (region or mark N) into each problem. summer_frame_nodes fits nodes with the REAL environment. Images come back inline, one grid per call; the verifying-scenes skill carries the loop and the beauty rubric. Preview tools.",
29
+ "Before calling a 3D scene done: summer_scene_audit (read-only, one call, at most 5 KB) lists what a camera may miss — see-through gaps, floor holes, floating or clipping props, facing look items, UV stretch, z-fighting, lights — sorted by severity with the node path, world position, evidence and next tool. Look at every error and look item up close (render:'sheet', or summer_frame_nodes / summer_zoom) and decide facings yourself; the audit never auto-fixes. Preview tool.",
22
30
  ],
23
31
  // ------------------------------------------------------------------
24
32
  // LIBRARY FEEDBACK — report how the library entries you used worked out.
@@ -80,6 +88,28 @@ export function buildAgentPlaybook(options = {}) {
80
88
  },
81
89
  },
82
90
  // ------------------------------------------------------------------
91
+ // 3D PLACEMENT — question -> tool. These bounded spatial tools ship
92
+ // today; an agent that never hears of them falls back to hand math.
93
+ // ------------------------------------------------------------------
94
+ placement3d: {
95
+ first: "For any 3D placement (props, kit pieces, walls, ledges, alcoves), call summer_search_library and read skill/spatial-placement (and skill/world-building-3d) before placing. Observe first still applies: snapshot, then place, then verify.",
96
+ questionToTool: [
97
+ "'How big is this kit piece, where is its origin, which side is its back, where are its pipe ends?' -> summer_inspect_asset (measures the file without adding it to a scene). Measure every piece type before placing it; never guess facing.",
98
+ "'Place it at this pose' -> summer_instantiate_scene with position / rotation_degrees (one call per piece). Many pieces -> one summer_batch of InstantiateScene ops carrying position / rotation_degrees, with receipt:'summary'.",
99
+ "'Next module edge to edge, next storey on top' -> summer_place_adjacent. 'Mount it on the wall' -> summer_attach_to_surface (backAxis from summer_inspect_asset). 'Clamps every 0.45 m' -> summer_repeat_along. 'Pipe to pipe' -> summer_connect_ports.",
100
+ "'What surface is in front of this point?' -> summer_raycast. 'Gap or overlap between these two, is the facade front flush?' -> summer_measure.",
101
+ "'What is around it, which side is blocked, is it flush against the wall?' -> summer_starcast (read-only, 26 directional casts from one node's bounds: open|blocked per direction with distance and object, contact-or-overlap, grounded). Call it BEFORE and AFTER placing; pass directionSpace:'local' for rotated pieces, wall gaps and alcoves.",
102
+ "'Will it fit here?' -> summer_test_placement: ghost-test a candidate global pose for overlap and floor gap without moving anything. Use it before committing a pose in a tight or dense spot.",
103
+ "'Seat it on the floor, table or wall' -> summer_snap_to_surface: move along a ray to the first surface at a requested gap (default straight down).",
104
+ "'Line these up / space them evenly' -> summer_align_distribute_3d: align (min/center/max) or equal-space 2-16 pieces along one axis from their visible AABBs.",
105
+ "'Did exactly that change, and does it look right?' -> summer_world_snapshot before + summer_snapshot_diff after, then summer_screenshot.",
106
+ "'What is wrong with this scene, and where?' -> summer_scene_audit after each build stage (save first; it reads the saved file in a private copy, so the tab stays clean): see-through holes in facades, exposed band ends and seams, open duct or pipe ends, floor gaps, floating/sunken/clipping props, facing look items, UV stretch, z-fighting (with the axis to nudge), lights; every check works from geometry, engine data and materials, never names or kit metadata. At most 5 KB, sorted error/warn/look; page with offset, narrow with checks/root/min_severity, render:'sheet' frames the first 6. Frame every error and look item before calling the scene done; accept:[{key, reason}] the look items you judged fine so later audits hide them.",
107
+ "'Can the player get from A to B?' -> summer_navigation_probe (reachability on the scene's navigation map).",
108
+ ],
109
+ evidence: "physics evidence comes from colliders; visual_aabb is a broad-phase fallback for mesh-only geometry. Read the evidence field before trusting a contact or a gap.",
110
+ status: "skill/spatial-placement, skill/world-building-3d and summer_starcast / summer_test_placement / summer_snap_to_surface / summer_align_distribute_3d / summer_navigation_probe / summer_world_snapshot / summer_snapshot_diff / summer_inspect_asset / summer_place_adjacent / summer_attach_to_surface / summer_repeat_along / summer_connect_ports / summer_raycast / summer_measure / summer_scene_audit are preview: verify their receipts with a screenshot. An engine build that predates an op answers engine_lacks_op naming the fallback.",
111
+ },
112
+ // ------------------------------------------------------------------
83
113
  // PHYSICAL INVARIANTS — hold after EVERY placement/import.
84
114
  // ------------------------------------------------------------------
85
115
  physicalInvariants: [
@@ -96,6 +126,15 @@ export function buildAgentPlaybook(options = {}) {
96
126
  "Engine calls are cheap; YOUR context is not: prefer summer_world_snapshot + summer_snapshot_diff (compact, capped, fingerprinted) over repeated full-tree dumps.",
97
127
  ],
98
128
  // ------------------------------------------------------------------
129
+ // READ IN PARTS — a result too big for the host is a result you never see.
130
+ // ------------------------------------------------------------------
131
+ readingInParts: [
132
+ "summer_get_project_context is compact by default (project, scene path + sceneSummary, health summary, projectMemory, warnings). Add include:['scene_tree'] / ['capabilities'] / ['settings'] only when you need that block; `omitted` names what was left out.",
133
+ "summer_world_snapshot lists at most 200 nodes by default. Read one subtree with path_prefix ('House3', 'Lane2/Props'), narrow with classes and fields (['pos','aabb']), page with offset/next_offset; matched_counts counts the subtree's classes. counts and the snapshot_id diff baseline always cover the whole scene.",
134
+ "summer_inspect_node fields:['transform','global_transform','scene_file_path'] reads where a node is (locally and in the world) and which scene it instances in a few hundred bytes instead of about 5 KB.",
135
+ "Search, then read the part: summer_grep (regex over project files; context_lines, path, glob, max_results) finds the lines; summer_read_file then reads just that part — offset/limit for text (data.window.next_offset continues), json_path / keys / keys_only for JSON (e.g. json_path:'pieces', keys:['wall_*'] on a kit manifest).",
136
+ ],
137
+ // ------------------------------------------------------------------
99
138
  // Verification ladder (climb only as high as the change demands).
100
139
  // ------------------------------------------------------------------
101
140
  verificationLadder: {
@@ -156,6 +195,8 @@ export function buildAgentPlaybook(options = {}) {
156
195
  "For live scene hierarchy and inspector changes, prefer scene tools. Guarded text writes support .tscn/.tres, and the engine schedules editor reloads after they land.",
157
196
  "Write GDScript by default; use C# only if the project already uses it.",
158
197
  "Never remove multiple top-level nodes unless the user explicitly requests destructive edits.",
198
+ "summer_snap_to_surface lifts a prop that is sunk into its support and settles it (receipt.recovery); when it still fails, read blocking and next_step in the failure instead of guessing a new position.",
199
+ "To swap one piece for another use summer_replace_node: it keeps parent, index, name, transform and added children, and proves the swap from the saved file. Trust persisted:true only; failure_reason not_persisted means the editor shows a change the file does not hold.",
159
200
  "Never change priority: locked .summer memory, voice IDs, canon, or provider bindings without explicit user confirmation.",
160
201
  ],
161
202
  liveEngineFlow: [
@@ -175,6 +216,11 @@ export function buildAgentPlaybook(options = {}) {
175
216
  "SimulateInput (drive the RUNNING game — summer_play first): summer_batch ops:[{op:'SimulateInput', type:'action', action:'jump', pressed:true}], sent ALONE as the only op. failure_reason 'not_running' = start the game first; 'unsupported' = the running game build predates the handler — use RunVerification instead.",
176
217
  "SINGLE-OP CONTRACT: the engine rejects any multi-op batch containing SaveScene, InstantiateScene, ReplaceNode, SimulateInput, ViewportSnapshot, GameSnapshot, GetRuntimeSceneTree, GetRuntimeNode, the runtime-control ops (SetRuntimeProp, CallRuntimeMethod, SpawnRuntimeScene, FreeRuntimeNode, RuntimeAnimation, RuntimeAnimationTree, GetRuntimeBones, GamePause, GameStep, GameSpeed, SimulateInputScript, InputRecordStart, InputRecordStop, InputReplay, GameProbe), Run*/Import* or Git* ops (failure_reason 'unsupported_transport', nothing executes). summer_batch splits these into sequential requests for you; when composing raw batches keep them as their own call anyway.",
177
218
  "WriteFile and ReplaceText are rejected here by design — use summer_write_file / summer_replace_text so project identity, content guards and same-file ordering are enforced.",
219
+ "A raw ReplaceNode with scene is rejected here too: the engine op shows the new scene but saves the OLD scene reference. summer_replace_node does the swap with ops that persist and verifies the saved file (persisted:true, or failure_reason not_persisted).",
220
+ "A raw ConnectSignal is rejected here: the engine connects without CONNECT_PERSIST, so the saved scene never holds it. summer_connect_signal connects with the flag and verifies the [connection] line in the saved file.",
221
+ "ReparentNode in summer_batch keeps the moved node's subtree: the engine op re-owns only the moved node, so the tool re-owns each scene-owned descendant in place and verifies every moved node in the saved file (persisted:true / verified:true, or failure_reason not_persisted).",
222
+ "scenePersistence.saved only says the SaveScene ran. Trust verified:true, which a tool sets only after reading the saved file back.",
223
+ "Searching project files needs no raw op: summer_grep wraps the engine's Grep op and adds context lines and a result cap.",
178
224
  "You do not need an engine op to run a shell command: your own host already has a shell. The engine binary that runs project scripts is at OS.get_executable_path() (on macOS, /Applications/Summer.app/Contents/MacOS/Summer); see the summer-cli headless-scripting skill.",
179
225
  "These are runtime ops, not scene mutations — the batch undo group is a harmless no-op for them.",
180
226
  ],
@@ -0,0 +1,55 @@
1
+ import { type Severity } from "./args.js";
2
+ import type { AuditIssue } from "./judge.js";
3
+ export declare const ACCEPT_FILE: string;
4
+ export declare const ACCEPT_MAX_ENTRIES = 500;
5
+ export declare const ACCEPT_SCORE_CHANGE = 0.25;
6
+ export interface AcceptEntry {
7
+ key: string;
8
+ scene: string;
9
+ reason: string;
10
+ check: string;
11
+ severity: Severity;
12
+ score: number;
13
+ why: string;
14
+ at: string;
15
+ }
16
+ export declare function issueKey(issue: Pick<AuditIssue, "check" | "path" | "pos">): string;
17
+ export declare function validIssueKey(key: string): boolean;
18
+ /** The file's entries (the kernel echoes the parsed file as result.accept). */
19
+ export declare function parseAcceptFile(raw: unknown): AcceptEntry[];
20
+ /** Why an accepted item shows again, or null while it still holds. */
21
+ export declare function staleReason(entry: AcceptEntry, issue: AuditIssue): string | null;
22
+ export interface AcceptOutcome {
23
+ /** The new file content (null: nothing to write). */
24
+ entries: AcceptEntry[] | null;
25
+ added: string[];
26
+ refused: Array<{
27
+ key: string;
28
+ why: string;
29
+ }>;
30
+ }
31
+ /** Merge accept requests for THIS run's issues into the file's entries. */
32
+ export declare function mergeAccepts(existing: readonly AcceptEntry[], requests: ReadonlyArray<{
33
+ key: string;
34
+ reason: string;
35
+ }>, issues: readonly AuditIssue[], scene: string, now?: Date): AcceptOutcome;
36
+ export interface AcceptFilter {
37
+ shown: AuditIssue[];
38
+ accepted: Array<{
39
+ issue: AuditIssue;
40
+ entry: AcceptEntry;
41
+ }>;
42
+ stale: Array<{
43
+ issue: AuditIssue;
44
+ why: string;
45
+ }>;
46
+ }
47
+ /** Split issues into shown and accepted (stale entries show again). */
48
+ export declare function applyAccepts(issues: readonly AuditIssue[], entries: readonly AcceptEntry[], scene: string): AcceptFilter;
49
+ export declare class AcceptStoreError extends Error {
50
+ readonly reason: "no_project" | "unsafe_path";
51
+ constructor(reason: "no_project" | "unsafe_path", message: string);
52
+ }
53
+ /** Write the entries to <project>/.summer/audit-accept.json: refuses symlinks
54
+ * at the folder and the file, writes a temp file and renames it. */
55
+ export declare function writeAcceptFile(projectRoot: string | undefined, entries: readonly AcceptEntry[]): Promise<string>;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Accepted audit items: res://.summer/audit-accept.json.
3
+ *
4
+ * Without it an agent re-judges the same look items (hidden contact faces,
5
+ * overlapping trim at an inside corner) on every audit. An agent that has
6
+ * looked at an item and judged it fine passes accept: [{key, reason}]; the
7
+ * MCP side writes the entry here (the kernel only reads it, and the audit
8
+ * writes nothing else), and later audits count the item but hide it until
9
+ * its evidence changes materially.
10
+ *
11
+ * - Key: check:path@x,y,z, the world position rounded to 0.1 m (node names
12
+ * never contain ":" or "@"). A moved piece is a new item.
13
+ * - Evidence: the severity and the issue's magnitude (score) when accepted.
14
+ * A higher severity, or a score that moves by more than 25% (and 0.005),
15
+ * makes the entry stale: the item shows again, marked accept_stale.
16
+ * - Errors cannot be accepted. Entries are per scene; the file keeps the
17
+ * newest 500.
18
+ */
19
+ import { lstat, mkdir, rename, writeFile } from "node:fs/promises";
20
+ import { existsSync } from "node:fs";
21
+ import { isAbsolute, join, resolve } from "node:path";
22
+ import { randomBytes } from "node:crypto";
23
+ import { ISSUE_KEY_PATTERN } from "./args.js";
24
+ export const ACCEPT_FILE = join(".summer", "audit-accept.json");
25
+ export const ACCEPT_MAX_ENTRIES = 500;
26
+ export const ACCEPT_SCORE_CHANGE = 0.25;
27
+ const SEVERITY_RANK = { error: 0, warn: 1, look: 2 };
28
+ const tenth = (n) => (Math.round(n * 10) / 10 || 0).toFixed(1).replace(/\.0$/, "");
29
+ export function issueKey(issue) {
30
+ return `${issue.check}:${issue.path}@${issue.pos.map(tenth).join(",")}`;
31
+ }
32
+ export function validIssueKey(key) {
33
+ return ISSUE_KEY_PATTERN.test(key);
34
+ }
35
+ /** The file's entries (the kernel echoes the parsed file as result.accept). */
36
+ export function parseAcceptFile(raw) {
37
+ const doc = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
38
+ const list = Array.isArray(doc.entries) ? doc.entries : [];
39
+ const out = [];
40
+ for (const e of list.slice(0, ACCEPT_MAX_ENTRIES * 2)) {
41
+ if (!e || typeof e !== "object")
42
+ continue;
43
+ const r = e;
44
+ const severity = r.severity === "warn" || r.severity === "look" ? r.severity : null;
45
+ if (typeof r.key !== "string" || !validIssueKey(r.key) || typeof r.scene !== "string" || !severity)
46
+ continue;
47
+ out.push({
48
+ key: r.key,
49
+ scene: r.scene,
50
+ reason: String(r.reason ?? "").slice(0, 200),
51
+ check: String(r.check ?? r.key.split(":")[0]),
52
+ severity,
53
+ score: typeof r.score === "number" && Number.isFinite(r.score) ? r.score : 0,
54
+ why: String(r.why ?? "").slice(0, 160),
55
+ at: String(r.at ?? ""),
56
+ });
57
+ }
58
+ return out;
59
+ }
60
+ /** Why an accepted item shows again, or null while it still holds. */
61
+ export function staleReason(entry, issue) {
62
+ if (SEVERITY_RANK[issue.severity] < SEVERITY_RANK[entry.severity])
63
+ return `severity rose from ${entry.severity} to ${issue.severity}`;
64
+ const delta = Math.abs(issue.score - entry.score);
65
+ if (delta > Math.max(ACCEPT_SCORE_CHANGE * Math.abs(entry.score), 0.005)) {
66
+ return `evidence changed: magnitude ${Math.round(entry.score * 1000) / 1000} -> ${Math.round(issue.score * 1000) / 1000}`;
67
+ }
68
+ return null;
69
+ }
70
+ /** Merge accept requests for THIS run's issues into the file's entries. */
71
+ export function mergeAccepts(existing, requests, issues, scene, now = new Date()) {
72
+ if (!requests.length)
73
+ return { entries: null, added: [], refused: [] };
74
+ const byKey = new Map(issues.map((i) => [issueKey(i), i]));
75
+ const added = [];
76
+ const refused = [];
77
+ const fresh = [];
78
+ for (const req of requests) {
79
+ const issue = byKey.get(req.key);
80
+ if (!issue) {
81
+ refused.push({ key: req.key, why: "no issue with this key in this audit (keys change when a piece moves 5 cm or more)" });
82
+ continue;
83
+ }
84
+ if (issue.severity === "error") {
85
+ refused.push({ key: req.key, why: "errors cannot be accepted: fix it" });
86
+ continue;
87
+ }
88
+ fresh.push({ key: req.key, scene, reason: req.reason.trim().slice(0, 200), check: issue.check, severity: issue.severity, score: issue.score, why: issue.why.slice(0, 160), at: now.toISOString() });
89
+ added.push(req.key);
90
+ }
91
+ if (!fresh.length)
92
+ return { entries: null, added, refused };
93
+ const keep = existing.filter((e) => !(e.scene === scene && fresh.some((f) => f.key === e.key)));
94
+ const entries = [...keep, ...fresh].slice(-ACCEPT_MAX_ENTRIES);
95
+ return { entries, added, refused };
96
+ }
97
+ /** Split issues into shown and accepted (stale entries show again). */
98
+ export function applyAccepts(issues, entries, scene) {
99
+ const mine = new Map(entries.filter((e) => e.scene === scene).map((e) => [e.key, e]));
100
+ const shown = [];
101
+ const accepted = [];
102
+ const stale = [];
103
+ for (const issue of issues) {
104
+ const entry = mine.get(issueKey(issue));
105
+ if (!entry || issue.severity === "error") {
106
+ shown.push(issue);
107
+ continue;
108
+ }
109
+ const why = staleReason(entry, issue);
110
+ if (why) {
111
+ stale.push({ issue, why });
112
+ shown.push(issue);
113
+ }
114
+ else {
115
+ accepted.push({ issue, entry });
116
+ }
117
+ }
118
+ return { shown, accepted, stale };
119
+ }
120
+ export class AcceptStoreError extends Error {
121
+ reason;
122
+ constructor(reason, message) {
123
+ super(message);
124
+ this.reason = reason;
125
+ this.name = "AcceptStoreError";
126
+ }
127
+ }
128
+ /** Write the entries to <project>/.summer/audit-accept.json: refuses symlinks
129
+ * at the folder and the file, writes a temp file and renames it. */
130
+ export async function writeAcceptFile(projectRoot, entries) {
131
+ if (!projectRoot || !isAbsolute(projectRoot) || !existsSync(join(projectRoot, "project.godot"))) {
132
+ throw new AcceptStoreError("no_project", "The project folder is not known to this session (no project.godot), so accepted items cannot be saved.");
133
+ }
134
+ const root = resolve(projectRoot);
135
+ const dir = join(root, ".summer");
136
+ await mkdir(dir, { recursive: true });
137
+ const dirInfo = await lstat(dir);
138
+ if (dirInfo.isSymbolicLink() || !dirInfo.isDirectory())
139
+ throw new AcceptStoreError("unsafe_path", `${dir} is not a real folder.`);
140
+ const file = join(root, ACCEPT_FILE);
141
+ if (existsSync(file) && (await lstat(file)).isSymbolicLink())
142
+ throw new AcceptStoreError("unsafe_path", `${file} is a symlink; refusing to write through it.`);
143
+ const tmp = `${file}.${randomBytes(6).toString("hex")}.tmp`;
144
+ await writeFile(tmp, JSON.stringify({ version: 1, note: "summer_scene_audit accepted items (written by accept:[{key, reason}])", entries }, null, 1));
145
+ await rename(tmp, file);
146
+ return file;
147
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Argument shape for summer_scene_audit — ONE copy for both faces (the MCP
3
+ * registration in src/mcp/tools/audit-tools.ts and `summer tool scene-audit`
4
+ * in tool-dispatch.ts). The descriptions are the docs agents read.
5
+ *
6
+ * Strings that name scene content are validated again in audit.ts with the
7
+ * seeing tools' strict patterns before anything is sent; they reach the
8
+ * engine only as JSON data in config.json, never as source.
9
+ */
10
+ import { z } from "zod";
11
+ export declare const AUDIT_CHECKS: readonly ["through_hole", "floor_gap", "floating", "sunken", "interpenetration", "exposed_edge", "open_fixture_end", "orientation", "uv_stretch", "duplicate", "z_fight", "depth_step", "lights", "transform", "resource"];
12
+ export type AuditCheck = (typeof AUDIT_CHECKS)[number];
13
+ export declare const SEVERITIES: readonly ["error", "warn", "look"];
14
+ export type Severity = (typeof SEVERITIES)[number];
15
+ export declare const AUDIT_DEFAULT_LIMIT = 15;
16
+ export declare const AUDIT_MAX_LIMIT = 50;
17
+ /** Editor time the checks may take before the slow ones stop early (partial). */
18
+ export declare const AUDIT_DEFAULT_BUDGET_MS = 3000;
19
+ export declare const AUDIT_MIN_BUDGET_MS = 250;
20
+ export declare const AUDIT_MAX_BUDGET_MS = 60000;
21
+ export declare const AUDIT_MAX_ACCEPT = 50;
22
+ /** An issue key: check:node path@x,y,z (world position rounded to 0.1 m;
23
+ * node names never contain ":" or "@"). */
24
+ export declare const ISSUE_KEY_PATTERN: RegExp;
25
+ export declare const sceneAuditShape: {
26
+ scenePath: z.ZodOptional<z.ZodString>;
27
+ checks: z.ZodOptional<z.ZodArray<z.ZodEnum<["through_hole", "floor_gap", "floating", "sunken", "interpenetration", "exposed_edge", "open_fixture_end", "orientation", "uv_stretch", "duplicate", "z_fight", "depth_step", "lights", "transform", "resource"]>, "many">>;
28
+ root: z.ZodOptional<z.ZodString>;
29
+ min_severity: z.ZodOptional<z.ZodEnum<["error", "warn", "look"]>>;
30
+ offset: z.ZodOptional<z.ZodNumber>;
31
+ limit: z.ZodOptional<z.ZodNumber>;
32
+ budget_ms: z.ZodOptional<z.ZodNumber>;
33
+ accept: z.ZodOptional<z.ZodArray<z.ZodObject<{
34
+ key: z.ZodString;
35
+ reason: z.ZodString;
36
+ }, "strict", z.ZodTypeAny, {
37
+ reason: string;
38
+ key: string;
39
+ }, {
40
+ reason: string;
41
+ key: string;
42
+ }>, "many">>;
43
+ show_accepted: z.ZodOptional<z.ZodBoolean>;
44
+ render: z.ZodOptional<z.ZodEnum<["sheet", "none"]>>;
45
+ };
46
+ export declare const sceneAuditArgsSchema: z.ZodObject<{
47
+ scenePath: z.ZodOptional<z.ZodString>;
48
+ checks: z.ZodOptional<z.ZodArray<z.ZodEnum<["through_hole", "floor_gap", "floating", "sunken", "interpenetration", "exposed_edge", "open_fixture_end", "orientation", "uv_stretch", "duplicate", "z_fight", "depth_step", "lights", "transform", "resource"]>, "many">>;
49
+ root: z.ZodOptional<z.ZodString>;
50
+ min_severity: z.ZodOptional<z.ZodEnum<["error", "warn", "look"]>>;
51
+ offset: z.ZodOptional<z.ZodNumber>;
52
+ limit: z.ZodOptional<z.ZodNumber>;
53
+ budget_ms: z.ZodOptional<z.ZodNumber>;
54
+ accept: z.ZodOptional<z.ZodArray<z.ZodObject<{
55
+ key: z.ZodString;
56
+ reason: z.ZodString;
57
+ }, "strict", z.ZodTypeAny, {
58
+ reason: string;
59
+ key: string;
60
+ }, {
61
+ reason: string;
62
+ key: string;
63
+ }>, "many">>;
64
+ show_accepted: z.ZodOptional<z.ZodBoolean>;
65
+ render: z.ZodOptional<z.ZodEnum<["sheet", "none"]>>;
66
+ }, "strict", z.ZodTypeAny, {
67
+ limit?: number | undefined;
68
+ root?: string | undefined;
69
+ scenePath?: string | undefined;
70
+ accept?: {
71
+ reason: string;
72
+ key: string;
73
+ }[] | undefined;
74
+ render?: "none" | "sheet" | undefined;
75
+ checks?: ("through_hole" | "floor_gap" | "floating" | "sunken" | "interpenetration" | "exposed_edge" | "open_fixture_end" | "orientation" | "uv_stretch" | "duplicate" | "z_fight" | "depth_step" | "lights" | "transform" | "resource")[] | undefined;
76
+ min_severity?: "error" | "warn" | "look" | undefined;
77
+ offset?: number | undefined;
78
+ budget_ms?: number | undefined;
79
+ show_accepted?: boolean | undefined;
80
+ }, {
81
+ limit?: number | undefined;
82
+ root?: string | undefined;
83
+ scenePath?: string | undefined;
84
+ accept?: {
85
+ reason: string;
86
+ key: string;
87
+ }[] | undefined;
88
+ render?: "none" | "sheet" | undefined;
89
+ checks?: ("through_hole" | "floor_gap" | "floating" | "sunken" | "interpenetration" | "exposed_edge" | "open_fixture_end" | "orientation" | "uv_stretch" | "duplicate" | "z_fight" | "depth_step" | "lights" | "transform" | "resource")[] | undefined;
90
+ min_severity?: "error" | "warn" | "look" | undefined;
91
+ offset?: number | undefined;
92
+ budget_ms?: number | undefined;
93
+ show_accepted?: boolean | undefined;
94
+ }>;
95
+ export type SceneAuditArgs = z.output<typeof sceneAuditArgsSchema>;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Argument shape for summer_scene_audit — ONE copy for both faces (the MCP
3
+ * registration in src/mcp/tools/audit-tools.ts and `summer tool scene-audit`
4
+ * in tool-dispatch.ts). The descriptions are the docs agents read.
5
+ *
6
+ * Strings that name scene content are validated again in audit.ts with the
7
+ * seeing tools' strict patterns before anything is sent; they reach the
8
+ * engine only as JSON data in config.json, never as source.
9
+ */
10
+ import { z } from "zod";
11
+ export const AUDIT_CHECKS = [
12
+ "through_hole",
13
+ "floor_gap",
14
+ "floating",
15
+ "sunken",
16
+ "interpenetration",
17
+ "exposed_edge",
18
+ "open_fixture_end",
19
+ "orientation",
20
+ "uv_stretch",
21
+ "duplicate",
22
+ "z_fight",
23
+ "depth_step",
24
+ "lights",
25
+ "transform",
26
+ "resource",
27
+ ];
28
+ export const SEVERITIES = ["error", "warn", "look"];
29
+ export const AUDIT_DEFAULT_LIMIT = 15;
30
+ export const AUDIT_MAX_LIMIT = 50;
31
+ /** Editor time the checks may take before the slow ones stop early (partial). */
32
+ export const AUDIT_DEFAULT_BUDGET_MS = 3000;
33
+ export const AUDIT_MIN_BUDGET_MS = 250;
34
+ export const AUDIT_MAX_BUDGET_MS = 60000;
35
+ export const AUDIT_MAX_ACCEPT = 50;
36
+ /** An issue key: check:node path@x,y,z (world position rounded to 0.1 m;
37
+ * node names never contain ":" or "@"). */
38
+ export const ISSUE_KEY_PATTERN = /^[a-z_]{3,24}:[^:@\n\r"\\]{1,300}@-?\d+(?:\.\d)?,-?\d+(?:\.\d)?,-?\d+(?:\.\d)?$/;
39
+ export const sceneAuditShape = {
40
+ scenePath: z
41
+ .string()
42
+ .optional()
43
+ .describe('Scene to audit, e.g. "res://levels/town.tscn" (the SAVED file). Omit for the scene open in the editor.'),
44
+ checks: z
45
+ .array(z.enum(AUDIT_CHECKS))
46
+ .min(1)
47
+ .max(AUDIT_CHECKS.length)
48
+ .optional()
49
+ .describe("Only these checks (default: all 15). through_hole, floor_gap, floating, sunken, interpenetration, exposed_edge, open_fixture_end, orientation, uv_stretch, duplicate, z_fight, depth_step, lights, transform, resource. Fewer checks run faster (duplicate, lights, transform and resource need no physics); rerun a check budget_ms left partial on its own."),
50
+ root: z
51
+ .string()
52
+ .optional()
53
+ .describe('Report only issues under this node (subtree), e.g. "Block3". The whole scene is still loaded so walls and floors outside it count as surroundings.'),
54
+ min_severity: z
55
+ .enum(SEVERITIES)
56
+ .optional()
57
+ .describe('"error" (only errors), "warn" (errors and warnings) or "look" (default: everything, including look items that only ask you to look).'),
58
+ offset: z.number().int().min(0).max(100000).optional().describe("Skip this many issues of the sorted, filtered list (paging; the result names the next offset)."),
59
+ limit: z
60
+ .number()
61
+ .int()
62
+ .min(1)
63
+ .max(AUDIT_MAX_LIMIT)
64
+ .optional()
65
+ .describe(`Issues per page (default ${AUDIT_DEFAULT_LIMIT}, max ${AUDIT_MAX_LIMIT}). The result is capped at 5 KB; a page that does not fit is cut and says so.`),
66
+ budget_ms: z
67
+ .number()
68
+ .int()
69
+ .min(AUDIT_MIN_BUDGET_MS)
70
+ .max(AUDIT_MAX_BUDGET_MS)
71
+ .optional()
72
+ .describe(`Editor time for the whole audit (default ${AUDIT_DEFAULT_BUDGET_MS} ms, ${AUDIT_MIN_BUDGET_MS}-${AUDIT_MAX_BUDGET_MS}). Each check gets a share weighted by its usual cost (unused time passes on); a check past its share stops and counts.<check>.partial gives the share it covered. Under load, rerun the partial checks with checks:[...] or a larger budget.`),
73
+ accept: z
74
+ .array(z
75
+ .object({
76
+ key: z.string().min(8).max(360).describe("The issue's key from a previous result (issues[].key)."),
77
+ reason: z.string().min(3).max(200).describe("Why it is fine, e.g. \"coplanar faces hidden behind a sign\"."),
78
+ })
79
+ .strict())
80
+ .max(AUDIT_MAX_ACCEPT)
81
+ .optional()
82
+ .describe(`Accept look or warn items you judged fine (up to ${AUDIT_MAX_ACCEPT} per call): [{key, reason}] with keys from this scene's issues. They are written to res://.summer/audit-accept.json (the only file the audit writes), then counted (counts.<check>.accepted) but hidden on every later audit, until their evidence changes materially (severity rises, or the measured size moves by over 25%): then they show again with accept_stale. Errors cannot be accepted.`),
83
+ show_accepted: z.boolean().optional().describe("List accepted items too (each with its accepted reason). Default false: hidden, only counted."),
84
+ render: z
85
+ .enum(["sheet", "none"])
86
+ .optional()
87
+ .describe('"sheet": also return ONE inline image of the first 6 issues of this page framed up close from their open side, each tile labelled with its issue number. Default "none" (no image).'),
88
+ };
89
+ export const sceneAuditArgsSchema = z.object(sceneAuditShape).strict();