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,373 +0,0 @@
1
- import { strict as assert } from "node:assert";
2
- import { test } from "node:test";
3
- import {
4
- existsSync,
5
- mkdirSync,
6
- mkdtempSync,
7
- readFileSync,
8
- rmSync,
9
- writeFileSync,
10
- chmodSync,
11
- } from "node:fs";
12
- import { tmpdir } from "node:os";
13
- import { join } from "node:path";
14
- import { __anonymousIdForTest, __resetTelemetryForTest, optedOut, track } from "./telemetry.mjs";
15
-
16
- function sandbox() {
17
- const root = mkdtempSync(join(tmpdir(), "mu-telemetry-"));
18
- const home = join(root, "home");
19
- mkdirSync(home, { recursive: true });
20
- process.env.HOME = home;
21
- return { root, home };
22
- }
23
-
24
- function restoreEnv(saved) {
25
- for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
26
- Object.assign(process.env, saved);
27
- }
28
-
29
- function withoutTelemetryOptOut() {
30
- for (const k of ["DO_NOT_TRACK", "HYPERFRAMES_NO_TELEMETRY", "CI", "NODE_ENV"])
31
- delete process.env[k];
32
- }
33
-
34
- function parseFetchBodies(calls) {
35
- return calls.flatMap((call) => JSON.parse(call.options.body).batch);
36
- }
37
-
38
- test("optedOut respects DO_NOT_TRACK / HYPERFRAMES_NO_TELEMETRY / CI", () => {
39
- const saved = { ...process.env };
40
- try {
41
- for (const k of ["DO_NOT_TRACK", "HYPERFRAMES_NO_TELEMETRY", "CI", "NODE_ENV"])
42
- delete process.env[k];
43
- assert.equal(optedOut(), false, "default: tracking allowed");
44
- process.env.DO_NOT_TRACK = "1";
45
- assert.equal(optedOut(), true, "DO_NOT_TRACK opts out");
46
- delete process.env.DO_NOT_TRACK;
47
- process.env.HYPERFRAMES_NO_TELEMETRY = "1";
48
- assert.equal(optedOut(), true, "HYPERFRAMES_NO_TELEMETRY opts out");
49
- delete process.env.HYPERFRAMES_NO_TELEMETRY;
50
- process.env.CI = "true";
51
- assert.equal(optedOut(), true, "CI opts out");
52
- } finally {
53
- for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
54
- Object.assign(process.env, saved);
55
- }
56
- });
57
-
58
- test("track is a no-op (no network, resolves) when opted out", async () => {
59
- const savedEnv = { ...process.env };
60
- const originalFetch = globalThis.fetch;
61
- const { root, home } = sandbox();
62
- const calls = [];
63
- globalThis.fetch = async (...args) => {
64
- calls.push(args);
65
- return { ok: true };
66
- };
67
- process.env.DO_NOT_TRACK = "1";
68
- try {
69
- // must resolve immediately without throwing or hitting the network
70
- await track("media_use_resolve", { type: "bgm", source: "search" });
71
- assert.equal(calls.length, 0);
72
- assert.equal(existsSync(join(home, ".hyperframes/config.json")), false);
73
- assert.equal(existsSync(join(home, ".media/telemetry-notice-shown")), false);
74
- } finally {
75
- globalThis.fetch = originalFetch;
76
- restoreEnv(savedEnv);
77
- rmSync(root, { recursive: true, force: true });
78
- __resetTelemetryForTest();
79
- }
80
- });
81
-
82
- test("anonymous id uses the shared hyperframes config", () => {
83
- const savedEnv = { ...process.env };
84
- const { root, home } = sandbox();
85
- try {
86
- withoutTelemetryOptOut();
87
- mkdirSync(join(home, ".hyperframes"), { recursive: true });
88
- writeFileSync(
89
- join(home, ".hyperframes/config.json"),
90
- JSON.stringify({ anonymousId: "shared-install-id", keep: true }),
91
- );
92
- mkdirSync(join(home, ".media"), { recursive: true });
93
- writeFileSync(join(home, ".media/anon-id"), "old-media-id");
94
-
95
- assert.equal(__anonymousIdForTest(), "shared-install-id");
96
- } finally {
97
- restoreEnv(savedEnv);
98
- rmSync(root, { recursive: true, force: true });
99
- __resetTelemetryForTest();
100
- }
101
- });
102
-
103
- test("anonymous id seeds missing config once and reuses it", () => {
104
- const savedEnv = { ...process.env };
105
- const { root, home } = sandbox();
106
- try {
107
- withoutTelemetryOptOut();
108
- const first = __anonymousIdForTest();
109
- const configPath = join(home, ".hyperframes/config.json");
110
- assert.ok(existsSync(configPath));
111
- assert.match(
112
- first,
113
- /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
114
- );
115
- const second = __anonymousIdForTest();
116
- assert.equal(second, first);
117
- assert.equal(JSON.parse(readFileSync(configPath, "utf8")).anonymousId, first);
118
- } finally {
119
- restoreEnv(savedEnv);
120
- rmSync(root, { recursive: true, force: true });
121
- __resetTelemetryForTest();
122
- }
123
- });
124
-
125
- test("anonymous id adopts a legacy ~/.media/anon-id on upgrade (persona continuity)", () => {
126
- const savedEnv = { ...process.env };
127
- const { root, home } = sandbox();
128
- try {
129
- withoutTelemetryOptOut();
130
- mkdirSync(join(home, ".media"), { recursive: true });
131
- writeFileSync(join(home, ".media/anon-id"), "legacy-media-id");
132
- // no ~/.hyperframes/config.json yet — the old media-use-only id must carry over
133
- assert.equal(__anonymousIdForTest(), "legacy-media-id");
134
- // and it is persisted into the shared config so CLI/studio see the same id
135
- assert.equal(
136
- JSON.parse(readFileSync(join(home, ".hyperframes/config.json"), "utf8")).anonymousId,
137
- "legacy-media-id",
138
- );
139
- } finally {
140
- restoreEnv(savedEnv);
141
- rmSync(root, { recursive: true, force: true });
142
- __resetTelemetryForTest();
143
- }
144
- });
145
-
146
- test("track identifies a signed-in HeyGen account once and still sends events", async () => {
147
- const savedEnv = { ...process.env };
148
- const originalFetch = globalThis.fetch;
149
- const { root, home } = sandbox();
150
- const calls = [];
151
- globalThis.fetch = async (url, options) => {
152
- calls.push({ url, options });
153
- return { ok: true };
154
- };
155
- try {
156
- withoutTelemetryOptOut();
157
- mkdirSync(join(home, ".hyperframes"), { recursive: true });
158
- writeFileSync(
159
- join(home, ".hyperframes/config.json"),
160
- JSON.stringify({ anonymousId: "anon-1" }),
161
- );
162
- mkdirSync(join(home, ".heygen"), { recursive: true });
163
- writeFileSync(
164
- join(home, ".heygen/credentials"),
165
- JSON.stringify({ user: { email: "alice@example.com", username: "alice" } }),
166
- );
167
-
168
- await track("media_use_resolve", { type: "bgm", source: "search" });
169
- await track("media_use_resolve", { type: "image", source: "generated" });
170
-
171
- const batch = parseFetchBodies(calls);
172
- const identify = batch.filter((item) => item.event === "$identify");
173
- assert.equal(identify.length, 1);
174
- assert.equal(identify[0].distinct_id, "alice@example.com");
175
- assert.equal(identify[0].properties.$anon_distinct_id, "anon-1");
176
- assert.equal(batch.filter((item) => item.event === "media_use_resolve").length, 2);
177
- } finally {
178
- globalThis.fetch = originalFetch;
179
- restoreEnv(savedEnv);
180
- rmSync(root, { recursive: true, force: true });
181
- __resetTelemetryForTest();
182
- }
183
- });
184
-
185
- test("track identifies with a lowercased email regardless of stored casing", async () => {
186
- const savedEnv = { ...process.env };
187
- const originalFetch = globalThis.fetch;
188
- const { root, home } = sandbox();
189
- const calls = [];
190
- globalThis.fetch = async (url, options) => {
191
- calls.push({ url, options });
192
- return { ok: true };
193
- };
194
- try {
195
- withoutTelemetryOptOut();
196
- mkdirSync(join(home, ".hyperframes"), { recursive: true });
197
- writeFileSync(
198
- join(home, ".hyperframes/config.json"),
199
- JSON.stringify({ anonymousId: "anon-3" }),
200
- );
201
- mkdirSync(join(home, ".heygen"), { recursive: true });
202
- writeFileSync(
203
- join(home, ".heygen/credentials"),
204
- JSON.stringify({ user: { email: "Alice@Example.com", username: "alice" } }),
205
- );
206
-
207
- await track("media_use_resolve", { type: "bgm", source: "search" });
208
-
209
- const batch = parseFetchBodies(calls);
210
- const identify = batch.filter((item) => item.event === "$identify");
211
- assert.equal(identify.length, 1);
212
- // Lowercased so this joins with heygen-cli's own identify call regardless
213
- // of the account's stored email casing -- otherwise the same person could
214
- // split into two PostHog profiles by email case alone.
215
- assert.equal(identify[0].distinct_id, "alice@example.com");
216
- } finally {
217
- globalThis.fetch = originalFetch;
218
- restoreEnv(savedEnv);
219
- rmSync(root, { recursive: true, force: true });
220
- __resetTelemetryForTest();
221
- }
222
- });
223
-
224
- test("track does not identify when signed out", async () => {
225
- const savedEnv = { ...process.env };
226
- const originalFetch = globalThis.fetch;
227
- const { root, home } = sandbox();
228
- const calls = [];
229
- globalThis.fetch = async (url, options) => {
230
- calls.push({ url, options });
231
- return { ok: true };
232
- };
233
- try {
234
- withoutTelemetryOptOut();
235
- mkdirSync(join(home, ".hyperframes"), { recursive: true });
236
- writeFileSync(
237
- join(home, ".hyperframes/config.json"),
238
- JSON.stringify({ anonymousId: "anon-2" }),
239
- );
240
-
241
- await track("media_use_resolve", { type: "bgm", source: "search" });
242
-
243
- const batch = parseFetchBodies(calls);
244
- assert.equal(
245
- batch.some((item) => item.event === "$identify"),
246
- false,
247
- );
248
- assert.equal(batch[0].event, "media_use_resolve");
249
- assert.equal(batch[0].distinct_id, "anon-2");
250
- } finally {
251
- globalThis.fetch = originalFetch;
252
- restoreEnv(savedEnv);
253
- rmSync(root, { recursive: true, force: true });
254
- __resetTelemetryForTest();
255
- }
256
- });
257
-
258
- test("first run notice prints to stderr once and never stdout", async () => {
259
- const savedEnv = { ...process.env };
260
- const originalFetch = globalThis.fetch;
261
- const originalError = console.error;
262
- const originalLog = console.log;
263
- const { root, home } = sandbox();
264
- const stderr = [];
265
- const stdout = [];
266
- globalThis.fetch = async () => ({ ok: true });
267
- console.error = (...args) => stderr.push(args.join(" "));
268
- console.log = (...args) => stdout.push(args.join(" "));
269
- try {
270
- withoutTelemetryOptOut();
271
- await track("media_use_resolve", { type: "bgm" });
272
- await track("media_use_resolve", { type: "sfx" });
273
-
274
- assert.equal(stderr.length, 1);
275
- assert.match(stderr[0], /media-use sends usage telemetry/);
276
- assert.equal(stdout.length, 0);
277
- // notice-shown lives in the shared config (config.telemetryNoticeShown), so
278
- // the CLI and media-use show it once per person — not a media-use-only marker.
279
- assert.equal(
280
- JSON.parse(readFileSync(join(home, ".hyperframes/config.json"), "utf8")).telemetryNoticeShown,
281
- true,
282
- );
283
- } finally {
284
- globalThis.fetch = originalFetch;
285
- console.error = originalError;
286
- console.log = originalLog;
287
- restoreEnv(savedEnv);
288
- rmSync(root, { recursive: true, force: true });
289
- __resetTelemetryForTest();
290
- }
291
- });
292
-
293
- test("warns once on stderr when MEDIA_USE_TELEMETRY_HOST is set outside a test/CI context", async () => {
294
- const savedEnv = { ...process.env };
295
- const originalFetch = globalThis.fetch;
296
- const originalError = console.error;
297
- const { root } = sandbox();
298
- const stderr = [];
299
- globalThis.fetch = async () => ({ ok: true });
300
- console.error = (...args) => stderr.push(args.join(" "));
301
- try {
302
- // No opt-out, no CI/NODE_ENV signal — mirrors a real user's shell where
303
- // this test-only var leaked in by accident and tracking is not disabled.
304
- withoutTelemetryOptOut();
305
- process.env.MEDIA_USE_TELEMETRY_HOST = "http://127.0.0.1:1"; // nothing listens; irrelevant here
306
- await track("media_use_resolve", { type: "bgm" });
307
- await track("media_use_resolve", { type: "bgm" });
308
-
309
- const warnings = stderr.filter((line) => line.includes("MEDIA_USE_TELEMETRY_HOST"));
310
- assert.equal(warnings.length, 1, "expected exactly one warning across repeated calls");
311
- } finally {
312
- globalThis.fetch = originalFetch;
313
- console.error = originalError;
314
- restoreEnv(savedEnv);
315
- rmSync(root, { recursive: true, force: true });
316
- __resetTelemetryForTest();
317
- }
318
- });
319
-
320
- test("does not warn when MEDIA_USE_TELEMETRY_HOST is set the way the U7 interception test sets it", async () => {
321
- const savedEnv = { ...process.env };
322
- const originalFetch = globalThis.fetch;
323
- const originalError = console.error;
324
- const { root } = sandbox();
325
- const stderr = [];
326
- globalThis.fetch = async () => ({ ok: true });
327
- console.error = (...args) => stderr.push(args.join(" "));
328
- try {
329
- withoutTelemetryOptOut();
330
- // Same env shape resolve.test.mjs's U7 test uses to allow tracking through:
331
- // DO_NOT_TRACK=0, CI unset/empty, NODE_ENV=test.
332
- process.env.DO_NOT_TRACK = "0";
333
- process.env.CI = "";
334
- process.env.NODE_ENV = "test";
335
- process.env.MEDIA_USE_TELEMETRY_HOST = "http://127.0.0.1:1";
336
- await track("media_use_resolve", { type: "bgm" });
337
-
338
- assert.equal(
339
- stderr.some((line) => line.includes("MEDIA_USE_TELEMETRY_HOST")),
340
- false,
341
- "the U7 test's own use of the override must not print a spurious warning",
342
- );
343
- } finally {
344
- globalThis.fetch = originalFetch;
345
- console.error = originalError;
346
- restoreEnv(savedEnv);
347
- rmSync(root, { recursive: true, force: true });
348
- __resetTelemetryForTest();
349
- }
350
- });
351
-
352
- test("read-only telemetry state degrades without throwing", async () => {
353
- const savedEnv = { ...process.env };
354
- const originalFetch = globalThis.fetch;
355
- const { root, home } = sandbox();
356
- globalThis.fetch = async () => ({ ok: true });
357
- try {
358
- withoutTelemetryOptOut();
359
- writeFileSync(join(home, ".hyperframes"), "not a directory");
360
- writeFileSync(join(home, ".media"), "not a directory");
361
- await track("media_use_resolve", { type: "bgm" });
362
- } finally {
363
- globalThis.fetch = originalFetch;
364
- try {
365
- chmodSync(join(home, ".hyperframes"), 0o600);
366
- } catch {
367
- // best effort for cleanup on platforms with different chmod behavior
368
- }
369
- restoreEnv(savedEnv);
370
- rmSync(root, { recursive: true, force: true });
371
- __resetTelemetryForTest();
372
- }
373
- });
@@ -1,92 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { existsSync, statSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { resolveSpawnCommand } from "../../audio/scripts/lib/tts.mjs";
6
-
7
- // Local voiceover via the packaged Kokoro-82M TTS (the `hyperframes tts` CLI),
8
- // the free/private default now that HeyGen TTS costs wallet credits. Kokoro runs
9
- // on-device (CPU, faster-than-realtime, bundled voices, native word timestamps),
10
- // so no key and no per-call charge. When Kokoro is not set up, this returns null
11
- // and the registry falls through to the HeyGen TTS upsell.
12
- //
13
- // Delegated to the hyperframes CLI (same as transcribe / remove-background), not
14
- // re-implemented here. ffprobe reads the duration back for the ledger.
15
-
16
- function probeDurationSeconds(file) {
17
- try {
18
- const out = execFileSync(
19
- "ffprobe",
20
- ["-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", "--", file],
21
- { encoding: "utf8", timeout: 15000 },
22
- );
23
- const d = parseFloat(String(out).trim());
24
- return Number.isFinite(d) ? d : undefined;
25
- } catch {
26
- return undefined;
27
- }
28
- }
29
-
30
- // `platform`/`execFn`/`env`/`pathExists` params (defaulting to the real
31
- // values) exist so tests can exercise the win32 branch without mocking
32
- // node:child_process (its ESM exports are non-configurable) — same idiom as
33
- // spawnP in ../../audio/scripts/lib/tts.mjs.
34
- export async function localTtsGenerate(
35
- intent,
36
- ctx,
37
- platform = process.platform,
38
- execFn = execFileSync,
39
- env = process.env,
40
- pathExists = existsSync,
41
- ) {
42
- const outPath = join(tmpdir(), `media-use-kokoro-${process.pid}-${Date.now()}.wav`);
43
- const argv = ["hyperframes", "tts", intent, "--output", outPath];
44
- if (ctx?.voice) argv.push("--voice", ctx.voice);
45
- if (ctx?.lang && ctx.lang !== "en") argv.push("--lang", ctx.lang);
46
- // On Windows a bare "npx" is npx.cmd, which execFileSync cannot exec
47
- // (spawnSync npx ENOENT) — resolveSpawnCommand reroutes it through
48
- // node + npx-cli.js, same as the audio engine's TTS spawns.
49
- const resolved = resolveSpawnCommand(
50
- "npx",
51
- argv,
52
- { encoding: "utf8", timeout: 300000, stdio: ["ignore", "pipe", "pipe"] },
53
- platform,
54
- env,
55
- pathExists,
56
- );
57
- if (!resolved) {
58
- // npx-on-win32 with no resolvable npx-cli.js — same terminal condition
59
- // spawnP warns about. Fall through to the next provider rather than crash.
60
- console.error(
61
- "media-use: local voice not enabled (kokoro). Cannot run npx on Windows: " +
62
- "npm's npx-cli.js was not found (install npm with Node, or run via npx/npm run so npm_execpath is set).",
63
- );
64
- return null;
65
- }
66
- try {
67
- execFn(resolved.cmd, resolved.args, resolved.opts);
68
- } catch (err) {
69
- // `hyperframes tts` prints its "kokoro-onnx not installed" hint to stdout
70
- // (clack UI), so read both streams and surface the actionable enable-command
71
- // rather than a bare "Command failed": otherwise resolve silently falls
72
- // through to the PAID HeyGen TTS upsell when free local voice was one pip away.
73
- const out = `${err.stdout?.toString() ?? ""}${err.stderr?.toString() ?? ""}`.trim();
74
- const hint = /not installed|pip install kokoro/i.test(out)
75
- ? "install for free on-device voice: pip install kokoro-onnx soundfile (or set HYPERFRAMES_PYTHON to a venv that has it)"
76
- : out.slice(-200) || err.message;
77
- console.error(`media-use: local voice not enabled (kokoro). ${hint}`);
78
- return null;
79
- }
80
- if (!existsSync(outPath) || statSync(outPath).size === 0) return null;
81
- return {
82
- localPath: outPath,
83
- ext: ".wav",
84
- source: "generated",
85
- metadata: {
86
- description: intent,
87
- provider: "kokoro.local",
88
- duration: probeDurationSeconds(outPath),
89
- provenance: { engine: "kokoro-82m", prompt: intent },
90
- },
91
- };
92
- }
@@ -1,72 +0,0 @@
1
- import { test } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { localTtsGenerate } from "./tts-local-provider.mjs";
4
-
5
- // Regression: on Windows, a bare "npx" is npx.cmd, which execFileSync cannot
6
- // exec — the Kokoro delegation failed with `spawnSync npx ENOENT` instead of
7
- // synthesizing (or cleanly falling through to the next provider). The spawn
8
- // must route through node + npx-cli.js on win32, same as the audio engine's
9
- // TTS spawns (see ../../audio/scripts/lib/tts.spawn.test.mjs).
10
-
11
- const envWithNpxCli = {
12
- npm_execpath: "C:/Program Files/nodejs/node_modules/npm/bin/npm-cli.js",
13
- npm_node_execpath: "C:/Program Files/nodejs/node.exe",
14
- };
15
- const npxCliPath = "C:/Program Files/nodejs/node_modules/npm/bin/npx-cli.js";
16
- const pathExists = (path) => path === npxCliPath;
17
-
18
- test("win32: routes the hyperframes tts call through node + npx-cli, never bare npx", async () => {
19
- const captured = [];
20
- const fakeExec = (cmd, args, opts) => {
21
- captured.push({ cmd, args, opts });
22
- // Synthesize nothing — the provider then returns null via the
23
- // missing-output check, which is fine: we only assert the spawn shape.
24
- };
25
-
26
- await localTtsGenerate(
27
- "hello there",
28
- { voice: "am_michael" },
29
- "win32",
30
- fakeExec,
31
- envWithNpxCli,
32
- pathExists,
33
- );
34
-
35
- assert.equal(captured.length, 1);
36
- assert.equal(captured[0].cmd, envWithNpxCli.npm_node_execpath);
37
- assert.equal(captured[0].args[0], npxCliPath);
38
- assert.deepEqual(captured[0].args.slice(1, 4), ["hyperframes", "tts", "hello there"]);
39
- assert.ok(captured[0].args.includes("--voice"));
40
- // execFileSync options survive the rerouting (pipes are what let the caller
41
- // read the "kokoro-onnx not installed" hint back out).
42
- assert.deepEqual(captured[0].opts.stdio, ["ignore", "pipe", "pipe"]);
43
- });
44
-
45
- test("win32 without a resolvable npx-cli: falls through to the next provider (null), no spawn", async () => {
46
- const captured = [];
47
- const fakeExec = (...call) => captured.push(call);
48
-
49
- const result = await localTtsGenerate(
50
- "hello",
51
- {},
52
- "win32",
53
- fakeExec,
54
- {}, // no npm_execpath, and pathExists finds nothing
55
- () => false,
56
- );
57
-
58
- assert.equal(result, null);
59
- assert.equal(captured.length, 0);
60
- });
61
-
62
- test("non-win32: spawns plain npx unchanged", async () => {
63
- const captured = [];
64
- const fakeExec = (cmd, args) => captured.push({ cmd, args });
65
-
66
- await localTtsGenerate("hola", { lang: "es" }, "darwin", fakeExec, {}, () => false);
67
-
68
- assert.equal(captured.length, 1);
69
- assert.equal(captured[0].cmd, "npx");
70
- assert.deepEqual(captured[0].args.slice(0, 3), ["hyperframes", "tts", "hola"]);
71
- assert.ok(captured[0].args.includes("--lang"));
72
- });
@@ -1,57 +0,0 @@
1
- import { readFileSync, readdirSync, existsSync } from "node:fs";
2
- import { join } from "node:path";
3
-
4
- // Asset in-use detection: which manifest assets are actually referenced by the
5
- // project's compositions. Answers "is this safe to prune?" from the CLI. (The
6
- // Studio Asset tab computes its in-use filter separately from the live timeline;
7
- // this is the skill-side equivalent for headless use.)
8
- //
9
- // ponytail: substring match of each asset's filename against the .html text
10
- // (covers src= / href= / url() / data-* without parsing HTML). False positives
11
- // only if a filename literally appears in prose; fine for a filter. Upgrade to
12
- // attribute parsing if that ever bites.
13
-
14
- function compositionHtml(projectDir) {
15
- let files = [];
16
- try {
17
- files = readdirSync(projectDir).filter((f) => f.endsWith(".html"));
18
- } catch {
19
- return "";
20
- }
21
- const sub = join(projectDir, "compositions");
22
- if (existsSync(sub)) {
23
- try {
24
- for (const f of readdirSync(sub))
25
- if (f.endsWith(".html")) files.push(join("compositions", f));
26
- } catch {
27
- // ignore unreadable subdir
28
- }
29
- }
30
- return files
31
- .map((f) => {
32
- try {
33
- return readFileSync(join(projectDir, f), "utf8");
34
- } catch {
35
- return "";
36
- }
37
- })
38
- .join("\n");
39
- }
40
-
41
- /** Tag each record with `inUse` (referenced by some composition). */
42
- export function tagUsage(records, projectDir) {
43
- const html = compositionHtml(projectDir);
44
- return records.map((r) => {
45
- const file = r.path ? r.path.split("/").pop() : r.id;
46
- return { ...r, inUse: Boolean(file) && html.includes(file) };
47
- });
48
- }
49
-
50
- /** Split records into { used, unused } for the filter. */
51
- export function partitionUsage(records, projectDir) {
52
- const tagged = tagUsage(records, projectDir);
53
- return {
54
- used: tagged.filter((r) => r.inUse),
55
- unused: tagged.filter((r) => !r.inUse),
56
- };
57
- }
@@ -1,54 +0,0 @@
1
- import { strict as assert } from "node:assert";
2
- import { test } from "node:test";
3
- import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { tagUsage, partitionUsage } from "./usage.mjs";
7
-
8
- function project(html) {
9
- const dir = mkdtempSync(join(tmpdir(), "mu-usage-"));
10
- writeFileSync(join(dir, "index.html"), html);
11
- return dir;
12
- }
13
-
14
- const records = [
15
- { id: "bgm_001", path: ".media/audio/bgm/bgm_001.wav", description: "used track" },
16
- { id: "bgm_002", path: ".media/audio/bgm/bgm_002.wav", description: "orphan track" },
17
- ];
18
-
19
- test("an asset referenced by a composition is marked in-use", () => {
20
- const dir = project(`<audio src="assets/bgm_001.wav"></audio>`);
21
- try {
22
- const tagged = tagUsage(records, dir);
23
- assert.equal(tagged.find((r) => r.id === "bgm_001").inUse, true);
24
- assert.equal(tagged.find((r) => r.id === "bgm_002").inUse, false);
25
- } finally {
26
- rmSync(dir, { recursive: true, force: true });
27
- }
28
- });
29
-
30
- test("partitionUsage splits used vs unused for the filter", () => {
31
- const dir = project(`<img src="bgm_001.wav">`);
32
- try {
33
- const { used, unused } = partitionUsage(records, dir);
34
- assert.deepEqual(
35
- used.map((r) => r.id),
36
- ["bgm_001"],
37
- );
38
- assert.deepEqual(
39
- unused.map((r) => r.id),
40
- ["bgm_002"],
41
- );
42
- } finally {
43
- rmSync(dir, { recursive: true, force: true });
44
- }
45
- });
46
-
47
- test("no compositions -> everything reads as unused (safe default)", () => {
48
- const dir = mkdtempSync(join(tmpdir(), "mu-usage-"));
49
- try {
50
- assert.equal(partitionUsage(records, dir).used.length, 0);
51
- } finally {
52
- rmSync(dir, { recursive: true, force: true });
53
- }
54
- });