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
@@ -12,7 +12,7 @@ import {
12
12
  } from "node:fs";
13
13
  import { homedir, tmpdir } from "node:os";
14
14
  import { basename, extname, join, resolve } from "node:path";
15
- import { parseArgs } from "node:util";
15
+ import { parseArgs, stripVTControlCharacters } from "node:util";
16
16
  import { mergeTokensToWords } from "./lib/parakeet-words.mjs";
17
17
  import { track } from "./lib/telemetry.mjs";
18
18
  import { resolveNpxInvocation } from "./lib/npx-sync.mjs";
@@ -49,7 +49,8 @@ Usage:
49
49
 
50
50
  Parakeet (default) beats whisper.cpp on accuracy + speed for English/European
51
51
  languages; whisper.cpp (99 languages) is the fallback. Install Parakeet once:
52
- uv venv ~/.venvs/parakeet && VIRTUAL_ENV=~/.venvs/parakeet uv pip install parakeet-mlx`);
52
+ npx hyperframes models install parakeet (macOS, Windows, Linux with glibc 2.32+)
53
+ uv venv ~/.venvs/parakeet && VIRTUAL_ENV=~/.venvs/parakeet uv pip install parakeet-mlx (Apple Silicon)`);
53
54
  process.exit(0);
54
55
  }
55
56
 
@@ -122,33 +123,57 @@ function runParakeet(runner) {
122
123
  }
123
124
  }
124
125
 
125
- // whisper.cpp via the hyperframes CLI (fetched/built on first use — see
126
+ // The hyperframes CLI (its Parakeet, else whisper.cpp built on first use — see
126
127
  // SKILL.md): writes transcript.json into --dir; relocate to --out.
