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.
- package/.agents/skills/ad-creative/SKILL.md +362 -0
- package/.agents/skills/ad-creative/evals/evals.json +90 -0
- package/.agents/skills/ad-creative/references/generative-tools.md +637 -0
- package/.agents/skills/ad-creative/references/platform-specs.md +213 -0
- package/.agents/skills/ai-seo/SKILL.md +489 -0
- package/.agents/skills/ai-seo/evals/evals.json +90 -0
- package/.agents/skills/ai-seo/references/content-patterns.md +285 -0
- package/.agents/skills/ai-seo/references/content-types.md +71 -0
- package/.agents/skills/ai-seo/references/okf.md +104 -0
- package/.agents/skills/ai-seo/references/platform-ranking-factors.md +154 -0
- package/.agents/skills/clerk-backend-api/SKILL.md +426 -0
- package/.agents/skills/clerk-backend-api/evals/evals.json +87 -0
- package/.agents/skills/clerk-backend-api/scripts/api-specs-context.sh +30 -0
- package/.agents/skills/clerk-backend-api/scripts/execute-request.sh +88 -0
- package/.agents/skills/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
- package/.agents/skills/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
- package/.agents/skills/clerk-backend-api/scripts/extract-tags.js +14 -0
- package/.agents/skills/copy-editing/SKILL.md +457 -0
- package/.agents/skills/copy-editing/evals/evals.json +89 -0
- package/.agents/skills/copy-editing/references/checklist.md +66 -0
- package/.agents/skills/copy-editing/references/content-refresh.md +38 -0
- package/.agents/skills/copy-editing/references/plain-english-alternatives.md +394 -0
- package/.agents/skills/hyperframes/SKILL.md +163 -0
- package/.agents/skills/hyperframes-cli/SKILL.md +109 -0
- package/.agents/skills/hyperframes-cli/references/doctor-browser.md +45 -0
- package/.agents/skills/hyperframes-cli/references/init-and-scaffold.md +51 -0
- package/.agents/skills/hyperframes-cli/references/lambda.md +132 -0
- package/.agents/skills/hyperframes-cli/references/lint-validate-inspect.md +121 -0
- package/.agents/skills/hyperframes-cli/references/preview-render.md +109 -0
- package/.agents/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
- package/.agents/skills/hyperframes-media/SKILL.md +97 -0
- package/.agents/skills/hyperframes-media/assets/sfx/CREDITS.md +35 -0
- package/.agents/skills/hyperframes-media/assets/sfx/manifest.json +97 -0
- package/.agents/skills/hyperframes-media/references/bgm.md +72 -0
- package/.agents/skills/hyperframes-media/references/captions/authoring.md +159 -0
- package/.agents/skills/hyperframes-media/references/captions/motion.md +87 -0
- package/.agents/skills/hyperframes-media/references/captions/transcript-handling.md +97 -0
- package/.agents/skills/hyperframes-media/references/remove-background.md +143 -0
- package/.agents/skills/hyperframes-media/references/requirements.md +29 -0
- package/.agents/skills/hyperframes-media/references/sfx.md +42 -0
- package/.agents/skills/hyperframes-media/references/transcribe.md +52 -0
- package/.agents/skills/hyperframes-media/references/tts-to-captions.md +24 -0
- package/.agents/skills/hyperframes-media/references/tts.md +135 -0
- package/.agents/skills/hyperframes-media/scripts/audio.mjs +282 -0
- package/.agents/skills/hyperframes-media/scripts/heygen-tts.mjs +121 -0
- package/.agents/skills/hyperframes-media/scripts/lib/bgm.mjs +235 -0
- package/.agents/skills/hyperframes-media/scripts/lib/heygen.mjs +131 -0
- package/.agents/skills/hyperframes-media/scripts/lib/sfx.mjs +128 -0
- package/.agents/skills/hyperframes-media/scripts/lib/tts.mjs +220 -0
- package/.agents/skills/hyperframes-media/scripts/lyria-recipe.py +128 -0
- package/.agents/skills/hyperframes-media/scripts/wait-bgm.mjs +167 -0
- package/.agents/skills/hyperframes-registry/SKILL.md +101 -0
- package/.agents/skills/hyperframes-registry/examples/add-block.md +51 -0
- package/.agents/skills/hyperframes-registry/examples/add-component.md +73 -0
- package/.agents/skills/hyperframes-registry/references/contributing.md +166 -0
- package/.agents/skills/hyperframes-registry/references/demo-html-pattern.md +54 -0
- package/.agents/skills/hyperframes-registry/references/discovery.md +215 -0
- package/.agents/skills/hyperframes-registry/references/install-locations.md +45 -0
- package/.agents/skills/hyperframes-registry/references/templates.md +417 -0
- package/.agents/skills/hyperframes-registry/references/wiring-blocks.md +61 -0
- package/.agents/skills/hyperframes-registry/references/wiring-components.md +39 -0
- package/.agents/skills/marketing-psychology/SKILL.md +455 -0
- package/.agents/skills/marketing-psychology/evals/evals.json +88 -0
- package/.agents/skills/mintlify/SKILL.md +328 -0
- package/.agents/skills/product-launch-video/SKILL.md +206 -0
- package/.agents/skills/product-launch-video/references/cut-catalog.md +220 -0
- package/.agents/skills/product-launch-video/references/motion-language.md +156 -0
- package/.agents/skills/product-launch-video/references/story-design.md +373 -0
- package/.agents/skills/product-launch-video/references/visual-design.md +126 -0
- package/.agents/skills/product-launch-video/scripts/assemble-index.mjs +570 -0
- package/.agents/skills/product-launch-video/scripts/audio.mjs +253 -0
- package/.agents/skills/product-launch-video/scripts/build-frame.mjs +536 -0
- package/.agents/skills/product-launch-video/scripts/captions.mjs +508 -0
- package/.agents/skills/product-launch-video/scripts/lib/assets.mjs +55 -0
- package/.agents/skills/product-launch-video/scripts/lib/dimensions.mjs +45 -0
- package/.agents/skills/product-launch-video/scripts/lib/storyboard.mjs +249 -0
- package/.agents/skills/product-launch-video/scripts/lib/tokens.mjs +204 -0
- package/.agents/skills/product-launch-video/scripts/lib/transition-registry.mjs +38 -0
- package/.agents/skills/product-launch-video/scripts/lib/transitions.json +71 -0
- package/.agents/skills/product-launch-video/scripts/stage-assets.mjs +39 -0
- package/.agents/skills/product-launch-video/scripts/transitions.mjs +320 -0
- package/.agents/skills/product-launch-video/sub-agents/frame-worker.md +74 -0
- package/.agents/skills/prompt-engineering-patterns/SKILL.md +144 -0
- package/.agents/skills/prompt-engineering-patterns/assets/few-shot-examples.json +106 -0
- package/.agents/skills/prompt-engineering-patterns/assets/prompt-template-library.md +264 -0
- package/.agents/skills/prompt-engineering-patterns/references/chain-of-thought.md +412 -0
- package/.agents/skills/prompt-engineering-patterns/references/details.md +338 -0
- package/.agents/skills/prompt-engineering-patterns/references/few-shot-learning.md +386 -0
- package/.agents/skills/prompt-engineering-patterns/references/prompt-optimization.md +428 -0
- package/.agents/skills/prompt-engineering-patterns/references/prompt-templates.md +484 -0
- package/.agents/skills/prompt-engineering-patterns/references/system-prompts.md +195 -0
- package/.agents/skills/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
- package/.agents/skills/remotion-to-hyperframes/SKILL.md +127 -0
- package/.agents/skills/remotion-to-hyperframes/assets/.gitkeep +0 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/run.sh +249 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/README.md +41 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +26 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +61 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/package.json +14 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/remotion.config.ts +13 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/Root.tsx +13 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/TitleCard.tsx +34 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/index.ts +4 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/tsconfig.json +15 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +54 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +29 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +118 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/package.json +14 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/remotion.config.ts +13 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/MultiScene.tsx +105 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/Root.tsx +13 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/index.ts +4 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/tsconfig.json +15 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/setup.sh +34 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/README.md +85 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +41 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +269 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/package.json +15 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/remotion.config.ts +13 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Root.tsx +27 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Stargazed.tsx +37 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/AnimatedNumber.tsx +23 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/StatCard.tsx +59 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/UnderlinedText.tsx +47 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/index.ts +4 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/OutroScene.tsx +18 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/StatsScene.tsx +34 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/TitleScene.tsx +55 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/tsconfig.json +15 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/README.md +51 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/01-use-state.tsx +29 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/02-use-effect-deps.tsx +29 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/03-async-metadata.tsx +39 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/04-third-party-react.tsx +30 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/05-lambda-config.tsx +38 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/06-warnings-only.tsx +36 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/07-custom-hook.tsx +28 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/08-mixed.tsx +41 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/expected.json +85 -0
- package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/validate.sh +110 -0
- package/.agents/skills/remotion-to-hyperframes/references/api-map.md +142 -0
- package/.agents/skills/remotion-to-hyperframes/references/escape-hatch.md +115 -0
- package/.agents/skills/remotion-to-hyperframes/references/eval.md +140 -0
- package/.agents/skills/remotion-to-hyperframes/references/fonts.md +112 -0
- package/.agents/skills/remotion-to-hyperframes/references/limitations.md +136 -0
- package/.agents/skills/remotion-to-hyperframes/references/lottie.md +121 -0
- package/.agents/skills/remotion-to-hyperframes/references/media.md +149 -0
- package/.agents/skills/remotion-to-hyperframes/references/parameters.md +167 -0
- package/.agents/skills/remotion-to-hyperframes/references/sequencing.md +195 -0
- package/.agents/skills/remotion-to-hyperframes/references/timing.md +165 -0
- package/.agents/skills/remotion-to-hyperframes/references/transitions.md +114 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/.gitkeep +0 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/frame_strip.sh +107 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/lint_source.py +358 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/render_diff.sh +103 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/blocker.tsx +50 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/clean.tsx +46 -0
- package/.agents/skills/remotion-to-hyperframes/scripts/tests/smoke.sh +90 -0
- package/.agents/skills/seo-audit/SKILL.md +497 -0
- package/.agents/skills/seo-audit/evals/evals.json +136 -0
- package/.agents/skills/seo-audit/references/ai-writing-detection.md +200 -0
- package/.agents/skills/seo-audit/references/international-seo.md +230 -0
- package/README.md +4 -2
- package/SDK.md +64 -1
- package/dist/agents/coding.agent.d.ts +632 -0
- package/dist/agents/coding.agent.d.ts.map +1 -1
- package/dist/agents/coding.agent.js +11 -3
- package/dist/agents/coding.agent.js.map +1 -1
- package/dist/agents/developer-helper.agent.d.ts +704 -0
- package/dist/agents/developer-helper.agent.d.ts.map +1 -1
- package/dist/agents/developer-helper.agent.js +6 -0
- package/dist/agents/developer-helper.agent.js.map +1 -1
- package/dist/agents/manager.d.ts +1009 -4
- package/dist/agents/manager.d.ts.map +1 -1
- package/dist/agents/manager.js +16 -5
- package/dist/agents/manager.js.map +1 -1
- package/dist/agents/security.agent.d.ts +157 -3
- package/dist/agents/security.agent.d.ts.map +1 -1
- package/dist/agents/security.agent.js +3 -0
- package/dist/agents/security.agent.js.map +1 -1
- package/dist/agents/swarm/main.js +1 -1
- package/dist/agents/swarm/main.js.map +1 -1
- package/dist/agents/swarm/registry.d.ts.map +1 -1
- package/dist/agents/swarm/registry.js +232 -26
- package/dist/agents/swarm/registry.js.map +1 -1
- package/dist/cli/chat.d.ts +1 -1
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +32 -10
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/doctor.d.ts +14 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +91 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/format.d.ts +1 -0
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +68 -14
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/menu.d.ts.map +1 -1
- package/dist/cli/menu.js +102 -0
- package/dist/cli/menu.js.map +1 -1
- package/dist/cli/setup.d.ts +10 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +187 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/update.d.ts +2 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +89 -1
- package/dist/cli/update.js.map +1 -1
- package/dist/config/env.d.ts +12 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +19 -5
- package/dist/config/env.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -5
- package/dist/index.js.map +1 -1
- package/dist/jobs/trigger-orchestrator.d.ts +5 -1
- package/dist/jobs/trigger-orchestrator.d.ts.map +1 -1
- package/dist/jobs/trigger-orchestrator.js +29 -9
- package/dist/jobs/trigger-orchestrator.js.map +1 -1
- package/dist/jobs/trigger-router.d.ts.map +1 -1
- package/dist/jobs/trigger-router.js +3 -2
- package/dist/jobs/trigger-router.js.map +1 -1
- package/dist/memory/corporate-wiki.d.ts +15 -0
- package/dist/memory/corporate-wiki.d.ts.map +1 -0
- package/dist/memory/corporate-wiki.js +152 -0
- package/dist/memory/corporate-wiki.js.map +1 -0
- package/dist/observability/doctor.d.ts.map +1 -1
- package/dist/observability/doctor.js +12 -4
- package/dist/observability/doctor.js.map +1 -1
- package/dist/pre-init.d.ts +2 -0
- package/dist/pre-init.d.ts.map +1 -0
- package/dist/pre-init.js +3 -0
- package/dist/pre-init.js.map +1 -0
- package/dist/runtime/chat-bridge.d.ts.map +1 -1
- package/dist/runtime/chat-bridge.js +33 -2
- package/dist/runtime/chat-bridge.js.map +1 -1
- package/dist/runtime/swarm.d.ts +0 -1
- package/dist/runtime/swarm.d.ts.map +1 -1
- package/dist/runtime/swarm.js +26 -15
- package/dist/runtime/swarm.js.map +1 -1
- package/dist/safety/approvals.d.ts +2 -2
- package/dist/safety/approvals.d.ts.map +1 -1
- package/dist/safety/approvals.js +47 -1
- package/dist/safety/approvals.js.map +1 -1
- package/dist/safety/redaction.d.ts.map +1 -1
- package/dist/safety/redaction.js +10 -0
- package/dist/safety/redaction.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/cloud.tool.d.ts +394 -0
- package/dist/tools/cloud.tool.d.ts.map +1 -0
- package/dist/tools/cloud.tool.js +784 -0
- package/dist/tools/cloud.tool.js.map +1 -0
- package/dist/tools/computer-use.tool.d.ts +6 -6
- package/dist/tools/corporate-wiki.tool.d.ts +22 -0
- package/dist/tools/corporate-wiki.tool.d.ts.map +1 -0
- package/dist/tools/corporate-wiki.tool.js +39 -0
- package/dist/tools/corporate-wiki.tool.js.map +1 -0
- package/dist/tools/desktop.tool.d.ts +96 -0
- package/dist/tools/desktop.tool.d.ts.map +1 -1
- package/dist/tools/desktop.tool.js +439 -0
- package/dist/tools/desktop.tool.js.map +1 -1
- package/dist/tools/devops.tool.d.ts +162 -0
- package/dist/tools/devops.tool.d.ts.map +1 -0
- package/dist/tools/devops.tool.js +377 -0
- package/dist/tools/devops.tool.js.map +1 -0
- package/dist/tools/multimedia.tool.d.ts +126 -0
- package/dist/tools/multimedia.tool.d.ts.map +1 -0
- package/dist/tools/multimedia.tool.js +533 -0
- package/dist/tools/multimedia.tool.js.map +1 -0
- package/dist/tools/osint.tool.d.ts +6 -6
- package/dist/tools/sandbox-dev.tool.d.ts +15 -0
- package/dist/tools/sandbox-dev.tool.d.ts.map +1 -0
- package/dist/tools/sandbox-dev.tool.js +139 -0
- package/dist/tools/sandbox-dev.tool.js.map +1 -0
- package/dist/tools/setup-assistant.tool.d.ts +1 -1
- package/dist/tools/shell.tool.d.ts +82 -0
- package/dist/tools/shell.tool.d.ts.map +1 -1
- package/dist/tools/shell.tool.js +192 -1
- package/dist/tools/shell.tool.js.map +1 -1
- package/dist/tools/swarm-ops.tool.d.ts +14 -0
- package/dist/tools/swarm-ops.tool.d.ts.map +1 -1
- package/dist/tools/swarm-ops.tool.js +44 -0
- package/dist/tools/swarm-ops.tool.js.map +1 -1
- package/dist/tools/sysops.tool.d.ts +157 -0
- package/dist/tools/sysops.tool.d.ts.map +1 -0
- package/dist/tools/sysops.tool.js +643 -0
- package/dist/tools/sysops.tool.js.map +1 -0
- package/dist/voice/cascade.d.ts +1 -0
- package/dist/voice/cascade.d.ts.map +1 -1
- package/dist/voice/cascade.js +16 -1
- package/dist/voice/cascade.js.map +1 -1
- package/dist/voice/deepgram.d.ts.map +1 -1
- package/dist/voice/deepgram.js +34 -2
- package/dist/voice/deepgram.js.map +1 -1
- package/dist/voice/terminal.d.ts +1 -0
- package/dist/voice/terminal.d.ts.map +1 -1
- package/dist/voice/terminal.js +28 -0
- package/dist/voice/terminal.js.map +1 -1
- package/dist/voice/types.d.ts +2 -0
- package/dist/voice/types.d.ts.map +1 -1
- package/package.json +9 -1
- 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"
|