@mevdragon/vidfarm-devcli 0.14.0 → 0.16.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 (427) hide show
  1. package/.agents/skills/embedded-captions/CATALOG.md +93 -0
  2. package/.agents/skills/embedded-captions/SKILL.md +286 -0
  3. package/.agents/skills/embedded-captions/assets/brand/CDPR-fankit-terms.txt +35 -0
  4. package/.agents/skills/embedded-captions/assets/brand/cyberpunk-widths.json +115 -0
  5. package/.agents/skills/embedded-captions/assets/fonts/char-widths.json +2211 -0
  6. package/.agents/skills/embedded-captions/assets/strokefonts/HersheyScript1.svg +242 -0
  7. package/.agents/skills/embedded-captions/assets/strokefonts/HersheyScriptMed.svg +242 -0
  8. package/.agents/skills/embedded-captions/dna/README.md +148 -0
  9. package/.agents/skills/embedded-captions/dna/chrome.json +68 -0
  10. package/.agents/skills/embedded-captions/dna/cream.json +69 -0
  11. package/.agents/skills/embedded-captions/dna/documentary.json +62 -0
  12. package/.agents/skills/embedded-captions/dna/editorial.json +70 -0
  13. package/.agents/skills/embedded-captions/dna/glitch.json +82 -0
  14. package/.agents/skills/embedded-captions/dna/ink.json +65 -0
  15. package/.agents/skills/embedded-captions/dna/keynote.json +62 -0
  16. package/.agents/skills/embedded-captions/dna/loud.json +75 -0
  17. package/.agents/skills/embedded-captions/dna/neon.json +58 -0
  18. package/.agents/skills/embedded-captions/dna/velocity.json +89 -0
  19. package/.agents/skills/embedded-captions/modes/cinematic/README.md +48 -0
  20. package/.agents/skills/embedded-captions/modes/cinematic/_archive/champion/spec.md +98 -0
  21. package/.agents/skills/embedded-captions/modes/cinematic/_archive/champion/template.html +188 -0
  22. package/.agents/skills/embedded-captions/modes/cinematic/_archive/memory-wall/spec.md +134 -0
  23. package/.agents/skills/embedded-captions/modes/cinematic/_archive/memory-wall/template.html +179 -0
  24. package/.agents/skills/embedded-captions/modes/cinematic/_archive/portrait-header/spec.md +81 -0
  25. package/.agents/skills/embedded-captions/modes/cinematic/_archive/portrait-header/template.html +170 -0
  26. package/.agents/skills/embedded-captions/modes/cinematic/cinematic-cream/spec.md +16 -0
  27. package/.agents/skills/embedded-captions/modes/cinematic/cinematic-cream/template.html +226 -0
  28. package/.agents/skills/embedded-captions/modes/cinematic/engine.html +355 -0
  29. package/.agents/skills/embedded-captions/modes/standard/_anatomy.md +233 -0
  30. package/.agents/skills/embedded-captions/modes/standard/_motion.md +192 -0
  31. package/.agents/skills/embedded-captions/modes/standard/fonts/build-fonts-css.cjs +97 -0
  32. package/.agents/skills/embedded-captions/references/aesthetic-principles.md +168 -0
  33. package/.agents/skills/embedded-captions/references/anti-patterns.md +188 -0
  34. package/.agents/skills/embedded-captions/references/bespoke-vs-presets.md +178 -0
  35. package/.agents/skills/embedded-captions/references/caption-grouping.md +130 -0
  36. package/.agents/skills/embedded-captions/references/composition-craft.md +522 -0
  37. package/.agents/skills/embedded-captions/references/direction-catalog.md +138 -0
  38. package/.agents/skills/embedded-captions/references/example-renders/champion.html +371 -0
  39. package/.agents/skills/embedded-captions/references/example-renders/memory-wall.html +334 -0
  40. package/.agents/skills/embedded-captions/references/failure-modes.md +145 -0
  41. package/.agents/skills/embedded-captions/references/layout-heuristics.md +238 -0
  42. package/.agents/skills/embedded-captions/references/motion-vocabulary.md +148 -0
  43. package/.agents/skills/embedded-captions/references/rail.md +68 -0
  44. package/.agents/skills/embedded-captions/references/reference-bar.md +47 -0
  45. package/.agents/skills/embedded-captions/references/scene-types.md +94 -0
  46. package/.agents/skills/embedded-captions/references/test-set.md +66 -0
  47. package/.agents/skills/embedded-captions/references/typographic-moves.md +266 -0
  48. package/.agents/skills/embedded-captions/references/typography-presets.md +63 -0
  49. package/.agents/skills/embedded-captions/scripts/audio-envelope.cjs +95 -0
  50. package/.agents/skills/embedded-captions/scripts/check-occlusion.cjs +250 -0
  51. package/.agents/skills/embedded-captions/scripts/check-overflow.cjs +194 -0
  52. package/.agents/skills/embedded-captions/scripts/check-rail-climax.cjs +233 -0
  53. package/.agents/skills/embedded-captions/scripts/check-timing.cjs +173 -0
  54. package/.agents/skills/embedded-captions/scripts/fill-timings.cjs +124 -0
  55. package/.agents/skills/embedded-captions/scripts/fit-fonts.cjs +158 -0
  56. package/.agents/skills/embedded-captions/scripts/gen-stroke-path.py +46 -0
  57. package/.agents/skills/embedded-captions/scripts/inject-fonts.cjs +169 -0
  58. package/.agents/skills/embedded-captions/scripts/lib-dna.cjs +201 -0
  59. package/.agents/skills/embedded-captions/scripts/make-cinematic.cjs +1205 -0
  60. package/.agents/skills/embedded-captions/scripts/make-composition.cjs +404 -0
  61. package/.agents/skills/embedded-captions/scripts/make-theme.cjs +8781 -0
  62. package/.agents/skills/embedded-captions/scripts/matte.cjs +294 -0
  63. package/.agents/skills/embedded-captions/scripts/measure-layout.cjs +293 -0
  64. package/.agents/skills/embedded-captions/scripts/prepare.sh +39 -0
  65. package/.agents/skills/embedded-captions/scripts/preview-frames.cjs +268 -0
  66. package/.agents/skills/embedded-captions/scripts/render-and-composite.sh +509 -0
  67. package/.agents/skills/embedded-captions/scripts/render-theme.sh +34 -0
  68. package/.agents/skills/embedded-captions/scripts/safe-zones.cjs +813 -0
  69. package/.agents/skills/embedded-captions/scripts/transcribe.cjs +349 -0
  70. package/.agents/skills/embedded-captions/themes/PORTING.md +49 -0
  71. package/.agents/skills/embedded-captions/themes/README.md +244 -0
  72. package/.agents/skills/embedded-captions/themes/anchor.json +27 -0
  73. package/.agents/skills/embedded-captions/themes/arcade.json +51 -0
  74. package/.agents/skills/embedded-captions/themes/aurora.json +41 -0
  75. package/.agents/skills/embedded-captions/themes/biolume.json +35 -0
  76. package/.agents/skills/embedded-captions/themes/brush.json +74 -0
  77. package/.agents/skills/embedded-captions/themes/chalkboard.json +52 -0
  78. package/.agents/skills/embedded-captions/themes/dossier.json +56 -0
  79. package/.agents/skills/embedded-captions/themes/graffiti.json +62 -0
  80. package/.agents/skills/embedded-captions/themes/hologram.json +41 -0
  81. package/.agents/skills/embedded-captions/themes/inkwater.json +43 -0
  82. package/.agents/skills/embedded-captions/themes/laser.json +46 -0
  83. package/.agents/skills/embedded-captions/themes/lastpage.json +33 -0
  84. package/.agents/skills/embedded-captions/themes/neonsign.json +53 -0
  85. package/.agents/skills/embedded-captions/themes/nightcity.json +56 -0
  86. package/.agents/skills/embedded-captions/themes/ordnance.json +54 -0
  87. package/.agents/skills/embedded-captions/themes/papercut.json +37 -0
  88. package/.agents/skills/embedded-captions/themes/popup.json +46 -0
  89. package/.agents/skills/embedded-captions/themes/ransom.json +62 -0
  90. package/.agents/skills/embedded-captions/themes/scoreboard.json +54 -0
  91. package/.agents/skills/embedded-captions/themes/spectrum.json +46 -0
  92. package/.agents/skills/embedded-captions/themes/stardust.json +29 -0
  93. package/.agents/skills/embedded-captions/themes/stomp.json +43 -0
  94. package/.agents/skills/embedded-captions/themes/terminal.json +49 -0
  95. package/.agents/skills/embedded-captions/themes/thunder.json +44 -0
  96. package/.agents/skills/embedded-captions/themes/transit.json +44 -0
  97. package/.agents/skills/embedded-captions/themes/vhs.json +47 -0
  98. package/.agents/skills/faceless-explainer/SKILL.md +209 -0
  99. package/.agents/skills/faceless-explainer/references/cut-catalog.md +215 -0
  100. package/.agents/skills/faceless-explainer/references/motion-language.md +156 -0
  101. package/.agents/skills/faceless-explainer/references/story-design.md +248 -0
  102. package/.agents/skills/faceless-explainer/references/visual-design.md +146 -0
  103. package/.agents/skills/faceless-explainer/scripts/assemble-index.mjs +570 -0
  104. package/.agents/skills/faceless-explainer/scripts/audio.mjs +260 -0
  105. package/.agents/skills/faceless-explainer/scripts/build-frame.mjs +536 -0
  106. package/.agents/skills/faceless-explainer/scripts/captions.mjs +508 -0
  107. package/.agents/skills/faceless-explainer/scripts/lib/assets.mjs +55 -0
  108. package/.agents/skills/faceless-explainer/scripts/lib/dimensions.mjs +45 -0
  109. package/.agents/skills/faceless-explainer/scripts/lib/storyboard.mjs +249 -0
  110. package/.agents/skills/faceless-explainer/scripts/lib/tokens.mjs +204 -0
  111. package/.agents/skills/faceless-explainer/scripts/lib/transition-registry.mjs +38 -0
  112. package/.agents/skills/faceless-explainer/scripts/lib/transitions.json +71 -0
  113. package/.agents/skills/faceless-explainer/scripts/transitions.mjs +320 -0
  114. package/.agents/skills/faceless-explainer/sub-agents/frame-worker.md +75 -0
  115. package/.agents/skills/flockposter/LICENSE +17 -0
  116. package/.agents/skills/flockposter/README.md +72 -0
  117. package/.agents/skills/flockposter/SKILL.md +562 -0
  118. package/.agents/skills/flockposter/examples/cli-instagram-reel.sh +21 -0
  119. package/.agents/skills/flockposter/examples/cli-multi-platform.json +69 -0
  120. package/.agents/skills/flockposter/examples/cli-tiktok-upload-only.sh +22 -0
  121. package/.agents/skills/flockposter/examples/cli-x-thread.sh +17 -0
  122. package/.agents/skills/flockposter/examples/cli-youtube-short.sh +21 -0
  123. package/.agents/skills/flockposter/examples/public-api-instagram-reel.json +28 -0
  124. package/.agents/skills/general-video/SKILL.md +143 -0
  125. package/.agents/skills/hyperframes/SKILL.md +160 -0
  126. package/.agents/skills/hyperframes-animation/SKILL.md +84 -0
  127. package/.agents/skills/hyperframes-animation/adapters/animate-text.md +64 -0
  128. package/.agents/skills/hyperframes-animation/adapters/animejs.md +114 -0
  129. package/.agents/skills/hyperframes-animation/adapters/css-animations.md +143 -0
  130. package/.agents/skills/hyperframes-animation/adapters/gsap-easing-and-stagger.md +118 -0
  131. package/.agents/skills/hyperframes-animation/adapters/gsap-timeline-and-labels.md +96 -0
  132. package/.agents/skills/hyperframes-animation/adapters/gsap-transforms-and-perf.md +96 -0
  133. package/.agents/skills/hyperframes-animation/adapters/gsap.md +105 -0
  134. package/.agents/skills/hyperframes-animation/adapters/html-in-canvas-patterns.md +507 -0
  135. package/.agents/skills/hyperframes-animation/adapters/lottie.md +117 -0
  136. package/.agents/skills/hyperframes-animation/adapters/three.md +131 -0
  137. package/.agents/skills/hyperframes-animation/adapters/typegpu.md +178 -0
  138. package/.agents/skills/hyperframes-animation/adapters/waapi.md +101 -0
  139. package/.agents/skills/hyperframes-animation/blueprints/comparison-split.md +27 -0
  140. package/.agents/skills/hyperframes-animation/blueprints/constellation-hub.md +44 -0
  141. package/.agents/skills/hyperframes-animation/blueprints/cta-morph-press.md +28 -0
  142. package/.agents/skills/hyperframes-animation/blueprints/cursor-ui-demo.md +57 -0
  143. package/.agents/skills/hyperframes-animation/blueprints/dataviz-countup.md +46 -0
  144. package/.agents/skills/hyperframes-animation/blueprints/device-surface-showcase.md +53 -0
  145. package/.agents/skills/hyperframes-animation/blueprints/grid-card-assemble.md +66 -0
  146. package/.agents/skills/hyperframes-animation/blueprints/kinetic-type-beats.md +78 -0
  147. package/.agents/skills/hyperframes-animation/blueprints/logo-assemble-lockup.md +69 -0
  148. package/.agents/skills/hyperframes-animation/blueprints/overwhelm-surround.md +28 -0
  149. package/.agents/skills/hyperframes-animation/blueprints/spatial-pan-stations.md +37 -0
  150. package/.agents/skills/hyperframes-animation/blueprints/ticker-takeover.md +29 -0
  151. package/.agents/skills/hyperframes-animation/blueprints/titlecard-reveal.md +42 -0
  152. package/.agents/skills/hyperframes-animation/blueprints/typewriter-reveal.md +51 -0
  153. package/.agents/skills/hyperframes-animation/blueprints/video-text-pivot.md +30 -0
  154. package/.agents/skills/hyperframes-animation/blueprints-index.md +146 -0
  155. package/.agents/skills/hyperframes-animation/examples/assets/avatars/02.avif +0 -0
  156. package/.agents/skills/hyperframes-animation/examples/assets/brands/github.avif +0 -0
  157. package/.agents/skills/hyperframes-animation/examples/assets/brands/nvidia.avif +0 -0
  158. package/.agents/skills/hyperframes-animation/examples/assets/brands/visa.avif +0 -0
  159. package/.agents/skills/hyperframes-animation/examples/assets/brands/zoominfo.avif +0 -0
  160. package/.agents/skills/hyperframes-animation/examples/brand-reveal-assemble-zoom.html +382 -0
  161. package/.agents/skills/hyperframes-animation/examples/comparison-split-cards.html +649 -0
  162. package/.agents/skills/hyperframes-animation/examples/concept-demo-decode-pan.html +520 -0
  163. package/.agents/skills/hyperframes-animation/examples/cta-morph-press.html +468 -0
  164. package/.agents/skills/hyperframes-animation/examples/cta-orbit-collapse.html +1298 -0
  165. package/.agents/skills/hyperframes-animation/examples/demo-page-scroll-spotlight.html +759 -0
  166. package/.agents/skills/hyperframes-animation/examples/hook-counter-burst.html +729 -0
  167. package/.agents/skills/hyperframes-animation/examples/messaging-multi-phrase.html +352 -0
  168. package/.agents/skills/hyperframes-animation/examples/metric-video-text-pivot.html +779 -0
  169. package/.agents/skills/hyperframes-animation/examples/problem-mockup-overwhelm.html +1374 -0
  170. package/.agents/skills/hyperframes-animation/examples/proof-logo-chain.html +861 -0
  171. package/.agents/skills/hyperframes-animation/examples/takeover-ticker-displace.html +347 -0
  172. package/.agents/skills/hyperframes-animation/examples/workflow-approve-press.html +606 -0
  173. package/.agents/skills/hyperframes-animation/rules/3d-page-scroll.md +227 -0
  174. package/.agents/skills/hyperframes-animation/rules/3d-text-depth-layers.md +297 -0
  175. package/.agents/skills/hyperframes-animation/rules/ai-tracking-box.md +382 -0
  176. package/.agents/skills/hyperframes-animation/rules/ambient-glow-bloom.md +305 -0
  177. package/.agents/skills/hyperframes-animation/rules/asr-keyword-glow.md +286 -0
  178. package/.agents/skills/hyperframes-animation/rules/avatar-cloud-network.md +371 -0
  179. package/.agents/skills/hyperframes-animation/rules/camera-cursor-tracking.md +246 -0
  180. package/.agents/skills/hyperframes-animation/rules/card-morph-anchor.md +267 -0
  181. package/.agents/skills/hyperframes-animation/rules/center-outward-expansion.md +227 -0
  182. package/.agents/skills/hyperframes-animation/rules/context-sensitive-cursor.md +257 -0
  183. package/.agents/skills/hyperframes-animation/rules/coordinate-target-zoom.md +332 -0
  184. package/.agents/skills/hyperframes-animation/rules/counting-dynamic-scale.md +283 -0
  185. package/.agents/skills/hyperframes-animation/rules/css-marker-patterns.md +373 -0
  186. package/.agents/skills/hyperframes-animation/rules/cursor-click-ripple.md +262 -0
  187. package/.agents/skills/hyperframes-animation/rules/depth-of-field-blur.md +313 -0
  188. package/.agents/skills/hyperframes-animation/rules/depth-scatter-assemble.md +303 -0
  189. package/.agents/skills/hyperframes-animation/rules/discrete-text-sequence.md +273 -0
  190. package/.agents/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +307 -0
  191. package/.agents/skills/hyperframes-animation/rules/gsap-effects.md +299 -0
  192. package/.agents/skills/hyperframes-animation/rules/hacker-flip-3d.md +223 -0
  193. package/.agents/skills/hyperframes-animation/rules/kinetic-beat-slam.md +183 -0
  194. package/.agents/skills/hyperframes-animation/rules/motion-blur-streak.md +328 -0
  195. package/.agents/skills/hyperframes-animation/rules/multi-phase-camera.md +273 -0
  196. package/.agents/skills/hyperframes-animation/rules/orbit-3d-entry.md +301 -0
  197. package/.agents/skills/hyperframes-animation/rules/physics-press-reaction.md +350 -0
  198. package/.agents/skills/hyperframes-animation/rules/press-release-spring.md +296 -0
  199. package/.agents/skills/hyperframes-animation/rules/reactive-displacement.md +277 -0
  200. package/.agents/skills/hyperframes-animation/rules/scale-swap-transition.md +298 -0
  201. package/.agents/skills/hyperframes-animation/rules/sine-wave-loop.md +278 -0
  202. package/.agents/skills/hyperframes-animation/rules/split-tilt-cards.md +277 -0
  203. package/.agents/skills/hyperframes-animation/rules/spring-pop-entrance.md +273 -0
  204. package/.agents/skills/hyperframes-animation/rules/stat-bars-and-fills.md +156 -0
  205. package/.agents/skills/hyperframes-animation/rules/svg-icon-enrichment.md +329 -0
  206. package/.agents/skills/hyperframes-animation/rules/svg-path-draw.md +274 -0
  207. package/.agents/skills/hyperframes-animation/rules/vertical-spring-ticker.md +239 -0
  208. package/.agents/skills/hyperframes-animation/rules/viewport-change.md +349 -0
  209. package/.agents/skills/hyperframes-animation/rules-index.md +86 -0
  210. package/.agents/skills/hyperframes-animation/scripts/animation-map.mjs +606 -0
  211. package/.agents/skills/hyperframes-animation/scripts/package-loader.mjs +288 -0
  212. package/.agents/skills/hyperframes-animation/scripts/package-loader.test.mjs +62 -0
  213. package/.agents/skills/hyperframes-animation/techniques.md +507 -0
  214. package/.agents/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +167 -0
  215. package/.agents/skills/hyperframes-animation/transitions/catalog.md +127 -0
  216. package/.agents/skills/hyperframes-animation/transitions/css-3d.md +12 -0
  217. package/.agents/skills/hyperframes-animation/transitions/css-blur.md +51 -0
  218. package/.agents/skills/hyperframes-animation/transitions/css-cover.md +43 -0
  219. package/.agents/skills/hyperframes-animation/transitions/css-destruction.md +95 -0
  220. package/.agents/skills/hyperframes-animation/transitions/css-dissolve.md +66 -0
  221. package/.agents/skills/hyperframes-animation/transitions/css-distortion.md +45 -0
  222. package/.agents/skills/hyperframes-animation/transitions/css-grid.md +10 -0
  223. package/.agents/skills/hyperframes-animation/transitions/css-light.md +49 -0
  224. package/.agents/skills/hyperframes-animation/transitions/css-mechanical.md +30 -0
  225. package/.agents/skills/hyperframes-animation/transitions/css-other.md +25 -0
  226. package/.agents/skills/hyperframes-animation/transitions/css-push.md +41 -0
  227. package/.agents/skills/hyperframes-animation/transitions/css-radial.md +37 -0
  228. package/.agents/skills/hyperframes-animation/transitions/css-scale.md +24 -0
  229. package/.agents/skills/hyperframes-animation/transitions/overview.md +153 -0
  230. package/.agents/skills/hyperframes-cli/SKILL.md +113 -0
  231. package/.agents/skills/hyperframes-cli/references/doctor-browser.md +45 -0
  232. package/.agents/skills/hyperframes-cli/references/init-and-scaffold.md +55 -0
  233. package/.agents/skills/hyperframes-cli/references/lambda.md +132 -0
  234. package/.agents/skills/hyperframes-cli/references/lint-validate-inspect.md +121 -0
  235. package/.agents/skills/hyperframes-cli/references/preview-render.md +149 -0
  236. package/.agents/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
  237. package/.agents/skills/hyperframes-core/SKILL.md +78 -0
  238. package/.agents/skills/hyperframes-core/references/composition-patterns.md +263 -0
  239. package/.agents/skills/hyperframes-core/references/data-attributes.md +70 -0
  240. package/.agents/skills/hyperframes-core/references/determinism-rules.md +68 -0
  241. package/.agents/skills/hyperframes-core/references/full-screen-motion.md +62 -0
  242. package/.agents/skills/hyperframes-core/references/minimal-composition.md +66 -0
  243. package/.agents/skills/hyperframes-core/references/script-format.md +49 -0
  244. package/.agents/skills/hyperframes-core/references/storyboard-format.md +95 -0
  245. package/.agents/skills/hyperframes-core/references/sub-compositions.md +237 -0
  246. package/.agents/skills/hyperframes-core/references/subagent-dispatch.md +41 -0
  247. package/.agents/skills/hyperframes-core/references/tailwind.md +125 -0
  248. package/.agents/skills/hyperframes-core/references/tracks-and-clips.md +76 -0
  249. package/.agents/skills/hyperframes-core/references/variables-and-media.md +90 -0
  250. package/.agents/skills/hyperframes-creative/SKILL.md +70 -0
  251. package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/FRAME.md +289 -0
  252. package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/caption-skin.html +221 -0
  253. package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/frame-showcase.html +1214 -0
  254. package/.agents/skills/hyperframes-creative/frame-presets/blockframe/FRAME.md +275 -0
  255. package/.agents/skills/hyperframes-creative/frame-presets/blockframe/caption-skin.html +224 -0
  256. package/.agents/skills/hyperframes-creative/frame-presets/blockframe/frame-showcase.html +1347 -0
  257. package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/FRAME.md +307 -0
  258. package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/caption-skin.html +223 -0
  259. package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/frame-showcase.html +1372 -0
  260. package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/FRAME.md +275 -0
  261. package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/caption-skin.html +230 -0
  262. package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/frame-showcase.html +1140 -0
  263. package/.agents/skills/hyperframes-creative/frame-presets/broadside/FRAME.md +284 -0
  264. package/.agents/skills/hyperframes-creative/frame-presets/broadside/caption-skin.html +225 -0
  265. package/.agents/skills/hyperframes-creative/frame-presets/broadside/frame-showcase.html +1166 -0
  266. package/.agents/skills/hyperframes-creative/frame-presets/capsule/FRAME.md +284 -0
  267. package/.agents/skills/hyperframes-creative/frame-presets/capsule/caption-skin.html +224 -0
  268. package/.agents/skills/hyperframes-creative/frame-presets/capsule/frame-showcase.html +1465 -0
  269. package/.agents/skills/hyperframes-creative/frame-presets/cartesian/FRAME.md +278 -0
  270. package/.agents/skills/hyperframes-creative/frame-presets/cartesian/caption-skin.html +222 -0
  271. package/.agents/skills/hyperframes-creative/frame-presets/cartesian/frame-showcase.html +1263 -0
  272. package/.agents/skills/hyperframes-creative/frame-presets/claude/FRAME.md +277 -0
  273. package/.agents/skills/hyperframes-creative/frame-presets/claude/caption-skin.html +224 -0
  274. package/.agents/skills/hyperframes-creative/frame-presets/claude/frame-showcase.html +1439 -0
  275. package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/FRAME.md +265 -0
  276. package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/caption-skin.html +238 -0
  277. package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/frame-showcase.html +1468 -0
  278. package/.agents/skills/hyperframes-creative/frame-presets/coral/FRAME.md +314 -0
  279. package/.agents/skills/hyperframes-creative/frame-presets/coral/caption-skin.html +226 -0
  280. package/.agents/skills/hyperframes-creative/frame-presets/coral/frame-showcase.html +1268 -0
  281. package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/FRAME.md +333 -0
  282. package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/caption-skin.html +227 -0
  283. package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/frame-showcase.html +1330 -0
  284. package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/FRAME.md +274 -0
  285. package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/caption-skin.html +230 -0
  286. package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/frame-showcase.html +1444 -0
  287. package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/FRAME.md +265 -0
  288. package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/caption-skin.html +223 -0
  289. package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/frame-showcase.html +1273 -0
  290. package/.agents/skills/hyperframes-creative/palettes/bold-energetic.md +14 -0
  291. package/.agents/skills/hyperframes-creative/palettes/clean-corporate.md +14 -0
  292. package/.agents/skills/hyperframes-creative/palettes/dark-premium.md +14 -0
  293. package/.agents/skills/hyperframes-creative/palettes/jewel-rich.md +14 -0
  294. package/.agents/skills/hyperframes-creative/palettes/monochrome.md +14 -0
  295. package/.agents/skills/hyperframes-creative/palettes/nature-earth.md +14 -0
  296. package/.agents/skills/hyperframes-creative/palettes/neon-electric.md +14 -0
  297. package/.agents/skills/hyperframes-creative/palettes/pastel-soft.md +14 -0
  298. package/.agents/skills/hyperframes-creative/palettes/warm-editorial.md +14 -0
  299. package/.agents/skills/hyperframes-creative/references/audio-reactive.md +76 -0
  300. package/.agents/skills/hyperframes-creative/references/beat-direction.md +164 -0
  301. package/.agents/skills/hyperframes-creative/references/composition-patterns.md +199 -0
  302. package/.agents/skills/hyperframes-creative/references/data-in-motion.md +19 -0
  303. package/.agents/skills/hyperframes-creative/references/design-adherence.md +19 -0
  304. package/.agents/skills/hyperframes-creative/references/design-picker.md +123 -0
  305. package/.agents/skills/hyperframes-creative/references/design-spec.md +55 -0
  306. package/.agents/skills/hyperframes-creative/references/house-style.md +73 -0
  307. package/.agents/skills/hyperframes-creative/references/motion-principles.md +150 -0
  308. package/.agents/skills/hyperframes-creative/references/narration.md +92 -0
  309. package/.agents/skills/hyperframes-creative/references/prompt-expansion.md +68 -0
  310. package/.agents/skills/hyperframes-creative/references/typography.md +218 -0
  311. package/.agents/skills/hyperframes-creative/references/video-composition.md +63 -0
  312. package/.agents/skills/hyperframes-creative/references/visual-styles.md +457 -0
  313. package/.agents/skills/hyperframes-creative/scripts/contrast-report.mjs +353 -0
  314. package/.agents/skills/hyperframes-creative/scripts/extract-audio-data.py +188 -0
  315. package/.agents/skills/hyperframes-creative/scripts/package-loader.mjs +288 -0
  316. package/.agents/skills/hyperframes-creative/scripts/package-loader.test.mjs +62 -0
  317. package/.agents/skills/hyperframes-creative/templates/design-picker.html +1432 -0
  318. package/.agents/skills/hyperframes-keyframes/SKILL.md +237 -0
  319. package/.agents/skills/hyperframes-keyframes/agents/openai.yaml +4 -0
  320. package/.agents/skills/hyperframes-keyframes/references/keyframe-patterns.md +106 -0
  321. package/.agents/skills/motion-graphics/SKILL.md +172 -0
  322. package/.agents/skills/motion-graphics/agents/builder.md +40 -0
  323. package/.agents/skills/motion-graphics/agents/director.md +53 -0
  324. package/.agents/skills/motion-graphics/agents/finalize.md +17 -0
  325. package/.agents/skills/motion-graphics/catalog-map.md +40 -0
  326. package/.agents/skills/motion-graphics/categories/asset-fusion/module.md +37 -0
  327. package/.agents/skills/motion-graphics/categories/charts/module.md +21 -0
  328. package/.agents/skills/motion-graphics/categories/kinetic-type/module.md +19 -0
  329. package/.agents/skills/motion-graphics/categories/logo-reveal/module.md +17 -0
  330. package/.agents/skills/motion-graphics/categories/lower-thirds/module.md +16 -0
  331. package/.agents/skills/motion-graphics/categories/maps/bake-basemap.mjs +273 -0
  332. package/.agents/skills/motion-graphics/categories/maps/module.md +61 -0
  333. package/.agents/skills/motion-graphics/categories/news/module.md +56 -0
  334. package/.agents/skills/motion-graphics/categories/stat/module.md +21 -0
  335. package/.agents/skills/motion-graphics/categories/tweet/module.md +16 -0
  336. package/.agents/skills/motion-graphics/categories/webpage/module.md +21 -0
  337. package/.agents/skills/motion-graphics/grounding/PROTOCOL.md +64 -0
  338. package/.agents/skills/motion-graphics/grounding/locate.mjs +227 -0
  339. package/.agents/skills/motion-graphics/phases/source/guide.md +21 -0
  340. package/.agents/skills/motion-graphics/references/builder-contract.md +38 -0
  341. package/.agents/skills/motion-graphics/references/motion-vocabulary.md +43 -0
  342. package/.agents/skills/motion-graphics/references/shot-plan-ir.md +54 -0
  343. package/.agents/skills/motion-graphics/samples/asset-fusion/_ref-circle-highlight.html +306 -0
  344. package/.agents/skills/product-launch-video/SKILL.md +208 -0
  345. package/.agents/skills/product-launch-video/references/cut-catalog.md +220 -0
  346. package/.agents/skills/product-launch-video/references/motion-language.md +156 -0
  347. package/.agents/skills/product-launch-video/references/story-design.md +373 -0
  348. package/.agents/skills/product-launch-video/references/visual-design.md +126 -0
  349. package/.agents/skills/product-launch-video/scripts/assemble-index.mjs +570 -0
  350. package/.agents/skills/product-launch-video/scripts/audio.mjs +260 -0
  351. package/.agents/skills/product-launch-video/scripts/build-frame.mjs +536 -0
  352. package/.agents/skills/product-launch-video/scripts/captions.mjs +508 -0
  353. package/.agents/skills/product-launch-video/scripts/lib/assets.mjs +55 -0
  354. package/.agents/skills/product-launch-video/scripts/lib/dimensions.mjs +45 -0
  355. package/.agents/skills/product-launch-video/scripts/lib/pad-frame-duration.mjs +36 -0
  356. package/.agents/skills/product-launch-video/scripts/lib/pad-frame-duration.test.mjs +76 -0
  357. package/.agents/skills/product-launch-video/scripts/lib/storyboard.mjs +249 -0
  358. package/.agents/skills/product-launch-video/scripts/lib/tokens.mjs +204 -0
  359. package/.agents/skills/product-launch-video/scripts/lib/transition-registry.mjs +38 -0
  360. package/.agents/skills/product-launch-video/scripts/lib/transitions.json +71 -0
  361. package/.agents/skills/product-launch-video/scripts/stage-assets.mjs +39 -0
  362. package/.agents/skills/product-launch-video/scripts/transitions.mjs +327 -0
  363. package/.agents/skills/product-launch-video/sub-agents/frame-worker.md +75 -0
  364. package/.agents/skills/slideshow/SKILL.md +550 -0
  365. package/.agents/skills/slideshow/references/standalone-harness.md +1020 -0
  366. package/.agents/skills/talking-head-recut/NOTICE.md +39 -0
  367. package/.agents/skills/talking-head-recut/SKILL.md +1195 -0
  368. package/.agents/skills/talking-head-recut/assets/vendor/gsap.min.js +10 -0
  369. package/.agents/skills/talking-head-recut/references/DESIGN_INDEX.md +145 -0
  370. package/.agents/skills/talking-head-recut/references/frames/clean.html +92 -0
  371. package/.agents/skills/talking-head-recut/references/frames/hairline.html +171 -0
  372. package/.agents/skills/talking-head-recut/references/frames/polaroid.html +131 -0
  373. package/.agents/skills/talking-head-recut/references/layouts/overlay.html +164 -0
  374. package/.agents/skills/talking-head-recut/references/layouts/pip.html +143 -0
  375. package/.agents/skills/talking-head-recut/references/layouts/split.html +121 -0
  376. package/.agents/skills/talking-head-recut/references/layouts/stack.html +109 -0
  377. package/.agents/skills/talking-head-recut/references/styles/academic.html +172 -0
  378. package/.agents/skills/talking-head-recut/references/styles/audit.html +170 -0
  379. package/.agents/skills/talking-head-recut/references/styles/editorial.html +189 -0
  380. package/.agents/skills/talking-head-recut/references/styles/geom.html +181 -0
  381. package/.agents/skills/talking-head-recut/references/styles/minimal.html +130 -0
  382. package/.agents/skills/talking-head-recut/references/styles/spotlight.html +142 -0
  383. package/.agents/skills/talking-head-recut/references/styles/swiss.html +188 -0
  384. package/.agents/skills/talking-head-recut/references/styles/terminal.html +196 -0
  385. package/.agents/skills/talking-head-recut/references/styles/whiteboard.html +170 -0
  386. package/.agents/skills/talking-head-recut/references/styles/xhs.html +206 -0
  387. package/.agents/skills/vidfarm-media/SKILL.md +85 -0
  388. package/.agents/skills/vidfarm-media/references/tts.md +54 -0
  389. package/.agents/skills/vidfarm-media/scripts/audio.mjs +628 -0
  390. package/.agents/skills/vidfarm-media/scripts/wait-bgm.mjs +31 -0
  391. package/.agents/skills/website-to-video/SKILL.md +145 -0
  392. package/.agents/skills/website-to-video/assets/sfx/CREDITS.md +35 -0
  393. package/.agents/skills/website-to-video/assets/sfx/manifest.json +97 -0
  394. package/.agents/skills/website-to-video/references/beat-builder-guide.md +263 -0
  395. package/.agents/skills/website-to-video/references/capabilities.md +713 -0
  396. package/.agents/skills/website-to-video/references/step-0-capture.md +55 -0
  397. package/.agents/skills/website-to-video/references/step-1-design.md +333 -0
  398. package/.agents/skills/website-to-video/references/step-2-brief.md +178 -0
  399. package/.agents/skills/website-to-video/references/step-3-storyboard.md +572 -0
  400. package/.agents/skills/website-to-video/references/step-4-vo.md +225 -0
  401. package/.agents/skills/website-to-video/references/step-5-build.md +483 -0
  402. package/.agents/skills/website-to-video/references/step-6-validate.md +341 -0
  403. package/.agents/skills/website-to-video/scripts/w2h-verify.mjs +759 -0
  404. package/SKILL.director.md +37 -0
  405. package/SKILL.platform.md +6 -1
  406. package/demo/dist/app.css +1 -1
  407. package/demo/dist/app.js +369 -85
  408. package/dist/src/app.js +630 -140
  409. package/dist/src/cli.js +529 -12
  410. package/dist/src/composition-runtime.js +262 -3
  411. package/dist/src/config.js +13 -0
  412. package/dist/src/devcli/doctor.js +192 -0
  413. package/dist/src/devcli/hyperframes-cli.js +118 -0
  414. package/dist/src/devcli/skills.js +476 -0
  415. package/dist/src/devcli/stills.js +249 -0
  416. package/dist/src/editor-chat.js +256 -0
  417. package/dist/src/frontend/homepage-client.js +50 -1
  418. package/dist/src/frontend/homepage-shared.js +197 -0
  419. package/dist/src/frontend/homepage-store.js +10 -1
  420. package/dist/src/frontend/homepage-view.js +43 -5
  421. package/dist/src/homepage.js +51 -18
  422. package/dist/src/hyperframes/composition.js +5 -5
  423. package/dist/src/services/composition-lint.js +199 -0
  424. package/dist/src/services/hyperframes.js +67 -13
  425. package/dist/src/services/serverless-records.js +9 -0
  426. package/package.json +2 -1
  427. package/public/assets/homepage-client-app.js +23 -23