127
- function runWhisper() {
128
+ function runCli(cliEngine) {
128
129
  const workDir = mkdtempSync(join(tmpdir(), "media-use-whisper-"));
129
130
  try {
130
131
  // On Windows a bare "npx" is npx.cmd, which execFileSync cannot exec
131
132
  // (spawnSync npx ENOENT) — resolveNpxInvocation reroutes it through
132
133
  // node + npx-cli.js (and throws actionably when it can't), same
133
134
  // mechanism as the audio engine's TTS spawns.
135
+ // Under auto or parakeet the CLI may run its own Parakeet; --json says which engine ran.
134
136
  const resolved = resolveNpxInvocation(
135
- ["hyperframes", "transcribe", inputPath, "--dir", workDir],
137
+ ["hyperframes", "transcribe", inputPath, "--dir", workDir, "--engine", cliEngine, "--json"],
136
138
  { stdio: ["ignore", "pipe", "pipe"], timeout: 1_800_000 },
137
139
  );
138
- execFileSync(resolved.cmd, resolved.args, resolved.opts);
140
+ let stdout;
141
+ try {
142
+ stdout = String(execFileSync(resolved.cmd, resolved.args, resolved.opts));
143
+ } catch (e) {
144
+ // stderr holds spinner redraws and --json prints the reason on stdout, so keep the last lines.
145
+ const tail = stripVTControlCharacters(`${e.stderr ?? ""}\n${e.stdout ?? ""}`)
146
+ .split(/[\r\n]+/)
147
+ .map((l) => l.trim())
148
+ .filter((l) => l && l !== "│")
149
+ .slice(-3)
150
+ .join("\n");
151
+ throw new Error(
152
+ `hyperframes transcribe failed (exit ${e.status ?? e.code})${tail ? `:\n${tail}` : ""}`,
153
+ );
154
+ }
139
155
  const produced = join(workDir, "transcript.json");
140
- if (!existsSync(produced)) throw new Error("whisper produced no transcript.json");
156
+ if (!existsSync(produced))
157
+ throw new Error("hyperframes transcribe produced no transcript.json");
141
158
  const tmp = `${outPath}.tmp-${process.pid}`;
142
159
  copyFileSync(produced, tmp);
143
160
  renameSync(tmp, outPath); // atomic publish
144
161
  let words;
145
162
  try {
146
163
  const t = JSON.parse(readFileSync(outPath, "utf8"));
147
- words = Array.isArray(t?.words) ? t.words.length : undefined;
164
+ // The CLI writes a flat word array, not { words }.
165
+ words = Array.isArray(t) ? t.length : undefined;
148
166
  } catch {
149
167
  /* leave undefined */
150
168
  }
151
- report("whisper", words);
169
+ let ran = "whisper";
170
+ try {
171
+ ran = JSON.parse(stdout.trim().split("\n").at(-1)).engine === "parakeet" ? "parakeet" : ran;
172
+ } catch {
173
+ /* no JSON line: keep whisper */
174
+ }
175
+ report(ran, words);
176
+ return ran;
152
177
  } finally {
153
178
  rmSync(workDir, { recursive: true, force: true });
154
179
  }
@@ -156,22 +181,11 @@ function runWhisper() {
156
181
 
157
182
  try {
158
183
  const parakeetBin = resolveParakeet();
159
- const engine =
160
- args.engine === "parakeet" || args.engine === "whisper"
161
- ? args.engine
162
- : parakeetBin
163
- ? "parakeet"
164
- : "whisper";
165
- if (engine === "parakeet") {
166
- if (!parakeetBin) {
167
- throw new Error(
168
- "parakeet-mlx not found (checked $HYPERFRAMES_PARAKEET, ~/.venvs/parakeet, and PATH). Install: uv venv ~/.venvs/parakeet && VIRTUAL_ENV=~/.venvs/parakeet uv pip install parakeet-mlx (or use --engine whisper)",
169
- );
170
- }
171
- runParakeet(parakeetBin);
172
- } else {
173
- runWhisper();
174
- }
184
+ const asked = args.engine === "parakeet" || args.engine === "whisper" ? args.engine : "auto";
185
+ // Without parakeet-mlx, the CLI's own Parakeet (any OS, `hyperframes models install parakeet`).
186
+ let engine = "parakeet";
187
+ if (asked !== "whisper" && parakeetBin) runParakeet(parakeetBin);
188
+ else engine = runCli(asked);
175
189
  await track("media_use_transcribe", { engine });
176
190
  } catch (err) {
177
191
  if (args.json) console.log(JSON.stringify({ ok: false, error: err.message }));
@@ -0,0 +1,112 @@
1
+ import assert from "node:assert/strict";
2
+ import { execFileSync, spawnSync } from "node:child_process";
3
+ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import test from "node:test";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ // The stub stands in for `npx hyperframes transcribe <in> --dir <dir> ...`: it records its args,
10
+ // writes the CLI's flat array and prints the CLI's --json line naming `cliEngine`.
11
+ function runScript(engine, cliEngine) {
12
+ const root = mkdtempSync(join(tmpdir(), "media-use-transcribe-"));
13
+ try {
14
+ const bin = join(root, "bin");
15
+ mkdirSync(bin);
16
+ writeFileSync(
17
+ join(bin, "npx"),
18
+ `#!/bin/sh\necho "$@" > "${root}/args"\nwhile [ "$1" != "--dir" ]; do shift; done\n` +
19
+ `echo '[{"text":"a","start":0,"end":1},{"text":"b","start":1,"end":2}]' > "$2/transcript.json"\n` +
20
+ `echo '{"ok":true,"engine":"${cliEngine}"}'\n`,
21
+ );
22
+ chmodSync(join(bin, "npx"), 0o755);
23
+ const input = join(root, "in.wav");
24
+ writeFileSync(input, "");
25
+ const script = fileURLToPath(new URL("./transcribe.mjs", import.meta.url));
26
+ const env = { ...process.env, PATH: `${bin}:${process.env.PATH}`, HOME: root };
27
+ delete env.HYPERFRAMES_PARAKEET;
28
+ const out = execFileSync(
29
+ process.execPath,
30
+ [script, "--input", input, "--engine", engine, "--json"],
31
+ {
32
+ encoding: "utf8",
33
+ env: { ...env, HYPERFRAMES_MEDIA_HOME: join(root, "home"), HYPERFRAMES_NO_TELEMETRY: "1" },
34
+ },
35
+ );
36
+ return { result: JSON.parse(out.trim()), args: readFileSync(join(root, "args"), "utf8") };
37
+ } finally {
38
+ rmSync(root, { recursive: true, force: true });
39
+ }
40
+ }
41
+
42
+ test(
43
+ "--engine whisper makes the CLI run whisper and reports the flat transcript's word count",
44
+ { skip: process.platform === "win32" },
45
+ () => {
46
+ const { result, args } = runScript("whisper", "whisper");
47
+ assert.match(args, /--engine whisper --json/);
48
+ assert.equal(result.words, 2);
49
+ assert.equal(result.engine, "whisper");
50
+ },
51
+ );
52
+
53
+ test(
54
+ "auto reports the engine the CLI says it ran, Parakeet included",
55
+ { skip: process.platform === "win32" },
56
+ () => {
57
+ const { result, args } = runScript("auto", "parakeet");
58
+ assert.match(args, /--engine auto --json/);
59
+ assert.equal(result.engine, "parakeet");
60
+ },
61
+ );
62
+
63
+ test(
64
+ "--engine parakeet without parakeet-mlx asks the CLI for its Parakeet",
65
+ { skip: process.platform === "win32" },
66
+ () => {
67
+ const { result, args } = runScript("parakeet", "parakeet");
68
+ assert.match(args, /--engine parakeet --json/);
69
+ assert.equal(result.engine, "parakeet");
70
+ },
71
+ );
72
+
73
+ test(
74
+ "a failed --engine parakeet run reports the reason the CLI printed on stdout",
75
+ { skip: process.platform === "win32" },
76
+ () => {
77
+ const root = mkdtempSync(join(tmpdir(), "media-use-transcribe-"));
78
+ try {
79
+ const bin = join(root, "bin");
80
+ mkdirSync(bin);
81
+ const reason =
82
+ "Parakeet is not installed. Install it with: hyperframes models install parakeet";
83
+ writeFileSync(
84
+ join(bin, "npx"),
85
+ `#!/bin/sh\necho '{"ok":false,"error":"${reason}"}'\nexit 1\n`,
86
+ );
87
+ chmodSync(join(bin, "npx"), 0o755);
88
+ const input = join(root, "in.wav");
89
+ writeFileSync(input, "");
90
+ const script = fileURLToPath(new URL("./transcribe.mjs", import.meta.url));
91
+ const res = spawnSync(
92
+ process.execPath,
93
+ [script, "--input", input, "--engine", "parakeet", "--json"],
94
+ {
95
+ encoding: "utf8",
96
+ env: {
97
+ ...process.env,
98
+ HYPERFRAMES_PARAKEET: "",
99
+ PATH: `${bin}:${process.env.PATH}`,
100
+ HOME: root,
101
+ HYPERFRAMES_MEDIA_HOME: join(root, "home"),
102
+ HYPERFRAMES_NO_TELEMETRY: "1",
103
+ },
104
+ },
105
+ );
106
+ assert.equal(res.status, 1);
107
+ assert.match(JSON.parse(res.stdout.trim()).error, /hyperframes models install parakeet/);
108
+ } finally {
109
+ rmSync(root, { recursive: true, force: true });
110
+ }
111
+ },
112
+ );
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
6
6
  import { dirname, extname, join, resolve } from "node:path";
7
7
  import { parseArgs } from "node:util";
8
8
  import { compileCutList } from "./lib/cutlist.mjs";
9
+ import { fadeFilterFor } from "./lib/transcriptCutFade.mjs";
9
10
  import { track } from "./lib/telemetry.mjs";
10
11
 
11
12
  const { values: args } = parseArgs({
@@ -92,11 +93,23 @@ function run() {
92
93
 
93
94
  try {
94
95
  const parts = segments.map((segment, index) => {
95
- const out = join(
96
- tmpDir,
97
- `segment-${String(index).padStart(4, "0")}${extname(outPath) || ".mp4"}`,
98
- );
99
- cutSegment(inputPath, segment, out, Boolean(args.copy));
96
+ // Intermediates carry PCM audio, not the final codec. Encoding each
97
+ // segment to a lossy codec separately makes the encoder pad every segment
98
+ // with priming silence (~25-35ms for AAC), which concat then bakes in as a
99
+ // gap at each cut -- a defect distinct from, and surviving, the fades
100
+ // below. PCM has no priming, so audio is encoded exactly once, at concat.
101
+ const ext = args.copy ? extname(outPath) || ".mp4" : isAudioOnly(outPath) ? ".wav" : ".mkv";
102
+ const out = join(tmpDir, `segment-${String(index).padStart(4, "0")}${ext}`);
103
+ // --copy stays fade-free (stream copy cannot filter). A segment's true
104
+ // start/end (index 0's start, the last segment's end) borders nothing
105
+ // kept, so only an interior splice edge gets a ramp.
106
+ const fade = args.copy
107
+ ? null
108
+ : fadeFilterFor(segment.end - segment.start, {
109
+ fadeIn: index > 0,
110
+ fadeOut: index < segments.length - 1,
111
+ });
112
+ cutSegment(inputPath, segment, out, args.copy, fade);
100
113
  return out;
101
114
  });
102
115
  const listPath = join(tmpDir, "list.txt");
@@ -107,9 +120,16 @@ function run() {
107
120
  // Encode to a sibling temp (same extension so ffmpeg picks the right muxer),
108
121
  // then atomic-rename so a SIGKILL mid-encode can't leave a truncated outPath.
109
122
  const tmpOut = `${outPath}.part${extname(outPath) || ".mp4"}`;
123
+ // --copy already produced final-codec segments, so concat can stream-copy.
124
+ // Otherwise the PCM intermediates are encoded here, once, for the whole file.
125
+ const concatCodecs = args.copy
126
+ ? ["-c", "copy"]
127
+ : isAudioOnly(outPath)
128
+ ? encodeArgsFor(extname(outPath).toLowerCase())
129
+ : ["-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart"];
110
130
  execFileSync(
111
131
  "ffmpeg",
112
- ["-y", "-f", "concat", "-safe", "0", "-i", listPath, "-c", "copy", tmpOut],
132
+ ["-y", "-f", "concat", "-safe", "0", "-i", listPath, ...concatCodecs, tmpOut],
113
133
  {
114
134
  stdio: "ignore",
115
135
  },
@@ -158,7 +178,7 @@ function run() {
158
178
  console.log(`next: resolve --from ${outPath} --type <type>`);
159
179
  }
160
180
 
161
- function cutSegment(inputPath, segment, outPath, copy) {
181
+ function cutSegment(inputPath, segment, outPath, copy, fade) {
162
182
  const argv = [
163
183
  "-y",
164
184
  "-nostdin",
@@ -171,8 +191,15 @@ function cutSegment(inputPath, segment, outPath, copy) {
171
191
  ];
172
192
  if (copy) {
173
193
  argv.push("-c", "copy", "-avoid_negative_ts", "make_zero");
194
+ } else if (extname(outPath).toLowerCase() === ".mkv") {
195
+ // Concat splices raw segment edges together; without a short ramp the
196
+ // waveform steps discontinuously at every boundary and you hear a click.
197
+ if (fade) argv.push("-af", fade);
198
+ // Video intermediate: keep the picture cheap and the audio uncompressed.
199
+ argv.push("-c:v", "libx264", "-preset", "veryfast", "-crf", "18", "-c:a", "pcm_s16le");
174
200
  } else {
175
- argv.push(...encodeArgsFor(extname(outPath).toLowerCase()));
201
+ if (fade) argv.push("-af", fade);
202
+ argv.push("-c:a", "pcm_s16le");
176
203
  }
177
204
  argv.push(outPath);
178
205
  execFileSync("ffmpeg", argv, { stdio: "ignore" });
@@ -180,6 +207,10 @@ function cutSegment(inputPath, segment, outPath, copy) {
180
207
 
181
208
  // Codec set per output container. Audio-only outputs must not get the
182
209
  // video-centric aac/x264 set (aac inside .wav breaks timing entirely).
210
+ function isAudioOnly(filePath) {
211
+ return [".wav", ".mp3", ".m4a", ".aac", ".flac"].includes(extname(filePath).toLowerCase());
212
+ }
213
+
183
214
  function encodeArgsFor(ext) {
184
215
  if (ext === ".wav") return ["-c:a", "pcm_s16le"];
185
216
  if (ext === ".mp3") return ["-c:a", "libmp3lame", "-q:a", "2"];
@@ -0,0 +1,117 @@
1
+ import assert from "node:assert/strict";
2
+ import { execFileSync, spawnSync } from "node:child_process";
3
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import test from "node:test";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const SCRIPT = fileURLToPath(new URL("./transcript-cut.mjs", import.meta.url));
10
+ const HAS_FFMPEG =
11
+ spawnSync("ffmpeg", ["-version"], { stdio: "ignore" }).status === 0 &&
12
+ spawnSync("ffprobe", ["-version"], { stdio: "ignore" }).status === 0;
13
+
14
+ const SAMPLE_RATE = 44100;
15
+ // A continuous tone's own slope between samples never exceeds amplitude * 2*pi*f/rate
16
+ // (~2054 for 440Hz at full scale here); a raw splice between two independently-cut
17
+ // points on the same tone lands at two unrelated phases, so an unfaded join jumps far
18
+ // past that. This threshold sits well above normal tone motion and well below a splice.
19
+ const MAX_CONTINUOUS_STEP = 6000;
20
+ const MIN_SUSPICIOUS_ZERO_RUN = 20;
21
+
22
+ function fixture() {
23
+ const dir = mkdtempSync(join(tmpdir(), "media-use-transcript-cut-"));
24
+ return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
25
+ }
26
+
27
+ function run(args) {
28
+ return spawnSync(process.execPath, [SCRIPT, ...args], { encoding: "utf8" });
29
+ }
30
+
31
+ function readPcm(filePath) {
32
+ const raw = execFileSync("ffmpeg", [
33
+ "-hide_banner",
34
+ "-loglevel",
35
+ "error",
36
+ "-i",
37
+ filePath,
38
+ "-f",
39
+ "s16le",
40
+ "-ac",
41
+ "1",
42
+ "-ar",
43
+ String(SAMPLE_RATE),
44
+ "-",
45
+ ]);
46
+ const samples = new Int16Array(raw.buffer, raw.byteOffset, raw.length / 2);
47
+ return samples;
48
+ }
49
+
50
+ test(
51
+ "keeps a spliced tone continuous at every cut, with no raw phase jump or silence gap",
52
+ { skip: !HAS_FFMPEG },
53
+ (t) => {
54
+ const { dir, cleanup } = fixture();
55
+ t.after(cleanup);
56
+
57
+ const source = join(dir, "tone.wav");
58
+ execFileSync("ffmpeg", [
59
+ "-y",
60
+ "-hide_banner",
61
+ "-loglevel",
62
+ "error",
63
+ "-f",
64
+ "lavfi",
65
+ "-i",
66
+ `sine=frequency=440:duration=6:sample_rate=${SAMPLE_RATE}`,
67
+ source,
68
+ ]);
69
+
70
+ const transcriptPath = join(dir, "transcript.json");
71
+ writeFileSync(transcriptPath, JSON.stringify({ words: [{ text: "tone", start: 0, end: 6 }] }));
72
+
73
+ const output = join(dir, "out.wav");
74
+ // Two "ugly" removal ranges: neither aligned to the 440Hz period, so the kept
75
+ // segments' cut edges land at unrelated phases of the same continuous tone --
76
+ // exactly the shape that clicks without a fade, and the shape jrusso1020's
77
+ // review proved this branch never actually fades.
78
+ const result = run([
79
+ "--input",
80
+ source,
81
+ "--transcript",
82
+ transcriptPath,
83
+ "--remove",
84
+ "1.37-1.83,3.29-3.71",
85
+ "--out",
86
+ output,
87
+ "--json",
88
+ ]);
89
+ assert.equal(result.status, 0, result.stderr || result.stdout);
90
+
91
+ const samples = readPcm(output);
92
+ assert.ok(samples.length > SAMPLE_RATE, "expected several seconds of audio");
93
+
94
+ let maxStep = 0;
95
+ let zeroRun = 0;
96
+ let maxZeroRun = 0;
97
+ for (let i = 1; i < samples.length; i++) {
98
+ const step = Math.abs(samples[i] - samples[i - 1]);
99
+ if (step > maxStep) maxStep = step;
100
+ if (samples[i] === 0) {
101
+ zeroRun++;
102
+ if (zeroRun > maxZeroRun) maxZeroRun = zeroRun;
103
+ } else {
104
+ zeroRun = 0;
105
+ }
106
+ }
107
+
108
+ assert.ok(
109
+ maxStep <= MAX_CONTINUOUS_STEP,
110
+ `largest sample-to-sample step was ${maxStep}, expected <= ${MAX_CONTINUOUS_STEP} (a raw, unfaded splice)`,
111
+ );
112
+ assert.ok(
113
+ maxZeroRun < MIN_SUSPICIOUS_ZERO_RUN,
114
+ `found a run of ${maxZeroRun} consecutive zero samples, expected < ${MIN_SUSPICIOUS_ZERO_RUN} (a priming-silence gap)`,
115
+ );
116
+ },
117
+ );
@@ -11,6 +11,8 @@ description: >
11
11
  Longer / narrated / multi-scene → /general-video. Unclear → /hyperframes.
12
12
  ---
13
13
 
14
+ **Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.
15
+
14
16
  > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update motion-graphics`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them.
15
17
 
16
18
  > **figma source**: If the logo/asset/animation to build from comes from a figma.com URL, run `/figma` first — asset export, brand tokens, and Motion→GSAP translation if the graphic is a Figma Motion import — then build from its output. Don't drive Figma via raw MCP tools directly: that skips SVG sanitization, `.media/manifest.jsonl` provenance, and brand-token `var()` binding, so a later brand change can't propagate without a full re-import.
@@ -19,7 +21,7 @@ description: >
19
21
 
20
22
  > **The front door is `/hyperframes`.** This skill makes a **short, design-led, unnarrated motion graphic** (motion is the message; ~under 10s, no voice-over). Anything longer, narrated, or multi-scene — or any uncertainty → read `/hyperframes` first: the intent layer owns every route decision.
21
23
 
22
- This workflow is **autonomous by design** — at most one clarifying question (`agents/director.md`), then build through verification without intermediate review. The intent layer (`/hyperframes` → `references/intent-interview.md`) routes here directly without run-shape questions; a storyboard and companion session add little to a piece this short. Rendering is still user-gated: after checks and proof snapshots pass, ask the canonical “preview first, or render?” question from `../hyperframes-core/references/brief-contract.md`. When a `BRIEF.md` exists, read it before the director's question.
24
+ This workflow is **autonomous by design** — at most one clarifying question (`agents/director.md`), then build through verification without intermediate review. The intent layer (`/hyperframes` → `references/intent-interview.md`) routes here directly without run-shape questions; a storyboard and companion session add little to a piece this short. Rendering is still user-gated: after checks and proof snapshots pass, ask the canonical “preview first, or render?” question from `../hyperframes/references/brief-contract.md`. When a `BRIEF.md` exists, read it before the director's question.
23
25
 
24
26
  A short design-led motion graphic. **Asset-first**: decide the asset strategy and source real material _before_ designing the shot, then design the shot around what you have, then compose by reusing catalog capabilities. All artifacts go to `PROJECT_DIR = videos/<project-name>/` (created in Step 0); all paths below are relative to it.
25
27
 
@@ -6,6 +6,7 @@ Turn `shot-plan.json` into one renderable HyperFrames composition (`compositions
6
6
 
7
7
  Default = **compose existing catalog capabilities, not hand-author**:
8
8
 
9
+ - **Search first, for every named effect** — `npx hyperframes catalog --query "<the move, in plain English>" --json`, including an effect the user names after the plan is written. It needs nothing installed and no project. Author by hand only after a search came back with nothing that does the job, and report that miss with `npx hyperframes feedback --search-miss`.
9
10
  - `npx hyperframes add <block>` (registry) → customize in place. Most blocks bake content/data into their own script (only a few expose CSS-var params), so reuse = **add + edit**.
10
11
  - `hyperframes-animation` rules / blueprints / transitions for motion; runtime adapters (GSAP default).
11
12
 
@@ -40,7 +40,8 @@ Emit a DRAFT `shot-plan.json`.
40
40
 
41
41
  Given the draft + resolved `assets/index.md` (if Step 2 ran) + `catalog-map.md`, design the shot **around the assets**:
42
42
 
43
- - Pick the **catalog block(s)** + the `hyperframes-animation` rules / blueprints (catalog-aware — see `catalog-map.md`).
43
+ - **Run `npx hyperframes catalog --query "<the move, in plain English>" --json` for every look or effect the brief names**, before naming any block. It ranks the whole hosted registry and needs nothing installed. `catalog-map.md` is a partial snapshot; the search is the source of truth.
44
+ - Pick the **catalog block(s)** from those results + the `hyperframes-animation` rules / blueprints (see `catalog-map.md` for how each category customizes).
44
45
  - Layout (hero-frame), motion (per `references/motion-vocabulary.md`), beats, pacing, exits.
45
46
  - `asset-fusion`: read the asset's **geometric affordance** → `element_positions` (center / extent / safe-zones / avoid-zones) + **eyedropper palette** from the asset.
46
47
  - Finalize `shot-plan.json`: `content.block` + `content.customize` + the per-category `content`.
@@ -1,5 +1,18 @@
1
1
  # Director → catalog block map (reuse-first)
2
2
 
3
+ <!-- registry-items: allow=geo-highlight,geo-flow,flag-borders,pin-rollout,stat-motion,data-composition-src -->
4
+ <!-- allow= entries above are deliberately NOT registry items: geo-*/flag-borders/pin-rollout and stat-motion are hand-authored gaps this file names in order to say they are gaps. -->
5
+
6
+ **Search the live catalog before you use this table, and again before any hand-authored effect.** Run
7
+ `npx hyperframes catalog --query "<the move, in plain English>" --json` — once per named look or effect the
8
+ user asks for ("CRT scanlines", "glitch", "film grain", "shimmer sweep"), including effects asked for
9
+ mid-build. **The search needs nothing installed**: it ranks the whole hosted registry (~400 items) from any
10
+ directory, with no project, no prior `add`, and no account. The table below is a hand-maintained snapshot of
11
+ part of that registry and it under-covers by design — treat a miss here as unproven, and only the search's
12
+ own empty result as evidence the catalog lacks the move. When it does, say so with
13
+ `npx hyperframes feedback --search-miss "<query>" --wanted "<the move>" --tier <tier from the envelope>`
14
+ before hand-authoring. Full flags and tiers: `/hyperframes-registry` → § Discovery.
15
+
3
16
  The Builder's **default is to compose existing HF catalog blocks, not hand-author.** `npx hyperframes add <block>` drops a block's source into `compositions/`; the Builder then **customizes in place** — most blocks bake their content/data into their own script (only a few expose CSS-var `params`), so reuse = **add + edit**, not pure variable injection. Hand-author only (a) gaps no block covers and (b) the asset-fusion affordance binding.
4
17
 
5
18
  After classifying intent → category, the **Director names the block(s) + what to customize** in the shot-plan IR:
@@ -10,19 +23,18 @@ After classifying intent → category, the **Director names the block(s) + what
10
23
 
11
24
  ## Category → block(s)
12
25
 
13
- | Category | Borrow from catalog | Customize | Gaps → hand-author |
14
- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
15
- | **kinetic-typo** | one of **18 `caption-*`** (kinetic-slam, editorial-emphasis, clip-wipe, gradient-fill, neon-glow, glitch-rgb, matrix-decode, particle-burst, weight-shift, pill-karaoke, highlight, blend-difference…) | words; `emphasis_words` → `word--emphasis`; palette; font; timing | a motif none of the 18 cover |
16
- | **charts** | `data-chart` (bar+line, staggered, value labels) | edit the data array + headline/subtitle; `--bg`/`--text` | pie/donut, bar-chart-race, ring/% |
17
- | **stat** | `apple-money-count` (finance: $ counter + burst + SFX) **or** our generic `stat-motion` (any %/number + ring) | target, prefix/suffix, label, palette | — |
18
- | maps/geo | **vector lane**: `us-map` (+bubble/hex/flow), `world-map`, `spain-map` + **hand-author** `geo-highlight`/`geo-flow`/`flag-borders`/`pin-rollout` (NOT in registry — build per `categories/maps/module.md`). **basemap lane** (real satellite/dark / zoom-to-place): bake via `categories/maps/bake-basemap.mjs` → `<video>` + geo-aligned SVG overlay (border draw-on + colour-block fills) | regions/data, palette, callouts/connectors, basemap style | (basemap lane now covers real imagery + zoom-to-address — was the gap) |
19
- | diagram | `flowchart` (SVG connectors + nodes) | nodes, edges, labels | — |
20
- | **code / code-reveal** | one of the **9 Code Animations** blocks: `code-typing` (live typing), `code-diff` (a change), `code-morph` (a refactor), `code-highlight` (spotlight a line; `line` is 0-based), `code-scroll` (walk a file), `code-snippet-flight` (assemble); GPU hero reveals `code-3d-extrude` / `code-shader-dissolve` / `code-particle-assemble` for a title-card moment; for a static themed code/terminal window use a `code-snippet-*` block | the baked code string / diff / theme | a code motion none cover |
21
- | **brand reveal** | `logo-outro` (piece assembly + glow + tagline + URL pill) | logo asset, tagline, URL, palette | — |
22
- | transitions | 15+ shader transitions (domain-warp, whip-pan, sdf-iris, glitch…) | direction, timing | — |
23
- | social overlay | ig / tiktok / yt / x / reddit / spotify / macos cards | handle, metrics, avatar | — |
24
- | polish | grain-overlay, vignette, shimmer-sweep, texture-mask-text | intensity | — |
25
- | **asset-fusion (RWA)** | **annotation kit from `north-korea-locked-down`** — scribble-circle draw-on, pop-up pill + pointer, red-wash, scanline, corners, camera push; + `us-map-bubble` callouts/connectors | asset, annotation position (`element_positions`), label, palette **eyedropper'd from the asset** | **NET-NEW (not in catalog): the affordance binding — asset geometry _becomes_ the chart axis (straw → gauge)** |
26
+ | Category | Borrow from catalog | Customize | Gaps → hand-author |
27
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
28
+ | **kinetic-typo** | one of **17 `caption-*`** (kinetic-slam, editorial-emphasis, clip-wipe, gradient-fill, neon-glow, glitch-rgb, matrix-decode, particle-burst, weight-shift, pill-karaoke, highlight, blend-difference…) | words; `emphasis_words` → `word--emphasis`; palette; font; timing | a motif none of the 17 cover |
29
+ | **charts** | `data-chart` (bar+line, staggered, value labels), `bar-chart-race` (a race) | edit the data array + headline/subtitle; `--bg`/`--text` | pie/donut, ring/% |
30
+ | **stat** | `apple-money-count` (finance: $ counter + burst + SFX) **or** hand-author a generic %/number + ring per `categories/stat/module.md` (there is no `stat-motion` block) | target, prefix/suffix, label, palette | — |
31
+ | maps/geo | **vector lane**: `us-map` (+bubble/hex/flow), `world-map`, `spain-map` + **hand-author** `geo-highlight`/`geo-flow`/`flag-borders`/`pin-rollout` (NOT in registry — build per `categories/maps/module.md`). **basemap lane** (real satellite/dark / zoom-to-place): bake via `categories/maps/bake-basemap.mjs` → `<video>` + geo-aligned SVG overlay (border draw-on + colour-block fills) | regions/data, palette, callouts/connectors, basemap style | (basemap lane now covers real imagery + zoom-to-address — was the gap) |
32
+ | diagram | `flowchart` (SVG connectors + nodes) | nodes, edges, labels | — |
33
+ | **brand reveal** | `logo-outro` (piece assembly + glow + tagline + URL pill) | logo asset, tagline, URL, palette | — |
34
+ | transitions | 15+ shader transitions (domain-warp, whip-pan, sdf-iris, glitch…) | direction, timing | — |
35
+ | social overlay | ig / tiktok / yt / x / reddit / spotify / macos cards | handle, metrics, avatar | — |
36
+ | polish | grain-overlay, vignette, shimmer-sweep, texture-mask-text | intensity | — |
37
+ | **asset-fusion (RWA)** | **annotation kit from `north-korea-locked-down`** — scribble-circle draw-on, pop-up pill + pointer, red-wash, scanline, corners, camera push; + `us-map-bubble` callouts/connectors | asset, annotation position (`element_positions`), label, palette **eyedropper'd from the asset** | **NET-NEW (not in catalog): the affordance binding — asset geometry _becomes_ the chart axis (straw → gauge)** |
26
38
 
27
39
  ## Reuse mechanics
28
40
 
@@ -9,7 +9,8 @@ Animated **data-viz** from data. Asset-free (the "input" is the data). "One char
9
9
  ## Vocabulary / leans on
10
10
 
11
11
  - Block: **`data-chart`** (animated **bar + line**, staggered reveal, value labels — proven: borrowed + customized + rendered to MP4 in the prototype `charts-demo`).
12
- - Gaps (hand-author): **pie / donut, bar-chart-race, ring/%** — `data-chart` doesn't cover these. Use D3/visx for data→geometry + GSAP for motion.
12
+ - Also in the registry: **`bar-chart-race`** — install it rather than hand-authoring a race.
13
+ - Gaps (hand-author): **pie / donut, ring/%** — no registry block covers these. Search first (`npx hyperframes catalog --query "pie chart reveal" --json`), then use D3/visx for data→geometry + GSAP for motion.
13
14
  - Signature animations: bar stagger-grow · line `stroke-dashoffset` draw-on · pie radial sweep · ring fill · KPI count-up · race reorder.
14
15
 
15
16
  ## Build (reuse-first)
@@ -1,5 +1,9 @@
1
1
  # lower-thirds — category module
2
2
 
3
+ <!-- registry-items: allow= -->
4
+
5
+ **The live search is the source of truth for what the registry has.** The table(s) below are a hand-maintained sample and under-cover by design: run `npx hyperframes catalog --query "<what you want>" --json` — it needs nothing installed — before concluding the registry lacks something. Item names here are checked against `registry/registry.json` by `bun run lint:skills`.
6
+
3
7
  **Name/title bars, callouts, social overlays** — graphics meant to sit over other footage. Asset-free (+ optional logo). Usually `export: alpha-overlay` (transparent). ~3–6s (or loop/hold).
4
8
 
5
9
  ## Plan (Director)
@@ -11,7 +11,7 @@ Two layouts share that one highlight core — pick by aspect + whether there's a
11
11
 
12
12
  ## Source (Step 2)
13
13
 
14
- RWA / web search (or `hyperframes capture`) → a real article. The Director extracts the **keyword** (1–2 words / a number / a name — the hook) and, for Layout B, also a **brand logo**, a **date**, and a **person photo**. `asset_needs: { kind: news|web|image, query }` — request the logo (Wikimedia / simple-icons) and the person photo (Wikimedia) as separate asset queries. Run the person photo through `hyperframes remove-background <in> -o <out>.png` to get a transparent cutout.
14
+ RWA / web search (or `hyperframes capture`) → a real article. The Director extracts the **keyword** (1–2 words / a number / a name — the hook) and, for Layout B, also a **brand logo**, a **date**, and a **person photo**. `asset_needs: { kind: news|web|image, query }` — request the logo (`media-use resolve --type logo`, theSVG-first) and the person photo (Wikimedia) as separate asset queries. Run the person photo through `hyperframes remove-background <in> -o <out>.png` to get a transparent cutout.
15
15
 
16
16
  ## The highlight core (shared by both layouts) — the marker-band technique
17
17
 
@@ -1,5 +1,9 @@
1
1
  # text module · motion vocabulary (primitive → GSAP)
2
2
 
3
+ <!-- registry-items: allow= -->
4
+
5
+ **The live search is the source of truth for what the registry has.** The table(s) below are a hand-maintained sample and under-cover by design: run `npx hyperframes catalog --query "<what you want>" --json` — it needs nothing installed — before concluding the registry lacks something. Item names here are checked against `registry/registry.json` by `bun run lint:skills`.
6
+
3
7
  Named primitives the Director references in `motion` strings and the Builder implements. `code_hint`s are framework-neutral physics; the GSAP recipe is the HF implementation. Prefer an HF **registry component** (bottom) when one fits — don't reinvent.
4
8
 
5
9
  ## Entry