oh-my-agent-commandcode 0.0.0-stage → 20.38.3
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/README.md +35 -2
- package/filter-test-output.sh +52 -0
- package/mods/oma.ts +347 -0
- package/package.json +30 -4
- package/skills/_shared/conditional/experiment-ledger.md +28 -0
- package/skills/_shared/conditional/exploration-loop.md +22 -0
- package/skills/_shared/conditional/quality-score.md +33 -0
- package/skills/_shared/core/anti-ai-prose.md +191 -0
- package/skills/_shared/core/api-contracts/README.md +28 -0
- package/skills/_shared/core/api-contracts/template.md +90 -0
- package/skills/_shared/core/clarification-protocol.md +23 -0
- package/skills/_shared/core/code-intelligence.md +46 -0
- package/skills/_shared/core/collaboration-policy.md +28 -0
- package/skills/_shared/core/common-checklist.md +28 -0
- package/skills/_shared/core/context-budget.md +22 -0
- package/skills/_shared/core/context-loading.md +34 -0
- package/skills/_shared/core/difficulty-guide.md +26 -0
- package/skills/_shared/core/execution-policy.md +31 -0
- package/skills/_shared/core/lessons-learned.md +33 -0
- package/skills/_shared/core/prompt-structure.md +18 -0
- package/skills/_shared/core/quality-principles.md +11 -0
- package/skills/_shared/core/session-metrics.md +35 -0
- package/skills/_shared/core/skill-routing.md +39 -0
- package/skills/_shared/core/test-approach.md +54 -0
- package/skills/_shared/core/vendor-detection.md +52 -0
- package/skills/_shared/runtime/memory-protocol.md +118 -0
- package/skills/_shared/runtime/result-contract.md +64 -0
- package/skills/_shared/ui-interaction-quality.md +133 -0
- package/skills/oma-academic-writing/SKILL.md +181 -0
- package/skills/oma-academic-writing/resources/abstract-guide.md +51 -0
- package/skills/oma-academic-writing/resources/academic-verb-tiers.md +42 -0
- package/skills/oma-academic-writing/resources/anti-ai-checklist.md +52 -0
- package/skills/oma-academic-writing/resources/hedging-guide.md +132 -0
- package/skills/oma-academic-writing/resources/sentence-structure-reference.md +151 -0
- package/skills/oma-architecture/SKILL.md +203 -0
- package/skills/oma-architecture/resources/api-evolution.md +41 -0
- package/skills/oma-architecture/resources/checklist.md +42 -0
- package/skills/oma-architecture/resources/execution-protocol.md +115 -0
- package/skills/oma-architecture/resources/methodology-selection.md +92 -0
- package/skills/oma-architecture/resources/migration-patterns.md +28 -0
- package/skills/oma-architecture/resources/output-templates.md +111 -0
- package/skills/oma-architecture/resources/stakeholder-synthesis.md +80 -0
- package/skills/oma-backend/SKILL.md +202 -0
- package/skills/oma-backend/resources/checklist.md +45 -0
- package/skills/oma-backend/resources/error-playbook.md +97 -0
- package/skills/oma-backend/resources/execution-protocol.md +39 -0
- package/skills/oma-backend/resources/orm-reference.md +61 -0
- package/skills/oma-backend/variants/node/api-template.ts +315 -0
- package/skills/oma-backend/variants/node/snippets.md +475 -0
- package/skills/oma-backend/variants/node/stack.yaml +21 -0
- package/skills/oma-backend/variants/node/tech-stack.md +33 -0
- package/skills/oma-backend/variants/python/api-template.py +378 -0
- package/skills/oma-backend/variants/python/snippets.md +199 -0
- package/skills/oma-backend/variants/python/stack.yaml +18 -0
- package/skills/oma-backend/variants/python/tech-stack.md +45 -0
- package/skills/oma-backend/variants/rust/api-template.rs +635 -0
- package/skills/oma-backend/variants/rust/snippets.md +471 -0
- package/skills/oma-backend/variants/rust/stack.yaml +20 -0
- package/skills/oma-backend/variants/rust/tech-stack.md +36 -0
- package/skills/oma-backend/variants/stack.schema.json +73 -0
- package/skills/oma-brainstorm/SKILL.md +166 -0
- package/skills/oma-brainstorm/resources/triz-lite.md +117 -0
- package/skills/oma-coordination/SKILL.md +171 -0
- package/skills/oma-db/SKILL.md +201 -0
- package/skills/oma-db/resources/anti-patterns.md +177 -0
- package/skills/oma-db/resources/checklist.md +67 -0
- package/skills/oma-db/resources/document-templates.md +107 -0
- package/skills/oma-db/resources/error-playbook.md +50 -0
- package/skills/oma-db/resources/examples.md +170 -0
- package/skills/oma-db/resources/execution-protocol.md +127 -0
- package/skills/oma-db/resources/iso-controls.md +98 -0
- package/skills/oma-db/resources/migration-playbook.md +93 -0
- package/skills/oma-db/resources/query-tuning.md +57 -0
- package/skills/oma-db/resources/vector-db.md +159 -0
- package/skills/oma-debug/SKILL.md +146 -0
- package/skills/oma-debug/resources/bug-report-template.md +327 -0
- package/skills/oma-debug/resources/checklist.md +31 -0
- package/skills/oma-debug/resources/common-patterns.md +734 -0
- package/skills/oma-debug/resources/debugging-checklist.md +364 -0
- package/skills/oma-debug/resources/error-playbook.md +94 -0
- package/skills/oma-debug/resources/execution-protocol.md +40 -0
- package/skills/oma-design/SKILL.md +135 -0
- package/skills/oma-design/resources/asset-strategy.md +34 -0
- package/skills/oma-design/resources/checklist.md +63 -0
- package/skills/oma-design/resources/data-grid.md +48 -0
- package/skills/oma-design/resources/design-md-spec.md +107 -0
- package/skills/oma-design/resources/design-tokens.md +53 -0
- package/skills/oma-design/resources/error-playbook.md +18 -0
- package/skills/oma-design/resources/execution-protocol.md +95 -0
- package/skills/oma-design/resources/external-design-skills.md +130 -0
- package/skills/oma-design/resources/getdesign-fetcher.md +104 -0
- package/skills/oma-design/resources/product-interface-review.md +59 -0
- package/skills/oma-design/resources/redesign-protocol.md +52 -0
- package/skills/oma-design/resources/stitch-integration.md +244 -0
- package/skills/oma-dev-workflow/SKILL.md +375 -0
- package/skills/oma-dev-workflow/resources/affected-checks.py +58 -0
- package/skills/oma-dev-workflow/resources/api-workflows.md +58 -0
- package/skills/oma-dev-workflow/resources/database-patterns.md +107 -0
- package/skills/oma-dev-workflow/resources/i18n-patterns.md +63 -0
- package/skills/oma-dev-workflow/resources/release-coordination.md +240 -0
- package/skills/oma-dev-workflow/resources/tooling-profiles.md +217 -0
- package/skills/oma-dev-workflow/resources/troubleshooting.md +78 -0
- package/skills/oma-dev-workflow/resources/validation-pipeline.md +246 -0
- package/skills/oma-docs/SKILL.md +77 -0
- package/skills/oma-docs/resources/commands.md +76 -0
- package/skills/oma-explanation/SKILL.md +212 -0
- package/skills/oma-explanation/resources/document-structure.md +115 -0
- package/skills/oma-explanation/resources/draft-format.md +199 -0
- package/skills/oma-explanation/resources/html-contract.md +102 -0
- package/skills/oma-frontend/SKILL.md +227 -0
- package/skills/oma-frontend/resources/angular-rules.md +110 -0
- package/skills/oma-frontend/resources/checklist.md +76 -0
- package/skills/oma-frontend/resources/error-playbook.md +125 -0
- package/skills/oma-frontend/resources/execution-protocol.md +46 -0
- package/skills/oma-frontend/resources/snippets.md +354 -0
- package/skills/oma-frontend/resources/specialized-visual-implementation.md +193 -0
- package/skills/oma-frontend/resources/tailwind-rules.md +83 -0
- package/skills/oma-frontend/resources/tech-stack.md +156 -0
- package/skills/oma-hwp/SKILL.md +166 -0
- package/skills/oma-hwp/config/hwp-config.yaml +35 -0
- package/skills/oma-hwp/resources/balanced-tables.ts +39 -0
- package/skills/oma-hwp/resources/execution-protocol.md +145 -0
- package/skills/oma-hwp/resources/flatten-tables.ts +101 -0
- package/skills/oma-hwp/resources/package.json +13 -0
- package/skills/oma-hwp/resources/troubleshooting.md +70 -0
- package/skills/oma-hwp/resources/tsconfig.json +10 -0
- package/skills/oma-hwp/resources/turndown-plugin-gfm.d.ts +9 -0
- package/skills/oma-image/SKILL.md +142 -0
- package/skills/oma-image/config/image-config.yaml +70 -0
- package/skills/oma-image/resources/checklist.md +18 -0
- package/skills/oma-image/resources/execution-protocol.md +117 -0
- package/skills/oma-image/resources/invocation.md +105 -0
- package/skills/oma-image/resources/prompt-tips.md +71 -0
- package/skills/oma-image/resources/vendor-matrix.md +90 -0
- package/skills/oma-market/SKILL.md +142 -0
- package/skills/oma-market/resources/checklist.md +12 -0
- package/skills/oma-market/resources/error-playbook.md +12 -0
- package/skills/oma-market/resources/execution-protocol.md +60 -0
- package/skills/oma-market/resources/frameworks/pestel.md +101 -0
- package/skills/oma-market/resources/frameworks/porters-5f.md +90 -0
- package/skills/oma-market/resources/frameworks/swot.md +61 -0
- package/skills/oma-market/resources/intent-rules.md +35 -0
- package/skills/oma-market/resources/output-laws.md +53 -0
- package/skills/oma-mobile/SKILL.md +168 -0
- package/skills/oma-mobile/resources/checklist.md +77 -0
- package/skills/oma-mobile/resources/error-playbook.md +198 -0
- package/skills/oma-mobile/resources/execution-protocol.md +47 -0
- package/skills/oma-mobile/resources/screen-template.dart +331 -0
- package/skills/oma-mobile/resources/screen-template.swift +239 -0
- package/skills/oma-mobile/resources/screen-template.tsx +179 -0
- package/skills/oma-mobile/resources/tech-stack.md +69 -0
- package/skills/oma-mobile/variants/README.md +45 -0
- package/skills/oma-mobile/variants/flutter/api-template.dart +461 -0
- package/skills/oma-mobile/variants/flutter/snippets.md +1353 -0
- package/skills/oma-mobile/variants/flutter/stack.yaml +21 -0
- package/skills/oma-mobile/variants/flutter/tech-stack.md +201 -0
- package/skills/oma-mobile/variants/kotlin-android/api-template.kt +182 -0
- package/skills/oma-mobile/variants/kotlin-android/snippets.md +871 -0
- package/skills/oma-mobile/variants/kotlin-android/stack.yaml +32 -0
- package/skills/oma-mobile/variants/kotlin-android/tech-stack.md +159 -0
- package/skills/oma-mobile/variants/react-native/api-template.ts +531 -0
- package/skills/oma-mobile/variants/react-native/snippets.md +1014 -0
- package/skills/oma-mobile/variants/react-native/stack.yaml +22 -0
- package/skills/oma-mobile/variants/react-native/tech-stack.md +192 -0
- package/skills/oma-mobile/variants/stack.schema.json +68 -0
- package/skills/oma-mobile/variants/swift-ios/api-template.swift +165 -0
- package/skills/oma-mobile/variants/swift-ios/snippets.md +1028 -0
- package/skills/oma-mobile/variants/swift-ios/stack.yaml +25 -0
- package/skills/oma-mobile/variants/swift-ios/tech-stack.md +174 -0
- package/skills/oma-observability/SKILL.md +225 -0
- package/skills/oma-observability/resources/anti-patterns.md +555 -0
- package/skills/oma-observability/resources/boundaries/cross-application.md +348 -0
- package/skills/oma-observability/resources/boundaries/multi-tenant.md +290 -0
- package/skills/oma-observability/resources/boundaries/release.md +372 -0
- package/skills/oma-observability/resources/boundaries/slo.md +225 -0
- package/skills/oma-observability/resources/checklist.md +123 -0
- package/skills/oma-observability/resources/examples.md +448 -0
- package/skills/oma-observability/resources/execution-protocol.md +166 -0
- package/skills/oma-observability/resources/incident-forensics.md +348 -0
- package/skills/oma-observability/resources/intent-rules.md +307 -0
- package/skills/oma-observability/resources/layers/L3-network.md +281 -0
- package/skills/oma-observability/resources/layers/L4-transport.md +299 -0
- package/skills/oma-observability/resources/layers/L7-application/crash-analytics.md +359 -0
- package/skills/oma-observability/resources/layers/L7-application/mobile-rum.md +278 -0
- package/skills/oma-observability/resources/layers/L7-application/waf.md +246 -0
- package/skills/oma-observability/resources/layers/L7-application/web-rum.md +350 -0
- package/skills/oma-observability/resources/layers/mesh.md +368 -0
- package/skills/oma-observability/resources/matrix.md +177 -0
- package/skills/oma-observability/resources/meta-observability.md +605 -0
- package/skills/oma-observability/resources/observability-as-code.md +666 -0
- package/skills/oma-observability/resources/signals/audit.md +310 -0
- package/skills/oma-observability/resources/signals/cost.md +303 -0
- package/skills/oma-observability/resources/signals/logs.md +263 -0
- package/skills/oma-observability/resources/signals/metrics.md +478 -0
- package/skills/oma-observability/resources/signals/privacy.md +351 -0
- package/skills/oma-observability/resources/signals/profiles.md +245 -0
- package/skills/oma-observability/resources/signals/traces.md +360 -0
- package/skills/oma-observability/resources/standards.md +214 -0
- package/skills/oma-observability/resources/transport/collector-topology.md +308 -0
- package/skills/oma-observability/resources/transport/otlp-grpc-vs-http.md +256 -0
- package/skills/oma-observability/resources/transport/sampling-recipes.md +297 -0
- package/skills/oma-observability/resources/transport/udp-statsd-mtu.md +167 -0
- package/skills/oma-observability/resources/vendor-categories.md +352 -0
- package/skills/oma-orchestration/SKILL.md +302 -0
- package/skills/oma-orchestration/config/cli-config.yaml +92 -0
- package/skills/oma-orchestration/resources/memory-schema.md +232 -0
- package/skills/oma-orchestration/resources/subagent-prompt-template.md +30 -0
- package/skills/oma-orchestration/scripts/parallel-run.sh +6 -0
- package/skills/oma-orchestration/scripts/spawn-agent.sh +5 -0
- package/skills/oma-orchestration/scripts/verify.sh +20 -0
- package/skills/oma-orchestration/templates/backend-task.md +19 -0
- package/skills/oma-orchestration/templates/debug-task.md +16 -0
- package/skills/oma-orchestration/templates/frontend-task.md +18 -0
- package/skills/oma-orchestration/templates/mobile-task.md +18 -0
- package/skills/oma-orchestration/templates/qa-task.md +16 -0
- package/skills/oma-orchestration/templates/tasks-example.yaml +15 -0
- package/skills/oma-pdf/SKILL.md +162 -0
- package/skills/oma-pdf/config/pdf-config.yaml +27 -0
- package/skills/oma-pdf/resources/execution-protocol.md +127 -0
- package/skills/oma-pm/SKILL.md +157 -0
- package/skills/oma-pm/resources/error-playbook.md +75 -0
- package/skills/oma-pm/resources/examples.md +439 -0
- package/skills/oma-pm/resources/execution-protocol.md +62 -0
- package/skills/oma-pm/resources/iso-planning.md +110 -0
- package/skills/oma-pm/resources/plan-phase-protocol.md +66 -0
- package/skills/oma-pm/resources/task-template.json +86 -0
- package/skills/oma-qa/SKILL.md +141 -0
- package/skills/oma-qa/resources/checklist.md +339 -0
- package/skills/oma-qa/resources/error-playbook.md +98 -0
- package/skills/oma-qa/resources/execution-protocol.md +169 -0
- package/skills/oma-qa/resources/iso-quality.md +82 -0
- package/skills/oma-qa/resources/verify-ship-protocol.md +116 -0
- package/skills/oma-recap/SKILL.md +245 -0
- package/skills/oma-recap/resources/output-formats.md +83 -0
- package/skills/oma-refactor/SKILL.md +184 -0
- package/skills/oma-refactor/resources/definition.md +108 -0
- package/skills/oma-refactor/resources/governance.md +33 -0
- package/skills/oma-refactor/resources/hotspots.py +44 -0
- package/skills/oma-refactor/resources/measurement.md +93 -0
- package/skills/oma-scholar/SKILL.md +276 -0
- package/skills/oma-scholar/config/scholar-config.yaml +79 -0
- package/skills/oma-scholar/resources/api-endpoints.md +174 -0
- package/skills/oma-scholar/resources/checklist.md +92 -0
- package/skills/oma-scholar/resources/execution-protocol.md +224 -0
- package/skills/oma-scholar/resources/fallback-providers.md +143 -0
- package/skills/oma-scholar/resources/setup-openalex.md +85 -0
- package/skills/oma-scholar/resources/sidecar-spec.md +341 -0
- package/skills/oma-scholar/resources/upstream-spec-cache.md +972 -0
- package/skills/oma-scm/SKILL.md +103 -0
- package/skills/oma-scm/resources/cm-operations.md +56 -0
- package/skills/oma-scm/resources/codeowners-playbook.md +45 -0
- package/skills/oma-scm/resources/conventional-commits.md +108 -0
- package/skills/oma-scm/resources/merge-risk.md +76 -0
- package/skills/oma-scm/resources/onboarding-risk-signals.md +71 -0
- package/skills/oma-search/SKILL.md +125 -0
- package/skills/oma-search/resources/checklist.md +36 -0
- package/skills/oma-search/resources/error-playbook.md +66 -0
- package/skills/oma-search/resources/examples.md +80 -0
- package/skills/oma-search/resources/execution-protocol.md +86 -0
- package/skills/oma-search/resources/intent-rules.md +47 -0
- package/skills/oma-search/resources/trust-registry.md +53 -0
- package/skills/oma-security/SKILL.md +172 -0
- package/skills/oma-security/resources/artex.md +108 -0
- package/skills/oma-security/resources/ci.md +134 -0
- package/skills/oma-security/resources/cisco-source.md +108 -0
- package/skills/oma-security/resources/deepsec-config.md +45 -0
- package/skills/oma-security/resources/deepsec-matchers.md +35 -0
- package/skills/oma-security/resources/deepsec-scanning.md +45 -0
- package/skills/oma-security/resources/deepsec-setup.md +41 -0
- package/skills/oma-security/resources/findings-contract.md +408 -0
- package/skills/oma-security/resources/mcp-scanning.md +122 -0
- package/skills/oma-security/resources/skill-scanning.md +106 -0
- package/skills/oma-security/resources/validation.md +71 -0
- package/skills/oma-skill-creation/SKILL.md +84 -0
- package/skills/oma-skill-creation/resources/prompt-evaluation.md +36 -0
- package/skills/oma-skill-creation/resources/ssl-lite-template.md +108 -0
- package/skills/oma-skill-creation/resources/validation-checklist.md +101 -0
- package/skills/oma-slide/SKILL.md +277 -0
- package/skills/oma-slide/resources/animation-patterns.md +245 -0
- package/skills/oma-slide/resources/assets/deck-stage.js +514 -0
- package/skills/oma-slide/resources/assets/viewport-base.css +257 -0
- package/skills/oma-slide/resources/canva-integration.md +203 -0
- package/skills/oma-slide/resources/checklist.md +46 -0
- package/skills/oma-slide/resources/design-doctrine.md +260 -0
- package/skills/oma-slide/resources/fixed-stage.md +232 -0
- package/skills/oma-slide/resources/generation-protocol.md +393 -0
- package/skills/oma-slide/resources/native-pptx.md +84 -0
- package/skills/oma-slide/resources/selection-index.json +461 -0
- package/skills/oma-slide/resources/style-presets.LICENSE +10 -0
- package/skills/oma-slide/resources/style-presets.md +520 -0
- package/skills/oma-tf-infra/SKILL.md +201 -0
- package/skills/oma-tf-infra/resources/checklist.md +53 -0
- package/skills/oma-tf-infra/resources/cost-optimization.md +574 -0
- package/skills/oma-tf-infra/resources/error-playbook.md +68 -0
- package/skills/oma-tf-infra/resources/examples.md +38 -0
- package/skills/oma-tf-infra/resources/execution-protocol.md +129 -0
- package/skills/oma-tf-infra/resources/iso-42001-infra.md +229 -0
- package/skills/oma-tf-infra/resources/multi-cloud-examples.md +241 -0
- package/skills/oma-tf-infra/resources/policy-testing-examples.md +332 -0
- package/skills/oma-translation/SKILL.md +229 -0
- package/skills/oma-translation/resources/anti-ai-patterns.md +104 -0
- package/skills/oma-translation/resources/lang/_template.md +73 -0
- package/skills/oma-translation/resources/lang/en.md +123 -0
- package/skills/oma-translation/resources/lang/ja.md +274 -0
- package/skills/oma-translation/resources/lang/ko.md +228 -0
- package/skills/oma-translation/resources/lang/zh.md +285 -0
- package/skills/oma-translation/resources/style-skills.md +104 -0
- package/skills/oma-translation/resources/translation-rubric.md +126 -0
- package/skills/oma-video/SKILL.md +229 -0
- package/skills/oma-video/config/video-config.yaml +60 -0
- package/skills/oma-video/resources/checklist.md +26 -0
- package/skills/oma-video/resources/execution-protocol.md +122 -0
- package/skills/oma-video/resources/existing-media.md +185 -0
- package/skills/oma-video/resources/hyperframes-authoring/README.md +44 -0
- package/skills/oma-video/resources/hyperframes-authoring/demo.md +31 -0
- package/skills/oma-video/resources/hyperframes-authoring/explainer.md +10 -0
- package/skills/oma-video/resources/hyperframes-authoring/shorts.md +10 -0
- package/skills/oma-video/resources/motion-principles.md +81 -0
- package/skills/oma-video/resources/mpt/driver.py +301 -0
- package/skills/oma-video/resources/prompt-tips.md +75 -0
- package/skills/oma-video/resources/script-schema.md +81 -0
- package/skills/oma-video/resources/sound-design.md +127 -0
- package/skills/oma-video/resources/strudel/package.json +11 -0
- package/skills/oma-video/resources/strudel/page.html +87 -0
- package/skills/oma-video/resources/strudel/render.mjs +295 -0
- package/skills/oma-video/resources/vendor-matrix.md +124 -0
- package/skills/oma-voice/SKILL.md +249 -0
- package/skills/oma-voice/config/voice-config.yaml +30 -0
- package/skills/oma-voice/resources/audio-processing.md +119 -0
- package/skills/oma-voice/resources/checklist.md +47 -0
- package/skills/oma-voice/resources/execution-protocol.md +215 -0
- package/skills/oma-voice/resources/prompt-tips.md +43 -0
- package/skills/oma-voice/resources/voice-matrix.md +51 -0
- package/skills/oma-voice/resources/voicestudio.md +87 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Existing Video Analysis and Editing
|
|
2
|
+
|
|
3
|
+
Use this branch for processing a supplied video or a real, already-rendered
|
|
4
|
+
video. Work in a new run directory and retain the source. Audio-only requests
|
|
5
|
+
stay with `oma-voice`. The OMA creation CLI, providers, asset bus and
|
|
6
|
+
`render-spec.json` remain separate from the upstream editing project format.
|
|
7
|
+
|
|
8
|
+
## Resolve the upstream skill
|
|
9
|
+
|
|
10
|
+
1. Check the runtime skill catalog for `ffmpeg-skill` from
|
|
11
|
+
`https://github.com/kajisho5/ffmpeg-skill`. Reuse its advertised installed
|
|
12
|
+
location and read its complete `SKILL.md` when available.
|
|
13
|
+
2. If it is absent or its support bundle is incomplete, load it without a
|
|
14
|
+
persistent installation or another agent:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bunx skills use kajisho5/ffmpeg-skill@ffmpeg-skill
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Omit `--agent`. Read the emitted skill instructions and use the actual
|
|
21
|
+
supporting-files directory from the result for every relative path. Do not
|
|
22
|
+
guess a cache path or treat successful prompt output as a completed edit.
|
|
23
|
+
3. Confirm that the supporting directory retains the complete package layout:
|
|
24
|
+
`SKILL.md`, `package.json`, all of `scripts/` including the `_common/` package,
|
|
25
|
+
`templates/` and `references/`. Scripts import sibling modules and use files
|
|
26
|
+
outside their own directory. An entry file or selected scripts alone are not
|
|
27
|
+
a usable bundle. Record the resolved source, version and support path with
|
|
28
|
+
the run; report an incomplete bundle rather than assembling guessed files.
|
|
29
|
+
4. Follow the loaded upstream instructions for the selected operation. They
|
|
30
|
+
own the script and flag selection, command recipes, templates, editing
|
|
31
|
+
`project.json`, and probe/check/look workflow. Consult the selected script's
|
|
32
|
+
help or the requested reference instead of copying the cookbook here.
|
|
33
|
+
Do not start every job with `doctor`; follow the upstream failure-diagnosis
|
|
34
|
+
order. The standalone `bunx ffmpeg-skill` installer is not this loading path.
|
|
35
|
+
5. An already-connected upstream MCP server is an optional execution surface.
|
|
36
|
+
Discover its actual tools and argument schemas before calling it. Retain
|
|
37
|
+
`mcp/server.py` and the complete script bundle when using that surface; its
|
|
38
|
+
contract comes from the same scripts. Do not configure a server or invent
|
|
39
|
+
tool names merely because a requested tool was not listed initially.
|
|
40
|
+
|
|
41
|
+
The upstream package needs local Python and FFmpeg/FFprobe, plus the encoders,
|
|
42
|
+
filters, fonts or optional transcription engine required by the chosen
|
|
43
|
+
operation. A loaded skill does not establish those capabilities. If a requested
|
|
44
|
+
operation or evidence is not supported by the loaded scripts or connected MCP,
|
|
45
|
+
report the missing capability. Do not invent a raw FFmpeg command fallback.
|
|
46
|
+
|
|
47
|
+
## Retain source-timeline evidence
|
|
48
|
+
|
|
49
|
+
Before deciding from the media, retain the measurements exposed by the upstream
|
|
50
|
+
probe and selected operation: actual stream indexes, rotation, dimensions, time
|
|
51
|
+
bases, start times, channel layout, duration and any measured frame timestamps.
|
|
52
|
+
Keep source PTS alongside time measured from the clip's start when available.
|
|
53
|
+
Frame number divided by nominal FPS is not a source timestamp for VFR or
|
|
54
|
+
nonzero-start media.
|
|
55
|
+
|
|
56
|
+
Express selected audio and video on one source timeline and preserve their
|
|
57
|
+
relative start offset. Map a detector's resampled audio clock back to that
|
|
58
|
+
common timeline before applying intervals to video. Record the conversion and
|
|
59
|
+
any gap, padding or frame/sample boundary adjustment; do not independently reset
|
|
60
|
+
each stream's first retained sample to zero.
|
|
61
|
+
|
|
62
|
+
The loaded upstream tools may not expose an exact source-PTS or frame inventory.
|
|
63
|
+
Contact-sheet labels and requested extraction times do not establish that
|
|
64
|
+
inventory. If the task requires exact mapping that the available tools cannot
|
|
65
|
+
prove, state the limitation and retain the evidence they do provide. Do not
|
|
66
|
+
fabricate source PTS, silently substitute nominal-FPS arithmetic, or claim exact
|
|
67
|
+
sampling from an approximate extraction.
|
|
68
|
+
|
|
69
|
+
## Select samples and interpret the picture
|
|
70
|
+
|
|
71
|
+
Resolve the sampling definition before extracting:
|
|
72
|
+
|
|
73
|
+
| Definition | Meaning |
|
|
74
|
+
|---|---|
|
|
75
|
+
| Codec keyframes | Decoder-marked random-access frames; encoding-dependent |
|
|
76
|
+
| I-pictures | Intra-coded pictures; not necessarily random-access frames |
|
|
77
|
+
| Scene changes | Visual-change candidates under a stated source-dependent threshold |
|
|
78
|
+
| Time sampling | Source frames selected under the requested interval or timestamp policy |
|
|
79
|
+
|
|
80
|
+
These are different sets. If an ambiguous "key frames" request changes the
|
|
81
|
+
result materially, resolve its meaning; otherwise state the selected method.
|
|
82
|
+
Choose an operation actually exposed by the loaded skill. An exact timestamp
|
|
83
|
+
grid additionally needs nearest-source-frame evidence; a contact sheet does
|
|
84
|
+
not prove it. Report unsupported sampling rather than silently changing its
|
|
85
|
+
definition. Keep the selection parameters and each image's supported timestamp
|
|
86
|
+
mapping. Image numbering alone carries no timing.
|
|
87
|
+
|
|
88
|
+
Run the host vision tool on the actual extracted images for visual analysis.
|
|
89
|
+
Record counts per supported source timestamp, with occluded or uncertain
|
|
90
|
+
instances marked. Visible instances in one frame and unique identities across
|
|
91
|
+
time are different claims; identity totals need tracking or explicit matching.
|
|
92
|
+
Never sum frame counts to claim a unique-object total. Sparse samples cannot
|
|
93
|
+
establish that an event never appeared between them; refine the relevant range
|
|
94
|
+
when that is the question. If vision is unavailable, retain the images and
|
|
95
|
+
report the missing interpretation. Mechanical measurements do not establish
|
|
96
|
+
subject identity, interesting content or visual quality.
|
|
97
|
+
|
|
98
|
+
## Decide which intervals may be removed
|
|
99
|
+
|
|
100
|
+
Amplitude-based silence, absence of speech and unwanted content are separate
|
|
101
|
+
criteria. Music can be loud without speech; speech can be quiet; a pause can
|
|
102
|
+
carry a meaningful visual event. Use the user's criterion to decide removals.
|
|
103
|
+
A long introduction is not automatically removable.
|
|
104
|
+
|
|
105
|
+
Choose amplitude thresholds from the request or measured room tone and quiet
|
|
106
|
+
speech, and minimum silence from the requested pause policy. Inspect channels
|
|
107
|
+
separately when a downmix could mask speech. With no audio stream there are no
|
|
108
|
+
audio-silence measurements. For speech-driven edits, require timed VAD or
|
|
109
|
+
transcription regions from an available provider. `oma-voice` may return only
|
|
110
|
+
text; inspect its response before treating it as a timing source. Missing words
|
|
111
|
+
are not evidence of silence. Never invent word or segment times. Retain the
|
|
112
|
+
model, channel, sample rate, thresholds and time-origin conversion.
|
|
113
|
+
|
|
114
|
+
Before handing intervals to the upstream edit operation:
|
|
115
|
+
|
|
116
|
+
1. Map measured candidates to source time, clip to media bounds, sort and merge
|
|
117
|
+
overlaps of the same classification. Close an open trailing silence at the
|
|
118
|
+
actual audio end; absent audio beyond that end is not measured silence.
|
|
119
|
+
2. Protect measured speech with request-appropriate context padding bounded by
|
|
120
|
+
the source. Subtract these protected regions from amplitude candidates so
|
|
121
|
+
quiet breaths and word boundaries are not removed solely for their level.
|
|
122
|
+
3. Listen to disputed regions with their adjacent phrase and inspect the video
|
|
123
|
+
when detectors or context disagree. Retain and identify unresolved regions
|
|
124
|
+
when the evidence cannot establish the requested removal criterion.
|
|
125
|
+
4. Apply the requested minimum removal length after reconciliation. Save the
|
|
126
|
+
accepted removals, their reasons and the complementary keep intervals.
|
|
127
|
+
This mapping must also govern captions and other retained tracks.
|
|
128
|
+
|
|
129
|
+
## Apply one edit timeline
|
|
130
|
+
|
|
131
|
+
Pass the accepted intervals and required stream relationships to the supported
|
|
132
|
+
upstream operation. Keep audio and video on the same edit mapping; removing
|
|
133
|
+
silence from audio alone while retaining an unchanged picture shifts sync.
|
|
134
|
+
Keyframe-constrained stream-copy cuts and frame/sample-accurate boundaries are
|
|
135
|
+
different results. Record the precision and any adjusted boundary, gap or
|
|
136
|
+
padding. An empty keep list needs an explicit empty-result decision.
|
|
137
|
+
|
|
138
|
+
Retained subtitle and additional audio tracks need the same mapping. Do not
|
|
139
|
+
silently drop them or keep their old timing. If the loaded operation cannot
|
|
140
|
+
preserve the required A/V offset, boundary precision or selected streams,
|
|
141
|
+
report that limitation rather than claiming a synchronized result.
|
|
142
|
+
|
|
143
|
+
For multi-step editing, let upstream own its `project.json` and templates.
|
|
144
|
+
OMA's creation `script.json`, `timing.json` and `render-spec.json` are distinct
|
|
145
|
+
contracts. Do not pass one format as the other or imply that an adapter exists.
|
|
146
|
+
|
|
147
|
+
## Assemble a dubbed clip
|
|
148
|
+
|
|
149
|
+
Use `oma-voice` for transcription, synthesis and utterance fitting, and
|
|
150
|
+
`oma-translation` for spoken translation. Timing must come from the supplied
|
|
151
|
+
timeline or a provider that returns measured regions; plain transcript text
|
|
152
|
+
has no timing. Return the fitted audio to this branch for placement through a
|
|
153
|
+
supported upstream operation.
|
|
154
|
+
|
|
155
|
+
When the user requests an unchanged picture, preserve its encoded video stream
|
|
156
|
+
and use a compatible container where the supported tool permits it. Do not
|
|
157
|
+
silently transcode it. Put source and dub on an explicit common clock, retaining
|
|
158
|
+
nonzero PTS and the intended leading and inter-utterance gaps. A delayed
|
|
159
|
+
container start alone is not proof that the dub contains the required silence.
|
|
160
|
+
|
|
161
|
+
Do not truncate the picture to conceal a short dub. Resolve missing coverage
|
|
162
|
+
through utterance fitting or request-appropriate silence padding. Preserve or
|
|
163
|
+
mix background audio only as requested. If the upstream operation cannot keep
|
|
164
|
+
the picture, coverage, container compatibility or offset requested, report the
|
|
165
|
+
unsupported requirement before claiming success.
|
|
166
|
+
|
|
167
|
+
## Return evidence in the requested format
|
|
168
|
+
|
|
169
|
+
Retain the upstream exit status, output probe, delivery checks and any required
|
|
170
|
+
contact-sheet path and actual inspection result. Interpret them against the
|
|
171
|
+
requested edit. File existence or successful decoding alone does not establish
|
|
172
|
+
correct cut choices, visual counts or natural speech. Report unresolved timing,
|
|
173
|
+
coverage, capability and visual-inspection limits.
|
|
174
|
+
|
|
175
|
+
Use the user's requested report or file format. The upstream's default report
|
|
176
|
+
layout does not override that request; keep its probe/check/look evidence even
|
|
177
|
+
when presenting it in a different shape. Do not claim an image was inspected
|
|
178
|
+
when no image-viewing tool was available.
|
|
179
|
+
|
|
180
|
+
## References
|
|
181
|
+
|
|
182
|
+
- [Upstream ffmpeg-skill](https://github.com/kajisho5/ffmpeg-skill): the loaded
|
|
183
|
+
`SKILL.md`, its conditional references, scripts, templates and MCP contract.
|
|
184
|
+
- [Skills CLI use](https://github.com/vercel-labs/skills/blob/main/docs/commands.md#skills-use):
|
|
185
|
+
prompt and complete support-directory loading without an installed skill.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# HyperFrames authoring
|
|
2
|
+
|
|
3
|
+
Each run has an HTML project at `<runDir>/hyperframes/`. `oma video compose`
|
|
4
|
+
prepares the latest HyperFrames CLI, local GSAP, optional Pretendard font,
|
|
5
|
+
asset copies, and an `AUTHORING.md` contract. The agent writes `index.html`.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
oma video compose <runDir> --output json
|
|
9
|
+
# Read hyperframes/AUTHORING.md and its upstream API references.
|
|
10
|
+
# Replace hyperframes/index.html with the finished composition.
|
|
11
|
+
oma video render <runDir> --output json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Contract
|
|
15
|
+
|
|
16
|
+
- The run's `render-spec.json` remains authoritative. The project copy rewrites
|
|
17
|
+
media paths to `assets/`; `render-spec.js` exposes it as `window.OMA_VIDEO_SPEC`.
|
|
18
|
+
- Use a standalone root with `data-composition-id`, `data-width`, `data-height`,
|
|
19
|
+
and `data-duration`. Convert frame offsets and durations to seconds with fps.
|
|
20
|
+
- Use HTML `img`, `video`, and `audio` elements. Each audio element needs a unique
|
|
21
|
+
id. HyperFrames controls media playback; do not seek or play it yourself.
|
|
22
|
+
Sound-effect takes live under `assets/audio/sfx/` and are placed per
|
|
23
|
+
`../sound-design.md`.
|
|
24
|
+
- Use local `vendor/gsap.min.js` and register a paused timeline at
|
|
25
|
+
`window.__timelines[composition]`. No remote dependencies, wall-clock timers,
|
|
26
|
+
or unseeded randomness. Animate children of timed clips.
|
|
27
|
+
- Convert the staged SRT into timed caption elements while authoring. Display
|
|
28
|
+
only the active cue and respect the caption safe area and maximum width.
|
|
29
|
+
- Load `assets/fonts/PretendardVariable.woff2` with `@font-face` when available;
|
|
30
|
+
otherwise use `system-ui`.
|
|
31
|
+
- `compose` preserves authored HTML and refreshes generated inputs. After
|
|
32
|
+
changing the render spec, update the composition to honor its timing/layout.
|
|
33
|
+
- Follow upstream skills for API details. OMA controls authorization, providers,
|
|
34
|
+
and delivery; upstream feedback, publishing, and extra installs are not part
|
|
35
|
+
of this local render path.
|
|
36
|
+
|
|
37
|
+
## Verification
|
|
38
|
+
|
|
39
|
+
`oma video render` runs HyperFrames lint and strict rendering, then checks the
|
|
40
|
+
MP4 video stream, dimensions, and duration with ffprobe. On success it updates
|
|
41
|
+
`manifest.json` with the output hash. Failed renders remain failures.
|
|
42
|
+
|
|
43
|
+
Read the relevant mode guide: `shorts.md`, `explainer.md`, or `demo.md`, and
|
|
44
|
+
`../motion-principles.md` for what appears, what moves, and when.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Demo (16:9, `--polish`) — layout spec
|
|
2
|
+
|
|
3
|
+
Composition id `Demo`. The human-recorded capture arrives as
|
|
4
|
+
`background.type: "video"` (full-frame, `<video>`, `objectFit: cover`);
|
|
5
|
+
scenes are overlays on top of it.
|
|
6
|
+
|
|
7
|
+
- **Opening**: start on the capture. Add an intro card only when the user asks for one: solid brand color, centered title 72px weight 800, hard cut in and out.
|
|
8
|
+
- **Callout scenes**: box the exact text or button the narration points to with a thin accent outline. `onScreenText` appears as a lower-third pill (bottom `safeArea.bottomPct%`, 40px, weight 700, band `rgba(0,0,0,.6)`) only when it adds something the narration does not say.
|
|
9
|
+
- **Zoom**: when the narration points at a region, scale and translate the background layer so that region fills the view. Ease out once on the frame where the narration reaches it, then hold still; drive it from the frame, not the clock. Choose the region from the inspected frames; the render spec's `kenBurns` flag is not the zoom signal.
|
|
10
|
+
- **Captions**: as `lower-third` in explainer.md.
|
|
11
|
+
- Never obscure the center 60% of the frame during callouts; the capture is the content.
|
|
12
|
+
- Audio as in shorts.md; the capture's own audio track is muted unless `audio.narration` is absent.
|
|
13
|
+
|
|
14
|
+
## Personal information (required)
|
|
15
|
+
|
|
16
|
+
A human recording shows whatever was on screen. Before authoring, sample frames
|
|
17
|
+
across the whole capture through the existing-media branch
|
|
18
|
+
(`../existing-media.md`) and inspect them with the host vision tool. Look for
|
|
19
|
+
user names in paths and prompts, account names and avatars, email addresses,
|
|
20
|
+
sidebars and file lists (editor, Finder, Orca), notifications, session and
|
|
21
|
+
request IDs, tokens or keys, memo and chat windows, and browser tabs or
|
|
22
|
+
bookmarks.
|
|
23
|
+
|
|
24
|
+
- Keep each finding out of the delivered frame: frame the zoom so it falls
|
|
25
|
+
outside the view, or cover it with an opaque layer timed to the interval it
|
|
26
|
+
is visible (`data-start` / `data-duration`). Do not rely on blur or
|
|
27
|
+
pixelation for text.
|
|
28
|
+
- Record each masked region and interval for the delivery report. The source
|
|
29
|
+
capture stays unmasked in the run directory; never deliver it.
|
|
30
|
+
- If frames cannot be inspected (no vision tool, no frame extraction), stop
|
|
31
|
+
and report that the capture is unchecked instead of rendering it.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Explainer (16:9 / 9:16) — layout spec
|
|
2
|
+
|
|
3
|
+
Composition id `Explainer`. 1920×1080 default; honour the spec's dimensions.
|
|
4
|
+
|
|
5
|
+
- Scenes are slide frames (`visual.type: "slide"`), diagrams/stills (`image`), or code frames; render with `<img>` `objectFit: contain` on the background color so slide edges are never cropped (use `cover` only for photographic stills).
|
|
6
|
+
- **On-screen text**: lower-left title band for 16:9 (bottom `safeArea.bottomPct%`, left `safeArea.leftPct%`, 48px, weight 700, dark 55% band, 12px radius); for 9:16 use the Shorts top placement.
|
|
7
|
+
- **Captions** (`lower-third` default): left-aligned, 40px, band `rgba(0,0,0,.55)`, `padding: 12px 20px`, radius 12, within the safe area.
|
|
8
|
+
- **Transitions**: hard cut at every scene boundary. Use a 12-frame cross-fade only when the user asked for a dissolve; an agent-authored `transitionOut` alone does not justify one.
|
|
9
|
+
- **Ken Burns** only when `kenBurns: true` (diagrams usually false).
|
|
10
|
+
- Audio as in shorts.md.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Shorts (9:16) — layout spec
|
|
2
|
+
|
|
3
|
+
Composition id `Shorts`. 1080×1920 (or the spec's dimensions), 30fps.
|
|
4
|
+
|
|
5
|
+
- **Background**: `background.src` fill (default `#0f1117`); `image`/`video` → full-frame under scenes.
|
|
6
|
+
- **Scenes**: each `scenes[i]` → a timed HTML element with `data-start=fromFrame/fps` and `data-duration=durationInFrames/fps`; visual fills the frame (`objectFit: cover`). `kenBurns: true` → slow zoom 1 → 1.08 over the scene, linear, clamped. `placeholder` / `#hex` src → solid color.
|
|
7
|
+
- **On-screen text** (`onScreenText[]`, joined by newlines): top area, `paddingTop: 10%`, centered, 56px, weight 800, white, `textShadow: 0 2px 10px rgba(0,0,0,.8)`, `maxWidth: 86%`. Enter by transform with an ease-out curve, never a fade; no per-letter animation. Leave it out when it repeats the caption (`../motion-principles.md`).
|
|
8
|
+
- **Captions** (`captions.style === "tiktok"`): bottom, centered, 64px, weight 800, white with shadow, no box, `paddingBottom: safeArea.bottomPct%`, `maxWidth: maxWidthPct%`. Show only the cue active at the current frame.
|
|
9
|
+
- **Audio**: an `<audio>` element with a unique id and the staged narration path; music at `10^(musicGainDb/20)` (default −18 dB).
|
|
10
|
+
- **Motion budget**: transform + opacity only; nothing faster than 150 ms; respect the seed for any variation.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Motion Principles
|
|
2
|
+
|
|
3
|
+
Rules for authoring `script.json` and the HyperFrames composition. The mode
|
|
4
|
+
guides in `hyperframes-authoring/` define layout; these rules define what
|
|
5
|
+
appears, what moves, and when, and they replace any conflicting guidance
|
|
6
|
+
elsewhere in this skill. Only an explicit user request or brand guide
|
|
7
|
+
overrides them. Numbers are starting points, not gates.
|
|
8
|
+
|
|
9
|
+
## Scenes
|
|
10
|
+
|
|
11
|
+
- One idea per scene, drawn as one diagram or visual. Each narration sentence
|
|
12
|
+
adds at most one element to it; no decoration per sentence.
|
|
13
|
+
- Explainer scenes run about 15-20 seconds. Split a scene that passes about 25
|
|
14
|
+
seconds or carries two subtopics. Shorts keep their short scenes.
|
|
15
|
+
- Scene cast: one hero object, one or two supporting objects, at most one text
|
|
16
|
+
block (a number, a label). Fold an eighth group into a summary instead of
|
|
17
|
+
adding it.
|
|
18
|
+
- When an event finishes, move on; do not hold a finished screen. Do not open
|
|
19
|
+
on a decorative intro.
|
|
20
|
+
|
|
21
|
+
## Motion
|
|
22
|
+
|
|
23
|
+
- Animate only when something enters or changes state, then settle. No idle
|
|
24
|
+
floating, wobble, breathing zoom, or colour cycling.
|
|
25
|
+
- Keep the anchor fixed and change only the subject. Motion that explains no
|
|
26
|
+
relation is an instant replace. No page-turn effects.
|
|
27
|
+
- A hard concept gets a still screen that defines it; a simple relation gets
|
|
28
|
+
one arrow.
|
|
29
|
+
- Entrances ease out (an `easeOutExpo`-like curve) and move by transform
|
|
30
|
+
rather than fading in.
|
|
31
|
+
- Transitions happen only at scene boundaries. Cut hard by default; use a
|
|
32
|
+
dissolve or fade only when requested.
|
|
33
|
+
- A standout effect appears once or twice per video, not per scene.
|
|
34
|
+
- Keep sizes, positions, emphasis boxes, and backing plates consistent across
|
|
35
|
+
the video.
|
|
36
|
+
|
|
37
|
+
## Text
|
|
38
|
+
|
|
39
|
+
- Text is the last resort. Drawable words become objects, logos, or charts:
|
|
40
|
+
a comparison as bars, a change as a line, a ratio as a 10-cell grid, an
|
|
41
|
+
order as arrows.
|
|
42
|
+
- Do not repeat the caption as on-screen text. Leave `onScreenText` empty when
|
|
43
|
+
it would duplicate the narration.
|
|
44
|
+
|
|
45
|
+
## Picture and narration
|
|
46
|
+
|
|
47
|
+
- The picture never runs ahead of the narration. Show a new object on the
|
|
48
|
+
frame where its word is spoken (`timing.json` word timings); captions start
|
|
49
|
+
with the speech.
|
|
50
|
+
- Show what is being talked about. "I built the app" shows the app; terminals
|
|
51
|
+
and code appear only when the process itself is the subject.
|
|
52
|
+
- Prefer the real thing. When a real source exists (the repository, the post,
|
|
53
|
+
the official site, the product screen), use a user-supplied screenshot or
|
|
54
|
+
recording rather than a generated image; never automate a login. Generated
|
|
55
|
+
stills cover concepts with no real counterpart.
|
|
56
|
+
- Use footage only for words meant literally: one clip per sentence, each held
|
|
57
|
+
for at least 3 seconds.
|
|
58
|
+
|
|
59
|
+
## Recordings
|
|
60
|
+
|
|
61
|
+
- Zoom to where the narration points and box the exact text or button; mark
|
|
62
|
+
people and objects with arrows. No decoration over the recording.
|
|
63
|
+
- Cut instantly from one recording to the next.
|
|
64
|
+
- Mask personal information before rendering (`hyperframes-authoring/demo.md`).
|
|
65
|
+
|
|
66
|
+
## Diagrams
|
|
67
|
+
|
|
68
|
+
Build each diagram in four steps:
|
|
69
|
+
|
|
70
|
+
1. Write its message as one sentence.
|
|
71
|
+
2. State the question it answers.
|
|
72
|
+
3. Pick its form from the vocabulary in Text (bars, line, grid, arrows) or a
|
|
73
|
+
plain object.
|
|
74
|
+
4. Check it against the scene cast, motion, and text rules above, marking each
|
|
75
|
+
pass, partial, or fail. Fix every fail before rendering.
|
|
76
|
+
|
|
77
|
+
## Sound
|
|
78
|
+
|
|
79
|
+
Sound effects follow `sound-design.md`: only at scene boundaries and points of
|
|
80
|
+
emphasis, at most 15 per minute, and never the same file twice within 30
|
|
81
|
+
seconds.
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# pyright: reportMissingImports=false
|
|
3
|
+
# The `app.*` imports below are MoneyPrinterTurbo's modules. MPT is never
|
|
4
|
+
# vendored into this repo; this driver only ever runs under the MPT checkout's
|
|
5
|
+
# own venv (`<MPT>/.venv/bin/python`), where `app` is importable. The repo's
|
|
6
|
+
# Python analyzer cannot see them, so this third-party-runtime directive is
|
|
7
|
+
# expected — it is not a suppression of any first-party issue.
|
|
8
|
+
"""Headless MoneyPrinterTurbo (MPT) driver — boundary-safe subprocess entrypoint.
|
|
9
|
+
|
|
10
|
+
oma-video never imports MPT. The TypeScript compositor (`internal/mpt-project.ts`
|
|
11
|
+
+ `providers/compositor.ts`) spawns *this* script with the MPT venv's python and
|
|
12
|
+
a single JSON argument describing the run. The driver loads MPT in-process here
|
|
13
|
+
(inside MPT's own venv, never inside oma's runtime) and drives the full pipeline
|
|
14
|
+
via `app.services.task.start(...)`, then copies the produced mp4 to the caller's
|
|
15
|
+
output path.
|
|
16
|
+
|
|
17
|
+
Key-free by construction (design 013 §5, backend rule 11):
|
|
18
|
+
* video_script is injected directly -> MPT's custom-script mode, NO LLM key.
|
|
19
|
+
* voice defaults to an edge-tts voice -> NO TTS key.
|
|
20
|
+
* subtitle_provider="edge" (MPT config) reuses the edge sub_maker -> no
|
|
21
|
+
faster-whisper model download.
|
|
22
|
+
* video_source defaults to "local": the driver synthesizes a few ffmpeg
|
|
23
|
+
test-pattern clips into MPT's storage/local_videos so composition needs NO
|
|
24
|
+
Pexels key. video_source="pexels" is used only when a key is provided.
|
|
25
|
+
|
|
26
|
+
Contract — stdin/argv is one JSON object, stdout's LAST line is one JSON result:
|
|
27
|
+
IN {
|
|
28
|
+
"mpt_dir": "<abs path to MPT repo>", # required
|
|
29
|
+
"script": "<narration text, one line per scene>", # required
|
|
30
|
+
"subject": "<short subject>", # optional, default "video"
|
|
31
|
+
"out_path": "<abs output mp4 path>", # required
|
|
32
|
+
"aspect": "9:16" | "16:9" | "1:1", # optional, default 9:16
|
|
33
|
+
"voice_name": "<edge voice>", # optional, default en-US
|
|
34
|
+
"video_source":"local" | "pexels", # optional, default local
|
|
35
|
+
"pexels_api_key": "<key>", # optional (pexels source)
|
|
36
|
+
"materials": ["<abs clip path>", ...], # optional, local source
|
|
37
|
+
"clip_duration": 5, # optional
|
|
38
|
+
"subtitle": true | false # optional, default true
|
|
39
|
+
}
|
|
40
|
+
OUT {"ok": true, "output": "<abs mp4>", "duration": <float>, "source": "..."}
|
|
41
|
+
| {"ok": false, "error": "<message>"}
|
|
42
|
+
|
|
43
|
+
Exit code is 0 on success, 1 on failure; the JSON result line is authoritative.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import json
|
|
49
|
+
import os
|
|
50
|
+
import shutil
|
|
51
|
+
import subprocess
|
|
52
|
+
import sys
|
|
53
|
+
import traceback
|
|
54
|
+
import uuid
|
|
55
|
+
|
|
56
|
+
# Default key-free edge-tts voice. parse_voice_name() strips the gender suffix.
|
|
57
|
+
DEFAULT_VOICE = "en-US-AvaNeural-Female"
|
|
58
|
+
# MPT's preprocess_video rejects materials below 480x480; portrait shorts need
|
|
59
|
+
# height >= width, so 1080x1920 is both safe and matches the design's frame.
|
|
60
|
+
RESOLUTIONS = {
|
|
61
|
+
"9:16": (1080, 1920),
|
|
62
|
+
"16:9": (1920, 1080),
|
|
63
|
+
"1:1": (1080, 1080),
|
|
64
|
+
}
|
|
65
|
+
# A small palette of solid colors so synthesized local clips are visually
|
|
66
|
+
# distinct (deterministic order — same input yields the same clips).
|
|
67
|
+
COLORS = ["0x1a2332", "0x2d3a4f", "0x3f5168", "0x52687f", "0x6a7f96"]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _emit(result: dict) -> None:
|
|
71
|
+
"""Print the single authoritative JSON result line and flush."""
|
|
72
|
+
sys.stdout.write(json.dumps(result) + "\n")
|
|
73
|
+
sys.stdout.flush()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _ffmpeg_bin() -> str:
|
|
77
|
+
return os.environ.get("IMAGEIO_FFMPEG_EXE") or shutil.which("ffmpeg") or "ffmpeg"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _make_test_clip(out_path: str, width: int, height: int, seconds: int,
|
|
81
|
+
color: str, label: str) -> None:
|
|
82
|
+
"""Synthesize a solid-color test clip with ffmpeg (key-free local material).
|
|
83
|
+
|
|
84
|
+
Drawn at the target resolution so MPT's >=480x480 check passes and no
|
|
85
|
+
upscaling/letterboxing is required. libx264 + yuv420p keeps the clip a
|
|
86
|
+
standard, broadly decodable mp4.
|
|
87
|
+
"""
|
|
88
|
+
ffmpeg = _ffmpeg_bin()
|
|
89
|
+
cmd = [
|
|
90
|
+
ffmpeg, "-y",
|
|
91
|
+
"-f", "lavfi",
|
|
92
|
+
"-i", f"color=c={color}:s={width}x{height}:d={seconds}:r=30",
|
|
93
|
+
"-vf", (
|
|
94
|
+
f"drawtext=text='{label}':fontcolor=white:fontsize={max(width, height)//22}"
|
|
95
|
+
":x=(w-text_w)/2:y=(h-text_h)/2"
|
|
96
|
+
),
|
|
97
|
+
"-c:v", "libx264", "-pix_fmt", "yuv420p", "-t", str(seconds),
|
|
98
|
+
out_path,
|
|
99
|
+
]
|
|
100
|
+
proc = subprocess.run(cmd, capture_output=True, text=True)
|
|
101
|
+
if proc.returncode != 0 or not os.path.isfile(out_path):
|
|
102
|
+
# Fallback without drawtext (some ffmpeg builds lack the freetype filter).
|
|
103
|
+
cmd_plain = [
|
|
104
|
+
ffmpeg, "-y",
|
|
105
|
+
"-f", "lavfi",
|
|
106
|
+
"-i", f"color=c={color}:s={width}x{height}:d={seconds}:r=30",
|
|
107
|
+
"-c:v", "libx264", "-pix_fmt", "yuv420p", "-t", str(seconds),
|
|
108
|
+
out_path,
|
|
109
|
+
]
|
|
110
|
+
proc2 = subprocess.run(cmd_plain, capture_output=True, text=True)
|
|
111
|
+
if proc2.returncode != 0 or not os.path.isfile(out_path):
|
|
112
|
+
raise RuntimeError(
|
|
113
|
+
f"ffmpeg failed to synthesize local material: "
|
|
114
|
+
f"{(proc2.stderr or proc.stderr or '').strip()[-400:]}"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _prepare_local_materials(spec: dict, width: int, height: int,
|
|
119
|
+
clip_duration: int, local_dir: str):
|
|
120
|
+
"""Resolve local material clips into a run-owned subdirectory of storage/local_videos.
|
|
121
|
+
|
|
122
|
+
MPT's preprocess_video resolves each material.url *within* storage/local_videos
|
|
123
|
+
(file_security.resolve_path_within_directory). So provided clips are copied in
|
|
124
|
+
and synthesized clips are written there. Returns a list of MaterialInfo.
|
|
125
|
+
"""
|
|
126
|
+
from app.models.schema import MaterialInfo # noqa: E402 (MPT import)
|
|
127
|
+
os.makedirs(local_dir, exist_ok=True)
|
|
128
|
+
materials = []
|
|
129
|
+
|
|
130
|
+
# MaterialInfo.url must be an ABSOLUTE path inside storage/local_videos.
|
|
131
|
+
# preprocess_video only rewrites .url for *image* materials; for video clips
|
|
132
|
+
# it returns the original url unchanged, and combine_videos then opens that
|
|
133
|
+
# url directly. A bare filename would be opened relative to cwd and fail
|
|
134
|
+
# ("'oma-synth-00.mp4' not found"). Absolute paths still pass MPT's
|
|
135
|
+
# resolve_path_within_directory guard because they live under local_videos.
|
|
136
|
+
provided = spec.get("materials") or []
|
|
137
|
+
if provided:
|
|
138
|
+
for idx, src in enumerate(provided):
|
|
139
|
+
if not os.path.isfile(src):
|
|
140
|
+
continue
|
|
141
|
+
ext = os.path.splitext(src)[1] or ".mp4"
|
|
142
|
+
dst = os.path.join(local_dir, f"oma-material-{idx:02d}{ext}")
|
|
143
|
+
shutil.copyfile(src, dst)
|
|
144
|
+
materials.append(MaterialInfo(provider="local", url=dst, duration=0))
|
|
145
|
+
if not materials:
|
|
146
|
+
# Synthesize a few deterministic test-pattern clips (key-free path).
|
|
147
|
+
count = 3
|
|
148
|
+
for idx in range(count):
|
|
149
|
+
dst = os.path.join(local_dir, f"oma-synth-{idx:02d}.mp4")
|
|
150
|
+
_make_test_clip(
|
|
151
|
+
dst, width, height, clip_duration,
|
|
152
|
+
COLORS[idx % len(COLORS)], f"scene {idx + 1}",
|
|
153
|
+
)
|
|
154
|
+
materials.append(MaterialInfo(provider="local", url=dst, duration=0))
|
|
155
|
+
return materials
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def run(spec: dict) -> dict:
|
|
159
|
+
mpt_dir = spec.get("mpt_dir")
|
|
160
|
+
if not mpt_dir or not os.path.isdir(mpt_dir):
|
|
161
|
+
return {"ok": False, "error": f"mpt_dir not found: {mpt_dir!r}"}
|
|
162
|
+
script = (spec.get("script") or "").strip()
|
|
163
|
+
if not script:
|
|
164
|
+
return {"ok": False, "error": "script is required and must be non-empty"}
|
|
165
|
+
out_path = spec.get("out_path")
|
|
166
|
+
if not out_path:
|
|
167
|
+
return {"ok": False, "error": "out_path is required"}
|
|
168
|
+
|
|
169
|
+
aspect = spec.get("aspect") or "9:16"
|
|
170
|
+
if aspect not in RESOLUTIONS:
|
|
171
|
+
aspect = "9:16"
|
|
172
|
+
width, height = RESOLUTIONS[aspect]
|
|
173
|
+
voice_name = spec.get("voice_name") or DEFAULT_VOICE
|
|
174
|
+
video_source = spec.get("video_source") or "local"
|
|
175
|
+
clip_duration = int(spec.get("clip_duration") or 5)
|
|
176
|
+
subtitle = bool(spec.get("subtitle", True))
|
|
177
|
+
subject = spec.get("subject") or "video"
|
|
178
|
+
|
|
179
|
+
# Make MPT importable. This driver runs under MPT's OWN venv python, so its
|
|
180
|
+
# third-party deps resolve; we only need MPT's package root on sys.path.
|
|
181
|
+
if mpt_dir not in sys.path:
|
|
182
|
+
sys.path.insert(0, mpt_dir)
|
|
183
|
+
|
|
184
|
+
# Pexels key (only when the pexels source is selected) — env-only, never
|
|
185
|
+
# logged. MPT reads pexels keys from its config; set it on config in-memory.
|
|
186
|
+
from app.config import config # noqa: E402
|
|
187
|
+
|
|
188
|
+
if video_source == "pexels":
|
|
189
|
+
key = spec.get("pexels_api_key") or os.environ.get("PEXELS_API_KEY")
|
|
190
|
+
if not key:
|
|
191
|
+
return {"ok": False, "error": "video_source=pexels but no PEXELS_API_KEY"}
|
|
192
|
+
config.app["pexels_api_keys"] = [key]
|
|
193
|
+
|
|
194
|
+
from app.models.schema import VideoConcatMode, VideoParams # noqa: E402
|
|
195
|
+
from app.services import task # noqa: E402
|
|
196
|
+
from app.utils import utils # noqa: E402
|
|
197
|
+
|
|
198
|
+
task_id = "oma-" + uuid.uuid4().hex[:12]
|
|
199
|
+
material_dir = None
|
|
200
|
+
try:
|
|
201
|
+
video_materials = None
|
|
202
|
+
if video_source == "local":
|
|
203
|
+
material_dir = os.path.join(
|
|
204
|
+
utils.storage_dir("local_videos", create=True), task_id
|
|
205
|
+
)
|
|
206
|
+
video_materials = _prepare_local_materials(
|
|
207
|
+
spec, width, height, clip_duration, material_dir
|
|
208
|
+
)
|
|
209
|
+
if not video_materials:
|
|
210
|
+
return {"ok": False, "error": "no local materials available"}
|
|
211
|
+
|
|
212
|
+
params = VideoParams(
|
|
213
|
+
video_subject=subject,
|
|
214
|
+
# Injecting video_script puts MPT in custom-script mode: generate_script
|
|
215
|
+
# returns it verbatim, no LLM call (backend rule 11 key-free path).
|
|
216
|
+
video_script=script,
|
|
217
|
+
# Empty terms + local source avoids generate_terms' LLM call too.
|
|
218
|
+
video_terms=[] if video_source == "local" else None,
|
|
219
|
+
video_aspect=aspect,
|
|
220
|
+
video_concat_mode=VideoConcatMode.sequential.value,
|
|
221
|
+
video_clip_duration=clip_duration,
|
|
222
|
+
video_count=1,
|
|
223
|
+
video_source=video_source,
|
|
224
|
+
video_materials=video_materials,
|
|
225
|
+
voice_name=voice_name,
|
|
226
|
+
voice_rate=1.0,
|
|
227
|
+
bgm_type="", # no background music (key-free, deterministic)
|
|
228
|
+
bgm_volume=0.0,
|
|
229
|
+
subtitle_enabled=subtitle,
|
|
230
|
+
n_threads=2,
|
|
231
|
+
paragraph_number=1,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
result = task.start(task_id=task_id, params=params, stop_at="video")
|
|
235
|
+
if not result or not result.get("videos"):
|
|
236
|
+
return {
|
|
237
|
+
"ok": False,
|
|
238
|
+
"error": "MPT task.start produced no videos (see stderr for the MPT log)",
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
final = result["videos"][0]
|
|
242
|
+
if not os.path.isfile(final):
|
|
243
|
+
return {"ok": False, "error": f"MPT reported video but file missing: {final}"}
|
|
244
|
+
|
|
245
|
+
os.makedirs(os.path.dirname(os.path.abspath(out_path)), exist_ok=True)
|
|
246
|
+
shutil.copyfile(final, out_path)
|
|
247
|
+
|
|
248
|
+
duration = 0.0
|
|
249
|
+
try:
|
|
250
|
+
duration = float(result.get("audio_duration") or 0.0)
|
|
251
|
+
except (TypeError, ValueError):
|
|
252
|
+
duration = 0.0
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
"ok": True,
|
|
256
|
+
"output": os.path.abspath(out_path),
|
|
257
|
+
"duration": duration,
|
|
258
|
+
"source": video_source,
|
|
259
|
+
}
|
|
260
|
+
finally:
|
|
261
|
+
# Each run owns its materials and task storage, including failed runs.
|
|
262
|
+
if material_dir is not None:
|
|
263
|
+
shutil.rmtree(material_dir, ignore_errors=True)
|
|
264
|
+
try:
|
|
265
|
+
shutil.rmtree(utils.task_dir(task_id), ignore_errors=True)
|
|
266
|
+
except Exception: # noqa: BLE001 (cleanup is best-effort)
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def main() -> int:
|
|
271
|
+
# Spec JSON comes from argv[1] (a path or inline JSON) or stdin.
|
|
272
|
+
raw = None
|
|
273
|
+
if len(sys.argv) > 1:
|
|
274
|
+
arg = sys.argv[1]
|
|
275
|
+
if os.path.isfile(arg):
|
|
276
|
+
with open(arg, "r", encoding="utf-8") as fh:
|
|
277
|
+
raw = fh.read()
|
|
278
|
+
else:
|
|
279
|
+
raw = arg
|
|
280
|
+
if raw is None:
|
|
281
|
+
raw = sys.stdin.read()
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
spec = json.loads(raw)
|
|
285
|
+
except Exception as exc: # noqa: BLE001
|
|
286
|
+
_emit({"ok": False, "error": f"invalid spec JSON: {exc}"})
|
|
287
|
+
return 1
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
result = run(spec)
|
|
291
|
+
except Exception as exc: # noqa: BLE001
|
|
292
|
+
traceback.print_exc(file=sys.stderr)
|
|
293
|
+
_emit({"ok": False, "error": f"{type(exc).__name__}: {exc}"})
|
|
294
|
+
return 1
|
|
295
|
+
|
|
296
|
+
_emit(result)
|
|
297
|
+
return 0 if result.get("ok") else 1
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
if __name__ == "__main__":
|
|
301
|
+
sys.exit(main())
|