dsh-hyperframes 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. package/README.en.md +6 -5
  2. package/README.md +7 -6
  3. package/lib/index.d.ts +1 -1
  4. package/lib/index.js +1 -0
  5. package/package.json +63 -63
  6. package/skills/embedded-captions/SKILL.md +28 -3
  7. package/skills/embedded-captions/references/aesthetic-principles.md +2 -3
  8. package/skills/embedded-captions/references/rail.md +1 -1
  9. package/skills/embedded-captions/scripts/check-occlusion.cjs +4 -1
  10. package/skills/embedded-captions/scripts/check-overflow.cjs +1 -0
  11. package/skills/embedded-captions/scripts/check-rail-climax.cjs +5 -1
  12. package/skills/embedded-captions/scripts/fixtures/heroless/theme.json +11 -0
  13. package/skills/embedded-captions/scripts/hf-cli.cjs +33 -0
  14. package/skills/embedded-captions/scripts/make-theme.cjs +95 -57
  15. package/skills/embedded-captions/scripts/make-theme.test.mjs +131 -0
  16. package/skills/embedded-captions/scripts/matte.cjs +1 -13
  17. package/skills/embedded-captions/scripts/measure-layout.cjs +2 -1
  18. package/skills/embedded-captions/scripts/preview-frames.cjs +39 -11
  19. package/skills/embedded-captions/scripts/preview-frames.test.mjs +24 -0
  20. package/skills/embedded-captions/scripts/render-and-composite.sh +8 -52
  21. package/skills/embedded-captions/scripts/safe-zones.cjs +4 -1
  22. package/skills/embedded-captions/scripts/transcribe.cjs +5 -14
  23. package/skills/embedded-captions/scripts/transcribe.test.mjs +41 -0
  24. package/skills/faceless-explainer/SKILL.md +20 -16
  25. package/skills/faceless-explainer/references/story-design.md +1 -1
  26. package/skills/faceless-explainer/scripts/assemble-index.mjs +6 -4
  27. package/skills/faceless-explainer/scripts/assemble-index.test.mjs +57 -2
  28. package/skills/faceless-explainer/scripts/audio.mjs +2 -0
  29. package/skills/faceless-explainer/scripts/build-frame.mjs +6 -3
  30. package/skills/faceless-explainer/scripts/captions.mjs +7 -4
  31. package/skills/faceless-explainer/scripts/frame-packets.mjs +4 -4
  32. package/skills/faceless-explainer/scripts/lib/bgm-volume.mjs +4 -0
  33. package/skills/faceless-explainer/scripts/lib/captured-fonts.mjs +58 -0
  34. package/skills/{hyperframes-core → faceless-explainer}/scripts/lib/frame-packets-core.mjs +5 -23
  35. package/skills/faceless-explainer/scripts/lib/storyboard.mjs +1 -1
  36. package/skills/faceless-explainer/scripts/lib/transitions.json +3 -3
  37. package/skills/faceless-explainer/scripts/transitions.mjs +1 -0
  38. package/skills/faceless-explainer/scripts/transitions.test.mjs +2 -0
  39. package/skills/faceless-explainer/sub-agents/frame-worker.md +1 -1
  40. package/skills/figma/SKILL.md +2 -0
  41. package/skills/general-video/SKILL.md +13 -11
  42. package/skills/general-video/scripts/frame-packets.mjs +4 -4
  43. package/skills/general-video/scripts/lib/frame-packets-core.mjs +194 -0
  44. package/skills/general-video/sub-agents/frame-worker.md +1 -1
  45. package/skills/hyperframes/SKILL.md +40 -30
  46. package/skills/{hyperframes-core → hyperframes}/references/brief-contract.md +15 -15
  47. package/skills/{hyperframes-core → hyperframes}/references/brief-format.md +8 -8
  48. package/skills/hyperframes/references/capability-menu.md +2 -2
  49. package/skills/{hyperframes-core → hyperframes}/references/frame-worker-core.md +1 -1
  50. package/skills/hyperframes/references/intent-interview.md +16 -16
  51. package/skills/hyperframes/references/plugin-installation.md +43 -0
  52. package/skills/{hyperframes-core → hyperframes}/references/production-loop.md +11 -11
  53. package/skills/hyperframes/references/review-loop.md +37 -0
  54. package/skills/hyperframes/references/route-briefs.md +1 -1
  55. package/skills/hyperframes/references/routes/motion-graphics.md +1 -1
  56. package/skills/{hyperframes-core → hyperframes}/references/script-format.md +3 -3
  57. package/skills/hyperframes/references/skill-lifecycle.md +2 -0
  58. package/skills/{hyperframes-core → hyperframes}/references/storyboard-format.md +3 -34
  59. package/skills/hyperframes/scripts/lib/frame-packets-core.mjs +194 -0
  60. package/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
  61. package/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
  62. package/skills/hyperframes-animation/SKILL.md +5 -1
  63. package/skills/hyperframes-animation/adapters/gsap.md +1 -1
  64. package/skills/hyperframes-animation/adapters/lottie.md +15 -2
  65. package/skills/hyperframes-animation/adapters/three.md +14 -0
  66. package/skills/hyperframes-animation/blueprints/titlecard-reveal.md +1 -1
  67. package/skills/hyperframes-animation/references/motion-blur.md +133 -0
  68. package/skills/hyperframes-animation/rules/cursor-click-ripple.md +1 -1
  69. package/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +1 -1
  70. package/skills/hyperframes-animation/rules/hacker-flip-3d.md +1 -1
  71. package/skills/hyperframes-animation/rules/kinetic-beat-slam.md +2 -2
  72. package/skills/hyperframes-animation/rules/motion-blur-streak.md +1 -1
  73. package/skills/hyperframes-animation/rules/vertical-spring-ticker.md +1 -1
  74. package/skills/hyperframes-animation/scripts/package-loader.mjs +47 -16
  75. package/skills/hyperframes-animation/scripts/package-loader.test.mjs +52 -0
  76. package/skills/hyperframes-animation/techniques.md +1 -1
  77. package/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +3 -3
  78. package/skills/hyperframes-animation/transitions/css-push.md +4 -4
  79. package/skills/hyperframes-audio/SKILL.md +30 -22
  80. package/skills/hyperframes-audio/scripts/carve.mjs +6 -3
  81. package/skills/hyperframes-audio/scripts/carve.test.mjs +16 -1
  82. package/skills/hyperframes-cli/SKILL.md +44 -35
  83. package/skills/hyperframes-cli/references/init-and-scaffold.md +8 -6
  84. package/skills/hyperframes-cli/references/lint-validate-inspect.md +3 -3
  85. package/skills/hyperframes-cli/references/preview-render.md +35 -32
  86. package/skills/hyperframes-cli/references/upgrade-info-misc.md +50 -1
  87. package/skills/hyperframes-core/SKILL.md +30 -24
  88. package/skills/hyperframes-core/references/composition-patterns.md +3 -42
  89. package/skills/hyperframes-core/references/creator-editing-recipes.md +131 -7
  90. package/skills/hyperframes-core/references/data-attributes.md +11 -11
  91. package/skills/hyperframes-core/references/determinism-rules.md +2 -19
  92. package/skills/hyperframes-core/references/full-screen-motion.md +0 -9
  93. package/skills/hyperframes-core/references/minimal-composition.md +2 -2
  94. package/skills/hyperframes-core/references/sub-compositions.md +3 -66
  95. package/skills/hyperframes-core/references/tailwind.md +1 -20
  96. package/skills/hyperframes-core/references/tracks-and-clips.md +2 -6
  97. package/skills/hyperframes-core/references/variables-and-media.md +3 -3
  98. package/skills/hyperframes-creative/SKILL.md +2 -0
  99. package/skills/hyperframes-creative/references/house-style.md +15 -15
  100. package/skills/hyperframes-creative/references/story-spine.md +3 -1
  101. package/skills/hyperframes-creative/references/storyboard-recipe.md +57 -0
  102. package/skills/hyperframes-creative/references/typography.md +1 -1
  103. package/skills/hyperframes-creative/scripts/package-loader.mjs +47 -16
  104. package/skills/hyperframes-creative/scripts/package-loader.test.mjs +52 -0
  105. package/skills/hyperframes-keyframes/SKILL.md +4 -2
  106. package/skills/hyperframes-registry/SKILL.md +3 -1
  107. package/skills/hyperframes-registry/references/contributing.md +8 -2
  108. package/skills/hyperframes-registry/references/discovery.md +20 -34
  109. package/skills/hyperframes-studio/SKILL.md +77 -0
  110. package/skills/media-use/SKILL.md +14 -12
  111. package/skills/media-use/audio/references/captions/authoring.md +4 -0
  112. package/skills/media-use/audio/references/remove-background.md +1 -1
  113. package/skills/media-use/audio/references/tts-to-captions.md +11 -2
  114. package/skills/media-use/audio/references/tts.md +84 -6
  115. package/skills/media-use/audio/scripts/audio.mjs +15 -6
  116. package/skills/media-use/audio/scripts/gemini-pipeline.test.mjs +113 -0
  117. package/skills/media-use/audio/scripts/lib/audio-meta.mjs +39 -0
  118. package/skills/media-use/audio/scripts/lib/audio-meta.test.mjs +136 -0
  119. package/skills/media-use/audio/scripts/lib/bgm-volume.mjs +4 -0
  120. package/skills/media-use/audio/scripts/lib/bgm.mjs +2 -6
  121. package/skills/media-use/audio/scripts/lib/gemini-auth.mjs +43 -0
  122. package/skills/media-use/audio/scripts/lib/gemini-auth.py +52 -0
  123. package/skills/media-use/audio/scripts/lib/gemini-auth.test.mjs +86 -0
  124. package/skills/media-use/audio/scripts/lib/gemini-auth_test.py +90 -0
  125. package/skills/media-use/audio/scripts/lib/gemini-tts.mjs +122 -0
  126. package/skills/media-use/audio/scripts/lib/gemini-tts.test.mjs +243 -0
  127. package/skills/media-use/audio/scripts/lib/heygen.mjs +2 -1
  128. package/skills/media-use/audio/scripts/lib/tts.mjs +15 -3
  129. package/skills/media-use/audio/scripts/wait-bgm.mjs +8 -4
  130. package/skills/media-use/audio/scripts/wait-bgm.test.mjs +65 -0
  131. package/skills/media-use/references/audio.md +3 -2
  132. package/skills/media-use/references/grading.md +7 -7
  133. package/skills/media-use/references/media-treatment-recipes.md +1 -1
  134. package/skills/media-use/references/memory.md +2 -2
  135. package/skills/media-use/references/meta.md +2 -2
  136. package/skills/media-use/references/operations.md +15 -13
  137. package/skills/media-use/references/resolve.md +22 -11
  138. package/skills/media-use/references/setup-providers.md +11 -11
  139. package/skills/media-use/scripts/audio-duck.mjs +19 -27
  140. package/skills/media-use/scripts/compatibility.test.mjs +96 -0
  141. package/skills/media-use/scripts/lib/config-lock.mjs +45 -0
  142. package/skills/media-use/scripts/lib/duck.mjs +16 -10
  143. package/skills/media-use/scripts/lib/media-fetch.mjs +85 -0
  144. package/skills/media-use/scripts/lib/media-home.mjs +20 -0
  145. package/skills/media-use/scripts/lib/npx-sync.mjs +28 -2
  146. package/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
  147. package/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
  148. package/skills/media-use/scripts/lib/telemetry.mjs +29 -41
  149. package/skills/media-use/scripts/lib/transcriptCutFade.mjs +21 -0
  150. package/skills/media-use/scripts/prefs.mjs +1 -1
  151. package/skills/media-use/scripts/recipe.mjs +1 -1
  152. package/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
  153. package/skills/media-use/scripts/resolve.mjs +27 -1267
  154. package/skills/media-use/scripts/transcribe.mjs +39 -25
  155. package/skills/media-use/scripts/transcribe.test.mjs +112 -0
  156. package/skills/media-use/scripts/transcript-cut.mjs +39 -8
  157. package/skills/media-use/scripts/transcript-cut.test.mjs +117 -0
  158. package/skills/motion-graphics/SKILL.md +3 -1
  159. package/skills/motion-graphics/agents/builder.md +1 -0
  160. package/skills/motion-graphics/agents/director.md +2 -1
  161. package/skills/motion-graphics/catalog-map.md +25 -13
  162. package/skills/motion-graphics/categories/charts/module.md +2 -1
  163. package/skills/motion-graphics/categories/lower-thirds/module.md +4 -0
  164. package/skills/motion-graphics/categories/news/module.md +1 -1
  165. package/skills/motion-graphics/references/motion-vocabulary.md +4 -0
  166. package/skills/music-to-video/SKILL.md +8 -6
  167. package/skills/music-to-video/references/montage.md +3 -6
  168. package/skills/music-to-video/references/motion-primitives/3d-card-flip/index.html +10 -111
  169. package/skills/music-to-video/references/motion-primitives/3d-card-flip/scene.html +119 -0
  170. package/skills/music-to-video/references/motion-primitives/bg-flow-field/index.html +11 -284
  171. package/skills/music-to-video/references/motion-primitives/bg-flow-field/scene.html +294 -0
  172. package/skills/music-to-video/references/motion-primitives/binary-decrypt/index.html +10 -76
  173. package/skills/music-to-video/references/motion-primitives/binary-decrypt/scene.html +85 -0
  174. package/skills/music-to-video/references/motion-primitives/blur-resolve/index.html +10 -52
  175. package/skills/music-to-video/references/motion-primitives/blur-resolve/scene.html +60 -0
  176. package/skills/music-to-video/references/motion-primitives/braam-punch/index.html +10 -92
  177. package/skills/music-to-video/references/motion-primitives/braam-punch/scene.html +100 -0
  178. package/skills/music-to-video/references/motion-primitives/chromatic-split/index.html +10 -103
  179. package/skills/music-to-video/references/motion-primitives/chromatic-split/scene.html +111 -0
  180. package/skills/music-to-video/references/motion-primitives/chrome-sweep/index.html +10 -60
  181. package/skills/music-to-video/references/motion-primitives/chrome-sweep/scene.html +68 -0
  182. package/skills/music-to-video/references/motion-primitives/counting-punch/index.html +10 -110
  183. package/skills/music-to-video/references/motion-primitives/counting-punch/scene.html +119 -0
  184. package/skills/music-to-video/references/motion-primitives/crash-zoom-in/index.html +10 -112
  185. package/skills/music-to-video/references/motion-primitives/crash-zoom-in/scene.html +120 -0
  186. package/skills/music-to-video/references/motion-primitives/datamosh-smear/index.html +10 -121
  187. package/skills/music-to-video/references/motion-primitives/datamosh-smear/scene.html +131 -0
  188. package/skills/music-to-video/references/motion-primitives/directional-fill/index.html +10 -90
  189. package/skills/music-to-video/references/motion-primitives/directional-fill/scene.html +103 -0
  190. package/skills/music-to-video/references/motion-primitives/dolly-zoom/index.html +10 -94
  191. package/skills/music-to-video/references/motion-primitives/dolly-zoom/scene.html +102 -0
  192. package/skills/music-to-video/references/motion-primitives/electric-arc/index.html +10 -100
  193. package/skills/music-to-video/references/motion-primitives/electric-arc/scene.html +105 -0
  194. package/skills/music-to-video/references/motion-primitives/flash-cut/index.html +10 -89
  195. package/skills/music-to-video/references/motion-primitives/flash-cut/scene.html +97 -0
  196. package/skills/music-to-video/references/motion-primitives/gooey-metaball/index.html +10 -197
  197. package/skills/music-to-video/references/motion-primitives/gooey-metaball/scene.html +206 -0
  198. package/skills/music-to-video/references/motion-primitives/hard-cut/index.html +10 -74
  199. package/skills/music-to-video/references/motion-primitives/hard-cut/scene.html +82 -0
  200. package/skills/music-to-video/references/motion-primitives/hypercut-whip/index.html +10 -36
  201. package/skills/music-to-video/references/motion-primitives/hypercut-whip/scene.html +44 -0
  202. package/skills/music-to-video/references/motion-primitives/iris-open/index.html +10 -61
  203. package/skills/music-to-video/references/motion-primitives/iris-open/scene.html +69 -0
  204. package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/index.html +10 -55
  205. package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/scene.html +64 -0
  206. package/skills/music-to-video/references/motion-primitives/liquid-morph/index.html +10 -88
  207. package/skills/music-to-video/references/motion-primitives/liquid-morph/scene.html +96 -0
  208. package/skills/music-to-video/references/motion-primitives/mask-reveal/index.html +10 -66
  209. package/skills/music-to-video/references/motion-primitives/mask-reveal/scene.html +74 -0
  210. package/skills/music-to-video/references/motion-primitives/mosaic-pack/index.html +10 -81
  211. package/skills/music-to-video/references/motion-primitives/mosaic-pack/scene.html +89 -0
  212. package/skills/music-to-video/references/motion-primitives/neon-flicker/index.html +10 -65
  213. package/skills/music-to-video/references/motion-primitives/neon-flicker/scene.html +73 -0
  214. package/skills/music-to-video/references/motion-primitives/outline-to-fill/index.html +10 -74
  215. package/skills/music-to-video/references/motion-primitives/outline-to-fill/scene.html +82 -0
  216. package/skills/music-to-video/references/motion-primitives/palette-flip/index.html +10 -89
  217. package/skills/music-to-video/references/motion-primitives/palette-flip/scene.html +97 -0
  218. package/skills/music-to-video/references/motion-primitives/particle-burst/index.html +10 -98
  219. package/skills/music-to-video/references/motion-primitives/particle-burst/scene.html +106 -0
  220. package/skills/music-to-video/references/motion-primitives/pixel-dissolve/index.html +10 -78
  221. package/skills/music-to-video/references/motion-primitives/pixel-dissolve/scene.html +86 -0
  222. package/skills/music-to-video/references/motion-primitives/radial-burst-lines/index.html +10 -117
  223. package/skills/music-to-video/references/motion-primitives/radial-burst-lines/scene.html +130 -0
  224. package/skills/music-to-video/references/motion-primitives/screen-shake/index.html +10 -70
  225. package/skills/music-to-video/references/motion-primitives/screen-shake/scene.html +78 -0
  226. package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/index.html +10 -87
  227. package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/scene.html +100 -0
  228. package/skills/music-to-video/references/motion-primitives/spotlight-sweep/index.html +10 -94
  229. package/skills/music-to-video/references/motion-primitives/spotlight-sweep/scene.html +102 -0
  230. package/skills/music-to-video/references/motion-primitives/staggered-exit/index.html +10 -74
  231. package/skills/music-to-video/references/motion-primitives/staggered-exit/scene.html +82 -0
  232. package/skills/music-to-video/references/motion-primitives/text-spectral-rays/index.html +11 -271
  233. package/skills/music-to-video/references/motion-primitives/text-spectral-rays/scene.html +280 -0
  234. package/skills/music-to-video/references/motion-primitives/text-wave-distort/index.html +10 -92
  235. package/skills/music-to-video/references/motion-primitives/text-wave-distort/scene.html +106 -0
  236. package/skills/music-to-video/references/motion-primitives/tile-mosaic/index.html +10 -101
  237. package/skills/music-to-video/references/motion-primitives/tile-mosaic/scene.html +109 -0
  238. package/skills/music-to-video/references/motion-primitives/typewriter-reveal/index.html +10 -79
  239. package/skills/music-to-video/references/motion-primitives/typewriter-reveal/scene.html +91 -0
  240. package/skills/music-to-video/references/motion-primitives/word-grid-burst/index.html +10 -83
  241. package/skills/music-to-video/references/motion-primitives/word-grid-burst/scene.html +96 -0
  242. package/skills/music-to-video/references/planning.md +2 -2
  243. package/skills/music-to-video/scripts/lib/storyboard.mjs +1 -1
  244. package/skills/music-to-video/sub-agents/frame-worker.md +2 -2
  245. package/skills/pr-to-video/SKILL.md +20 -16
  246. package/skills/pr-to-video/references/code-vocabulary.md +14 -12
  247. package/skills/pr-to-video/references/story-design.md +1 -1
  248. package/skills/pr-to-video/scripts/assemble-index.mjs +6 -4
  249. package/skills/pr-to-video/scripts/assemble-index.test.mjs +57 -2
  250. package/skills/pr-to-video/scripts/audio.mjs +2 -0
  251. package/skills/pr-to-video/scripts/build-frame.mjs +6 -3
  252. package/skills/pr-to-video/scripts/captions.mjs +7 -4
  253. package/skills/pr-to-video/scripts/captions.test.mjs +41 -0
  254. package/skills/pr-to-video/scripts/frame-packets.mjs +4 -4
  255. package/skills/pr-to-video/scripts/lib/bgm-volume.mjs +4 -0
  256. package/skills/pr-to-video/scripts/lib/captured-fonts.mjs +58 -0
  257. package/skills/pr-to-video/scripts/lib/frame-packets-core.mjs +194 -0
  258. package/skills/pr-to-video/scripts/lib/storyboard.mjs +1 -1
  259. package/skills/pr-to-video/scripts/lib/transitions.json +3 -3
  260. package/skills/pr-to-video/scripts/transitions.mjs +7 -5
  261. package/skills/pr-to-video/sub-agents/frame-worker.md +1 -1
  262. package/skills/product-launch-video/SKILL.md +20 -16
  263. package/skills/product-launch-video/references/story-design.md +1 -1
  264. package/skills/product-launch-video/scripts/assemble-index.mjs +6 -4
  265. package/skills/product-launch-video/scripts/assemble-index.test.mjs +57 -2
  266. package/skills/product-launch-video/scripts/audio.mjs +2 -0
  267. package/skills/product-launch-video/scripts/audio.test.mjs +16 -0
  268. package/skills/product-launch-video/scripts/build-frame-subsets.test.mjs +152 -0
  269. package/skills/product-launch-video/scripts/build-frame.mjs +6 -3
  270. package/skills/product-launch-video/scripts/captions.mjs +7 -4
  271. package/skills/product-launch-video/scripts/frame-packets.mjs +4 -4
  272. package/skills/product-launch-video/scripts/lib/bgm-volume.mjs +4 -0
  273. package/skills/product-launch-video/scripts/lib/captured-fonts.mjs +58 -0
  274. package/skills/product-launch-video/scripts/lib/frame-packets-core.mjs +194 -0
  275. package/skills/product-launch-video/scripts/lib/storyboard.mjs +1 -1
  276. package/skills/product-launch-video/scripts/lib/transitions.json +3 -3
  277. package/skills/product-launch-video/scripts/transitions.mjs +7 -5
  278. package/skills/product-launch-video/scripts/transitions.test.mjs +68 -0
  279. package/skills/product-launch-video/sub-agents/frame-worker.md +1 -1
  280. package/skills/remotion-to-hyperframes/SKILL.md +7 -4
  281. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +5 -5
  282. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/compositions/title.html +31 -0
  283. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +10 -25
  284. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +8 -8
  285. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +9 -9
  286. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html +35 -0
  287. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html +35 -0
  288. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html +36 -0
  289. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +28 -71
  290. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +3 -3
  291. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html +52 -0
  292. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html +99 -0
  293. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html +55 -0
  294. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +13 -212
  295. package/skills/remotion-to-hyperframes/references/api-map.md +16 -16
  296. package/skills/remotion-to-hyperframes/references/limitations.md +7 -7
  297. package/skills/remotion-to-hyperframes/references/lottie.md +5 -7
  298. package/skills/remotion-to-hyperframes/references/media.md +3 -3
  299. package/skills/remotion-to-hyperframes/references/parameters.md +5 -0
  300. package/skills/remotion-to-hyperframes/references/sequencing.md +71 -18
  301. package/skills/slideshow/SKILL.md +4 -1
  302. package/skills/slideshow/references/standalone-harness.md +0 -3
  303. package/skills/talking-head-recut/SKILL.md +10 -1
  304. package/skills/hyperframes-core/references/review-loop.md +0 -41
  305. package/skills/media-use/scripts/lib/adopt.mjs +0 -128
  306. package/skills/media-use/scripts/lib/adopt.test.mjs +0 -95
  307. package/skills/media-use/scripts/lib/bgm-provider.mjs +0 -20
  308. package/skills/media-use/scripts/lib/brand-provider.mjs +0 -59
  309. package/skills/media-use/scripts/lib/bundled-sfx-provider.mjs +0 -108
  310. package/skills/media-use/scripts/lib/bundled-sfx-provider.test.mjs +0 -85
  311. package/skills/media-use/scripts/lib/cache.mjs +0 -150
  312. package/skills/media-use/scripts/lib/candidates.mjs +0 -91
  313. package/skills/media-use/scripts/lib/candidates.test.mjs +0 -155
  314. package/skills/media-use/scripts/lib/codex-provider.mjs +0 -161
  315. package/skills/media-use/scripts/lib/codex-provider.test.mjs +0 -34
  316. package/skills/media-use/scripts/lib/coverage.test.mjs +0 -117
  317. package/skills/media-use/scripts/lib/cube-build.mjs +0 -181
  318. package/skills/media-use/scripts/lib/cube-build.test.mjs +0 -80
  319. package/skills/media-use/scripts/lib/cube-validate.mjs +0 -198
  320. package/skills/media-use/scripts/lib/cube-validate.test.mjs +0 -125
  321. package/skills/media-use/scripts/lib/cutlist.test.mjs +0 -148
  322. package/skills/media-use/scripts/lib/duck.test.mjs +0 -118
  323. package/skills/media-use/scripts/lib/error-diffusion.test.mjs +0 -102
  324. package/skills/media-use/scripts/lib/freeze.mjs +0 -70
  325. package/skills/media-use/scripts/lib/freeze.test.mjs +0 -46
  326. package/skills/media-use/scripts/lib/grade-analyzer.mjs +0 -264
  327. package/skills/media-use/scripts/lib/grade-analyzer.test.mjs +0 -285
  328. package/skills/media-use/scripts/lib/heygen-cli.mjs +0 -191
  329. package/skills/media-use/scripts/lib/heygen-cli.test.mjs +0 -301
  330. package/skills/media-use/scripts/lib/heygen-search.mjs +0 -45
  331. package/skills/media-use/scripts/lib/heygen-search.test.mjs +0 -49
  332. package/skills/media-use/scripts/lib/heygen-video-provider.mjs +0 -127
  333. package/skills/media-use/scripts/lib/heygen-video-provider.test.mjs +0 -344
  334. package/skills/media-use/scripts/lib/image-provider.mjs +0 -44
  335. package/skills/media-use/scripts/lib/local-models.mjs +0 -318
  336. package/skills/media-use/scripts/lib/local-models.test.mjs +0 -220
  337. package/skills/media-use/scripts/lib/local-run.mjs +0 -78
  338. package/skills/media-use/scripts/lib/local-run.test.mjs +0 -121
  339. package/skills/media-use/scripts/lib/logo-provider.mjs +0 -222
  340. package/skills/media-use/scripts/lib/logo-provider.test.mjs +0 -137
  341. package/skills/media-use/scripts/lib/ltx-video-provider.mjs +0 -94
  342. package/skills/media-use/scripts/lib/ltx-video-provider.test.mjs +0 -316
  343. package/skills/media-use/scripts/lib/lut-preset-provider.mjs +0 -266
  344. package/skills/media-use/scripts/lib/lut-preset-provider.test.mjs +0 -176
  345. package/skills/media-use/scripts/lib/manifest.test.mjs +0 -388
  346. package/skills/media-use/scripts/lib/match.mjs +0 -47
  347. package/skills/media-use/scripts/lib/mflux-provider.mjs +0 -128
  348. package/skills/media-use/scripts/lib/mflux-provider.test.mjs +0 -174
  349. package/skills/media-use/scripts/lib/misses.mjs +0 -49
  350. package/skills/media-use/scripts/lib/misses.test.mjs +0 -78
  351. package/skills/media-use/scripts/lib/npx-sync.test.mjs +0 -50
  352. package/skills/media-use/scripts/lib/parakeet-words.test.mjs +0 -44
  353. package/skills/media-use/scripts/lib/prefs-store.test.mjs +0 -189
  354. package/skills/media-use/scripts/lib/probe.mjs +0 -39
  355. package/skills/media-use/scripts/lib/probe.test.mjs +0 -31
  356. package/skills/media-use/scripts/lib/providers.mjs +0 -5
  357. package/skills/media-use/scripts/lib/recipe-store.test.mjs +0 -367
  358. package/skills/media-use/scripts/lib/registry.mjs +0 -218
  359. package/skills/media-use/scripts/lib/registry.test.mjs +0 -261
  360. package/skills/media-use/scripts/lib/search.mjs +0 -24
  361. package/skills/media-use/scripts/lib/search.test.mjs +0 -32
  362. package/skills/media-use/scripts/lib/sfx-provider.mjs +0 -23
  363. package/skills/media-use/scripts/lib/specs.mjs +0 -132
  364. package/skills/media-use/scripts/lib/specs.test.mjs +0 -157
  365. package/skills/media-use/scripts/lib/stats.mjs +0 -117
  366. package/skills/media-use/scripts/lib/stats.test.mjs +0 -165
  367. package/skills/media-use/scripts/lib/telemetry.test.mjs +0 -373
  368. package/skills/media-use/scripts/lib/tts-local-provider.mjs +0 -92
  369. package/skills/media-use/scripts/lib/tts-local-provider.test.mjs +0 -72
  370. package/skills/media-use/scripts/lib/usage.mjs +0 -57
  371. package/skills/media-use/scripts/lib/usage.test.mjs +0 -54
  372. package/skills/media-use/scripts/lib/voice-provider.mjs +0 -57
  373. package/skills/media-use/scripts/lib/voice-provider.test.mjs +0 -46
  374. package/skills/media-use/scripts/resolve.test.mjs +0 -1120
  375. /package/skills/{hyperframes-core → hyperframes}/references/subagent-dispatch.md +0 -0