@@ -0,0 +1,249 @@
1
+ // `vidfarm stills` — render PNG still frames of the CURRENT composition
2
+ // in-process, without a full MP4 render. This is the coding agent's visual
3
+ // self-verification loop: edit composition.html, render 3-8 stills, look at
4
+ // them, iterate.
5
+ //
6
+ // Implementation mirrors HyperframesService.renderStill (src/services/
7
+ // hyperframes.ts): stage the composition as a HyperFrames project dir, drive
8
+ // @hyperframes/producer's createRenderJob/executeRenderJob with the
9
+ // "png-sequence" format, then pick the captured frames nearest each requested
10
+ // timestamp. Deliberately NOT importing HyperframesService — that class drags
11
+ // in config.ts (which mkdirs at import) and the whole cloud-render surface;
12
+ // this is a lean local-file implementation.
13
+ //
14
+ // Cost control: png-sequence captures EVERY frame on the fps grid, so we
15
+ // (a) cap the staged composition's duration at the last requested timestamp
16
+ // and (b) render on a coarse integer fps grid (4fps → 0.25s accuracy, dropped
17
+ // to 2/1fps for long timelines) instead of the composition's native 30fps.
18
+ // Requested timestamps are snapped to that grid; the actually-captured time
19
+ // is reported next to each written PNG.
20
+ import { copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
21
+ import os from "node:os";
22
+ import path from "node:path";
23
+ import { parseHTML } from "linkedom";
24
+ import { inspectComposition } from "./composition-edit.js";
25
+ const MAX_DEFAULT_STILLS = 8;
26
+ const MAX_GRID_FRAMES = 600;
27
+ // The in-process producer shells out to ffmpeg/ffprobe resolved from
28
+ // HYPERFRAMES_FFMPEG_PATH / HYPERFRAMES_FFPROBE_PATH (then PATH). Point it at
29
+ // the bundled static binaries, same as src/services/hyperframes.ts does.
30
+ async function ensureProducerFfmpegEnv() {
31
+ if (!process.env.HYPERFRAMES_FFMPEG_PATH?.trim()) {
32
+ try {
33
+ const mod = (await import("ffmpeg-static"));
34
+ const resolved = typeof mod === "string" ? mod : mod.default;
35
+ if (typeof resolved === "string" && resolved && existsSync(resolved)) {
36
+ process.env.HYPERFRAMES_FFMPEG_PATH = resolved;
37
+ }
38
+ }
39
+ catch { /* PATH fallback */ }
40
+ }
41
+ if (!process.env.HYPERFRAMES_FFPROBE_PATH?.trim()) {
42
+ try {
43
+ const mod = (await import("ffprobe-static"));
44
+ const resolved = (mod.path ?? mod.default?.path);
45
+ if (typeof resolved === "string" && resolved && existsSync(resolved)) {
46
+ process.env.HYPERFRAMES_FFPROBE_PATH = resolved;
47
+ }
48
+ }
49
+ catch { /* PATH fallback */ }
50
+ }
51
+ }
52
+ /**
53
+ * Default timestamps when --at is omitted: the midpoint of each distinct
54
+ * scene clip on the LOWEST video track (capped at 8), else 0 / mid / end of
55
+ * the composition.
56
+ */
57
+ export function defaultStillTimestamps(layers, durationSeconds) {
58
+ const isSceneClip = (layer) => layer.duration > 0 && (layer.is_timeline_proxy ||
59
+ layer.kind === "video" ||
60
+ layer.kind === "scene" ||
61
+ (layer.src !== null && /\.(mp4|mov|webm|m4v|mkv)(\?|#|$)/i.test(layer.src)) ||
62
+ // Canvas-filling clips of any kind read as scenes (image scenes,
63
+ // solid/title cards) — overlays/captions are never full-canvas.
64
+ layer.is_full_canvas);
65
+ const scenes = layers.filter(isSceneClip);
66
+ if (scenes.length > 0) {
67
+ const lowestTrack = Math.min(...scenes.map((layer) => layer.track));
68
+ const seen = new Set();
69
+ const midpoints = [];
70
+ for (const layer of scenes.filter((l) => l.track === lowestTrack).sort((a, b) => a.start - b.start)) {
71
+ const key = layer.key || `${layer.start}-${layer.duration}`;
72
+ if (seen.has(key))
73
+ continue;
74
+ seen.add(key);
75
+ midpoints.push(layer.start + layer.duration / 2);
76
+ if (midpoints.length >= MAX_DEFAULT_STILLS)
77
+ break;
78
+ }
79
+ if (midpoints.length > 0)
80
+ return midpoints;
81
+ }
82
+ if (durationSeconds <= 0)
83
+ return [0];
84
+ return [...new Set([0, durationSeconds / 2, Math.max(0, durationSeconds - 0.5)])];
85
+ }
86
+ // Pick the coarsest integer fps grid that keeps capture cheap while staying
87
+ // within 0.25s of any requested timestamp where possible.
88
+ function pickGridFps(maxTimestamp) {
89
+ for (const fps of [4, 2, 1]) {
90
+ if ((maxTimestamp + 1) * fps <= MAX_GRID_FRAMES)
91
+ return fps;
92
+ }
93
+ return 1;
94
+ }
95
+ // Cap the staged copy's root data-duration so the producer never captures
96
+ // frames past the last requested still.
97
+ function capCompositionDuration(html, capSeconds) {
98
+ const { document } = parseHTML(html);
99
+ const root = document.querySelector("[data-composition-id]");
100
+ if (!root)
101
+ throw new Error("Composition root ([data-composition-id]) not found in composition.html.");
102
+ const existing = Number.parseFloat(root.getAttribute("data-duration") ?? "");
103
+ const capped = Number.isFinite(existing) && existing > 0 ? Math.min(existing, capSeconds) : capSeconds;
104
+ root.setAttribute("data-duration", String(Number(capped.toFixed(3))));
105
+ return `<!doctype html>\n${document.documentElement.outerHTML}`;
106
+ }
107
+ // Same guard as renderLocal: libx264-adjacent tooling (and the runtime's
108
+ // canvas sizing) prefers even data-width/data-height.
109
+ function forceEvenDimensions(html) {
110
+ return html.replace(/\bdata-(width|height)=(["'])(\d+)\2/gi, (match, dim, quote, value) => {
111
+ const n = Number.parseInt(value, 10);
112
+ if (!Number.isFinite(n) || n % 2 === 0)
113
+ return match;
114
+ return `data-${dim}=${quote}${n - 1}${quote}`;
115
+ });
116
+ }
117
+ /**
118
+ * Stage the composition as a HyperFrames project dir: the (duration-capped)
119
+ * composition.html becomes index.html, and every sibling file/dir is copied in
120
+ * so relative asset references keep resolving. Mirrors stageProject in
121
+ * src/services/hyperframes.ts, but against a local composition dir.
122
+ */
123
+ function stageCompositionProject(htmlPath, stagedHtml) {
124
+ const sourceDir = path.dirname(htmlPath);
125
+ const projectDir = mkdtempSync(path.join(os.tmpdir(), "vidfarm-stills-"));
126
+ writeFileSync(path.join(projectDir, "index.html"), stagedHtml, "utf8");
127
+ for (const entry of readdirSync(sourceDir)) {
128
+ if (entry === path.basename(htmlPath) || entry === "index.html" || entry === "stills")
129
+ continue;
130
+ const from = path.join(sourceDir, entry);
131
+ try {
132
+ const info = statSync(from);
133
+ if (info.isDirectory()) {
134
+ cpSync(from, path.join(projectDir, entry), { recursive: true, force: true });
135
+ }
136
+ else if (info.isFile()) {
137
+ copyFileSync(from, path.join(projectDir, entry));
138
+ }
139
+ }
140
+ catch {
141
+ // best-effort: a missing/unreadable sibling never blocks the stills
142
+ }
143
+ }
144
+ return projectDir;
145
+ }
146
+ export async function renderCompositionStills(input) {
147
+ const html = readFileSync(input.htmlPath, "utf8");
148
+ if (!html.includes("data-composition-id=")) {
149
+ throw new Error(`stills requires composition HTML with data-composition-id (${input.htmlPath}).`);
150
+ }
151
+ const info = inspectComposition(html);
152
+ const durationSeconds = info.duration_seconds > 0 ? info.duration_seconds : 1;
153
+ const requested = (input.at && input.at.length > 0
154
+ ? input.at
155
+ : defaultStillTimestamps(info.layers, durationSeconds))
156
+ .map((t) => Math.min(Math.max(0, t), Math.max(0, durationSeconds - 0.01)))
157
+ .sort((a, b) => a - b);
158
+ if (requested.length === 0)
159
+ throw new Error("stills: no timestamps to render.");
160
+ const gridFps = pickGridFps(requested[requested.length - 1]);
161
+ const capSeconds = Math.min(durationSeconds, requested[requested.length - 1] + 2 / gridFps);
162
+ await ensureProducerFfmpegEnv();
163
+ const stagedHtml = forceEvenDimensions(capCompositionDuration(html, capSeconds));
164
+ const projectDir = stageCompositionProject(input.htmlPath, stagedHtml);
165
+ const framesDir = mkdtempSync(path.join(os.tmpdir(), "vidfarm-stills-frames-"));
166
+ const outDir = path.resolve(input.outDir ?? path.join(path.dirname(input.htmlPath), "stills"));
167
+ mkdirSync(outDir, { recursive: true });
168
+ // Declarative vidfarm compositions carry no scripts (cloud saves strip
169
+ // them), so they can never satisfy the engine's window.__timelines
170
+ // sub-composition poll — which otherwise stalls capture-session init for the
171
+ // full 45s playerReadyTimeout before proceeding anyway. When the html has no
172
+ // timeline registration at all, cap that wait via the engine's env knob.
173
+ // Compositions that DO register GSAP timelines keep the default.
174
+ const priorReadyTimeout = process.env.PRODUCER_PLAYER_READY_TIMEOUT_MS;
175
+ if (!priorReadyTimeout?.trim() && !stagedHtml.includes("__timelines")) {
176
+ process.env.PRODUCER_PLAYER_READY_TIMEOUT_MS = "10000";
177
+ }
178
+ try {
179
+ // Same producer entry points renderStill uses; "png-sequence" writes the
180
+ // frames dir as frame_000001.png … at 1/gridFps intervals.
181
+ const { createRenderJob, executeRenderJob } = await import("@hyperframes/producer");
182
+ // Quiet mode (--json) swaps in a no-op logger so stdout stays parseable —
183
+ // the producer's default logger writes progress lines to stdout.
184
+ const silentLogger = { error() { }, warn() { }, info() { }, debug() { } };
185
+ const job = createRenderJob({
186
+ fps: gridFps,
187
+ quality: "high",
188
+ format: "png-sequence",
189
+ ...(input.quiet ? { logger: silentLogger } : {})
190
+ });
191
+ let lastStatus = null;
192
+ const executeRender = () => executeRenderJob(job, projectDir, framesDir, (progressJob) => {
193
+ if (!input.quiet && progressJob.status !== lastStatus) {
194
+ lastStatus = progressJob.status;
195
+ console.log(`\x1b[2m ${progressJob.status}\x1b[0m`);
196
+ }
197
+ });
198
+ // The engine also logs init/browser lines via console.log directly (they
199
+ // bypass the producer logger); in quiet mode reroute console stdout to
200
+ // stderr for the duration of the render so stdout stays pure JSON.
201
+ const originalConsole = { log: console.log, info: console.info, debug: console.debug };
202
+ if (input.quiet) {
203
+ const toStderr = (...args) => {
204
+ process.stderr.write(`${args.map((value) => (typeof value === "string" ? value : JSON.stringify(value))).join(" ")}\n`);
205
+ };
206
+ console.log = toStderr;
207
+ console.info = toStderr;
208
+ console.debug = toStderr;
209
+ }
210
+ try {
211
+ await executeRender();
212
+ }
213
+ finally {
214
+ console.log = originalConsole.log;
215
+ console.info = originalConsole.info;
216
+ console.debug = originalConsole.debug;
217
+ }
218
+ const frameFiles = readdirSync(framesDir)
219
+ .filter((name) => name.toLowerCase().endsWith(".png"))
220
+ .sort();
221
+ if (frameFiles.length === 0) {
222
+ throw new Error("stills render produced no frames — check the composition with `vidfarm lint` first.");
223
+ }
224
+ const stills = [];
225
+ for (const t of requested) {
226
+ const frameIndex = Math.min(frameFiles.length - 1, Math.max(0, Math.round(t * gridFps)));
227
+ const capturedSec = Number((frameIndex / gridFps).toFixed(3));
228
+ const stillName = `still-${capturedSec.toFixed(2).padStart(6, "0")}s.png`;
229
+ const dest = path.join(outDir, stillName);
230
+ copyFileSync(path.join(framesDir, frameFiles[frameIndex]), dest);
231
+ stills.push({ requested_sec: Number(t.toFixed(3)), captured_sec: capturedSec, path: dest });
232
+ }
233
+ return {
234
+ ok: true,
235
+ composition: input.htmlPath,
236
+ out_dir: outDir,
237
+ grid_fps: gridFps,
238
+ duration_seconds: durationSeconds,
239
+ stills
240
+ };
241
+ }
242
+ finally {
243
+ if (!priorReadyTimeout?.trim())
244
+ delete process.env.PRODUCER_PLAYER_READY_TIMEOUT_MS;
245
+ rmSync(framesDir, { recursive: true, force: true });
246
+ rmSync(projectDir, { recursive: true, force: true });
247
+ }
248
+ }
249
+ //# sourceMappingURL=stills.js.map
@@ -1,3 +1,6 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { lintCompositionHtml } from "./services/composition-lint.js";
1
4
  export const EDITOR_CHAT_PROVIDER_PRIORITY = [
2
5
  "openai",
3
6
  "gemini",
@@ -69,6 +72,7 @@ export function buildTemplateEditorChatSystemPrompt(input) {
69
72
  "The editor_context block includes last_export_url, last_export_title, last_export_status, and last_export_render_id when a render has succeeded in this session. Treat last_export_url as the current 'finished Export MP4' for this composition. If the user asks to approve, package, share, or schedule the exported video without providing an explicit URL, use last_export_url as the primary MP4 media (kind=video, role=primary) for POST /api/v1/approved/posts. If last_export_url is null, tell the user to Export first (or offer to call editor_action with action_type=export_composition on their behalf) instead of guessing a URL.",
70
73
  "Each layer in editor_context includes element_id (data-hf-id), optional slug (data-hf-slug), and optional note (data-hf-note). layer_key on any editor_action may be EITHER the element_id or the slug — prefer the slug when the user (or a viral DNA blurb) refers to a layer by its human name like 'the hero_image'. Use set_layer_identity to add/change a layer's slug or note. Slugs are the stable AI-friendly handle: when planning viral DNA notes, reference the element by its slug (e.g., 'raise the {{hero_image}} to full canvas at 4s') so future turns can resolve it deterministically.",
71
74
  "Treat the most recent <editor_context> block in the user message as the source of truth for current layer_keys, layer indices (called track in HTML: track=layer index, higher = drawn on top), durations, and composition_duration_seconds. Never invent layer_keys for remove/set/duplicate/split actions; only reference keys that appear there.",
75
+ "CRITICAL — verify your edits actually landed. An editor_action tool result only echoes the arguments you sent; it is NOT proof the change was applied or saved. The real outcome shows up in the NEXT <editor_context>. Before telling the user an edit succeeded, check that block: (1) if it contains save_status:\"error\" (or is_read_only:true / a save_error message), the composition is NOT being saved — do NOT claim any edit worked; tell the user exactly what save_error says (e.g. the video is read-only from this link and they should open their own copy to edit) and stop making mutating editor_action calls until it clears; (2) if it contains recent_action_results, each entry with ok:false is an edit from your previous turn that FAILED (read its error) — acknowledge the failure and retry or explain it rather than repeating a false success; (3) otherwise confirm the intended change is reflected in the layers (e.g. the new text on the target layer) before saying it's done. Never report a timeline edit as successful without this check.",
72
76
  "editor_context also carries the canvas shape and free time: composition_width, composition_height, and aspect_ratio (e.g. '9:16') describe the output frame — when generating media to place on this timeline, request the matching aspect_ratio on /videos/generate and /images/generate so it fills the canvas without letterboxing or wrong cropping. timeline_gaps is a precomputed array of {start, end, duration} spans (seconds) where NO visual layer is on screen — i.e. the literal blank/black moments. When the user says something like 'at 00:05:52 there's blank space, add X', map their timestamp (interpret mm:ss or seconds sensibly) to the covering entry in timeline_gaps, set the new layer's start to that gap's start (or the user's exact time if inside the gap) and clamp its duration to fit the gap unless the user asks otherwise. If the requested time is NOT inside any timeline_gaps entry, tell the user it already has content there and offer to overlay on a higher track or replace the existing layer instead of silently colliding.",
73
77
  "For add_layer, always supply a stable layer_key (e.g., vf-caption-hook, vf-hero-image, vf-narration-audio) so later tool calls in the same turn can reference the new layer without waiting for the next <editor_context>. The editor uses that layer_key as the new layer's id. Choose kind from video, image, audio, text, or html. Provide src (exact URL) for video/image/audio, text for text layers, and html for html layers. Pick a track index higher than every existing layer on the requested time range, or set start/duration so the range is collision-free. Default visual layers fill roughly the upper-middle of the canvas; text layers default to a lower-third band. Always include explanation.",
74
78
  "GENERATE-AND-PLACE (preferred for NEW AI footage/images on the timeline): when the user wants to generate a NEW AI video or image and drop it on the timeline — fill a blank gap, replace a scene, or add an overlay — call editor_action with action_type=generate_layer. This ONE action submits the primitive generate job, immediately drops a 'Generating…' placeholder clip into the target slot, and auto-swaps in the finished media when the job settles — you do NOT poll and you do NOT make a separate add_layer call. Set media_type ('video' or 'image'), prompt, and aspect_ratio to the editor_context aspect_ratio so it fills the canvas. Set intent: 'fill_gap' (also set start=the timeline_gaps span start and duration to fit the gap), 'replace_layer' (also set replace_layer_key to the scene's layer_key/slug — its timing and geometry, including full-canvas, are copied automatically), or 'add' (set start/track/geometry for an overlay). For video pass gen_duration (seconds of footage, e.g. 4) and, for character consistency, input_references=[cast reference_url or still_url]; for image pass prompt_attachments=[reference image URL]. Optionally set provider/model/resolution/generate_audio. Give explanation. Because generation is asynchronous, the placeholder appears now and the real media lands in a later turn — do NOT claim the media is finished; tell the user it is generating and will appear on the timeline shortly.",
@@ -89,6 +93,8 @@ export function buildTemplateEditorChatSystemPrompt(input) {
89
93
  "When you simply want to change a layer's media without moving it, prefer set_layer_media (kind must match: video↔video, image↔image, audio↔audio) — it keeps the same node, id, track, and geometry.",
90
94
  "CONTEXT AVAILABLE FOR USE — this composition can expose TWO video sources plus a text/scene context: (1) the ORIGINAL video — the raw uploaded source, with any baked-in subtitles/captions; (2) the DECOMPOSED video — a processed, caption-free copy Vidfarm produces asynchronously (subtitles removed) that the composition timeline renders from; and (3) the decomposed video context (transcript + per-scene visual descriptions + viral DNA) available via the video_context tool. The original and the decomposed video are NOT the same URL, and you should not carry both URLs in default context. When the user asks to reuse, re-render, or feed the composition's video into a primitive route (image extract, ai video edit, media/dedupe, video trim, video probe, etc.), call GET /api/v1/compositions/:forkId/remove-video-captions FIRST to read { status, original_source_url, mirrored_url } and pick correctly — here original_source_url is the ORIGINAL video and mirrored_url is the DECOMPOSED video: prefer the DECOMPOSED video (mirrored_url) when status=\"done\" and the downstream call should be caption-free (thumbnails, style references, re-cut hooks); prefer the ORIGINAL video (original_source_url) when the user explicitly wants the raw upload (branded intros, provenance, when captions are the subject matter). If status is \"pending\" and the user is not blocked on subtitle removal, use the ORIGINAL video and mention that the decomposed (caption-free) video is still processing. If status is \"none\" or \"failed\", there is no decomposed video yet — use the ORIGINAL video only. Do not call POST /remove-video-captions-poll unless the user explicitly asks to advance the subtitle-removal job. To strip burned-in captions from any OTHER video URL (not this fork's decompose flow), use the POST /api/v1/primitives/videos/remove-captions primitive instead.",
91
95
  "The OPTIONAL video_context tool returns this fork's decomposed video context: the source video's verbatim audio transcript (full text plus timestamped segments), a literal visual description of every scene, per-scene transcript excerpts, and viral DNA. Call it with the fork id from editor_context whenever the user asks what the source video says or shows, when writing/translating captions, hooks, or dubs that must match the spoken audio, or when you need scene-by-scene grounding before planning timeline edits. It is read-only and non-billing, so prefer it over guessing from frames or memory. If it returns status=\"none\", the fork was never smart-decomposed — offer to run POST /api/v1/compositions/:forkId/auto-decompose first. The same data is available via GET /api/v1/compositions/:forkId/video-context.json through http_request (useful for other forks).",
96
+ "KNOWLEDGE ON DEMAND (load_skill): deeper authoring knowledge ships as vidfarm skill packs you can read mid-conversation with the load_skill tool — the composition HTML contract (hyperframes-core), motion/animation craft and transition doctrine (hyperframes-animation), seek-safe keyframe patterns (hyperframes-keyframes), creative direction/palettes/typography (hyperframes-creative), caption identities (embedded-captions), media/audio resolution (vidfarm-media), and end-to-end workflow playbooks (product-launch-video, faceless-explainer, website-to-video, general-video, motion-graphics, slideshow, talking-head-recut). Call load_skill with the pack name to read its SKILL.md; it lists reference files you can then load by relative path (e.g. file='references/step-4-vo.md'). Use it when a request needs craft beyond the built-in preset vocabulary — designing a whole composition via replace_composition_html, picking motion or transition language for a style, or following a named workflow. Load only what the task needs, never bulk-load packs, and never paste large skill content back to the user.",
97
+ "replace_composition_html is validated before it reaches the editor: when the HTML violates the composition contract (missing data-composition-id, invalid clip timing, same-track overlaps, unknown preset names, media without src) the tool result comes back with rejected=true and lint_errors, and NOTHING is applied — fix the reported issues and call it again. lint_warnings are advisory and the action still applies (e.g. <script> tags survive local renders but are stripped on cloud/web saves — keep web-editor compositions declarative).",
92
98
  "TEMPLATE FORMAT — MATCH IT WHEN WRITING TEXT: the COMPOSITION BRIEF (stable template context in the system prompt) carries composition_context — WHAT THIS TEMPLATE IS: its format/genre and narrative arc (trend_tagline, hook, retention, payoff, preserve, avoid). ALWAYS read it before writing or adjusting ANY captions, text layers, or on-screen copy, and make your copy fit the template's format and voice — do NOT default to generic product/app ad copy. The format dictates the caption style: a text-message / DM / iMessage conversation template's captions must read like short back-and-forth chat messages between people (in-character, conversational), a 'story time' / talking-head template reads like first-person spoken narration, a listicle reads like punchy numbered items, a fake-news / headline template reads like a news chyron, etc. `preserve` usually names format cues that must stay (e.g. the messaging-bubble layout); `avoid` names what to keep out. When the template's format is a conversation, skit, or roleplay, keep the captions in that voice and only weave the product in the way that format allows (subtly, in-dialogue) rather than replacing the messages with feature bullets. If the brief's composition_context is thin or you are unsure what the video actually shows, call video_context (scene visual descriptions + transcript) to ground the format BEFORE writing captions.",
93
99
  "MY FILES: the user has a personal file library ('My Files') of uploaded assets — videos (mp4/mov/webm), images (png/jpg/jpeg/gif/webp/svg), audio (mp3/wav/m4a/aac), and documents (pdf/md/txt/csv) — organized into virtual folders. Use the browse_files tool to explore AND write it: call action=list (optionally with folder_path) to see what files and folders exist, action=search with a plain-language query to find files by MEANING (keyword + vector match over every file's name, folder, and metadata notes — prefer it over paging list when the library is large or the user references an asset vaguely), action=read with a file_id (copied from a prior list/search) to fetch one file, action=write with file_name + content (or source_url) to SAVE a file, and action=annotate with file_id + notes to attach metadata notes to any existing file. When the user references their own footage, brand assets, logos, music, a brief, or a script ('use my product photo', 'the video I uploaded', 'my brand logo', 'the script in my files'), browse_files search or list first to find it rather than asking them to re-upload or paste a URL. For text files (md/txt/csv/srt/vtt/json), read returns the full text_content inline so you can read a brief or script directly. For images/video/audio/pdf, read returns only a durable view_url — use that URL as media: drop it into the timeline via editor_action add_layer/set_layer_media, or pass it into primitive routes (images/edit source_image_url, videos/generate input_references, videos/download). Never invent file ids or view URLs — always list or search to discover the real ones first.",
94
100
  "SAVING CONTEXT TO MY FILES: browse_files action=write persists durable, reusable context the user (and future chats/agents) can read back. write saves text files (md/txt/csv/json/srt/vtt) via content, and IMPORTS media into the library via source_url (a durable URL from a finished primitive job or existing asset) — use source_url to persist a generated asset the user will want again (a character sprite card, a recurring background, a logo variant) instead of leaving it stranded in a job result. Use write to capture the Getting Started artifacts as Markdown: a product About.md (basic offer/product context) or a deeper Interview.md, plus awareness-levels.md, persuasive-angles.md, and ad-hooks.md distilled from the matching brainstorm outputs. When a brainstorm job returns strategy the user wants to keep, offer to save it (or save it) as the corresponding .md so it is not lost when the chat ends. Namescope every write under the right product/offer folder (see MY FILES IS MULTI-OFFER) — e.g. folder_path='acme-skincare', file_name='About.md'. Pass notes on write (or annotate afterwards) for any asset worth finding again: notes describe what the file is, who/what it depicts, and when to use it, and they are vector-embedded so action=search finds the file by meaning in future sessions. Before overwriting an existing file of the same name in the same folder, confirm with the user (writing the same name replaces it).",
@@ -451,4 +457,254 @@ export function compactEditorChatModelMessages(messages) {
451
457
  ...recentMessages
452
458
  ];
453
459
  }
460
+ // ——— Skill packs the load_skill chat tool can fetch on demand. Served from
461
+ // GET /skill/:name (SKILL.md) and GET /skill-pack/:name/files/<path>
462
+ // (reference files), vendored under .agents/skills. Keep this list in sync
463
+ // with the packs actually vendored in the repo.
464
+ export const EDITOR_CHAT_SKILL_PACKS = [
465
+ { name: "vidfarm-director", summary: "the full director playbook (REST + devcli flows)" },
466
+ { name: "hyperframes", summary: "workflow router for the HyperFrames skill suite" },
467
+ { name: "hyperframes-cli", summary: "HyperFrames CLI dev loop (init/lint/validate/inspect/snapshot/render)" },
468
+ { name: "hyperframes-core", summary: "composition HTML contract: data-* timing attrs, tracks, clips, determinism rules" },
469
+ { name: "hyperframes-animation", summary: "motion rules catalog, scene blueprints, transition doctrine, runtime adapters" },
470
+ { name: "hyperframes-keyframes", summary: "seek-safe keyframe/CSS animation patterns" },
471
+ { name: "hyperframes-creative", summary: "design specs, palettes, typography, house style, narration craft" },
472
+ { name: "vidfarm-media", summary: "narration TTS/STT, BGM/SFX/media resolution through vidfarm primitives" },
473
+ { name: "embedded-captions", summary: "caption identity catalog + subject-occlusion caption pipeline" },
474
+ { name: "talking-head-recut", summary: "timed graphic overlay cards on existing footage" },
475
+ { name: "product-launch-video", summary: "product/launch promo workflow (time-coded shot sequences)" },
476
+ { name: "faceless-explainer", summary: "text-to-faceless-explainer workflow" },
477
+ { name: "website-to-video", summary: "website capture to video workflow" },
478
+ { name: "general-video", summary: "general multi-scene composition workflow" },
479
+ { name: "motion-graphics", summary: "short motion-graphic workflow with category modules" },
480
+ { name: "slideshow", summary: "interactive slideshow/deck composition contract" }
481
+ ];
482
+ export const EDITOR_CHAT_SKILL_FILE_MAX_CHARS = 24_000;
483
+ // ——— Stream guardrails shared by the in-process chat (src/app.ts) and the
484
+ // deployed Lambda copy (infra/lambda/editor-chat.ts). One implementation so
485
+ // the two stream loops cannot drift.
486
+ export const EDITOR_CHAT_JOB_ID_PATTERN = /\bjob_[a-z0-9]+\b/gi;
487
+ const HTTP_TOOL_CLAIM_PATTERN = /\b(?:http_request|REST API call|API call|GET\s+https?:\/\/|POST\s+https?:\/\/|GET\s+\/api\/|POST\s+\/api\/)/i;
488
+ export function collectJobIdsFromUnknown(value, output = new Set()) {
489
+ if (typeof value === "string") {
490
+ for (const match of value.matchAll(EDITOR_CHAT_JOB_ID_PATTERN)) {
491
+ output.add(match[0]);
492
+ }
493
+ return output;
494
+ }
495
+ if (Array.isArray(value)) {
496
+ for (const entry of value) {
497
+ collectJobIdsFromUnknown(entry, output);
498
+ }
499
+ return output;
500
+ }
501
+ if (value && typeof value === "object") {
502
+ for (const entry of Object.values(value)) {
503
+ collectJobIdsFromUnknown(entry, output);
504
+ }
505
+ }
506
+ return output;
507
+ }
508
+ export function collectVerifiedHttpJobIds(toolResult) {
509
+ if (!toolResult || typeof toolResult !== "object") {
510
+ return new Set();
511
+ }
512
+ const record = toolResult;
513
+ return collectJobIdsFromUnknown(record.response?.body ?? record.result?.body);
514
+ }
515
+ export function readAsyncJobHandoff(output) {
516
+ if (!output || typeof output !== "object") {
517
+ return null;
518
+ }
519
+ const record = output;
520
+ const body = record.response?.body;
521
+ if (!body || typeof body !== "object") {
522
+ return null;
523
+ }
524
+ const job = body;
525
+ if (typeof job.job_id !== "string" || typeof job.status !== "string") {
526
+ return null;
527
+ }
528
+ if (job.status === "succeeded" || job.status === "failed" || job.status === "cancelled") {
529
+ return null;
530
+ }
531
+ return {
532
+ jobId: job.job_id,
533
+ tracer: typeof job.tracer === "string" ? job.tracer : null,
534
+ status: job.status,
535
+ isNewQueuedJob: record.request?.method === "POST" && record.response?.status === 202
536
+ };
537
+ }
538
+ export function buildAsyncJobHandoffText(input) {
539
+ // "Render …" wording is load-bearing: template-editor-chat.tsx emits the
540
+ // identical strings client-side and dedupes with a substring check, so any
541
+ // wording change here double-prints every queued-job handoff.
542
+ if (!input.isNewQueuedJob) {
543
+ return `Render \`${input.jobId}\` is ${input.status}.`;
544
+ }
545
+ return [
546
+ `Started render \`${input.jobId}\`${input.tracer ? ` with tracer \`${input.tracer}\`` : ""}.`,
547
+ "If it takes a while, ask for an update later and I can check it for you."
548
+ ].join("\n\n");
549
+ }
550
+ export function buildEditorChatGuardrailNotice(input) {
551
+ const mentionedJobIds = collectJobIdsFromUnknown(input.assistantText);
552
+ const unverifiedJobIds = [...mentionedJobIds].filter((jobId) => !input.verifiedJobIds.has(jobId));
553
+ const notices = [];
554
+ if (input.redactedJobIds.size) {
555
+ notices.push("Guardrail: I withheld unverified job ID text because it did not come from a visible HTTP response card.");
556
+ }
557
+ else if (unverifiedJobIds.length) {
558
+ notices.push("Guardrail: I mentioned an unverified job ID. Ignore it unless it appears in a visible HTTP response card.");
559
+ }
560
+ if (!input.hasHttpRequestResult && HTTP_TOOL_CLAIM_PATTERN.test(input.assistantText)) {
561
+ notices.push("Guardrail: no `http_request` tool result was recorded for this response, so any claimed REST/API call should be treated as not executed.");
562
+ }
563
+ return notices.length ? `\n\n${notices.join("\n\n")}` : "";
564
+ }
565
+ export function redactUnverifiedJobIds(input) {
566
+ return input.text.replace(EDITOR_CHAT_JOB_ID_PATTERN, (jobId) => {
567
+ if (input.verifiedJobIds.has(jobId)) {
568
+ return jobId;
569
+ }
570
+ input.redactedJobIds.add(jobId);
571
+ return "[unverified job id]";
572
+ });
573
+ }
574
+ const SKILL_PACK_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
575
+ const SKILL_PACK_TEXT_FILE_RE = /\.(md|txt|json|ya?ml|mjs|cjs|js|html|css|srt|vtt|sh)$/i;
576
+ // Shared implementation behind the load_skill chat tool (registered in both
577
+ // src/app.ts and infra/lambda/editor-chat.ts). Fetches vendored skill-pack
578
+ // content from the vidfarm host's public /skill + /skill-pack routes.
579
+ export async function loadEditorChatSkillContent(input) {
580
+ const skill = input.skill.trim().toLowerCase().replace(/^\//, "");
581
+ if (!SKILL_PACK_NAME_RE.test(skill)) {
582
+ return { skill: input.skill, error: "Invalid skill pack name." };
583
+ }
584
+ const knownPack = EDITOR_CHAT_SKILL_PACKS.some((pack) => pack.name === skill);
585
+ const file = (input.file ?? "").trim().replace(/^\.?\//, "");
586
+ if (file && (file.includes("..") || !SKILL_PACK_TEXT_FILE_RE.test(file))) {
587
+ return { skill, file, error: "file must be a text file path inside the pack (no '..'; markdown/scripts/data files only)." };
588
+ }
589
+ const base = input.publicBaseUrl.replace(/\/$/, "");
590
+ const url = file
591
+ ? `${base}/skill-pack/${encodeURIComponent(skill)}/files/${file.split("/").map(encodeURIComponent).join("/")}`
592
+ : `${base}/skill/${encodeURIComponent(skill)}`;
593
+ const timeoutController = new AbortController();
594
+ const timeoutId = setTimeout(() => {
595
+ timeoutController.abort(new Error("Skill fetch timed out after 30 seconds."));
596
+ }, 30_000);
597
+ try {
598
+ const signal = input.abortSignal
599
+ ? AbortSignal.any([input.abortSignal, timeoutController.signal])
600
+ : timeoutController.signal;
601
+ const response = await fetch(url, { method: "GET", signal });
602
+ if (!response.ok) {
603
+ return {
604
+ explanation: input.explanation ?? null,
605
+ skill,
606
+ file: file || null,
607
+ status: response.status,
608
+ error: `Skill content unavailable (${response.status}).${knownPack ? "" : " Unknown pack — the tool description lists the available packs."}`
609
+ };
610
+ }
611
+ const raw = await response.text();
612
+ const truncated = raw.length > EDITOR_CHAT_SKILL_FILE_MAX_CHARS;
613
+ return {
614
+ explanation: input.explanation ?? null,
615
+ skill,
616
+ file: file || "SKILL.md",
617
+ truncated,
618
+ content: truncated
619
+ ? `${raw.slice(0, EDITOR_CHAT_SKILL_FILE_MAX_CHARS)}\n\n[truncated — load a specific reference file for the rest]`
620
+ : raw
621
+ };
622
+ }
623
+ catch (error) {
624
+ return {
625
+ explanation: input.explanation ?? null,
626
+ skill,
627
+ file: file || null,
628
+ error: error instanceof Error ? error.message : "Skill fetch failed."
629
+ };
630
+ }
631
+ finally {
632
+ clearTimeout(timeoutId);
633
+ }
634
+ }
635
+ // ——— load_skill tool factory + replace_composition_html lint gate, shared by
636
+ // both chat copies (src/app.ts and infra/lambda/editor-chat.ts) so the tool
637
+ // description, schema, and gating semantics cannot drift between deployments.
638
+ export function createLoadSkillTool(input) {
639
+ return tool({
640
+ description: `OPTIONAL knowledge fetch: load a vidfarm skill pack document on demand instead of guessing at authoring craft. Available packs: ${EDITOR_CHAT_SKILL_PACKS.map((pack) => `${pack.name} — ${pack.summary}`).join("; ")}. Call with just skill=<name> to read the pack's SKILL.md (it lists the pack's reference files), then load a specific reference with file=<relative path> (e.g. 'references/step-4-vo.md', 'rules-index.md'). Text files only; long files are truncated. Read-only and non-billing. Load only what the current task needs.`,
641
+ inputSchema: z.object({
642
+ skill: z.string().min(1).describe("Skill pack name, e.g. 'hyperframes-animation'."),
643
+ file: z.string().optional().describe("Optional text file path inside the pack. Omit to fetch SKILL.md."),
644
+ explanation: z.string().optional().describe("Short reason for loading this knowledge.")
645
+ }),
646
+ execute: async ({ skill, file, explanation }) => loadEditorChatSkillContent({
647
+ skill,
648
+ file,
649
+ explanation,
650
+ publicBaseUrl: input.publicBaseUrl,
651
+ abortSignal: input.abortSignal
652
+ })
653
+ });
654
+ }
655
+ // Lint gate for editor_action's replace_composition_html escape hatch. Returns
656
+ // the tool result to send INSTEAD of the echoed args, or null to pass through.
657
+ // A rejected result omits action_type, so the browser's extractEditorAction
658
+ // never applies it — rejected payloads are inert by construction.
659
+ export function lintReplaceCompositionAction(args) {
660
+ if (args.action_type !== "replace_composition_html") {
661
+ return null;
662
+ }
663
+ const lint = lintCompositionHtml(typeof args.composition_html === "string" ? args.composition_html : "");
664
+ if (!lint.ok) {
665
+ return {
666
+ rejected: true,
667
+ reason: "composition_html failed validation and was NOT applied. Fix the lint_errors and call replace_composition_html again.",
668
+ lint_errors: lint.errors,
669
+ lint_warnings: lint.warnings,
670
+ explanation: args.explanation
671
+ };
672
+ }
673
+ if (lint.warnings.length) {
674
+ return { ...args, lint_warnings: lint.warnings };
675
+ }
676
+ return null;
677
+ }
678
+ // Streaming wrapper around redactUnverifiedJobIds: ai-sdk text deltas split at
679
+ // arbitrary byte boundaries, so a verified id like job_abc123 can arrive as
680
+ // "…job_ab" + "c123…" — redacting each chunk independently mangles the leading
681
+ // fragment. Hold back a trailing potential id fragment until the next delta
682
+ // (or flush) completes it.
683
+ export function createStreamingJobIdRedactor(input) {
684
+ let carry = "";
685
+ const TRAILING_FRAGMENT = /\bjob_[a-z0-9]*$/i;
686
+ const MAX_CARRY_CHARS = 64;
687
+ const redact = (text) => redactUnverifiedJobIds({
688
+ text,
689
+ verifiedJobIds: input.verifiedJobIds,
690
+ redactedJobIds: input.redactedJobIds
691
+ });
692
+ return {
693
+ push(text) {
694
+ let combined = carry + text;
695
+ carry = "";
696
+ const match = combined.match(TRAILING_FRAGMENT);
697
+ if (match && match[0].length <= MAX_CARRY_CHARS) {
698
+ carry = match[0];
699
+ combined = combined.slice(0, combined.length - carry.length);
700
+ }
701
+ return combined ? redact(combined) : "";
702
+ },
703
+ flush() {
704
+ const rest = carry;
705
+ carry = "";
706
+ return rest ? redact(rest) : "";
707
+ }
708
+ };
709
+ }
454
710
  //# sourceMappingURL=editor-chat.js.map
@@ -65,6 +65,10 @@ function HomepageClientApp({ boot }) {
65
65
  const bookmarks = useStore(store, (state) => state.bookmarks);
66
66
  const loading = useStore(store, (state) => state.loading);
67
67
  const error = useStore(store, (state) => state.error);
68
+ const view = useStore(store, (state) => state.view);
69
+ const popularTemplates = useStore(store, (state) => state.popularTemplates);
70
+ const popularLoading = useStore(store, (state) => state.popularLoading);
71
+ const popularError = useStore(store, (state) => state.popularError);
68
72
  const [bookmarksReady, setBookmarksReady] = useState(false);
69
73
  const visibleTemplates = useMemo(() => filterTemplates({ templates, searchQuery, bookmarksOnly, bookmarks }), [templates, searchQuery, bookmarksOnly, bookmarks]);
70
74
  useEffect(() => {
@@ -121,6 +125,51 @@ function HomepageClientApp({ boot }) {
121
125
  }, 8000);
122
126
  return () => window.clearInterval(timer);
123
127
  }, [hasProcessingTemplates, refreshFeed]);
128
+ // The curated Popular picks come from a separate endpoint and are the same
129
+ // for everyone, so they're fetched lazily the first time the toggle is opened
130
+ // (and re-fetched, without a spinner, while any pick is still downloading).
131
+ const refreshPopular = useCallback(async (options) => {
132
+ if (options?.showLoading) {
133
+ store.getState().setPopularLoading(true);
134
+ }
135
+ try {
136
+ const response = await fetch("/discover/popular", {
137
+ credentials: "same-origin",
138
+ cache: "no-store",
139
+ headers: { Accept: "application/json" }
140
+ });
141
+ const payload = await response.json().catch(() => ({}));
142
+ if (!response.ok) {
143
+ throw new Error(typeof payload.error === "string" && payload.error ? payload.error : "Unable to load popular formats.");
144
+ }
145
+ store.getState().setPopularTemplates(Array.isArray(payload.templates) ? payload.templates : []);
146
+ }
147
+ catch (fetchError) {
148
+ if (options?.showLoading) {
149
+ store.getState().setPopularError(fetchError instanceof Error ? fetchError.message : "Unable to load popular formats.");
150
+ }
151
+ else {
152
+ debugError("homepage.popular_refresh_failed", { message: fetchError instanceof Error ? fetchError.message : String(fetchError) });
153
+ }
154
+ }
155
+ }, [store]);
156
+ const popularLoaded = useStore(store, (state) => state.popularLoaded);
157
+ useEffect(() => {
158
+ if (view !== "popular" || popularLoaded) {
159
+ return;
160
+ }
161
+ void refreshPopular({ showLoading: true });
162
+ }, [view, popularLoaded, refreshPopular]);
163
+ const hasProcessingPopular = view === "popular" && popularTemplates.some((template) => template.status === "processing");
164
+ useEffect(() => {
165
+ if (!hasProcessingPopular) {
166
+ return;
167
+ }
168
+ const timer = window.setInterval(() => {
169
+ void refreshPopular();
170
+ }, 8000);
171
+ return () => window.clearInterval(timer);
172
+ }, [hasProcessingPopular, refreshPopular]);
124
173
  const handleDeleteTemplate = useCallback(async (templateId) => {
125
174
  try {
126
175
  const response = await fetch(`/discover/templates/${encodeURIComponent(templateId)}`, {
@@ -219,7 +268,7 @@ function HomepageClientApp({ boot }) {
219
268
  return { ok: false, error: submitError instanceof Error ? submitError.message : "Unable to add that video." };
220
269
  }
221
270
  }, [refreshFeed]);
222
- return (_jsx(HomepageShell, { account: boot.account, templates: templates, visibleTemplates: visibleTemplates, searchQuery: searchQuery, bookmarksOnly: bookmarksOnly, bookmarks: bookmarks, loading: loading, error: error, skillEndpointPrefix: boot.skillEndpointPrefix, onSearchQueryChange: (value) => store.getState().setSearchQuery(value), onBookmarksOnlyChange: (value) => store.getState().setBookmarksOnly(value), onToggleBookmark: (templateId) => store.getState().toggleBookmark(templateId), onAddTemplate: handleAddTemplate, onDeleteTemplate: handleDeleteTemplate }));
271
+ return (_jsx(HomepageShell, { account: boot.account, templates: templates, visibleTemplates: visibleTemplates, searchQuery: searchQuery, bookmarksOnly: bookmarksOnly, bookmarks: bookmarks, loading: loading, error: error, view: view, popularTemplates: popularTemplates, popularLoading: popularLoading, popularError: popularError, skillEndpointPrefix: boot.skillEndpointPrefix, onSearchQueryChange: (value) => store.getState().setSearchQuery(value), onBookmarksOnlyChange: (value) => store.getState().setBookmarksOnly(value), onViewChange: (value) => store.getState().setView(value), onToggleBookmark: (templateId) => store.getState().toggleBookmark(templateId), onAddTemplate: handleAddTemplate, onDeleteTemplate: handleDeleteTemplate }));
223
272
  }
224
273
  const boot = readBoot();
225
274
  const root = document.getElementById("homepage-root");