summer-engine 3.8.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -13
- package/.claude-plugin/plugin.json +23 -22
- package/.codex-plugin/plugin.json +4 -12
- package/.cursor-plugin/plugin.json +4 -12
- package/.factory-plugin/plugin.json +4 -12
- package/AGENTS.md +19 -12
- package/CHANGELOG.md +96 -0
- package/GEMINI.md +3 -3
- package/README.md +103 -33
- package/assets/icon.png +0 -0
- package/commands/start.md +71 -0
- package/commands/summer.md +3 -2
- package/dist/cli/banner.js +3 -0
- package/dist/cli/commands/login.d.ts +27 -0
- package/dist/cli/commands/login.js +58 -12
- package/dist/cli/commands/ops.d.ts +47 -0
- package/dist/cli/commands/ops.js +301 -0
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.js +37 -12
- package/dist/cli/commands/skills.js +11 -7
- package/dist/cli/index.js +7 -0
- package/dist/core/agent-host.d.ts +65 -0
- package/dist/core/agent-host.js +193 -0
- package/dist/core/capabilities/agent-playbook.d.ts +3 -0
- package/dist/core/capabilities/agent-playbook.js +54 -2
- package/dist/core/capabilities/apps.d.ts +25 -2
- package/dist/core/capabilities/apps.js +25 -12
- package/dist/core/capabilities/doctor.d.ts +3 -0
- package/dist/core/capabilities/doctor.js +20 -15
- package/dist/core/capabilities/fabricate-mesh.d.ts +2 -2
- package/dist/core/capabilities/game-task-plan.d.ts +12 -6
- package/dist/core/capabilities/game-task-plan.js +80 -5
- package/dist/core/capabilities/motion-generation.js +2 -2
- package/dist/core/capabilities/navigation/open.d.ts +6 -2
- package/dist/core/capabilities/navigation/open.js +3 -0
- package/dist/core/capabilities/publish-build.js +2 -0
- package/dist/core/capabilities/runtime-control.d.ts +4 -4
- package/dist/core/capabilities/scene-script-target.d.ts +17 -0
- package/dist/core/capabilities/scene-script-target.js +70 -0
- package/dist/core/capabilities/sfx-choices.d.ts +4 -0
- package/dist/core/capabilities/sfx-choices.js +13 -6
- package/dist/core/capabilities/tool-dispatch.d.ts +12 -3
- package/dist/core/capabilities/tool-dispatch.js +81 -46
- package/dist/core/embeddings.d.ts +2 -0
- package/dist/core/embeddings.js +1 -1
- package/dist/core/error-codes.d.ts +62 -0
- package/dist/core/error-codes.js +97 -0
- package/dist/core/format.d.ts +2 -0
- package/dist/core/format.js +2 -1
- package/dist/core/hosted-mcp.d.ts +22 -0
- package/dist/core/hosted-mcp.js +56 -1
- package/dist/core/library-read.js +5 -0
- package/dist/core/library-search.js +2 -1
- package/dist/core/oauth.js +4 -0
- package/dist/core/ops/catalog.d.ts +41 -0
- package/dist/core/ops/catalog.js +145 -0
- package/dist/core/ops/cloud.d.ts +56 -0
- package/dist/core/ops/cloud.js +175 -0
- package/dist/core/ops/contract.d.ts +119 -0
- package/dist/core/ops/contract.js +142 -0
- package/dist/core/ops/engine-answer.d.ts +25 -0
- package/dist/core/ops/engine-answer.js +117 -0
- package/dist/core/ops/engine.d.ts +55 -0
- package/dist/core/ops/engine.js +649 -0
- package/dist/core/ops/save-files.d.ts +78 -0
- package/dist/core/ops/save-files.js +419 -0
- package/dist/core/ops/search.d.ts +9 -0
- package/dist/core/ops/search.js +55 -0
- package/dist/core/skills-registry.d.ts +16 -1
- package/dist/core/skills-registry.js +35 -2
- package/dist/core/telemetry.d.ts +32 -0
- package/dist/core/telemetry.js +38 -10
- package/dist/core/tool-profiles.d.ts +25 -0
- package/dist/core/tool-profiles.js +41 -0
- package/dist/installer/agent-config.d.ts +17 -4
- package/dist/installer/agent-config.js +22 -6
- package/dist/installer/agent-table.d.ts +3 -0
- package/dist/installer/agent-table.js +10 -7
- package/dist/installer/setup.d.ts +2 -0
- package/dist/installer/setup.js +9 -4
- package/dist/installer/version-check.d.ts +29 -9
- package/dist/installer/version-check.js +81 -28
- package/dist/mcp/command.js +7 -0
- package/dist/mcp/hosted-mount.d.ts +27 -2
- package/dist/mcp/hosted-mount.js +53 -23
- package/dist/mcp/legacy-toolset.d.ts +27 -0
- package/dist/mcp/legacy-toolset.js +43 -0
- package/dist/mcp/ops-surface.d.ts +59 -0
- package/dist/mcp/ops-surface.js +443 -0
- package/dist/mcp/server.d.ts +35 -11
- package/dist/mcp/server.js +234 -89
- package/dist/mcp/tool-results.d.ts +26 -0
- package/dist/mcp/tool-results.js +81 -0
- package/dist/mcp/tool-surface.d.ts +65 -0
- package/dist/mcp/tool-surface.js +317 -0
- package/dist/mcp/tools/app-tools.js +9 -2
- package/dist/mcp/tools/asset-tools.js +55 -53
- package/dist/mcp/tools/audit-tools.js +21 -21
- package/dist/mcp/tools/debug-tools.js +6 -6
- package/dist/mcp/tools/fabricate-tools.js +6 -6
- package/dist/mcp/tools/generate-tools.js +76 -70
- package/dist/mcp/tools/project-tools.d.ts +9 -4
- package/dist/mcp/tools/project-tools.js +16 -10
- package/dist/mcp/tools/scene-tools.js +12 -23
- package/dist/mcp/tools/script-tools.js +9 -5
- package/dist/mcp/tools/with-engine.d.ts +6 -0
- package/dist/mcp/tools/with-engine.js +19 -0
- package/gemini-extension.json +4 -12
- package/hooks/pre-commit-doctor.sh +5 -4
- package/hooks/session-start.sh +24 -28
- package/integrations/claude-desktop/README.md +1 -1
- package/integrations/copilot-jetbrains/README.md +1 -1
- package/integrations/junie/README.md +1 -1
- package/integrations/lm-studio/README.md +1 -1
- package/integrations/trae/README.md +1 -1
- package/integrations/visual-studio/README.md +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +79 -51
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/product-map/resource.yaml +2 -2
- package/library/skills/3d-lighting/SKILL.md +53 -28
- package/library/skills/3d-lighting/resource.yaml +1 -1
- package/library/skills/README.md +119 -117
- package/library/skills/adaptive-music/SKILL.md +19 -23
- package/library/skills/adaptive-music/resource.yaml +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/agent-playtesting/resource.yaml +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/resource.yaml +1 -1
- package/library/skills/ambient-bed/SKILL.md +22 -13
- package/library/skills/ambient-bed/resource.yaml +1 -1
- package/library/skills/animated-loop/SKILL.md +41 -32
- package/library/skills/animated-loop/resource.yaml +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/animation-tree/resource.yaml +1 -1
- package/library/skills/art-direction/SKILL.md +3 -3
- package/library/skills/art-direction/resource.yaml +1 -1
- package/library/skills/asset-strategy/SKILL.md +21 -4
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +10 -10
- package/library/skills/audio-direction/resource.yaml +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/resource.yaml +1 -1
- package/library/skills/bounded-engine-runs/SKILL.md +1 -1
- package/library/skills/bounded-engine-runs/resource.yaml +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorm-game/resource.yaml +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/brainstorming/resource.yaml +1 -1
- package/library/skills/browse-templates/SKILL.md +1 -1
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +2 -2
- package/library/skills/camera-rigs/resource.yaml +1 -1
- package/library/skills/celeste-momentum-platforming/resource.yaml +1 -0
- package/library/skills/character-animation-wiring/SKILL.md +4 -4
- package/library/skills/character-animation-wiring/resource.yaml +1 -1
- package/library/skills/character-model/SKILL.md +55 -47
- package/library/skills/character-model/resource.yaml +1 -1
- package/library/skills/character-movement/SKILL.md +3 -3
- package/library/skills/character-movement/resource.yaml +1 -1
- package/library/skills/character-portrait/SKILL.md +24 -19
- package/library/skills/character-portrait/resource.yaml +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +58 -46
- package/library/skills/cinematic-cutscene/resource.yaml +1 -1
- package/library/skills/combat-basics/SKILL.md +1 -1
- package/library/skills/combat-basics/resource.yaml +1 -1
- package/library/skills/compatibility-renderer-traps/SKILL.md +1 -1
- package/library/skills/compatibility-renderer-traps/resource.yaml +1 -1
- package/library/skills/concept-art/SKILL.md +16 -12
- package/library/skills/concept-art/resource.yaml +1 -1
- package/library/skills/concept-to-level/SKILL.md +239 -0
- package/library/skills/concept-to-level/references/technical-rules.md +150 -0
- package/library/skills/concept-to-level/resource.yaml +44 -0
- package/library/skills/create-asset-sheet/SKILL.md +19 -13
- package/library/skills/create-asset-sheet/resource.yaml +1 -1
- package/library/skills/debug/SKILL.md +2 -2
- package/library/skills/debug/resource.yaml +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/resource.yaml +1 -1
- package/library/skills/design-level/SKILL.md +23 -35
- package/library/skills/design-level/resource.yaml +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-mechanic/resource.yaml +1 -1
- package/library/skills/design-npc/SKILL.md +22 -24
- package/library/skills/design-npc/resource.yaml +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/resource.yaml +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/resource.yaml +1 -1
- package/library/skills/environment-kit/SKILL.md +20 -16
- package/library/skills/environment-kit/resource.yaml +1 -1
- package/library/skills/export-and-ship/SKILL.md +6 -6
- package/library/skills/export-and-ship/resource.yaml +1 -1
- package/library/skills/fabricating-assets/SKILL.md +7 -7
- package/library/skills/fabricating-assets/resource.yaml +3 -3
- package/library/skills/facial-and-lipsync/SKILL.md +14 -15
- package/library/skills/facial-and-lipsync/resource.yaml +1 -1
- package/library/skills/fps-controller/SKILL.md +33 -41
- package/library/skills/fps-controller/resource.yaml +1 -1
- package/library/skills/game-feel/SKILL.md +8 -8
- package/library/skills/game-feel/resource.yaml +1 -1
- package/library/skills/gameskill/SKILL.md +4 -4
- package/library/skills/gameskill/resource.yaml +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/resource.yaml +1 -1
- package/library/skills/generate-motion/SKILL.md +30 -21
- package/library/skills/generate-motion/resource.yaml +1 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/godogen-visual-proof-game-generation/resource.yaml +1 -0
- package/library/skills/grow-analytics/SKILL.md +3 -3
- package/library/skills/grow-analytics/resource.yaml +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/headless-scripting/resource.yaml +1 -1
- package/library/skills/in-game-purchases/SKILL.md +1 -1
- package/library/skills/in-game-purchases/resource.yaml +1 -1
- package/library/skills/instantiate-asset-pack/SKILL.md +2 -2
- package/library/skills/instantiate-asset-pack/resource.yaml +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +1 -0
- package/library/skills/make-game/SKILL.md +4 -4
- package/library/skills/make-game/references/2d-platformer.md +1 -1
- package/library/skills/make-game/references/3d-fps.md +1 -1
- package/library/skills/make-game/references/3d-horror.md +1 -1
- package/library/skills/make-game/references/3d-third-person.md +2 -2
- package/library/skills/make-game/resource.yaml +1 -1
- package/library/skills/multiplayer/SKILL.md +1 -1
- package/library/skills/multiplayer/resource.yaml +1 -1
- package/library/skills/multiplayer-movement/SKILL.md +1 -1
- package/library/skills/multiplayer-movement/resource.yaml +1 -1
- package/library/skills/multiplayer-project/SKILL.md +1 -1
- package/library/skills/multiplayer-project/resource.yaml +1 -1
- package/library/skills/multiplayer-publish/SKILL.md +8 -7
- package/library/skills/multiplayer-publish/resource.yaml +3 -3
- package/library/skills/multiplayer-state/SKILL.md +1 -1
- package/library/skills/multiplayer-state/resource.yaml +1 -1
- package/library/skills/multiplayer-testing/SKILL.md +1 -1
- package/library/skills/multiplayer-testing/resource.yaml +1 -1
- package/library/skills/music-track/SKILL.md +22 -13
- package/library/skills/music-track/resource.yaml +1 -1
- package/library/skills/navigate-summer/SKILL.md +11 -9
- package/library/skills/navigate-summer/resource.yaml +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/new-project/resource.yaml +1 -1
- package/library/skills/organic-model/SKILL.md +24 -18
- package/library/skills/organic-model/resource.yaml +1 -1
- package/library/skills/pixel-art/SKILL.md +27 -23
- package/library/skills/pixel-art/resource.yaml +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/play/resource.yaml +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/resource.yaml +1 -1
- package/library/skills/procedural-animation/SKILL.md +20 -16
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +25 -19
- package/library/skills/prop-model/resource.yaml +1 -1
- package/library/skills/psx-retro-rendering/resource.yaml +1 -0
- package/library/skills/publish-your-game/SKILL.md +16 -6
- package/library/skills/publish-your-game/resource.yaml +3 -3
- package/library/skills/realtime-wet-surfaces/resource.yaml +1 -0
- package/library/skills/release-readiness/SKILL.md +1 -1
- package/library/skills/release-readiness/resource.yaml +1 -1
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/remote-deploy/resource.yaml +1 -1
- package/library/skills/retarget/SKILL.md +23 -20
- package/library/skills/retarget/resource.yaml +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/resource.yaml +1 -1
- package/library/skills/save-load/SKILL.md +1 -1
- package/library/skills/save-load/resource.yaml +1 -1
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-composition/resource.yaml +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +2 -2
- package/library/skills/scene-hierarchy-design/resource.yaml +1 -1
- package/library/skills/scene-scripting/SKILL.md +66 -636
- package/library/skills/scene-scripting/references/ctx-api.md +184 -0
- package/library/skills/scene-scripting/references/recipes-2d-ui.md +193 -0
- package/library/skills/scene-scripting/references/recipes-3d.md +280 -0
- package/library/skills/scene-scripting/resource.yaml +4 -4
- package/library/skills/scene-to-level/SKILL.md +2 -2
- package/library/skills/scene-to-level/resource.yaml +1 -1
- package/library/skills/skill-create/resource.yaml +1 -0
- package/library/skills/skill-improve/resource.yaml +1 -0
- package/library/skills/skill-test/SKILL.md +2 -2
- package/library/skills/skill-test/resource.yaml +1 -0
- package/library/skills/skintokens-auto-rigging/resource.yaml +1 -0
- package/library/skills/skybox-panorama/SKILL.md +27 -21
- package/library/skills/skybox-panorama/resource.yaml +1 -1
- package/library/skills/sound-effect/SKILL.md +29 -20
- package/library/skills/sound-effect/resource.yaml +1 -1
- package/library/skills/spatial-placement/SKILL.md +14 -9
- package/library/skills/spatial-placement/resource.yaml +1 -1
- package/library/skills/sprite-sheet/SKILL.md +32 -27
- package/library/skills/sprite-sheet/resource.yaml +1 -1
- package/library/skills/store-art/SKILL.md +3 -3
- package/library/skills/store-art/resource.yaml +2 -2
- package/library/skills/store-listing/SKILL.md +3 -3
- package/library/skills/store-listing/resource.yaml +2 -2
- package/library/skills/summer-analytics/SKILL.md +7 -5
- package/library/skills/summer-analytics/resource.yaml +2 -2
- package/library/skills/summer-apps/SKILL.md +48 -32
- package/library/skills/summer-apps/manifest.md +1 -1
- package/library/skills/summer-apps/minimal-app.md +7 -7
- package/library/skills/summer-apps/resource.yaml +1 -1
- package/library/skills/summer-apps/sdk-reference.md +5 -5
- package/library/skills/summer-apps-design/SKILL.md +1 -1
- package/library/skills/summer-apps-design/resource.yaml +1 -1
- package/library/skills/summer-assets/SKILL.md +56 -0
- package/library/skills/summer-assets/resource.yaml +34 -0
- package/library/skills/summer-store/SKILL.md +1 -1
- package/library/skills/text-to-motion/SKILL.md +30 -23
- package/library/skills/text-to-motion/prompts-and-controls.md +5 -5
- package/library/skills/text-to-motion/resource.yaml +1 -1
- package/library/skills/text-to-motion/rigging-for-text-motion.md +2 -2
- package/library/skills/tileable-texture/SKILL.md +19 -15
- package/library/skills/tileable-texture/resource.yaml +1 -1
- package/library/skills/trailer-shot/SKILL.md +28 -24
- package/library/skills/trailer-shot/resource.yaml +1 -1
- package/library/skills/tune-performance/SKILL.md +6 -6
- package/library/skills/tune-performance/resource.yaml +1 -1
- package/library/skills/ui-basics/SKILL.md +41 -36
- package/library/skills/ui-basics/resource.yaml +1 -1
- package/library/skills/ui-graphics/SKILL.md +47 -38
- package/library/skills/ui-graphics/resource.yaml +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/use-widget-asset/resource.yaml +1 -1
- package/library/skills/using-summer/SKILL.md +6 -5
- package/library/skills/using-summer/resource.yaml +1 -1
- package/library/skills/vehicle-model/SKILL.md +65 -57
- package/library/skills/vehicle-model/resource.yaml +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verification-before-completion/resource.yaml +1 -1
- package/library/skills/verifying-scenes/SKILL.md +2 -2
- package/library/skills/verifying-scenes/resource.yaml +1 -1
- package/library/skills/vfx-dissolve/SKILL.md +2 -2
- package/library/skills/vfx-dissolve/resource.yaml +1 -1
- package/library/skills/vfx-fire/SKILL.md +15 -41
- package/library/skills/vfx-fire/resource.yaml +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/resource.yaml +1 -1
- package/library/skills/vfx-lightning/SKILL.md +7 -16
- package/library/skills/vfx-lightning/resource.yaml +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +23 -32
- package/library/skills/vfx-magic-glow/resource.yaml +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +20 -35
- package/library/skills/vfx-muzzle-flash/resource.yaml +1 -1
- package/library/skills/vfx-smoke/SKILL.md +13 -31
- package/library/skills/vfx-smoke/resource.yaml +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +12 -31
- package/library/skills/vfx-water-ripple/resource.yaml +1 -1
- package/library/skills/voice-line/SKILL.md +71 -40
- package/library/skills/voice-line/resource.yaml +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/resource.yaml +1 -1
- package/library/skills/web-compatibility-look/SKILL.md +1 -1
- package/library/skills/web-compatibility-look/resource.yaml +1 -1
- package/library/skills/world-building-3d/SKILL.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +1 -1
- package/library/skills/writing-skills/resource.yaml +1 -0
- package/library/tools/add-node/GUIDE.md +14 -0
- package/library/tools/align-distribute-3d/GUIDE.md +7 -0
- package/library/tools/app-call/resource.yaml +1 -1
- package/library/tools/app-guide/resource.yaml +2 -2
- package/library/tools/app-page/resource.yaml +1 -1
- package/library/tools/app-screenshot/resource.yaml +19 -1
- package/library/tools/app-tools/resource.yaml +1 -1
- package/library/tools/attach-to-surface/GUIDE.md +15 -0
- package/library/tools/batch/GUIDE.md +27 -0
- package/library/tools/camera-bookmark/GUIDE.md +12 -0
- package/library/tools/capture-gameplay/GUIDE.md +3 -0
- package/library/tools/capture-gameplay/resource.yaml +3 -3
- package/library/tools/check-job/resource.yaml +1 -1
- package/library/tools/clear-console/GUIDE.md +3 -0
- package/library/tools/connect-ports/GUIDE.md +9 -0
- package/library/tools/connect-signal/GUIDE.md +14 -0
- package/library/tools/create-debug-report/GUIDE.md +10 -0
- package/library/tools/create-scene/GUIDE.md +10 -0
- package/library/tools/debug-views/GUIDE.md +7 -0
- package/library/tools/enable-plugin/GUIDE.md +3 -0
- package/library/tools/export-game/GUIDE.md +3 -0
- package/library/tools/export-game/resource.yaml +3 -3
- package/library/tools/export-templates/GUIDE.md +3 -0
- package/library/tools/fabricate-3d/GUIDE.md +19 -0
- package/library/tools/frame-nodes/GUIDE.md +9 -0
- package/library/tools/frame-shot/GUIDE.md +9 -0
- package/library/tools/game-control/GUIDE.md +9 -0
- package/library/tools/game-input/GUIDE.md +9 -0
- package/library/tools/game-probe/GUIDE.md +9 -0
- package/library/tools/generate-3d/resource.yaml +3 -3
- package/library/tools/generate-motion/resource.yaml +2 -2
- package/library/tools/get-console/GUIDE.md +9 -0
- package/library/tools/get-console/resource.yaml +5 -0
- package/library/tools/get-debugger-errors/GUIDE.md +7 -0
- package/library/tools/get-debugger-errors/resource.yaml +5 -0
- package/library/tools/get-debugger-warnings/GUIDE.md +7 -0
- package/library/tools/get-debugger-warnings/resource.yaml +5 -0
- package/library/tools/get-diagnostics/GUIDE.md +13 -0
- package/library/tools/get-project-context/GUIDE.md +25 -0
- package/library/tools/get-runtime-tree/GUIDE.md +7 -0
- package/library/tools/get-runtime-tree/resource.yaml +5 -0
- package/library/tools/get-scene-tree/GUIDE.md +11 -0
- package/library/tools/get-scene-tree/resource.yaml +5 -0
- package/library/tools/get-script-errors/GUIDE.md +5 -0
- package/library/tools/grep/GUIDE.md +11 -0
- package/library/tools/import-asset/GUIDE.md +8 -0
- package/library/tools/import-asset-by-id/GUIDE.md +12 -0
- package/library/tools/import-from-url/GUIDE.md +10 -0
- package/library/tools/import-from-url-batch/GUIDE.md +3 -0
- package/library/tools/import-hdri/GUIDE.md +16 -0
- package/library/tools/input-map-bind/GUIDE.md +11 -0
- package/library/tools/inspect-asset/GUIDE.md +14 -0
- package/library/tools/inspect-asset/resource.yaml +5 -0
- package/library/tools/inspect-node/GUIDE.md +11 -0
- package/library/tools/inspect-node/resource.yaml +5 -0
- package/library/tools/inspect-resource/GUIDE.md +7 -0
- package/library/tools/inspect-resource/resource.yaml +5 -0
- package/library/tools/inspect-runtime-node/GUIDE.md +7 -0
- package/library/tools/inspect-runtime-node/resource.yaml +5 -0
- package/library/tools/instantiate-scene/GUIDE.md +12 -0
- package/library/tools/is-running/GUIDE.md +3 -0
- package/library/tools/measure/GUIDE.md +8 -0
- package/library/tools/navigation-probe/GUIDE.md +9 -0
- package/library/tools/open/resource.yaml +2 -2
- package/library/tools/open-main-scene/GUIDE.md +6 -0
- package/library/tools/open-scene/GUIDE.md +8 -0
- package/library/tools/place-adjacent/GUIDE.md +7 -0
- package/library/tools/play/GUIDE.md +13 -0
- package/library/tools/project-setting/GUIDE.md +9 -0
- package/library/tools/publish-build/resource.yaml +6 -6
- package/library/tools/raycast/GUIDE.md +7 -0
- package/library/tools/read-file/GUIDE.md +10 -0
- package/library/tools/read-library/resource.yaml +5 -0
- package/library/tools/recent-events/GUIDE.md +7 -0
- package/library/tools/remove-node/GUIDE.md +3 -0
- package/library/tools/repeat-along/GUIDE.md +5 -0
- package/library/tools/replace-node/GUIDE.md +9 -0
- package/library/tools/replace-text/GUIDE.md +5 -0
- package/library/tools/run-editor-script/GUIDE.md +16 -0
- package/library/tools/run-script/GUIDE.md +30 -0
- package/library/tools/run-script/resource.yaml +6 -3
- package/library/tools/runtime-animate/GUIDE.md +7 -0
- package/library/tools/runtime-call/GUIDE.md +7 -0
- package/library/tools/runtime-set/GUIDE.md +9 -0
- package/library/tools/runtime-spawn/GUIDE.md +9 -0
- package/library/tools/save-scene/GUIDE.md +3 -0
- package/library/tools/scene-audit/GUIDE.md +29 -0
- package/library/tools/screenshot/GUIDE.md +17 -0
- package/library/tools/select-node/GUIDE.md +3 -0
- package/library/tools/set-prop/GUIDE.md +23 -0
- package/library/tools/set-resource-property/GUIDE.md +8 -0
- package/library/tools/shot-sheet/GUIDE.md +7 -0
- package/library/tools/snap-to-surface/GUIDE.md +17 -0
- package/library/tools/snapshot-diff/GUIDE.md +7 -0
- package/library/tools/starcast/GUIDE.md +12 -0
- package/library/tools/stop/GUIDE.md +3 -0
- package/library/tools/test-placement/GUIDE.md +7 -0
- package/library/tools/ui-actions/GUIDE.md +12 -0
- package/library/tools/ui-activate/GUIDE.md +11 -0
- package/library/tools/ui-screenshot/GUIDE.md +9 -0
- package/library/tools/ui-tree/GUIDE.md +10 -0
- package/library/tools/wait-for-event/GUIDE.md +11 -0
- package/library/tools/world-snapshot/GUIDE.md +9 -0
- package/library/tools/write-file/GUIDE.md +5 -0
- package/library/tools/zoom/GUIDE.md +5 -0
- package/package.json +6 -4
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +4 -12
- package/registry/generated/index.json +433 -339
- package/registry/generated/marketplace.claude.json +2 -13
- package/registry/generated/plugin.claude.json +23 -22
- package/registry/generated/plugin.codex.json +4 -12
- package/registry/generated/plugin.cursor.json +4 -12
- package/registry/generated/plugin.factory.json +4 -12
- package/registry/generated/server.json +34 -0
- package/registry/generated/skills-index.md +119 -117
- package/registry/generated/skills-registry.json +153 -115
- package/registry/operations/fixtures/invalid-deprecated-without-replacement.json +60 -0
- package/registry/operations/fixtures/invalid-name.json +60 -0
- package/registry/operations/fixtures/invalid-publish-without-owner.json +82 -0
- package/registry/operations/fixtures/invalid-spend-without-estimate.json +42 -0
- package/registry/operations/fixtures/invalid-unknown-field.json +61 -0
- package/registry/operations/fixtures/valid-player-facing.json +82 -0
- package/registry/operations/fixtures/valid-read.json +60 -0
- package/registry/operations/fixtures/valid-spend.json +43 -0
- package/registry/operations/legacy-names.json +1206 -0
- package/registry/schemas/operation.schema.json +318 -0
- package/registry/schemas/skill.schema.json +4 -0
- package/CLAUDE.md +0 -7
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadLibraryIndex } from "../core/library-search.js";
|
|
3
|
+
import { PROFILES, TOOLSETS, parseProfile } from "../core/tool-profiles.js";
|
|
4
|
+
/**
|
|
5
|
+
* The shape of the MCP tool surface: which toolset each tool belongs to, each
|
|
6
|
+
* local tool's annotations, which results are capped, and which calls count
|
|
7
|
+
* as single scene edits. Read by server.ts (registration, results) and, with
|
|
8
|
+
* the profiles in core/tool-profiles.ts, by hosted-mount.ts (which hosted
|
|
9
|
+
* tools a profile mounts).
|
|
10
|
+
*
|
|
11
|
+
* Tool names never change here: a profile only leaves tools out.
|
|
12
|
+
*/
|
|
13
|
+
export { TOOLSETS, PROFILES, PROFILE_TOOL_LIMIT } from "../core/tool-profiles.js";
|
|
14
|
+
const LOCAL_TOOLSETS = {
|
|
15
|
+
core: [
|
|
16
|
+
"summer_search_library",
|
|
17
|
+
"summer_read_library",
|
|
18
|
+
"summer_library_feedback",
|
|
19
|
+
"summer_get_agent_playbook",
|
|
20
|
+
"summer_start_game_task",
|
|
21
|
+
"summer_open",
|
|
22
|
+
],
|
|
23
|
+
assets: [
|
|
24
|
+
"summer_generate_3d",
|
|
25
|
+
"summer_generate_audio",
|
|
26
|
+
"summer_generate_image",
|
|
27
|
+
"summer_generate_motion",
|
|
28
|
+
"summer_generate_sfx_choices",
|
|
29
|
+
"summer_generate_video",
|
|
30
|
+
"summer_check_job",
|
|
31
|
+
"summer_get_choices",
|
|
32
|
+
"summer_slice_asset_sheet",
|
|
33
|
+
"summer_get_studio_workflow",
|
|
34
|
+
"summer_search_assets",
|
|
35
|
+
"summer_list_my_assets",
|
|
36
|
+
"summer_get_asset",
|
|
37
|
+
"summer_get_asset_download_url",
|
|
38
|
+
],
|
|
39
|
+
ship: ["summer_export_game", "summer_export_templates", "summer_publish_build", "summer_capture_gameplay"],
|
|
40
|
+
ui: ["summer_ui_actions", "summer_ui_activate", "summer_ui_screenshot", "summer_ui_tree"],
|
|
41
|
+
};
|
|
42
|
+
const localToolset = new Map(Object.entries(LOCAL_TOOLSETS).flatMap(([set, names]) => names.map((name) => [name, set])));
|
|
43
|
+
/** Hosted tool names by pattern, first match wins. Unknown names land in `other` (full only). */
|
|
44
|
+
const HOSTED_TOOLSETS = [
|
|
45
|
+
[/^summer_(account|team)$/, "core"],
|
|
46
|
+
[/^summer_(remove_background|update_asset|delete_asset|upload_image_\w+)$/, "assets"],
|
|
47
|
+
[/^summer_tool_\w+$/, "tools"],
|
|
48
|
+
[/^summer_(store_version_(begin_upload|sign_parts|complete)|build_(begin_upload|sign_parts|complete_upload))$/, "uploads"],
|
|
49
|
+
[/^summer_(store_\w+|list_games)$/, "ship"],
|
|
50
|
+
[/^summer_board_\w+$/, "board"],
|
|
51
|
+
[/^summer_grow_\w+$/, "grow"],
|
|
52
|
+
[/^summer_studio_\w+$/, "studio"],
|
|
53
|
+
[/^summer_app_\w+$/, "apps"],
|
|
54
|
+
[/^summer_(build_\w+|make_game|game|game_\w+|list_projects|get_project)$/, "cloud"],
|
|
55
|
+
];
|
|
56
|
+
/** The toolset a tool belongs to: local tools by the table above, hosted ones by name. */
|
|
57
|
+
export function toolsetOf(name, local) {
|
|
58
|
+
if (local) {
|
|
59
|
+
if (localToolset.has(name))
|
|
60
|
+
return localToolset.get(name);
|
|
61
|
+
return name.startsWith("summer_app_") ? "apps" : "engine";
|
|
62
|
+
}
|
|
63
|
+
if (localToolset.has(name))
|
|
64
|
+
return localToolset.get(name);
|
|
65
|
+
return HOSTED_TOOLSETS.find(([pattern]) => pattern.test(name))?.[1] ?? "other";
|
|
66
|
+
}
|
|
67
|
+
const splitList = (value) => (value ?? "")
|
|
68
|
+
.split(",")
|
|
69
|
+
.map((part) => part.trim().toLowerCase())
|
|
70
|
+
.filter(Boolean);
|
|
71
|
+
/**
|
|
72
|
+
* `summer mcp --profile <p> --toolsets <a,b>` (env SUMMER_PROFILE,
|
|
73
|
+
* SUMMER_TOOLSETS). Without either: the full profile. `--toolsets` alone
|
|
74
|
+
* selects those toolsets plus core; with a profile it adds to it. `all` is
|
|
75
|
+
* every toolset. Unknown names are ignored and reported, never fatal: a
|
|
76
|
+
* typo must not stop an agent's MCP server from starting.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveToolSelection(options = {}, env = process.env) {
|
|
79
|
+
const ignored = [];
|
|
80
|
+
const profileName = (options.profile ?? env.SUMMER_PROFILE ?? "").trim().toLowerCase();
|
|
81
|
+
const requested = splitList(options.toolsets ?? env.SUMMER_TOOLSETS);
|
|
82
|
+
const profile = parseProfile(profileName);
|
|
83
|
+
if (profileName && !profile)
|
|
84
|
+
ignored.push(profileName);
|
|
85
|
+
const extra = new Set();
|
|
86
|
+
let all = false;
|
|
87
|
+
for (const name of requested) {
|
|
88
|
+
if (name === "all")
|
|
89
|
+
all = true;
|
|
90
|
+
else if (TOOLSETS.includes(name))
|
|
91
|
+
extra.add(name);
|
|
92
|
+
else
|
|
93
|
+
ignored.push(name);
|
|
94
|
+
}
|
|
95
|
+
if (all || profile === "full" || (profile === null && extra.size === 0)) {
|
|
96
|
+
return { profile: "full", toolsets: null, ignored };
|
|
97
|
+
}
|
|
98
|
+
if (profile === null)
|
|
99
|
+
return { profile: "custom", toolsets: new Set(["core", ...extra]), ignored };
|
|
100
|
+
return { profile, toolsets: new Set([...PROFILES[profile], ...extra]), ignored };
|
|
101
|
+
}
|
|
102
|
+
/** True when the selection keeps this tool. */
|
|
103
|
+
export function selectionIncludes(selection, name, local) {
|
|
104
|
+
return selection.toolsets === null || selection.toolsets.has(toolsetOf(name, local));
|
|
105
|
+
}
|
|
106
|
+
// ── Annotations ──────────────────────────────────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Local tools carry the registry's five `authority` booleans
|
|
109
|
+
* (library/tools/<slug>/resource.yaml). They say what a tool may touch, not
|
|
110
|
+
* whether it only reads, so a few sets refine them; the result is the MCP
|
|
111
|
+
* effect hints clients use for approvals.
|
|
112
|
+
*/
|
|
113
|
+
/** Tools whose only file output is an image or render for the agent; they never change the game. */
|
|
114
|
+
const EVIDENCE_ONLY = new Set([
|
|
115
|
+
"summer_screenshot",
|
|
116
|
+
"summer_debug_views",
|
|
117
|
+
"summer_shot_sheet",
|
|
118
|
+
"summer_zoom",
|
|
119
|
+
"summer_scene_audit",
|
|
120
|
+
"summer_game_probe",
|
|
121
|
+
"summer_capture_gameplay",
|
|
122
|
+
"summer_app_screenshot",
|
|
123
|
+
]);
|
|
124
|
+
/** Generation on Summer's servers: costs credits, creates new assets. */
|
|
125
|
+
const SPENDS_CREDITS = new Set([
|
|
126
|
+
"summer_generate_3d",
|
|
127
|
+
"summer_generate_audio",
|
|
128
|
+
"summer_generate_image",
|
|
129
|
+
"summer_generate_motion",
|
|
130
|
+
"summer_generate_sfx_choices",
|
|
131
|
+
"summer_generate_video",
|
|
132
|
+
]);
|
|
133
|
+
/** Network-only tools that change the person's Summer data (the rest of the network tools read). */
|
|
134
|
+
const NETWORK_WRITES = new Set([
|
|
135
|
+
"summer_app_create",
|
|
136
|
+
"summer_app_data",
|
|
137
|
+
"summer_app_page",
|
|
138
|
+
"summer_app_call",
|
|
139
|
+
"summer_app_report_gap",
|
|
140
|
+
"summer_app_update",
|
|
141
|
+
"summer_slice_asset_sheet",
|
|
142
|
+
]);
|
|
143
|
+
/** Changes that only add something new, or only touch the editor view or the running game. */
|
|
144
|
+
const ADDITIVE_OR_TRANSIENT = new Set([
|
|
145
|
+
"summer_add_node",
|
|
146
|
+
"summer_create_scene",
|
|
147
|
+
"summer_instantiate_scene",
|
|
148
|
+
"summer_connect_signal",
|
|
149
|
+
"summer_input_map_bind",
|
|
150
|
+
"summer_import_asset",
|
|
151
|
+
"summer_import_asset_by_id",
|
|
152
|
+
"summer_import_from_url",
|
|
153
|
+
"summer_import_from_url_batch",
|
|
154
|
+
"summer_import_hdri",
|
|
155
|
+
"summer_repeat_along",
|
|
156
|
+
"summer_fabricate_3d",
|
|
157
|
+
"summer_frame_nodes",
|
|
158
|
+
"summer_frame_shot",
|
|
159
|
+
"summer_save_scene",
|
|
160
|
+
"summer_enable_plugin",
|
|
161
|
+
"summer_export_game",
|
|
162
|
+
"summer_export_templates",
|
|
163
|
+
"summer_create_debug_report",
|
|
164
|
+
"summer_library_feedback",
|
|
165
|
+
"summer_app_create",
|
|
166
|
+
"summer_app_report_gap",
|
|
167
|
+
"summer_app_dock",
|
|
168
|
+
"summer_slice_asset_sheet",
|
|
169
|
+
"summer_open_scene",
|
|
170
|
+
"summer_open_main_scene",
|
|
171
|
+
"summer_select_node",
|
|
172
|
+
"summer_clear_console",
|
|
173
|
+
"summer_play",
|
|
174
|
+
"summer_stop",
|
|
175
|
+
"summer_game_control",
|
|
176
|
+
"summer_game_input",
|
|
177
|
+
"summer_runtime_animate",
|
|
178
|
+
"summer_runtime_call",
|
|
179
|
+
"summer_runtime_set",
|
|
180
|
+
"summer_runtime_spawn",
|
|
181
|
+
]);
|
|
182
|
+
/** Changes where a second identical call has no further effect. */
|
|
183
|
+
const IDEMPOTENT_WRITES = new Set([
|
|
184
|
+
"summer_set_prop",
|
|
185
|
+
"summer_set_resource_property",
|
|
186
|
+
"summer_project_setting",
|
|
187
|
+
"summer_remove_node",
|
|
188
|
+
"summer_write_file",
|
|
189
|
+
"summer_create_scene",
|
|
190
|
+
"summer_save_scene",
|
|
191
|
+
"summer_open_scene",
|
|
192
|
+
"summer_open_main_scene",
|
|
193
|
+
"summer_select_node",
|
|
194
|
+
"summer_clear_console",
|
|
195
|
+
"summer_stop",
|
|
196
|
+
"summer_enable_plugin",
|
|
197
|
+
"summer_place_adjacent",
|
|
198
|
+
"summer_attach_to_surface",
|
|
199
|
+
"summer_connect_ports",
|
|
200
|
+
"summer_align_distribute_3d",
|
|
201
|
+
"summer_snap_to_surface",
|
|
202
|
+
"summer_camera_bookmark",
|
|
203
|
+
"summer_frame_nodes",
|
|
204
|
+
"summer_runtime_set",
|
|
205
|
+
"summer_export_game",
|
|
206
|
+
"summer_export_templates",
|
|
207
|
+
"summer_app_update",
|
|
208
|
+
"summer_app_dock",
|
|
209
|
+
]);
|
|
210
|
+
/** Every name the refinement sets mention (a test checks each is a real tool). */
|
|
211
|
+
export const ANNOTATION_REFINEMENTS = new Set([
|
|
212
|
+
...EVIDENCE_ONLY,
|
|
213
|
+
...SPENDS_CREDITS,
|
|
214
|
+
...NETWORK_WRITES,
|
|
215
|
+
...ADDITIVE_OR_TRANSIENT,
|
|
216
|
+
...IDEMPOTENT_WRITES,
|
|
217
|
+
]);
|
|
218
|
+
/** MCP annotations for one local tool from its registry authority. */
|
|
219
|
+
export function annotationsFor(name, authority) {
|
|
220
|
+
const openWorldHint = Boolean(authority.network || authority.publish);
|
|
221
|
+
if (authority.publish) {
|
|
222
|
+
return { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true };
|
|
223
|
+
}
|
|
224
|
+
if (SPENDS_CREDITS.has(name)) {
|
|
225
|
+
return { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
|
|
226
|
+
}
|
|
227
|
+
const writes = !EVIDENCE_ONLY.has(name) &&
|
|
228
|
+
Boolean(authority.filesystem || authority.editor_mutation || authority.credentials || NETWORK_WRITES.has(name));
|
|
229
|
+
if (!writes) {
|
|
230
|
+
return { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint };
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
readOnlyHint: false,
|
|
234
|
+
destructiveHint: !ADDITIVE_OR_TRANSIENT.has(name),
|
|
235
|
+
idempotentHint: IDEMPOTENT_WRITES.has(name),
|
|
236
|
+
openWorldHint,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
let authorityCache = null;
|
|
240
|
+
/** Registry authority per MCP tool name (registry/generated/index.json). */
|
|
241
|
+
export function registryAuthorities() {
|
|
242
|
+
if (authorityCache)
|
|
243
|
+
return authorityCache;
|
|
244
|
+
const map = new Map();
|
|
245
|
+
try {
|
|
246
|
+
for (const entry of loadLibraryIndex()) {
|
|
247
|
+
if (entry.kind === "tool" && entry.mcp_tool_name && entry.authority)
|
|
248
|
+
map.set(entry.mcp_tool_name, entry.authority);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// A broken install still serves tools; they go out without annotations.
|
|
253
|
+
}
|
|
254
|
+
authorityCache = map;
|
|
255
|
+
return map;
|
|
256
|
+
}
|
|
257
|
+
// ── Output caps ──────────────────────────────────────────────────────────────
|
|
258
|
+
/** Default text cap for tools whose results can be large. */
|
|
259
|
+
export const DEFAULT_MAX_CHARACTERS = 4_000;
|
|
260
|
+
/**
|
|
261
|
+
* Tools whose text results are capped by default, with their default cap.
|
|
262
|
+
* Scene and runtime trees, console and debugger reads, inspect reads and
|
|
263
|
+
* library reads. The scene tree and library reads get more room: a median
|
|
264
|
+
* skill is about 12K characters, and cutting every skill read would double
|
|
265
|
+
* the core search-then-read loop. Writes, job ids and file reads are never
|
|
266
|
+
* capped.
|
|
267
|
+
*/
|
|
268
|
+
export const OUTPUT_CAPS = new Map([
|
|
269
|
+
["summer_get_scene_tree", 8_000],
|
|
270
|
+
["summer_get_runtime_tree", DEFAULT_MAX_CHARACTERS],
|
|
271
|
+
["summer_get_console", DEFAULT_MAX_CHARACTERS],
|
|
272
|
+
["summer_get_debugger_errors", DEFAULT_MAX_CHARACTERS],
|
|
273
|
+
["summer_get_debugger_warnings", DEFAULT_MAX_CHARACTERS],
|
|
274
|
+
["summer_inspect_node", DEFAULT_MAX_CHARACTERS],
|
|
275
|
+
["summer_inspect_resource", DEFAULT_MAX_CHARACTERS],
|
|
276
|
+
["summer_inspect_runtime_node", DEFAULT_MAX_CHARACTERS],
|
|
277
|
+
["summer_inspect_asset", DEFAULT_MAX_CHARACTERS],
|
|
278
|
+
["summer_read_library", 24_000],
|
|
279
|
+
]);
|
|
280
|
+
/**
|
|
281
|
+
* A capped tool's input shape plus `maxCharacters`. server.ts adds it at
|
|
282
|
+
* registration and strips it before the handler runs; the descriptor parity
|
|
283
|
+
* test applies the same function, so library/tools/<slug>/resource.yaml
|
|
284
|
+
* lists the argument too.
|
|
285
|
+
*/
|
|
286
|
+
export function withMaxCharactersArg(name, shape) {
|
|
287
|
+
const cap = OUTPUT_CAPS.get(name);
|
|
288
|
+
if (cap === undefined || "maxCharacters" in shape)
|
|
289
|
+
return shape;
|
|
290
|
+
return {
|
|
291
|
+
...shape,
|
|
292
|
+
maxCharacters: z
|
|
293
|
+
.number()
|
|
294
|
+
.int()
|
|
295
|
+
.min(500)
|
|
296
|
+
.max(1_000_000)
|
|
297
|
+
.optional()
|
|
298
|
+
.describe(`Most characters of text to return (default ${cap}). A longer result is cut and says so.`),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// ── Single scene edits ───────────────────────────────────────────────────────
|
|
302
|
+
/** One-change scene tools; a run of them is one summer_run_script. */
|
|
303
|
+
export const SINGLE_EDIT_TOOLS = new Set([
|
|
304
|
+
"summer_set_prop",
|
|
305
|
+
"summer_add_node",
|
|
306
|
+
"summer_remove_node",
|
|
307
|
+
"summer_set_resource_property",
|
|
308
|
+
"summer_instantiate_scene",
|
|
309
|
+
"summer_connect_signal",
|
|
310
|
+
"summer_replace_node",
|
|
311
|
+
]);
|
|
312
|
+
/** Calls that already batch edits; they end a run of single edits. */
|
|
313
|
+
export const BATCH_EDIT_TOOLS = new Set([
|
|
314
|
+
"summer_run_script",
|
|
315
|
+
"summer_batch",
|
|
316
|
+
"summer_run_editor_script",
|
|
317
|
+
]);
|
|
@@ -19,7 +19,14 @@ function cloud(name, schema, run) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
export function registerAppTools(server) {
|
|
22
|
-
server.tool("summer_app_guide", `Read this before building or changing a Summer App (a tool for the person's team inside Summer Studio
|
|
22
|
+
server.tool("summer_app_guide", `Read this before building or changing a Summer App (a tool for the person's team inside Summer Studio). Start with the default section overview (when to build one, Layout vs code, the build loop, the rules), then read what the app needs: examples (a complete working app to copy), sdk, manifest, kit, design, layout, types (exact SDK TypeScript types).\n\n${CLOUD}`, appGuideArgsSchema.shape, async (args) => {
|
|
23
|
+
const parsed = appGuideArgsSchema.safeParse(args ?? {});
|
|
24
|
+
if (!parsed.success)
|
|
25
|
+
return result({ ok: false, code: "invalid", error: parsed.error.issues[0]?.message ?? "Invalid arguments" });
|
|
26
|
+
const guide = await appGuide(parsed.data, { request: appsGateway("mcp", "summer_app_guide") });
|
|
27
|
+
// The guide reads as Markdown, not as one escaped JSON line.
|
|
28
|
+
return guide.ok === true && typeof guide.markdown === "string" ? { content: [{ type: "text", text: guide.markdown }] } : result(guide);
|
|
29
|
+
});
|
|
23
30
|
server.tool("summer_app_list", `The Summer Apps in the person's workspaces: id, name, purpose, folder, status (scratch, kept, archived), version, format.\n\n${CLOUD}`, appListArgsSchema.shape, cloud("summer_app_list", appListArgsSchema, appList));
|
|
24
31
|
server.tool("summer_app_create", `Create a new scratch Summer App; their open Summer Studio tab opens it. Without workspaceId it goes to the person's team (the one their Studio tab is on, else their latest team, else personal); the result's workspace says which: tell the person. Read summer_app_guide first. format code (index.html + JS/CSS) or layout (a JSON layout of kit nodes for a quick decision UI). Then summer_app_publish.\n\n${CLOUD}`, appCreateArgsSchema.shape, cloud("summer_app_create", appCreateArgsSchema, appCreate));
|
|
25
32
|
server.tool("summer_app_publish", `Publish a new immutable version of a Summer App: dir (a local folder with index.html at its root) or files [{path, content, encoding}] for code apps, layout for layout apps, plus the manifest. Limits: 10 MB, 300 files, 5 MB per file. The person's Studio tab boots it. An error names the file or rule to fix: fix it and publish again.\n\n${CLOUD}`, appPublishArgsSchema.shape, cloud("summer_app_publish", appPublishArgsSchema, appPublish));
|
|
@@ -27,7 +34,7 @@ export function registerAppTools(server) {
|
|
|
27
34
|
server.tool("summer_app_update", `Rename a Summer App, change its purpose or folder, or set its status: kept (the team keeps it), archived (hidden from the app list; restore with scratch or kept), scratch. Archive the scratch apps you made that the person did not keep.\n\n${CLOUD}`, appUpdateArgsSchema.shape, cloud("summer_app_update", appUpdateArgsSchema, appUpdate));
|
|
28
35
|
server.tool("summer_app_events", `Wait for what a Summer App sends you: summer.agent.send events (kind agent), errors in the app (kind error), feedback (kind feedback), agent tool calls (kind tool). Waits up to timeoutSeconds (max ${MAX_APP_EVENTS_WAIT_SECONDS}) for the first event after the cursor; pass the returned cursor as after next time.\n\n${CLOUD}`, appEventsArgsSchema.shape, cloud("summer_app_events", appEventsArgsSchema, appEvents));
|
|
29
36
|
server.tool("summer_app_page", `Read or use the fields and buttons a Summer App declared while it is open in the person's Studio tab or editor dock: no fill and no press reads the page; fill [{field, value}] and press an action id use it. Critical actions (delete, publish, pay, send) are pressed only by the person. app_not_open when the app is not open.\n\n${CLOUD}`, appPageArgsSchema.shape, cloud("summer_app_page", appPageArgsSchema, appPage));
|
|
30
|
-
server.tool("summer_app_screenshot", `See a Summer App without the person: Summer renders its current version (or version) headless with the person's data and returns the picture plus what to fix: console lines, uncaught errors, refused calls, whether the SDK connected and rendered, and the fields, buttons and agent tools it declared. Read-only: writes, sends and paid calls are refused, nothing changes. width 390 for a phone, theme dark, fullPage for the whole scroll; out also saves the PNG. Use it after every publish.\n\n${CLOUD}`, appScreenshotArgsSchema.shape, async (args) => {
|
|
37
|
+
server.tool("summer_app_screenshot", `See a Summer App without the person: Summer renders its current version (or version) headless with the person's data and returns the picture plus what to fix: console lines, uncaught errors, refused calls, whether the SDK connected and rendered, and the fields, buttons and agent tools it declared. Read-only: writes, sends and paid calls are refused, nothing changes. width 390 for a phone, theme dark, fullPage for the whole scroll, steps to click or press something first (a dialog, a second state); out also saves the PNG. It waits until the app's calls settle. Use it after every publish.\n\n${CLOUD}`, appScreenshotArgsSchema.shape, async (args) => {
|
|
31
38
|
const parsed = appScreenshotArgsSchema.safeParse(args ?? {});
|
|
32
39
|
if (!parsed.success)
|
|
33
40
|
return result({ ok: false, code: "invalid", error: parsed.error.issues[0]?.message ?? "Invalid arguments" });
|
|
@@ -4,14 +4,15 @@ import { resolveGatewayUrl } from "../../core/config.js";
|
|
|
4
4
|
import { getClient } from "../server.js";
|
|
5
5
|
import { ImportHdriError, importPolyHavenHdri, } from "../../core/capabilities/hdri-import.js";
|
|
6
6
|
import { importResolvedAsset } from "../../core/capabilities/asset-import.js";
|
|
7
|
+
import { summerRequestHeaders } from "../../core/agent-host.js";
|
|
8
|
+
import { codedFailure, describeNetworkError, EngineNotRunningError, networkDetails, NOT_SIGNED_IN, SIGN_IN_AGAIN_RECOVERY, } from "../../core/error-codes.js";
|
|
9
|
+
import { engineNotRunningResult } from "./with-engine.js";
|
|
10
|
+
/** No Summer account token: the same failure for every asset tool. */
|
|
11
|
+
const notSignedIn = { error: "not_logged_in", message: NOT_SIGNED_IN, code: "not_signed_in" };
|
|
7
12
|
async function searchAssetsApi(params) {
|
|
8
13
|
const token = await getAuthToken();
|
|
9
|
-
if (!token)
|
|
10
|
-
return {
|
|
11
|
-
error: "Not logged in",
|
|
12
|
-
message: "Not signed in. The user needs to run this in their terminal:\n npx -y summer-engine@latest login\nOr open: https://www.summerengine.com/login\nAsset search requires a Summer Engine account (free to create).",
|
|
13
|
-
};
|
|
14
|
-
}
|
|
14
|
+
if (!token)
|
|
15
|
+
return { ...notSignedIn };
|
|
15
16
|
const searchParams = new URLSearchParams();
|
|
16
17
|
searchParams.set("query", params.query);
|
|
17
18
|
if (params.assetType && params.assetType !== "all") {
|
|
@@ -30,17 +31,24 @@ async function searchAssetsApi(params) {
|
|
|
30
31
|
if (params.preview)
|
|
31
32
|
searchParams.set("contactSheet", "true");
|
|
32
33
|
const gatewayUrl = await resolveGatewayUrl();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
let res;
|
|
35
|
+
try {
|
|
36
|
+
res = await fetch(`${gatewayUrl}/api/mcp/assets?${searchParams}`, {
|
|
37
|
+
headers: { ...summerRequestHeaders({ surface: "mcp" }), Authorization: `Bearer ${token}` },
|
|
38
|
+
signal: AbortSignal.timeout(15000),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return { error: "network_error", message: `Asset request failed: ${describeNetworkError(err)}`, code: "unavailable", ...networkDetails(err) };
|
|
43
|
+
}
|
|
37
44
|
const data = (await res.json());
|
|
38
45
|
if (!res.ok) {
|
|
39
46
|
if (res.status === 401) {
|
|
40
47
|
return {
|
|
41
48
|
error: "unauthorized",
|
|
42
|
-
message:
|
|
43
|
-
|
|
49
|
+
message: data.message || "Auth token expired.",
|
|
50
|
+
code: "not_signed_in",
|
|
51
|
+
recovery: SIGN_IN_AGAIN_RECOVERY,
|
|
44
52
|
};
|
|
45
53
|
}
|
|
46
54
|
if (res.status === 429) {
|
|
@@ -59,18 +67,19 @@ async function searchAssetsApi(params) {
|
|
|
59
67
|
}
|
|
60
68
|
async function authedGetJson(endpoint, timeoutMs = 15_000) {
|
|
61
69
|
const token = await getAuthToken();
|
|
62
|
-
if (!token)
|
|
63
|
-
return {
|
|
64
|
-
status: 401,
|
|
65
|
-
error: "not_logged_in",
|
|
66
|
-
message: "Not signed in. Run in your terminal:\n npx -y summer-engine@latest login",
|
|
67
|
-
};
|
|
68
|
-
}
|
|
70
|
+
if (!token)
|
|
71
|
+
return { status: 401, ...notSignedIn };
|
|
69
72
|
const gatewayUrl = await resolveGatewayUrl();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
let res;
|
|
74
|
+
try {
|
|
75
|
+
res = await fetch(`${gatewayUrl}${endpoint}`, {
|
|
76
|
+
headers: { ...summerRequestHeaders({ surface: "mcp" }), Authorization: `Bearer ${token}` },
|
|
77
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
return { status: 0, error: "network_error", message: `Asset request failed: ${describeNetworkError(err)}`, code: "unavailable", ...networkDetails(err) };
|
|
82
|
+
}
|
|
74
83
|
const data = (await res.json());
|
|
75
84
|
if (!res.ok) {
|
|
76
85
|
return {
|
|
@@ -78,6 +87,7 @@ async function authedGetJson(endpoint, timeoutMs = 15_000) {
|
|
|
78
87
|
data,
|
|
79
88
|
error: data?.error || "request_failed",
|
|
80
89
|
message: data?.message || "Request failed",
|
|
90
|
+
...(res.status === 401 ? { code: "not_signed_in", recovery: SIGN_IN_AGAIN_RECOVERY } : {}),
|
|
81
91
|
};
|
|
82
92
|
}
|
|
83
93
|
return { status: res.status, data };
|
|
@@ -85,14 +95,14 @@ async function authedGetJson(endpoint, timeoutMs = 15_000) {
|
|
|
85
95
|
async function getAssetApi(assetId) {
|
|
86
96
|
const result = await authedGetJson(`/api/mcp/assets/${encodeURIComponent(assetId)}`);
|
|
87
97
|
if (result.error) {
|
|
88
|
-
return { error: result.error, message: result.message };
|
|
98
|
+
return { error: result.error, message: result.message, code: result.code, recovery: result.recovery, details: result.details };
|
|
89
99
|
}
|
|
90
100
|
return { asset: result.data?.asset };
|
|
91
101
|
}
|
|
92
102
|
async function getAssetDownloadUrlApi(assetId, role) {
|
|
93
103
|
const result = await authedGetJson(`/api/mcp/assets/${encodeURIComponent(assetId)}/download-url?role=${encodeURIComponent(role)}`);
|
|
94
104
|
if (result.error) {
|
|
95
|
-
return { error: result.error, message: result.message };
|
|
105
|
+
return { error: result.error, message: result.message, code: result.code, recovery: result.recovery, details: result.details };
|
|
96
106
|
}
|
|
97
107
|
return { data: result.data };
|
|
98
108
|
}
|
|
@@ -106,6 +116,14 @@ function jsonSuccess(data) {
|
|
|
106
116
|
],
|
|
107
117
|
};
|
|
108
118
|
}
|
|
119
|
+
/** The body of a failed lookup: `{ok:false, code, error, recovery, message}`
|
|
120
|
+
* for the shared failures, else the older `{error, message}`. */
|
|
121
|
+
function failureBody(result, fallback) {
|
|
122
|
+
const message = result.message || fallback.message;
|
|
123
|
+
return result.code
|
|
124
|
+
? codedFailure(result.code, message, { message, ...(result.details ? { details: result.details } : {}) }, result.recovery)
|
|
125
|
+
: { error: result.error || fallback.error, message };
|
|
126
|
+
}
|
|
109
127
|
function jsonError(data) {
|
|
110
128
|
return {
|
|
111
129
|
content: [
|
|
@@ -146,15 +164,7 @@ Requires authentication (so we can attribute usage and apply per-user rate limit
|
|
|
146
164
|
}, async ({ query, assetType, limit, source, style, preview, includeCommunity }) => {
|
|
147
165
|
const result = await searchAssetsApi({ query, assetType, limit: Math.min(limit, 20), source, style, preview, includeCommunity });
|
|
148
166
|
if (result.error) {
|
|
149
|
-
return {
|
|
150
|
-
content: [
|
|
151
|
-
{
|
|
152
|
-
type: "text",
|
|
153
|
-
text: JSON.stringify({ error: result.error, message: result.message }, null, 2),
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
isError: true,
|
|
157
|
-
};
|
|
167
|
+
return jsonError(failureBody(result, { error: "Search failed", message: "Asset search failed" }));
|
|
158
168
|
}
|
|
159
169
|
const sheet = result.contactSheet?.match(/^data:(image\/[a-z]+);base64,(.+)$/);
|
|
160
170
|
return {
|
|
@@ -200,7 +210,7 @@ Cloud tool — works WITHOUT the Summer Engine app open.`, {
|
|
|
200
210
|
source: "my_assets",
|
|
201
211
|
});
|
|
202
212
|
if (result.error) {
|
|
203
|
-
return jsonError({ error:
|
|
213
|
+
return jsonError(failureBody(result, { error: "List failed", message: "Listing your assets failed" }));
|
|
204
214
|
}
|
|
205
215
|
return jsonSuccess({
|
|
206
216
|
assets: result.assets,
|
|
@@ -220,10 +230,7 @@ Cloud tool — works WITHOUT the Summer Engine app open.`, {
|
|
|
220
230
|
}, async ({ assetId }) => {
|
|
221
231
|
const result = await getAssetApi(assetId);
|
|
222
232
|
if (result.error || !result.asset) {
|
|
223
|
-
return jsonError({
|
|
224
|
-
error: result.error || "asset_not_found",
|
|
225
|
-
message: result.message || "Asset not found.",
|
|
226
|
-
});
|
|
233
|
+
return jsonError(failureBody(result, { error: "asset_not_found", message: "Asset not found." }));
|
|
227
234
|
}
|
|
228
235
|
return jsonSuccess({ asset: result.asset });
|
|
229
236
|
});
|
|
@@ -242,7 +249,7 @@ Cloud tool — works WITHOUT the Summer Engine app open.`, {
|
|
|
242
249
|
}, async ({ assetId, role }) => {
|
|
243
250
|
const result = await getAssetDownloadUrlApi(assetId, role);
|
|
244
251
|
if (result.error) {
|
|
245
|
-
return jsonError({ error:
|
|
252
|
+
return jsonError(failureBody(result, { error: "request_failed", message: "Request failed" }));
|
|
246
253
|
}
|
|
247
254
|
return jsonSuccess(result.data);
|
|
248
255
|
});
|
|
@@ -276,10 +283,7 @@ itself does not — only this import step does).`, {
|
|
|
276
283
|
}, async ({ assetId, path, parent, scenePath, name }) => {
|
|
277
284
|
const fetched = await getAssetApi(assetId);
|
|
278
285
|
if (fetched.error || !fetched.asset) {
|
|
279
|
-
return jsonError({
|
|
280
|
-
error: fetched.error || "asset_not_found",
|
|
281
|
-
message: fetched.message || "Asset not found.",
|
|
282
|
-
});
|
|
286
|
+
return jsonError(failureBody(fetched, { error: "asset_not_found", message: "Asset not found." }));
|
|
283
287
|
}
|
|
284
288
|
try {
|
|
285
289
|
const imported = await importResolvedAsset(await getClient(), {
|
|
@@ -297,6 +301,8 @@ itself does not — only this import step does).`, {
|
|
|
297
301
|
});
|
|
298
302
|
}
|
|
299
303
|
catch (err) {
|
|
304
|
+
if (err instanceof EngineNotRunningError)
|
|
305
|
+
return engineNotRunningResult(err);
|
|
300
306
|
const msg = err instanceof Error ? err.message : String(err);
|
|
301
307
|
return jsonError({
|
|
302
308
|
error: "import_failed",
|
|
@@ -319,15 +325,7 @@ Requires authentication. If the user gets an auth error, they need to run 'npx -
|
|
|
319
325
|
}, async ({ query, parent, scenePath, assetType, source }) => {
|
|
320
326
|
const searchResult = await searchAssetsApi({ query, assetType, limit: 5, source });
|
|
321
327
|
if (searchResult.error) {
|
|
322
|
-
return {
|
|
323
|
-
content: [
|
|
324
|
-
{
|
|
325
|
-
type: "text",
|
|
326
|
-
text: JSON.stringify({ error: searchResult.error, message: searchResult.message }, null, 2),
|
|
327
|
-
},
|
|
328
|
-
],
|
|
329
|
-
isError: true,
|
|
330
|
-
};
|
|
328
|
+
return jsonError(failureBody(searchResult, { error: "Search failed", message: "Asset search failed" }));
|
|
331
329
|
}
|
|
332
330
|
const assets = searchResult.assets || [];
|
|
333
331
|
if (assets.length === 0) {
|
|
@@ -378,6 +376,8 @@ Requires authentication. If the user gets an auth error, they need to run 'npx -
|
|
|
378
376
|
};
|
|
379
377
|
}
|
|
380
378
|
catch (err) {
|
|
379
|
+
if (err instanceof EngineNotRunningError)
|
|
380
|
+
return engineNotRunningResult(err);
|
|
381
381
|
const msg = err instanceof Error ? err.message : String(err);
|
|
382
382
|
return {
|
|
383
383
|
content: [
|
|
@@ -433,6 +433,8 @@ runs through the engine). No Summer login needed.`, {
|
|
|
433
433
|
...(err.hint ? { hint: err.hint } : {}),
|
|
434
434
|
});
|
|
435
435
|
}
|
|
436
|
+
if (err instanceof EngineNotRunningError)
|
|
437
|
+
return engineNotRunningResult(err);
|
|
436
438
|
const msg = err instanceof Error ? err.message : String(err);
|
|
437
439
|
return jsonError({
|
|
438
440
|
error: "hdri_import_failed",
|
|
@@ -27,33 +27,33 @@ export function auditContent(result) {
|
|
|
27
27
|
return { content };
|
|
28
28
|
}
|
|
29
29
|
export function registerAuditTools(server) {
|
|
30
|
-
server.tool("summer_scene_audit", `Audit a 3D scene in one fast, read-only call: every node and subnode
|
|
30
|
+
server.tool("summer_scene_audit", `Audit a 3D scene in one fast, read-only call: it walks every node and subnode and lists likely visual and placement problems, sorted by severity. Each issue is a flag to LOOK at, never an auto-fix.
|
|
31
31
|
|
|
32
|
-
Checks (
|
|
33
|
-
- through_hole:
|
|
34
|
-
- exposed_edge:
|
|
35
|
-
- open_fixture_end: open ends of
|
|
36
|
-
- depth_step (look): ray rows
|
|
37
|
-
- floor_gap: down rays over
|
|
38
|
-
- floating / sunken:
|
|
39
|
-
- interpenetration:
|
|
40
|
-
- orientation (look): long props within 1.2 m of a wall
|
|
41
|
-
- uv_stretch:
|
|
42
|
-
- duplicate:
|
|
43
|
-
- z_fight: coplanar overlapping faces
|
|
44
|
-
- lights:
|
|
45
|
-
- transform: NaN, negative or non-uniform scale,
|
|
46
|
-
- resource: missing
|
|
32
|
+
Checks (pick with checks; the verifying-scenes skill explains each):
|
|
33
|
+
- through_hole: ray grids through each facade line that pass the wall AND reach the building's far side (an insert narrower than its frame, a missing module). Props do not close an opening.
|
|
34
|
+
- exposed_edge: edges of walls, bands, piers and corner blocks nothing covers within 4-7 mm, seen from walkable space, with a 2-60 cm reveal, a 4 cm seam or a 35 cm gap. Warn on bands or when depth_step agrees, else look.
|
|
35
|
+
- open_fixture_end (warn): open ends of pipe, duct or gutter runs (2+ open rims of one size) nothing joins within 2.5 cm; caps, funnels, outlets are open by design.
|
|
36
|
+
- depth_step (look): ray rows at each facade's band levels and every 1.25 m: recesses, missing runs, seams, proud modules, holes (a hole by a through_hole makes it an error).
|
|
37
|
+
- floor_gap: down rays over tiles, seams and the strip to a wall within 1 m, by FIRST surface hit: the void (error), underlay over the floor's own drain (warn, not a hole), underlay through a hole.
|
|
38
|
+
- floating / sunken: over 2 cm above or 3 cm into the first surface from above; structure and wall-mounted pieces excluded.
|
|
39
|
+
- interpenetration: prop hulls cutting props, walls, structure or inserts over 3 cm, with every partner (up to 3).
|
|
40
|
+
- orientation (look): long props within 1.2 m of a wall over 15 deg off parallel; never says which way to face.
|
|
41
|
+
- uv_stretch: shown triangles with UVs stretched over 8:1 or collapsed.
|
|
42
|
+
- duplicate: same scene, same transform.
|
|
43
|
+
- z_fight: coplanar overlapping faces of two pieces (opposite-facing only if both double-sided) or of one mesh: a gap under twice the 24-bit depth step at the view distance (else 30 m). Warn over 0.05 m2 seen; ev.nudge names the axis to move (world and local). render_priority, depth or normal offsets, alpha, no depth test or writes demote it to look, with the reason.
|
|
44
|
+
- lights: over the per-object omni/spot limit (Compatibility: 8 each), spot_angle_attenuation under 3; shadowed counts.
|
|
45
|
+
- transform: NaN, negative or non-uniform scale, far out of bounds, pieces left at the origin (several = warn, one = look).
|
|
46
|
+
- resource: missing dependencies, a MeshInstance3D without a mesh, surfaces without a material.
|
|
47
47
|
|
|
48
|
-
budget_ms (default 3000) bounds
|
|
48
|
+
budget_ms (default 3000) bounds editor time: each check gets a weighted share; one past it stops, marked counts.<check>.partial (the share covered), never clean. exposed_edge, open_fixture_end and depth_step run last on the time left: rerun them alone when partial.
|
|
49
49
|
|
|
50
|
-
accept:[{key, reason}] (
|
|
50
|
+
accept:[{key, reason}] (issues[].key) records look/warn items you judged fine in res://.summer/audit-accept.json, the only file the audit writes. Later audits count them (counts.<check>.accepted) but hide them until severity rises or the measured size moves over 25% (then accept_stale). Errors cannot be accepted; show_accepted lists them.
|
|
51
51
|
|
|
52
|
-
Returns at most 5 KB of JSON: counts
|
|
52
|
+
Returns at most 5 KB of JSON: counts and editor ms per check, scene size, ONE page of issues {n, check, sev (error|warn|look), path, pos (world), why, ev (evidence numbers, the ray to reproduce), next (the next tool), key}. Page with offset/limit (next_offset); filter with checks, root (subtree), min_severity. render:"sheet" adds ONE image of the page's first 6 issues framed close from their open side, tiles labelled #n.
|
|
53
53
|
|
|
54
|
-
Every check works
|
|
54
|
+
Every check works from geometry, node and resource data and materials alone: never node or file names, never kit metadata. Each piece's role (dressing, wall, floor, roof, underlay, insert, structure, a prop up to 3.5 m) comes from its shape.
|
|
55
55
|
|
|
56
|
-
Read-only:
|
|
56
|
+
Read-only: audits the SAVED file (save first) in a private offscreen copy (ScenePreview): the open tab never becomes unsaved, undo is untouched, nothing is saved. Failures are structured (failure_reason), never a silent fallback.`, sceneAuditShape, async (args) => withEngine(async (client) => {
|
|
57
57
|
const audit = await sceneAudit(client, args);
|
|
58
58
|
return audit.ok ? { audit } : { audit, failure_reason: audit.failure_reason };
|
|
59
59
|
}, { onResult: (wrapped) => auditContent(wrapped.audit) }));
|