@oh-story/dsh 0.1.4 → 0.1.5

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 (479) hide show
  1. package/README.md +56 -12
  2. package/lib/client.js +2692 -268
  3. package/lib/client.js.map +4 -4
  4. package/lib/drama/manifest.json +136 -101
  5. package/lib/drama/skills/short-drama/SKILL.md +61 -1
  6. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +7 -4
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +133 -8
  8. package/lib/drama/skills/short-drama/references/creator-workflow.md +38 -2
  9. package/lib/drama/skills/short-drama/references/knowhow-index.md +3 -0
  10. package/lib/drama/skills/short-drama/references/reference-roles.md +10 -7
  11. package/lib/drama/skills/short-drama/references/runtime-preflight.md +3 -0
  12. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +1002 -0
  13. package/lib/drama/skills/short-drama/scripts/project_tool.py +485 -5
  14. package/lib/drama/skills/short-drama-assets/SKILL.md +13 -3
  15. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +116 -0
  16. package/lib/drama/skills/short-drama-assets/references/identity-vs-variant.md +2 -1
  17. package/lib/drama/skills/short-drama-assets/references/prop-and-state.md +19 -0
  18. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  19. package/lib/drama/skills/short-drama-assets/scripts/asset_check.py +1 -1
  20. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +6 -2
  21. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +4 -3
  22. package/lib/drama/skills/short-drama-image-prompts/references/lookdev-frame.md +1 -1
  23. package/lib/drama/skills/short-drama-image-prompts/references/review-and-fixtures.md +1 -1
  24. package/lib/drama/skills/short-drama-image-prompts/references/stage-contract.md +3 -1
  25. package/lib/drama/skills/short-drama-image-prompts/scripts/image_prompt_check.py +8 -8
  26. package/lib/drama/skills/short-drama-image-prompts/scripts/selftest.py +4 -2
  27. package/lib/drama/skills/short-drama-produce/SKILL.md +46 -10
  28. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +65 -8
  29. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +85 -0
  30. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +27 -10
  31. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +362 -3
  32. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +627 -40
  33. package/lib/drama/skills/short-drama-produce/scripts/selftest.py +5 -4
  34. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +12 -0
  35. package/lib/drama/skills/short-drama-review/scripts/review_check.py +1 -1
  36. package/lib/drama/skills/short-drama-storyboard/SKILL.md +76 -7
  37. package/lib/drama/skills/short-drama-storyboard/assets/shot-template.jsonl +1 -1
  38. package/lib/drama/skills/short-drama-storyboard/references/keyframe-craft.md +6 -2
  39. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +19 -4
  40. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +5 -2
  41. package/lib/drama/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +16 -0
  42. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +8 -3
  43. package/lib/drama/skills/short-drama-storyboard/scripts/storyboard_check.py +1 -1
  44. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +81 -13
  45. package/lib/drama/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +6 -1
  46. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +17 -1
  47. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +21 -1
  48. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +154 -0
  49. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -6
  50. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +11 -7
  51. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +41 -0
  52. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +66 -0
  53. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +5 -2
  54. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +115 -0
  55. package/lib/drama/skills/short-drama-video-prompts/scripts/container_check.py +1 -1
  56. package/lib/drama/skills/short-drama-video-prompts/scripts/music_spec_check.py +1 -1
  57. package/lib/drama/skills/short-drama-write/SKILL.md +3 -2
  58. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +1 -1
  59. package/lib/drama/skills/short-drama-write/scripts/screenplay_index.py +2 -2
  60. package/lib/drama/skills/short-drama-write/scripts/voice_sheet_check.py +1 -1
  61. package/lib/index.js +865 -33
  62. package/lib/index.js.map +4 -4
  63. package/lib/novel-to-game/LICENSE +21 -0
  64. package/lib/novel-to-game/README.md +180 -0
  65. package/lib/novel-to-game/README_ZH.md +178 -0
  66. package/lib/novel-to-game/VERSION +1 -0
  67. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +217 -0
  68. package/lib/novel-to-game/examples/jin-ping-mei/build/app/.vercelignore +3 -0
  69. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +130 -0
  70. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  71. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  72. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  73. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  74. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  75. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  76. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  77. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  78. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  79. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  80. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  81. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  82. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  83. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  84. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  85. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  86. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  87. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  88. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  89. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  90. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  91. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  92. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  93. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  94. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  95. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  96. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  97. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  98. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  99. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  100. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  101. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  102. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  103. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  104. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  105. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  106. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  107. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  108. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +1105 -0
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +2539 -0
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +18 -0
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +147 -0
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +332 -0
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +7472 -0
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +13077 -0
  118. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +3028 -0
  119. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +32 -0
  120. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +10 -0
  121. package/lib/novel-to-game/examples/jin-ping-mei/build/app/vercel.json +23 -0
  122. package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +161 -0
  123. package/lib/novel-to-game/examples/jin-ping-mei/example.json +12 -0
  124. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +31 -0
  125. package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +99 -0
  126. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +41 -0
  127. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +129 -0
  128. package/lib/novel-to-game/manifest.json +508 -0
  129. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +73 -0
  130. package/lib/novel-to-game/skills/game-art-direction/agents/openai.yaml +4 -0
  131. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +119 -0
  132. package/lib/novel-to-game/skills/game-build/SKILL.md +84 -0
  133. package/lib/novel-to-game/skills/game-build/agents/openai.yaml +4 -0
  134. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +109 -0
  135. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +56 -0
  136. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +81 -0
  137. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +37 -0
  138. package/lib/novel-to-game/skills/game-concept/SKILL.md +72 -0
  139. package/lib/novel-to-game/skills/game-concept/agents/openai.yaml +4 -0
  140. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +94 -0
  141. package/lib/novel-to-game/skills/game-qa/SKILL.md +50 -0
  142. package/lib/novel-to-game/skills/game-qa/agents/openai.yaml +4 -0
  143. package/lib/novel-to-game/skills/game-qa/references/qa-contract.md +66 -0
  144. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +45 -0
  145. package/lib/novel-to-game/skills/game-world-design/SKILL.md +63 -0
  146. package/lib/novel-to-game/skills/game-world-design/agents/openai.yaml +4 -0
  147. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +59 -0
  148. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +53 -0
  149. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +84 -0
  150. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +56 -0
  151. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +124 -0
  152. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +77 -0
  153. package/lib/novel-to-game/skills/novel-game-analyze/agents/openai.yaml +4 -0
  154. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +65 -0
  155. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +71 -0
  156. package/lib/novel-to-game/skills/novel-to-game/agents/openai.yaml +4 -0
  157. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +42 -0
  158. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +51 -0
  159. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +65 -0
  160. package/lib/oh-story/manifest.json +415 -305
  161. package/lib/oh-story/roles/consistency-checker.md +1 -1
  162. package/lib/oh-story/roles/narrative-writer.md +32 -33
  163. package/lib/oh-story/roles/story-architect.md +16 -26
  164. package/lib/oh-story/skills/browser-cdp/SKILL.md +1 -1
  165. package/lib/oh-story/skills/story/SKILL.md +16 -9
  166. package/lib/oh-story/skills/story/VERSION +1 -1
  167. package/lib/oh-story/skills/story/references/author-memory.md +163 -0
  168. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +871 -0
  169. package/lib/oh-story/skills/story-cover/SKILL.md +1 -1
  170. package/lib/oh-story/skills/story-deslop/SKILL.md +10 -5
  171. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +2 -1
  172. package/lib/oh-story/skills/story-deslop/references/author-memory.md +163 -0
  173. package/lib/oh-story/skills/story-deslop/references/banned-words.md +6 -4
  174. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +871 -0
  175. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +64 -1
  176. package/lib/oh-story/skills/story-import/SKILL.md +19 -10
  177. package/lib/oh-story/skills/story-import/references/format-and-structure.md +9 -10
  178. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +2 -2
  179. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +1 -1
  180. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +14 -6
  181. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +251 -0
  182. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +98 -9
  183. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +246 -0
  184. package/lib/oh-story/skills/story-long-analyze/SKILL.md +4 -4
  185. package/lib/oh-story/skills/story-long-scan/SKILL.md +1 -1
  186. package/lib/oh-story/skills/story-long-write/SKILL.md +35 -21
  187. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +2 -1
  188. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +7 -3
  189. package/lib/oh-story/skills/story-long-write/references/author-memory.md +163 -0
  190. package/lib/oh-story/skills/story-long-write/references/banned-words.md +6 -4
  191. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +6 -6
  192. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  193. package/lib/oh-story/skills/story-long-write/references/genre-readers.md +1 -1
  194. package/lib/oh-story/skills/{story-setup/references/agent-references/hooks-chapter.md → story-long-write/references/long-chapter-hooks.md} +5 -5
  195. package/lib/oh-story/skills/story-long-write/references/{quality-checklist.md → long-chapter-quality.md} +2 -1
  196. package/lib/oh-story/skills/story-long-write/references/long-format.md +42 -0
  197. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-catalog.md → story-long-write/references/long-genre-catalog.md} +11 -97
  198. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-core-mechanics.md → story-long-write/references/long-genre-mechanics.md} +1 -1
  199. package/lib/oh-story/skills/{story-short-write/references/reversal-toolkit.md → story-long-write/references/long-reversal.md} +18 -32
  200. package/lib/oh-story/skills/story-long-write/references/{hooks-suspense.md → long-suspense.md} +6 -6
  201. package/lib/oh-story/skills/story-long-write/references/opening-design.md +1 -1
  202. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +1 -1
  203. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  204. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +14 -6
  205. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +32 -27
  206. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +4 -4
  207. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +2 -2
  208. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +14 -14
  209. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +54 -54
  210. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +871 -0
  211. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +64 -1
  212. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +251 -0
  213. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +303 -0
  214. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +98 -9
  215. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +246 -0
  216. package/lib/oh-story/skills/story-review/SKILL.md +26 -17
  217. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +2 -1
  218. package/lib/oh-story/skills/story-review/references/author-memory.md +163 -0
  219. package/lib/oh-story/skills/story-review/references/banned-words.md +6 -4
  220. package/lib/oh-story/skills/story-review/references/{quality-checklist.md → review-quality.md} +22 -21
  221. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +14 -6
  222. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +871 -0
  223. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +64 -1
  224. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +98 -9
  225. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +246 -0
  226. package/lib/oh-story/skills/story-setup/SKILL.md +74 -33
  227. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +72 -0
  228. package/lib/oh-story/skills/story-setup/references/agent-references/agent-reference-profiles.md +55 -0
  229. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +2 -1
  230. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +6 -4
  231. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +9 -10
  232. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  233. package/lib/oh-story/skills/story-setup/references/agent-references/genre-readers.md +1 -1
  234. package/lib/oh-story/skills/{story-short-analyze/references/hooks-chapter.md → story-setup/references/agent-references/long-chapter-hooks.md} +5 -5
  235. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +209 -0
  236. package/lib/oh-story/skills/{story-long-write/references/genre-catalog.md → story-setup/references/agent-references/long-genre-catalog.md} +11 -97
  237. package/lib/oh-story/skills/{story-long-write/references/genre-core-mechanics.md → story-setup/references/agent-references/long-genre-mechanics.md} +1 -1
  238. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +40 -0
  239. package/lib/oh-story/skills/story-setup/references/agent-references/{reversal-toolkit.md → long-reversal.md} +18 -32
  240. package/lib/oh-story/skills/{story-short-write/references/hooks-suspense.md → story-setup/references/agent-references/long-suspense.md} +6 -6
  241. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +1 -1
  242. package/lib/oh-story/skills/story-setup/references/agent-references/{emotional-methods.md → short-emotional-methods.md} +4 -4
  243. package/lib/oh-story/skills/{story-long-write/references/genre-writing-formulas.md → story-setup/references/agent-references/short-genre-formulas.md} +32 -44
  244. package/lib/oh-story/skills/story-setup/references/agent-references/short-quality.md +39 -0
  245. package/lib/oh-story/skills/{story-long-write/references/reversal-toolkit.md → story-setup/references/agent-references/short-reversal.md} +29 -23
  246. package/lib/oh-story/skills/story-setup/references/agent-references/{hooks-suspense.md → short-suspense.md} +31 -31
  247. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  248. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +55 -55
  249. package/lib/oh-story/skills/story-short-analyze/SKILL.md +28 -28
  250. package/lib/oh-story/skills/story-short-analyze/references/{character-basics.md → analysis-character-basics.md} +1 -1
  251. package/lib/oh-story/skills/story-short-analyze/references/{character-design-methods.md → analysis-character-design.md} +1 -1
  252. package/lib/oh-story/skills/story-short-analyze/references/{character-relations.md → analysis-character-relations.md} +1 -1
  253. package/lib/oh-story/skills/story-short-analyze/references/{hooks-paragraph.md → analysis-paragraph-hooks.md} +1 -1
  254. package/lib/oh-story/skills/story-short-analyze/references/{genre-readers.md → analysis-reader-profile.md} +2 -2
  255. package/lib/oh-story/skills/story-short-analyze/references/analysis-report-style.md +45 -0
  256. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-genres.md +65 -0
  257. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-hooks.md +77 -0
  258. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-mechanics.md +82 -0
  259. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-patterns.md +50 -0
  260. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-suspense.md +98 -0
  261. package/lib/oh-story/skills/story-short-analyze/references/{genre-writing-techniques.md → analysis-writing-techniques.md} +21 -19
  262. package/lib/oh-story/skills/story-short-analyze/references/deconstruction-examples.md +1 -1
  263. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +21 -28
  264. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +1 -1
  265. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +17 -17
  266. package/lib/oh-story/skills/story-short-analyze/references/{quality-checklist.md → source-story-quality.md} +28 -49
  267. package/lib/oh-story/skills/story-short-analyze/references/zhihu-style.md +2 -2
  268. package/lib/oh-story/skills/story-short-scan/SKILL.md +1 -1
  269. package/lib/oh-story/skills/story-short-write/SKILL.md +78 -55
  270. package/lib/oh-story/skills/story-short-write/references/author-memory.md +163 -0
  271. package/lib/oh-story/skills/story-short-write/references/banned-words.md +6 -4
  272. package/lib/oh-story/skills/story-short-write/references/cross-book-recall.md +3 -3
  273. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +4 -4
  274. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +3 -3
  275. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +2 -2
  276. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +3 -3
  277. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +6 -6
  278. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +3 -3
  279. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +4 -4
  280. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +7 -7
  281. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/262/231/351/233/225/350/204/221/346/264/236.md +5 -5
  282. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +4 -4
  283. package/lib/oh-story/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +3 -3
  284. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +32 -44
  285. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +20 -18
  286. package/lib/oh-story/skills/story-short-write/references/output-contract.md +1 -1
  287. package/lib/oh-story/skills/story-short-write/references/short-craft.md +18 -18
  288. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +1 -1
  289. package/lib/oh-story/skills/story-short-write/references/short-format.md +38 -8
  290. package/lib/oh-story/skills/story-short-write/references/{quality-checklist.md → short-prose-quality.md} +22 -21
  291. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +355 -0
  292. package/lib/oh-story/skills/{story-short-analyze/references/hooks-suspense.md → story-short-write/references/short-suspense.md} +31 -31
  293. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +2 -2
  294. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +31 -22
  295. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +871 -0
  296. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +64 -1
  297. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +212 -0
  298. package/lib/oh-story/skills/story-short-write/scripts/check-phase2-contract.js +363 -0
  299. package/lib/types/client/drama-production-view.d.ts +34 -0
  300. package/lib/types/client/drama-production-view.d.ts.map +1 -0
  301. package/lib/types/client/drama-production.d.ts +92 -0
  302. package/lib/types/client/drama-production.d.ts.map +1 -0
  303. package/lib/types/client/file-activity.d.ts +4 -2
  304. package/lib/types/client/file-activity.d.ts.map +1 -1
  305. package/lib/types/client/index.d.ts +1 -1
  306. package/lib/types/client/index.d.ts.map +1 -1
  307. package/lib/types/client/production-intents.d.ts +9 -0
  308. package/lib/types/client/production-intents.d.ts.map +1 -0
  309. package/lib/types/client/production-prompts.d.ts +9 -0
  310. package/lib/types/client/production-prompts.d.ts.map +1 -0
  311. package/lib/types/client/production-runtime.d.ts +58 -0
  312. package/lib/types/client/production-runtime.d.ts.map +1 -0
  313. package/lib/types/client/video-studio.d.ts +41 -0
  314. package/lib/types/client/video-studio.d.ts.map +1 -0
  315. package/lib/types/client/workbench-ui.d.ts +5 -0
  316. package/lib/types/client/workbench-ui.d.ts.map +1 -0
  317. package/lib/types/game-verification.d.ts +16 -0
  318. package/lib/types/game-verification.d.ts.map +1 -0
  319. package/lib/types/index.d.ts +3 -1
  320. package/lib/types/index.d.ts.map +1 -1
  321. package/lib/types/production-intent.d.ts +21 -0
  322. package/lib/types/production-intent.d.ts.map +1 -0
  323. package/lib/types/production-tool.d.ts +5 -0
  324. package/lib/types/production-tool.d.ts.map +1 -0
  325. package/lib/types/role-tool.d.ts.map +1 -1
  326. package/lib/types/skill-provider.d.ts +7 -0
  327. package/lib/types/skill-provider.d.ts.map +1 -1
  328. package/lib/types/video-project.d.ts +42 -0
  329. package/lib/types/video-project.d.ts.map +1 -0
  330. package/lib/types/workspace-request-trust.d.ts +6 -0
  331. package/lib/types/workspace-request-trust.d.ts.map +1 -1
  332. package/lib/types/workspace-route.d.ts +20 -0
  333. package/lib/types/workspace-route.d.ts.map +1 -1
  334. package/lib/video-recap/CHANGELOG.md +282 -0
  335. package/lib/video-recap/LICENSE +21 -0
  336. package/lib/video-recap/README.en.md +257 -0
  337. package/lib/video-recap/README.md +255 -0
  338. package/lib/video-recap/manifest.json +684 -0
  339. package/lib/video-recap/skills/video-assemble/SKILL.md +97 -0
  340. package/lib/video-recap/skills/video-assemble/references/jianying/LICENSE.duo-video +21 -0
  341. package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +15 -0
  342. package/lib/video-recap/skills/video-assemble/references/jianying/empty_draft_meta_info.json +69 -0
  343. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +84 -0
  344. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +98 -0
  345. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +204 -0
  346. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_material_video.json +83 -0
  347. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_meta_material_value.json +23 -0
  348. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_project_info.json +133 -0
  349. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_segment.json +70 -0
  350. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_text_styles.json +30 -0
  351. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_audio.json +41 -0
  352. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_text.json +109 -0
  353. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +88 -0
  354. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +345 -0
  355. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +34 -0
  356. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +305 -0
  357. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +94 -0
  358. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +205 -0
  359. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +555 -0
  360. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +261 -0
  361. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +767 -0
  362. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +111 -0
  363. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +24 -0
  364. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +109 -0
  365. package/lib/video-recap/skills/video-assemble/scripts/jianying_templates.py +37 -0
  366. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +191 -0
  367. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +75 -0
  368. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +394 -0
  369. package/lib/video-recap/skills/video-assemble/scripts/lib.py +239 -0
  370. package/lib/video-recap/skills/video-assemble/scripts/media.py +243 -0
  371. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +477 -0
  372. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +46 -0
  373. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +486 -0
  374. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +376 -0
  375. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +88 -0
  376. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +248 -0
  377. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +89 -0
  378. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +512 -0
  379. package/lib/video-recap/skills/video-cut/SKILL.md +90 -0
  380. package/lib/video-recap/skills/video-cut/scripts/cut.py +67 -0
  381. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +320 -0
  382. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +545 -0
  383. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +348 -0
  384. package/lib/video-recap/skills/video-cut/scripts/lib.py +77 -0
  385. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +341 -0
  386. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +249 -0
  387. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +745 -0
  388. package/lib/video-recap/skills/video-recap/SKILL.md +216 -0
  389. package/lib/video-recap/skills/video-recap/references/config-playbook.md +61 -0
  390. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +225 -0
  391. package/lib/video-recap/skills/video-recap/references/data-schema.md +534 -0
  392. package/lib/video-recap/skills/video-recap/references/research-guide.md +74 -0
  393. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +23 -0
  394. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +251 -0
  395. package/lib/video-recap/skills/video-recap/scripts/doctor.py +483 -0
  396. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +737 -0
  397. package/lib/video-recap/skills/video-recap/scripts/lib.py +242 -0
  398. package/lib/video-recap/skills/video-recap/scripts/materials.py +457 -0
  399. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +95 -0
  400. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +18 -0
  401. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_contract.py +9 -0
  402. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +371 -0
  403. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +201 -0
  404. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +227 -0
  405. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +362 -0
  406. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +129 -0
  407. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +338 -0
  408. package/lib/video-recap/skills/video-recap/scripts/recap.py +9 -0
  409. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +140 -0
  410. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +550 -0
  411. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +77 -0
  412. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +654 -0
  413. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +335 -0
  414. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +217 -0
  415. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +795 -0
  416. package/lib/video-recap/skills/video-script/SKILL.md +248 -0
  417. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +225 -0
  418. package/lib/video-recap/skills/video-script/references/research-guide.md +75 -0
  419. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +399 -0
  420. package/lib/video-recap/skills/video-script/scripts/agent_text.py +410 -0
  421. package/lib/video-recap/skills/video-script/scripts/brief_context.py +403 -0
  422. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +332 -0
  423. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +442 -0
  424. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +237 -0
  425. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +492 -0
  426. package/lib/video-recap/skills/video-script/scripts/lib.py +327 -0
  427. package/lib/video-recap/skills/video-script/scripts/narration.py +12 -0
  428. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +678 -0
  429. package/lib/video-recap/skills/video-script/scripts/review.py +42 -0
  430. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +262 -0
  431. package/lib/video-recap/skills/video-script/scripts/review_response.py +761 -0
  432. package/lib/video-recap/skills/video-script/scripts/review_runner.py +200 -0
  433. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +204 -0
  434. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +199 -0
  435. package/lib/video-recap/skills/video-script/scripts/validate.py +164 -0
  436. package/lib/video-recap/skills/video-understanding/SKILL.md +75 -0
  437. package/lib/video-recap/skills/video-understanding/references/data-schema.md +299 -0
  438. package/lib/video-recap/skills/video-understanding/references/prompt-templates.md +37 -0
  439. package/lib/video-recap/skills/video-understanding/references/research-guide.md +74 -0
  440. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +399 -0
  441. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +410 -0
  442. package/lib/video-recap/skills/video-understanding/scripts/asr.py +260 -0
  443. package/lib/video-recap/skills/video-understanding/scripts/brief.py +12 -0
  444. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +403 -0
  445. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +332 -0
  446. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +442 -0
  447. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +647 -0
  448. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +237 -0
  449. package/lib/video-recap/skills/video-understanding/scripts/detect.py +513 -0
  450. package/lib/video-recap/skills/video-understanding/scripts/extract.py +62 -0
  451. package/lib/video-recap/skills/video-understanding/scripts/lib.py +470 -0
  452. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +678 -0
  453. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +204 -0
  454. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +507 -0
  455. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +199 -0
  456. package/lib/video-recap/skills/video-understanding/scripts/understand.py +9 -0
  457. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +216 -0
  458. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +327 -0
  459. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +367 -0
  460. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +162 -0
  461. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +705 -0
  462. package/lib/video-recap/skills/video-voiceover/SKILL.md +81 -0
  463. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +637 -0
  464. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +109 -0
  465. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +421 -0
  466. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +113 -0
  467. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +782 -0
  468. package/package.json +55 -40
  469. package/lib/oh-story/skills/story-deslop/scripts/check-outline-copy.js +0 -241
  470. package/lib/oh-story/skills/story-long-write/references/format-and-structure.md +0 -169
  471. package/lib/oh-story/skills/story-setup/references/agent-references/genre-writing-formulas.md +0 -444
  472. package/lib/oh-story/skills/story-setup/references/agent-references/quality-checklist.md +0 -247
  473. package/lib/oh-story/skills/story-short-analyze/references/anti-ai-writing.md +0 -582
  474. package/lib/oh-story/skills/story-short-analyze/references/banned-words.md +0 -147
  475. package/lib/oh-story/skills/story-short-analyze/references/genre-catalog.md +0 -490
  476. package/lib/oh-story/skills/story-short-analyze/references/genre-core-mechanics.md +0 -425
  477. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-formulas.md +0 -453
  478. /package/lib/oh-story/skills/{story-long-write/references/hooks-chapter.md → story-setup/references/agent-references/short-chapter-hooks.md} +0 -0
  479. /package/lib/oh-story/skills/{story-long-write/references/hooks-paragraph.md → story-setup/references/agent-references/short-paragraph-hooks.md} +0 -0
