oh-my-agent-commandcode 0.0.0-stage → 20.38.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/README.md +35 -2
  2. package/filter-test-output.sh +52 -0
  3. package/mods/oma.ts +347 -0
  4. package/package.json +30 -4
  5. package/skills/_shared/conditional/experiment-ledger.md +28 -0
  6. package/skills/_shared/conditional/exploration-loop.md +22 -0
  7. package/skills/_shared/conditional/quality-score.md +33 -0
  8. package/skills/_shared/core/anti-ai-prose.md +191 -0
  9. package/skills/_shared/core/api-contracts/README.md +28 -0
  10. package/skills/_shared/core/api-contracts/template.md +90 -0
  11. package/skills/_shared/core/clarification-protocol.md +23 -0
  12. package/skills/_shared/core/code-intelligence.md +46 -0
  13. package/skills/_shared/core/collaboration-policy.md +28 -0
  14. package/skills/_shared/core/common-checklist.md +28 -0
  15. package/skills/_shared/core/context-budget.md +22 -0
  16. package/skills/_shared/core/context-loading.md +34 -0
  17. package/skills/_shared/core/difficulty-guide.md +26 -0
  18. package/skills/_shared/core/execution-policy.md +31 -0
  19. package/skills/_shared/core/lessons-learned.md +33 -0
  20. package/skills/_shared/core/prompt-structure.md +18 -0
  21. package/skills/_shared/core/quality-principles.md +11 -0
  22. package/skills/_shared/core/session-metrics.md +35 -0
  23. package/skills/_shared/core/skill-routing.md +39 -0
  24. package/skills/_shared/core/test-approach.md +54 -0
  25. package/skills/_shared/core/vendor-detection.md +52 -0
  26. package/skills/_shared/runtime/memory-protocol.md +118 -0
  27. package/skills/_shared/runtime/result-contract.md +64 -0
  28. package/skills/_shared/ui-interaction-quality.md +133 -0
  29. package/skills/oma-academic-writing/SKILL.md +181 -0
  30. package/skills/oma-academic-writing/resources/abstract-guide.md +51 -0
  31. package/skills/oma-academic-writing/resources/academic-verb-tiers.md +42 -0
  32. package/skills/oma-academic-writing/resources/anti-ai-checklist.md +52 -0
  33. package/skills/oma-academic-writing/resources/hedging-guide.md +132 -0
  34. package/skills/oma-academic-writing/resources/sentence-structure-reference.md +151 -0
  35. package/skills/oma-architecture/SKILL.md +203 -0
  36. package/skills/oma-architecture/resources/api-evolution.md +41 -0
  37. package/skills/oma-architecture/resources/checklist.md +42 -0
  38. package/skills/oma-architecture/resources/execution-protocol.md +115 -0
  39. package/skills/oma-architecture/resources/methodology-selection.md +92 -0
  40. package/skills/oma-architecture/resources/migration-patterns.md +28 -0
  41. package/skills/oma-architecture/resources/output-templates.md +111 -0
  42. package/skills/oma-architecture/resources/stakeholder-synthesis.md +80 -0
  43. package/skills/oma-backend/SKILL.md +202 -0
  44. package/skills/oma-backend/resources/checklist.md +45 -0
  45. package/skills/oma-backend/resources/error-playbook.md +97 -0
  46. package/skills/oma-backend/resources/execution-protocol.md +39 -0
  47. package/skills/oma-backend/resources/orm-reference.md +61 -0
  48. package/skills/oma-backend/variants/node/api-template.ts +315 -0
  49. package/skills/oma-backend/variants/node/snippets.md +475 -0
  50. package/skills/oma-backend/variants/node/stack.yaml +21 -0
  51. package/skills/oma-backend/variants/node/tech-stack.md +33 -0
  52. package/skills/oma-backend/variants/python/api-template.py +378 -0
  53. package/skills/oma-backend/variants/python/snippets.md +199 -0
  54. package/skills/oma-backend/variants/python/stack.yaml +18 -0
  55. package/skills/oma-backend/variants/python/tech-stack.md +45 -0
  56. package/skills/oma-backend/variants/rust/api-template.rs +635 -0
  57. package/skills/oma-backend/variants/rust/snippets.md +471 -0
  58. package/skills/oma-backend/variants/rust/stack.yaml +20 -0
  59. package/skills/oma-backend/variants/rust/tech-stack.md +36 -0
  60. package/skills/oma-backend/variants/stack.schema.json +73 -0
  61. package/skills/oma-brainstorm/SKILL.md +166 -0
  62. package/skills/oma-brainstorm/resources/triz-lite.md +117 -0
  63. package/skills/oma-coordination/SKILL.md +171 -0
  64. package/skills/oma-db/SKILL.md +201 -0
  65. package/skills/oma-db/resources/anti-patterns.md +177 -0
  66. package/skills/oma-db/resources/checklist.md +67 -0
  67. package/skills/oma-db/resources/document-templates.md +107 -0
  68. package/skills/oma-db/resources/error-playbook.md +50 -0
  69. package/skills/oma-db/resources/examples.md +170 -0
  70. package/skills/oma-db/resources/execution-protocol.md +127 -0
  71. package/skills/oma-db/resources/iso-controls.md +98 -0
  72. package/skills/oma-db/resources/migration-playbook.md +93 -0
  73. package/skills/oma-db/resources/query-tuning.md +57 -0
  74. package/skills/oma-db/resources/vector-db.md +159 -0
  75. package/skills/oma-debug/SKILL.md +146 -0
  76. package/skills/oma-debug/resources/bug-report-template.md +327 -0
  77. package/skills/oma-debug/resources/checklist.md +31 -0
  78. package/skills/oma-debug/resources/common-patterns.md +734 -0
  79. package/skills/oma-debug/resources/debugging-checklist.md +364 -0
  80. package/skills/oma-debug/resources/error-playbook.md +94 -0
  81. package/skills/oma-debug/resources/execution-protocol.md +40 -0
  82. package/skills/oma-design/SKILL.md +135 -0
  83. package/skills/oma-design/resources/asset-strategy.md +34 -0
  84. package/skills/oma-design/resources/checklist.md +63 -0
  85. package/skills/oma-design/resources/data-grid.md +48 -0
  86. package/skills/oma-design/resources/design-md-spec.md +107 -0
  87. package/skills/oma-design/resources/design-tokens.md +53 -0
  88. package/skills/oma-design/resources/error-playbook.md +18 -0
  89. package/skills/oma-design/resources/execution-protocol.md +95 -0
  90. package/skills/oma-design/resources/external-design-skills.md +130 -0
  91. package/skills/oma-design/resources/getdesign-fetcher.md +104 -0
  92. package/skills/oma-design/resources/product-interface-review.md +59 -0
  93. package/skills/oma-design/resources/redesign-protocol.md +52 -0
  94. package/skills/oma-design/resources/stitch-integration.md +244 -0
  95. package/skills/oma-dev-workflow/SKILL.md +375 -0
  96. package/skills/oma-dev-workflow/resources/affected-checks.py +58 -0
  97. package/skills/oma-dev-workflow/resources/api-workflows.md +58 -0
  98. package/skills/oma-dev-workflow/resources/database-patterns.md +107 -0
  99. package/skills/oma-dev-workflow/resources/i18n-patterns.md +63 -0
  100. package/skills/oma-dev-workflow/resources/release-coordination.md +240 -0
  101. package/skills/oma-dev-workflow/resources/tooling-profiles.md +217 -0
  102. package/skills/oma-dev-workflow/resources/troubleshooting.md +78 -0
  103. package/skills/oma-dev-workflow/resources/validation-pipeline.md +246 -0
  104. package/skills/oma-docs/SKILL.md +77 -0
  105. package/skills/oma-docs/resources/commands.md +76 -0
  106. package/skills/oma-explanation/SKILL.md +212 -0
  107. package/skills/oma-explanation/resources/document-structure.md +115 -0
  108. package/skills/oma-explanation/resources/draft-format.md +199 -0
  109. package/skills/oma-explanation/resources/html-contract.md +102 -0
  110. package/skills/oma-frontend/SKILL.md +227 -0
  111. package/skills/oma-frontend/resources/angular-rules.md +110 -0
  112. package/skills/oma-frontend/resources/checklist.md +76 -0
  113. package/skills/oma-frontend/resources/error-playbook.md +125 -0
  114. package/skills/oma-frontend/resources/execution-protocol.md +46 -0
  115. package/skills/oma-frontend/resources/snippets.md +354 -0
  116. package/skills/oma-frontend/resources/specialized-visual-implementation.md +193 -0
  117. package/skills/oma-frontend/resources/tailwind-rules.md +83 -0
  118. package/skills/oma-frontend/resources/tech-stack.md +156 -0
  119. package/skills/oma-hwp/SKILL.md +166 -0
  120. package/skills/oma-hwp/config/hwp-config.yaml +35 -0
  121. package/skills/oma-hwp/resources/balanced-tables.ts +39 -0
  122. package/skills/oma-hwp/resources/execution-protocol.md +145 -0
  123. package/skills/oma-hwp/resources/flatten-tables.ts +101 -0
  124. package/skills/oma-hwp/resources/package.json +13 -0
  125. package/skills/oma-hwp/resources/troubleshooting.md +70 -0
  126. package/skills/oma-hwp/resources/tsconfig.json +10 -0
  127. package/skills/oma-hwp/resources/turndown-plugin-gfm.d.ts +9 -0
  128. package/skills/oma-image/SKILL.md +142 -0
  129. package/skills/oma-image/config/image-config.yaml +70 -0
  130. package/skills/oma-image/resources/checklist.md +18 -0
  131. package/skills/oma-image/resources/execution-protocol.md +117 -0
  132. package/skills/oma-image/resources/invocation.md +105 -0
  133. package/skills/oma-image/resources/prompt-tips.md +71 -0
  134. package/skills/oma-image/resources/vendor-matrix.md +90 -0
  135. package/skills/oma-market/SKILL.md +142 -0
  136. package/skills/oma-market/resources/checklist.md +12 -0
  137. package/skills/oma-market/resources/error-playbook.md +12 -0
  138. package/skills/oma-market/resources/execution-protocol.md +60 -0
  139. package/skills/oma-market/resources/frameworks/pestel.md +101 -0
  140. package/skills/oma-market/resources/frameworks/porters-5f.md +90 -0
  141. package/skills/oma-market/resources/frameworks/swot.md +61 -0
  142. package/skills/oma-market/resources/intent-rules.md +35 -0
  143. package/skills/oma-market/resources/output-laws.md +53 -0
  144. package/skills/oma-mobile/SKILL.md +168 -0
  145. package/skills/oma-mobile/resources/checklist.md +77 -0
  146. package/skills/oma-mobile/resources/error-playbook.md +198 -0
  147. package/skills/oma-mobile/resources/execution-protocol.md +47 -0
  148. package/skills/oma-mobile/resources/screen-template.dart +331 -0
  149. package/skills/oma-mobile/resources/screen-template.swift +239 -0
  150. package/skills/oma-mobile/resources/screen-template.tsx +179 -0
  151. package/skills/oma-mobile/resources/tech-stack.md +69 -0
  152. package/skills/oma-mobile/variants/README.md +45 -0
  153. package/skills/oma-mobile/variants/flutter/api-template.dart +461 -0
  154. package/skills/oma-mobile/variants/flutter/snippets.md +1353 -0
  155. package/skills/oma-mobile/variants/flutter/stack.yaml +21 -0
  156. package/skills/oma-mobile/variants/flutter/tech-stack.md +201 -0
  157. package/skills/oma-mobile/variants/kotlin-android/api-template.kt +182 -0
  158. package/skills/oma-mobile/variants/kotlin-android/snippets.md +871 -0
  159. package/skills/oma-mobile/variants/kotlin-android/stack.yaml +32 -0
  160. package/skills/oma-mobile/variants/kotlin-android/tech-stack.md +159 -0
  161. package/skills/oma-mobile/variants/react-native/api-template.ts +531 -0
  162. package/skills/oma-mobile/variants/react-native/snippets.md +1014 -0
  163. package/skills/oma-mobile/variants/react-native/stack.yaml +22 -0
  164. package/skills/oma-mobile/variants/react-native/tech-stack.md +192 -0
  165. package/skills/oma-mobile/variants/stack.schema.json +68 -0
  166. package/skills/oma-mobile/variants/swift-ios/api-template.swift +165 -0
  167. package/skills/oma-mobile/variants/swift-ios/snippets.md +1028 -0
  168. package/skills/oma-mobile/variants/swift-ios/stack.yaml +25 -0
  169. package/skills/oma-mobile/variants/swift-ios/tech-stack.md +174 -0
  170. package/skills/oma-observability/SKILL.md +225 -0
  171. package/skills/oma-observability/resources/anti-patterns.md +555 -0
  172. package/skills/oma-observability/resources/boundaries/cross-application.md +348 -0
  173. package/skills/oma-observability/resources/boundaries/multi-tenant.md +290 -0
  174. package/skills/oma-observability/resources/boundaries/release.md +372 -0
  175. package/skills/oma-observability/resources/boundaries/slo.md +225 -0
  176. package/skills/oma-observability/resources/checklist.md +123 -0
  177. package/skills/oma-observability/resources/examples.md +448 -0
  178. package/skills/oma-observability/resources/execution-protocol.md +166 -0
  179. package/skills/oma-observability/resources/incident-forensics.md +348 -0
  180. package/skills/oma-observability/resources/intent-rules.md +307 -0
  181. package/skills/oma-observability/resources/layers/L3-network.md +281 -0
  182. package/skills/oma-observability/resources/layers/L4-transport.md +299 -0
  183. package/skills/oma-observability/resources/layers/L7-application/crash-analytics.md +359 -0
  184. package/skills/oma-observability/resources/layers/L7-application/mobile-rum.md +278 -0
  185. package/skills/oma-observability/resources/layers/L7-application/waf.md +246 -0
  186. package/skills/oma-observability/resources/layers/L7-application/web-rum.md +350 -0
  187. package/skills/oma-observability/resources/layers/mesh.md +368 -0
  188. package/skills/oma-observability/resources/matrix.md +177 -0
  189. package/skills/oma-observability/resources/meta-observability.md +605 -0
  190. package/skills/oma-observability/resources/observability-as-code.md +666 -0
  191. package/skills/oma-observability/resources/signals/audit.md +310 -0
  192. package/skills/oma-observability/resources/signals/cost.md +303 -0
  193. package/skills/oma-observability/resources/signals/logs.md +263 -0
  194. package/skills/oma-observability/resources/signals/metrics.md +478 -0
  195. package/skills/oma-observability/resources/signals/privacy.md +351 -0
  196. package/skills/oma-observability/resources/signals/profiles.md +245 -0
  197. package/skills/oma-observability/resources/signals/traces.md +360 -0
  198. package/skills/oma-observability/resources/standards.md +214 -0
  199. package/skills/oma-observability/resources/transport/collector-topology.md +308 -0
  200. package/skills/oma-observability/resources/transport/otlp-grpc-vs-http.md +256 -0
  201. package/skills/oma-observability/resources/transport/sampling-recipes.md +297 -0
  202. package/skills/oma-observability/resources/transport/udp-statsd-mtu.md +167 -0
  203. package/skills/oma-observability/resources/vendor-categories.md +352 -0
  204. package/skills/oma-orchestration/SKILL.md +302 -0
  205. package/skills/oma-orchestration/config/cli-config.yaml +92 -0
  206. package/skills/oma-orchestration/resources/memory-schema.md +232 -0
  207. package/skills/oma-orchestration/resources/subagent-prompt-template.md +30 -0
  208. package/skills/oma-orchestration/scripts/parallel-run.sh +6 -0
  209. package/skills/oma-orchestration/scripts/spawn-agent.sh +5 -0
  210. package/skills/oma-orchestration/scripts/verify.sh +20 -0
  211. package/skills/oma-orchestration/templates/backend-task.md +19 -0
  212. package/skills/oma-orchestration/templates/debug-task.md +16 -0
  213. package/skills/oma-orchestration/templates/frontend-task.md +18 -0
  214. package/skills/oma-orchestration/templates/mobile-task.md +18 -0
  215. package/skills/oma-orchestration/templates/qa-task.md +16 -0
  216. package/skills/oma-orchestration/templates/tasks-example.yaml +15 -0
  217. package/skills/oma-pdf/SKILL.md +162 -0
  218. package/skills/oma-pdf/config/pdf-config.yaml +27 -0
  219. package/skills/oma-pdf/resources/execution-protocol.md +127 -0
  220. package/skills/oma-pm/SKILL.md +157 -0
  221. package/skills/oma-pm/resources/error-playbook.md +75 -0
  222. package/skills/oma-pm/resources/examples.md +439 -0
  223. package/skills/oma-pm/resources/execution-protocol.md +62 -0
  224. package/skills/oma-pm/resources/iso-planning.md +110 -0
  225. package/skills/oma-pm/resources/plan-phase-protocol.md +66 -0
  226. package/skills/oma-pm/resources/task-template.json +86 -0
  227. package/skills/oma-qa/SKILL.md +141 -0
  228. package/skills/oma-qa/resources/checklist.md +339 -0
  229. package/skills/oma-qa/resources/error-playbook.md +98 -0
  230. package/skills/oma-qa/resources/execution-protocol.md +169 -0
  231. package/skills/oma-qa/resources/iso-quality.md +82 -0
  232. package/skills/oma-qa/resources/verify-ship-protocol.md +116 -0
  233. package/skills/oma-recap/SKILL.md +245 -0
  234. package/skills/oma-recap/resources/output-formats.md +83 -0
  235. package/skills/oma-refactor/SKILL.md +184 -0
  236. package/skills/oma-refactor/resources/definition.md +108 -0
  237. package/skills/oma-refactor/resources/governance.md +33 -0
  238. package/skills/oma-refactor/resources/hotspots.py +44 -0
  239. package/skills/oma-refactor/resources/measurement.md +93 -0
  240. package/skills/oma-scholar/SKILL.md +276 -0
  241. package/skills/oma-scholar/config/scholar-config.yaml +79 -0
  242. package/skills/oma-scholar/resources/api-endpoints.md +174 -0
  243. package/skills/oma-scholar/resources/checklist.md +92 -0
  244. package/skills/oma-scholar/resources/execution-protocol.md +224 -0
  245. package/skills/oma-scholar/resources/fallback-providers.md +143 -0
  246. package/skills/oma-scholar/resources/setup-openalex.md +85 -0
  247. package/skills/oma-scholar/resources/sidecar-spec.md +341 -0
  248. package/skills/oma-scholar/resources/upstream-spec-cache.md +972 -0
  249. package/skills/oma-scm/SKILL.md +103 -0
  250. package/skills/oma-scm/resources/cm-operations.md +56 -0
  251. package/skills/oma-scm/resources/codeowners-playbook.md +45 -0
  252. package/skills/oma-scm/resources/conventional-commits.md +108 -0
  253. package/skills/oma-scm/resources/merge-risk.md +76 -0
  254. package/skills/oma-scm/resources/onboarding-risk-signals.md +71 -0
  255. package/skills/oma-search/SKILL.md +125 -0
  256. package/skills/oma-search/resources/checklist.md +36 -0
  257. package/skills/oma-search/resources/error-playbook.md +66 -0
  258. package/skills/oma-search/resources/examples.md +80 -0
  259. package/skills/oma-search/resources/execution-protocol.md +86 -0
  260. package/skills/oma-search/resources/intent-rules.md +47 -0
  261. package/skills/oma-search/resources/trust-registry.md +53 -0
  262. package/skills/oma-security/SKILL.md +172 -0
  263. package/skills/oma-security/resources/artex.md +108 -0
  264. package/skills/oma-security/resources/ci.md +134 -0
  265. package/skills/oma-security/resources/cisco-source.md +108 -0
  266. package/skills/oma-security/resources/deepsec-config.md +45 -0
  267. package/skills/oma-security/resources/deepsec-matchers.md +35 -0
  268. package/skills/oma-security/resources/deepsec-scanning.md +45 -0
  269. package/skills/oma-security/resources/deepsec-setup.md +41 -0
  270. package/skills/oma-security/resources/findings-contract.md +408 -0
  271. package/skills/oma-security/resources/mcp-scanning.md +122 -0
  272. package/skills/oma-security/resources/skill-scanning.md +106 -0
  273. package/skills/oma-security/resources/validation.md +71 -0
  274. package/skills/oma-skill-creation/SKILL.md +84 -0
  275. package/skills/oma-skill-creation/resources/prompt-evaluation.md +36 -0
  276. package/skills/oma-skill-creation/resources/ssl-lite-template.md +108 -0
  277. package/skills/oma-skill-creation/resources/validation-checklist.md +101 -0
  278. package/skills/oma-slide/SKILL.md +277 -0
  279. package/skills/oma-slide/resources/animation-patterns.md +245 -0
  280. package/skills/oma-slide/resources/assets/deck-stage.js +514 -0
  281. package/skills/oma-slide/resources/assets/viewport-base.css +257 -0
  282. package/skills/oma-slide/resources/canva-integration.md +203 -0
  283. package/skills/oma-slide/resources/checklist.md +46 -0
  284. package/skills/oma-slide/resources/design-doctrine.md +260 -0
  285. package/skills/oma-slide/resources/fixed-stage.md +232 -0
  286. package/skills/oma-slide/resources/generation-protocol.md +393 -0
  287. package/skills/oma-slide/resources/native-pptx.md +84 -0
  288. package/skills/oma-slide/resources/selection-index.json +461 -0
  289. package/skills/oma-slide/resources/style-presets.LICENSE +10 -0
  290. package/skills/oma-slide/resources/style-presets.md +520 -0
  291. package/skills/oma-tf-infra/SKILL.md +201 -0
  292. package/skills/oma-tf-infra/resources/checklist.md +53 -0
  293. package/skills/oma-tf-infra/resources/cost-optimization.md +574 -0
  294. package/skills/oma-tf-infra/resources/error-playbook.md +68 -0
  295. package/skills/oma-tf-infra/resources/examples.md +38 -0
  296. package/skills/oma-tf-infra/resources/execution-protocol.md +129 -0
  297. package/skills/oma-tf-infra/resources/iso-42001-infra.md +229 -0
  298. package/skills/oma-tf-infra/resources/multi-cloud-examples.md +241 -0
  299. package/skills/oma-tf-infra/resources/policy-testing-examples.md +332 -0
  300. package/skills/oma-translation/SKILL.md +229 -0
  301. package/skills/oma-translation/resources/anti-ai-patterns.md +104 -0
  302. package/skills/oma-translation/resources/lang/_template.md +73 -0
  303. package/skills/oma-translation/resources/lang/en.md +123 -0
  304. package/skills/oma-translation/resources/lang/ja.md +274 -0
  305. package/skills/oma-translation/resources/lang/ko.md +228 -0
  306. package/skills/oma-translation/resources/lang/zh.md +285 -0
  307. package/skills/oma-translation/resources/style-skills.md +104 -0
  308. package/skills/oma-translation/resources/translation-rubric.md +126 -0
  309. package/skills/oma-video/SKILL.md +229 -0
  310. package/skills/oma-video/config/video-config.yaml +60 -0
  311. package/skills/oma-video/resources/checklist.md +26 -0
  312. package/skills/oma-video/resources/execution-protocol.md +122 -0
  313. package/skills/oma-video/resources/existing-media.md +185 -0
  314. package/skills/oma-video/resources/hyperframes-authoring/README.md +44 -0
  315. package/skills/oma-video/resources/hyperframes-authoring/demo.md +31 -0
  316. package/skills/oma-video/resources/hyperframes-authoring/explainer.md +10 -0
  317. package/skills/oma-video/resources/hyperframes-authoring/shorts.md +10 -0
  318. package/skills/oma-video/resources/motion-principles.md +81 -0
  319. package/skills/oma-video/resources/mpt/driver.py +301 -0
  320. package/skills/oma-video/resources/prompt-tips.md +75 -0
  321. package/skills/oma-video/resources/script-schema.md +81 -0
  322. package/skills/oma-video/resources/sound-design.md +127 -0
  323. package/skills/oma-video/resources/strudel/package.json +11 -0
  324. package/skills/oma-video/resources/strudel/page.html +87 -0
  325. package/skills/oma-video/resources/strudel/render.mjs +295 -0
  326. package/skills/oma-video/resources/vendor-matrix.md +124 -0
  327. package/skills/oma-voice/SKILL.md +249 -0
  328. package/skills/oma-voice/config/voice-config.yaml +30 -0
  329. package/skills/oma-voice/resources/audio-processing.md +119 -0
  330. package/skills/oma-voice/resources/checklist.md +47 -0
  331. package/skills/oma-voice/resources/execution-protocol.md +215 -0
  332. package/skills/oma-voice/resources/prompt-tips.md +43 -0
  333. package/skills/oma-voice/resources/voice-matrix.md +51 -0
  334. 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())