@@ -1,176 +0,0 @@
1
- import { strict as assert } from "node:assert";
2
- import { mkdtempSync, rmSync, existsSync, readFileSync, readdirSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { tmpdir } from "node:os";
5
- import { test } from "node:test";
6
- import {
7
- LIBRARY_LUT_OFFLINE_CODE,
8
- RESOLVABLE_PRESET_IDS,
9
- freezeLibraryLut,
10
- matchColorLook,
11
- readBundledLutIndex,
12
- } from "./lut-preset-provider.mjs";
13
- import { buildCube } from "./cube-build.mjs";
14
- import { validateCube, validateCubeFile } from "./cube-validate.mjs";
15
-
16
- const REPO_ROOT = join(import.meta.dirname, "..", "..", "..", "..");
17
-
18
- function corePresetIdsFromSource() {
19
- const src = readFileSync(join(REPO_ROOT, "packages/core/src/colorGrading.ts"), "utf8");
20
- const match = src.match(/export type HfColorGradingPresetId =([\s\S]*?);/);
21
- assert.ok(match, "core preset union should be readable");
22
- return [...match[1].matchAll(/"([^"]+)"/g)].map((m) => m[1]);
23
- }
24
-
25
- test("warm daylight and warm natural light resolve to the core warm-daylight preset", () => {
26
- assert.deepEqual(matchColorLook("warm daylight"), {
27
- kind: "preset",
28
- preset: "warm-daylight",
29
- score: 2,
30
- });
31
- assert.equal(matchColorLook("warm natural light").preset, "warm-daylight");
32
- });
33
-
34
- test("high contrast punchy resolves to deep-contrast", () => {
35
- assert.equal(matchColorLook("high contrast punchy").preset, "deep-contrast");
36
- });
37
-
38
- test("removed preset phrases resolve to surviving looks", () => {
39
- assert.equal(matchColorLook("natural lift").preset, "soft-boost");
40
- assert.equal(matchColorLook("fresh pop").preset, "bright-pop");
41
- assert.equal(matchColorLook("warm clean").preset, "warm-daylight");
42
- assert.equal(matchColorLook("cool clean").preset, "clean-studio");
43
- });
44
-
45
- test("complete-filter intent aliases resolve deterministically", () => {
46
- assert.equal(matchColorLook("analog tape").preset, "vhs-playback");
47
- assert.equal(matchColorLook("creator video").preset, "creator-camcorder");
48
- });
49
-
50
- test("library look freezes a validated cube from params offline (--local-only)", async () => {
51
- const projectDir = mkdtempSync(join(tmpdir(), "mu-lut-provider-"));
52
- try {
53
- const match = matchColorLook("teal orange blockbuster");
54
- assert.equal(match.kind, "library");
55
- // localOnly forces the deterministic params path (no network); online, the
56
- // same look downloads its .cube from the CDN url (via "url").
57
- const frozen = await freezeLibraryLut(match, { projectDir, type: "grade", localOnly: true });
58
- assert.match(frozen.localPath, /^\.media\/luts\/grade_001\.cube$/);
59
- assert.ok(existsSync(join(projectDir, frozen.localPath)));
60
- assert.equal(validateCubeFile(join(projectDir, frozen.localPath)).ok, true);
61
- assert.equal(frozen.lut.src, frozen.localPath);
62
- assert.equal(frozen.metadata.provenance.via, "params-fallback");
63
- } finally {
64
- rmSync(projectDir, { recursive: true, force: true });
65
- }
66
- });
67
-
68
- test("every resolver preset exists in packages/core/src/colorGrading.ts", () => {
69
- const corePresetIds = corePresetIdsFromSource();
70
- assert.deepEqual(
71
- RESOLVABLE_PRESET_IDS.filter((id) => !corePresetIds.includes(id)),
72
- [],
73
- );
74
- for (const id of RESOLVABLE_PRESET_IDS) {
75
- const match = matchColorLook(id);
76
- assert.equal(match.kind, "preset");
77
- assert.equal(match.preset, id);
78
- }
79
- });
80
-
81
- test("zero-overlap intent returns no preset or library match", () => {
82
- assert.equal(matchColorLook("zqxv imaginary neutron look"), null);
83
- });
84
-
85
- test("bundled LUT index entries resolve from params or url", () => {
86
- for (const entry of readBundledLutIndex()) {
87
- assert.ok(entry.id);
88
- assert.ok(entry.description);
89
- assert.ok(entry.params || entry.url, `${entry.id} should define params or url`);
90
- if (entry.params) {
91
- assert.equal(typeof entry.params, "object");
92
- assert.equal(validateCube(buildCube(entry.params)).ok, true, `${entry.id} params validate`);
93
- }
94
- if (entry.url) assert.equal(typeof entry.url, "string");
95
- }
96
- });
97
-
98
- test("url library entries respect localOnly and freeze through fetch", async () => {
99
- const projectDir = mkdtempSync(join(tmpdir(), "mu-lut-url-provider-"));
100
- const match = {
101
- kind: "library",
102
- id: "cdn-look",
103
- description: "CDN-hosted look",
104
- tags: ["cdn"],
105
- intensity: 0.7,
106
- url: "https://example.invalid/look.cube",
107
- };
108
- const originalFetch = globalThis.fetch;
109
- let fetchCalls = 0;
110
- try {
111
- globalThis.fetch = async () => {
112
- fetchCalls++;
113
- throw new Error("network should be skipped under localOnly");
114
- };
115
- await assert.rejects(
116
- freezeLibraryLut(match, { projectDir, type: "lut", localOnly: true }),
117
- (err) => {
118
- assert.equal(err.code, LIBRARY_LUT_OFFLINE_CODE);
119
- assert.match(err.message, /--local-only/);
120
- return true;
121
- },
122
- );
123
- assert.equal(fetchCalls, 0);
124
-
125
- const cube = buildCube({ contrast: 0.1 });
126
- const body = Buffer.from(cube);
127
- globalThis.fetch = async (url) => {
128
- fetchCalls++;
129
- assert.equal(url, match.url);
130
- return {
131
- ok: true,
132
- headers: { get: () => String(body.length) },
133
- body: [body],
134
- };
135
- };
136
-
137
- const frozen = await freezeLibraryLut(match, { projectDir, type: "lut" });
138
- assert.equal(fetchCalls, 1);
139
- assert.match(frozen.localPath, /^\.media\/luts\/lut_001\.cube$/);
140
- assert.equal(validateCubeFile(join(projectDir, frozen.localPath)).ok, true);
141
- assert.equal(frozen.metadata.provenance.via, "url");
142
- } finally {
143
- globalThis.fetch = originalFetch;
144
- rmSync(projectDir, { recursive: true, force: true });
145
- }
146
- });
147
-
148
- test("failed URL library freeze releases its reservation", async () => {
149
- const projectDir = mkdtempSync(join(tmpdir(), "mu-lut-failure-"));
150
- const originalFetch = globalThis.fetch;
151
- try {
152
- globalThis.fetch = async () => ({
153
- ok: true,
154
- headers: { get: () => "12" },
155
- body: [Buffer.from("not a cube\n")],
156
- });
157
- await assert.rejects(
158
- freezeLibraryLut(
159
- {
160
- kind: "library",
161
- id: "broken-cdn-look",
162
- description: "Broken CDN look",
163
- tags: ["broken"],
164
- intensity: 1,
165
- url: "https://example.com/broken.cube",
166
- },
167
- { projectDir, type: "lut" },
168
- ),
169
- /failed to freeze library LUT/,
170
- );
171
- assert.deepStrictEqual(readdirSync(join(projectDir, ".media/luts")), []);
172
- } finally {
173
- globalThis.fetch = originalFetch;
174
- rmSync(projectDir, { recursive: true, force: true });
175
- }
176
- });
@@ -1,388 +0,0 @@
1
- import { strict as assert } from "node:assert";
2
- import {
3
- mkdtempSync,
4
- rmSync,
5
- readFileSync,
6
- writeFileSync,
7
- mkdirSync,
8
- existsSync,
9
- readdirSync,
10
- } from "node:fs";
11
- import { join } from "node:path";
12
- import { tmpdir } from "node:os";
13
- import {
14
- readManifest,
15
- appendRecord,
16
- findByPrompt,
17
- findByEntity,
18
- nextId,
19
- allocateId,
20
- withReservedFileSync,
21
- normalizePrompt,
22
- manifestPath,
23
- mediaDir,
24
- typeDirPath,
25
- typeSubdir,
26
- } from "./manifest.mjs";
27
- import { regenerateIndex, generateIndexContent } from "./index-gen.mjs";
28
- import {
29
- contentHash,
30
- cachePut,
31
- cacheGet,
32
- cacheGetByEntity,
33
- importFromCache,
34
- promote,
35
- } from "./cache.mjs";
36
-
37
- let tmp;
38
-
39
- function setup() {
40
- tmp = mkdtempSync(join(tmpdir(), "mu-test-"));
41
- }
42
-
43
- function cleanup() {
44
- if (tmp) rmSync(tmp, { recursive: true, force: true });
45
- }
46
-
47
- function makeRecord(overrides = {}) {
48
- return {
49
- id: "bgm_001",
50
- type: "bgm",
51
- path: ".media/audio/bgm/bgm_001.wav",
52
- source: "search",
53
- description: "soft minimal ambient",
54
- duration: 11,
55
- provenance: { provider: "heygen.audio.sounds", prompt: "subtle tech" },
56
- ...overrides,
57
- };
58
- }
59
-
60
- function runTests() {
61
- const tests = [];
62
-
63
- function test(name, fn) {
64
- tests.push({ name, fn });
65
- }
66
-
67
- // --- manifest.mjs ---
68
-
69
- test("readManifest returns empty array when no manifest exists", () => {
70
- setup();
71
- const result = readManifest(tmp);
72
- assert.deepStrictEqual(result, []);
73
- cleanup();
74
- });
75
-
76
- test("appendRecord writes valid JSONL and readManifest parses it back", () => {
77
- setup();
78
- const record = makeRecord();
79
- appendRecord(tmp, record);
80
- const records = readManifest(tmp);
81
- assert.equal(records.length, 1);
82
- assert.deepStrictEqual(records[0], record);
83
- cleanup();
84
- });
85
-
86
- test("appendRecord creates .media/ and type subdirs on first write", () => {
87
- setup();
88
- appendRecord(tmp, makeRecord());
89
- assert.ok(existsSync(mediaDir(tmp)));
90
- assert.ok(existsSync(typeDirPath(tmp, "bgm")));
91
- cleanup();
92
- });
93
-
94
- test("lut and grade artifacts use the shared .media/luts subdir", () => {
95
- assert.equal(typeSubdir("lut"), "luts");
96
- assert.equal(typeSubdir("grade"), "luts");
97
-
98
- setup();
99
- const allocated = allocateId(tmp, "lut", ".cube");
100
- assert.equal(allocated.localPath, ".media/luts/lut_001.cube");
101
- assert.ok(existsSync(join(tmp, allocated.localPath)));
102
- cleanup();
103
- });
104
-
105
- test("appendRecord appends multiple records", () => {
106
- setup();
107
- appendRecord(tmp, makeRecord({ id: "bgm_001" }));
108
- appendRecord(tmp, makeRecord({ id: "bgm_002", provenance: { prompt: "energetic" } }));
109
- const records = readManifest(tmp);
110
- assert.equal(records.length, 2);
111
- assert.equal(records[0].id, "bgm_001");
112
- assert.equal(records[1].id, "bgm_002");
113
- cleanup();
114
- });
115
-
116
- test("findByPrompt returns exact-match record", () => {
117
- setup();
118
- appendRecord(tmp, makeRecord());
119
- const found = findByPrompt(tmp, "subtle tech", "bgm");
120
- assert.ok(found);
121
- assert.equal(found.id, "bgm_001");
122
- cleanup();
123
- });
124
-
125
- test("findByPrompt returns null on miss", () => {
126
- setup();
127
- appendRecord(tmp, makeRecord());
128
- assert.equal(findByPrompt(tmp, "nonexistent", "bgm"), null);
129
- cleanup();
130
- });
131
-
132
- test("findByPrompt filters by type", () => {
133
- setup();
134
- appendRecord(tmp, makeRecord({ type: "sfx" }));
135
- assert.equal(findByPrompt(tmp, "subtle tech", "bgm"), null);
136
- assert.ok(findByPrompt(tmp, "subtle tech", "sfx"));
137
- cleanup();
138
- });
139
-
140
- test("findByPrompt matches across case and whitespace variants", () => {
141
- setup();
142
- appendRecord(tmp, makeRecord({ provenance: { provider: "x", prompt: "calm ambient piano" } }));
143
- assert.ok(findByPrompt(tmp, "Calm Ambient Piano", "bgm"), "case-insensitive");
144
- assert.ok(findByPrompt(tmp, " calm ambient piano ", "bgm"), "whitespace-insensitive");
145
- assert.equal(findByPrompt(tmp, "calm ambient guitar", "bgm"), null, "still a real miss");
146
- cleanup();
147
- });
148
-
149
- test("normalizePrompt trims, lowercases, collapses whitespace", () => {
150
- assert.equal(normalizePrompt(" Upbeat Tech Launch "), "upbeat tech launch");
151
- assert.equal(normalizePrompt(null), "");
152
- });
153
-
154
- test("allocateId reserves the id on disk so a pre-append caller can't reuse it (MU-23)", () => {
155
- setup();
156
- const a = allocateId(tmp, "bgm", ".wav");
157
- assert.equal(a.id, "bgm_001");
158
- assert.ok(existsSync(join(tmp, a.localPath)), "placeholder reserved on disk");
159
- // Second allocation BEFORE any manifest append (the download window) must not
160
- // hand back bgm_001 again, even with a different extension.
161
- const b = allocateId(tmp, "bgm", ".mp3");
162
- assert.equal(b.id, "bgm_002");
163
- assert.notEqual(a.localPath, b.localPath);
164
- // Lock file is released (not left behind).
165
- assert.ok(!existsSync(join(tmp, ".media", ".lock")), "lock released");
166
- cleanup();
167
- });
168
-
169
- test("allocateId continues past the highest manifest id", () => {
170
- setup();
171
- appendRecord(tmp, makeRecord({ id: "bgm_005" }));
172
- assert.equal(allocateId(tmp, "bgm", ".wav").id, "bgm_006");
173
- cleanup();
174
- });
175
-
176
- test("failed reservation rollback preserves another completed reservation", () => {
177
- setup();
178
- const kept = withReservedFileSync(tmp, "bgm", ".wav", (reservation) => {
179
- writeFileSync(reservation.fullPath, "completed asset");
180
- return reservation;
181
- });
182
-
183
- assert.throws(
184
- () =>
185
- withReservedFileSync(tmp, "bgm", ".wav", () => {
186
- throw new Error("populate failed");
187
- }),
188
- /populate failed/,
189
- );
190
-
191
- assert.equal(kept.id, "bgm_001");
192
- assert.equal(readFileSync(kept.fullPath, "utf8"), "completed asset");
193
- assert.deepStrictEqual(readdirSync(typeDirPath(tmp, "bgm")), ["bgm_001.wav"]);
194
- assert.equal(allocateId(tmp, "bgm", ".wav").id, "bgm_002");
195
- cleanup();
196
- });
197
-
198
- test("empty reservation result releases the placeholder", () => {
199
- setup();
200
- assert.equal(
201
- withReservedFileSync(tmp, "image", ".jpg", () => null),
202
- null,
203
- );
204
- assert.deepStrictEqual(readdirSync(typeDirPath(tmp, "image")), []);
205
- cleanup();
206
- });
207
-
208
- test("findByEntity matches case-insensitively", () => {
209
- setup();
210
- appendRecord(tmp, makeRecord({ entity: "GitHub", type: "icon" }));
211
- assert.ok(findByEntity(tmp, "github"));
212
- assert.ok(findByEntity(tmp, "GITHUB"));
213
- assert.equal(findByEntity(tmp, "gitlab"), null);
214
- cleanup();
215
- });
216
-
217
- test("nextId generates sequential ids", () => {
218
- setup();
219
- assert.equal(nextId(tmp, "bgm"), "bgm_001");
220
- appendRecord(tmp, makeRecord({ id: "bgm_001" }));
221
- assert.equal(nextId(tmp, "bgm"), "bgm_002");
222
- appendRecord(tmp, makeRecord({ id: "bgm_002" }));
223
- assert.equal(nextId(tmp, "bgm"), "bgm_003");
224
- cleanup();
225
- });
226
-
227
- // --- index-gen.mjs ---
228
-
229
- test("regenerateIndex produces plain-column table", () => {
230
- setup();
231
- appendRecord(tmp, makeRecord());
232
- regenerateIndex(tmp);
233
- const content = readFileSync(join(tmp, ".media", "index.md"), "utf8");
234
- assert.ok(content.includes("# .media · 1 asset"));
235
- assert.ok(content.includes("bgm_001"));
236
- assert.ok(content.includes("soft minimal ambient"));
237
- assert.ok(content.includes("11s"));
238
- cleanup();
239
- });
240
-
241
- test("regenerateIndex handles empty manifest", () => {
242
- setup();
243
- mkdirSync(join(tmp, ".media"), { recursive: true });
244
- writeFileSync(manifestPath(tmp), "");
245
- regenerateIndex(tmp);
246
- const content = readFileSync(join(tmp, ".media", "index.md"), "utf8");
247
- assert.ok(content.includes("# .media · 0 assets"));
248
- cleanup();
249
- });
250
-
251
- test("generateIndexContent includes dims for images", () => {
252
- const records = [
253
- makeRecord({ id: "img_001", type: "image", width: 1920, height: 1080, duration: null }),
254
- ];
255
- const content = generateIndexContent(records);
256
- assert.ok(content.includes("1920×1080"));
257
- assert.ok(content.includes("img_001"));
258
- });
259
-
260
- test("regenerateIndex matches manifest content after multiple writes", () => {
261
- setup();
262
- appendRecord(tmp, makeRecord({ id: "bgm_001" }));
263
- appendRecord(
264
- tmp,
265
- makeRecord({ id: "sfx_001", type: "sfx", description: "whoosh", duration: 3 }),
266
- );
267
- regenerateIndex(tmp);
268
- const content = readFileSync(join(tmp, ".media", "index.md"), "utf8");
269
- assert.ok(content.includes("# .media · 2 assets"));
270
- assert.ok(content.includes("bgm_001"));
271
- assert.ok(content.includes("sfx_001"));
272
- assert.ok(content.includes("whoosh"));
273
- cleanup();
274
- });
275
-
276
- // --- cache.mjs ---
277
-
278
- test("cacheGet returns null when cache is empty", () => {
279
- const result = cacheGet("nonexistent prompt", "bgm");
280
- assert.equal(result, null);
281
- });
282
-
283
- test("cachePut + cacheGet round-trip", () => {
284
- setup();
285
- const filePath = join(tmp, "test.wav");
286
- writeFileSync(filePath, "fake audio bytes for testing");
287
- const record = makeRecord({ provenance: { prompt: "cache test" } });
288
-
289
- const { sha } = cachePut(filePath, record);
290
- assert.ok(sha);
291
- assert.equal(sha.length, 64);
292
-
293
- const found = cacheGet("cache test", "bgm");
294
- assert.ok(found);
295
- assert.equal(found.reusable, true);
296
- assert.equal(found.sha, sha);
297
-
298
- // cross-project reuse must survive trivial prompt variation, not just
299
- // byte-identical intents (the whole point of normalizePrompt).
300
- assert.ok(cacheGet(" Cache Test ", "bgm"), "cacheGet is case/whitespace-insensitive");
301
- cleanup();
302
- });
303
-
304
- test("cacheGetByEntity finds cached asset", () => {
305
- setup();
306
- const filePath = join(tmp, "logo.png");
307
- writeFileSync(filePath, "fake png bytes");
308
- const record = makeRecord({
309
- type: "icon",
310
- entity: "TestCorp",
311
- provenance: { prompt: "TestCorp logo" },
312
- });
313
-
314
- cachePut(filePath, record);
315
- const found = cacheGetByEntity("testcorp");
316
- assert.ok(found);
317
- assert.equal(found.entity, "TestCorp");
318
- cleanup();
319
- });
320
-
321
- test("contentHash is deterministic", () => {
322
- setup();
323
- const filePath = join(tmp, "det.bin");
324
- writeFileSync(filePath, "deterministic content");
325
- const h1 = contentHash(filePath);
326
- const h2 = contentHash(filePath);
327
- assert.equal(h1, h2);
328
- cleanup();
329
- });
330
-
331
- test("promote copies project asset to global cache", () => {
332
- setup();
333
- const record = makeRecord();
334
- appendRecord(tmp, record);
335
- const filePath = join(tmp, record.path);
336
- mkdirSync(join(filePath, ".."), { recursive: true });
337
- writeFileSync(filePath, "promotable audio data");
338
-
339
- const { sha } = promote(tmp, "bgm_001");
340
- assert.ok(sha);
341
-
342
- const cached = cacheGet("subtle tech", "bgm");
343
- assert.ok(cached);
344
- assert.equal(cached.sha, sha);
345
- cleanup();
346
- });
347
-
348
- test("importFromCache copies cached file into project", () => {
349
- setup();
350
- const filePath = join(tmp, "source.wav");
351
- writeFileSync(filePath, "importable audio");
352
- const record = makeRecord({ provenance: { prompt: "import test" } });
353
- const { sha } = cachePut(filePath, record);
354
-
355
- const cached = cacheGet("import test", "bgm");
356
- const projectDir = mkdtempSync(join(tmpdir(), "mu-import-"));
357
- const imported = importFromCache(cached, projectDir, "bgm_001", ".media/audio/bgm/bgm_001.wav");
358
-
359
- assert.ok(imported);
360
- assert.equal(imported.id, "bgm_001");
361
- assert.equal(imported.provenance.imported_from, sha);
362
- assert.ok(existsSync(join(projectDir, ".media/audio/bgm/bgm_001.wav")));
363
-
364
- rmSync(projectDir, { recursive: true, force: true });
365
- cleanup();
366
- });
367
-
368
- // --- run ---
369
-
370
- let passed = 0;
371
- let failed = 0;
372
- for (const { name, fn } of tests) {
373
- try {
374
- fn();
375
- passed++;
376
- console.log(` \x1b[32m✓\x1b[0m ${name}`);
377
- } catch (err) {
378
- failed++;
379
- console.log(` \x1b[31m✗\x1b[0m ${name}`);
380
- console.log(` ${err.message}`);
381
- }
382
- }
383
- console.log(`\n${passed} passed, ${failed} failed`);
384
- if (failed > 0) process.exit(1);
385
- }
386
-
387
- console.log("media-use · manifest/index/cache tests\n");
388
- runTests();
@@ -1,47 +0,0 @@
1
- // Shared lexical-matching helpers used by both the assets/ scan (adopt.mjs) and
2
- // the reuse-candidate ranker (candidates.mjs), and the type-equivalence check
3
- // used by resolve.mjs and candidates.mjs. Kept in one place so the icon<->image
4
- // equivalence and the token rules can't drift between the "do" path (resolve)
5
- // and the "look" path (candidates).
6
-
7
- // Common filler words that should never, on their own, make two strings match.
8
- const MATCH_STOPWORDS = new Set([
9
- "the",
10
- "and",
11
- "for",
12
- "with",
13
- "from",
14
- "this",
15
- "that",
16
- "your",
17
- "our",
18
- ]);
19
-
20
- // Split into lowercased word tokens of length >= 3, minus stopwords.
21
- export function matchTokens(text) {
22
- return new Set(
23
- String(text)
24
- .toLowerCase()
25
- .split(/[^a-z0-9]+/)
26
- .filter((t) => t.length >= 3 && !MATCH_STOPWORDS.has(t)),
27
- );
28
- }
29
-
30
- // Count of shared meaningful word tokens between two strings. 0 = no lexical
31
- // overlap (the candidate ranker still surfaces these, ordered after overlaps).
32
- export function tokenOverlap(a, b) {
33
- const ta = matchTokens(a);
34
- const tb = matchTokens(b);
35
- let n = 0;
36
- for (const t of ta) if (tb.has(t)) n++;
37
- return n;
38
- }
39
-
40
- // icon, image, and logo are interchangeable: all live in images/, and
41
- // figma-imported brand marks are recorded as type image while agents ask for
42
- // logos as icon or logo.
43
- export function typesMatch(a, b) {
44
- if (a === b) return true;
45
- const visual = new Set(["icon", "image", "logo"]);
46
- return visual.has(a) && visual.has(b);
47
- }