dsh-hyperframes 0.3.2 → 0.4.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/README.en.md +5 -4
- package/README.md +6 -5
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -0
- package/package.json +2 -2
- package/skills/embedded-captions/SKILL.md +28 -3
- package/skills/embedded-captions/references/aesthetic-principles.md +2 -3
- package/skills/embedded-captions/references/rail.md +1 -1
- package/skills/embedded-captions/scripts/check-occlusion.cjs +4 -1
- package/skills/embedded-captions/scripts/check-overflow.cjs +1 -0
- package/skills/embedded-captions/scripts/check-rail-climax.cjs +5 -1
- package/skills/embedded-captions/scripts/fixtures/heroless/theme.json +11 -0
- package/skills/embedded-captions/scripts/hf-cli.cjs +33 -0
- package/skills/embedded-captions/scripts/make-theme.cjs +95 -57
- package/skills/embedded-captions/scripts/make-theme.test.mjs +131 -0
- package/skills/embedded-captions/scripts/matte.cjs +1 -13
- package/skills/embedded-captions/scripts/measure-layout.cjs +2 -1
- package/skills/embedded-captions/scripts/preview-frames.cjs +39 -11
- package/skills/embedded-captions/scripts/preview-frames.test.mjs +24 -0
- package/skills/embedded-captions/scripts/render-and-composite.sh +8 -52
- package/skills/embedded-captions/scripts/safe-zones.cjs +4 -1
- package/skills/embedded-captions/scripts/transcribe.cjs +5 -14
- package/skills/embedded-captions/scripts/transcribe.test.mjs +41 -0
- package/skills/faceless-explainer/SKILL.md +20 -16
- package/skills/faceless-explainer/references/story-design.md +1 -1
- package/skills/faceless-explainer/scripts/assemble-index.mjs +6 -4
- package/skills/faceless-explainer/scripts/assemble-index.test.mjs +57 -2
- package/skills/faceless-explainer/scripts/audio.mjs +2 -0
- package/skills/faceless-explainer/scripts/build-frame.mjs +6 -3
- package/skills/faceless-explainer/scripts/captions.mjs +7 -4
- package/skills/faceless-explainer/scripts/frame-packets.mjs +4 -4
- package/skills/faceless-explainer/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/faceless-explainer/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/{hyperframes-core → faceless-explainer}/scripts/lib/frame-packets-core.mjs +5 -23
- package/skills/faceless-explainer/scripts/lib/storyboard.mjs +1 -1
- package/skills/faceless-explainer/scripts/lib/transitions.json +3 -3
- package/skills/faceless-explainer/scripts/transitions.mjs +1 -0
- package/skills/faceless-explainer/scripts/transitions.test.mjs +2 -0
- package/skills/faceless-explainer/sub-agents/frame-worker.md +1 -1
- package/skills/figma/SKILL.md +2 -0
- package/skills/general-video/SKILL.md +13 -11
- package/skills/general-video/scripts/frame-packets.mjs +4 -4
- package/skills/general-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/general-video/sub-agents/frame-worker.md +1 -1
- package/skills/hyperframes/SKILL.md +40 -30
- package/skills/{hyperframes-core → hyperframes}/references/brief-contract.md +15 -15
- package/skills/{hyperframes-core → hyperframes}/references/brief-format.md +8 -8
- package/skills/hyperframes/references/capability-menu.md +2 -2
- package/skills/{hyperframes-core → hyperframes}/references/frame-worker-core.md +1 -1
- package/skills/hyperframes/references/intent-interview.md +16 -16
- package/skills/hyperframes/references/plugin-installation.md +43 -0
- package/skills/{hyperframes-core → hyperframes}/references/production-loop.md +11 -11
- package/skills/hyperframes/references/review-loop.md +37 -0
- package/skills/hyperframes/references/route-briefs.md +1 -1
- package/skills/hyperframes/references/routes/motion-graphics.md +1 -1
- package/skills/{hyperframes-core → hyperframes}/references/script-format.md +3 -3
- package/skills/hyperframes/references/skill-lifecycle.md +2 -0
- package/skills/{hyperframes-core → hyperframes}/references/storyboard-format.md +3 -34
- package/skills/hyperframes/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
- package/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
- package/skills/hyperframes-animation/SKILL.md +5 -1
- package/skills/hyperframes-animation/adapters/gsap.md +1 -1
- package/skills/hyperframes-animation/adapters/lottie.md +15 -2
- package/skills/hyperframes-animation/adapters/three.md +14 -0
- package/skills/hyperframes-animation/blueprints/titlecard-reveal.md +1 -1
- package/skills/hyperframes-animation/references/motion-blur.md +133 -0
- package/skills/hyperframes-animation/rules/cursor-click-ripple.md +1 -1
- package/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +1 -1
- package/skills/hyperframes-animation/rules/hacker-flip-3d.md +1 -1
- package/skills/hyperframes-animation/rules/kinetic-beat-slam.md +2 -2
- package/skills/hyperframes-animation/rules/motion-blur-streak.md +1 -1
- package/skills/hyperframes-animation/rules/vertical-spring-ticker.md +1 -1
- package/skills/hyperframes-animation/scripts/package-loader.mjs +47 -16
- package/skills/hyperframes-animation/scripts/package-loader.test.mjs +52 -0
- package/skills/hyperframes-animation/techniques.md +1 -1
- package/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +3 -3
- package/skills/hyperframes-animation/transitions/css-push.md +4 -4
- package/skills/hyperframes-audio/SKILL.md +30 -22
- package/skills/hyperframes-audio/scripts/carve.mjs +6 -3
- package/skills/hyperframes-audio/scripts/carve.test.mjs +16 -1
- package/skills/hyperframes-cli/SKILL.md +44 -35
- package/skills/hyperframes-cli/references/init-and-scaffold.md +8 -6
- package/skills/hyperframes-cli/references/lint-validate-inspect.md +3 -3
- package/skills/hyperframes-cli/references/preview-render.md +35 -32
- package/skills/hyperframes-cli/references/upgrade-info-misc.md +50 -1
- package/skills/hyperframes-core/SKILL.md +30 -24
- package/skills/hyperframes-core/references/composition-patterns.md +3 -42
- package/skills/hyperframes-core/references/creator-editing-recipes.md +131 -7
- package/skills/hyperframes-core/references/data-attributes.md +11 -11
- package/skills/hyperframes-core/references/determinism-rules.md +2 -19
- package/skills/hyperframes-core/references/full-screen-motion.md +0 -9
- package/skills/hyperframes-core/references/minimal-composition.md +2 -2
- package/skills/hyperframes-core/references/sub-compositions.md +3 -66
- package/skills/hyperframes-core/references/tailwind.md +1 -20
- package/skills/hyperframes-core/references/tracks-and-clips.md +2 -6
- package/skills/hyperframes-core/references/variables-and-media.md +3 -3
- package/skills/hyperframes-creative/SKILL.md +2 -0
- package/skills/hyperframes-creative/references/house-style.md +15 -15
- package/skills/hyperframes-creative/references/story-spine.md +3 -1
- package/skills/hyperframes-creative/references/storyboard-recipe.md +57 -0
- package/skills/hyperframes-creative/references/typography.md +1 -1
- package/skills/hyperframes-creative/scripts/package-loader.mjs +47 -16
- package/skills/hyperframes-creative/scripts/package-loader.test.mjs +52 -0
- package/skills/hyperframes-keyframes/SKILL.md +4 -2
- package/skills/hyperframes-registry/SKILL.md +3 -1
- package/skills/hyperframes-registry/references/contributing.md +8 -2
- package/skills/hyperframes-registry/references/discovery.md +20 -34
- package/skills/hyperframes-studio/SKILL.md +77 -0
- package/skills/media-use/SKILL.md +14 -12
- package/skills/media-use/audio/references/captions/authoring.md +4 -0
- package/skills/media-use/audio/references/remove-background.md +1 -1
- package/skills/media-use/audio/references/tts-to-captions.md +11 -2
- package/skills/media-use/audio/references/tts.md +84 -6
- package/skills/media-use/audio/scripts/audio.mjs +15 -6
- package/skills/media-use/audio/scripts/gemini-pipeline.test.mjs +113 -0
- package/skills/media-use/audio/scripts/lib/audio-meta.mjs +39 -0
- package/skills/media-use/audio/scripts/lib/audio-meta.test.mjs +136 -0
- package/skills/media-use/audio/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/media-use/audio/scripts/lib/bgm.mjs +2 -6
- package/skills/media-use/audio/scripts/lib/gemini-auth.mjs +43 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth.py +52 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth.test.mjs +86 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth_test.py +90 -0
- package/skills/media-use/audio/scripts/lib/gemini-tts.mjs +122 -0
- package/skills/media-use/audio/scripts/lib/gemini-tts.test.mjs +243 -0
- package/skills/media-use/audio/scripts/lib/heygen.mjs +2 -1
- package/skills/media-use/audio/scripts/lib/tts.mjs +15 -3
- package/skills/media-use/audio/scripts/wait-bgm.mjs +8 -4
- package/skills/media-use/audio/scripts/wait-bgm.test.mjs +65 -0
- package/skills/media-use/references/audio.md +3 -2
- package/skills/media-use/references/grading.md +7 -7
- package/skills/media-use/references/media-treatment-recipes.md +1 -1
- package/skills/media-use/references/memory.md +2 -2
- package/skills/media-use/references/meta.md +2 -2
- package/skills/media-use/references/operations.md +15 -13
- package/skills/media-use/references/resolve.md +22 -11
- package/skills/media-use/references/setup-providers.md +11 -11
- package/skills/media-use/scripts/audio-duck.mjs +19 -27
- package/skills/media-use/scripts/compatibility.test.mjs +96 -0
- package/skills/media-use/scripts/lib/config-lock.mjs +45 -0
- package/skills/media-use/scripts/lib/duck.mjs +16 -10
- package/skills/media-use/scripts/lib/media-fetch.mjs +85 -0
- package/skills/media-use/scripts/lib/media-home.mjs +20 -0
- package/skills/media-use/scripts/lib/npx-sync.mjs +28 -2
- package/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
- package/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
- package/skills/media-use/scripts/lib/telemetry.mjs +29 -41
- package/skills/media-use/scripts/lib/transcriptCutFade.mjs +21 -0
- package/skills/media-use/scripts/prefs.mjs +1 -1
- package/skills/media-use/scripts/recipe.mjs +1 -1
- package/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
- package/skills/media-use/scripts/resolve.mjs +27 -1267
- package/skills/media-use/scripts/transcribe.mjs +39 -25
- package/skills/media-use/scripts/transcribe.test.mjs +112 -0
- package/skills/media-use/scripts/transcript-cut.mjs +39 -8
- package/skills/media-use/scripts/transcript-cut.test.mjs +117 -0
- package/skills/motion-graphics/SKILL.md +3 -1
- package/skills/motion-graphics/agents/builder.md +1 -0
- package/skills/motion-graphics/agents/director.md +2 -1
- package/skills/motion-graphics/catalog-map.md +25 -13
- package/skills/motion-graphics/categories/charts/module.md +2 -1
- package/skills/motion-graphics/categories/lower-thirds/module.md +4 -0
- package/skills/motion-graphics/categories/news/module.md +1 -1
- package/skills/motion-graphics/references/motion-vocabulary.md +4 -0
- package/skills/music-to-video/SKILL.md +8 -6
- package/skills/music-to-video/references/montage.md +3 -6
- package/skills/music-to-video/references/motion-primitives/3d-card-flip/index.html +10 -111
- package/skills/music-to-video/references/motion-primitives/3d-card-flip/scene.html +119 -0
- package/skills/music-to-video/references/motion-primitives/bg-flow-field/index.html +11 -284
- package/skills/music-to-video/references/motion-primitives/bg-flow-field/scene.html +294 -0
- package/skills/music-to-video/references/motion-primitives/binary-decrypt/index.html +10 -76
- package/skills/music-to-video/references/motion-primitives/binary-decrypt/scene.html +85 -0
- package/skills/music-to-video/references/motion-primitives/blur-resolve/index.html +10 -52
- package/skills/music-to-video/references/motion-primitives/blur-resolve/scene.html +60 -0
- package/skills/music-to-video/references/motion-primitives/braam-punch/index.html +10 -92
- package/skills/music-to-video/references/motion-primitives/braam-punch/scene.html +100 -0
- package/skills/music-to-video/references/motion-primitives/chromatic-split/index.html +10 -103
- package/skills/music-to-video/references/motion-primitives/chromatic-split/scene.html +111 -0
- package/skills/music-to-video/references/motion-primitives/chrome-sweep/index.html +10 -60
- package/skills/music-to-video/references/motion-primitives/chrome-sweep/scene.html +68 -0
- package/skills/music-to-video/references/motion-primitives/counting-punch/index.html +10 -110
- package/skills/music-to-video/references/motion-primitives/counting-punch/scene.html +119 -0
- package/skills/music-to-video/references/motion-primitives/crash-zoom-in/index.html +10 -112
- package/skills/music-to-video/references/motion-primitives/crash-zoom-in/scene.html +120 -0
- package/skills/music-to-video/references/motion-primitives/datamosh-smear/index.html +10 -121
- package/skills/music-to-video/references/motion-primitives/datamosh-smear/scene.html +131 -0
- package/skills/music-to-video/references/motion-primitives/directional-fill/index.html +10 -90
- package/skills/music-to-video/references/motion-primitives/directional-fill/scene.html +103 -0
- package/skills/music-to-video/references/motion-primitives/dolly-zoom/index.html +10 -94
- package/skills/music-to-video/references/motion-primitives/dolly-zoom/scene.html +102 -0
- package/skills/music-to-video/references/motion-primitives/electric-arc/index.html +10 -100
- package/skills/music-to-video/references/motion-primitives/electric-arc/scene.html +105 -0
- package/skills/music-to-video/references/motion-primitives/flash-cut/index.html +10 -89
- package/skills/music-to-video/references/motion-primitives/flash-cut/scene.html +97 -0
- package/skills/music-to-video/references/motion-primitives/gooey-metaball/index.html +10 -197
- package/skills/music-to-video/references/motion-primitives/gooey-metaball/scene.html +206 -0
- package/skills/music-to-video/references/motion-primitives/hard-cut/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/hard-cut/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/hypercut-whip/index.html +10 -36
- package/skills/music-to-video/references/motion-primitives/hypercut-whip/scene.html +44 -0
- package/skills/music-to-video/references/motion-primitives/iris-open/index.html +10 -61
- package/skills/music-to-video/references/motion-primitives/iris-open/scene.html +69 -0
- package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/index.html +10 -55
- package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/scene.html +64 -0
- package/skills/music-to-video/references/motion-primitives/liquid-morph/index.html +10 -88
- package/skills/music-to-video/references/motion-primitives/liquid-morph/scene.html +96 -0
- package/skills/music-to-video/references/motion-primitives/mask-reveal/index.html +10 -66
- package/skills/music-to-video/references/motion-primitives/mask-reveal/scene.html +74 -0
- package/skills/music-to-video/references/motion-primitives/mosaic-pack/index.html +10 -81
- package/skills/music-to-video/references/motion-primitives/mosaic-pack/scene.html +89 -0
- package/skills/music-to-video/references/motion-primitives/neon-flicker/index.html +10 -65
- package/skills/music-to-video/references/motion-primitives/neon-flicker/scene.html +73 -0
- package/skills/music-to-video/references/motion-primitives/outline-to-fill/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/outline-to-fill/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/palette-flip/index.html +10 -89
- package/skills/music-to-video/references/motion-primitives/palette-flip/scene.html +97 -0
- package/skills/music-to-video/references/motion-primitives/particle-burst/index.html +10 -98
- package/skills/music-to-video/references/motion-primitives/particle-burst/scene.html +106 -0
- package/skills/music-to-video/references/motion-primitives/pixel-dissolve/index.html +10 -78
- package/skills/music-to-video/references/motion-primitives/pixel-dissolve/scene.html +86 -0
- package/skills/music-to-video/references/motion-primitives/radial-burst-lines/index.html +10 -117
- package/skills/music-to-video/references/motion-primitives/radial-burst-lines/scene.html +130 -0
- package/skills/music-to-video/references/motion-primitives/screen-shake/index.html +10 -70
- package/skills/music-to-video/references/motion-primitives/screen-shake/scene.html +78 -0
- package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/index.html +10 -87
- package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/scene.html +100 -0
- package/skills/music-to-video/references/motion-primitives/spotlight-sweep/index.html +10 -94
- package/skills/music-to-video/references/motion-primitives/spotlight-sweep/scene.html +102 -0
- package/skills/music-to-video/references/motion-primitives/staggered-exit/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/staggered-exit/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/text-spectral-rays/index.html +11 -271
- package/skills/music-to-video/references/motion-primitives/text-spectral-rays/scene.html +280 -0
- package/skills/music-to-video/references/motion-primitives/text-wave-distort/index.html +10 -92
- package/skills/music-to-video/references/motion-primitives/text-wave-distort/scene.html +106 -0
- package/skills/music-to-video/references/motion-primitives/tile-mosaic/index.html +10 -101
- package/skills/music-to-video/references/motion-primitives/tile-mosaic/scene.html +109 -0
- package/skills/music-to-video/references/motion-primitives/typewriter-reveal/index.html +10 -79
- package/skills/music-to-video/references/motion-primitives/typewriter-reveal/scene.html +91 -0
- package/skills/music-to-video/references/motion-primitives/word-grid-burst/index.html +10 -83
- package/skills/music-to-video/references/motion-primitives/word-grid-burst/scene.html +96 -0
- package/skills/music-to-video/references/planning.md +2 -2
- package/skills/music-to-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/music-to-video/sub-agents/frame-worker.md +2 -2
- package/skills/pr-to-video/SKILL.md +20 -16
- package/skills/pr-to-video/references/code-vocabulary.md +14 -12
- package/skills/pr-to-video/references/story-design.md +1 -1
- package/skills/pr-to-video/scripts/assemble-index.mjs +6 -4
- package/skills/pr-to-video/scripts/assemble-index.test.mjs +57 -2
- package/skills/pr-to-video/scripts/audio.mjs +2 -0
- package/skills/pr-to-video/scripts/build-frame.mjs +6 -3
- package/skills/pr-to-video/scripts/captions.mjs +7 -4
- package/skills/pr-to-video/scripts/captions.test.mjs +41 -0
- package/skills/pr-to-video/scripts/frame-packets.mjs +4 -4
- package/skills/pr-to-video/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/pr-to-video/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/pr-to-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/pr-to-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/pr-to-video/scripts/lib/transitions.json +3 -3
- package/skills/pr-to-video/scripts/transitions.mjs +7 -5
- package/skills/pr-to-video/sub-agents/frame-worker.md +1 -1
- package/skills/product-launch-video/SKILL.md +20 -16
- package/skills/product-launch-video/references/story-design.md +1 -1
- package/skills/product-launch-video/scripts/assemble-index.mjs +6 -4
- package/skills/product-launch-video/scripts/assemble-index.test.mjs +57 -2
- package/skills/product-launch-video/scripts/audio.mjs +2 -0
- package/skills/product-launch-video/scripts/audio.test.mjs +16 -0
- package/skills/product-launch-video/scripts/build-frame-subsets.test.mjs +152 -0
- package/skills/product-launch-video/scripts/build-frame.mjs +6 -3
- package/skills/product-launch-video/scripts/captions.mjs +7 -4
- package/skills/product-launch-video/scripts/frame-packets.mjs +4 -4
- package/skills/product-launch-video/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/product-launch-video/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/product-launch-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/product-launch-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/product-launch-video/scripts/lib/transitions.json +3 -3
- package/skills/product-launch-video/scripts/transitions.mjs +7 -5
- package/skills/product-launch-video/scripts/transitions.test.mjs +68 -0
- package/skills/product-launch-video/sub-agents/frame-worker.md +1 -1
- package/skills/remotion-to-hyperframes/SKILL.md +7 -4
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +5 -5
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/compositions/title.html +31 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +10 -25
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +8 -8
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +9 -9
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html +35 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html +35 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html +36 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +28 -71
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +3 -3
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html +52 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html +99 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html +55 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +13 -212
- package/skills/remotion-to-hyperframes/references/api-map.md +16 -16
- package/skills/remotion-to-hyperframes/references/limitations.md +7 -7
- package/skills/remotion-to-hyperframes/references/lottie.md +5 -7
- package/skills/remotion-to-hyperframes/references/media.md +3 -3
- package/skills/remotion-to-hyperframes/references/parameters.md +5 -0
- package/skills/remotion-to-hyperframes/references/sequencing.md +71 -18
- package/skills/slideshow/SKILL.md +4 -1
- package/skills/slideshow/references/standalone-harness.md +0 -3
- package/skills/talking-head-recut/SKILL.md +10 -1
- package/skills/hyperframes-core/references/review-loop.md +0 -41
- package/skills/media-use/scripts/lib/adopt.mjs +0 -128
- package/skills/media-use/scripts/lib/adopt.test.mjs +0 -95
- package/skills/media-use/scripts/lib/bgm-provider.mjs +0 -20
- package/skills/media-use/scripts/lib/brand-provider.mjs +0 -59
- package/skills/media-use/scripts/lib/bundled-sfx-provider.mjs +0 -108
- package/skills/media-use/scripts/lib/bundled-sfx-provider.test.mjs +0 -85
- package/skills/media-use/scripts/lib/cache.mjs +0 -150
- package/skills/media-use/scripts/lib/candidates.mjs +0 -91
- package/skills/media-use/scripts/lib/candidates.test.mjs +0 -155
- package/skills/media-use/scripts/lib/codex-provider.mjs +0 -161
- package/skills/media-use/scripts/lib/codex-provider.test.mjs +0 -34
- package/skills/media-use/scripts/lib/coverage.test.mjs +0 -117
- package/skills/media-use/scripts/lib/cube-build.mjs +0 -181
- package/skills/media-use/scripts/lib/cube-build.test.mjs +0 -80
- package/skills/media-use/scripts/lib/cube-validate.mjs +0 -198
- package/skills/media-use/scripts/lib/cube-validate.test.mjs +0 -125
- package/skills/media-use/scripts/lib/cutlist.test.mjs +0 -148
- package/skills/media-use/scripts/lib/duck.test.mjs +0 -118
- package/skills/media-use/scripts/lib/error-diffusion.test.mjs +0 -102
- package/skills/media-use/scripts/lib/freeze.mjs +0 -70
- package/skills/media-use/scripts/lib/freeze.test.mjs +0 -46
- package/skills/media-use/scripts/lib/grade-analyzer.mjs +0 -264
- package/skills/media-use/scripts/lib/grade-analyzer.test.mjs +0 -285
- package/skills/media-use/scripts/lib/heygen-cli.mjs +0 -191
- package/skills/media-use/scripts/lib/heygen-cli.test.mjs +0 -301
- package/skills/media-use/scripts/lib/heygen-search.mjs +0 -45
- package/skills/media-use/scripts/lib/heygen-search.test.mjs +0 -49
- package/skills/media-use/scripts/lib/heygen-video-provider.mjs +0 -127
- package/skills/media-use/scripts/lib/heygen-video-provider.test.mjs +0 -344
- package/skills/media-use/scripts/lib/image-provider.mjs +0 -44
- package/skills/media-use/scripts/lib/local-models.mjs +0 -318
- package/skills/media-use/scripts/lib/local-models.test.mjs +0 -220
- package/skills/media-use/scripts/lib/local-run.mjs +0 -78
- package/skills/media-use/scripts/lib/local-run.test.mjs +0 -121
- package/skills/media-use/scripts/lib/logo-provider.mjs +0 -222
- package/skills/media-use/scripts/lib/logo-provider.test.mjs +0 -137
- package/skills/media-use/scripts/lib/ltx-video-provider.mjs +0 -94
- package/skills/media-use/scripts/lib/ltx-video-provider.test.mjs +0 -316
- package/skills/media-use/scripts/lib/lut-preset-provider.mjs +0 -266
- package/skills/media-use/scripts/lib/lut-preset-provider.test.mjs +0 -176
- package/skills/media-use/scripts/lib/manifest.test.mjs +0 -388
- package/skills/media-use/scripts/lib/match.mjs +0 -47
- package/skills/media-use/scripts/lib/mflux-provider.mjs +0 -128
- package/skills/media-use/scripts/lib/mflux-provider.test.mjs +0 -174
- package/skills/media-use/scripts/lib/misses.mjs +0 -49
- package/skills/media-use/scripts/lib/misses.test.mjs +0 -78
- package/skills/media-use/scripts/lib/npx-sync.test.mjs +0 -50
- package/skills/media-use/scripts/lib/parakeet-words.test.mjs +0 -44
- package/skills/media-use/scripts/lib/prefs-store.test.mjs +0 -189
- package/skills/media-use/scripts/lib/probe.mjs +0 -39
- package/skills/media-use/scripts/lib/probe.test.mjs +0 -31
- package/skills/media-use/scripts/lib/providers.mjs +0 -5
- package/skills/media-use/scripts/lib/recipe-store.test.mjs +0 -367
- package/skills/media-use/scripts/lib/registry.mjs +0 -218
- package/skills/media-use/scripts/lib/registry.test.mjs +0 -261
- package/skills/media-use/scripts/lib/search.mjs +0 -24
- package/skills/media-use/scripts/lib/search.test.mjs +0 -32
- package/skills/media-use/scripts/lib/sfx-provider.mjs +0 -23
- package/skills/media-use/scripts/lib/specs.mjs +0 -132
- package/skills/media-use/scripts/lib/specs.test.mjs +0 -157
- package/skills/media-use/scripts/lib/stats.mjs +0 -117
- package/skills/media-use/scripts/lib/stats.test.mjs +0 -165
- package/skills/media-use/scripts/lib/telemetry.test.mjs +0 -373
- package/skills/media-use/scripts/lib/tts-local-provider.mjs +0 -92
- package/skills/media-use/scripts/lib/tts-local-provider.test.mjs +0 -72
- package/skills/media-use/scripts/lib/usage.mjs +0 -57
- package/skills/media-use/scripts/lib/usage.test.mjs +0 -54
- package/skills/media-use/scripts/lib/voice-provider.mjs +0 -57
- package/skills/media-use/scripts/lib/voice-provider.test.mjs +0 -46
- package/skills/media-use/scripts/resolve.test.mjs +0 -1120
- /package/skills/{hyperframes-core → hyperframes}/references/subagent-dispatch.md +0 -0
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { readdirSync, statSync, existsSync } from "node:fs";
|
|
2
|
-
import { join, extname, basename } from "node:path";
|
|
3
|
-
import { readManifest, appendRecord, nextId } from "./manifest.mjs";
|
|
4
|
-
import { regenerateIndex } from "./index-gen.mjs";
|
|
5
|
-
import { probe } from "./probe.mjs";
|
|
6
|
-
import { matchTokens } from "./match.mjs";
|
|
7
|
-
|
|
8
|
-
const AUDIO_EXT = new Set([".mp3", ".wav", ".ogg", ".m4a", ".aac"]);
|
|
9
|
-
const IMAGE_EXT = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico"]);
|
|
10
|
-
const VIDEO_EXT = new Set([".mp4", ".webm", ".mov"]);
|
|
11
|
-
|
|
12
|
-
function inferType(filePath) {
|
|
13
|
-
const ext = extname(filePath).toLowerCase();
|
|
14
|
-
if (AUDIO_EXT.has(ext)) {
|
|
15
|
-
const lower = filePath.toLowerCase();
|
|
16
|
-
if (lower.includes("/bgm/") || lower.includes("/music/") || lower.startsWith("bgm/"))
|
|
17
|
-
return "bgm";
|
|
18
|
-
if (lower.includes("/sfx/") || lower.includes("/sound") || lower.startsWith("sfx/"))
|
|
19
|
-
return "sfx";
|
|
20
|
-
if (lower.includes("/voice/") || lower.includes("/narrat") || lower.startsWith("voice/"))
|
|
21
|
-
return "voice";
|
|
22
|
-
return "bgm";
|
|
23
|
-
}
|
|
24
|
-
if (IMAGE_EXT.has(ext)) {
|
|
25
|
-
if (ext === ".svg" || ext === ".ico") return "icon";
|
|
26
|
-
return "image";
|
|
27
|
-
}
|
|
28
|
-
if (VIDEO_EXT.has(ext)) return "video";
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function walkDir(dir, base = "") {
|
|
33
|
-
const files = [];
|
|
34
|
-
if (!existsSync(dir)) return files;
|
|
35
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
36
|
-
const rel = base ? `${base}/${entry.name}` : entry.name;
|
|
37
|
-
if (entry.isDirectory()) {
|
|
38
|
-
files.push(...walkDir(join(dir, entry.name), rel));
|
|
39
|
-
} else {
|
|
40
|
-
files.push(rel);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return files;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function scanExistingAssets(projectDir) {
|
|
47
|
-
const assetsDir = join(projectDir, "assets");
|
|
48
|
-
if (!existsSync(assetsDir)) return [];
|
|
49
|
-
|
|
50
|
-
const files = walkDir(assetsDir);
|
|
51
|
-
const found = [];
|
|
52
|
-
for (const rel of files) {
|
|
53
|
-
const type = inferType(rel);
|
|
54
|
-
if (!type) continue;
|
|
55
|
-
const fullPath = join(assetsDir, rel);
|
|
56
|
-
const stat = statSync(fullPath);
|
|
57
|
-
if (stat.size === 0) {
|
|
58
|
-
// A 0-byte asset would register clean but fail at render — skip it loudly
|
|
59
|
-
// rather than adopt a broken file.
|
|
60
|
-
console.error(`media-use: skipping 0-byte asset assets/${rel}`);
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
const meta = probe(fullPath);
|
|
64
|
-
found.push({
|
|
65
|
-
relativePath: `assets/${rel}`,
|
|
66
|
-
type,
|
|
67
|
-
size: stat.size,
|
|
68
|
-
name: basename(rel, extname(rel)),
|
|
69
|
-
...meta,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return found;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function adoptExistingAssets(projectDir) {
|
|
76
|
-
const existing = scanExistingAssets(projectDir);
|
|
77
|
-
if (existing.length === 0) return [];
|
|
78
|
-
|
|
79
|
-
const manifest = readManifest(projectDir);
|
|
80
|
-
const knownPaths = new Set(manifest.map((r) => r.path));
|
|
81
|
-
|
|
82
|
-
const adopted = [];
|
|
83
|
-
for (const asset of existing) {
|
|
84
|
-
if (knownPaths.has(asset.relativePath)) continue;
|
|
85
|
-
|
|
86
|
-
const id = nextId(projectDir, asset.type);
|
|
87
|
-
const record = {
|
|
88
|
-
id,
|
|
89
|
-
type: asset.type,
|
|
90
|
-
path: asset.relativePath,
|
|
91
|
-
source: "existing",
|
|
92
|
-
description: asset.name.replace(/[-_]/g, " "),
|
|
93
|
-
...(asset.duration != null && { duration: asset.duration }),
|
|
94
|
-
...(asset.width != null && { width: asset.width }),
|
|
95
|
-
...(asset.height != null && { height: asset.height }),
|
|
96
|
-
provenance: { provider: "local", adopted: true },
|
|
97
|
-
};
|
|
98
|
-
appendRecord(projectDir, record);
|
|
99
|
-
adopted.push(record);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (adopted.length > 0) regenerateIndex(projectDir);
|
|
103
|
-
return adopted;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Adopt a pre-existing assets/ file only when it shares a meaningful word with
|
|
107
|
-
// the intent. The old test — `name.includes(intent) || intent.includes(name)` —
|
|
108
|
-
// silently returned the WRONG file: "whoosh" grabbed a stray who.mp3, and a
|
|
109
|
-
// one-letter filename matched every intent. A false negative just falls through
|
|
110
|
-
// to a catalog search (safe); a false positive ships the wrong asset. So bias to
|
|
111
|
-
// precision: require a shared token, don't guess from substrings.
|
|
112
|
-
export function findExistingAsset(projectDir, intent, type) {
|
|
113
|
-
const assetsDir = join(projectDir, "assets");
|
|
114
|
-
if (!existsSync(assetsDir)) return null;
|
|
115
|
-
const intentTokens = matchTokens(intent);
|
|
116
|
-
if (intentTokens.size === 0) return null;
|
|
117
|
-
for (const rel of walkDir(assetsDir)) {
|
|
118
|
-
const t = inferType(rel);
|
|
119
|
-
if (!t || (type && t !== type)) continue;
|
|
120
|
-
const stem = basename(rel, extname(rel));
|
|
121
|
-
for (const tok of matchTokens(stem)) {
|
|
122
|
-
if (intentTokens.has(tok)) {
|
|
123
|
-
return { relativePath: `assets/${rel}`, type: t, name: stem };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from "node:assert";
|
|
2
|
-
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { findExistingAsset, adoptExistingAssets } from "./adopt.mjs";
|
|
6
|
-
|
|
7
|
-
let tmp;
|
|
8
|
-
function setup() {
|
|
9
|
-
tmp = mkdtempSync(join(tmpdir(), "mu-adopt-test-"));
|
|
10
|
-
}
|
|
11
|
-
function cleanup() {
|
|
12
|
-
if (tmp) rmSync(tmp, { recursive: true, force: true });
|
|
13
|
-
}
|
|
14
|
-
function drop(rel) {
|
|
15
|
-
const full = join(tmp, "assets", rel);
|
|
16
|
-
mkdirSync(join(full, ".."), { recursive: true });
|
|
17
|
-
writeFileSync(full, "x");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function runTests() {
|
|
21
|
-
const tests = [];
|
|
22
|
-
const test = (name, fn) => tests.push({ name, fn });
|
|
23
|
-
|
|
24
|
-
test("does NOT false-match a short filename stem (who.mp3 vs 'whoosh')", () => {
|
|
25
|
-
setup();
|
|
26
|
-
drop("sfx/who.mp3");
|
|
27
|
-
assert.equal(findExistingAsset(tmp, "whoosh", "sfx"), null);
|
|
28
|
-
cleanup();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("does NOT match a one-letter filename against any intent", () => {
|
|
32
|
-
setup();
|
|
33
|
-
drop("images/a.jpg");
|
|
34
|
-
assert.equal(findExistingAsset(tmp, "gradient tech background", "image"), null);
|
|
35
|
-
cleanup();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("matches on a shared meaningful word", () => {
|
|
39
|
-
setup();
|
|
40
|
-
drop("images/hero-shot.jpg");
|
|
41
|
-
const hit = findExistingAsset(tmp, "hero image", "image");
|
|
42
|
-
assert.ok(hit);
|
|
43
|
-
assert.equal(hit.relativePath, "assets/images/hero-shot.jpg");
|
|
44
|
-
cleanup();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("matches multi-word overlap", () => {
|
|
48
|
-
setup();
|
|
49
|
-
drop("images/gradient-tech-bg.jpg");
|
|
50
|
-
assert.ok(findExistingAsset(tmp, "gradient tech background", "image"));
|
|
51
|
-
cleanup();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("a shared stopword alone does not match", () => {
|
|
55
|
-
setup();
|
|
56
|
-
drop("video/the-video.mp4");
|
|
57
|
-
assert.equal(findExistingAsset(tmp, "the rocket", null), null);
|
|
58
|
-
cleanup();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("respects the type filter", () => {
|
|
62
|
-
setup();
|
|
63
|
-
drop("sfx/rocket.mp3");
|
|
64
|
-
assert.equal(findExistingAsset(tmp, "rocket", "image"), null, "wrong type is skipped");
|
|
65
|
-
assert.ok(findExistingAsset(tmp, "rocket", "sfx"), "right type matches");
|
|
66
|
-
cleanup();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("adoptExistingAssets still imports every typed file (unaffected by match rule)", () => {
|
|
70
|
-
setup();
|
|
71
|
-
drop("sfx/who.mp3");
|
|
72
|
-
drop("images/a.jpg");
|
|
73
|
-
assert.equal(adoptExistingAssets(tmp).length, 2);
|
|
74
|
-
cleanup();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
let passed = 0;
|
|
78
|
-
let failed = 0;
|
|
79
|
-
for (const { name, fn } of tests) {
|
|
80
|
-
try {
|
|
81
|
-
fn();
|
|
82
|
-
passed++;
|
|
83
|
-
console.log(` \x1b[32m✓\x1b[0m ${name}`);
|
|
84
|
-
} catch (err) {
|
|
85
|
-
failed++;
|
|
86
|
-
console.log(` \x1b[31m✗\x1b[0m ${name}`);
|
|
87
|
-
console.log(` ${err.message}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
console.log(`\n${passed} passed, ${failed} failed`);
|
|
91
|
-
if (failed > 0) process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
console.log("media-use · adopt / findExistingAsset tests\n");
|
|
95
|
-
runTests();
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { heygenSearch } from "./heygen-search.mjs";
|
|
2
|
-
|
|
3
|
-
export const bgmProvider = {
|
|
4
|
-
async search(intent) {
|
|
5
|
-
const results = heygenSearch("audio sounds list", intent, { type: "music" });
|
|
6
|
-
if (!results) return null;
|
|
7
|
-
const best = results[0];
|
|
8
|
-
return {
|
|
9
|
-
url: best.audio_url,
|
|
10
|
-
source: "search",
|
|
11
|
-
// ext derived from audio_url by resolve.mjs — catalog tracks are .mp3 or .wav
|
|
12
|
-
metadata: {
|
|
13
|
-
description: best.description || intent,
|
|
14
|
-
duration: best.duration || null,
|
|
15
|
-
provider: "heygen.audio.sounds",
|
|
16
|
-
provenance: { track_id: best.id, score: best.score, query: intent },
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
function findDesignSpec(projectDir) {
|
|
5
|
-
for (const name of ["frame.md", "design.md", "DESIGN.md"]) {
|
|
6
|
-
const p = join(projectDir, name);
|
|
7
|
-
if (existsSync(p)) return { path: p, name };
|
|
8
|
-
}
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function parseFrontmatter(content) {
|
|
13
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
14
|
-
if (!match) return null;
|
|
15
|
-
const yaml = match[1];
|
|
16
|
-
const tokens = {};
|
|
17
|
-
for (const line of yaml.split("\n")) {
|
|
18
|
-
const m = line.match(/^\s*(\w[\w-]*):\s*(.+)/);
|
|
19
|
-
if (m) tokens[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
|
|
20
|
-
}
|
|
21
|
-
return tokens;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function extractColors(tokens) {
|
|
25
|
-
const colors = [];
|
|
26
|
-
for (const [k, v] of Object.entries(tokens)) {
|
|
27
|
-
if (typeof v === "string" && /^#[0-9a-fA-F]{3,8}$/.test(v)) {
|
|
28
|
-
colors.push({ name: k, hex: v });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return colors;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const brandProvider = {
|
|
35
|
-
async search(intent, { projectDir } = {}) {
|
|
36
|
-
if (!projectDir) return null;
|
|
37
|
-
const spec = findDesignSpec(projectDir);
|
|
38
|
-
if (!spec) return null;
|
|
39
|
-
const content = readFileSync(spec.path, "utf8");
|
|
40
|
-
const tokens = parseFrontmatter(content);
|
|
41
|
-
if (!tokens) return null;
|
|
42
|
-
const colors = extractColors(tokens);
|
|
43
|
-
return {
|
|
44
|
-
localPath: spec.path,
|
|
45
|
-
source: "local",
|
|
46
|
-
ext: ".md",
|
|
47
|
-
metadata: {
|
|
48
|
-
description: "Brand tokens from " + spec.name,
|
|
49
|
-
provider: "design_spec",
|
|
50
|
-
provenance: {
|
|
51
|
-
file: spec.name,
|
|
52
|
-
colors,
|
|
53
|
-
font: tokens.font || tokens.typography || null,
|
|
54
|
-
logo: tokens.logo || null,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
|
-
};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { extname, join } from "node:path";
|
|
3
|
-
|
|
4
|
-
const LIB_DIR =
|
|
5
|
-
process.env.HYPERFRAMES_MEDIA_USE_SFX_DIR ||
|
|
6
|
-
join(import.meta.dirname, "..", "..", "audio", "assets", "sfx");
|
|
7
|
-
|
|
8
|
-
export const BUNDLED_SFX_RECOVERY_COMMAND = "npx hyperframes skills update media-use";
|
|
9
|
-
|
|
10
|
-
export class BundledSfxAssetsError extends Error {
|
|
11
|
-
constructor(health) {
|
|
12
|
-
super(
|
|
13
|
-
`bundled SFX assets are missing or incomplete (${health.detail}). Repair the installed media-use skill: ${health.fix}`,
|
|
14
|
-
);
|
|
15
|
-
this.name = "BundledSfxAssetsError";
|
|
16
|
-
this.code = health.code;
|
|
17
|
-
this.fix = health.fix;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function unhealthy(detail) {
|
|
22
|
-
return {
|
|
23
|
-
ok: false,
|
|
24
|
-
code: "bundled_sfx_assets_missing",
|
|
25
|
-
detail,
|
|
26
|
-
fix: BUNDLED_SFX_RECOVERY_COMMAND,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function inspectBundledSfxAssets(libraryDir = LIB_DIR) {
|
|
31
|
-
const manifestPath = join(libraryDir, "manifest.json");
|
|
32
|
-
if (!existsSync(manifestPath)) return unhealthy(`manifest not found: ${manifestPath}`);
|
|
33
|
-
|
|
34
|
-
let manifest;
|
|
35
|
-
try {
|
|
36
|
-
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
37
|
-
} catch {
|
|
38
|
-
return unhealthy(`manifest is not valid JSON: ${manifestPath}`);
|
|
39
|
-
}
|
|
40
|
-
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
41
|
-
return unhealthy(`manifest must contain an object: ${manifestPath}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const entries = Object.entries(manifest);
|
|
45
|
-
if (entries.length === 0) return unhealthy(`manifest contains no SFX entries: ${manifestPath}`);
|
|
46
|
-
for (const [key, entry] of entries) {
|
|
47
|
-
if (!entry?.file || typeof entry.file !== "string") {
|
|
48
|
-
return unhealthy(`manifest entry "${key}" has no file`);
|
|
49
|
-
}
|
|
50
|
-
const assetPath = join(libraryDir, entry.file);
|
|
51
|
-
if (!existsSync(assetPath)) return unhealthy(`asset not found: ${assetPath}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
ok: true,
|
|
56
|
-
count: entries.length,
|
|
57
|
-
detail: `${entries.length} bundled SFX asset${entries.length === 1 ? "" : "s"} available`,
|
|
58
|
-
fix: "",
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const normalize = (value) =>
|
|
63
|
-
String(value)
|
|
64
|
-
.toLowerCase()
|
|
65
|
-
.replace(/[^a-z0-9]+/g, " ")
|
|
66
|
-
.trim();
|
|
67
|
-
|
|
68
|
-
export function extensionForBundledSfxFile(filename) {
|
|
69
|
-
return extname(filename) || ".mp3";
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function score(intent, key, entry) {
|
|
73
|
-
const query = normalize(intent);
|
|
74
|
-
const name = normalize(key);
|
|
75
|
-
if (query === name) return 100;
|
|
76
|
-
if (query.includes(name) || name.includes(query)) return 50;
|
|
77
|
-
const haystack = new Set(normalize(`${key} ${entry.description || ""}`).split(/\s+/));
|
|
78
|
-
return query.split(/\s+/).filter((token) => token && haystack.has(token)).length;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export const bundledSfxProvider = {
|
|
82
|
-
async search(intent, ctx = {}) {
|
|
83
|
-
const libraryDir = ctx.libraryDir || LIB_DIR;
|
|
84
|
-
const health = inspectBundledSfxAssets(libraryDir);
|
|
85
|
-
if (!health.ok) throw new BundledSfxAssetsError(health);
|
|
86
|
-
const manifest = JSON.parse(readFileSync(join(libraryDir, "manifest.json"), "utf8"));
|
|
87
|
-
|
|
88
|
-
const ranked = Object.entries(manifest)
|
|
89
|
-
.map(([key, entry]) => ({ key, entry, score: score(intent, key, entry) }))
|
|
90
|
-
.filter(({ entry, score }) => entry?.file && score > 0)
|
|
91
|
-
.sort((a, b) => b.score - a.score || a.key.localeCompare(b.key));
|
|
92
|
-
const best = ranked[0];
|
|
93
|
-
if (!best) return null;
|
|
94
|
-
|
|
95
|
-
const localPath = join(libraryDir, best.entry.file);
|
|
96
|
-
return {
|
|
97
|
-
localPath,
|
|
98
|
-
ext: extensionForBundledSfxFile(best.entry.file),
|
|
99
|
-
source: "bundled",
|
|
100
|
-
metadata: {
|
|
101
|
-
description: best.entry.description || best.key,
|
|
102
|
-
duration: best.entry.duration ?? null,
|
|
103
|
-
provider: "bundled.sfx",
|
|
104
|
-
provenance: { library_key: best.key },
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
},
|
|
108
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from "node:assert";
|
|
2
|
-
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { test } from "node:test";
|
|
6
|
-
import {
|
|
7
|
-
BUNDLED_SFX_RECOVERY_COMMAND,
|
|
8
|
-
BundledSfxAssetsError,
|
|
9
|
-
bundledSfxProvider,
|
|
10
|
-
extensionForBundledSfxFile,
|
|
11
|
-
inspectBundledSfxAssets,
|
|
12
|
-
} from "./bundled-sfx-provider.mjs";
|
|
13
|
-
|
|
14
|
-
test("derives bundled SFX extension from the manifest filename", () => {
|
|
15
|
-
assert.equal(extensionForBundledSfxFile("impact.wav"), ".wav");
|
|
16
|
-
assert.equal(extensionForBundledSfxFile("whoosh.ogg"), ".ogg");
|
|
17
|
-
assert.equal(extensionForBundledSfxFile("extensionless"), ".mp3");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("reports an agent-friendly recovery when the bundled SFX manifest is absent", () => {
|
|
21
|
-
const libraryDir = mkdtempSync(join(tmpdir(), "media-use-sfx-missing-"));
|
|
22
|
-
try {
|
|
23
|
-
const health = inspectBundledSfxAssets(libraryDir);
|
|
24
|
-
assert.equal(health.ok, false);
|
|
25
|
-
assert.equal(health.code, "bundled_sfx_assets_missing");
|
|
26
|
-
assert.match(health.detail, /manifest\.json/);
|
|
27
|
-
assert.match(health.fix, /hyperframes skills update media-use/);
|
|
28
|
-
assert.equal(health.fix, BUNDLED_SFX_RECOVERY_COMMAND);
|
|
29
|
-
} finally {
|
|
30
|
-
rmSync(libraryDir, { recursive: true, force: true });
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("reports the exact missing file from an incomplete bundled SFX install", () => {
|
|
35
|
-
const libraryDir = mkdtempSync(join(tmpdir(), "media-use-sfx-incomplete-"));
|
|
36
|
-
try {
|
|
37
|
-
writeFileSync(
|
|
38
|
-
join(libraryDir, "manifest.json"),
|
|
39
|
-
JSON.stringify({ whoosh: { file: "whoosh.mp3", description: "transition" } }),
|
|
40
|
-
);
|
|
41
|
-
const health = inspectBundledSfxAssets(libraryDir);
|
|
42
|
-
assert.equal(health.ok, false);
|
|
43
|
-
assert.equal(health.code, "bundled_sfx_assets_missing");
|
|
44
|
-
assert.match(health.detail, /whoosh\.mp3/);
|
|
45
|
-
} finally {
|
|
46
|
-
rmSync(libraryDir, { recursive: true, force: true });
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("bundled provider raises a typed install error instead of a generic catalog miss", async () => {
|
|
51
|
-
const libraryDir = mkdtempSync(join(tmpdir(), "media-use-sfx-provider-"));
|
|
52
|
-
try {
|
|
53
|
-
await assert.rejects(
|
|
54
|
-
() => bundledSfxProvider.search("whoosh", { libraryDir }),
|
|
55
|
-
(error) => {
|
|
56
|
-
assert.ok(error instanceof BundledSfxAssetsError);
|
|
57
|
-
assert.equal(error.code, "bundled_sfx_assets_missing");
|
|
58
|
-
assert.match(error.message, /hyperframes skills update media-use/);
|
|
59
|
-
return true;
|
|
60
|
-
},
|
|
61
|
-
);
|
|
62
|
-
} finally {
|
|
63
|
-
rmSync(libraryDir, { recursive: true, force: true });
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("accepts a complete bundled SFX library", () => {
|
|
68
|
-
const libraryDir = mkdtempSync(join(tmpdir(), "media-use-sfx-complete-"));
|
|
69
|
-
try {
|
|
70
|
-
mkdirSync(libraryDir, { recursive: true });
|
|
71
|
-
writeFileSync(
|
|
72
|
-
join(libraryDir, "manifest.json"),
|
|
73
|
-
JSON.stringify({ whoosh: { file: "whoosh.mp3", description: "transition" } }),
|
|
74
|
-
);
|
|
75
|
-
writeFileSync(join(libraryDir, "whoosh.mp3"), "audio");
|
|
76
|
-
assert.deepEqual(inspectBundledSfxAssets(libraryDir), {
|
|
77
|
-
ok: true,
|
|
78
|
-
count: 1,
|
|
79
|
-
detail: "1 bundled SFX asset available",
|
|
80
|
-
fix: "",
|
|
81
|
-
});
|
|
82
|
-
} finally {
|
|
83
|
-
rmSync(libraryDir, { recursive: true, force: true });
|
|
84
|
-
}
|
|
85
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "node:fs";
|
|
2
|
-
import { join, basename } from "node:path";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { readManifest, appendRecord, normalizePrompt } from "./manifest.mjs";
|
|
6
|
-
|
|
7
|
-
const SCHEMA_PREFIX = "mu-v1-";
|
|
8
|
-
const KEY_HEX_CHARS = 16;
|
|
9
|
-
const COMPLETE_SENTINEL = ".hf-complete";
|
|
10
|
-
|
|
11
|
-
export function globalMediaDir() {
|
|
12
|
-
return join(homedir(), ".media");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function contentHash(filePath) {
|
|
16
|
-
const bytes = readFileSync(filePath);
|
|
17
|
-
return createHash("sha256").update(bytes).digest("hex");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function cacheEntryDir(rootDir, sha) {
|
|
21
|
-
return join(rootDir, SCHEMA_PREFIX + sha.slice(0, KEY_HEX_CHARS));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isComplete(entryDir) {
|
|
25
|
-
return existsSync(join(entryDir, COMPLETE_SENTINEL));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function markComplete(entryDir) {
|
|
29
|
-
writeFileSync(join(entryDir, COMPLETE_SENTINEL), "", "utf8");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// The manifest helpers append their own ".media" to the dir they get, so the
|
|
33
|
-
// global manifest must be addressed by HOME, not by globalMediaDir() — passing
|
|
34
|
-
// the latter nested it at ~/.media/.media/manifest.jsonl, invisible to the
|
|
35
|
-
// Studio /api/assets/global route (which reads the documented flat path).
|
|
36
|
-
export function readGlobalManifest() {
|
|
37
|
-
return readManifest(homedir());
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Resolve a content-sha (full or unambiguous prefix) to a reusable global-cache
|
|
41
|
-
// record, for `resolve --reuse <sha>`. Returns null on no match, or
|
|
42
|
-
// { ambiguous: true, count } when a prefix matches multiple distinct entries.
|
|
43
|
-
// Completeness (the .hf-complete sentinel) is left to importFromCache so the
|
|
44
|
-
// caller can surface an "incomplete cache entry" error distinctly from a miss.
|
|
45
|
-
export function findGlobalBySha(shaPrefix) {
|
|
46
|
-
const p = String(shaPrefix || "")
|
|
47
|
-
.toLowerCase()
|
|
48
|
-
.trim();
|
|
49
|
-
if (!p) return null;
|
|
50
|
-
const matches = readGlobalManifest().filter(
|
|
51
|
-
(r) => r.reusable && typeof r.sha === "string" && r.sha.startsWith(p),
|
|
52
|
-
);
|
|
53
|
-
if (matches.length === 0) return null;
|
|
54
|
-
if (matches.length > 1) {
|
|
55
|
-
const exact = matches.find((r) => r.sha === p);
|
|
56
|
-
if (exact) return exact;
|
|
57
|
-
return { ambiguous: true, count: matches.length };
|
|
58
|
-
}
|
|
59
|
-
return matches[0];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function validateCacheHit(match) {
|
|
63
|
-
if (!match?.sha) return null;
|
|
64
|
-
return isComplete(cacheEntryDir(globalMediaDir(), match.sha)) ? match : null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function cacheGet(prompt, type) {
|
|
68
|
-
const key = normalizePrompt(prompt);
|
|
69
|
-
if (!key) return null;
|
|
70
|
-
return validateCacheHit(
|
|
71
|
-
readGlobalManifest().find(
|
|
72
|
-
(r) =>
|
|
73
|
-
r.reusable &&
|
|
74
|
-
normalizePrompt(r.provenance?.prompt) === key &&
|
|
75
|
-
(type == null || r.type === type),
|
|
76
|
-
),
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function cacheGetByEntity(entity) {
|
|
81
|
-
const lower = entity.toLowerCase();
|
|
82
|
-
return validateCacheHit(
|
|
83
|
-
readGlobalManifest().find((r) => r.reusable && r.entity && r.entity.toLowerCase() === lower),
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function cachePut(filePath, record) {
|
|
88
|
-
const sha = contentHash(filePath);
|
|
89
|
-
// Idempotent: same content already promoted -> don't duplicate the global
|
|
90
|
-
// record. ponytail: skips usage_count bump; add it when the metric is needed.
|
|
91
|
-
const existing = readGlobalManifest().find((r) => r.sha === sha);
|
|
92
|
-
if (existing) return { sha, cached_path: existing.cached_path, deduped: true };
|
|
93
|
-
|
|
94
|
-
const dir = globalMediaDir();
|
|
95
|
-
const entryDir = cacheEntryDir(dir, sha);
|
|
96
|
-
mkdirSync(entryDir, { recursive: true });
|
|
97
|
-
|
|
98
|
-
const dest = join(entryDir, basename(filePath));
|
|
99
|
-
copyFileSync(filePath, dest);
|
|
100
|
-
markComplete(entryDir);
|
|
101
|
-
|
|
102
|
-
const globalRecord = {
|
|
103
|
-
...record,
|
|
104
|
-
sha,
|
|
105
|
-
reusable: true,
|
|
106
|
-
cached_path: dest,
|
|
107
|
-
};
|
|
108
|
-
appendRecord(homedir(), globalRecord);
|
|
109
|
-
return { sha, cached_path: dest };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function importFromCache(cacheRecord, projectDir, localId, localPath) {
|
|
113
|
-
const sha = cacheRecord.sha;
|
|
114
|
-
const entryDir = cacheEntryDir(globalMediaDir(), sha);
|
|
115
|
-
if (!isComplete(entryDir)) return null;
|
|
116
|
-
|
|
117
|
-
const cachedFile = cacheRecord.cached_path;
|
|
118
|
-
if (!cachedFile || !existsSync(cachedFile)) return null;
|
|
119
|
-
|
|
120
|
-
mkdirSync(join(projectDir, ".media"), { recursive: true });
|
|
121
|
-
const fullDest = join(projectDir, localPath);
|
|
122
|
-
mkdirSync(join(fullDest, ".."), { recursive: true });
|
|
123
|
-
copyFileSync(cachedFile, fullDest);
|
|
124
|
-
|
|
125
|
-
const projectRecord = {
|
|
126
|
-
...cacheRecord,
|
|
127
|
-
id: localId,
|
|
128
|
-
path: localPath,
|
|
129
|
-
provenance: {
|
|
130
|
-
...cacheRecord.provenance,
|
|
131
|
-
imported_from: sha,
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
delete projectRecord.sha;
|
|
135
|
-
delete projectRecord.reusable;
|
|
136
|
-
delete projectRecord.cached_path;
|
|
137
|
-
|
|
138
|
-
return projectRecord;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function promote(projectDir, id) {
|
|
142
|
-
const records = readManifest(projectDir);
|
|
143
|
-
const record = records.find((r) => r.id === id);
|
|
144
|
-
if (!record) throw new Error(`asset not found in project manifest: ${id}`);
|
|
145
|
-
|
|
146
|
-
const filePath = join(projectDir, record.path);
|
|
147
|
-
if (!existsSync(filePath)) throw new Error(`asset file not found: ${filePath}`);
|
|
148
|
-
|
|
149
|
-
return cachePut(filePath, record);
|
|
150
|
-
}
|