@hybridlabor-api/aos 4.6.6 → 4.7.1
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/.agents/agents.md +9 -3
- package/.agents/nodes.json +7 -2
- package/.agents/state.schema.json +26 -1
- package/.agents/vendor-manifest.json +56 -0
- package/.agents/workflows/startcycle-graph.md +2 -2
- package/.claude/agents/architect.md +7 -2
- package/.claude/agents/security-reviewer.md +1 -1
- package/.claude/agents/techlead.md +2 -0
- package/.claude/hooks/memb-inject.mjs +257 -135
- package/.claude/hooks/trail-relay.mjs +18 -0
- package/.claude/settings.json +70 -0
- package/.claude/workflows/startcycle-dispatch.mjs +203 -17
- package/.opencode/agents/architect.md +7 -2
- package/.opencode/agents/techlead.md +2 -0
- package/.opencode/plugins/bdb-aos.js +75 -0
- package/THIRD_PARTY_NOTICES.md +57 -1
- package/bin/aos-doctor.mjs +366 -0
- package/bin/aos-store.mjs +167 -0
- package/bin/aos-uninstall.mjs +55 -1
- package/docs/AUDIT_BRIEF_ECC_STORE.md +122 -0
- package/docs/PLAN_AOS_STORE_PHASE1.md +190 -0
- package/docs/handover/agenttrail-for-ao.md +58 -0
- package/docs/handover/deja-for-ao.md +51 -0
- package/installer.js +182 -38
- package/lib/aos-archify-contract.mjs +277 -0
- package/lib/ecc-store-index.json +1 -0
- package/mcp_config.json +8 -0
- package/mcps/mcsc/README.md +24 -0
- package/mcps/mcsc/package-lock.json +1234 -0
- package/mcps/mcsc/package.json +14 -0
- package/mcps/mcsc/packages/core/bin/mcsc.js +162 -0
- package/mcps/mcsc/packages/core/rulebook.default.yaml +49 -0
- package/mcps/mcsc/packages/core/schema/rulebook.schema.json +56 -0
- package/mcps/mcsc/packages/core/src/adapters/agy.js +107 -0
- package/mcps/mcsc/packages/core/src/adapters/codex.js +179 -0
- package/mcps/mcsc/packages/core/src/adapters/native.js +14 -0
- package/mcps/mcsc/packages/core/src/adapters/opencode.js +183 -0
- package/mcps/mcsc/packages/core/src/adapters/types.js +21 -0
- package/mcps/mcsc/packages/core/src/capabilities/buildOfferTable.js +34 -0
- package/mcps/mcsc/packages/core/src/capabilities/registry.js +99 -0
- package/mcps/mcsc/packages/core/src/capabilities/types.js +24 -0
- package/mcps/mcsc/packages/core/src/detect/cache.js +47 -0
- package/mcps/mcsc/packages/core/src/detect/inventory.js +73 -0
- package/mcps/mcsc/packages/core/src/detect/probes.js +158 -0
- package/mcps/mcsc/packages/core/src/detect/types.js +26 -0
- package/mcps/mcsc/packages/core/src/rulebook/load.js +53 -0
- package/mcps/mcsc/packages/core/src/rulebook/resolve.js +227 -0
- package/mcps/mcsc/packages/core/src/rulebook/rulebook.types.js +89 -0
- package/mcps/mcsc/packages/core/src/trail.js +17 -0
- package/mcps/mcsc/packages/mcp/server.js +302 -0
- package/mcps/mcsc/server.js +2 -0
- package/package.json +8 -2
- package/scripts/build-ecc-store-index.mjs +147 -0
- package/skills/basic/bdbmediastorm/SKILL.md +1 -1
- package/skills/basic/godmode-ui-ux/SKILL.md +2 -2
- package/skills/basic/godmode-ui-ux/references/anti-slop-rules.md +44 -40
- package/skills/basic/godmode-ui-ux/references/taste-archetypes.md +49 -0
- package/skills/basic/startcycle/SKILL.md +7 -0
- package/skills/basic/startcycle-graph/SKILL.md +25 -0
- package/skills/basic/startcycle-graph-user/SKILL.md +3 -0
- package/skills/basic/teamwork-preview/SKILL.md +2 -0
- package/skills/bdbrainstorm/SKILL.md +1 -0
- package/skills/global_config/agenttrail/LICENSE +21 -0
- package/skills/global_config/agenttrail/SKILL.md +48 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +1113 -0
- package/skills/global_config/agenttrail/public/index.html +662 -0
- package/skills/global_config/aos-project-init/SKILL.md +25 -5
- package/skills/global_config/aos-setup/SKILL.md +34 -1
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +115 -1
- package/skills/global_config/archify/LICENSE +22 -0
- package/skills/global_config/archify/SKILL.md +140 -0
- package/skills/global_config/archify/THIRD_PARTY_NOTICES.md +69 -0
- package/skills/global_config/archify/assets/JetBrainsMono-OFL.txt +93 -0
- package/skills/global_config/archify/assets/template.html +14937 -0
- package/skills/global_config/archify/bin/archify.mjs +2139 -0
- package/skills/global_config/archify/bin/open-artifact.mjs +86 -0
- package/skills/global_config/archify/bin/preview.mjs +653 -0
- package/skills/global_config/archify/bin/visual-check.mjs +829 -0
- package/skills/global_config/archify/brand-marks/README.md +31 -0
- package/skills/global_config/archify/brand-marks/catalog.json +131 -0
- package/skills/global_config/archify/delta/architecture-delta.mjs +1232 -0
- package/skills/global_config/archify/examples/agent-run.lifecycle.json +60 -0
- package/skills/global_config/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/global_config/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/global_config/archify/examples/brand-aware-delivery.architecture.json +47 -0
- package/skills/global_config/archify/examples/cache-miss-request.sequence.json +82 -0
- package/skills/global_config/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/global_config/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/global_config/archify/examples/dataflow-product-analytics.html +15048 -0
- package/skills/global_config/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/global_config/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/global_config/archify/examples/incident-response.workflow.json +64 -0
- package/skills/global_config/archify/examples/lifecycle-agent-run.html +14983 -0
- package/skills/global_config/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/global_config/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/global_config/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/global_config/archify/examples/sequence-cache-miss-request.html +15063 -0
- package/skills/global_config/archify/examples/web-app-rendered.html +15012 -0
- package/skills/global_config/archify/examples/web-app.architecture.json +46 -0
- package/skills/global_config/archify/examples/workflow-agent-tool-call-rendered.html +15054 -0
- package/skills/global_config/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/global_config/archify/package.json +39 -0
- package/skills/global_config/archify/recipes/scenarios.mjs +391 -0
- package/skills/global_config/archify/references/authoring-contract.md +247 -0
- package/skills/global_config/archify/references/brand-marks.md +65 -0
- package/skills/global_config/archify/references/delivery-contract.md +138 -0
- package/skills/global_config/archify/references/viewer-runtime.md +45 -0
- package/skills/global_config/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/global_config/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/global_config/archify/renderers/dataflow/README.md +104 -0
- package/skills/global_config/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/global_config/archify/renderers/lifecycle/README.md +115 -0
- package/skills/global_config/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/global_config/archify/renderers/sequence/README.md +114 -0
- package/skills/global_config/archify/renderers/sequence/render-sequence.mjs +464 -0
- package/skills/global_config/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/global_config/archify/renderers/shared/cli.mjs +218 -0
- package/skills/global_config/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/global_config/archify/renderers/shared/diagnostics.mjs +160 -0
- package/skills/global_config/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/global_config/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- package/skills/global_config/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/global_config/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/global_config/archify/renderers/shared/i18n.mjs +595 -0
- package/skills/global_config/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/global_config/archify/renderers/shared/legend.mjs +217 -0
- package/skills/global_config/archify/renderers/shared/output-path.mjs +340 -0
- package/skills/global_config/archify/renderers/shared/repository-evidence.mjs +239 -0
- package/skills/global_config/archify/renderers/shared/repository-location.mjs +58 -0
- package/skills/global_config/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/global_config/archify/renderers/shared/utils.mjs +232 -0
- package/skills/global_config/archify/renderers/shared/validator.mjs +86 -0
- package/skills/global_config/archify/renderers/workflow/README.md +223 -0
- package/skills/global_config/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/global_config/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/global_config/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/global_config/archify/schemas/README.md +211 -0
- package/skills/global_config/archify/schemas/architecture.schema.json +178 -0
- package/skills/global_config/archify/schemas/common.schema.json +115 -0
- package/skills/global_config/archify/schemas/dataflow.schema.json +243 -0
- package/skills/global_config/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/global_config/archify/schemas/sequence.schema.json +223 -0
- package/skills/global_config/archify/schemas/workflow.schema.json +428 -0
- package/skills/global_config/archify/scripts/check-render-output.mjs +916 -0
- package/skills/global_config/archify/scripts/generate-brand-marks.mjs +141 -0
- package/skills/global_config/archify/scripts/generate-validators.mjs +66 -0
- package/skills/global_config/archify/scripts/render-examples.mjs +26 -0
- package/skills/global_config/archify/skill-release.json +10 -0
- package/skills/global_config/ask-tim/SKILL.md +10 -0
- package/skills/global_config/bdb-security-audit/SKILL.md +109 -0
- package/skills/global_config/bdb-security-audit/references/audit-checklists.md +172 -0
- package/skills/global_config/bdb-security-audit/references/skylos-guide.md +139 -0
- package/skills/global_config/bdb-security-audit/references/xalgorix-bridge.md +110 -0
- package/skills/global_config/bdbdesignpro/SKILL.md +4 -4
- package/skills/global_config/bdbdesignpro/references/bdb-motion-tokens.md +52 -31
- package/skills/global_config/bdbresilience/SKILL.md +10 -0
- package/skills/global_config/brag/SKILL.md +179 -0
- package/skills/global_config/brag/assets/music/README.md +20 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/bong_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_005.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/drop_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/drop_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/drop_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/error_005.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/error_006.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/glitch_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/glitch_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/select_008.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_005.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_006.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_007.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
- package/skills/global_config/brag/assets/sfx/sfx-analysis.json +15584 -0
- package/skills/global_config/brag/assets/sfx/sfx-analysis.md +75 -0
- package/skills/global_config/brag/assets/sfx/ui/click1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch10.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch11.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch12.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch13.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch14.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch15.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch16.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch17.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch18.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch19.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch20.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch21.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch22.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch23.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch24.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch25.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch26.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch27.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch28.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch29.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch30.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch31.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch32.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch33.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch34.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch35.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch36.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch37.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch38.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch7.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch8.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch9.ogg +0 -0
- package/skills/global_config/brag/references/audio.md +307 -0
- package/skills/global_config/brag/references/step-1-inspect.md +133 -0
- package/skills/global_config/brag/references/step-2-plan.md +234 -0
- package/skills/global_config/brag/references/step-3-compose.md +210 -0
- package/skills/global_config/brag/references/step-4-deliver.md +168 -0
- package/skills/global_config/brag/references/tones.md +231 -0
- package/skills/global_config/brag/scripts/analyze_music_cues.py +309 -0
- package/skills/global_config/brag/scripts/pyproject.toml +14 -0
- package/skills/global_config/brag/scripts/uv.lock +819 -0
- package/skills/global_config/deja-memory/SKILL.md +42 -0
- package/skills/global_config/editable-design/LICENSE +202 -0
- package/skills/global_config/editable-design/README.md +34 -0
- package/skills/global_config/editable-design/SKILL.md +630 -0
- package/skills/global_config/editable-design/THIRD_PARTY_NOTICES.md +10 -0
- package/skills/global_config/editable-design/agents/openai.yaml +4 -0
- package/skills/global_config/editable-design/assets/editor/layer-editor.css +262 -0
- package/skills/global_config/editable-design/assets/editor/layer-editor.js +1929 -0
- package/skills/global_config/editable-design/assets/poster-starter/README.md +92 -0
- package/skills/global_config/editable-design/assets/poster-starter/assets/.gitkeep +0 -0
- package/skills/global_config/editable-design/assets/poster-starter/index.html +91 -0
- package/skills/global_config/editable-design/assets/poster-starter/poster.json +6 -0
- package/skills/global_config/editable-design/assets/poster-starter/reference/.gitkeep +1 -0
- package/skills/global_config/editable-design/assets/replay/icons/assets.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/html.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/input.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/layers.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/plan.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/reference.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/review.png +0 -0
- package/skills/global_config/editable-design/assets/replay/index.html +59 -0
- package/skills/global_config/editable-design/assets/replay/replay-motion.js +410 -0
- package/skills/global_config/editable-design/assets/replay/replay.css +380 -0
- package/skills/global_config/editable-design/assets/replay/replay.js +374 -0
- package/skills/global_config/editable-design/font-kit/package-lock.json +115 -0
- package/skills/global_config/editable-design/font-kit/package.json +19 -0
- package/skills/global_config/editable-design/pack.sh +20 -0
- package/skills/global_config/editable-design/references/asset-architecture.md +72 -0
- package/skills/global_config/editable-design/references/editor-pitfalls.md +70 -0
- package/skills/global_config/editable-design/references/editor-runtime.md +131 -0
- package/skills/global_config/editable-design/references/font-system.md +89 -0
- package/skills/global_config/editable-design/references/imagery.md +145 -0
- package/skills/global_config/editable-design/references/layout-typography.md +116 -0
- package/skills/global_config/editable-design/references/replay-contract.md +169 -0
- package/skills/global_config/editable-design/scripts/_browser.mjs +247 -0
- package/skills/global_config/editable-design/scripts/bake.mjs +87 -0
- package/skills/global_config/editable-design/scripts/build-replay.mjs +316 -0
- package/skills/global_config/editable-design/scripts/check-contract.mjs +293 -0
- package/skills/global_config/editable-design/scripts/check-fonts.sh +252 -0
- package/skills/global_config/editable-design/scripts/check-poster.sh +70 -0
- package/skills/global_config/editable-design/scripts/doctor.sh +78 -0
- package/skills/global_config/editable-design/scripts/explode.mjs +115 -0
- package/skills/global_config/editable-design/scripts/font-kit.mjs +187 -0
- package/skills/global_config/editable-design/scripts/import-asset.sh +23 -0
- package/skills/global_config/editable-design/scripts/init-poster.sh +64 -0
- package/skills/global_config/editable-design/scripts/install-font-kit.sh +13 -0
- package/skills/global_config/editable-design/scripts/package-lock.json +334 -0
- package/skills/global_config/editable-design/scripts/package.json +14 -0
- package/skills/global_config/editable-design/scripts/remove-chroma-key.py +201 -0
- package/skills/global_config/editable-design/scripts/render-poster.sh +202 -0
- package/skills/global_config/editable-design/scripts/trace.sh +159 -0
- package/skills/global_config/editable-design/scripts/verify-replay.mjs +97 -0
- package/skills/global_config/editable-design/scripts/verify.mjs +782 -0
- package/skills/global_config/editable-design/scripts/wire-editor.mjs +104 -0
- package/skills/global_config/editable-design/tests/fixtures/small-explode/index.html +86 -0
- package/skills/global_config/mcsc/SKILL.md +60 -0
- package/skills/global_config/plan-canvas/SKILL.md +8 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +31 -6
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +4 -2
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
copyFileSync,
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
statSync,
|
|
10
|
+
writeFileSync,
|
|
11
|
+
} from 'node:fs';
|
|
12
|
+
import { dirname, extname, join, relative, resolve, sep } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const projectArg = args.find((arg) => !arg.startsWith('--')) || process.cwd();
|
|
18
|
+
const outIndex = args.indexOf('--out');
|
|
19
|
+
const projectRoot = resolve(projectArg);
|
|
20
|
+
const outDir = resolve(outIndex >= 0 && args[outIndex + 1] ? args[outIndex + 1] : join(projectRoot, 'replay'));
|
|
21
|
+
const templateCandidates = [
|
|
22
|
+
join(scriptDir, 'replay-assets'),
|
|
23
|
+
resolve(scriptDir, '../assets/replay'),
|
|
24
|
+
];
|
|
25
|
+
const templateDir = templateCandidates.find((path) => existsSync(join(path, 'index.html')));
|
|
26
|
+
|
|
27
|
+
if (!templateDir) {
|
|
28
|
+
console.error('Replay template is unavailable. Re-run poster initialization or use the skill-local builder.');
|
|
29
|
+
process.exit(2);
|
|
30
|
+
}
|
|
31
|
+
if (!existsSync(join(projectRoot, 'index.html'))) {
|
|
32
|
+
console.error(`No poster index.html found in ${projectRoot}`);
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const posix = (value) => value.split(sep).join('/');
|
|
37
|
+
const read = (path, fallback = '') => existsSync(path) ? readFileSync(path, 'utf8') : fallback;
|
|
38
|
+
const readJSON = (path, fallback = {}) => {
|
|
39
|
+
try { return JSON.parse(read(path)); } catch { return fallback; }
|
|
40
|
+
};
|
|
41
|
+
const firstExisting = (paths) => paths.find((path) => existsSync(path));
|
|
42
|
+
const projectPath = (...parts) => join(projectRoot, ...parts);
|
|
43
|
+
const browserPath = (path, query = '') => path ? `../${posix(relative(projectRoot, path))}${query}` : '';
|
|
44
|
+
|
|
45
|
+
function filesIn(path, extensions = []) {
|
|
46
|
+
if (!existsSync(path)) return [];
|
|
47
|
+
return readdirSync(path)
|
|
48
|
+
.filter((name) => !name.startsWith('.') && (!extensions.length || extensions.includes(extname(name).toLowerCase())))
|
|
49
|
+
.map((name) => join(path, name))
|
|
50
|
+
.filter((file) => statSync(file).isFile())
|
|
51
|
+
.sort();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function markdownSections(markdown) {
|
|
55
|
+
const sections = [];
|
|
56
|
+
const lines = markdown.split(/\r?\n/);
|
|
57
|
+
let current = null;
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
const match = line.match(/^##\s+(.+?)\s*$/);
|
|
60
|
+
if (match) {
|
|
61
|
+
if (current) sections.push({ ...current, body: current.lines.join('\n').trim() });
|
|
62
|
+
current = { title: match[1].trim(), lines: [] };
|
|
63
|
+
} else if (current) {
|
|
64
|
+
current.lines.push(line);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (current) sections.push({ ...current, body: current.lines.join('\n').trim() });
|
|
68
|
+
return sections;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function findSection(sections, key) {
|
|
72
|
+
if (!key) return null;
|
|
73
|
+
const exact = sections.find((section) => section.title.toLowerCase() === String(key).toLowerCase());
|
|
74
|
+
if (exact) return exact;
|
|
75
|
+
const normalized = String(key).toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
76
|
+
return sections.find((section) => section.title.toLowerCase().replace(/[^a-z0-9]+/g, '') === normalized)
|
|
77
|
+
|| sections.find((section) => section.title.toLowerCase().includes(String(key).toLowerCase()));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function traceEvents() {
|
|
81
|
+
return read(projectPath('.trace', 'events.jsonl'))
|
|
82
|
+
.split(/\r?\n/)
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.flatMap((line) => { try { return [JSON.parse(line)]; } catch { return []; } });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const manifest = readJSON(projectPath('.trace', 'manifest.json'));
|
|
88
|
+
const poster = readJSON(projectPath('poster.json'));
|
|
89
|
+
const assetPlanPath = firstExisting([
|
|
90
|
+
projectPath('asset-plan.json'),
|
|
91
|
+
projectPath('.trace', 'artifacts', 'P11_asset_plan', 'asset-plan.json'),
|
|
92
|
+
]);
|
|
93
|
+
const assetPlanRaw = assetPlanPath ? read(assetPlanPath) : '';
|
|
94
|
+
const assetPlan = assetPlanPath ? readJSON(assetPlanPath) : {};
|
|
95
|
+
const promptPath = firstExisting([
|
|
96
|
+
projectPath('prompts.md'),
|
|
97
|
+
projectPath('.trace', 'artifacts', 'P12_generate_imagery', 'prompts.md'),
|
|
98
|
+
]);
|
|
99
|
+
const promptsRaw = promptPath ? read(promptPath) : '';
|
|
100
|
+
const promptSections = markdownSections(promptsRaw);
|
|
101
|
+
const briefPath = firstExisting([
|
|
102
|
+
projectPath('brief.md'),
|
|
103
|
+
projectPath('.trace', 'artifacts', 'P01_input', 'brief.md'),
|
|
104
|
+
]);
|
|
105
|
+
const briefRaw = briefPath ? read(briefPath).trim() : '';
|
|
106
|
+
const compositionPromptPath = firstExisting([
|
|
107
|
+
projectPath('reference', 'composition-prompt.md'),
|
|
108
|
+
projectPath('reference', 'reference-prompt.md'),
|
|
109
|
+
projectPath('.trace', 'artifacts', 'P02_reference', 'composition-prompt.md'),
|
|
110
|
+
]);
|
|
111
|
+
const compositionSection = promptSections.find((section) => /composition reference|art-directed composition/i.test(section.title));
|
|
112
|
+
const compositionPrompt = compositionPromptPath ? read(compositionPromptPath).trim() : (compositionSection?.body || '');
|
|
113
|
+
const referenceImage = firstExisting([
|
|
114
|
+
projectPath('reference', 'composition-reference.png'),
|
|
115
|
+
projectPath('reference', 'art-direction.png'),
|
|
116
|
+
...filesIn(projectPath('reference'), ['.png', '.jpg', '.jpeg', '.webp']),
|
|
117
|
+
]);
|
|
118
|
+
const notesPath = firstExisting([
|
|
119
|
+
projectPath('design-plan.md'),
|
|
120
|
+
projectPath('reference', 'composition-notes.md'),
|
|
121
|
+
projectPath('zones-plan.md'),
|
|
122
|
+
projectPath('.trace', 'artifacts', 'P11_asset_plan', 'design-plan.md'),
|
|
123
|
+
projectPath('.trace', 'artifacts', 'P11_asset_plan', 'zones-plan.md'),
|
|
124
|
+
]);
|
|
125
|
+
const notesRaw = notesPath ? read(notesPath).trim() : '';
|
|
126
|
+
const reviewPath = firstExisting([
|
|
127
|
+
projectPath('render-review.md'),
|
|
128
|
+
projectPath('.trace', 'artifacts', 'P16_read_render', 'render-review.md'),
|
|
129
|
+
]);
|
|
130
|
+
const reviewRaw = reviewPath ? read(reviewPath).trim() : '';
|
|
131
|
+
const indexRaw = read(projectPath('index.html'));
|
|
132
|
+
const renderPath = firstExisting([
|
|
133
|
+
projectPath('out', 'poster.png'),
|
|
134
|
+
...filesIn(projectPath('out'), ['.png', '.jpg', '.jpeg', '.webp']),
|
|
135
|
+
]);
|
|
136
|
+
const layersPath = firstExisting([
|
|
137
|
+
projectPath('layers.html'),
|
|
138
|
+
projectPath('editor.html'),
|
|
139
|
+
]);
|
|
140
|
+
const events = traceEvents();
|
|
141
|
+
const assetFiles = filesIn(projectPath('assets'), ['.png', '.jpg', '.jpeg', '.webp']);
|
|
142
|
+
|
|
143
|
+
function eventTime(match) {
|
|
144
|
+
return events.find(match)?.ts || '';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const startedAt = events.find((event) => event.type === 'run_start')?.ts || manifest.created_at || '';
|
|
148
|
+
function timeLabel(match) {
|
|
149
|
+
const stamp = eventTime(match);
|
|
150
|
+
if (!startedAt || !stamp) return '';
|
|
151
|
+
const seconds = Math.max(0, Math.round((new Date(stamp) - new Date(startedAt)) / 1000));
|
|
152
|
+
const minutes = Math.floor(seconds / 60);
|
|
153
|
+
return `T+${String(minutes).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function findAssetFile(id, index) {
|
|
157
|
+
if (id) {
|
|
158
|
+
const exact = assetFiles.find((path) => {
|
|
159
|
+
const stem = path.slice(0, -extname(path).length).split(sep).pop();
|
|
160
|
+
return stem.toLowerCase() === String(id).toLowerCase();
|
|
161
|
+
});
|
|
162
|
+
if (exact) return exact;
|
|
163
|
+
const loose = assetFiles.find((path) => path.toLowerCase().includes(String(id).toLowerCase()));
|
|
164
|
+
if (loose) return loose;
|
|
165
|
+
}
|
|
166
|
+
return assetFiles[index] || null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let assetItems = [];
|
|
170
|
+
if (Array.isArray(assetPlan.assets) && assetPlan.assets.length) {
|
|
171
|
+
assetItems = assetPlan.assets.map((asset, index) => {
|
|
172
|
+
const promptKey = asset.prompt || asset.prompt_id || asset.id;
|
|
173
|
+
const section = findSection(promptSections, promptKey) || findSection(promptSections, asset.id);
|
|
174
|
+
const file = findAssetFile(asset.id, index);
|
|
175
|
+
return {
|
|
176
|
+
id: asset.id || `asset-${index + 1}`,
|
|
177
|
+
title: asset.label || asset.id || section?.title || `Asset ${index + 1}`,
|
|
178
|
+
promptId: promptKey || '',
|
|
179
|
+
prompt: section?.body || '',
|
|
180
|
+
src: browserPath(file),
|
|
181
|
+
source: file ? posix(relative(projectRoot, file)) : '',
|
|
182
|
+
contain: /cutout|relief|map|diagram|logo/i.test(`${asset.form || ''} ${asset.id || ''}`),
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
const shippingSections = promptSections.filter((section) => !/composition reference|art-directed composition/i.test(section.title));
|
|
187
|
+
assetItems = assetFiles.map((file, index) => ({
|
|
188
|
+
id: file.slice(0, -extname(file).length).split(sep).pop(),
|
|
189
|
+
title: shippingSections[index]?.title || file.split(sep).pop(),
|
|
190
|
+
promptId: shippingSections[index]?.title || '',
|
|
191
|
+
prompt: shippingSections[index]?.body || '',
|
|
192
|
+
src: browserPath(file),
|
|
193
|
+
source: posix(relative(projectRoot, file)),
|
|
194
|
+
contain: /cutout|relief|map|diagram|logo/i.test(file),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const layerIds = [...indexRaw.matchAll(/data-layer-id\s*=\s*["']([^"']+)["']/g)].map((match) => match[1]);
|
|
199
|
+
const groupIds = [...indexRaw.matchAll(/data-explode-group\s*=\s*["']([^"']+)["']/g)].map((match) => match[1]);
|
|
200
|
+
const uniqueLayers = new Set(layerIds);
|
|
201
|
+
const uniqueGroups = new Set(groupIds);
|
|
202
|
+
const layersRaw = layersPath && layersPath.endsWith('layers.html') ? read(layersPath) : '';
|
|
203
|
+
const componentCount = layersRaw ? (layersRaw.match(/class=["'][^"']*hf-exploded-item\b/g) || []).length : 0;
|
|
204
|
+
const mainMatch = indexRaw.match(/<main\b[\s\S]*?<\/main>/i);
|
|
205
|
+
const codeExcerpt = (mainMatch?.[0] || indexRaw).slice(0, 5200).trim();
|
|
206
|
+
const reviewBullets = [...reviewRaw.matchAll(/^\s*-\s+(.+)$/gm)].map((match) => match[1].trim());
|
|
207
|
+
const explicitPasses = (reviewRaw.match(/^##\s+Pass\b/gmi) || []).length;
|
|
208
|
+
const renderPasses = events.filter((event) => event.type === 'exec_done' && /^P15_render/.test(event.step || '')).length;
|
|
209
|
+
const passCount = explicitPasses || Math.max(1, renderPasses);
|
|
210
|
+
const canvas = poster.canvas?.width && poster.canvas?.height ? `${poster.canvas.width} × ${poster.canvas.height}` : 'fixed canvas';
|
|
211
|
+
const documentTitle = (indexRaw.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1] || '').replace(/<[^>]+>/g, '').trim();
|
|
212
|
+
const title = manifest.label || documentTitle || projectRoot.split(sep).pop();
|
|
213
|
+
|
|
214
|
+
const stages = [
|
|
215
|
+
{
|
|
216
|
+
id: 'input', order: '01', kind: 'input', phase: 'think', title: '需求输入',
|
|
217
|
+
statusLabel: briefRaw ? '原始记录' : '记录缺失',
|
|
218
|
+
raw: briefRaw,
|
|
219
|
+
fallbackLabel: briefRaw ? '' : manifest.label || '',
|
|
220
|
+
attachments: filesIn(projectPath('input')).map((path) => posix(relative(projectRoot, path))),
|
|
221
|
+
timeLabel: timeLabel((event) => event.type === 'run_start'),
|
|
222
|
+
artifactLabel: briefRaw ? '1 个原始 Prompt' : '仅 trace label',
|
|
223
|
+
sources: briefPath ? [posix(relative(projectRoot, briefPath))] : ['missing: brief.md'],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'reference', order: '02', kind: 'reference', phase: 'think', title: '视觉参考',
|
|
227
|
+
statusLabel: compositionPrompt && referenceImage ? '已确定' : '证据不完整',
|
|
228
|
+
prompt: compositionPrompt,
|
|
229
|
+
promptSource: compositionPromptPath ? posix(relative(projectRoot, compositionPromptPath)) : (compositionSection ? 'prompts.md / composition reference' : ''),
|
|
230
|
+
preview: browserPath(referenceImage),
|
|
231
|
+
timeLabel: timeLabel((event) => /P11_reference|P02_reference/.test(event.step || '')),
|
|
232
|
+
artifactLabel: referenceImage ? '1 个参考结果' : '无参考图',
|
|
233
|
+
sources: [compositionPromptPath, referenceImage].filter(Boolean).map((path) => posix(relative(projectRoot, path))).concat(compositionSection && !compositionPromptPath ? ['prompts.md / composition reference'] : []),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: 'plan', order: '03', kind: 'plan', phase: 'think', title: '设计规划',
|
|
237
|
+
statusLabel: notesRaw || assetPlanRaw ? '已决策' : '记录缺失',
|
|
238
|
+
raw: notesRaw || assetPlanRaw,
|
|
239
|
+
assetPlanRaw,
|
|
240
|
+
architecture: assetPlan.architecture || assetPlan.type || '',
|
|
241
|
+
planTitle: assetPlan.architecture || (notesPath ? notesPath.split(sep).pop() : '版式与信息层级'),
|
|
242
|
+
slots: Array.isArray(assetPlan.assets) ? assetPlan.assets : [],
|
|
243
|
+
timeLabel: timeLabel((event) => /P09_canvas|P11_asset_plan/.test(event.step || '')),
|
|
244
|
+
artifactLabel: `${Array.isArray(assetPlan.assets) ? assetPlan.assets.length : 0} 个视觉槽位`,
|
|
245
|
+
sources: [notesPath, assetPlanPath].filter(Boolean).map((path) => posix(relative(projectRoot, path))),
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: 'assets', order: '04', kind: 'assets', phase: 'make', title: '素材生成',
|
|
249
|
+
statusLabel: assetItems.length ? '已生成' : '无生成素材',
|
|
250
|
+
items: assetItems,
|
|
251
|
+
raw: promptsRaw,
|
|
252
|
+
timeLabel: timeLabel((event) => /P12_generate_imagery|P21_bottom_scene/.test(event.step || '')),
|
|
253
|
+
artifactLabel: `${assetItems.length} 个素材`,
|
|
254
|
+
sources: [promptPath, ...assetFiles].filter(Boolean).map((path) => posix(relative(projectRoot, path))),
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'html', order: '05', kind: 'html', phase: 'make', title: 'HTML 构建',
|
|
258
|
+
statusLabel: renderPath ? '已渲染' : '已编码',
|
|
259
|
+
code: codeExcerpt,
|
|
260
|
+
fullCode: indexRaw.slice(0, 80000),
|
|
261
|
+
render: browserPath(renderPath),
|
|
262
|
+
canvas,
|
|
263
|
+
layerCount: uniqueLayers.size,
|
|
264
|
+
groupCount: uniqueGroups.size,
|
|
265
|
+
timeLabel: timeLabel((event) => /P14_cleanup|P15_render/.test(event.step || '')),
|
|
266
|
+
artifactLabel: renderPath ? 'HTML + PNG' : 'HTML',
|
|
267
|
+
sources: ['index.html'].concat(renderPath ? [posix(relative(projectRoot, renderPath))] : []),
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'review', order: '06', kind: 'review', phase: 'prove', title: 'Review 反馈',
|
|
271
|
+
statusLabel: reviewRaw ? '已修正' : '记录缺失',
|
|
272
|
+
raw: reviewRaw,
|
|
273
|
+
bullets: reviewBullets,
|
|
274
|
+
passCount,
|
|
275
|
+
timeLabel: timeLabel((event) => /P16_read_render|P16_optimization|P25_read_render/.test(event.step || '')),
|
|
276
|
+
artifactLabel: `${passCount} 轮 Review`,
|
|
277
|
+
sources: reviewPath ? [posix(relative(projectRoot, reviewPath)), '.trace/events.jsonl'] : ['missing: render-review.md', '.trace/events.jsonl'],
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: 'layers', order: '07', kind: 'layers', phase: 'prove', title: '分层展示',
|
|
281
|
+
statusLabel: layersPath ? '已验证' : '未生成',
|
|
282
|
+
layerCount: uniqueLayers.size,
|
|
283
|
+
groupCount: uniqueGroups.size,
|
|
284
|
+
componentCount,
|
|
285
|
+
preview: browserPath(layersPath),
|
|
286
|
+
previewSource: layersPath ? posix(relative(projectRoot, layersPath)) : '',
|
|
287
|
+
timeLabel: timeLabel((event) => /P17_editor|P20_editor|P26_editor/.test(event.step || '')),
|
|
288
|
+
artifactLabel: `${uniqueLayers.size} 图层 · ${uniqueGroups.size} 组`,
|
|
289
|
+
sources: layersPath ? [posix(relative(projectRoot, layersPath)), 'index.html / data-layer-id'] : ['missing: editor.html or layers.html'],
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
const replay = {
|
|
294
|
+
schema: 'poster-replay/v1',
|
|
295
|
+
generatedAt: new Date().toISOString(),
|
|
296
|
+
project: {
|
|
297
|
+
title,
|
|
298
|
+
slug: projectRoot.split(sep).pop(),
|
|
299
|
+
status: manifest.status || 'ok',
|
|
300
|
+
canvas,
|
|
301
|
+
},
|
|
302
|
+
stages,
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
mkdirSync(outDir, { recursive: true });
|
|
306
|
+
for (const file of ['index.html', 'replay.css', 'replay.js', 'replay-motion.js']) {
|
|
307
|
+
copyFileSync(join(templateDir, file), join(outDir, file));
|
|
308
|
+
}
|
|
309
|
+
cpSync(join(templateDir, 'icons'), join(outDir, 'icons'), { recursive: true });
|
|
310
|
+
writeFileSync(join(outDir, 'data.js'), `window.__POSTER_REPLAY__ = ${JSON.stringify(replay, null, 2)};\n`);
|
|
311
|
+
|
|
312
|
+
const missing = stages.filter((stage) => /缺失|不完整|未生成/.test(stage.statusLabel));
|
|
313
|
+
console.log(`replay ${join(outDir, 'index.html')}`);
|
|
314
|
+
console.log(`stages ${stages.length}`);
|
|
315
|
+
console.log(`assets ${assetItems.length}`);
|
|
316
|
+
if (missing.length) console.log(`evidence missing or partial: ${missing.map((stage) => stage.id).join(', ')}`);
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-contract.mjs —— 检查一张海报是否满足可编辑性契约
|
|
4
|
+
*
|
|
5
|
+
* node check-contract.mjs <海报 index.html> [--json]
|
|
6
|
+
*
|
|
7
|
+
* 退出码 0 = 通过,1 = 有违规,2 = 用法/环境错误。
|
|
8
|
+
* 违规项交给 agent 修(改成什么值取决于设计意图),修完重跑。
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
import { launch, open, probe, diff } from './_browser.mjs';
|
|
13
|
+
|
|
14
|
+
const file = process.argv[2];
|
|
15
|
+
const asJson = process.argv.includes('--json');
|
|
16
|
+
if (!file || !existsSync(resolve(file))) {
|
|
17
|
+
console.error('用法: node check-contract.mjs <海报 index.html> [--json]');
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const report = { file: resolve(file), pass: true, checks: [], hints: {} };
|
|
22
|
+
const add = (id, ok, msg, fix) => {
|
|
23
|
+
report.checks.push({ id, ok, msg, fix });
|
|
24
|
+
if (!ok) report.pass = false;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const browser = await launch();
|
|
28
|
+
try {
|
|
29
|
+
const { page, errors } = await open(browser, file, { width: 1400, height: 1000 });
|
|
30
|
+
|
|
31
|
+
const info = await page.evaluate(() => {
|
|
32
|
+
const cv = document.querySelector('[data-canvas-width]') || document.querySelector('.poster-canvas');
|
|
33
|
+
if (!cv) return { canvas: false };
|
|
34
|
+
const cs = getComputedStyle(cv);
|
|
35
|
+
const layers = [...cv.querySelectorAll('[data-layer-id]')];
|
|
36
|
+
const ids = layers.map((l) => l.dataset.layerId);
|
|
37
|
+
const groupNodes = [...cv.querySelectorAll('[data-explode-group]')];
|
|
38
|
+
const groupIds = groupNodes.map((n) => (n.dataset.explodeGroup || '').trim());
|
|
39
|
+
const groupSet = new Set(groupIds.filter(Boolean));
|
|
40
|
+
const allowedRoles = new Set(['surface', 'image', 'copy', 'icon', 'decoration']);
|
|
41
|
+
const groups = groupNodes.map((node) => {
|
|
42
|
+
const id = (node.dataset.explodeGroup || '').trim();
|
|
43
|
+
const members = layers.filter((layer) =>
|
|
44
|
+
layer.closest('[data-explode-group]') === node
|
|
45
|
+
|| (layer.dataset.explodeParent || '').trim() === id);
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
label: (node.dataset.explodeLabel || '').trim(),
|
|
49
|
+
members: [...new Set(members)].map((layer) => layer.dataset.layerId),
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// 编辑器会把每个文字叶子自动标成 hf-slot,并用命中位置进入编辑。
|
|
54
|
+
// 像素没有重叠不代表交互框没有重叠;若 slot 中心被另一个图层截获,
|
|
55
|
+
// 双击看似点中文字,实际却永远进不了编辑态。
|
|
56
|
+
const blockedTextTargets = [];
|
|
57
|
+
const textSlots = (root) => {
|
|
58
|
+
const out = [];
|
|
59
|
+
(function walk(node) {
|
|
60
|
+
if (node instanceof SVGElement) return;
|
|
61
|
+
const ownText = [...node.childNodes]
|
|
62
|
+
.some((child) => child.nodeType === 3 && child.textContent.trim());
|
|
63
|
+
if (!node.children.length || ownText) {
|
|
64
|
+
if (node.textContent.trim()) out.push(node);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
for (const child of node.children) walk(child);
|
|
68
|
+
})(root);
|
|
69
|
+
return out;
|
|
70
|
+
};
|
|
71
|
+
for (const layer of layers) {
|
|
72
|
+
for (const slot of textSlots(layer)) {
|
|
73
|
+
slot.scrollIntoView({ block: 'center', inline: 'center' });
|
|
74
|
+
const rect = slot.getBoundingClientRect();
|
|
75
|
+
if (rect.width < 1 || rect.height < 1) continue;
|
|
76
|
+
const top = document.elementFromPoint(
|
|
77
|
+
rect.left + rect.width / 2,
|
|
78
|
+
rect.top + rect.height / 2,
|
|
79
|
+
);
|
|
80
|
+
const owner = top?.closest('[data-layer-id]');
|
|
81
|
+
if (owner !== layer) {
|
|
82
|
+
blockedTextTargets.push({
|
|
83
|
+
layer: layer.dataset.layerId,
|
|
84
|
+
text: slot.textContent.trim().replace(/\s+/g, ' ').slice(0, 24),
|
|
85
|
+
blocker: owner?.dataset.layerId || top?.tagName?.toLowerCase() || '(none)',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
window.scrollTo(0, 0);
|
|
91
|
+
|
|
92
|
+
// 候选漏标块:有文字、不属于任何图层、且内部也不含图层。
|
|
93
|
+
// 最后一条很关键 —— 图层的祖先容器(如 .content / .footer)本身
|
|
94
|
+
// 没有 data-layer-id,但它不是漏标,只是包装。
|
|
95
|
+
const unlabeled = [];
|
|
96
|
+
for (const el of cv.querySelectorAll('*')) {
|
|
97
|
+
if (el.closest('[data-layer-id]') || el instanceof SVGElement) continue;
|
|
98
|
+
if (el.querySelector('[data-layer-id]')) continue;
|
|
99
|
+
if (!el.textContent.trim()) continue;
|
|
100
|
+
if (unlabeled.some((o) => o.node.contains(el))) continue;
|
|
101
|
+
unlabeled.push({ node: el });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 静态定位 vw/vh 的出现处,便于修复时直接跳到那一行
|
|
105
|
+
const viewportUnits = [];
|
|
106
|
+
for (const sheet of document.styleSheets) {
|
|
107
|
+
let rules;
|
|
108
|
+
try { rules = sheet.cssRules; } catch { continue; }
|
|
109
|
+
for (const r of rules) {
|
|
110
|
+
if (!r.cssText) continue;
|
|
111
|
+
for (const m of r.cssText.matchAll(/[^;{}]*\b\d*\.?\d+v(?:w|h|min|max)\b[^;{}]*/g)) {
|
|
112
|
+
const frag = m[0].trim();
|
|
113
|
+
const sel = r.selectorText || r.conditionText || '@rule';
|
|
114
|
+
if (viewportUnits.length < 25) viewportUnits.push(`${sel} { ${frag} }`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
canvas: true,
|
|
121
|
+
declaredW: +cv.dataset.canvasWidth || null,
|
|
122
|
+
declaredH: +cv.dataset.canvasHeight || null,
|
|
123
|
+
actualW: Math.round(cv.getBoundingClientRect().width),
|
|
124
|
+
actualH: Math.round(cv.getBoundingClientRect().height),
|
|
125
|
+
position: cs.position,
|
|
126
|
+
layerCount: layers.length,
|
|
127
|
+
duplicateIds: ids.filter((v, i) => ids.indexOf(v) !== i),
|
|
128
|
+
emptyIds: ids.filter((v) => !v || !v.trim()).length,
|
|
129
|
+
nestedLayers: layers.filter((layer) => layer.querySelector('[data-layer-id]'))
|
|
130
|
+
.map((layer) => layer.dataset.layerId),
|
|
131
|
+
groups,
|
|
132
|
+
duplicateGroups: groupIds.filter((v, i) => groupIds.indexOf(v) !== i),
|
|
133
|
+
emptyGroups: groupIds.filter((v) => !v).length,
|
|
134
|
+
groupsAlsoLayers: groupNodes.filter((node) => node.hasAttribute('data-layer-id'))
|
|
135
|
+
.map((node) => node.dataset.explodeGroup || '(empty)'),
|
|
136
|
+
nestedGroups: groupNodes.filter((node) => node.parentElement?.closest('[data-explode-group]'))
|
|
137
|
+
.map((node) => node.dataset.explodeGroup || '(empty)'),
|
|
138
|
+
orphanParents: layers.filter((layer) => {
|
|
139
|
+
const parent = (layer.dataset.explodeParent || '').trim();
|
|
140
|
+
return parent && !groupSet.has(parent);
|
|
141
|
+
}).map((layer) => `${layer.dataset.layerId}→${layer.dataset.explodeParent}`),
|
|
142
|
+
invalidRoles: layers.filter((layer) => {
|
|
143
|
+
const role = (layer.dataset.explodeRole || '').trim();
|
|
144
|
+
return role && !allowedRoles.has(role);
|
|
145
|
+
}).map((layer) => `${layer.dataset.layerId}:${layer.dataset.explodeRole}`),
|
|
146
|
+
blockedTextTargets,
|
|
147
|
+
unlabeled: unlabeled.map((o) => ({
|
|
148
|
+
tag: o.node.tagName.toLowerCase(),
|
|
149
|
+
cls: (typeof o.node.className === 'string' ? o.node.className : '') || '-',
|
|
150
|
+
text: o.node.textContent.trim().replace(/\s+/g, ' ').slice(0, 34),
|
|
151
|
+
})),
|
|
152
|
+
viewportUnits,
|
|
153
|
+
fontStatus: document.fonts.status,
|
|
154
|
+
/* 声明了但没被用到的 @font-face 会一直是 unloaded,这是浏览器按需加载的
|
|
155
|
+
正常行为,仅作提示。注意 Google Fonts 把中文字体切成上百个
|
|
156
|
+
unicode-range 分片,同一 family 下必然部分 loaded 部分 unloaded,
|
|
157
|
+
所以只有「没有任何分片被加载」才算这个 family 真的没用上。 */
|
|
158
|
+
idleFonts: (() => {
|
|
159
|
+
const tally = new Map();
|
|
160
|
+
for (const f of document.fonts) {
|
|
161
|
+
const cur = tally.get(f.family) || { loaded: 0 };
|
|
162
|
+
if (f.status === 'loaded') cur.loaded++;
|
|
163
|
+
tally.set(f.family, cur);
|
|
164
|
+
}
|
|
165
|
+
return [...tally].filter(([, v]) => v.loaded === 0).map(([k]) => k).slice(0, 10);
|
|
166
|
+
})(),
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (!info.canvas) {
|
|
171
|
+
add('C1-canvas', false, '找不到画布根节点',
|
|
172
|
+
'给海报根节点加 data-canvas-width / data-canvas-height(或使用 class="poster-canvas")');
|
|
173
|
+
} else {
|
|
174
|
+
add('C1-canvas', true, `画布 ${info.actualW}×${info.actualH}`);
|
|
175
|
+
|
|
176
|
+
const declared = info.declaredW && info.declaredH;
|
|
177
|
+
add('C1-declared', !!declared,
|
|
178
|
+
declared ? `已声明 data-canvas-width/height = ${info.declaredW}×${info.declaredH}` : '缺少 data-canvas-width / data-canvas-height',
|
|
179
|
+
'在画布根节点上声明 data-canvas-width="<宽>" data-canvas-height="<高>"');
|
|
180
|
+
|
|
181
|
+
if (declared) {
|
|
182
|
+
const match = Math.abs(info.declaredW - info.actualW) <= 1 && Math.abs(info.declaredH - info.actualH) <= 1;
|
|
183
|
+
add('C1-match', match,
|
|
184
|
+
match ? '声明尺寸与实际渲染尺寸一致' : `声明 ${info.declaredW}×${info.declaredH} 与实际 ${info.actualW}×${info.actualH} 不一致`,
|
|
185
|
+
'把画布宽高改成 px 写死,并与 data-canvas-* 的声明保持一致');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
add('C1-position', ['relative', 'absolute', 'fixed'].includes(info.position),
|
|
189
|
+
`画布 position: ${info.position}`,
|
|
190
|
+
'画布需要 position:relative,否则图层没有确定的包含块');
|
|
191
|
+
|
|
192
|
+
add('C3-layers', info.layerCount > 0,
|
|
193
|
+
`找到 ${info.layerCount} 个 data-layer-id 图层`,
|
|
194
|
+
'给每个语义模块加 data-layer-id(判据:使用者是否会想单独移动它)');
|
|
195
|
+
|
|
196
|
+
add('C3-unique', info.duplicateIds.length === 0 && info.emptyIds === 0,
|
|
197
|
+
info.duplicateIds.length ? `data-layer-id 重复: ${[...new Set(info.duplicateIds)].join(', ')}` : 'data-layer-id 无重复无空值',
|
|
198
|
+
'data-layer-id 必须唯一且非空');
|
|
199
|
+
|
|
200
|
+
add('C3-flat', info.nestedLayers.length === 0,
|
|
201
|
+
info.nestedLayers.length ? `data-layer-id 不可嵌套: ${info.nestedLayers.join(', ')}` : 'data-layer-id 叶子图层无嵌套',
|
|
202
|
+
'把共同容器改为不带 data-layer-id 的 data-explode-group,实际可编辑单元保留为叶子图层');
|
|
203
|
+
|
|
204
|
+
const groupIdsOk = info.duplicateGroups.length === 0 && info.emptyGroups === 0;
|
|
205
|
+
add('C4-group-ids', groupIdsOk,
|
|
206
|
+
!info.groups.length ? '未声明一级内容分组(使用扁平组件模式)'
|
|
207
|
+
: groupIdsOk ? `找到 ${info.groups.length} 个唯一分组` : `分组 id 重复或为空: ${[...new Set(info.duplicateGroups)].join(', ') || '(empty)'}`,
|
|
208
|
+
'data-explode-group 必须唯一且非空');
|
|
209
|
+
|
|
210
|
+
const groupShapeOk = info.groupsAlsoLayers.length === 0 && info.nestedGroups.length === 0;
|
|
211
|
+
add('C4-group-shape', groupShapeOk,
|
|
212
|
+
groupShapeOk ? '分组容器不参与拖拽且不互相嵌套'
|
|
213
|
+
: `无效分组容器: ${[...info.groupsAlsoLayers, ...info.nestedGroups].join(', ')}`,
|
|
214
|
+
'分组容器只加 data-explode-group,不加 data-layer-id,也不要嵌套另一个分组容器');
|
|
215
|
+
|
|
216
|
+
const usefulGroups = info.groups.filter((group) => group.members.length >= 2).length;
|
|
217
|
+
const membersOk = info.groups.every((group) => group.members.length >= 2)
|
|
218
|
+
&& info.orphanParents.length === 0 && info.invalidRoles.length === 0;
|
|
219
|
+
add('C4-group-members', membersOk,
|
|
220
|
+
!info.groups.length ? '无需分组成员检查'
|
|
221
|
+
: membersOk ? `${usefulGroups} 个分组均含至少 2 个叶子图层`
|
|
222
|
+
: `分组成员/角色无效${info.orphanParents.length ? `;孤立归属 ${info.orphanParents.join(', ')}` : ''}${info.invalidRoles.length ? `;未知角色 ${info.invalidRoles.join(', ')}` : ''}`,
|
|
223
|
+
'每个分组至少包含 2 个叶子 data-layer-id;角色仅用 surface/image/copy/icon/decoration');
|
|
224
|
+
|
|
225
|
+
add('C5-text-hit', info.blockedTextTargets.length === 0,
|
|
226
|
+
info.blockedTextTargets.length
|
|
227
|
+
? `文字编辑命中区被遮挡: ${info.blockedTextTargets.slice(0, 8)
|
|
228
|
+
.map((item) => `${item.layer}→${item.blocker} (${item.text})`).join(', ')}`
|
|
229
|
+
: '所有文字编辑命中区均可到达',
|
|
230
|
+
'调整重叠图层的位置、pointer-events 或 DOM/z-index 顺序,使文字槽中心能命中所属 data-layer-id');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* C2 的实质检测:调整同一真实浏览器页面的视口,图层坐标不应变化。
|
|
234
|
+
这比正则查 vh 精确得多 —— 百分比、vmin、calc() 混用都能抓到。 */
|
|
235
|
+
const m1 = await probe(page);
|
|
236
|
+
await page.setViewport({ width: 1000, height: 1500, deviceScaleFactor: 1 });
|
|
237
|
+
await page.evaluate(() => new Promise((resolveFrame) => {
|
|
238
|
+
requestAnimationFrame(() => requestAnimationFrame(resolveFrame));
|
|
239
|
+
}));
|
|
240
|
+
const m2 = await probe(page);
|
|
241
|
+
const d = diff(m1, m2, 0.5);
|
|
242
|
+
add('C2-viewport', d.ok,
|
|
243
|
+
d.ok ? '调整视口后坐标不变(布局不依赖视口)'
|
|
244
|
+
: `调整视口后坐标发生变化,最大 ${d.worst}px:${d.rows.filter((r) => r.fail).map((r) => r.id).join(', ')}`,
|
|
245
|
+
'布局尺寸不能用 vw / vh / 百分比承担,改为 px 固定值(渐变、纹理等装饰不限)');
|
|
246
|
+
|
|
247
|
+
add('fonts', info.fontStatus === 'loaded',
|
|
248
|
+
info.fontStatus === 'loaded' ? '字体加载已完成(document.fonts.status = loaded)' : `字体仍在加载: ${info.fontStatus}`,
|
|
249
|
+
'字体未落地会导致测量到 fallback 字体的坐标;检查网络或字体引用');
|
|
250
|
+
|
|
251
|
+
add('runtime', errors.length === 0,
|
|
252
|
+
errors.length ? `页面有 ${errors.length} 处报错` : '页面无控制台报错');
|
|
253
|
+
|
|
254
|
+
report.hints = {
|
|
255
|
+
unlabeled: info.unlabeled || [],
|
|
256
|
+
viewportUnits: info.viewportUnits || [],
|
|
257
|
+
idleFonts: info.idleFonts || [],
|
|
258
|
+
pageErrors: errors,
|
|
259
|
+
};
|
|
260
|
+
} finally {
|
|
261
|
+
await browser.close();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (asJson) {
|
|
265
|
+
console.log(JSON.stringify(report, null, 2));
|
|
266
|
+
process.exit(report.pass ? 0 : 1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log(`\n契约检查 · ${report.file}\n`);
|
|
270
|
+
for (const c of report.checks) {
|
|
271
|
+
console.log(` ${c.ok ? '✅' : '❌'} [${c.id}] ${c.msg}`);
|
|
272
|
+
if (!c.ok && c.fix) console.log(` 修法: ${c.fix}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const { unlabeled, viewportUnits, idleFonts, pageErrors } = report.hints;
|
|
276
|
+
if (idleFonts.length) {
|
|
277
|
+
console.log(`\n声明但未被使用的字体(按需加载,正常现象): ${idleFonts.join(', ')}`);
|
|
278
|
+
}
|
|
279
|
+
if (viewportUnits.length) {
|
|
280
|
+
console.log(`\n视口单位出现处(供定位,装饰性用途可忽略):`);
|
|
281
|
+
for (const v of viewportUnits) console.log(' ' + v);
|
|
282
|
+
}
|
|
283
|
+
if (unlabeled.length) {
|
|
284
|
+
console.log(`\n可能漏标 data-layer-id 的候选块(${unlabeled.length} 个,需人工判断):`);
|
|
285
|
+
for (const u of unlabeled) console.log(` <${u.tag} class="${u.cls}"> ${u.text}`);
|
|
286
|
+
}
|
|
287
|
+
if (pageErrors.length) {
|
|
288
|
+
console.log(`\n页面报错:`);
|
|
289
|
+
for (const e of pageErrors) console.log(' ' + e);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log(report.pass ? '\n契约通过 ✅ 可以执行 bake.mjs\n' : '\n契约未通过 ❌ 修复后重跑\n');
|
|
293
|
+
process.exit(report.pass ? 0 : 1);
|