@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,40 @@
|
|
|
1
|
+
/** Serialize computed layout for dry-run / inspect (#9). */
|
|
2
|
+
|
|
3
|
+
export function componentBox(c) {
|
|
4
|
+
return {
|
|
5
|
+
id: c.id,
|
|
6
|
+
type: c.type,
|
|
7
|
+
label: c.label,
|
|
8
|
+
x: Math.round(c.x),
|
|
9
|
+
y: Math.round(c.y),
|
|
10
|
+
width: c.width,
|
|
11
|
+
height: c.height,
|
|
12
|
+
...(Number.isInteger(c.row) ? { row: c.row } : {}),
|
|
13
|
+
...(Number.isInteger(c.col) ? { col: c.col } : {}),
|
|
14
|
+
...(Array.isArray(c.pos) ? { pos: c.pos.map(Math.round) } : {}),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function boundaryBox(b) {
|
|
19
|
+
return {
|
|
20
|
+
kind: b.kind,
|
|
21
|
+
label: b.label,
|
|
22
|
+
x: Math.round(b.x),
|
|
23
|
+
y: Math.round(b.y),
|
|
24
|
+
width: Math.round(b.width),
|
|
25
|
+
height: Math.round(b.height),
|
|
26
|
+
wraps: b.wraps,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function connectionPath(conn, routed, labelAt) {
|
|
31
|
+
return {
|
|
32
|
+
from: conn.from,
|
|
33
|
+
to: conn.to,
|
|
34
|
+
label: conn.label ?? null,
|
|
35
|
+
variant: conn.variant ?? 'default',
|
|
36
|
+
route: conn.route ?? 'auto',
|
|
37
|
+
points: routed.points.map(([x, y]) => [Math.round(x), Math.round(y)]),
|
|
38
|
+
...(labelAt ? { labelAt: labelAt.map(Math.round) } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { throwDiagnosticError } from './diagnostics.mjs';
|
|
2
|
+
import { rectsOverlap, segmentIntersectsRect } from './geometry.mjs';
|
|
3
|
+
import { esc, textUnits } from './utils.mjs';
|
|
4
|
+
import { translateMessage } from './i18n.mjs';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_FONT_SIZE = 8;
|
|
7
|
+
const DEFAULT_ITEM_GAP = 22;
|
|
8
|
+
const DEFAULT_LINE_GAP = 22;
|
|
9
|
+
const DEFAULT_SWATCH_GAP = 8;
|
|
10
|
+
const TEXT_ADVANCE_EM = 0.62;
|
|
11
|
+
const INTERACTIVE_BADGE_ALLOWANCE = 21;
|
|
12
|
+
|
|
13
|
+
export function relationshipLegendObstacles(relations, { pointsFor, labelRectFor } = {}) {
|
|
14
|
+
const obstacles = [];
|
|
15
|
+
for (const [index, relation] of (Array.isArray(relations) ? relations : []).entries()) {
|
|
16
|
+
const points = typeof pointsFor === 'function' ? pointsFor(relation, index) : [];
|
|
17
|
+
const finitePoints = (Array.isArray(points) ? points : []).filter((point) => (
|
|
18
|
+
Array.isArray(point) && point.length === 2 && point.every(Number.isFinite)
|
|
19
|
+
));
|
|
20
|
+
for (let pointIndex = 0; pointIndex < finitePoints.length - 1; pointIndex += 1) {
|
|
21
|
+
obstacles.push({
|
|
22
|
+
kind: 'relationship-segment',
|
|
23
|
+
start: finitePoints[pointIndex],
|
|
24
|
+
end: finitePoints[pointIndex + 1],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const labelRect = typeof labelRectFor === 'function' ? labelRectFor(relation, index) : null;
|
|
28
|
+
if (labelRect && [labelRect.x, labelRect.y, labelRect.width, labelRect.height].every(Number.isFinite)) {
|
|
29
|
+
obstacles.push({ kind: 'relationship-label', ...labelRect });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return obstacles;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveLegend(config, catalog, presentKinds) {
|
|
36
|
+
const mode = config?.mode || 'auto';
|
|
37
|
+
if (mode === 'hidden') return [];
|
|
38
|
+
const present = presentKinds instanceof Set ? presentKinds : new Set(presentKinds || []);
|
|
39
|
+
const overrides = config?.entries || {};
|
|
40
|
+
|
|
41
|
+
return catalog.flatMap((catalogEntry) => {
|
|
42
|
+
const override = overrides[catalogEntry.kind] || {};
|
|
43
|
+
const selectedByMode = mode === 'all' || present.has(catalogEntry.kind);
|
|
44
|
+
const visible = override.visible === true || (selectedByMode && override.visible !== false);
|
|
45
|
+
if (!visible) return [];
|
|
46
|
+
return [{
|
|
47
|
+
...catalogEntry,
|
|
48
|
+
label: override.label || catalogEntry.label,
|
|
49
|
+
present: present.has(catalogEntry.kind),
|
|
50
|
+
interactive: catalogEntry.interactive !== false && present.has(catalogEntry.kind),
|
|
51
|
+
}];
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function measuredEntryWidth(entry, fontSize, swatchGap) {
|
|
56
|
+
const swatchWidth = entry.swatchWidth ?? 14;
|
|
57
|
+
return Math.ceil(
|
|
58
|
+
swatchWidth
|
|
59
|
+
+ swatchGap
|
|
60
|
+
+ textUnits(entry.label) * fontSize * TEXT_ADVANCE_EM
|
|
61
|
+
+ (entry.interactive ? INTERACTIVE_BADGE_ALLOWANCE : 0),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// One pure footprint calculation owns both auto-viewBox sizing and final SVG
|
|
66
|
+
// placement. Callers must not maintain a second approximation of legend width
|
|
67
|
+
// or row count; that would make generated geometry disagree with validation.
|
|
68
|
+
export function legendFootprint(entries, {
|
|
69
|
+
width,
|
|
70
|
+
fontSize = DEFAULT_FONT_SIZE,
|
|
71
|
+
itemGap = DEFAULT_ITEM_GAP,
|
|
72
|
+
lineGap = DEFAULT_LINE_GAP,
|
|
73
|
+
swatchGap = DEFAULT_SWATCH_GAP,
|
|
74
|
+
} = {}) {
|
|
75
|
+
if (!entries.length) {
|
|
76
|
+
return { measured: [], rows: [], rowCount: 0, minWidth: 0, extraHeight: 0 };
|
|
77
|
+
}
|
|
78
|
+
const measured = entries.map((entry) => ({
|
|
79
|
+
...entry,
|
|
80
|
+
width: measuredEntryWidth(entry, fontSize, entry.swatchGap ?? swatchGap),
|
|
81
|
+
}));
|
|
82
|
+
const rows = [[]];
|
|
83
|
+
let cursor = 0;
|
|
84
|
+
for (const entry of measured) {
|
|
85
|
+
const row = rows.at(-1);
|
|
86
|
+
const required = (row.length ? itemGap : 0) + entry.width;
|
|
87
|
+
if (row.length && cursor + required > width) {
|
|
88
|
+
rows.push([entry]);
|
|
89
|
+
cursor = entry.width;
|
|
90
|
+
} else {
|
|
91
|
+
row.push(entry);
|
|
92
|
+
cursor += required;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
measured,
|
|
97
|
+
rows,
|
|
98
|
+
rowCount: rows.length,
|
|
99
|
+
minWidth: Math.max(...measured.map((entry) => entry.width)),
|
|
100
|
+
extraHeight: (rows.length - 1) * lineGap,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function measureLegend(entries, {
|
|
105
|
+
x,
|
|
106
|
+
baselineY,
|
|
107
|
+
width,
|
|
108
|
+
fontSize = DEFAULT_FONT_SIZE,
|
|
109
|
+
itemGap = DEFAULT_ITEM_GAP,
|
|
110
|
+
lineGap = DEFAULT_LINE_GAP,
|
|
111
|
+
swatchGap = DEFAULT_SWATCH_GAP,
|
|
112
|
+
minTitleY = 0,
|
|
113
|
+
obstacles = [],
|
|
114
|
+
unfit = 'error',
|
|
115
|
+
diagramType = 'diagram',
|
|
116
|
+
} = {}) {
|
|
117
|
+
if (!entries.length) return { entries: [], rowCount: 0, titleY: null };
|
|
118
|
+
const footprint = legendFootprint(entries, { width, fontSize, itemGap, lineGap, swatchGap });
|
|
119
|
+
const tooWide = footprint.measured.find((entry) => entry.width > width);
|
|
120
|
+
if (tooWide) {
|
|
121
|
+
if (unfit === 'hide') return null;
|
|
122
|
+
const message = `[legend/label-too-wide] ${diagramType} legend label for "${tooWide.kind}" needs ${tooWide.width}px but only ${width}px is available.`;
|
|
123
|
+
throwDiagnosticError(message, [{
|
|
124
|
+
code: 'legend/label-too-wide',
|
|
125
|
+
severity: 'error',
|
|
126
|
+
message,
|
|
127
|
+
subject: { diagramType, path: `/meta/legend/entries/${tooWide.kind}/label` },
|
|
128
|
+
evidence: { kind: tooWide.kind, measuredWidthPx: tooWide.width, availableWidthPx: width },
|
|
129
|
+
supportedFixes: ['shorten the legend label or use a wider viewBox'],
|
|
130
|
+
}]);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const titleY = baselineY - footprint.extraHeight - 20;
|
|
134
|
+
const legendTopY = titleY - 10;
|
|
135
|
+
if (legendTopY < minTitleY) {
|
|
136
|
+
if (unfit === 'hide') return null;
|
|
137
|
+
const message = `[legend/vertical-overflow] ${diagramType} legend needs ${footprint.rowCount} rows, which would start at y=${legendTopY} above the available legend band at y=${minTitleY}.`;
|
|
138
|
+
throwDiagnosticError(message, [{
|
|
139
|
+
code: 'legend/vertical-overflow',
|
|
140
|
+
severity: 'error',
|
|
141
|
+
message,
|
|
142
|
+
subject: { diagramType, path: '/meta/legend' },
|
|
143
|
+
evidence: { rowCount: footprint.rowCount, requiredTopY: legendTopY, availableTopY: minTitleY },
|
|
144
|
+
supportedFixes: ['shorten legend labels, hide nonessential entries, or use a wider viewBox'],
|
|
145
|
+
}]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const positioned = [];
|
|
149
|
+
footprint.rows.forEach((row, rowIndex) => {
|
|
150
|
+
let entryX = x;
|
|
151
|
+
const baseline = baselineY - (footprint.rowCount - rowIndex - 1) * lineGap;
|
|
152
|
+
for (const entry of row) {
|
|
153
|
+
positioned.push({ ...entry, x: entryX, baseline, row: rowIndex });
|
|
154
|
+
entryX += entry.width + itemGap;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const legendRects = [
|
|
159
|
+
{ kind: 'title', x, y: legendTopY, width: 48, height: 14 },
|
|
160
|
+
...positioned.map((entry) => ({
|
|
161
|
+
kind: entry.kind,
|
|
162
|
+
x: entry.x,
|
|
163
|
+
y: entry.baseline - 10,
|
|
164
|
+
width: entry.width,
|
|
165
|
+
height: 14,
|
|
166
|
+
})),
|
|
167
|
+
];
|
|
168
|
+
const collision = legendRects.find((legendRect) => obstacles.some((obstacle) => (
|
|
169
|
+
Array.isArray(obstacle.start) && Array.isArray(obstacle.end)
|
|
170
|
+
? segmentIntersectsRect({ start: obstacle.start, end: obstacle.end }, legendRect)
|
|
171
|
+
: rectsOverlap(obstacle, legendRect)
|
|
172
|
+
)));
|
|
173
|
+
if (collision) {
|
|
174
|
+
if (unfit === 'hide') return null;
|
|
175
|
+
const message = `[legend/content-overlap] ${diagramType} legend entry "${collision.kind}" overlaps authored relationship geometry.`;
|
|
176
|
+
throwDiagnosticError(message, [{
|
|
177
|
+
code: 'legend/content-overlap',
|
|
178
|
+
severity: 'error',
|
|
179
|
+
message,
|
|
180
|
+
subject: { diagramType, path: '/meta/legend' },
|
|
181
|
+
evidence: { legendKind: collision.kind, legendRect: collision },
|
|
182
|
+
supportedFixes: ['shorten or hide legend entries, use a wider viewBox, or move the authored relationship route/label out of the legend band'],
|
|
183
|
+
}]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
entries: positioned,
|
|
188
|
+
rowCount: footprint.rowCount,
|
|
189
|
+
titleY,
|
|
190
|
+
fontSize,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function renderLegend({ entries, layout, renderSwatch, locale }) {
|
|
195
|
+
if (!entries.length) return '';
|
|
196
|
+
const measured = measureLegend(entries, layout);
|
|
197
|
+
if (!measured) return '';
|
|
198
|
+
const hasInteractiveEntries = measured.entries.some((entry) => entry.interactive);
|
|
199
|
+
const renderedFontSize = measured.fontSize < 8 ? measured.fontSize + 0.5 : measured.fontSize + 2;
|
|
200
|
+
const rootAttributes = hasInteractiveEntries ? ' data-legend="" data-legend-bridge=""' : ' data-legend=""';
|
|
201
|
+
const parts = [
|
|
202
|
+
` <g${rootAttributes}>`,
|
|
203
|
+
` <text x="${layout.x}" y="${measured.titleY}" class="t-primary" font-size="12" font-weight="650">${esc(translateMessage(locale, 'legend.title'))}</text>`,
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
for (const entry of measured.entries) {
|
|
207
|
+
const interactive = entry.interactive
|
|
208
|
+
? ` data-legend-kind="${esc(entry.kind)}" data-legend-label="${esc(entry.label)}"`
|
|
209
|
+
: '';
|
|
210
|
+
parts.push(` <g data-legend-semantic-kind="${esc(entry.kind)}"${interactive} data-legend-x="${entry.x}" data-legend-baseline="${entry.baseline}" data-legend-width="${entry.width}">`);
|
|
211
|
+
parts.push(` ${renderSwatch(entry)}`);
|
|
212
|
+
parts.push(` <text x="${entry.x + (entry.swatchWidth ?? 14) + (entry.swatchGap ?? DEFAULT_SWATCH_GAP)}" y="${entry.baseline}" class="t-muted" font-size="${renderedFontSize}" font-weight="500">${esc(entry.label)}</text>`);
|
|
213
|
+
parts.push(' </g>');
|
|
214
|
+
}
|
|
215
|
+
parts.push(' </g>');
|
|
216
|
+
return parts.join('\n');
|
|
217
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const MAX_SYMLINK_DEPTH = 64;
|
|
5
|
+
const directorySemanticsCache = new Map();
|
|
6
|
+
let semanticsProbeSequence = 0;
|
|
7
|
+
|
|
8
|
+
function splitAbsolute(absolutePath) {
|
|
9
|
+
const root = path.parse(absolutePath).root;
|
|
10
|
+
return {
|
|
11
|
+
root,
|
|
12
|
+
segments: absolutePath.slice(root.length).split(path.sep).filter(Boolean),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function canonicalize(targetPath, depth) {
|
|
17
|
+
const absolutePath = path.resolve(targetPath);
|
|
18
|
+
const { root, segments } = splitAbsolute(absolutePath);
|
|
19
|
+
let current = root;
|
|
20
|
+
|
|
21
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
22
|
+
const candidate = path.join(current, segments[index]);
|
|
23
|
+
let stat;
|
|
24
|
+
try {
|
|
25
|
+
stat = fs.lstatSync(candidate);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
|
|
28
|
+
return path.resolve(current, ...segments.slice(index));
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (stat.isSymbolicLink()) {
|
|
34
|
+
if (depth >= MAX_SYMLINK_DEPTH) {
|
|
35
|
+
const error = new Error(`Could not resolve path because a symbolic-link cycle includes "${candidate}".`);
|
|
36
|
+
error.code = 'ELOOP';
|
|
37
|
+
error.path = candidate;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
const link = fs.readlinkSync(candidate);
|
|
41
|
+
const linkTarget = path.isAbsolute(link) ? link : path.resolve(path.dirname(candidate), link);
|
|
42
|
+
return canonicalize(path.join(linkTarget, ...segments.slice(index + 1)), depth + 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
current = fs.realpathSync.native(candidate);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return path.normalize(current);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function canonicalFuturePath(targetPath) {
|
|
52
|
+
try {
|
|
53
|
+
return canonicalize(targetPath, 0);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error?.code !== 'ELOOP') throw error;
|
|
56
|
+
const output = path.resolve(targetPath);
|
|
57
|
+
throw new OutputPathError(`Output path contains a symbolic-link cycle: "${output}".`, {
|
|
58
|
+
code: 'output/symlink-cycle',
|
|
59
|
+
message: 'Output path could not be resolved because it contains a symbolic-link cycle.',
|
|
60
|
+
subject: { output },
|
|
61
|
+
evidence: {
|
|
62
|
+
systemCode: 'ELOOP',
|
|
63
|
+
...(error.path ? { cycleAt: path.resolve(error.path) } : {}),
|
|
64
|
+
},
|
|
65
|
+
supportedFixes: ['remove the symbolic-link cycle or choose an output path outside it'],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hasFileIdentity(stat) {
|
|
71
|
+
return stat.ino !== 0 && stat.ino !== 0n;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sameFileIdentity(left, right) {
|
|
75
|
+
return hasFileIdentity(left)
|
|
76
|
+
&& hasFileIdentity(right)
|
|
77
|
+
&& left.dev === right.dev
|
|
78
|
+
&& left.ino === right.ino;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function nearestExistingDirectory(targetPath) {
|
|
82
|
+
let directory = path.dirname(targetPath);
|
|
83
|
+
while (true) {
|
|
84
|
+
try {
|
|
85
|
+
const stat = fs.statSync(directory);
|
|
86
|
+
if (stat.isDirectory()) {
|
|
87
|
+
return {
|
|
88
|
+
path: fs.realpathSync.native(directory),
|
|
89
|
+
stat,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR') return null;
|
|
94
|
+
}
|
|
95
|
+
const parent = path.dirname(directory);
|
|
96
|
+
if (parent === directory) return null;
|
|
97
|
+
directory = parent;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function directoryIdentityKey(directory) {
|
|
102
|
+
if (!hasFileIdentity(directory.stat)) return null;
|
|
103
|
+
return `${directory.stat.dev}:${directory.stat.ino}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function probeNamesAlias(directoryPath, authoredName, lookupName) {
|
|
107
|
+
let fileDescriptor;
|
|
108
|
+
let created = false;
|
|
109
|
+
let result = null;
|
|
110
|
+
let cleaned = true;
|
|
111
|
+
const authoredPath = path.join(directoryPath, authoredName);
|
|
112
|
+
const lookupPath = path.join(directoryPath, lookupName);
|
|
113
|
+
try {
|
|
114
|
+
fileDescriptor = fs.openSync(authoredPath, 'wx', 0o600);
|
|
115
|
+
created = true;
|
|
116
|
+
fs.closeSync(fileDescriptor);
|
|
117
|
+
fileDescriptor = undefined;
|
|
118
|
+
|
|
119
|
+
let authored;
|
|
120
|
+
let lookup;
|
|
121
|
+
try {
|
|
122
|
+
authored = fs.statSync(authoredPath);
|
|
123
|
+
lookup = fs.statSync(lookupPath);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error.code === 'ENOENT') result = false;
|
|
126
|
+
}
|
|
127
|
+
if (authored && lookup) {
|
|
128
|
+
if (sameFileIdentity(authored, lookup)) {
|
|
129
|
+
result = true;
|
|
130
|
+
} else {
|
|
131
|
+
try {
|
|
132
|
+
result = fs.realpathSync.native(authoredPath) === fs.realpathSync.native(lookupPath);
|
|
133
|
+
} catch {
|
|
134
|
+
result = null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
result = null;
|
|
140
|
+
} finally {
|
|
141
|
+
if (fileDescriptor !== undefined) {
|
|
142
|
+
try {
|
|
143
|
+
fs.closeSync(fileDescriptor);
|
|
144
|
+
} catch {
|
|
145
|
+
cleaned = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (created) {
|
|
149
|
+
try {
|
|
150
|
+
fs.unlinkSync(authoredPath);
|
|
151
|
+
} catch {
|
|
152
|
+
cleaned = false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return cleaned ? result : null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function probeDirectorySemantics(directory) {
|
|
160
|
+
const cacheKey = directoryIdentityKey(directory);
|
|
161
|
+
if (cacheKey && directorySemanticsCache.has(cacheKey)) {
|
|
162
|
+
return directorySemanticsCache.get(cacheKey);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
semanticsProbeSequence += 1;
|
|
166
|
+
const suffix = `${process.pid}-${Date.now().toString(36)}-${semanticsProbeSequence}`;
|
|
167
|
+
const caseAuthored = `.archify-Case-Probe-${suffix}`;
|
|
168
|
+
const normalizationAuthored = `.archify-norm-\u00e9-probe-${suffix}`;
|
|
169
|
+
const semantics = {
|
|
170
|
+
caseInsensitive: probeNamesAlias(
|
|
171
|
+
directory.path,
|
|
172
|
+
caseAuthored,
|
|
173
|
+
caseAuthored.toLowerCase(),
|
|
174
|
+
),
|
|
175
|
+
normalizationInsensitive: probeNamesAlias(
|
|
176
|
+
directory.path,
|
|
177
|
+
normalizationAuthored,
|
|
178
|
+
normalizationAuthored.normalize('NFD'),
|
|
179
|
+
),
|
|
180
|
+
};
|
|
181
|
+
if (
|
|
182
|
+
cacheKey
|
|
183
|
+
&& semantics.caseInsensitive !== null
|
|
184
|
+
&& semantics.normalizationInsensitive !== null
|
|
185
|
+
) {
|
|
186
|
+
directorySemanticsCache.set(cacheKey, semantics);
|
|
187
|
+
}
|
|
188
|
+
return semantics;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function sameDirectory(left, right) {
|
|
192
|
+
return left.path === right.path || sameFileIdentity(left.stat, right.stat);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function futurePathsAlias(leftPath, rightPath) {
|
|
196
|
+
const left = canonicalFuturePath(leftPath);
|
|
197
|
+
const right = canonicalFuturePath(rightPath);
|
|
198
|
+
if (left === right) return true;
|
|
199
|
+
|
|
200
|
+
const leftDirectory = nearestExistingDirectory(left);
|
|
201
|
+
const rightDirectory = nearestExistingDirectory(right);
|
|
202
|
+
if (!leftDirectory || !rightDirectory || !sameDirectory(leftDirectory, rightDirectory)) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const semantics = probeDirectorySemantics(leftDirectory);
|
|
207
|
+
let comparableLeft = path.relative(leftDirectory.path, left);
|
|
208
|
+
let comparableRight = path.relative(rightDirectory.path, right);
|
|
209
|
+
if (semantics.normalizationInsensitive !== false) {
|
|
210
|
+
comparableLeft = comparableLeft.normalize('NFC');
|
|
211
|
+
comparableRight = comparableRight.normalize('NFC');
|
|
212
|
+
}
|
|
213
|
+
if (semantics.caseInsensitive !== false) {
|
|
214
|
+
comparableLeft = comparableLeft.toLowerCase();
|
|
215
|
+
comparableRight = comparableRight.toLowerCase();
|
|
216
|
+
}
|
|
217
|
+
return comparableLeft === comparableRight;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function pathsAlias(leftPath, rightPath) {
|
|
221
|
+
if (futurePathsAlias(leftPath, rightPath)) return true;
|
|
222
|
+
try {
|
|
223
|
+
const left = fs.statSync(leftPath);
|
|
224
|
+
const right = fs.statSync(rightPath);
|
|
225
|
+
return sameFileIdentity(left, right);
|
|
226
|
+
} catch {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function pathIsInside(directoryPath, targetPath) {
|
|
232
|
+
const relative = path.relative(canonicalFuturePath(directoryPath), canonicalFuturePath(targetPath));
|
|
233
|
+
return relative === '' || (!path.isAbsolute(relative) && relative !== '..' && !relative.startsWith(`..${path.sep}`));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export class OutputPathError extends Error {
|
|
237
|
+
constructor(message, diagnostic) {
|
|
238
|
+
super(message);
|
|
239
|
+
this.name = 'OutputPathError';
|
|
240
|
+
this.archifyDiagnostics = [{
|
|
241
|
+
severity: 'error',
|
|
242
|
+
subject: {},
|
|
243
|
+
evidence: {},
|
|
244
|
+
supportedFixes: [],
|
|
245
|
+
...diagnostic,
|
|
246
|
+
}];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function resolveOutputPath({
|
|
251
|
+
requestedOutput,
|
|
252
|
+
authoredOutput,
|
|
253
|
+
defaultOutput,
|
|
254
|
+
inputPaths = [],
|
|
255
|
+
inputDescription = 'an input',
|
|
256
|
+
otherOutputPaths = [],
|
|
257
|
+
cwd = process.cwd(),
|
|
258
|
+
requiredExtension = '.html',
|
|
259
|
+
}) {
|
|
260
|
+
const rawOutput = requestedOutput || authoredOutput || defaultOutput;
|
|
261
|
+
const source = requestedOutput ? 'cli' : (authoredOutput ? 'meta' : 'default');
|
|
262
|
+
if (
|
|
263
|
+
source === 'meta'
|
|
264
|
+
&& (path.isAbsolute(rawOutput) || path.posix.isAbsolute(rawOutput) || path.win32.isAbsolute(rawOutput))
|
|
265
|
+
) {
|
|
266
|
+
throw new OutputPathError('meta.output must be a relative path.', {
|
|
267
|
+
code: 'output/meta-absolute',
|
|
268
|
+
message: 'meta.output must be a relative path resolved from the current working directory.',
|
|
269
|
+
subject: { output: rawOutput },
|
|
270
|
+
supportedFixes: ['set meta.output to a relative .html path inside the current working directory'],
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (source === 'meta' && path.extname(rawOutput).toLowerCase() !== '.html') {
|
|
274
|
+
throw new OutputPathError('meta.output must target an .html file.', {
|
|
275
|
+
code: 'output/meta-extension',
|
|
276
|
+
message: 'meta.output must target an .html file.',
|
|
277
|
+
subject: { output: rawOutput },
|
|
278
|
+
supportedFixes: ['change meta.output to a path ending in .html'],
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
const outputPath = path.resolve(cwd, rawOutput);
|
|
282
|
+
if (source === 'meta' && path.extname(canonicalFuturePath(outputPath)).toLowerCase() !== '.html') {
|
|
283
|
+
throw new OutputPathError('meta.output must resolve to an .html file.', {
|
|
284
|
+
code: 'output/meta-resolved-extension',
|
|
285
|
+
message: 'meta.output must resolve to an .html file after symbolic links are followed.',
|
|
286
|
+
subject: { output: rawOutput },
|
|
287
|
+
supportedFixes: ['remove the symbolic-link alias or point it to an .html target inside the current working directory'],
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
if (source === 'meta' && !pathIsInside(cwd, outputPath)) {
|
|
291
|
+
throw new OutputPathError('meta.output must stay inside the current working directory.', {
|
|
292
|
+
code: 'output/meta-outside-cwd',
|
|
293
|
+
message: 'meta.output must stay inside the current working directory after symbolic links are resolved.',
|
|
294
|
+
subject: { output: rawOutput, cwd: path.resolve(cwd) },
|
|
295
|
+
supportedFixes: ['set meta.output to a relative .html path inside the current working directory'],
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
for (const inputPath of inputPaths) {
|
|
300
|
+
if (!pathsAlias(outputPath, inputPath)) continue;
|
|
301
|
+
throw new OutputPathError(`Output must not replace ${inputDescription}.`, {
|
|
302
|
+
code: 'output/input-alias',
|
|
303
|
+
message: `Output must not replace ${inputDescription}, including through a symbolic-link or future-path alias.`,
|
|
304
|
+
subject: { output: outputPath, input: path.resolve(inputPath) },
|
|
305
|
+
supportedFixes: ['choose an output path that is distinct from every input path'],
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
for (const otherOutputPath of otherOutputPaths) {
|
|
309
|
+
if (!pathsAlias(outputPath, otherOutputPath)) continue;
|
|
310
|
+
throw new OutputPathError('Output targets must use distinct paths.', {
|
|
311
|
+
code: 'output/target-alias',
|
|
312
|
+
message: 'Output targets must use distinct paths, including symbolic-link and future-path aliases.',
|
|
313
|
+
subject: { output: outputPath, conflictingOutput: path.resolve(otherOutputPath) },
|
|
314
|
+
supportedFixes: ['choose distinct paths for every generated output'],
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Keep explicit CLI directories unrestricted, but reject mistaken file types.
|
|
319
|
+
// Alias checks above retain priority when a target would overwrite an input.
|
|
320
|
+
if (source === 'cli') {
|
|
321
|
+
const resolvedOutput = canonicalFuturePath(outputPath);
|
|
322
|
+
const authoredMatches = path.extname(rawOutput).toLowerCase() === requiredExtension;
|
|
323
|
+
const resolvedMatches = path.extname(resolvedOutput).toLowerCase() === requiredExtension;
|
|
324
|
+
if (!authoredMatches || !resolvedMatches) {
|
|
325
|
+
const message = `CLI output must ${authoredMatches ? 'resolve to' : 'target'} a ${requiredExtension} file.`;
|
|
326
|
+
throw new OutputPathError(message, {
|
|
327
|
+
code: authoredMatches ? 'output/cli-resolved-extension' : 'output/cli-extension',
|
|
328
|
+
message,
|
|
329
|
+
subject: { output: rawOutput },
|
|
330
|
+
evidence: { resolvedOutput, requiredExtension },
|
|
331
|
+
supportedFixes: [`choose a path ending in ${requiredExtension} whose symbolic-link target also ends in ${requiredExtension}`],
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
outputPath,
|
|
338
|
+
source,
|
|
339
|
+
};
|
|
340
|
+
}
|