summer-engine 3.0.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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +27 -5
- package/.codex-plugin/plugin.json +28 -6
- package/.cursor-plugin/plugin.json +27 -5
- package/.factory-plugin/plugin.json +27 -5
- package/.opencode/plugins/summer.js +1 -1
- package/CHANGELOG.md +120 -0
- package/README.md +22 -7
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/setup.js +6 -19
- package/dist/cli/commands/skills.js +57 -32
- package/dist/core/api-client.d.ts +9 -1
- package/dist/core/api-client.js +21 -4
- package/dist/core/capabilities/agent-playbook.js +46 -0
- package/dist/core/capabilities/audit/accept.d.ts +55 -0
- package/dist/core/capabilities/audit/accept.js +147 -0
- package/dist/core/capabilities/audit/args.d.ts +95 -0
- package/dist/core/capabilities/audit/args.js +89 -0
- package/dist/core/capabilities/audit/audit.d.ts +56 -0
- package/dist/core/capabilities/audit/audit.js +287 -0
- package/dist/core/capabilities/audit/frame.d.ts +14 -0
- package/dist/core/capabilities/audit/frame.js +35 -0
- package/dist/core/capabilities/audit/gaps.d.ts +96 -0
- package/dist/core/capabilities/audit/gaps.js +257 -0
- package/dist/core/capabilities/audit/judge.d.ts +162 -0
- package/dist/core/capabilities/audit/judge.js +1134 -0
- package/dist/core/capabilities/audit/math.d.ts +110 -0
- package/dist/core/capabilities/audit/math.js +263 -0
- package/dist/core/capabilities/audit/summary.d.ts +74 -0
- package/dist/core/capabilities/audit/summary.js +212 -0
- package/dist/core/capabilities/board.d.ts +31 -0
- package/dist/core/capabilities/board.js +43 -0
- package/dist/core/capabilities/camera-view.d.ts +4 -2
- package/dist/core/capabilities/camera-view.js +6 -3
- package/dist/core/capabilities/connect-signal.d.ts +85 -0
- package/dist/core/capabilities/connect-signal.js +285 -0
- package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
- package/dist/core/capabilities/engine-fallbacks.js +5 -0
- package/dist/core/capabilities/engine-ops.d.ts +5 -1
- package/dist/core/capabilities/engine-ops.js +0 -3
- package/dist/core/capabilities/engine-receipt.d.ts +12 -0
- package/dist/core/capabilities/engine-receipt.js +26 -0
- package/dist/core/capabilities/file-read.d.ts +64 -0
- package/dist/core/capabilities/file-read.js +325 -0
- package/dist/core/capabilities/game-task-plan.js +4 -2
- package/dist/core/capabilities/grep.d.ts +60 -0
- package/dist/core/capabilities/grep.js +190 -0
- package/dist/core/capabilities/inspect-node.d.ts +56 -0
- package/dist/core/capabilities/inspect-node.js +225 -0
- package/dist/core/capabilities/inspect-resource.d.ts +61 -0
- package/dist/core/capabilities/inspect-resource.js +376 -0
- package/dist/core/capabilities/math3d.d.ts +28 -0
- package/dist/core/capabilities/math3d.js +88 -0
- package/dist/core/capabilities/motion-generation.d.ts +62 -0
- package/dist/core/capabilities/motion-generation.js +138 -0
- package/dist/core/capabilities/placement-batch.d.ts +102 -0
- package/dist/core/capabilities/placement-batch.js +483 -0
- package/dist/core/capabilities/placement-math.d.ts +70 -0
- package/dist/core/capabilities/placement-math.js +209 -0
- package/dist/core/capabilities/placement-script.d.ts +42 -0
- package/dist/core/capabilities/placement-script.js +1096 -0
- package/dist/core/capabilities/placement.d.ts +485 -0
- package/dist/core/capabilities/placement.js +1731 -0
- package/dist/core/capabilities/project-context.d.ts +33 -2
- package/dist/core/capabilities/project-context.js +87 -4
- package/dist/core/capabilities/replace-node.d.ts +91 -0
- package/dist/core/capabilities/replace-node.js +423 -0
- package/dist/core/capabilities/runtime-control.d.ts +20 -4
- package/dist/core/capabilities/runtime-control.js +67 -2
- package/dist/core/capabilities/scene-batch.d.ts +63 -0
- package/dist/core/capabilities/scene-batch.js +394 -0
- package/dist/core/capabilities/scene-readback.d.ts +36 -0
- package/dist/core/capabilities/scene-readback.js +54 -0
- package/dist/core/capabilities/seeing/args.d.ts +348 -0
- package/dist/core/capabilities/seeing/args.js +178 -0
- package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
- package/dist/core/capabilities/seeing/candidates.js +282 -0
- package/dist/core/capabilities/seeing/math.d.ts +134 -0
- package/dist/core/capabilities/seeing/math.js +269 -0
- package/dist/core/capabilities/seeing/probe.d.ts +62 -0
- package/dist/core/capabilities/seeing/probe.js +188 -0
- package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
- package/dist/core/capabilities/seeing/scoring.js +639 -0
- package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
- package/dist/core/capabilities/seeing/seeing.js +1341 -0
- package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
- package/dist/core/capabilities/seeing/shot-store.js +204 -0
- package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
- package/dist/core/capabilities/surface-snap-mesh.js +425 -0
- package/dist/core/capabilities/surface-snap.d.ts +77 -0
- package/dist/core/capabilities/surface-snap.js +461 -0
- package/dist/core/capabilities/tool-dispatch.js +231 -72
- package/dist/core/capabilities/tscn.d.ts +85 -0
- package/dist/core/capabilities/tscn.js +391 -0
- package/dist/core/capabilities/world-snapshot.d.ts +58 -0
- package/dist/core/capabilities/world-snapshot.js +150 -0
- package/dist/core/capability-skew.d.ts +5 -5
- package/dist/core/capability-skew.js +14 -6
- package/dist/core/headless/worker-engine-client.d.ts +1 -1
- package/dist/core/headless/worker-engine-client.js +2 -2
- package/dist/core/library-read.d.ts +37 -5
- package/dist/core/library-read.js +264 -33
- package/dist/core/library-search.d.ts +2 -2
- package/dist/core/skills-registry.d.ts +9 -1
- package/dist/core/skills-registry.js +25 -2
- package/dist/installer/agent-config.d.ts +9 -4
- package/dist/installer/agent-config.js +194 -392
- package/dist/installer/agent-table.d.ts +119 -0
- package/dist/installer/agent-table.js +619 -0
- package/dist/installer/setup.js +4 -2
- package/dist/installer/skill-locations.d.ts +8 -17
- package/dist/installer/skill-locations.js +22 -101
- package/dist/installer/version-check.d.ts +5 -1
- package/dist/installer/version-check.js +38 -28
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/tools/asset-tools.js +21 -3
- package/dist/mcp/tools/audit-tools.d.ts +23 -0
- package/dist/mcp/tools/audit-tools.js +60 -0
- package/dist/mcp/tools/debug-tools.js +3 -1
- package/dist/mcp/tools/file-tools.js +17 -4
- package/dist/mcp/tools/generate-tools.js +74 -66
- package/dist/mcp/tools/library-tools.js +2 -0
- package/dist/mcp/tools/perception-tools.js +6 -19
- package/dist/mcp/tools/placement-tools.d.ts +24 -0
- package/dist/mcp/tools/placement-tools.js +91 -0
- package/dist/mcp/tools/project-tools.js +18 -15
- package/dist/mcp/tools/scene-tools.js +71 -83
- package/dist/mcp/tools/seeing-tools.d.ts +24 -0
- package/dist/mcp/tools/seeing-tools.js +64 -0
- package/dist/mcp/tools/spatial-tools.js +17 -9
- package/dist/mcp/tools/visual-tools.js +59 -7
- package/gemini-extension.json +26 -4
- package/integrations/README.md +48 -19
- package/integrations/amp/README.md +12 -0
- package/integrations/amp/manifest-target.json +3 -0
- package/integrations/antigravity/README.md +12 -0
- package/integrations/antigravity/manifest-target.json +3 -0
- package/integrations/claude-desktop/README.md +12 -0
- package/integrations/claude-desktop/manifest-target.json +3 -0
- package/integrations/cline/README.md +8 -9
- package/integrations/cline-cli/README.md +12 -0
- package/integrations/cline-cli/manifest-target.json +3 -0
- package/integrations/copilot-jetbrains/README.md +12 -0
- package/integrations/copilot-jetbrains/manifest-target.json +3 -0
- package/integrations/crush/README.md +12 -0
- package/integrations/crush/manifest-target.json +3 -0
- package/integrations/github-copilot/README.md +8 -8
- package/integrations/goose/README.md +12 -0
- package/integrations/goose/manifest-target.json +3 -0
- package/integrations/grok-build/README.md +12 -0
- package/integrations/grok-build/manifest-target.json +3 -0
- package/integrations/hermes/README.md +12 -0
- package/integrations/hermes/manifest-target.json +3 -0
- package/integrations/junie/README.md +12 -0
- package/integrations/junie/manifest-target.json +3 -0
- package/integrations/kilo-code/README.md +6 -10
- package/integrations/kimi-code/README.md +12 -0
- package/integrations/kimi-code/manifest-target.json +3 -0
- package/integrations/kiro/README.md +12 -0
- package/integrations/kiro/manifest-target.json +3 -0
- package/integrations/lm-studio/README.md +8 -8
- package/integrations/mistral-vibe/README.md +12 -0
- package/integrations/mistral-vibe/manifest-target.json +3 -0
- package/integrations/opencode/README.md +9 -12
- package/integrations/qoder/README.md +12 -0
- package/integrations/qoder/manifest-target.json +3 -0
- package/integrations/qwen-code/README.md +12 -0
- package/integrations/qwen-code/manifest-target.json +3 -0
- package/integrations/roo-code/README.md +8 -7
- package/integrations/rovo-dev/README.md +12 -0
- package/integrations/rovo-dev/manifest-target.json +3 -0
- package/integrations/trae/README.md +12 -0
- package/integrations/trae/manifest-target.json +3 -0
- package/integrations/visual-studio/README.md +12 -0
- package/integrations/visual-studio/manifest-target.json +3 -0
- package/integrations/vscode-copilot/README.md +9 -9
- package/integrations/warp/README.md +12 -0
- package/integrations/warp/manifest-target.json +3 -0
- package/integrations/windsurf/README.md +8 -9
- package/integrations/zed/README.md +12 -0
- package/integrations/zed/manifest-target.json +3 -0
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/summer-folder/resource.yaml +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +1 -1
- package/library/skills/README.md +264 -0
- package/library/skills/adaptive-music/SKILL.md +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +220 -0
- package/library/skills/ai-characters-in-games/resource.yaml +30 -0
- package/library/skills/ambient-bed/SKILL.md +1 -1
- package/library/skills/animated-loop/SKILL.md +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/art-direction/SKILL.md +1 -1
- package/library/skills/asset-strategy/SKILL.md +2 -1
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/browse-templates/SKILL.md +3 -3
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +128 -0
- package/library/skills/camera-rigs/resource.yaml +29 -0
- package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +1 -1
- package/library/skills/character-model/SKILL.md +1 -1
- package/library/skills/character-movement/SKILL.md +326 -0
- package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
- package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
- package/library/skills/character-movement/references/movement-3d.md +136 -0
- package/library/skills/character-movement/references/platformer-feel.md +83 -0
- package/library/skills/character-movement/resource.yaml +40 -0
- package/library/skills/character-portrait/SKILL.md +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +1 -1
- package/library/skills/combat-basics/SKILL.md +159 -0
- package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
- package/library/skills/combat-basics/resource.yaml +34 -0
- package/library/skills/concept-art/SKILL.md +1 -1
- package/library/skills/create-asset-sheet/SKILL.md +1 -1
- package/library/skills/debug/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/design-level/SKILL.md +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/environment-kit/SKILL.md +1 -1
- package/library/skills/export-and-ship/SKILL.md +1 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +1 -1
- package/library/skills/fps-controller/SKILL.md +9 -7
- package/library/skills/fps-controller/resource.yaml +4 -1
- package/library/skills/game-feel/SKILL.md +1 -1
- package/library/skills/gameskill/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/generate-motion/SKILL.md +9 -9
- package/library/skills/generate-motion/resource.yaml +2 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/in-game-purchases/SKILL.md +254 -0
- package/library/skills/in-game-purchases/resource.yaml +29 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
- package/library/skills/investigating-bugs/SKILL.md +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
- package/library/skills/make-game/SKILL.md +25 -3
- package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
- package/library/skills/make-game/references/2d-platformer.md +117 -0
- package/library/skills/make-game/references/2d-rpg-content.md +234 -0
- package/library/skills/make-game/references/2d-rpg.md +241 -0
- package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
- package/library/skills/make-game/references/2d-survivors.md +237 -0
- package/library/skills/make-game/references/3d-fps-systems.md +216 -0
- package/library/skills/make-game/references/3d-fps.md +177 -0
- package/library/skills/make-game/references/3d-horror-systems.md +186 -0
- package/library/skills/make-game/references/3d-horror.md +201 -0
- package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
- package/library/skills/make-game/references/3d-third-person.md +215 -0
- package/library/skills/make-game/resource.yaml +6 -1
- package/library/skills/multiplayer/SKILL.md +150 -0
- package/library/skills/multiplayer/resource.yaml +33 -0
- package/library/skills/multiplayer-movement/SKILL.md +510 -0
- package/library/skills/multiplayer-movement/files.md +501 -0
- package/library/skills/multiplayer-movement/resource.yaml +30 -0
- package/library/skills/multiplayer-project/SKILL.md +521 -0
- package/library/skills/multiplayer-project/files.md +448 -0
- package/library/skills/multiplayer-project/resource.yaml +36 -0
- package/library/skills/multiplayer-publish/SKILL.md +229 -0
- package/library/skills/multiplayer-publish/export-errors.md +89 -0
- package/library/skills/multiplayer-publish/resource.yaml +28 -0
- package/library/skills/multiplayer-state/SKILL.md +438 -0
- package/library/skills/multiplayer-state/files.md +592 -0
- package/library/skills/multiplayer-state/resource.yaml +35 -0
- package/library/skills/multiplayer-testing/SKILL.md +236 -0
- package/library/skills/multiplayer-testing/resource.yaml +39 -0
- package/library/skills/music-track/SKILL.md +1 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/organic-model/SKILL.md +1 -1
- package/library/skills/pixel-art/SKILL.md +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/procedural-animation/SKILL.md +2 -2
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +1 -1
- package/library/skills/psx-retro-rendering/SKILL.md +1 -1
- package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/retarget/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/save-load/SKILL.md +156 -0
- package/library/skills/save-load/resource.yaml +27 -0
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
- package/library/skills/scene-scripting/SKILL.md +1 -1
- package/library/skills/scene-to-level/SKILL.md +1 -1
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +1 -1
- package/library/skills/skill-test/SKILL.md +1 -1
- package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
- package/library/skills/skybox-panorama/SKILL.md +1 -1
- package/library/skills/sound-effect/SKILL.md +1 -1
- package/library/skills/spatial-placement/SKILL.md +134 -3
- package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
- package/library/skills/spatial-placement/resource.yaml +16 -2
- package/library/skills/sprite-sheet/SKILL.md +1 -1
- package/library/skills/summer-analytics/SKILL.md +147 -0
- package/library/skills/summer-analytics/resource.yaml +28 -0
- package/library/skills/summer-friends/SKILL.md +226 -0
- package/library/skills/summer-friends/resource.yaml +28 -0
- package/library/skills/summer-leaderboards/SKILL.md +231 -0
- package/library/skills/summer-leaderboards/resource.yaml +30 -0
- package/library/skills/summer-match-results/SKILL.md +286 -0
- package/library/skills/summer-match-results/resource.yaml +32 -0
- package/library/skills/summer-matchmaking/SKILL.md +301 -0
- package/library/skills/summer-matchmaking/resource.yaml +32 -0
- package/library/skills/summer-parties/SKILL.md +253 -0
- package/library/skills/summer-parties/resource.yaml +30 -0
- package/library/skills/summer-player-data/SKILL.md +322 -0
- package/library/skills/summer-player-data/resource.yaml +30 -0
- package/library/skills/summer-store/SKILL.md +271 -0
- package/library/skills/summer-store/resource.yaml +29 -0
- package/library/skills/summer-world-chat/SKILL.md +195 -0
- package/library/skills/summer-world-chat/resource.yaml +30 -0
- package/library/skills/summer-world-saves/SKILL.md +284 -0
- package/library/skills/summer-world-saves/resource.yaml +29 -0
- package/library/skills/text-to-motion/SKILL.md +99 -0
- package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
- package/library/skills/text-to-motion/resource.yaml +41 -0
- package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +1 -1
- package/library/skills/tune-performance/SKILL.md +1 -1
- package/library/skills/ui-basics/SKILL.md +10 -1
- package/library/skills/ui-graphics/SKILL.md +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/using-summer/SKILL.md +2 -2
- package/library/skills/using-summer/resource.yaml +2 -2
- package/library/skills/vehicle-model/SKILL.md +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verifying-scenes/SKILL.md +76 -2
- package/library/skills/verifying-scenes/resource.yaml +13 -2
- package/library/skills/vfx-dissolve/SKILL.md +1 -1
- package/library/skills/vfx-fire/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-lightning/SKILL.md +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
- package/library/skills/vfx-smoke/SKILL.md +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +1 -1
- package/library/skills/voice-line/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +38 -0
- package/library/skills/web-and-touch-input/resource.yaml +28 -0
- package/library/skills/web-compatibility-look/SKILL.md +62 -0
- package/library/skills/web-compatibility-look/resource.yaml +30 -0
- package/library/skills/world-building-3d/SKILL.md +8 -1
- package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +2 -1
- package/library/skills/writing-plans/SKILL.md +1 -1
- package/library/skills/writing-skills/SKILL.md +1 -1
- package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
- package/library/tools/attach-to-surface/resource.yaml +142 -0
- package/library/tools/batch/resource.yaml +9 -3
- package/library/tools/connect-ports/resource.yaml +120 -0
- package/library/tools/connect-signal/resource.yaml +3 -2
- package/library/tools/debug-views/resource.yaml +94 -0
- package/library/tools/frame-nodes/resource.yaml +104 -0
- package/library/tools/frame-shot/resource.yaml +134 -0
- package/library/tools/generate-motion/resource.yaml +44 -9
- package/library/tools/get-board/resource.yaml +50 -0
- package/library/tools/get-project-context/resource.yaml +13 -2
- package/library/tools/grep/resource.yaml +77 -0
- package/library/tools/inspect-asset/resource.yaml +71 -0
- package/library/tools/inspect-node/resource.yaml +9 -2
- package/library/tools/inspect-resource/resource.yaml +16 -5
- package/library/tools/instantiate-scene/resource.yaml +27 -2
- package/library/tools/measure/resource.yaml +96 -0
- package/library/tools/place-adjacent/resource.yaml +113 -0
- package/library/tools/play/resource.yaml +15 -2
- package/library/tools/raycast/resource.yaml +101 -0
- package/library/tools/read-file/resource.yaml +36 -2
- package/library/tools/read-library/resource.yaml +2 -2
- package/library/tools/repeat-along/resource.yaml +129 -0
- package/library/tools/replace-node/resource.yaml +11 -5
- package/library/tools/scene-audit/resource.yaml +144 -0
- package/library/tools/screenshot/resource.yaml +13 -2
- package/library/tools/search-assets/resource.yaml +18 -0
- package/library/tools/shot-sheet/resource.yaml +110 -0
- package/library/tools/snap-to-surface/resource.yaml +4 -1
- package/library/tools/world-snapshot/resource.yaml +24 -3
- package/library/tools/zoom/resource.yaml +105 -0
- package/package.json +3 -3
- package/registry/generated/aliases.json +17 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +26 -4
- package/registry/generated/index.json +2154 -469
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +27 -5
- package/registry/generated/plugin.codex.json +28 -6
- package/registry/generated/plugin.cursor.json +27 -5
- package/registry/generated/plugin.factory.json +27 -5
- package/registry/generated/skills-index.md +264 -0
- package/registry/generated/skills-registry.json +901 -206
- package/registry/generated/templates-registry.json +2 -2
- package/library/skills/host-authoritative-state/SKILL.md +0 -332
- package/library/skills/host-authoritative-state/resource.yaml +0 -28
- package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
- package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
- package/library/skills/setup-multiplayer/SKILL.md +0 -336
- package/library/skills/setup-multiplayer/resource.yaml +0 -28
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tscn — a small reader for the Godot text scene format (`.tscn`, format 3),
|
|
3
|
+
* enough to answer "what did the editor actually SAVE": which ext_resources
|
|
4
|
+
* the file references, every [node] entry (name, parent, type/instance,
|
|
5
|
+
* groups, its property overrides as raw text), [connection] and [editable]
|
|
6
|
+
* entries.
|
|
7
|
+
*
|
|
8
|
+
* Used by summer_replace_node to read the scene before a replacement (which
|
|
9
|
+
* children the user added under the node, which properties it overrides) and
|
|
10
|
+
* to verify afterwards that the saved file really references the new scene.
|
|
11
|
+
* Property values stay raw Godot literal text; nothing here evaluates them.
|
|
12
|
+
*/
|
|
13
|
+
/** Advance bracket depth / string state over `text`. */
|
|
14
|
+
function scanDepth(text, state) {
|
|
15
|
+
let { depth, inString, escape } = state;
|
|
16
|
+
for (const ch of text) {
|
|
17
|
+
if (inString) {
|
|
18
|
+
if (escape)
|
|
19
|
+
escape = false;
|
|
20
|
+
else if (ch === "\\")
|
|
21
|
+
escape = true;
|
|
22
|
+
else if (ch === '"')
|
|
23
|
+
inString = false;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (ch === '"')
|
|
27
|
+
inString = true;
|
|
28
|
+
else if (ch === "(" || ch === "[" || ch === "{")
|
|
29
|
+
depth++;
|
|
30
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
31
|
+
depth--;
|
|
32
|
+
}
|
|
33
|
+
// A newline inside a string keeps the string open; an escape never spans lines.
|
|
34
|
+
return { depth, inString, escape: false };
|
|
35
|
+
}
|
|
36
|
+
/** Decode a Godot quoted string body (without the quotes). */
|
|
37
|
+
export function decodeGodotString(body) {
|
|
38
|
+
let out = "";
|
|
39
|
+
for (let i = 0; i < body.length; i++) {
|
|
40
|
+
const ch = body[i];
|
|
41
|
+
if (ch !== "\\" || i === body.length - 1) {
|
|
42
|
+
out += ch;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const next = body[++i];
|
|
46
|
+
switch (next) {
|
|
47
|
+
case "n":
|
|
48
|
+
out += "\n";
|
|
49
|
+
break;
|
|
50
|
+
case "t":
|
|
51
|
+
out += "\t";
|
|
52
|
+
break;
|
|
53
|
+
case "r":
|
|
54
|
+
out += "\r";
|
|
55
|
+
break;
|
|
56
|
+
case "b":
|
|
57
|
+
out += "\b";
|
|
58
|
+
break;
|
|
59
|
+
case "f":
|
|
60
|
+
out += "\f";
|
|
61
|
+
break;
|
|
62
|
+
case "u": {
|
|
63
|
+
const hex = body.slice(i + 1, i + 5);
|
|
64
|
+
if (/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
65
|
+
out += String.fromCharCode(Number.parseInt(hex, 16));
|
|
66
|
+
i += 4;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
out += "u";
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
default: out += next;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** If `raw` is exactly one quoted string literal (optionally prefixed with
|
|
79
|
+
* & for StringName or ^ for NodePath), its decoded text; else undefined. */
|
|
80
|
+
export function quotedLiteral(raw) {
|
|
81
|
+
const text = raw.trim();
|
|
82
|
+
const body = text.startsWith("&\"") || text.startsWith("^\"") ? text.slice(1) : text;
|
|
83
|
+
if (body.length < 2 || !body.startsWith('"') || !body.endsWith('"'))
|
|
84
|
+
return undefined;
|
|
85
|
+
const inner = body.slice(1, -1);
|
|
86
|
+
// Reject `"a" + "b"`-like shapes: an unescaped quote inside means two literals.
|
|
87
|
+
let escape = false;
|
|
88
|
+
for (const ch of inner) {
|
|
89
|
+
if (escape) {
|
|
90
|
+
escape = false;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (ch === "\\") {
|
|
94
|
+
escape = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (ch === '"')
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return decodeGodotString(inner);
|
|
101
|
+
}
|
|
102
|
+
/** Split a tag body (`name a=1 b="x y" c=ExtResource("1")`) into its name
|
|
103
|
+
* and attribute map (raw value text). */
|
|
104
|
+
function parseTag(line) {
|
|
105
|
+
const trimmed = line.trim();
|
|
106
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]"))
|
|
107
|
+
return null;
|
|
108
|
+
const body = trimmed.slice(1, -1);
|
|
109
|
+
const nameMatch = /^([A-Za-z_][A-Za-z0-9_]*)/.exec(body);
|
|
110
|
+
if (!nameMatch)
|
|
111
|
+
return null;
|
|
112
|
+
const attrs = new Map();
|
|
113
|
+
let i = nameMatch[1].length;
|
|
114
|
+
while (i < body.length) {
|
|
115
|
+
while (i < body.length && /\s/.test(body[i]))
|
|
116
|
+
i++;
|
|
117
|
+
if (i >= body.length)
|
|
118
|
+
break;
|
|
119
|
+
const keyMatch = /^([A-Za-z_][A-Za-z0-9_]*)=/.exec(body.slice(i));
|
|
120
|
+
if (!keyMatch)
|
|
121
|
+
break;
|
|
122
|
+
const key = keyMatch[1];
|
|
123
|
+
i += keyMatch[0].length;
|
|
124
|
+
const start = i;
|
|
125
|
+
let depth = 0;
|
|
126
|
+
let inString = false;
|
|
127
|
+
while (i < body.length) {
|
|
128
|
+
const ch = body[i];
|
|
129
|
+
if (inString) {
|
|
130
|
+
if (ch === "\\") {
|
|
131
|
+
i += 2; // the escaped character belongs to the string
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (ch === '"')
|
|
135
|
+
inString = false;
|
|
136
|
+
i++;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (depth === 0 && /\s/.test(ch))
|
|
140
|
+
break;
|
|
141
|
+
if (ch === '"')
|
|
142
|
+
inString = true;
|
|
143
|
+
else if (ch === "(" || ch === "[" || ch === "{")
|
|
144
|
+
depth++;
|
|
145
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
146
|
+
depth--;
|
|
147
|
+
i++;
|
|
148
|
+
}
|
|
149
|
+
attrs.set(key, body.slice(start, Math.min(i, body.length)));
|
|
150
|
+
}
|
|
151
|
+
return { tag: nameMatch[1], attrs };
|
|
152
|
+
}
|
|
153
|
+
function attrString(attrs, key) {
|
|
154
|
+
const raw = attrs.get(key);
|
|
155
|
+
if (raw === undefined)
|
|
156
|
+
return undefined;
|
|
157
|
+
return quotedLiteral(raw) ?? raw;
|
|
158
|
+
}
|
|
159
|
+
/** `ExtResource("1_ab")` / `ExtResource( 1 )` -> "1_ab" / "1". */
|
|
160
|
+
export function extResourceId(raw) {
|
|
161
|
+
if (!raw)
|
|
162
|
+
return undefined;
|
|
163
|
+
const match = /^ExtResource\(\s*(?:"((?:[^"\\]|\\.)*)"|([^)\s]+))\s*\)$/.exec(raw.trim());
|
|
164
|
+
if (!match)
|
|
165
|
+
return undefined;
|
|
166
|
+
return match[1] !== undefined ? decodeGodotString(match[1]) : match[2];
|
|
167
|
+
}
|
|
168
|
+
/** `SubResource("Box_x")` -> "Box_x". */
|
|
169
|
+
export function subResourceId(raw) {
|
|
170
|
+
const match = /^SubResource\(\s*(?:"((?:[^"\\]|\\.)*)"|([^)\s]+))\s*\)$/.exec(raw.trim());
|
|
171
|
+
if (!match)
|
|
172
|
+
return undefined;
|
|
173
|
+
return match[1] !== undefined ? decodeGodotString(match[1]) : match[2];
|
|
174
|
+
}
|
|
175
|
+
function parseStringArray(raw) {
|
|
176
|
+
if (!raw)
|
|
177
|
+
return [];
|
|
178
|
+
const out = [];
|
|
179
|
+
const pattern = /[&^]?"((?:[^"\\]|\\.)*)"/g;
|
|
180
|
+
for (const match of raw.matchAll(pattern))
|
|
181
|
+
out.push(decodeGodotString(match[1]));
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
/** A property line `key = value` (key may be a quoted string). */
|
|
185
|
+
function parsePropStart(line) {
|
|
186
|
+
if (line.startsWith('"')) {
|
|
187
|
+
let i = 1;
|
|
188
|
+
let escape = false;
|
|
189
|
+
for (; i < line.length; i++) {
|
|
190
|
+
const ch = line[i];
|
|
191
|
+
if (escape) {
|
|
192
|
+
escape = false;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (ch === "\\") {
|
|
196
|
+
escape = true;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (ch === '"')
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
const rest = line.slice(i + 1);
|
|
203
|
+
const eq = /^\s*=\s?/.exec(rest);
|
|
204
|
+
if (!eq)
|
|
205
|
+
return null;
|
|
206
|
+
return { key: decodeGodotString(line.slice(1, i)), value: rest.slice(eq[0].length) };
|
|
207
|
+
}
|
|
208
|
+
const match = /^([^\s=]+)\s*=\s?(.*)$/s.exec(line);
|
|
209
|
+
if (!match)
|
|
210
|
+
return null;
|
|
211
|
+
return { key: match[1], value: match[2] };
|
|
212
|
+
}
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Paths
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
/** Normalize a scene-relative node path the way the engine resolves it:
|
|
217
|
+
* "", ".", "./" and "/" are the root (returned as "."); a leading "./" is
|
|
218
|
+
* dropped; trailing slashes are dropped. */
|
|
219
|
+
export function normalizeNodePath(path) {
|
|
220
|
+
let p = path.trim().replace(/\\/g, "/");
|
|
221
|
+
if (p === "" || p === "." || p === "./" || p === "/")
|
|
222
|
+
return ".";
|
|
223
|
+
while (p.startsWith("./"))
|
|
224
|
+
p = p.slice(2);
|
|
225
|
+
p = p.replace(/\/+$/, "");
|
|
226
|
+
return p === "" ? "." : p;
|
|
227
|
+
}
|
|
228
|
+
/** Join a parent path ("." or "A/B") and a child name. */
|
|
229
|
+
export function joinNodePath(parent, name) {
|
|
230
|
+
return parent === "." ? name : `${parent}/${name}`;
|
|
231
|
+
}
|
|
232
|
+
/** Parent of a normalized path ("A/B" -> "A", "A" -> "."). The root has none. */
|
|
233
|
+
export function parentNodePath(path) {
|
|
234
|
+
if (path === ".")
|
|
235
|
+
return null;
|
|
236
|
+
const slash = path.lastIndexOf("/");
|
|
237
|
+
return slash < 0 ? "." : path.slice(0, slash);
|
|
238
|
+
}
|
|
239
|
+
/** True when `path` is `ancestor` or below it. */
|
|
240
|
+
export function isAtOrBelow(path, ancestor) {
|
|
241
|
+
if (ancestor === ".")
|
|
242
|
+
return true;
|
|
243
|
+
return path === ancestor || path.startsWith(`${ancestor}/`);
|
|
244
|
+
}
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Parser
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
export function parseTscn(text) {
|
|
249
|
+
const extResources = new Map();
|
|
250
|
+
const nodes = [];
|
|
251
|
+
const connections = [];
|
|
252
|
+
const editable = [];
|
|
253
|
+
let format;
|
|
254
|
+
let currentNode = null;
|
|
255
|
+
let pending = null;
|
|
256
|
+
const finishPending = () => {
|
|
257
|
+
if (pending && currentNode)
|
|
258
|
+
currentNode.props.push({ key: pending.key, value: pending.value });
|
|
259
|
+
pending = null;
|
|
260
|
+
};
|
|
261
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
262
|
+
for (const line of lines) {
|
|
263
|
+
if (pending) {
|
|
264
|
+
pending.value += `\n${line}`;
|
|
265
|
+
pending.state = scanDepth(`\n${line}`, pending.state);
|
|
266
|
+
if (pending.state.depth <= 0 && !pending.state.inString)
|
|
267
|
+
finishPending();
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (line.startsWith("[")) {
|
|
271
|
+
const tag = parseTag(line);
|
|
272
|
+
currentNode = null;
|
|
273
|
+
if (!tag)
|
|
274
|
+
continue;
|
|
275
|
+
const { attrs } = tag;
|
|
276
|
+
switch (tag.tag) {
|
|
277
|
+
case "gd_scene":
|
|
278
|
+
case "gd_resource": {
|
|
279
|
+
const raw = attrs.get("format");
|
|
280
|
+
if (raw && /^\d+$/.test(raw))
|
|
281
|
+
format = Number(raw);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case "ext_resource": {
|
|
285
|
+
const id = attrString(attrs, "id");
|
|
286
|
+
if (id !== undefined) {
|
|
287
|
+
extResources.set(id, {
|
|
288
|
+
id,
|
|
289
|
+
type: attrString(attrs, "type"),
|
|
290
|
+
path: attrString(attrs, "path"),
|
|
291
|
+
uid: attrString(attrs, "uid"),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case "node": {
|
|
297
|
+
const name = attrString(attrs, "name") ?? "";
|
|
298
|
+
const parentRaw = attrString(attrs, "parent");
|
|
299
|
+
const parent = parentRaw === undefined ? null : normalizeNodePath(parentRaw);
|
|
300
|
+
const instance = extResourceId(attrs.get("instance"));
|
|
301
|
+
const indexRaw = attrString(attrs, "index");
|
|
302
|
+
const node = {
|
|
303
|
+
name,
|
|
304
|
+
path: parent === null ? "." : joinNodePath(parent, name),
|
|
305
|
+
parent,
|
|
306
|
+
groups: parseStringArray(attrs.get("groups")),
|
|
307
|
+
props: [],
|
|
308
|
+
order: nodes.length,
|
|
309
|
+
};
|
|
310
|
+
const type = attrString(attrs, "type");
|
|
311
|
+
if (type !== undefined)
|
|
312
|
+
node.type = type;
|
|
313
|
+
if (instance !== undefined) {
|
|
314
|
+
node.instance = instance;
|
|
315
|
+
const resolved = extResources.get(instance)?.path;
|
|
316
|
+
if (resolved)
|
|
317
|
+
node.instancePath = resolved;
|
|
318
|
+
}
|
|
319
|
+
const placeholder = attrString(attrs, "instance_placeholder");
|
|
320
|
+
if (placeholder !== undefined)
|
|
321
|
+
node.instancePlaceholder = placeholder;
|
|
322
|
+
if (indexRaw !== undefined && /^-?\d+$/.test(indexRaw))
|
|
323
|
+
node.index = Number(indexRaw);
|
|
324
|
+
nodes.push(node);
|
|
325
|
+
currentNode = node;
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case "connection": {
|
|
329
|
+
const flagsRaw = attrs.get("flags");
|
|
330
|
+
connections.push({
|
|
331
|
+
signal: attrString(attrs, "signal") ?? "",
|
|
332
|
+
from: normalizeNodePath(attrString(attrs, "from") ?? "."),
|
|
333
|
+
to: normalizeNodePath(attrString(attrs, "to") ?? "."),
|
|
334
|
+
method: attrString(attrs, "method") ?? "",
|
|
335
|
+
...(flagsRaw && /^\d+$/.test(flagsRaw) ? { flags: Number(flagsRaw) } : {}),
|
|
336
|
+
raw: line.trim(),
|
|
337
|
+
});
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
case "editable": {
|
|
341
|
+
const path = attrString(attrs, "path");
|
|
342
|
+
if (path !== undefined)
|
|
343
|
+
editable.push(normalizeNodePath(path));
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
default:
|
|
347
|
+
// sub_resource / resource bodies are not needed; their property
|
|
348
|
+
// lines are skipped because currentNode is null.
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!currentNode || line.trim() === "")
|
|
354
|
+
continue;
|
|
355
|
+
const prop = parsePropStart(line);
|
|
356
|
+
if (!prop)
|
|
357
|
+
continue;
|
|
358
|
+
const state = scanDepth(prop.value, { depth: 0, inString: false, escape: false });
|
|
359
|
+
if (state.depth > 0 || state.inString) {
|
|
360
|
+
pending = { key: prop.key, value: prop.value, state };
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
currentNode.props.push({ key: prop.key, value: prop.value });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
finishPending();
|
|
367
|
+
// An ext_resource listed after the node that uses it (never written by the
|
|
368
|
+
// editor, but cheap to tolerate).
|
|
369
|
+
for (const node of nodes) {
|
|
370
|
+
if (node.instance && !node.instancePath) {
|
|
371
|
+
const resolved = extResources.get(node.instance)?.path;
|
|
372
|
+
if (resolved)
|
|
373
|
+
node.instancePath = resolved;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return { ...(format !== undefined ? { format } : {}), extResources, nodes, connections, editable };
|
|
377
|
+
}
|
|
378
|
+
export function findTscnNode(parsed, path) {
|
|
379
|
+
const target = normalizeNodePath(path);
|
|
380
|
+
return parsed.nodes.find((node) => node.path === target);
|
|
381
|
+
}
|
|
382
|
+
/** A node the scene itself creates (and owns): it has a type, an instance or
|
|
383
|
+
* a placeholder. An entry with none of those is a property override of a
|
|
384
|
+
* node that an instanced (or inherited) scene creates. */
|
|
385
|
+
export function isSceneCreatedNode(node) {
|
|
386
|
+
return node.type !== undefined || node.instance !== undefined || node.instancePlaceholder !== undefined;
|
|
387
|
+
}
|
|
388
|
+
/** Normalize a res:// path for comparison. */
|
|
389
|
+
export function normalizeResPath(path) {
|
|
390
|
+
return path.trim().replace(/\\/g, "/");
|
|
391
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* world-snapshot — the shared request builder and result filter of
|
|
3
|
+
* `summer_world_snapshot` (src/mcp/tools/perception-tools.ts and
|
|
4
|
+
* tool-dispatch.ts).
|
|
5
|
+
*
|
|
6
|
+
* The engine's GetWorldSnapshot (world_snapshot_ops.cpp) takes only
|
|
7
|
+
* scene_path / max_nodes / max_lights / include_2d and returns every node it
|
|
8
|
+
* walked, about 250 bytes each: a scene of a few thousand nodes is far too
|
|
9
|
+
* big to read.
|
|
10
|
+
* The engine snapshot is still taken whole — its snapshot_id is the baseline
|
|
11
|
+
* summer_snapshot_diff compares against, and capping it would hide changes —
|
|
12
|
+
* but the agent gets a filtered, capped view: a subtree (path_prefix), some
|
|
13
|
+
* classes, some fields, a page (offset/max_nodes), plus class counts of what
|
|
14
|
+
* matched. Whole-scene `counts` and `total_nodes` stay as the engine sent them.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { type JsonRecord } from "../util/json.js";
|
|
18
|
+
/** Node entries returned to the agent unless max_nodes says otherwise. */
|
|
19
|
+
export declare const WORLD_SNAPSHOT_DEFAULT_MAX_NODES = 200;
|
|
20
|
+
/** The engine's own default cap (SNAPSHOT_MAX_NODES_DEFAULT) for the stored snapshot. */
|
|
21
|
+
export declare const ENGINE_SNAPSHOT_MAX_NODES = 4000;
|
|
22
|
+
/** Per-node fields the engine writes (path is always kept). */
|
|
23
|
+
export declare const WORLD_SNAPSHOT_NODE_FIELDS: readonly string[];
|
|
24
|
+
export declare const worldSnapshotInputShape: {
|
|
25
|
+
scene_path: z.ZodOptional<z.ZodString>;
|
|
26
|
+
max_nodes: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
28
|
+
path_prefix: z.ZodOptional<z.ZodString>;
|
|
29
|
+
classes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
30
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
31
|
+
};
|
|
32
|
+
export declare const worldSnapshotInputSchema: z.ZodObject<{
|
|
33
|
+
scene_path: z.ZodOptional<z.ZodString>;
|
|
34
|
+
max_nodes: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
path_prefix: z.ZodOptional<z.ZodString>;
|
|
37
|
+
classes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
38
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
39
|
+
}, "strict", z.ZodTypeAny, {
|
|
40
|
+
scene_path?: string | undefined;
|
|
41
|
+
offset?: number | undefined;
|
|
42
|
+
max_nodes?: number | undefined;
|
|
43
|
+
path_prefix?: string | undefined;
|
|
44
|
+
classes?: string[] | undefined;
|
|
45
|
+
fields?: string[] | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
scene_path?: string | undefined;
|
|
48
|
+
offset?: number | undefined;
|
|
49
|
+
max_nodes?: number | undefined;
|
|
50
|
+
path_prefix?: string | undefined;
|
|
51
|
+
classes?: string[] | undefined;
|
|
52
|
+
fields?: string[] | undefined;
|
|
53
|
+
}>;
|
|
54
|
+
export type WorldSnapshotArgs = z.infer<typeof worldSnapshotInputSchema>;
|
|
55
|
+
export declare function buildWorldSnapshotOp(args: WorldSnapshotArgs): JsonRecord;
|
|
56
|
+
/** Filter and cap a successful snapshot for the agent. Anything that is not a
|
|
57
|
+
* snapshot payload (a failure, an older shape) passes through untouched. */
|
|
58
|
+
export declare function shapeWorldSnapshot(result: unknown, args: WorldSnapshotArgs): unknown;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* world-snapshot — the shared request builder and result filter of
|
|
3
|
+
* `summer_world_snapshot` (src/mcp/tools/perception-tools.ts and
|
|
4
|
+
* tool-dispatch.ts).
|
|
5
|
+
*
|
|
6
|
+
* The engine's GetWorldSnapshot (world_snapshot_ops.cpp) takes only
|
|
7
|
+
* scene_path / max_nodes / max_lights / include_2d and returns every node it
|
|
8
|
+
* walked, about 250 bytes each: a scene of a few thousand nodes is far too
|
|
9
|
+
* big to read.
|
|
10
|
+
* The engine snapshot is still taken whole — its snapshot_id is the baseline
|
|
11
|
+
* summer_snapshot_diff compares against, and capping it would hide changes —
|
|
12
|
+
* but the agent gets a filtered, capped view: a subtree (path_prefix), some
|
|
13
|
+
* classes, some fields, a page (offset/max_nodes), plus class counts of what
|
|
14
|
+
* matched. Whole-scene `counts` and `total_nodes` stay as the engine sent them.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { asRecord } from "../util/json.js";
|
|
18
|
+
import { isAtOrBelow, normalizeNodePath } from "./tscn.js";
|
|
19
|
+
/** Node entries returned to the agent unless max_nodes says otherwise. */
|
|
20
|
+
export const WORLD_SNAPSHOT_DEFAULT_MAX_NODES = 200;
|
|
21
|
+
/** The engine's own default cap (SNAPSHOT_MAX_NODES_DEFAULT) for the stored snapshot. */
|
|
22
|
+
export const ENGINE_SNAPSHOT_MAX_NODES = 4000;
|
|
23
|
+
/** Per-node fields the engine writes (path is always kept). */
|
|
24
|
+
export const WORLD_SNAPSHOT_NODE_FIELDS = [
|
|
25
|
+
"class", "name", "pos", "rot_deg", "scale", "visible", "aabb", "scene_file",
|
|
26
|
+
"script_fp", "material_fps", "material_fps_truncated", "z_index", "rect", "view_rect", "limit_rect",
|
|
27
|
+
];
|
|
28
|
+
// Mirrors library/tools/world-snapshot/resource.yaml input_schema (parity-tested).
|
|
29
|
+
export const worldSnapshotInputShape = {
|
|
30
|
+
scene_path: z
|
|
31
|
+
.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Scene to snapshot, e.g. 'res://main.tscn'. Omit for the currently edited scene."),
|
|
34
|
+
max_nodes: z
|
|
35
|
+
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.positive()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe(`Most node entries to return (default ${WORLD_SNAPSHOT_DEFAULT_MAX_NODES}). The engine still snapshots the whole scene for the diff baseline; the result says when the list is cut (truncated, next_offset).`),
|
|
40
|
+
offset: z
|
|
41
|
+
.number()
|
|
42
|
+
.int()
|
|
43
|
+
.min(0)
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Skip this many matching nodes (path-sorted) before listing — page with the result's next_offset."),
|
|
46
|
+
path_prefix: z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Only this subtree: the node at this scene-relative path and everything below it, e.g. 'House3' or 'Lane2/Props'."),
|
|
50
|
+
classes: z
|
|
51
|
+
.array(z.string())
|
|
52
|
+
.max(32)
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Only nodes of these classes, e.g. ['MeshInstance3D', 'OmniLight3D'] (* wildcards allowed)."),
|
|
55
|
+
fields: z
|
|
56
|
+
.array(z.string())
|
|
57
|
+
.max(16)
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Only these per-node fields (path is always kept): class, name, pos, rot_deg, scale, visible, aabb, scene_file, script_fp, material_fps, z_index, rect, view_rect, limit_rect."),
|
|
60
|
+
};
|
|
61
|
+
export const worldSnapshotInputSchema = z.object(worldSnapshotInputShape).strict();
|
|
62
|
+
export function buildWorldSnapshotOp(args) {
|
|
63
|
+
const op = { op: "GetWorldSnapshot" };
|
|
64
|
+
if (args.scene_path)
|
|
65
|
+
op.scene_path = args.scene_path;
|
|
66
|
+
// The stored snapshot is the diff baseline: never smaller than the engine
|
|
67
|
+
// default, larger only when the caller asks to see more than that.
|
|
68
|
+
if (args.max_nodes !== undefined && args.max_nodes > ENGINE_SNAPSHOT_MAX_NODES)
|
|
69
|
+
op.max_nodes = args.max_nodes;
|
|
70
|
+
return op;
|
|
71
|
+
}
|
|
72
|
+
function globMatcher(patterns) {
|
|
73
|
+
const regexes = patterns.map((glob) => new RegExp(`^${glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`));
|
|
74
|
+
return (value) => regexes.some((regex) => regex.test(value));
|
|
75
|
+
}
|
|
76
|
+
/** The GetWorldSnapshot payload inside an /api/ops envelope (or the envelope itself). */
|
|
77
|
+
function snapshotPayload(result) {
|
|
78
|
+
const root = asRecord(result);
|
|
79
|
+
if (!root)
|
|
80
|
+
return null;
|
|
81
|
+
if (Array.isArray(root.nodes))
|
|
82
|
+
return { holder: root, index: null };
|
|
83
|
+
const results = Array.isArray(root.results) ? root.results : [];
|
|
84
|
+
const index = results.findIndex((entry) => {
|
|
85
|
+
const record = asRecord(entry);
|
|
86
|
+
return record?.op === "GetWorldSnapshot" && Array.isArray(record.nodes);
|
|
87
|
+
});
|
|
88
|
+
return index >= 0 ? { holder: root, index } : null;
|
|
89
|
+
}
|
|
90
|
+
/** Filter and cap a successful snapshot for the agent. Anything that is not a
|
|
91
|
+
* snapshot payload (a failure, an older shape) passes through untouched. */
|
|
92
|
+
export function shapeWorldSnapshot(result, args) {
|
|
93
|
+
const found = snapshotPayload(result);
|
|
94
|
+
if (!found)
|
|
95
|
+
return result;
|
|
96
|
+
const payload = (found.index === null ? found.holder : asRecord(found.holder.results[found.index]));
|
|
97
|
+
const nodes = payload.nodes.map((node) => asRecord(node)).filter((node) => !!node);
|
|
98
|
+
const prefix = args.path_prefix !== undefined ? normalizeNodePath(args.path_prefix) : undefined;
|
|
99
|
+
const classMatch = args.classes && args.classes.length > 0 ? globMatcher(args.classes) : undefined;
|
|
100
|
+
const inSubtree = (path) => prefix === undefined || (typeof path === "string" && isAtOrBelow(normalizeNodePath(path), prefix));
|
|
101
|
+
const matched = nodes.filter((node) => inSubtree(node.path) && (!classMatch || classMatch(String(node.class ?? ""))));
|
|
102
|
+
const offset = args.offset ?? 0;
|
|
103
|
+
const maxNodes = args.max_nodes ?? WORLD_SNAPSHOT_DEFAULT_MAX_NODES;
|
|
104
|
+
const page = matched.slice(offset, offset + maxNodes);
|
|
105
|
+
const keep = args.fields && args.fields.length > 0 ? new Set(["path", ...args.fields]) : undefined;
|
|
106
|
+
const listed = keep
|
|
107
|
+
? page.map((node) => Object.fromEntries(Object.entries(node).filter(([key]) => keep.has(key))))
|
|
108
|
+
: page;
|
|
109
|
+
const engineTruncated = payload.truncated === true;
|
|
110
|
+
const end = offset + page.length;
|
|
111
|
+
const filtered = prefix !== undefined || classMatch !== undefined;
|
|
112
|
+
const shaped = {
|
|
113
|
+
...payload,
|
|
114
|
+
nodes: listed,
|
|
115
|
+
// truncated: this list is not every matching node (page cut, or the engine's own cap).
|
|
116
|
+
truncated: end < matched.length || engineTruncated,
|
|
117
|
+
engine_truncated: engineTruncated,
|
|
118
|
+
engine_listed_nodes: nodes.length,
|
|
119
|
+
matched_nodes: matched.length,
|
|
120
|
+
returned_nodes: listed.length,
|
|
121
|
+
offset,
|
|
122
|
+
next_offset: end < matched.length ? end : null,
|
|
123
|
+
...(filtered ? { filter: { ...(prefix !== undefined ? { path_prefix: prefix } : {}), ...(args.classes ? { classes: args.classes } : {}) } } : {}),
|
|
124
|
+
...(keep ? { fields: [...keep] } : {}),
|
|
125
|
+
};
|
|
126
|
+
if (filtered) {
|
|
127
|
+
const counts = {};
|
|
128
|
+
for (const node of matched) {
|
|
129
|
+
const cls = String(node.class ?? "?");
|
|
130
|
+
counts[cls] = (counts[cls] ?? 0) + 1;
|
|
131
|
+
}
|
|
132
|
+
shaped.matched_counts = counts;
|
|
133
|
+
}
|
|
134
|
+
if (prefix !== undefined) {
|
|
135
|
+
for (const key of ["lights", "cameras"]) {
|
|
136
|
+
if (Array.isArray(payload[key])) {
|
|
137
|
+
shaped[key] = payload[key].filter((entry) => inSubtree(asRecord(entry)?.path));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (end < matched.length || offset > 0 || filtered) {
|
|
142
|
+
shaped.note =
|
|
143
|
+
"nodes is a filtered/paged view; counts and total_nodes describe the whole scene, and snapshot_id covers the whole scene for summer_snapshot_diff.";
|
|
144
|
+
}
|
|
145
|
+
if (found.index === null)
|
|
146
|
+
return shaped;
|
|
147
|
+
const results = [...found.holder.results];
|
|
148
|
+
results[found.index] = shaped;
|
|
149
|
+
return { ...found.holder, results };
|
|
150
|
+
}
|
|
@@ -27,11 +27,11 @@ export declare const CLI_PROTOCOL_VERSION = 1;
|
|
|
27
27
|
* inside the engine monorepo (never send an op the engine has no branch for).
|
|
28
28
|
*
|
|
29
29
|
* Deliberately NOT listed: ops an agent may compose by hand through
|
|
30
|
-
* summer_batch / `summer tool batch` (
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* summer_batch / `summer tool batch` (DisconnectSignal, Undo, Git*,
|
|
31
|
+
* RunCommand, ExtractZipFromUrl, CustomBake, ...). The CLI only classifies
|
|
32
|
+
* those for dispatch (single-only / scene-mutation sets) and never sends them
|
|
33
|
+
* on its own, so listing them would warn about skew the CLI cannot cause; the
|
|
34
|
+
* engine's per-op "unknown op" error still covers them at call time.
|
|
35
35
|
*/
|
|
36
36
|
export declare const CLI_KNOWN_OP_NEEDS: readonly string[];
|
|
37
37
|
/** The `capabilities` block of /api/health, shape-checked. Every field is
|
|
@@ -27,16 +27,24 @@ export const CLI_PROTOCOL_VERSION = 1;
|
|
|
27
27
|
* inside the engine monorepo (never send an op the engine has no branch for).
|
|
28
28
|
*
|
|
29
29
|
* Deliberately NOT listed: ops an agent may compose by hand through
|
|
30
|
-
* summer_batch / `summer tool batch` (
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* summer_batch / `summer tool batch` (DisconnectSignal, Undo, Git*,
|
|
31
|
+
* RunCommand, ExtractZipFromUrl, CustomBake, ...). The CLI only classifies
|
|
32
|
+
* those for dispatch (single-only / scene-mutation sets) and never sends them
|
|
33
|
+
* on its own, so listing them would warn about skew the CLI cannot cause; the
|
|
34
|
+
* engine's per-op "unknown op" error still covers them at call time.
|
|
35
35
|
*/
|
|
36
36
|
export const CLI_KNOWN_OP_NEEDS = [
|
|
37
37
|
// Scene graph + properties
|
|
38
|
+
// (No ConnectSignal: it never saves the connection, so summer_connect_signal
|
|
39
|
+
// connects through RunSceneScript and summer_batch refuses the raw op —
|
|
40
|
+
// core/capabilities/connect-signal.ts.)
|
|
38
41
|
"AddNode", "RemoveNode", "ReplaceNode", "SetProp", "SetResourceProperty",
|
|
39
|
-
"
|
|
42
|
+
"SelectNode", "OpenScene", "SaveScene", "InstantiateScene",
|
|
43
|
+
// summer_replace_node's instantiate/move/remove path (core/capabilities/replace-node.ts)
|
|
44
|
+
// and summer_batch's ReparentNode re-owning (core/capabilities/scene-batch.ts)
|
|
45
|
+
"MoveNode", "ReparentNode",
|
|
46
|
+
// summer_grep (core/capabilities/grep.ts)
|
|
47
|
+
"Grep",
|
|
40
48
|
// Navigation (summer_open: core/capabilities/navigation/) — Navigate is the
|
|
41
49
|
// one-table op (engine navigate_ops.cpp); the other three are the legacy
|
|
42
50
|
// fallbacks on engines that predate it.
|
|
@@ -45,7 +45,7 @@ export declare class WorkerEngineClient {
|
|
|
45
45
|
getProjectState(_prefix?: string): Promise<unknown>;
|
|
46
46
|
getDiagnostics(): Promise<unknown>;
|
|
47
47
|
inspectNode(_path: string): Promise<unknown>;
|
|
48
|
-
|
|
48
|
+
inspectNodeResource(_nodePath: string, _property: string): Promise<unknown>;
|
|
49
49
|
getScriptErrors(_path: string): Promise<unknown>;
|
|
50
50
|
getSelection(): Promise<unknown>;
|
|
51
51
|
viewportSnapshot(): Promise<EngineSnapshot>;
|
|
@@ -222,8 +222,8 @@ export class WorkerEngineClient {
|
|
|
222
222
|
async inspectNode(_path) {
|
|
223
223
|
unsupported("inspectNode");
|
|
224
224
|
}
|
|
225
|
-
async
|
|
226
|
-
unsupported("
|
|
225
|
+
async inspectNodeResource(_nodePath, _property) {
|
|
226
|
+
unsupported("inspectNodeResource");
|
|
227
227
|
}
|
|
228
228
|
async getScriptErrors(_path) {
|
|
229
229
|
unsupported("getScriptErrors");
|