zilmate 1.9.3 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/.agents/skills/ad-creative/SKILL.md +362 -0
  2. package/.agents/skills/ad-creative/evals/evals.json +90 -0
  3. package/.agents/skills/ad-creative/references/generative-tools.md +637 -0
  4. package/.agents/skills/ad-creative/references/platform-specs.md +213 -0
  5. package/.agents/skills/ai-seo/SKILL.md +489 -0
  6. package/.agents/skills/ai-seo/evals/evals.json +90 -0
  7. package/.agents/skills/ai-seo/references/content-patterns.md +285 -0
  8. package/.agents/skills/ai-seo/references/content-types.md +71 -0
  9. package/.agents/skills/ai-seo/references/okf.md +104 -0
  10. package/.agents/skills/ai-seo/references/platform-ranking-factors.md +154 -0
  11. package/.agents/skills/clerk-backend-api/SKILL.md +426 -0
  12. package/.agents/skills/clerk-backend-api/evals/evals.json +87 -0
  13. package/.agents/skills/clerk-backend-api/scripts/api-specs-context.sh +30 -0
  14. package/.agents/skills/clerk-backend-api/scripts/execute-request.sh +88 -0
  15. package/.agents/skills/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
  16. package/.agents/skills/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
  17. package/.agents/skills/clerk-backend-api/scripts/extract-tags.js +14 -0
  18. package/.agents/skills/copy-editing/SKILL.md +457 -0
  19. package/.agents/skills/copy-editing/evals/evals.json +89 -0
  20. package/.agents/skills/copy-editing/references/checklist.md +66 -0
  21. package/.agents/skills/copy-editing/references/content-refresh.md +38 -0
  22. package/.agents/skills/copy-editing/references/plain-english-alternatives.md +394 -0
  23. package/.agents/skills/hyperframes/SKILL.md +163 -0
  24. package/.agents/skills/hyperframes-cli/SKILL.md +109 -0
  25. package/.agents/skills/hyperframes-cli/references/doctor-browser.md +45 -0
  26. package/.agents/skills/hyperframes-cli/references/init-and-scaffold.md +51 -0
  27. package/.agents/skills/hyperframes-cli/references/lambda.md +132 -0
  28. package/.agents/skills/hyperframes-cli/references/lint-validate-inspect.md +121 -0
  29. package/.agents/skills/hyperframes-cli/references/preview-render.md +109 -0
  30. package/.agents/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
  31. package/.agents/skills/hyperframes-media/SKILL.md +97 -0
  32. package/.agents/skills/hyperframes-media/assets/sfx/CREDITS.md +35 -0
  33. package/.agents/skills/hyperframes-media/assets/sfx/manifest.json +97 -0
  34. package/.agents/skills/hyperframes-media/references/bgm.md +72 -0
  35. package/.agents/skills/hyperframes-media/references/captions/authoring.md +159 -0
  36. package/.agents/skills/hyperframes-media/references/captions/motion.md +87 -0
  37. package/.agents/skills/hyperframes-media/references/captions/transcript-handling.md +97 -0
  38. package/.agents/skills/hyperframes-media/references/remove-background.md +143 -0
  39. package/.agents/skills/hyperframes-media/references/requirements.md +29 -0
  40. package/.agents/skills/hyperframes-media/references/sfx.md +42 -0
  41. package/.agents/skills/hyperframes-media/references/transcribe.md +52 -0
  42. package/.agents/skills/hyperframes-media/references/tts-to-captions.md +24 -0
  43. package/.agents/skills/hyperframes-media/references/tts.md +135 -0
  44. package/.agents/skills/hyperframes-media/scripts/audio.mjs +282 -0
  45. package/.agents/skills/hyperframes-media/scripts/heygen-tts.mjs +121 -0
  46. package/.agents/skills/hyperframes-media/scripts/lib/bgm.mjs +235 -0
  47. package/.agents/skills/hyperframes-media/scripts/lib/heygen.mjs +131 -0
  48. package/.agents/skills/hyperframes-media/scripts/lib/sfx.mjs +128 -0
  49. package/.agents/skills/hyperframes-media/scripts/lib/tts.mjs +220 -0
  50. package/.agents/skills/hyperframes-media/scripts/lyria-recipe.py +128 -0
  51. package/.agents/skills/hyperframes-media/scripts/wait-bgm.mjs +167 -0
  52. package/.agents/skills/hyperframes-registry/SKILL.md +101 -0
  53. package/.agents/skills/hyperframes-registry/examples/add-block.md +51 -0
  54. package/.agents/skills/hyperframes-registry/examples/add-component.md +73 -0
  55. package/.agents/skills/hyperframes-registry/references/contributing.md +166 -0
  56. package/.agents/skills/hyperframes-registry/references/demo-html-pattern.md +54 -0
  57. package/.agents/skills/hyperframes-registry/references/discovery.md +215 -0
  58. package/.agents/skills/hyperframes-registry/references/install-locations.md +45 -0
  59. package/.agents/skills/hyperframes-registry/references/templates.md +417 -0
  60. package/.agents/skills/hyperframes-registry/references/wiring-blocks.md +61 -0
  61. package/.agents/skills/hyperframes-registry/references/wiring-components.md +39 -0
  62. package/.agents/skills/marketing-psychology/SKILL.md +455 -0
  63. package/.agents/skills/marketing-psychology/evals/evals.json +88 -0
  64. package/.agents/skills/mintlify/SKILL.md +328 -0
  65. package/.agents/skills/product-launch-video/SKILL.md +206 -0
  66. package/.agents/skills/product-launch-video/references/cut-catalog.md +220 -0
  67. package/.agents/skills/product-launch-video/references/motion-language.md +156 -0
  68. package/.agents/skills/product-launch-video/references/story-design.md +373 -0
  69. package/.agents/skills/product-launch-video/references/visual-design.md +126 -0
  70. package/.agents/skills/product-launch-video/scripts/assemble-index.mjs +570 -0
  71. package/.agents/skills/product-launch-video/scripts/audio.mjs +253 -0
  72. package/.agents/skills/product-launch-video/scripts/build-frame.mjs +536 -0
  73. package/.agents/skills/product-launch-video/scripts/captions.mjs +508 -0
  74. package/.agents/skills/product-launch-video/scripts/lib/assets.mjs +55 -0
  75. package/.agents/skills/product-launch-video/scripts/lib/dimensions.mjs +45 -0
  76. package/.agents/skills/product-launch-video/scripts/lib/storyboard.mjs +249 -0
  77. package/.agents/skills/product-launch-video/scripts/lib/tokens.mjs +204 -0
  78. package/.agents/skills/product-launch-video/scripts/lib/transition-registry.mjs +38 -0
  79. package/.agents/skills/product-launch-video/scripts/lib/transitions.json +71 -0
  80. package/.agents/skills/product-launch-video/scripts/stage-assets.mjs +39 -0
  81. package/.agents/skills/product-launch-video/scripts/transitions.mjs +320 -0
  82. package/.agents/skills/product-launch-video/sub-agents/frame-worker.md +74 -0
  83. package/.agents/skills/prompt-engineering-patterns/SKILL.md +144 -0
  84. package/.agents/skills/prompt-engineering-patterns/assets/few-shot-examples.json +106 -0
  85. package/.agents/skills/prompt-engineering-patterns/assets/prompt-template-library.md +264 -0
  86. package/.agents/skills/prompt-engineering-patterns/references/chain-of-thought.md +412 -0
  87. package/.agents/skills/prompt-engineering-patterns/references/details.md +338 -0
  88. package/.agents/skills/prompt-engineering-patterns/references/few-shot-learning.md +386 -0
  89. package/.agents/skills/prompt-engineering-patterns/references/prompt-optimization.md +428 -0
  90. package/.agents/skills/prompt-engineering-patterns/references/prompt-templates.md +484 -0
  91. package/.agents/skills/prompt-engineering-patterns/references/system-prompts.md +195 -0
  92. package/.agents/skills/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
  93. package/.agents/skills/remotion-to-hyperframes/SKILL.md +127 -0
  94. package/.agents/skills/remotion-to-hyperframes/assets/.gitkeep +0 -0
  95. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/run.sh +249 -0
  96. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/README.md +41 -0
  97. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +26 -0
  98. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +61 -0
  99. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/package.json +14 -0
  100. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/remotion.config.ts +13 -0
  101. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/Root.tsx +13 -0
  102. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/TitleCard.tsx +34 -0
  103. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/index.ts +4 -0
  104. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/tsconfig.json +15 -0
  105. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +54 -0
  106. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +29 -0
  107. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +118 -0
  108. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/package.json +14 -0
  109. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/remotion.config.ts +13 -0
  110. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/MultiScene.tsx +105 -0
  111. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/Root.tsx +13 -0
  112. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/index.ts +4 -0
  113. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/tsconfig.json +15 -0
  114. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/setup.sh +34 -0
  115. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/README.md +85 -0
  116. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +41 -0
  117. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +269 -0
  118. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/package.json +15 -0
  119. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/remotion.config.ts +13 -0
  120. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Root.tsx +27 -0
  121. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Stargazed.tsx +37 -0
  122. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/AnimatedNumber.tsx +23 -0
  123. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/StatCard.tsx +59 -0
  124. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/UnderlinedText.tsx +47 -0
  125. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/index.ts +4 -0
  126. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/OutroScene.tsx +18 -0
  127. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/StatsScene.tsx +34 -0
  128. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/TitleScene.tsx +55 -0
  129. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/tsconfig.json +15 -0
  130. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/README.md +51 -0
  131. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/01-use-state.tsx +29 -0
  132. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/02-use-effect-deps.tsx +29 -0
  133. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/03-async-metadata.tsx +39 -0
  134. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/04-third-party-react.tsx +30 -0
  135. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/05-lambda-config.tsx +38 -0
  136. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/06-warnings-only.tsx +36 -0
  137. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/07-custom-hook.tsx +28 -0
  138. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/08-mixed.tsx +41 -0
  139. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/expected.json +85 -0
  140. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/validate.sh +110 -0
  141. package/.agents/skills/remotion-to-hyperframes/references/api-map.md +142 -0
  142. package/.agents/skills/remotion-to-hyperframes/references/escape-hatch.md +115 -0
  143. package/.agents/skills/remotion-to-hyperframes/references/eval.md +140 -0
  144. package/.agents/skills/remotion-to-hyperframes/references/fonts.md +112 -0
  145. package/.agents/skills/remotion-to-hyperframes/references/limitations.md +136 -0
  146. package/.agents/skills/remotion-to-hyperframes/references/lottie.md +121 -0
  147. package/.agents/skills/remotion-to-hyperframes/references/media.md +149 -0
  148. package/.agents/skills/remotion-to-hyperframes/references/parameters.md +167 -0
  149. package/.agents/skills/remotion-to-hyperframes/references/sequencing.md +195 -0
  150. package/.agents/skills/remotion-to-hyperframes/references/timing.md +165 -0
  151. package/.agents/skills/remotion-to-hyperframes/references/transitions.md +114 -0
  152. package/.agents/skills/remotion-to-hyperframes/scripts/.gitkeep +0 -0
  153. package/.agents/skills/remotion-to-hyperframes/scripts/frame_strip.sh +107 -0
  154. package/.agents/skills/remotion-to-hyperframes/scripts/lint_source.py +358 -0
  155. package/.agents/skills/remotion-to-hyperframes/scripts/render_diff.sh +103 -0
  156. package/.agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/blocker.tsx +50 -0
  157. package/.agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/clean.tsx +46 -0
  158. package/.agents/skills/remotion-to-hyperframes/scripts/tests/smoke.sh +90 -0
  159. package/.agents/skills/seo-audit/SKILL.md +497 -0
  160. package/.agents/skills/seo-audit/evals/evals.json +136 -0
  161. package/.agents/skills/seo-audit/references/ai-writing-detection.md +200 -0
  162. package/.agents/skills/seo-audit/references/international-seo.md +230 -0
  163. package/README.md +4 -2
  164. package/SDK.md +64 -1
  165. package/dist/agents/coding.agent.d.ts +632 -0
  166. package/dist/agents/coding.agent.d.ts.map +1 -1
  167. package/dist/agents/coding.agent.js +11 -3
  168. package/dist/agents/coding.agent.js.map +1 -1
  169. package/dist/agents/developer-helper.agent.d.ts +704 -0
  170. package/dist/agents/developer-helper.agent.d.ts.map +1 -1
  171. package/dist/agents/developer-helper.agent.js +6 -0
  172. package/dist/agents/developer-helper.agent.js.map +1 -1
  173. package/dist/agents/manager.d.ts +1009 -4
  174. package/dist/agents/manager.d.ts.map +1 -1
  175. package/dist/agents/manager.js +16 -5
  176. package/dist/agents/manager.js.map +1 -1
  177. package/dist/agents/security.agent.d.ts +157 -3
  178. package/dist/agents/security.agent.d.ts.map +1 -1
  179. package/dist/agents/security.agent.js +3 -0
  180. package/dist/agents/security.agent.js.map +1 -1
  181. package/dist/agents/swarm/main.js +1 -1
  182. package/dist/agents/swarm/main.js.map +1 -1
  183. package/dist/agents/swarm/registry.d.ts.map +1 -1
  184. package/dist/agents/swarm/registry.js +232 -26
  185. package/dist/agents/swarm/registry.js.map +1 -1
  186. package/dist/cli/chat.d.ts +1 -1
  187. package/dist/cli/chat.d.ts.map +1 -1
  188. package/dist/cli/chat.js +32 -10
  189. package/dist/cli/chat.js.map +1 -1
  190. package/dist/cli/doctor.d.ts +14 -0
  191. package/dist/cli/doctor.d.ts.map +1 -1
  192. package/dist/cli/doctor.js +91 -0
  193. package/dist/cli/doctor.js.map +1 -1
  194. package/dist/cli/format.d.ts +1 -0
  195. package/dist/cli/format.d.ts.map +1 -1
  196. package/dist/cli/format.js +68 -14
  197. package/dist/cli/format.js.map +1 -1
  198. package/dist/cli/menu.d.ts.map +1 -1
  199. package/dist/cli/menu.js +102 -0
  200. package/dist/cli/menu.js.map +1 -1
  201. package/dist/cli/setup.d.ts +10 -0
  202. package/dist/cli/setup.d.ts.map +1 -1
  203. package/dist/cli/setup.js +187 -3
  204. package/dist/cli/setup.js.map +1 -1
  205. package/dist/cli/update.d.ts +2 -0
  206. package/dist/cli/update.d.ts.map +1 -1
  207. package/dist/cli/update.js +89 -1
  208. package/dist/cli/update.js.map +1 -1
  209. package/dist/config/env.d.ts +12 -0
  210. package/dist/config/env.d.ts.map +1 -1
  211. package/dist/config/env.js +19 -5
  212. package/dist/config/env.js.map +1 -1
  213. package/dist/index.d.ts +1 -1
  214. package/dist/index.d.ts.map +1 -1
  215. package/dist/index.js +61 -5
  216. package/dist/index.js.map +1 -1
  217. package/dist/jobs/trigger-orchestrator.d.ts +5 -1
  218. package/dist/jobs/trigger-orchestrator.d.ts.map +1 -1
  219. package/dist/jobs/trigger-orchestrator.js +29 -9
  220. package/dist/jobs/trigger-orchestrator.js.map +1 -1
  221. package/dist/jobs/trigger-router.d.ts.map +1 -1
  222. package/dist/jobs/trigger-router.js +3 -2
  223. package/dist/jobs/trigger-router.js.map +1 -1
  224. package/dist/memory/corporate-wiki.d.ts +15 -0
  225. package/dist/memory/corporate-wiki.d.ts.map +1 -0
  226. package/dist/memory/corporate-wiki.js +152 -0
  227. package/dist/memory/corporate-wiki.js.map +1 -0
  228. package/dist/observability/doctor.d.ts.map +1 -1
  229. package/dist/observability/doctor.js +12 -4
  230. package/dist/observability/doctor.js.map +1 -1
  231. package/dist/pre-init.d.ts +2 -0
  232. package/dist/pre-init.d.ts.map +1 -0
  233. package/dist/pre-init.js +3 -0
  234. package/dist/pre-init.js.map +1 -0
  235. package/dist/runtime/chat-bridge.d.ts.map +1 -1
  236. package/dist/runtime/chat-bridge.js +33 -2
  237. package/dist/runtime/chat-bridge.js.map +1 -1
  238. package/dist/runtime/swarm.d.ts +0 -1
  239. package/dist/runtime/swarm.d.ts.map +1 -1
  240. package/dist/runtime/swarm.js +26 -15
  241. package/dist/runtime/swarm.js.map +1 -1
  242. package/dist/safety/approvals.d.ts +2 -2
  243. package/dist/safety/approvals.d.ts.map +1 -1
  244. package/dist/safety/approvals.js +47 -1
  245. package/dist/safety/approvals.js.map +1 -1
  246. package/dist/safety/redaction.d.ts.map +1 -1
  247. package/dist/safety/redaction.js +10 -0
  248. package/dist/safety/redaction.js.map +1 -1
  249. package/dist/server.d.ts +1 -0
  250. package/dist/server.d.ts.map +1 -1
  251. package/dist/server.js +1 -0
  252. package/dist/server.js.map +1 -1
  253. package/dist/tools/cloud.tool.d.ts +394 -0
  254. package/dist/tools/cloud.tool.d.ts.map +1 -0
  255. package/dist/tools/cloud.tool.js +784 -0
  256. package/dist/tools/cloud.tool.js.map +1 -0
  257. package/dist/tools/computer-use.tool.d.ts +6 -6
  258. package/dist/tools/corporate-wiki.tool.d.ts +22 -0
  259. package/dist/tools/corporate-wiki.tool.d.ts.map +1 -0
  260. package/dist/tools/corporate-wiki.tool.js +39 -0
  261. package/dist/tools/corporate-wiki.tool.js.map +1 -0
  262. package/dist/tools/desktop.tool.d.ts +96 -0
  263. package/dist/tools/desktop.tool.d.ts.map +1 -1
  264. package/dist/tools/desktop.tool.js +439 -0
  265. package/dist/tools/desktop.tool.js.map +1 -1
  266. package/dist/tools/devops.tool.d.ts +162 -0
  267. package/dist/tools/devops.tool.d.ts.map +1 -0
  268. package/dist/tools/devops.tool.js +377 -0
  269. package/dist/tools/devops.tool.js.map +1 -0
  270. package/dist/tools/multimedia.tool.d.ts +126 -0
  271. package/dist/tools/multimedia.tool.d.ts.map +1 -0
  272. package/dist/tools/multimedia.tool.js +533 -0
  273. package/dist/tools/multimedia.tool.js.map +1 -0
  274. package/dist/tools/osint.tool.d.ts +6 -6
  275. package/dist/tools/sandbox-dev.tool.d.ts +15 -0
  276. package/dist/tools/sandbox-dev.tool.d.ts.map +1 -0
  277. package/dist/tools/sandbox-dev.tool.js +139 -0
  278. package/dist/tools/sandbox-dev.tool.js.map +1 -0
  279. package/dist/tools/setup-assistant.tool.d.ts +1 -1
  280. package/dist/tools/shell.tool.d.ts +82 -0
  281. package/dist/tools/shell.tool.d.ts.map +1 -1
  282. package/dist/tools/shell.tool.js +192 -1
  283. package/dist/tools/shell.tool.js.map +1 -1
  284. package/dist/tools/swarm-ops.tool.d.ts +14 -0
  285. package/dist/tools/swarm-ops.tool.d.ts.map +1 -1
  286. package/dist/tools/swarm-ops.tool.js +44 -0
  287. package/dist/tools/swarm-ops.tool.js.map +1 -1
  288. package/dist/tools/sysops.tool.d.ts +157 -0
  289. package/dist/tools/sysops.tool.d.ts.map +1 -0
  290. package/dist/tools/sysops.tool.js +643 -0
  291. package/dist/tools/sysops.tool.js.map +1 -0
  292. package/dist/voice/cascade.d.ts +1 -0
  293. package/dist/voice/cascade.d.ts.map +1 -1
  294. package/dist/voice/cascade.js +16 -1
  295. package/dist/voice/cascade.js.map +1 -1
  296. package/dist/voice/deepgram.d.ts.map +1 -1
  297. package/dist/voice/deepgram.js +34 -2
  298. package/dist/voice/deepgram.js.map +1 -1
  299. package/dist/voice/terminal.d.ts +1 -0
  300. package/dist/voice/terminal.d.ts.map +1 -1
  301. package/dist/voice/terminal.js +28 -0
  302. package/dist/voice/terminal.js.map +1 -1
  303. package/dist/voice/types.d.ts +2 -0
  304. package/dist/voice/types.d.ts.map +1 -1
  305. package/package.json +9 -1
  306. package/scripts/release-github.mjs +23 -18
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env python3
2
+ """Lint a Remotion project for patterns that don't translate cleanly to HyperFrames.
3
+
4
+ The skill should run this *before* attempting a translation. If any blocker
5
+ findings come back, the recommendation is to use the runtime interop pattern
6
+ from PR #214 instead of producing broken HTML.
7
+
8
+ Usage:
9
+ lint_source.py <path-to-remotion-src> [--json]
10
+
11
+ Output (default human-readable, --json for machine-readable):
12
+ For each .ts/.tsx file, a list of findings with:
13
+ - severity: blocker | warning | info
14
+ - line, column
15
+ - rule id
16
+ - message
17
+ - recommendation
18
+
19
+ Blockers (skill should refuse to translate):
20
+ - r2hf/use-state React state machine drives animation
21
+ - r2hf/use-effect-deps useEffect/useLayoutEffect with non-empty deps (side effects)
22
+ - r2hf/use-reducer useReducer drives animation
23
+ - r2hf/async-metadata calculateMetadata returns a Promise
24
+ - r2hf/third-party-react-ui Imports a React UI library (shadcn, mui, antd, mantine, chakra)
25
+
26
+ Warnings (translate but flag — drop the construct, keep the rest):
27
+ - r2hf/lambda-import @remotion/lambda configuration — drop, HF is single-machine
28
+ - r2hf/delay-render delayRender() — HF handles asset loading differently
29
+ - r2hf/use-callback useCallback — usually decorative, drop
30
+ - r2hf/use-memo useMemo — usually decorative, drop
31
+ - r2hf/custom-hook Custom hook (use*) defined locally; may need manual rewrite
32
+
33
+ Info (translate and document):
34
+ - r2hf/static-file staticFile("x") — convert to relative path
35
+ - r2hf/interpolate-colors interpolateColors — translate to GSAP color tween
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ import argparse
41
+ import json
42
+ import re
43
+ import sys
44
+ from collections.abc import Iterable
45
+ from dataclasses import dataclass, asdict
46
+ from pathlib import Path
47
+ from typing import Callable
48
+
49
+ BLOCKER = "blocker"
50
+ WARNING = "warning"
51
+ INFO = "info"
52
+
53
+ THIRD_PARTY_UI_PACKAGES = {
54
+ "@mui/material",
55
+ "@mui/icons-material",
56
+ "@chakra-ui/react",
57
+ "@mantine/core",
58
+ "antd",
59
+ "@shadcn/ui",
60
+ "@radix-ui",
61
+ "@nextui-org/react",
62
+ }
63
+
64
+
65
+ @dataclass
66
+ class Finding:
67
+ file: str
68
+ line: int
69
+ column: int
70
+ severity: str
71
+ rule: str
72
+ message: str
73
+ recommendation: str
74
+
75
+
76
+ @dataclass
77
+ class Rule:
78
+ """A lint rule: a matcher that yields hits, plus the metadata each hit gets.
79
+
80
+ A matcher is a function `src -> Iterable[(offset, override_message)]`. If
81
+ `override_message` is None, the rule's default `message` is used; matchers
82
+ that need to embed the matched text (custom-hook name, third-party package
83
+ name) return the customized message instead.
84
+ """
85
+
86
+ rule_id: str
87
+ severity: str
88
+ matcher: Callable[[str], Iterable[tuple[int, str | None]]]
89
+ message: str
90
+ recommendation: str
91
+
92
+
93
+ def _regex_matcher(pattern: re.Pattern[str]) -> Callable[[str], Iterable[tuple[int, str | None]]]:
94
+ def _match(src: str) -> Iterable[tuple[int, str | None]]:
95
+ for m in pattern.finditer(src):
96
+ yield m.start(), None
97
+
98
+ return _match
99
+
100
+
101
+ def _use_effect_with_deps(src: str) -> Iterable[tuple[int, str | None]]:
102
+ # Find use(Layout)?Effect(, walk to its matching ), and check if the call
103
+ # ends with `, [<non-empty>])`. Empty `[]` is mount-only, allowed.
104
+ for m in re.finditer(r"\buse(?:Layout)?Effect\s*\(", src):
105
+ end = _find_matching_paren(src, m.end() - 1)
106
+ if end is None:
107
+ continue
108
+ call = src[m.start() : end + 1]
109
+ m2 = re.search(r",\s*\[([^\]]*)\]\s*$", call[:-1])
110
+ if m2 and m2.group(1).strip():
111
+ yield m.start(), None
112
+
113
+
114
+ _CUSTOM_HOOK_DECL = re.compile(
115
+ r"^\s*(?:export\s+(?:default\s+)?)?(?:function|const|let|var)\s+(use[A-Z]\w+)\b",
116
+ re.MULTILINE,
117
+ )
118
+ _REMOTION_BUILTIN_HOOKS = {"useCurrentFrame", "useVideoConfig"}
119
+
120
+
121
+ def _custom_hook(src: str) -> Iterable[tuple[int, str | None]]:
122
+ for m in _CUSTOM_HOOK_DECL.finditer(src):
123
+ name = m.group(1)
124
+ if name in _REMOTION_BUILTIN_HOOKS:
125
+ continue
126
+ yield m.start(), f"Custom hook `{name}` defined locally — may need manual rewrite"
127
+
128
+
129
+ _IMPORT_FROM = re.compile(r"from\s+['\"]([^'\"]+)['\"]")
130
+
131
+
132
+ def _third_party_react_ui(src: str) -> Iterable[tuple[int, str | None]]:
133
+ for m in _IMPORT_FROM.finditer(src):
134
+ pkg = m.group(1)
135
+ if any(pkg.startswith(p) for p in THIRD_PARTY_UI_PACKAGES):
136
+ yield m.start(), f"Imports `{pkg}` — third-party React UI library has no HF equivalent"
137
+
138
+
139
+ RULES: list[Rule] = [
140
+ Rule(
141
+ "r2hf/use-state",
142
+ BLOCKER,
143
+ _regex_matcher(re.compile(r"\buseState\s*[(<]")),
144
+ "useState detected — Remotion compositions that drive animation via React state are not deterministic frame-capture targets in HyperFrames",
145
+ "Use the runtime interop pattern from PR #214 instead of attempting a translation",
146
+ ),
147
+ Rule(
148
+ "r2hf/use-reducer",
149
+ BLOCKER,
150
+ _regex_matcher(re.compile(r"\buseReducer\s*[(<]")),
151
+ "useReducer detected — same issue as useState",
152
+ "Use the runtime interop pattern from PR #214",
153
+ ),
154
+ Rule(
155
+ "r2hf/use-effect-deps",
156
+ BLOCKER,
157
+ _use_effect_with_deps,
158
+ "useEffect/useLayoutEffect with non-empty deps — side effects don't translate to HF's seek-driven model",
159
+ "Move the side-effect work into a build step, or use the runtime interop pattern",
160
+ ),
161
+ Rule(
162
+ "r2hf/async-metadata",
163
+ BLOCKER,
164
+ _regex_matcher(
165
+ re.compile(
166
+ r"calculateMetadata[^=]*=\s*async\b|async\s+calculateMetadata\b|calculateMetadata\s*:\s*async"
167
+ )
168
+ ),
169
+ "calculateMetadata returns a Promise — HF needs composition metadata up front",
170
+ "Resolve metadata at build time and pass concrete values, or use runtime interop",
171
+ ),
172
+ Rule(
173
+ "r2hf/third-party-react-ui",
174
+ BLOCKER,
175
+ _third_party_react_ui,
176
+ "Imports a third-party React UI library — no HF equivalent",
177
+ "Use runtime interop, or rewrite the affected components as HTML+CSS",
178
+ ),
179
+ # Lambda is a warning, not a blocker: it's deployment config, orthogonal
180
+ # to the rendered composition. The skill drops the import and translates
181
+ # the rest. See references/escape-hatch.md.
182
+ Rule(
183
+ "r2hf/lambda-import",
184
+ WARNING,
185
+ _regex_matcher(re.compile(r"from\s+['\"]@remotion/lambda['\"]")),
186
+ "@remotion/lambda is Remotion-specific distributed rendering — no HF equivalent today",
187
+ "Drop the Lambda config; HF runs single-machine. Document the gap in TRANSLATION_NOTES.md.",
188
+ ),
189
+ Rule(
190
+ "r2hf/delay-render",
191
+ WARNING,
192
+ _regex_matcher(re.compile(r"\bdelayRender\s*\(")),
193
+ "delayRender() — HF waits on asset readiness via the Frame Adapter pattern",
194
+ "Drop the call; HF handles this transparently",
195
+ ),
196
+ Rule(
197
+ "r2hf/use-callback",
198
+ WARNING,
199
+ _regex_matcher(re.compile(r"\buseCallback\s*\(")),
200
+ "useCallback — typically decorative for render performance, no HF equivalent needed",
201
+ "Drop the wrapper, inline the function",
202
+ ),
203
+ Rule(
204
+ "r2hf/use-memo",
205
+ WARNING,
206
+ _regex_matcher(re.compile(r"\buseMemo\s*\(")),
207
+ "useMemo — typically decorative, no HF equivalent needed",
208
+ "Drop the wrapper, compute inline",
209
+ ),
210
+ Rule(
211
+ "r2hf/custom-hook",
212
+ WARNING,
213
+ _custom_hook,
214
+ "Custom hook defined locally — may need manual rewrite",
215
+ "Inline the hook body if pure; bow out to runtime interop if it uses useState/useEffect",
216
+ ),
217
+ Rule(
218
+ "r2hf/static-file",
219
+ INFO,
220
+ _regex_matcher(re.compile(r"\bstaticFile\s*\(")),
221
+ "staticFile() reference — convert to a relative path in the HF composition",
222
+ "Replace `staticFile(\"x.png\")` with `\"x.png\"` and copy the asset alongside the HTML",
223
+ ),
224
+ Rule(
225
+ "r2hf/interpolate-colors",
226
+ INFO,
227
+ _regex_matcher(re.compile(r"\binterpolateColors\s*\(")),
228
+ "interpolateColors() — translate to a GSAP color tween",
229
+ "See references/timing.md for the GSAP equivalent",
230
+ ),
231
+ ]
232
+
233
+
234
+ def _find_matching_paren(src: str, open_idx: int) -> int | None:
235
+ """Given the index of an open `(`, return the index of its matching `)`.
236
+
237
+ Skips parens that appear inside `'...'`, `"..."`, or `` `...` `` string
238
+ literals. Returns None if no matching close paren is found.
239
+
240
+ This is good enough for hand-written Remotion source. It does not handle
241
+ template-literal interpolations `${...}` recursively or comments — both
242
+ are uncommon in Remotion code we expect to lint and would only matter
243
+ if the unbalanced paren landed inside such a region.
244
+ """
245
+ if open_idx >= len(src) or src[open_idx] != "(":
246
+ return None
247
+ depth = 0
248
+ i = open_idx
249
+ in_str: str | None = None
250
+ while i < len(src):
251
+ c = src[i]
252
+ if in_str is not None:
253
+ if c == "\\":
254
+ i += 2
255
+ continue
256
+ if c == in_str:
257
+ in_str = None
258
+ i += 1
259
+ continue
260
+ if c in ("'", '"', "`"):
261
+ in_str = c
262
+ i += 1
263
+ continue
264
+ if c == "(":
265
+ depth += 1
266
+ elif c == ")":
267
+ depth -= 1
268
+ if depth == 0:
269
+ return i
270
+ i += 1
271
+ return None
272
+
273
+
274
+ def lint_file(path: Path) -> list[Finding]:
275
+ src = path.read_text()
276
+ findings: list[Finding] = []
277
+
278
+ def loc(offset: int) -> tuple[int, int]:
279
+ line = src.count("\n", 0, offset) + 1
280
+ col = offset - (src.rfind("\n", 0, offset) + 1) + 1
281
+ return line, col
282
+
283
+ for rule in RULES:
284
+ for offset, override_message in rule.matcher(src):
285
+ line, col = loc(offset)
286
+ findings.append(
287
+ Finding(
288
+ str(path),
289
+ line,
290
+ col,
291
+ rule.severity,
292
+ rule.rule_id,
293
+ override_message or rule.message,
294
+ rule.recommendation,
295
+ )
296
+ )
297
+
298
+ findings.sort(key=lambda f: (f.file, f.line, f.column))
299
+ return findings
300
+
301
+
302
+ def main() -> int:
303
+ ap = argparse.ArgumentParser()
304
+ ap.add_argument("path", type=Path, help="Directory or file to lint")
305
+ ap.add_argument("--json", action="store_true", help="Emit JSON instead of human-readable output")
306
+ args = ap.parse_args()
307
+
308
+ if not args.path.exists():
309
+ print(f"error: {args.path} does not exist", file=sys.stderr)
310
+ return 2
311
+
312
+ files: list[Path]
313
+ if args.path.is_file():
314
+ files = [args.path]
315
+ else:
316
+ files = sorted(
317
+ p
318
+ for p in args.path.rglob("*")
319
+ if p.is_file()
320
+ and p.suffix in {".ts", ".tsx", ".jsx", ".js"}
321
+ and "node_modules" not in p.parts
322
+ )
323
+
324
+ all_findings: list[Finding] = []
325
+ for f in files:
326
+ all_findings.extend(lint_file(f))
327
+
328
+ blockers = sum(1 for f in all_findings if f.severity == BLOCKER)
329
+ warnings = sum(1 for f in all_findings if f.severity == WARNING)
330
+ infos = sum(1 for f in all_findings if f.severity == INFO)
331
+
332
+ if args.json:
333
+ json.dump(
334
+ {
335
+ "files_scanned": len(files),
336
+ "blockers": blockers,
337
+ "warnings": warnings,
338
+ "infos": infos,
339
+ "findings": [asdict(f) for f in all_findings],
340
+ },
341
+ sys.stdout,
342
+ indent=2,
343
+ )
344
+ sys.stdout.write("\n")
345
+ else:
346
+ for f in all_findings:
347
+ print(f"{f.file}:{f.line}:{f.column} [{f.severity}] {f.rule}: {f.message}")
348
+ print(f" -> {f.recommendation}")
349
+ print()
350
+ print(f"{len(files)} files scanned · {blockers} blocker · {warnings} warning · {infos} info")
351
+ if blockers:
352
+ print("RECOMMENDATION: do not attempt translation. Use the runtime interop pattern from PR #214.")
353
+
354
+ return 1 if blockers else 0
355
+
356
+
357
+ if __name__ == "__main__":
358
+ sys.exit(main())
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ # render_diff.sh — compute per-frame SSIM between two video files.
3
+ #
4
+ # The eval primitive for the remotion-to-hyperframes skill: given a Remotion
5
+ # render and a HyperFrames render of the same composition, report whether the
6
+ # translation is visually equivalent.
7
+ #
8
+ # Usage:
9
+ # render_diff.sh <baseline.mp4> <translated.mp4> [output-dir]
10
+ #
11
+ # Output (in output-dir, defaults to ./diff-out):
12
+ # ssim.log — per-frame SSIM lines from ffmpeg
13
+ # summary.json — { mean, min, p05, p95, frame_count, pass, threshold }
14
+ #
15
+ # Exit codes:
16
+ # 0 — pass (mean SSIM >= threshold)
17
+ # 1 — fail (mean SSIM < threshold)
18
+ # 2 — usage / setup error
19
+ #
20
+ # Threshold defaults to 0.85 (loose; tier-specific thresholds are applied by
21
+ # the orchestrator). Override with R2HF_SSIM_THRESHOLD=0.95 in the environment.
22
+
23
+ set -euo pipefail
24
+
25
+ THRESHOLD="${R2HF_SSIM_THRESHOLD:-0.85}"
26
+
27
+ if [[ $# -lt 2 || $# -gt 3 ]]; then
28
+ echo "usage: $0 <baseline.mp4> <translated.mp4> [output-dir]" >&2
29
+ exit 2
30
+ fi
31
+
32
+ BASELINE="$1"
33
+ TRANSLATED="$2"
34
+ OUTDIR="${3:-./diff-out}"
35
+
36
+ if [[ ! -f "$BASELINE" ]]; then
37
+ echo "error: baseline not found: $BASELINE" >&2
38
+ exit 2
39
+ fi
40
+ if [[ ! -f "$TRANSLATED" ]]; then
41
+ echo "error: translated not found: $TRANSLATED" >&2
42
+ exit 2
43
+ fi
44
+ if ! command -v ffmpeg >/dev/null 2>&1; then
45
+ echo "error: ffmpeg not on PATH" >&2
46
+ exit 2
47
+ fi
48
+
49
+ mkdir -p "$OUTDIR"
50
+ SSIM_LOG="$OUTDIR/ssim.log"
51
+ SUMMARY="$OUTDIR/summary.json"
52
+
53
+ # ffmpeg's ssim filter writes one line per frame to stats_file and a single
54
+ # Mean SSIM line to stderr. We capture both — per-frame for distribution
55
+ # stats, and the mean for the headline number.
56
+ ffmpeg -hide_banner -nostats -loglevel info \
57
+ -i "$BASELINE" -i "$TRANSLATED" \
58
+ -lavfi "[0:v]scale=iw:ih[ref];[1:v]scale=iw:ih[main];[main][ref]ssim=stats_file=$SSIM_LOG" \
59
+ -f null - 2>"$OUTDIR/ffmpeg.stderr"
60
+
61
+ # Parse: each line in ssim.log looks like
62
+ # n:1 Y:0.987655 U:0.992345 V:0.991234 All:0.989012 (19.512345)
63
+ # We want the All:N column.
64
+ python3 - "$SSIM_LOG" "$SUMMARY" "$THRESHOLD" <<'PY'
65
+ import json, math, re, sys
66
+ from pathlib import Path
67
+
68
+ log_path = Path(sys.argv[1])
69
+ out_path = Path(sys.argv[2])
70
+ threshold = float(sys.argv[3])
71
+
72
+ values = []
73
+ pattern = re.compile(r"All:([\d.]+)")
74
+ for line in log_path.read_text().splitlines():
75
+ m = pattern.search(line)
76
+ if m:
77
+ try:
78
+ values.append(float(m.group(1)))
79
+ except ValueError:
80
+ pass
81
+
82
+ if not values:
83
+ print(f"error: no SSIM samples parsed from {log_path}", file=sys.stderr)
84
+ sys.exit(2)
85
+
86
+ values.sort()
87
+ n = len(values)
88
+ mean = sum(values) / n
89
+ p_idx = lambda p: min(n - 1, max(0, int(math.floor(p * n))))
90
+ summary = {
91
+ "frame_count": n,
92
+ "mean": round(mean, 6),
93
+ "min": round(values[0], 6),
94
+ "max": round(values[-1], 6),
95
+ "p05": round(values[p_idx(0.05)], 6),
96
+ "p95": round(values[p_idx(0.95)], 6),
97
+ "threshold": threshold,
98
+ "pass": bool(mean >= threshold),
99
+ }
100
+ out_path.write_text(json.dumps(summary, indent=2) + "\n")
101
+ print(json.dumps(summary, indent=2))
102
+ sys.exit(0 if summary["pass"] else 1)
103
+ PY
@@ -0,0 +1,50 @@
1
+ import React, { useState, useEffect, useLayoutEffect } from "react";
2
+ import { useCurrentFrame, AbsoluteFill, delayRender, continueRender } from "remotion";
3
+ import { Button } from "@mui/material";
4
+
5
+ // Custom hook in `export const useFoo = ...` form — earlier custom-hook
6
+ // regex anchored to `^\s*(?:function|const|let)` and missed the `export`
7
+ // prefix. This covers the regression.
8
+ export const useFadeMixed = (n: number) => {
9
+ const f = useCurrentFrame();
10
+ return f / n;
11
+ };
12
+
13
+ export const BadComposition: React.FC = () => {
14
+ const frame = useCurrentFrame();
15
+ const [data, setData] = useState<string | null>(null);
16
+ const [handle] = useState(() => delayRender());
17
+
18
+ // Multi-line useEffect body with commas inside (fillRect args) — regression
19
+ // coverage for r2hf/use-effect-deps. An earlier regex `[^,]+` would stop at
20
+ // the first comma inside the body and miss the deps array entirely.
21
+ useEffect(() => {
22
+ fetch("/api/data")
23
+ .then((r) => r.json())
24
+ .then((d) => {
25
+ const ctx = document.createElement("canvas").getContext("2d");
26
+ ctx?.fillRect(0, 0, 100, 100);
27
+ setData(d.text);
28
+ continueRender(handle);
29
+ });
30
+ }, [handle]);
31
+
32
+ // Expression-bodied useEffect — the form `useEffect(() => fetch(...), [deps])`
33
+ // has no closing `}`, which an earlier regex anchored on. This and the
34
+ // useLayoutEffect below cover the false-negative cases Miguel surfaced.
35
+ useEffect(() => fetch("/api/heartbeat"), [frame]);
36
+ useLayoutEffect(() => (document.title = `frame ${frame}`), [frame]);
37
+
38
+ return (
39
+ <AbsoluteFill>
40
+ <Button>{data ?? "loading"}</Button>
41
+ <span>{frame}</span>
42
+ </AbsoluteFill>
43
+ );
44
+ };
45
+
46
+ export const calculateMetadata = async () => {
47
+ const res = await fetch("/api/duration");
48
+ const { duration } = await res.json();
49
+ return { durationInFrames: duration };
50
+ };
@@ -0,0 +1,46 @@
1
+ import React, { useEffect } from "react";
2
+ import {
3
+ useCurrentFrame,
4
+ useVideoConfig,
5
+ AbsoluteFill,
6
+ interpolate,
7
+ spring,
8
+ Sequence,
9
+ staticFile,
10
+ Audio,
11
+ Img,
12
+ } from "remotion";
13
+
14
+ // Mount-only useEffect with empty deps + a later expression containing a
15
+ // non-empty array — regression coverage for the over-match Miguel reported:
16
+ // the earlier regex spanned past `[]` and matched `[frame]` from `pick(...)`,
17
+ // falsely flagging this clean fixture as having a blocker.
18
+ function pick<T>(_key: string, items: T[]): T {
19
+ return items[0];
20
+ }
21
+
22
+ const TitleCard: React.FC = () => {
23
+ const frame = useCurrentFrame();
24
+ const { fps } = useVideoConfig();
25
+ useEffect(() => {
26
+ console.log("mounted");
27
+ }, []);
28
+ const _picked = pick("x", [frame]);
29
+ const opacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: "clamp" });
30
+ const scale = spring({ frame, fps, config: { damping: 12 } });
31
+ return (
32
+ <AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
33
+ <div style={{ fontSize: 72, opacity, transform: `scale(${scale})` }}>Hello</div>
34
+ <Img src={staticFile("logo.png")} />
35
+ </AbsoluteFill>
36
+ );
37
+ };
38
+
39
+ export const MyComposition: React.FC = () => (
40
+ <AbsoluteFill>
41
+ <Sequence from={0} durationInFrames={90}>
42
+ <TitleCard />
43
+ </Sequence>
44
+ <Audio src={staticFile("music.mp3")} volume={0.5} />
45
+ </AbsoluteFill>
46
+ );
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env bash
2
+ # smoke.sh — exercise the eval harness scripts against synthetic inputs.
3
+ #
4
+ # Generates two synthetic videos with ffmpeg's testsrc filter, runs render_diff
5
+ # and frame_strip against them, and runs lint_source against fixture .tsx files.
6
+ # Asserts the harness produces sensible output without depending on a real
7
+ # Remotion or HyperFrames render pipeline being installed.
8
+ #
9
+ # Usage: ./smoke.sh
10
+ # Exit 0 on pass.
11
+
12
+ set -euo pipefail
13
+
14
+ THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ SCRIPTS_DIR="$(cd "$THIS_DIR/.." && pwd)"
16
+ WORK="$(mktemp -d)"
17
+ trap 'rm -rf "$WORK"' EXIT
18
+
19
+ echo "==> smoke: render_diff.sh against identical inputs"
20
+ # Generate the same test pattern twice. Identical inputs → SSIM should be ~1.0.
21
+ ffmpeg -y -hide_banner -loglevel error \
22
+ -f lavfi -i "testsrc=duration=2:size=320x240:rate=30" \
23
+ -pix_fmt yuv420p "$WORK/baseline.mp4"
24
+ cp "$WORK/baseline.mp4" "$WORK/translated.mp4"
25
+
26
+ R2HF_SSIM_THRESHOLD=0.99 "$SCRIPTS_DIR/render_diff.sh" \
27
+ "$WORK/baseline.mp4" "$WORK/translated.mp4" "$WORK/diff" >/dev/null
28
+
29
+ MEAN=$(python3 -c "import json,sys; print(json.load(open('$WORK/diff/summary.json'))['mean'])")
30
+ PASS=$(python3 -c "import json,sys; print(json.load(open('$WORK/diff/summary.json'))['pass'])")
31
+ if [[ "$PASS" != "True" ]]; then
32
+ echo "FAIL: identical inputs failed pass check (mean=$MEAN)"
33
+ exit 1
34
+ fi
35
+ echo " identical inputs → mean SSIM=$MEAN (pass=True)"
36
+
37
+ echo "==> smoke: render_diff.sh against different inputs"
38
+ # Different test pattern → SSIM should be lower. With a high threshold it should fail.
39
+ ffmpeg -y -hide_banner -loglevel error \
40
+ -f lavfi -i "testsrc2=duration=2:size=320x240:rate=30" \
41
+ -pix_fmt yuv420p "$WORK/different.mp4"
42
+
43
+ set +e
44
+ R2HF_SSIM_THRESHOLD=0.99 "$SCRIPTS_DIR/render_diff.sh" \
45
+ "$WORK/baseline.mp4" "$WORK/different.mp4" "$WORK/diff2" >/dev/null
46
+ RC=$?
47
+ set -e
48
+ if [[ "$RC" -eq 0 ]]; then
49
+ echo "FAIL: different inputs unexpectedly passed at threshold 0.99"
50
+ exit 1
51
+ fi
52
+ DIFF_MEAN=$(python3 -c "import json; print(json.load(open('$WORK/diff2/summary.json'))['mean'])")
53
+ echo " different inputs → mean SSIM=$DIFF_MEAN (correctly failed at 0.99)"
54
+
55
+ echo "==> smoke: frame_strip.sh produces a strip"
56
+ "$SCRIPTS_DIR/frame_strip.sh" "$WORK/baseline.mp4" "$WORK/different.mp4" "$WORK/strip" 4 >/dev/null
57
+ if [[ ! -f "$WORK/strip/strip.png" ]]; then
58
+ echo "FAIL: frame_strip.sh did not produce strip.png"
59
+ exit 1
60
+ fi
61
+ echo " strip.png written ($(stat -c%s "$WORK/strip/strip.png" 2>/dev/null || stat -f%z "$WORK/strip/strip.png") bytes)"
62
+
63
+ echo "==> smoke: lint_source.py on clean fixture (expect exit 0)"
64
+ set +e
65
+ python3 "$SCRIPTS_DIR/lint_source.py" "$THIS_DIR/fixtures/clean.tsx" --json >"$WORK/clean.json"
66
+ RC=$?
67
+ set -e
68
+ BLOCKERS=$(python3 -c "import json; print(json.load(open('$WORK/clean.json'))['blockers'])")
69
+ if [[ "$RC" -ne 0 || "$BLOCKERS" -ne 0 ]]; then
70
+ echo "FAIL: clean fixture reported $BLOCKERS blockers (rc=$RC)"
71
+ exit 1
72
+ fi
73
+ INFOS=$(python3 -c "import json; print(json.load(open('$WORK/clean.json'))['infos'])")
74
+ echo " clean.tsx → 0 blockers, $INFOS info findings"
75
+
76
+ echo "==> smoke: lint_source.py on blocker fixture (expect exit 1)"
77
+ set +e
78
+ python3 "$SCRIPTS_DIR/lint_source.py" "$THIS_DIR/fixtures/blocker.tsx" --json >"$WORK/blocker.json"
79
+ RC=$?
80
+ set -e
81
+ BLOCKERS=$(python3 -c "import json; print(json.load(open('$WORK/blocker.json'))['blockers'])")
82
+ if [[ "$RC" -eq 0 || "$BLOCKERS" -lt 3 ]]; then
83
+ echo "FAIL: blocker fixture reported $BLOCKERS blockers, expected >=3 (rc=$RC)"
84
+ cat "$WORK/blocker.json"
85
+ exit 1
86
+ fi
87
+ echo " blocker.tsx → $BLOCKERS blockers detected (correctly refused)"
88
+
89
+ echo
90
+ echo "✅ smoke tests passed"