@@ -0,0 +1,129 @@
1
+ """Run and persist advisory MiMo QC stages."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+
8
+ import json
9
+
10
+
11
+ from pathlib import Path
12
+
13
+ from typing import Any, Callable, Mapping, Sequence
14
+
15
+ import qc_contract
16
+
17
+ from mimo_qc_report import _stage_reports, build_report, write_report
18
+ from mimo_qc_contract import ARTIFACT_NAME, DEFAULT_STAGE
19
+
20
+ JudgeCallable = Callable[
21
+ [Mapping[str, Any]], Mapping[str, Any] | Sequence[Mapping[str, Any]]
22
+ ]
23
+
24
+ FrameSampler = Callable[..., Sequence[Mapping[str, Any]]]
25
+
26
+
27
+ def run(
28
+ work_dir: str | Path,
29
+ *,
30
+ stage: str = DEFAULT_STAGE,
31
+ fixture: Any | None = None,
32
+ dry_run: bool = False,
33
+ judge: JudgeCallable | None = None,
34
+ config: Mapping[str, Any] | None = None,
35
+ final_output: str | Path | None = None,
36
+ output: str | Path | None = None,
37
+ live: bool = False,
38
+ refresh: bool = False,
39
+ frame_sampler: FrameSampler | None = None,
40
+ api_call: Callable[..., Any] | None = None,
41
+ ) -> dict[str, Any]:
42
+ path = Path(output) if output else Path(work_dir) / ARTIFACT_NAME
43
+ existing = _stage_reports(path).get(stage)
44
+ report = build_report(
45
+ work_dir,
46
+ stage=stage,
47
+ fixture=fixture,
48
+ dry_run=dry_run,
49
+ judge=judge,
50
+ config=config,
51
+ final_output=final_output,
52
+ live=live,
53
+ refresh=refresh,
54
+ frame_sampler=frame_sampler,
55
+ existing=existing,
56
+ api_call=api_call,
57
+ )
58
+ written_path, aggregate = write_report(work_dir, report, output=output)
59
+ return {"path": str(written_path), "report": aggregate}
60
+
61
+
62
+ def clear_report(work_dir: str | Path, *, output: str | Path | None = None) -> bool:
63
+ path = Path(output) if output else Path(work_dir) / ARTIFACT_NAME
64
+ try:
65
+ path.unlink()
66
+ return True
67
+ except FileNotFoundError:
68
+ return False
69
+
70
+
71
+ def _load_fixture(path: str | Path | None) -> Any | None:
72
+ if not path:
73
+ return None
74
+ with Path(path).open("r", encoding="utf-8") as handle:
75
+ return json.load(handle)
76
+
77
+
78
+ def main(
79
+ argv: Sequence[str] | None = None,
80
+ *,
81
+ run_callable: Callable[..., dict[str, Any]] | None = None,
82
+ ) -> int:
83
+ parser = argparse.ArgumentParser(
84
+ description="Write advisory MiMo QC from local recap evidence."
85
+ )
86
+ parser.add_argument("--work-dir", required=True)
87
+ parser.add_argument(
88
+ "--stage", default=DEFAULT_STAGE, choices=sorted(qc_contract.STAGES)
89
+ )
90
+ parser.add_argument("--fixture", help="offline model-response fixture JSON")
91
+ parser.add_argument(
92
+ "--live", action="store_true", help="make one live MiMo request for this stage"
93
+ )
94
+ parser.add_argument(
95
+ "--refresh", action="store_true", help="ignore a matching stage cache"
96
+ )
97
+ parser.add_argument(
98
+ "--dry-run",
99
+ action="store_true",
100
+ help="write evidence only; never access the network",
101
+ )
102
+ parser.add_argument("--model", help="override MIMO_QC_MODEL for this call")
103
+ parser.add_argument("--final-output")
104
+ parser.add_argument("--output")
105
+ args = parser.parse_args(argv)
106
+ fixture = _load_fixture(args.fixture)
107
+ config = {"mimo_qc_model": args.model} if args.model else None
108
+ result = (run_callable or run)(
109
+ args.work_dir,
110
+ stage=args.stage,
111
+ fixture=fixture,
112
+ live=args.live and fixture is None,
113
+ refresh=args.refresh,
114
+ dry_run=args.dry_run or (not args.live and fixture is None),
115
+ config=config,
116
+ final_output=args.final_output,
117
+ output=args.output,
118
+ )
119
+ print(
120
+ json.dumps(
121
+ {
122
+ "ok": result["report"]["ok"],
123
+ "status": result["report"]["metadata"]["status"],
124
+ "path": result["path"],
125
+ },
126
+ ensure_ascii=False,
127
+ )
128
+ )
129
+ return 0
@@ -0,0 +1,338 @@
1
+ """Shared shift-left QC report contract for video-recap artifacts.
2
+
3
+ This module is intentionally standalone: it defines a small schema and local
4
+ validation helpers only. It does not call MiMo, read credentials, connect the
5
+ pipeline, or attempt automatic fixes.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import re
11
+ from pathlib import Path
12
+ from typing import Any, Mapping, Sequence
13
+ from urllib.parse import urlsplit, urlunsplit
14
+
15
+
16
+ SCHEMA_VERSION = 1
17
+
18
+ STAGES = frozenset({
19
+ "pre_cut",
20
+ "post_cut",
21
+ "pre_tts",
22
+ "post_tts",
23
+ "pre_assemble",
24
+ "post_render",
25
+ "golden",
26
+ })
27
+ SEVERITIES = frozenset({"info", "advisory", "warning", "blocker"})
28
+ CONFIDENCES = frozenset({"low", "medium", "high", "objective"})
29
+ SAMPLE_POLICIES = frozenset({"all", "deterministic", "sampled", "semantic", "aesthetic"})
30
+ ARTIFACTS = frozenset({"final_qc.json", "golden_eval.json", "mimo_qc.json", "preflight_qc.json"})
31
+
32
+ DETERMINISTIC_CATEGORIES = frozenset({
33
+ "missing_artifact",
34
+ "stale_fingerprint",
35
+ "duration",
36
+ "stream",
37
+ "subtitle",
38
+ "tts_placement",
39
+ "schema_invalid",
40
+ "placement",
41
+ })
42
+ NON_DETERMINISTIC_CATEGORIES = frozenset({"semantic", "aesthetic", "mimo_semantic", "mimo_aesthetic"})
43
+ BLOCKING_SEVERITIES = frozenset({"blocker"})
44
+ _SECRET_KEY_RE = re.compile(r"(?i)(api[_-]?key|token|secret|password|authorization|credential)")
45
+ _SECRET_VALUE_RE = re.compile(r"\b(?:sk|tp)-[A-Za-z0-9_-]{6,}\b")
46
+ _URL_SCHEMES_TO_SCRUB = frozenset({"http", "https", "ws", "wss"})
47
+ REDACTED = "<redacted>"
48
+
49
+
50
+ def _redact_url(value: str) -> str:
51
+ """Strip URL credentials, query, and fragment while preserving useful host/path."""
52
+ try:
53
+ parts = urlsplit(value)
54
+ except ValueError:
55
+ return value
56
+ if parts.scheme.lower() not in _URL_SCHEMES_TO_SCRUB or not parts.netloc:
57
+ return value
58
+ host = parts.hostname or ""
59
+ if not host:
60
+ return value
61
+ if ":" in host and not host.startswith("["):
62
+ host = f"[{host}]"
63
+ netloc = host
64
+ try:
65
+ port = parts.port
66
+ except ValueError:
67
+ port = None
68
+ if port is not None:
69
+ netloc = f"{netloc}:{port}"
70
+ return urlunsplit((parts.scheme, netloc, parts.path, "", ""))
71
+
72
+
73
+ def redact_secrets(value: Any) -> Any:
74
+ """Return a JSON-safe copy with likely credentials removed.
75
+
76
+ Centralized for all video-recap QC reports: redacts secret-looking keys,
77
+ common synthetic key/token values, and URL userinfo/query/fragment fields.
78
+ """
79
+ if isinstance(value, Mapping):
80
+ redacted: dict[str, Any] = {}
81
+ for key, item in value.items():
82
+ key_s = str(key)
83
+ redacted[key_s] = REDACTED if _SECRET_KEY_RE.search(key_s) else redact_secrets(item)
84
+ return redacted
85
+ if isinstance(value, list):
86
+ return [redact_secrets(item) for item in value]
87
+ if isinstance(value, tuple):
88
+ return [redact_secrets(item) for item in value]
89
+ if isinstance(value, Path):
90
+ return str(value)
91
+ if isinstance(value, str):
92
+ return _SECRET_VALUE_RE.sub(REDACTED, _redact_url(value))
93
+ return value
94
+
95
+
96
+ _REQUIRED_FINDING_FIELDS = frozenset({
97
+ "finding_id",
98
+ "stage",
99
+ "severity",
100
+ "blocking",
101
+ "deterministic",
102
+ "confidence",
103
+ "rule_id",
104
+ "decision_reason",
105
+ "location",
106
+ "evidence",
107
+ "sample_policy",
108
+ "model_used",
109
+ "artifact_fingerprints",
110
+ "next_action",
111
+ # Helper fields kept for current rule semantics and compatibility.
112
+ "category",
113
+ "code",
114
+ "message",
115
+ "source",
116
+ "objective_corroboration",
117
+ })
118
+ _REQUIRED_LOCATION_FIELDS = frozenset({"timecode", "source_span"})
119
+ _REQUIRED_REPORT_FIELDS = frozenset({
120
+ "schema_version",
121
+ "artifact",
122
+ "stage",
123
+ "ok",
124
+ "blocker_count",
125
+ "finding_count",
126
+ "findings",
127
+ })
128
+
129
+
130
+ class QCContractError(ValueError):
131
+ """Raised when a QC report or finding violates the shared contract."""
132
+
133
+
134
+ def artifact_fingerprint(path: str | Path) -> str:
135
+ """Return a stable sha256 hex digest for an artifact file."""
136
+ h = hashlib.sha256()
137
+ with Path(path).open("rb") as f:
138
+ for chunk in iter(lambda: f.read(1024 * 1024), b""):
139
+ h.update(chunk)
140
+ return h.hexdigest()
141
+
142
+
143
+ def build_finding(
144
+ *,
145
+ stage: str,
146
+ severity: str,
147
+ confidence: str,
148
+ sample_policy: str | Mapping[str, Any],
149
+ category: str,
150
+ code: str,
151
+ message: str,
152
+ finding_id: str | None = None,
153
+ id: str | None = None,
154
+ rule_id: str | None = None,
155
+ decision_reason: str | None = None,
156
+ model_used: str | None = None,
157
+ artifact_fingerprints: Mapping[str, Any] | None = None,
158
+ next_action: str | None = None,
159
+ deterministic: bool | None = None,
160
+ blocking: bool | None = None,
161
+ source: Mapping[str, Any] | None = None,
162
+ location: Mapping[str, Any] | None = None,
163
+ evidence: Mapping[str, Any] | None = None,
164
+ objective_corroboration: Mapping[str, Any] | None = None,
165
+ **extra: Any,
166
+ ) -> dict[str, Any]:
167
+ """Build and validate one normalized QC finding.
168
+
169
+ Deterministic objective findings may block. MiMo semantic/aesthetic findings
170
+ default to advisory and may never block. Objective checks belong in the
171
+ deterministic QC producers instead of upgrading subjective model findings.
172
+ """
173
+ if finding_id is None:
174
+ finding_id = id
175
+ if finding_id is None:
176
+ raise QCContractError("finding_id is required")
177
+ if rule_id is None:
178
+ rule_id = code
179
+ if decision_reason is None:
180
+ decision_reason = message
181
+ if model_used is None:
182
+ model_used = "local_deterministic"
183
+ if artifact_fingerprints is None:
184
+ artifact_fingerprints = {}
185
+ if next_action is None:
186
+ next_action = "manual_review"
187
+ if isinstance(sample_policy, str):
188
+ sample_policy = {"type": sample_policy}
189
+
190
+ if deterministic is None:
191
+ deterministic = category in DETERMINISTIC_CATEGORIES
192
+ if source is None:
193
+ source = {}
194
+ source = redact_secrets(source)
195
+ if location is None:
196
+ location = {"timecode": None, "source_span": None}
197
+ else:
198
+ location = {"timecode": location.get("timecode"), "source_span": location.get("source_span")}
199
+ if evidence is None:
200
+ evidence = {}
201
+ evidence = redact_secrets(evidence)
202
+ artifact_fingerprints = redact_secrets(artifact_fingerprints)
203
+ objective_corroboration = redact_secrets(objective_corroboration or {})
204
+
205
+ is_non_deterministic = (not deterministic) or category in NON_DETERMINISTIC_CATEGORIES
206
+ if is_non_deterministic and blocking is None:
207
+ blocking = False
208
+ elif blocking is None:
209
+ blocking = severity in BLOCKING_SEVERITIES
210
+
211
+ if is_non_deterministic and blocking:
212
+ raise QCContractError(f"non-deterministic blocking finding is not allowed: {category}/{code}")
213
+
214
+ finding = {
215
+ "finding_id": finding_id,
216
+ "id": finding_id,
217
+ "stage": stage,
218
+ "severity": severity,
219
+ "blocking": bool(blocking),
220
+ "deterministic": bool(deterministic),
221
+ "confidence": confidence,
222
+ "rule_id": rule_id,
223
+ "decision_reason": decision_reason,
224
+ "location": dict(location),
225
+ "evidence": dict(evidence),
226
+ "sample_policy": dict(sample_policy),
227
+ "model_used": model_used,
228
+ "artifact_fingerprints": dict(artifact_fingerprints),
229
+ "next_action": next_action,
230
+ "category": category,
231
+ "code": code,
232
+ "message": message,
233
+ "source": dict(source),
234
+ "objective_corroboration": dict(objective_corroboration),
235
+ }
236
+ finding.update(redact_secrets(extra))
237
+ _validate_finding(finding)
238
+ return finding
239
+
240
+
241
+ def build_report(
242
+ *,
243
+ artifact: str,
244
+ stage: str,
245
+ findings: Sequence[Mapping[str, Any]] | None = None,
246
+ metadata: Mapping[str, Any] | None = None,
247
+ ) -> dict[str, Any]:
248
+ """Build and validate a minimal report for a supported QC artifact."""
249
+ normalized_findings = [redact_secrets(dict(f)) for f in (findings or [])]
250
+ blocker_count = sum(1 for f in normalized_findings if f.get("blocking") is True)
251
+ report = {
252
+ "schema_version": SCHEMA_VERSION,
253
+ "artifact": artifact,
254
+ "stage": stage,
255
+ "ok": blocker_count == 0,
256
+ "blocker_count": blocker_count,
257
+ "finding_count": len(normalized_findings),
258
+ "findings": normalized_findings,
259
+ "metadata": redact_secrets(dict(metadata or {})),
260
+ }
261
+ validate_report(report)
262
+ return report
263
+
264
+
265
+ def validate_report(report: Mapping[str, Any]) -> bool:
266
+ """Validate report shape, value domains, and blocking semantics."""
267
+ if not isinstance(report, Mapping):
268
+ raise QCContractError("report must be an object")
269
+ missing = _REQUIRED_REPORT_FIELDS - set(report)
270
+ if missing:
271
+ raise QCContractError(f"report missing required fields: {sorted(missing)}")
272
+ if report["schema_version"] != SCHEMA_VERSION:
273
+ raise QCContractError(f"unsupported schema_version: {report['schema_version']!r}")
274
+ if report["artifact"] not in ARTIFACTS:
275
+ raise QCContractError(f"unsupported artifact: {report['artifact']!r}")
276
+ if report["stage"] not in STAGES:
277
+ raise QCContractError(f"unsupported stage: {report['stage']!r}")
278
+ if not isinstance(report["findings"], list):
279
+ raise QCContractError("findings must be a list")
280
+ for finding in report["findings"]:
281
+ _validate_finding(finding)
282
+ blocker_count = sum(1 for f in report["findings"] if f.get("blocking") is True)
283
+ if report["blocker_count"] != blocker_count:
284
+ raise QCContractError("blocker_count does not match findings")
285
+ if report["finding_count"] != len(report["findings"]):
286
+ raise QCContractError("finding_count does not match findings")
287
+ if report["ok"] != (blocker_count == 0):
288
+ raise QCContractError("ok must be true exactly when blocker_count is zero")
289
+ return True
290
+
291
+
292
+ def _validate_finding(finding: Mapping[str, Any]) -> None:
293
+ if not isinstance(finding, Mapping):
294
+ raise QCContractError("finding must be an object")
295
+ missing = _REQUIRED_FINDING_FIELDS - set(finding)
296
+ if missing:
297
+ raise QCContractError(f"finding missing required fields: {sorted(missing)}")
298
+ if finding["stage"] not in STAGES:
299
+ raise QCContractError(f"unsupported finding stage: {finding['stage']!r}")
300
+ if finding["severity"] not in SEVERITIES:
301
+ raise QCContractError(f"unsupported severity: {finding['severity']!r}")
302
+ if finding["confidence"] not in CONFIDENCES:
303
+ raise QCContractError(f"unsupported confidence: {finding['confidence']!r}")
304
+ sample_policy = finding["sample_policy"]
305
+ if not isinstance(sample_policy, Mapping):
306
+ raise QCContractError("sample_policy must be an object")
307
+ sample_policy_type = sample_policy.get("type")
308
+ if sample_policy_type not in SAMPLE_POLICIES:
309
+ raise QCContractError(f"unsupported sample_policy.type: {sample_policy_type!r}")
310
+ if not isinstance(finding["deterministic"], bool):
311
+ raise QCContractError("deterministic must be boolean")
312
+ if not isinstance(finding["blocking"], bool):
313
+ raise QCContractError("blocking must be boolean")
314
+ if finding["blocking"] and finding["severity"] != "blocker":
315
+ raise QCContractError("blocking findings must use severity='blocker'")
316
+ location = finding["location"]
317
+ if not isinstance(location, Mapping):
318
+ raise QCContractError("location must be an object")
319
+ missing_location = _REQUIRED_LOCATION_FIELDS - set(location)
320
+ if missing_location:
321
+ raise QCContractError(f"location missing required fields: {sorted(missing_location)}")
322
+ if finding["source"] is None or not isinstance(finding["source"], Mapping):
323
+ raise QCContractError("source must be an object")
324
+ if finding["evidence"] is None or not isinstance(finding["evidence"], Mapping):
325
+ raise QCContractError("evidence must be an object")
326
+ if finding["artifact_fingerprints"] is None or not isinstance(finding["artifact_fingerprints"], Mapping):
327
+ raise QCContractError("artifact_fingerprints must be an object")
328
+ if finding["objective_corroboration"] is None or not isinstance(finding["objective_corroboration"], Mapping):
329
+ raise QCContractError("objective_corroboration must be an object")
330
+ for field in ("finding_id", "rule_id", "decision_reason", "model_used", "next_action"):
331
+ if not isinstance(finding[field], str) or not finding[field]:
332
+ raise QCContractError(f"{field} must be a non-empty string")
333
+
334
+ is_non_deterministic = (not finding["deterministic"]) or finding["category"] in NON_DETERMINISTIC_CATEGORIES
335
+ if is_non_deterministic and finding["blocking"]:
336
+ raise QCContractError(
337
+ f"non-deterministic blocking finding is not allowed: {finding['category']}/{finding['code']}"
338
+ )
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python3
2
+ """CLI entrypoint for the self-contained video-recap orchestrator."""
3
+
4
+ from recap_runner import main
5
+
6
+ __all__ = ["main"]
7
+
8
+ if __name__ == "__main__":
9
+ main()
@@ -0,0 +1,140 @@
1
+ """Define the video-recap command-line contract."""
2
+
3
+ import argparse
4
+ import os
5
+
6
+ from recap_runtime import _env_bool
7
+
8
+ TTS_PROVIDERS = ("auto", "mimo-tts", "fish-audio")
9
+
10
+
11
+ def parse_args():
12
+ parser = argparse.ArgumentParser(
13
+ description="Full video recap orchestrator (video-* skill bundle)."
14
+ )
15
+ parser.add_argument("video", nargs="*")
16
+ parser.add_argument("--work-dir", default=None)
17
+ parser.add_argument("--context", default="")
18
+ parser.add_argument("--scene-threshold", type=float, default=None)
19
+ parser.add_argument("--style", default="纪录片")
20
+ parser.add_argument(
21
+ "--edit-mode",
22
+ default=os.environ.get("EDIT_MODE", "full"),
23
+ choices=["full", "cut", "dub"],
24
+ )
25
+ parser.add_argument(
26
+ "--target-duration", default=os.environ.get("TARGET_DURATION") or None
27
+ )
28
+ parser.add_argument(
29
+ "--allow-duration-drift",
30
+ action="store_true",
31
+ help="cut mode: accept clip duration drift from --target-duration (primary override)",
32
+ )
33
+ parser.add_argument(
34
+ "--allow-sparse-cut",
35
+ action="store_true",
36
+ help="compatibility: accept sparse cut mapping and legacy duration drift override",
37
+ )
38
+ parser.add_argument("--skip-asr", action="store_true")
39
+ parser.add_argument("--mimo-video-overview", action="store_true")
40
+ parser.add_argument(
41
+ "--mimo-qc",
42
+ default=os.environ.get("MIMO_QC", "off"),
43
+ choices=["off", "pre-assemble", "post-render", "both"],
44
+ help="optional advisory MiMo QC stage(s); never blocks the pipeline",
45
+ )
46
+ parser.add_argument(
47
+ "--mimo-qc-refresh",
48
+ action="store_true",
49
+ default=_env_bool("MIMO_QC_REFRESH", False),
50
+ help="ignore a matching MiMo QC stage cache",
51
+ )
52
+ parser.add_argument(
53
+ "--consolidate",
54
+ action=argparse.BooleanOptionalAction,
55
+ default=True,
56
+ help="build the understanding story index (Pass B); default ON, --no-consolidate to skip",
57
+ )
58
+ parser.add_argument(
59
+ "--consolidate-asr", action="store_true", help="also clean ASR (Pass A)"
60
+ )
61
+ parser.add_argument("--mimo-tts-voice", default=None, help="MiMo TTS voice")
62
+ parser.add_argument(
63
+ "--tts-provider",
64
+ default=os.environ.get("TTS_PROVIDER", "auto"),
65
+ choices=TTS_PROVIDERS,
66
+ help="voiceover provider; auto prefers configured MiMo, then Fish Audio",
67
+ )
68
+ parser.add_argument(
69
+ "--voice-ref",
70
+ default=None,
71
+ help="reference audio for cloned narration voice (mimo-v2.5-tts-voiceclone)",
72
+ )
73
+ parser.add_argument(
74
+ "--allow-partial-tts",
75
+ action="store_true",
76
+ help="allow video-voiceover to continue when some narration segments fail TTS",
77
+ )
78
+ parser.add_argument(
79
+ "--burn-subtitles",
80
+ action=argparse.BooleanOptionalAction,
81
+ default=None,
82
+ help="burn narration subtitles into the video (default on; --no-burn-subtitles to disable)",
83
+ )
84
+ parser.add_argument(
85
+ "--subtitle-y-top",
86
+ type=int,
87
+ default=None,
88
+ help="inclusive auto-rotated display-frame Y at the top of the measured subtitle band",
89
+ )
90
+ parser.add_argument(
91
+ "--subtitle-y-bot",
92
+ type=int,
93
+ default=None,
94
+ help="exclusive auto-rotated display-frame Y at the bottom of the measured subtitle band",
95
+ )
96
+ parser.add_argument(
97
+ "--review-narration",
98
+ action=argparse.BooleanOptionalAction,
99
+ default=None,
100
+ help="run advisory narration quality review before TTS (default on; fail-open)",
101
+ )
102
+ parser.add_argument(
103
+ "--require-narration-review",
104
+ action="store_true",
105
+ help="make narration review a strict pre-TTS gate (also REQUIRE_NARRATION_REVIEW=1)",
106
+ )
107
+ parser.add_argument("--output-dir", default=None)
108
+ parser.add_argument(
109
+ "--export-jianying",
110
+ action="store_true",
111
+ help="also export an OPTIONAL 剪映/JianYing draft (decoupled; never required)",
112
+ )
113
+ parser.add_argument(
114
+ "--jianying-bundle-media",
115
+ action="store_true",
116
+ help="copy media into the 剪映 draft (default on; portable to another machine)",
117
+ )
118
+ parser.add_argument(
119
+ "--jianying-no-bundle-media",
120
+ action="store_true",
121
+ help="reference media in place instead of copying it into the draft",
122
+ )
123
+ parser.add_argument(
124
+ "--material-library-dir",
125
+ default=None,
126
+ help="filesystem material library dir (or VIDEO_RECAP_MATERIAL_LIBRARY_DIR)",
127
+ )
128
+ parser.add_argument(
129
+ "--use-materials",
130
+ action=argparse.BooleanOptionalAction,
131
+ default=False,
132
+ help="restore compatible analyzed artifacts from the material library",
133
+ )
134
+ parser.add_argument(
135
+ "--save-materials",
136
+ action="store_true",
137
+ help="save analyzed JSON/MD artifacts into the material library",
138
+ )
139
+ parser.add_argument("--doctor", action="store_true")
140
+ return parser, parser.parse_args()