@oh-story/dsh 0.1.3 → 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 (524) hide show
  1. package/README.md +58 -12
  2. package/lib/client.js +2970 -537
  3. package/lib/client.js.map +4 -4
  4. package/lib/drama/manifest.json +218 -178
  5. package/lib/drama/skills/short-drama/SKILL.md +110 -117
  6. package/lib/drama/skills/short-drama/references/audience-reveal.md +14 -67
  7. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +26 -77
  8. package/lib/drama/skills/short-drama/references/creator-documents.md +204 -0
  9. package/lib/drama/skills/short-drama/references/creator-workflow.md +73 -63
  10. package/lib/drama/skills/short-drama/references/knowhow-index.md +5 -0
  11. package/lib/drama/skills/short-drama/references/look-development.md +2 -3
  12. package/lib/drama/skills/short-drama/references/pickup-and-alternate.md +14 -48
  13. package/lib/drama/skills/short-drama/references/production-form-profiles.md +1 -1
  14. package/lib/drama/skills/short-drama/references/reference-roles.md +32 -135
  15. package/lib/drama/skills/short-drama/references/runtime-preflight.md +16 -19
  16. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +1002 -0
  17. package/lib/drama/skills/short-drama/scripts/project_tool.py +485 -5
  18. package/lib/drama/skills/short-drama-assets/SKILL.md +59 -170
  19. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +18 -25
  20. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +13 -15
  21. package/lib/drama/skills/short-drama-assets/references/continuity-delta.md +11 -14
  22. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +116 -0
  23. package/lib/drama/skills/short-drama-assets/references/identity-vs-variant.md +6 -12
  24. package/lib/drama/skills/short-drama-assets/references/occurrence-extraction.md +8 -11
  25. package/lib/drama/skills/short-drama-assets/references/prop-and-state.md +21 -2
  26. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +12 -105
  27. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +12 -32
  28. package/lib/drama/skills/short-drama-assets/scripts/asset_check.py +1 -1
  29. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +45 -155
  30. package/lib/drama/skills/short-drama-image-prompts/references/character-and-look.md +12 -13
  31. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +12 -13
  32. package/lib/drama/skills/short-drama-image-prompts/references/edit-and-revision.md +39 -51
  33. package/lib/drama/skills/short-drama-image-prompts/references/location-plate.md +3 -4
  34. package/lib/drama/skills/short-drama-image-prompts/references/look-and-state-variant.md +15 -14
  35. package/lib/drama/skills/short-drama-image-prompts/references/lookdev-frame.md +11 -15
  36. package/lib/drama/skills/short-drama-image-prompts/references/production-sheet-recipes.md +1 -1
  37. package/lib/drama/skills/short-drama-image-prompts/references/prop-plate.md +4 -7
  38. package/lib/drama/skills/short-drama-image-prompts/references/review-and-fixtures.md +23 -23
  39. package/lib/drama/skills/short-drama-image-prompts/references/stage-contract.md +8 -65
  40. package/lib/drama/skills/short-drama-image-prompts/scripts/image_prompt_check.py +8 -8
  41. package/lib/drama/skills/short-drama-image-prompts/scripts/selftest.py +4 -2
  42. package/lib/drama/skills/short-drama-produce/SKILL.md +67 -15
  43. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +65 -8
  44. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +85 -0
  45. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +27 -10
  46. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +362 -3
  47. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +627 -40
  48. package/lib/drama/skills/short-drama-produce/scripts/selftest.py +5 -4
  49. package/lib/drama/skills/short-drama-review/SKILL.md +61 -123
  50. package/lib/drama/skills/short-drama-review/references/anti-template-repair.md +10 -15
  51. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +15 -16
  52. package/lib/drama/skills/short-drama-review/references/project-calibration.md +7 -9
  53. package/lib/drama/skills/short-drama-review/references/review-method.md +8 -9
  54. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +9 -13
  55. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +104 -4
  56. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +27 -19
  57. package/lib/drama/skills/short-drama-review/references/stage-contract.md +4 -63
  58. package/lib/drama/skills/short-drama-review/scripts/review_check.py +1 -1
  59. package/lib/drama/skills/short-drama-storyboard/SKILL.md +116 -238
  60. package/lib/drama/skills/short-drama-storyboard/assets/shot-template.jsonl +1 -1
  61. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +9 -34
  62. package/lib/drama/skills/short-drama-storyboard/references/comic-keyframe-lexicon.md +156 -0
  63. package/lib/drama/skills/short-drama-storyboard/references/coverage-audition.md +5 -10
  64. package/lib/drama/skills/short-drama-storyboard/references/keyframe-craft.md +12 -7
  65. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +19 -4
  66. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +27 -62
  67. package/lib/drama/skills/short-drama-storyboard/references/scene-visual-plan.md +16 -25
  68. package/lib/drama/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +21 -23
  69. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +8 -10
  70. package/lib/drama/skills/short-drama-storyboard/references/shot-revision-identity.md +9 -13
  71. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +16 -71
  72. package/lib/drama/skills/short-drama-storyboard/scripts/storyboard_check.py +1 -1
  73. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +123 -230
  74. package/lib/drama/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +6 -1
  75. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +17 -1
  76. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +10 -8
  77. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +24 -6
  78. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +2 -2
  79. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +154 -0
  80. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +35 -27
  81. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +8 -11
  82. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +13 -10
  83. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +34 -63
  84. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +41 -0
  85. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +66 -0
  86. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +17 -73
  87. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +115 -0
  88. package/lib/drama/skills/short-drama-video-prompts/scripts/container_check.py +1 -1
  89. package/lib/drama/skills/short-drama-video-prompts/scripts/music_spec_check.py +1 -1
  90. package/lib/drama/skills/short-drama-write/SKILL.md +74 -268
  91. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +7 -0
  92. package/lib/drama/skills/short-drama-write/references/scene-handoff-capsule.md +17 -43
  93. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +15 -15
  94. package/lib/drama/skills/short-drama-write/references/script-craft.md +208 -21
  95. package/lib/drama/skills/short-drama-write/references/stage-contract.md +11 -70
  96. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +1 -1
  97. package/lib/drama/skills/short-drama-write/scripts/screenplay_index.py +2 -2
  98. package/lib/drama/skills/short-drama-write/scripts/voice_sheet_check.py +1 -1
  99. package/lib/index.js +971 -46
  100. package/lib/index.js.map +4 -4
  101. package/lib/novel-to-game/LICENSE +21 -0
  102. package/lib/novel-to-game/README.md +180 -0
  103. package/lib/novel-to-game/README_ZH.md +178 -0
  104. package/lib/novel-to-game/VERSION +1 -0
  105. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +217 -0
  106. package/lib/novel-to-game/examples/jin-ping-mei/build/app/.vercelignore +3 -0
  107. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +130 -0
  108. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  118. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  119. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  120. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  121. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  122. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  123. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  124. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  125. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  126. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  127. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  128. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  129. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  130. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  131. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  132. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  133. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  134. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  135. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  136. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  137. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  138. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  139. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  140. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  141. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  142. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  143. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  144. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  145. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  146. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  147. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  148. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  149. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +1105 -0
  150. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +2539 -0
  151. package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +18 -0
  152. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +147 -0
  153. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +332 -0
  154. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +7472 -0
  155. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +13077 -0
  156. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +3028 -0
  157. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +32 -0
  158. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +10 -0
  159. package/lib/novel-to-game/examples/jin-ping-mei/build/app/vercel.json +23 -0
  160. package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +161 -0
  161. package/lib/novel-to-game/examples/jin-ping-mei/example.json +12 -0
  162. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +31 -0
  163. package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +99 -0
  164. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +41 -0
  165. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +129 -0
  166. package/lib/novel-to-game/manifest.json +508 -0
  167. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +73 -0
  168. package/lib/novel-to-game/skills/game-art-direction/agents/openai.yaml +4 -0
  169. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +119 -0
  170. package/lib/novel-to-game/skills/game-build/SKILL.md +84 -0
  171. package/lib/novel-to-game/skills/game-build/agents/openai.yaml +4 -0
  172. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +109 -0
  173. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +56 -0
  174. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +81 -0
  175. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +37 -0
  176. package/lib/novel-to-game/skills/game-concept/SKILL.md +72 -0
  177. package/lib/novel-to-game/skills/game-concept/agents/openai.yaml +4 -0
  178. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +94 -0
  179. package/lib/novel-to-game/skills/game-qa/SKILL.md +50 -0
  180. package/lib/novel-to-game/skills/game-qa/agents/openai.yaml +4 -0
  181. package/lib/novel-to-game/skills/game-qa/references/qa-contract.md +66 -0
  182. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +45 -0
  183. package/lib/novel-to-game/skills/game-world-design/SKILL.md +63 -0
  184. package/lib/novel-to-game/skills/game-world-design/agents/openai.yaml +4 -0
  185. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +59 -0
  186. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +53 -0
  187. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +84 -0
  188. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +56 -0
  189. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +124 -0
  190. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +77 -0
  191. package/lib/novel-to-game/skills/novel-game-analyze/agents/openai.yaml +4 -0
  192. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +65 -0
  193. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +71 -0
  194. package/lib/novel-to-game/skills/novel-to-game/agents/openai.yaml +4 -0
  195. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +42 -0
  196. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +51 -0
  197. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +65 -0
  198. package/lib/oh-story/manifest.json +419 -309
  199. package/lib/oh-story/roles/character-designer.md +4 -4
  200. package/lib/oh-story/roles/consistency-checker.md +1 -1
  201. package/lib/oh-story/roles/narrative-writer.md +41 -42
  202. package/lib/oh-story/roles/story-architect.md +19 -23
  203. package/lib/oh-story/roles/story-explorer.md +8 -5
  204. package/lib/oh-story/skills/browser-cdp/SKILL.md +1 -1
  205. package/lib/oh-story/skills/story/SKILL.md +16 -9
  206. package/lib/oh-story/skills/story/VERSION +1 -1
  207. package/lib/oh-story/skills/story/references/author-memory.md +163 -0
  208. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +871 -0
  209. package/lib/oh-story/skills/story-cover/SKILL.md +1 -1
  210. package/lib/oh-story/skills/story-deslop/SKILL.md +10 -5
  211. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +2 -1
  212. package/lib/oh-story/skills/story-deslop/references/author-memory.md +163 -0
  213. package/lib/oh-story/skills/story-deslop/references/banned-words.md +6 -4
  214. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +871 -0
  215. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +64 -1
  216. package/lib/oh-story/skills/story-import/SKILL.md +21 -8
  217. package/lib/oh-story/skills/story-import/references/format-and-structure.md +9 -10
  218. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +2 -2
  219. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +1 -1
  220. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +14 -6
  221. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +251 -0
  222. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +98 -9
  223. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +246 -0
  224. package/lib/oh-story/skills/story-long-analyze/SKILL.md +4 -4
  225. package/lib/oh-story/skills/story-long-scan/SKILL.md +1 -1
  226. package/lib/oh-story/skills/story-long-write/SKILL.md +35 -21
  227. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +2 -1
  228. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +7 -3
  229. package/lib/oh-story/skills/story-long-write/references/author-memory.md +163 -0
  230. package/lib/oh-story/skills/story-long-write/references/banned-words.md +6 -4
  231. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +6 -6
  232. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  233. package/lib/oh-story/skills/story-long-write/references/genre-readers.md +1 -1
  234. package/lib/oh-story/skills/{story-setup/references/agent-references/hooks-chapter.md → story-long-write/references/long-chapter-hooks.md} +5 -5
  235. package/lib/oh-story/skills/story-long-write/references/{quality-checklist.md → long-chapter-quality.md} +2 -1
  236. package/lib/oh-story/skills/story-long-write/references/long-format.md +42 -0
  237. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-catalog.md → story-long-write/references/long-genre-catalog.md} +11 -97
  238. package/lib/oh-story/skills/story-long-write/references/{genre-core-mechanics.md → long-genre-mechanics.md} +1 -1
  239. package/lib/oh-story/skills/story-long-write/references/{reversal-toolkit.md → long-reversal.md} +18 -32
  240. package/lib/oh-story/skills/story-long-write/references/{hooks-suspense.md → long-suspense.md} +6 -6
  241. package/lib/oh-story/skills/story-long-write/references/opening-design.md +1 -1
  242. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +1 -1
  243. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  244. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +14 -6
  245. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +32 -27
  246. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +5 -4
  247. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +2 -2
  248. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +17 -11
  249. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +54 -54
  250. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +871 -0
  251. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +64 -1
  252. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +251 -0
  253. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +303 -0
  254. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +98 -9
  255. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +246 -0
  256. package/lib/oh-story/skills/story-review/SKILL.md +26 -17
  257. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +2 -1
  258. package/lib/oh-story/skills/story-review/references/author-memory.md +163 -0
  259. package/lib/oh-story/skills/story-review/references/banned-words.md +6 -4
  260. package/lib/oh-story/skills/story-review/references/{quality-checklist.md → review-quality.md} +22 -21
  261. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +14 -6
  262. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +871 -0
  263. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +64 -1
  264. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +98 -9
  265. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +246 -0
  266. package/lib/oh-story/skills/story-setup/SKILL.md +85 -41
  267. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +72 -0
  268. package/lib/oh-story/skills/story-setup/references/agent-references/agent-reference-profiles.md +55 -0
  269. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +2 -1
  270. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +6 -4
  271. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +9 -10
  272. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  273. package/lib/oh-story/skills/story-setup/references/agent-references/genre-readers.md +1 -1
  274. package/lib/oh-story/skills/{story-short-analyze/references/hooks-chapter.md → story-setup/references/agent-references/long-chapter-hooks.md} +5 -5
  275. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +209 -0
  276. package/lib/oh-story/skills/{story-long-write/references/genre-catalog.md → story-setup/references/agent-references/long-genre-catalog.md} +11 -97
  277. package/lib/oh-story/skills/story-setup/references/agent-references/{genre-core-mechanics.md → long-genre-mechanics.md} +1 -1
  278. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +40 -0
  279. package/lib/oh-story/skills/{story-short-write/references/reversal-toolkit.md → story-setup/references/agent-references/long-reversal.md} +18 -32
  280. package/lib/oh-story/skills/{story-short-write/references/hooks-suspense.md → story-setup/references/agent-references/long-suspense.md} +6 -6
  281. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +1 -1
  282. package/lib/oh-story/skills/story-setup/references/agent-references/{emotional-methods.md → short-emotional-methods.md} +4 -4
  283. package/lib/oh-story/skills/{story-long-write/references/genre-writing-formulas.md → story-setup/references/agent-references/short-genre-formulas.md} +32 -44
  284. package/lib/oh-story/skills/story-setup/references/agent-references/short-quality.md +39 -0
  285. package/lib/oh-story/skills/story-setup/references/agent-references/{reversal-toolkit.md → short-reversal.md} +29 -23
  286. package/lib/oh-story/skills/story-setup/references/agent-references/{hooks-suspense.md → short-suspense.md} +31 -31
  287. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  288. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +55 -55
  289. package/lib/oh-story/skills/story-short-analyze/SKILL.md +28 -28
  290. package/lib/oh-story/skills/story-short-analyze/references/{character-basics.md → analysis-character-basics.md} +1 -1
  291. package/lib/oh-story/skills/story-short-analyze/references/{character-design-methods.md → analysis-character-design.md} +1 -1
  292. package/lib/oh-story/skills/story-short-analyze/references/{character-relations.md → analysis-character-relations.md} +1 -1
  293. package/lib/oh-story/skills/story-short-analyze/references/{hooks-paragraph.md → analysis-paragraph-hooks.md} +1 -1
  294. package/lib/oh-story/skills/story-short-analyze/references/{genre-readers.md → analysis-reader-profile.md} +2 -2
  295. package/lib/oh-story/skills/story-short-analyze/references/analysis-report-style.md +45 -0
  296. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-genres.md +65 -0
  297. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-hooks.md +77 -0
  298. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-mechanics.md +82 -0
  299. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-patterns.md +50 -0
  300. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-suspense.md +98 -0
  301. package/lib/oh-story/skills/story-short-analyze/references/{genre-writing-techniques.md → analysis-writing-techniques.md} +21 -19
  302. package/lib/oh-story/skills/story-short-analyze/references/deconstruction-examples.md +1 -1
  303. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +21 -28
  304. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +1 -1
  305. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +17 -17
  306. package/lib/oh-story/skills/story-short-analyze/references/{quality-checklist.md → source-story-quality.md} +28 -49
  307. package/lib/oh-story/skills/story-short-analyze/references/zhihu-style.md +2 -2
  308. package/lib/oh-story/skills/story-short-scan/SKILL.md +1 -1
  309. package/lib/oh-story/skills/story-short-write/SKILL.md +78 -55
  310. package/lib/oh-story/skills/story-short-write/references/author-memory.md +163 -0
  311. package/lib/oh-story/skills/story-short-write/references/banned-words.md +6 -4
  312. package/lib/oh-story/skills/story-short-write/references/cross-book-recall.md +3 -3
  313. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +4 -4
  314. 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
  315. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +2 -2
  316. 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
  317. 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
  318. 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
  319. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +4 -4
  320. 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
  321. 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
  322. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +4 -4
  323. 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
  324. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +32 -44
  325. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +20 -18
  326. package/lib/oh-story/skills/story-short-write/references/output-contract.md +1 -1
  327. package/lib/oh-story/skills/story-short-write/references/short-craft.md +18 -18
  328. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +1 -1
  329. package/lib/oh-story/skills/story-short-write/references/short-format.md +38 -8
  330. package/lib/oh-story/skills/story-short-write/references/{quality-checklist.md → short-prose-quality.md} +22 -21
  331. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +355 -0
  332. package/lib/oh-story/skills/{story-short-analyze/references/hooks-suspense.md → story-short-write/references/short-suspense.md} +31 -31
  333. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +2 -2
  334. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +31 -22
  335. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +871 -0
  336. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +64 -1
  337. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +212 -0
  338. package/lib/oh-story/skills/story-short-write/scripts/check-phase2-contract.js +363 -0
  339. package/lib/types/client/drama-production-view.d.ts +34 -0
  340. package/lib/types/client/drama-production-view.d.ts.map +1 -0
  341. package/lib/types/client/drama-production.d.ts +92 -0
  342. package/lib/types/client/drama-production.d.ts.map +1 -0
  343. package/lib/types/client/file-activity.d.ts +9 -2
  344. package/lib/types/client/file-activity.d.ts.map +1 -1
  345. package/lib/types/client/index.d.ts +1 -1
  346. package/lib/types/client/index.d.ts.map +1 -1
  347. package/lib/types/client/production-intents.d.ts +9 -0
  348. package/lib/types/client/production-intents.d.ts.map +1 -0
  349. package/lib/types/client/production-prompts.d.ts +9 -0
  350. package/lib/types/client/production-prompts.d.ts.map +1 -0
  351. package/lib/types/client/production-runtime.d.ts +58 -0
  352. package/lib/types/client/production-runtime.d.ts.map +1 -0
  353. package/lib/types/client/video-studio.d.ts +41 -0
  354. package/lib/types/client/video-studio.d.ts.map +1 -0
  355. package/lib/types/client/workbench-ui.d.ts +5 -0
  356. package/lib/types/client/workbench-ui.d.ts.map +1 -0
  357. package/lib/types/game-verification.d.ts +16 -0
  358. package/lib/types/game-verification.d.ts.map +1 -0
  359. package/lib/types/index.d.ts +5 -2
  360. package/lib/types/index.d.ts.map +1 -1
  361. package/lib/types/production-intent.d.ts +21 -0
  362. package/lib/types/production-intent.d.ts.map +1 -0
  363. package/lib/types/production-tool.d.ts +5 -0
  364. package/lib/types/production-tool.d.ts.map +1 -0
  365. package/lib/types/reference-tool.d.ts +6 -0
  366. package/lib/types/reference-tool.d.ts.map +1 -0
  367. package/lib/types/role-provider.d.ts.map +1 -1
  368. package/lib/types/role-tool.d.ts +3 -1
  369. package/lib/types/role-tool.d.ts.map +1 -1
  370. package/lib/types/skill-provider.d.ts +7 -0
  371. package/lib/types/skill-provider.d.ts.map +1 -1
  372. package/lib/types/video-project.d.ts +42 -0
  373. package/lib/types/video-project.d.ts.map +1 -0
  374. package/lib/types/workspace-request-trust.d.ts +6 -0
  375. package/lib/types/workspace-request-trust.d.ts.map +1 -1
  376. package/lib/types/workspace-route.d.ts +20 -0
  377. package/lib/types/workspace-route.d.ts.map +1 -1
  378. package/lib/video-recap/CHANGELOG.md +282 -0
  379. package/lib/video-recap/LICENSE +21 -0
  380. package/lib/video-recap/README.en.md +257 -0
  381. package/lib/video-recap/README.md +255 -0
  382. package/lib/video-recap/manifest.json +684 -0
  383. package/lib/video-recap/skills/video-assemble/SKILL.md +97 -0
  384. package/lib/video-recap/skills/video-assemble/references/jianying/LICENSE.duo-video +21 -0
  385. package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +15 -0
  386. package/lib/video-recap/skills/video-assemble/references/jianying/empty_draft_meta_info.json +69 -0
  387. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +84 -0
  388. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +98 -0
  389. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +204 -0
  390. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_material_video.json +83 -0
  391. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_meta_material_value.json +23 -0
  392. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_project_info.json +133 -0
  393. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_segment.json +70 -0
  394. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_text_styles.json +30 -0
  395. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_audio.json +41 -0
  396. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_text.json +109 -0
  397. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +88 -0
  398. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +345 -0
  399. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +34 -0
  400. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +305 -0
  401. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +94 -0
  402. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +205 -0
  403. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +555 -0
  404. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +261 -0
  405. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +767 -0
  406. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +111 -0
  407. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +24 -0
  408. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +109 -0
  409. package/lib/video-recap/skills/video-assemble/scripts/jianying_templates.py +37 -0
  410. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +191 -0
  411. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +75 -0
  412. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +394 -0
  413. package/lib/video-recap/skills/video-assemble/scripts/lib.py +239 -0
  414. package/lib/video-recap/skills/video-assemble/scripts/media.py +243 -0
  415. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +477 -0
  416. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +46 -0
  417. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +486 -0
  418. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +376 -0
  419. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +88 -0
  420. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +248 -0
  421. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +89 -0
  422. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +512 -0
  423. package/lib/video-recap/skills/video-cut/SKILL.md +90 -0
  424. package/lib/video-recap/skills/video-cut/scripts/cut.py +67 -0
  425. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +320 -0
  426. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +545 -0
  427. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +348 -0
  428. package/lib/video-recap/skills/video-cut/scripts/lib.py +77 -0
  429. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +341 -0
  430. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +249 -0
  431. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +745 -0
  432. package/lib/video-recap/skills/video-recap/SKILL.md +216 -0
  433. package/lib/video-recap/skills/video-recap/references/config-playbook.md +61 -0
  434. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +225 -0
  435. package/lib/video-recap/skills/video-recap/references/data-schema.md +534 -0
  436. package/lib/video-recap/skills/video-recap/references/research-guide.md +74 -0
  437. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +23 -0
  438. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +251 -0
  439. package/lib/video-recap/skills/video-recap/scripts/doctor.py +483 -0
  440. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +737 -0
  441. package/lib/video-recap/skills/video-recap/scripts/lib.py +242 -0
  442. package/lib/video-recap/skills/video-recap/scripts/materials.py +457 -0
  443. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +95 -0
  444. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +18 -0
  445. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_contract.py +9 -0
  446. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +371 -0
  447. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +201 -0
  448. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +227 -0
  449. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +362 -0
  450. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +129 -0
  451. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +338 -0
  452. package/lib/video-recap/skills/video-recap/scripts/recap.py +9 -0
  453. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +140 -0
  454. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +550 -0
  455. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +77 -0
  456. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +654 -0
  457. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +335 -0
  458. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +217 -0
  459. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +795 -0
  460. package/lib/video-recap/skills/video-script/SKILL.md +248 -0
  461. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +225 -0
  462. package/lib/video-recap/skills/video-script/references/research-guide.md +75 -0
  463. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +399 -0
  464. package/lib/video-recap/skills/video-script/scripts/agent_text.py +410 -0
  465. package/lib/video-recap/skills/video-script/scripts/brief_context.py +403 -0
  466. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +332 -0
  467. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +442 -0
  468. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +237 -0
  469. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +492 -0
  470. package/lib/video-recap/skills/video-script/scripts/lib.py +327 -0
  471. package/lib/video-recap/skills/video-script/scripts/narration.py +12 -0
  472. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +678 -0
  473. package/lib/video-recap/skills/video-script/scripts/review.py +42 -0
  474. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +262 -0
  475. package/lib/video-recap/skills/video-script/scripts/review_response.py +761 -0
  476. package/lib/video-recap/skills/video-script/scripts/review_runner.py +200 -0
  477. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +204 -0
  478. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +199 -0
  479. package/lib/video-recap/skills/video-script/scripts/validate.py +164 -0
  480. package/lib/video-recap/skills/video-understanding/SKILL.md +75 -0
  481. package/lib/video-recap/skills/video-understanding/references/data-schema.md +299 -0
  482. package/lib/video-recap/skills/video-understanding/references/prompt-templates.md +37 -0
  483. package/lib/video-recap/skills/video-understanding/references/research-guide.md +74 -0
  484. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +399 -0
  485. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +410 -0
  486. package/lib/video-recap/skills/video-understanding/scripts/asr.py +260 -0
  487. package/lib/video-recap/skills/video-understanding/scripts/brief.py +12 -0
  488. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +403 -0
  489. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +332 -0
  490. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +442 -0
  491. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +647 -0
  492. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +237 -0
  493. package/lib/video-recap/skills/video-understanding/scripts/detect.py +513 -0
  494. package/lib/video-recap/skills/video-understanding/scripts/extract.py +62 -0
  495. package/lib/video-recap/skills/video-understanding/scripts/lib.py +470 -0
  496. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +678 -0
  497. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +204 -0
  498. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +507 -0
  499. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +199 -0
  500. package/lib/video-recap/skills/video-understanding/scripts/understand.py +9 -0
  501. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +216 -0
  502. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +327 -0
  503. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +367 -0
  504. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +162 -0
  505. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +705 -0
  506. package/lib/video-recap/skills/video-voiceover/SKILL.md +81 -0
  507. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +637 -0
  508. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +109 -0
  509. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +421 -0
  510. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +113 -0
  511. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +782 -0
  512. package/package.json +57 -41
  513. package/lib/drama/skills/short-drama/references/lifecycle-commands.md +0 -130
  514. package/lib/oh-story/skills/story-deslop/scripts/check-outline-copy.js +0 -241
  515. package/lib/oh-story/skills/story-long-write/references/format-and-structure.md +0 -169
  516. package/lib/oh-story/skills/story-setup/references/agent-references/genre-writing-formulas.md +0 -444
  517. package/lib/oh-story/skills/story-setup/references/agent-references/quality-checklist.md +0 -247
  518. package/lib/oh-story/skills/story-short-analyze/references/anti-ai-writing.md +0 -582
  519. package/lib/oh-story/skills/story-short-analyze/references/banned-words.md +0 -147
  520. package/lib/oh-story/skills/story-short-analyze/references/genre-catalog.md +0 -490
  521. package/lib/oh-story/skills/story-short-analyze/references/genre-core-mechanics.md +0 -425
  522. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-formulas.md +0 -453
  523. /package/lib/oh-story/skills/{story-long-write/references/hooks-chapter.md → story-setup/references/agent-references/short-chapter-hooks.md} +0 -0
  524. /package/lib/oh-story/skills/{story-long-write/references/hooks-paragraph.md → story-setup/references/agent-references/short-paragraph-hooks.md} +0 -0
@@ -13,6 +13,7 @@ import argparse
13
13
  import contextlib
14
14
  import hashlib
15
15
  import json
16
+ import math
16
17
  import os
17
18
  import re
18
19
  import shutil
@@ -20,7 +21,7 @@ import stat
20
21
  import sys
21
22
  import unicodedata
22
23
  import uuid
23
- from collections.abc import Callable, Iterable, Mapping
24
+ from collections.abc import Callable, Iterable, Mapping, Sequence
24
25
  from datetime import datetime, timezone
25
26
  from pathlib import Path, PurePosixPath
26
27
  from typing import Any, Iterator
@@ -40,6 +41,15 @@ STATE_SCHEMA = "2.0"
40
41
  DEFAULT_PROMPT_LANGUAGE = "en"
41
42
  LANGUAGE_TAG_RE = re.compile(r"[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*")
42
43
  DELIVERY_SUFFIXES = {".md", ".json", ".jsonl"}
44
+ CREATOR_DOCUMENTS = (
45
+ "剧本.md",
46
+ "视觉设定.md",
47
+ "分镜.md",
48
+ "图片提示词.md",
49
+ "视频提示词.md",
50
+ )
51
+ EXPORT_MEDIA_DIRECTORY = "制作成果"
52
+ EXPORT_SCHEMA = "1.0"
43
53
  EPISODE_ID_RE = re.compile(r"EP(?:[0-9]{3}|[1-9][0-9]{3,})")
44
54
  WINDOWS_FORBIDDEN_PATH_CHARACTERS = frozenset('<>:"|?*')
45
55
  WINDOWS_RESERVED_PATH_STEMS = frozenset(
@@ -193,12 +203,56 @@ def project_languages(project: Mapping[str, Any]) -> dict[str, str]:
193
203
  if isinstance(format_block, Mapping)
194
204
  else None
195
205
  )
206
+ authority = project.get("creator_authority")
207
+ production_profile = (
208
+ authority.get("production_profile")
209
+ if isinstance(authority, Mapping)
210
+ else None
211
+ )
212
+ choices = (
213
+ production_profile.get("choices")
214
+ if isinstance(production_profile, Mapping)
215
+ else None
216
+ )
217
+ video_prompt_language = (
218
+ choices.get("video_prompt_language") if isinstance(choices, Mapping) else None
219
+ )
196
220
  return {
197
221
  "language": str(project.get("language") or "zh-CN"),
198
222
  "prompt_language": str(prompt_language or DEFAULT_PROMPT_LANGUAGE),
223
+ "video_prompt_language": str(
224
+ video_prompt_language or prompt_language or DEFAULT_PROMPT_LANGUAGE
225
+ ),
199
226
  }
200
227
 
201
228
 
229
+ def project_video_model_profile(project: Mapping[str, Any]) -> dict[str, Any]:
230
+ """Expose the accepted prompt-facing model choices without guessing defaults."""
231
+ authority = project.get("creator_authority")
232
+ production_profile = (
233
+ authority.get("production_profile")
234
+ if isinstance(authority, Mapping)
235
+ else None
236
+ )
237
+ if (
238
+ not isinstance(production_profile, Mapping)
239
+ or production_profile.get("status") != "accepted"
240
+ ):
241
+ return {}
242
+ choices = production_profile.get("choices")
243
+ if not isinstance(choices, Mapping):
244
+ return {}
245
+ fields = (
246
+ "target_video_model",
247
+ "video_prompt_dialect",
248
+ "video_prompt_language",
249
+ "native_duration_seconds",
250
+ "supported_generation_modes",
251
+ "audio_generation",
252
+ )
253
+ return {field: choices[field] for field in fields if field in choices}
254
+
255
+
202
256
  def initialize_project(
203
257
  path: Path,
204
258
  *,
@@ -206,10 +260,25 @@ def initialize_project(
206
260
  language: str,
207
261
  aspect_ratio: str,
208
262
  prompt_language: str = DEFAULT_PROMPT_LANGUAGE,
263
+ episode_count: int | None = None,
264
+ target_seconds_per_episode: float | None = None,
209
265
  suite_root: Path | None = None,
210
266
  ) -> dict[str, Any]:
211
267
  language = normalize_language_tag(language, field="language")
212
268
  prompt_language = normalize_language_tag(prompt_language, field="prompt_language")
269
+ if episode_count is not None and (
270
+ isinstance(episode_count, bool)
271
+ or not isinstance(episode_count, int)
272
+ or episode_count <= 0
273
+ ):
274
+ raise ValueError("episode_count must be a positive integer")
275
+ if target_seconds_per_episode is not None and (
276
+ isinstance(target_seconds_per_episode, bool)
277
+ or not isinstance(target_seconds_per_episode, (int, float))
278
+ or not math.isfinite(target_seconds_per_episode)
279
+ or target_seconds_per_episode <= 0
280
+ ):
281
+ raise ValueError("target_seconds_per_episode must be a positive number of seconds")
213
282
  root = path.expanduser().resolve()
214
283
  project_path = root / PROJECT_FILE
215
284
  if project_path.exists():
@@ -233,6 +302,10 @@ def initialize_project(
233
302
  )
234
303
  project["format"]["aspect_ratio"] = aspect_ratio
235
304
  project["format"]["prompt_language"] = prompt_language
305
+ if episode_count is not None:
306
+ project["format"]["episode_count"] = episode_count
307
+ if target_seconds_per_episode is not None:
308
+ project["format"]["target_seconds_per_episode"] = target_seconds_per_episode
236
309
  state = {
237
310
  "schema_version": STATE_SCHEMA,
238
311
  "project_id": project["project_id"],
@@ -620,13 +693,26 @@ def _project_path(root: Path, relative: str, *, create_parent: bool = False) ->
620
693
  current = root
621
694
  for part in PurePosixPath(relative).parts[:-1]:
622
695
  current /= part
623
- if current.exists() and current.is_symlink():
696
+ # ``is_symlink`` misses the reparse points that are not name
697
+ # surrogates, and a Windows creator can make those without any extra
698
+ # privilege. Ask about the attribute directly instead.
699
+ try:
700
+ details = os.lstat(current)
701
+ except FileNotFoundError:
702
+ details = None
703
+ if details is not None and _is_link_or_reparse(details):
624
704
  raise ProjectConflictError(f"project parent cannot be a symlink: {part}")
625
- if current.exists() and not current.is_dir():
705
+ if details is not None and not stat.S_ISDIR(details.st_mode):
626
706
  raise ProjectConflictError(f"project parent is not a directory: {part}")
627
- if create_parent and not current.exists():
707
+ if create_parent and details is None:
628
708
  current.mkdir()
629
- if target.exists() and (target.is_symlink() or not target.is_file()):
709
+ try:
710
+ target_details = os.lstat(target)
711
+ except (FileNotFoundError, NotADirectoryError):
712
+ target_details = None
713
+ if target_details is not None and (
714
+ _is_link_or_reparse(target_details) or not stat.S_ISREG(target_details.st_mode)
715
+ ):
630
716
  raise ProjectConflictError(f"project target is not a regular file: {relative}")
631
717
  if not target.parent.resolve().is_relative_to(root):
632
718
  raise ValueError(f"path escapes project root: {relative}")
@@ -819,11 +905,14 @@ def _build_status(
819
905
  if isinstance(output, str) and output:
820
906
  ownership[output] = owner
821
907
  languages = project_languages(project)
908
+ video_model_profile = project_video_model_profile(project)
822
909
  return {
823
910
  "project_id": project.get("project_id"),
824
911
  "title": project.get("title"),
825
912
  "language": languages["language"],
826
913
  "prompt_language": languages["prompt_language"],
914
+ "video_prompt_language": languages["video_prompt_language"],
915
+ "video_model_profile": video_model_profile,
827
916
  "project_root": project_root,
828
917
  "last_action": state.get("last_action"),
829
918
  "layout": dict(layout),
@@ -1007,6 +1096,113 @@ def project_path_lifecycle_at(
1007
1096
  return None
1008
1097
 
1009
1098
 
1099
+ # The three functions below are the path-based half of the dashboard contract.
1100
+ # Windows has no ``openat``, so the dashboard pins a project root by verified
1101
+ # path there instead of by directory descriptor and calls these. They compose
1102
+ # the same rules the descriptor twins do -- ``_build_status``,
1103
+ # ``_artifact_state_from``, ``_normalize_state`` -- so the two halves cannot
1104
+ # drift on what a status or a lifecycle state means; they differ only in how a
1105
+ # file is reached.
1106
+
1107
+
1108
+ def _read_regular(root: Path, relative: str) -> bytes:
1109
+ """Read one project file, refusing every link and reparse point on the way.
1110
+
1111
+ ``os.lstat`` never follows, so a component swapped for a symlink or a
1112
+ junction is rejected rather than traversed. This is the Windows stand-in
1113
+ for opening each component with ``O_NOFOLLOW``.
1114
+ """
1115
+
1116
+ pure = PurePosixPath(relative)
1117
+ current = root
1118
+ for part in pure.parts[:-1]:
1119
+ current = current / part
1120
+ details = os.lstat(current)
1121
+ if _is_link_or_reparse(details) or not stat.S_ISDIR(details.st_mode):
1122
+ raise ProjectConflictError(f"project parent is unsafe: {part}")
1123
+ target = current / pure.name
1124
+ details = os.lstat(target)
1125
+ if _is_link_or_reparse(details) or not stat.S_ISREG(details.st_mode):
1126
+ raise ProjectConflictError(f"project file is unsafe: {relative}")
1127
+ with open(target, "rb") as handle:
1128
+ return handle.read()
1129
+
1130
+
1131
+ def project_status_from_root(root: Path, *, project_root: str) -> dict[str, Any]:
1132
+ """Report status for an already-pinned project root.
1133
+
1134
+ ``project_status`` locates the project first and requires recorded state;
1135
+ this twin takes the root the caller pinned and tolerates a project that has
1136
+ never been tracked, matching ``project_status_at``.
1137
+ """
1138
+
1139
+ project = json.loads(_read_regular(root, PROJECT_FILE).decode("utf-8"))
1140
+ try:
1141
+ raw_state = json.loads(
1142
+ _read_regular(root, STATE_FILE.as_posix()).decode("utf-8")
1143
+ )
1144
+ except FileNotFoundError:
1145
+ raw_state = {
1146
+ "schema_version": STATE_SCHEMA,
1147
+ "project_id": project.get("project_id"),
1148
+ "project_layout_mode": "auto",
1149
+ "last_action": "untracked",
1150
+ "artifacts": {},
1151
+ }
1152
+ if not isinstance(project, dict) or not isinstance(raw_state, dict):
1153
+ raise ValueError("project files must contain objects")
1154
+ state = _normalize_state(raw_state)
1155
+ return _build_status(
1156
+ project=project,
1157
+ state=state,
1158
+ layout=_project_layout_from_root(root, state),
1159
+ project_root=project_root,
1160
+ artifact_state=lambda record: _artifact_state(root, record),
1161
+ )
1162
+
1163
+
1164
+ def project_path_lifecycle(root: Path, relative: str) -> dict[str, str] | None:
1165
+ """Return the one creator-facing state for a tracked path."""
1166
+
1167
+ normalized = _relative_path(relative, allow_operations=True)
1168
+ try:
1169
+ raw_state = json.loads(
1170
+ _read_regular(root, STATE_FILE.as_posix()).decode("utf-8")
1171
+ )
1172
+ except (OSError, ProjectConflictError, UnicodeError, json.JSONDecodeError):
1173
+ return None
1174
+ if not isinstance(raw_state, Mapping):
1175
+ return None
1176
+ state = _normalize_state(raw_state)
1177
+ for record in state["artifacts"].values():
1178
+ if isinstance(record, Mapping) and normalized in record.get("outputs", []):
1179
+ return {"artifact_state": _artifact_state(root, record)}
1180
+ return None
1181
+
1182
+
1183
+ @contextlib.contextmanager
1184
+ def coordinated_project_text_edit(
1185
+ root: Path, relative: str, expected_version: str
1186
+ ) -> Iterator[None]:
1187
+ normalized = _relative_path(relative)
1188
+ if not re.fullmatch(r"[0-9a-f]{64}", expected_version):
1189
+ raise ValueError("expected version must be a SHA-256 digest")
1190
+ # The descriptor twin opens the operations directory with O_NOFOLLOW. Match
1191
+ # it: a lock taken through a redirected `.short-drama` would leave two
1192
+ # dashboards each believing they hold the project.
1193
+ try:
1194
+ operations = os.lstat(root / ".short-drama")
1195
+ except FileNotFoundError:
1196
+ operations = None
1197
+ if operations is not None and _is_link_or_reparse(operations):
1198
+ raise OSError("project operations directory is unsafe")
1199
+ with _project_lock(root):
1200
+ current = sha256_bytes(_read_regular(root, normalized))
1201
+ if current != expected_version:
1202
+ raise ProjectConflictError("file changed since it was opened")
1203
+ yield
1204
+
1205
+
1010
1206
  def _validate_structured_content(relative: str, content: bytes) -> None:
1011
1207
  try:
1012
1208
  text = content.decode("utf-8")
@@ -1668,6 +1864,255 @@ def _manifest_problems(
1668
1864
  return problems
1669
1865
 
1670
1866
 
1867
+ def _same_directory(left: Path, right: Path) -> bool:
1868
+ """Do these two paths name the same directory on disk?
1869
+
1870
+ String comparison is not enough: a case-insensitive volume and a filesystem
1871
+ that stores a different Unicode normalisation both spell one directory two
1872
+ ways, and either spelling would otherwise slip past a containment check.
1873
+ """
1874
+ try:
1875
+ return os.path.samefile(left, right)
1876
+ except OSError:
1877
+ return False
1878
+
1879
+
1880
+ def _require_safe_export_destination(destination: Path, root: Path) -> None:
1881
+ """Refuse any destination that would take the project down with it.
1882
+
1883
+ The export replaces its destination directory wholesale, so a destination
1884
+ that is -- or contains -- the project being exported would delete the
1885
+ source. Ancestors matter as much as descendants here.
1886
+ """
1887
+ if destination == root or root in destination.parents:
1888
+ raise ValueError("export destination must live outside the project root")
1889
+ for ancestor in (root, *root.parents):
1890
+ if destination == ancestor or _same_directory(destination, ancestor):
1891
+ raise ValueError(
1892
+ "export destination must not be the project root or any directory containing it"
1893
+ )
1894
+
1895
+
1896
+ def _require_replaceable_export(destination: Path) -> None:
1897
+ """Only ever overwrite a directory this command itself produced.
1898
+
1899
+ `--overwrite` removes the destination tree. Without this check a mistyped
1900
+ path turns an ordinary directory into a deletion, and nothing else in this
1901
+ tool can erase a directory outside the project.
1902
+ """
1903
+ if not destination.exists():
1904
+ return
1905
+ details = os.lstat(destination)
1906
+ if _is_link_or_reparse(details) or not stat.S_ISDIR(details.st_mode):
1907
+ raise ProjectConflictError("export destination is not a regular directory")
1908
+ manifest = destination / "manifest.json"
1909
+ try:
1910
+ document = json.loads(manifest.read_text(encoding="utf-8"))
1911
+ except (OSError, ValueError) as exc:
1912
+ raise ValueError(
1913
+ "refusing to overwrite a directory that is not a previous export: "
1914
+ f"{destination}"
1915
+ ) from exc
1916
+ if not isinstance(document, Mapping) or document.get("kind") != "creator_export":
1917
+ raise ValueError(
1918
+ "refusing to overwrite a directory that is not a previous export: "
1919
+ f"{destination}"
1920
+ )
1921
+
1922
+
1923
+ def _export_regular_file(source: Path, destination: Path) -> str:
1924
+ """Copy one regular project file into the export tree and return its digest."""
1925
+ details = os.lstat(source)
1926
+ if _is_link_or_reparse(details) or not stat.S_ISREG(details.st_mode):
1927
+ raise ProjectConflictError(f"export source is not a regular file: {source.name}")
1928
+ if "\n" in source.name or "\r" in source.name:
1929
+ # checksums.sha256 is one record per line; such a name cannot be written
1930
+ # in a form any verifier could read back.
1931
+ raise ProjectConflictError(
1932
+ "export source name contains a line break and cannot be checksummed"
1933
+ )
1934
+ destination.parent.mkdir(parents=True, exist_ok=True)
1935
+ shutil.copyfile(source, destination)
1936
+ return sha256_file(destination)
1937
+
1938
+
1939
+ def _export_episode(
1940
+ episode_directory: Path, target: Path, *, include_media: bool
1941
+ ) -> tuple[list[dict[str, str]], list[str]]:
1942
+ files: list[dict[str, str]] = []
1943
+ missing: list[str] = []
1944
+ for name in CREATOR_DOCUMENTS:
1945
+ source = episode_directory / name
1946
+ if not source.exists():
1947
+ missing.append(name)
1948
+ continue
1949
+ digest = _export_regular_file(source, target / name)
1950
+ files.append({"path": name, "sha256": digest})
1951
+ media_root = episode_directory / EXPORT_MEDIA_DIRECTORY
1952
+ if include_media and media_root.exists():
1953
+ media_details = os.lstat(media_root)
1954
+ if _is_link_or_reparse(media_details) or not stat.S_ISDIR(media_details.st_mode):
1955
+ # Silently exporting no media reads as "this episode has none".
1956
+ raise ProjectConflictError(
1957
+ f"{EXPORT_MEDIA_DIRECTORY} is not a regular directory: {episode_directory.name}"
1958
+ )
1959
+ for source in sorted(media_root.rglob("*")):
1960
+ if source.is_dir() and not source.is_symlink():
1961
+ continue
1962
+ relative = source.relative_to(episode_directory).as_posix()
1963
+ digest = _export_regular_file(source, target / relative)
1964
+ files.append({"path": relative, "sha256": digest})
1965
+ return files, missing
1966
+
1967
+
1968
+ def _export_exclusions(root: Path, episodes_name: str) -> list[str]:
1969
+ """Name every top-level project directory this export did not copy.
1970
+
1971
+ A fixed list lies twice over: it names roots a legacy-layout project does
1972
+ not have, and it stays silent about a published 设定集/ that the handover is
1973
+ missing.
1974
+ """
1975
+ excluded: list[str] = []
1976
+ for entry in sorted(root.iterdir(), key=lambda item: item.name):
1977
+ if entry.name == episodes_name or entry.name == PROJECT_FILE:
1978
+ continue
1979
+ if entry.is_dir() or entry.is_symlink():
1980
+ excluded.append(entry.name)
1981
+ return excluded
1982
+
1983
+
1984
+ def _is_portable_windows_path(relative: str) -> bool:
1985
+ for part in PurePosixPath(relative).parts:
1986
+ stem = part.split(".")[0].casefold()
1987
+ if stem in WINDOWS_RESERVED_PATH_STEMS:
1988
+ return False
1989
+ if set(part) & WINDOWS_FORBIDDEN_PATH_CHARACTERS:
1990
+ return False
1991
+ if part.endswith((" ", ".")):
1992
+ return False
1993
+ return True
1994
+
1995
+
1996
+ def build_creator_export(
1997
+ root: Path,
1998
+ *,
1999
+ out: Path,
2000
+ episodes: Sequence[str] | None = None,
2001
+ include_media: bool = True,
2002
+ overwrite: bool = False,
2003
+ ) -> dict[str, Any]:
2004
+ """Copy the current creator documents and produced media into a handover tree.
2005
+
2006
+ This is a snapshot of what the project holds right now. It deliberately
2007
+ asserts nothing about review or creator acceptance; `package` remains the
2008
+ only command that speaks for an approved delivery.
2009
+ """
2010
+ root = find_project(root)
2011
+ destination = out.expanduser().resolve()
2012
+ _require_safe_export_destination(destination, root)
2013
+ layout = _project_layout_from_root(root)
2014
+ if layout["mode"] == "mixed":
2015
+ raise PackageBlockedError("mixed project layouts cannot be exported")
2016
+ episodes_root = root / str(layout["roots"]["episodes"])
2017
+ available = sorted(
2018
+ entry.name
2019
+ for entry in episodes_root.iterdir()
2020
+ if entry.is_dir() and not entry.is_symlink()
2021
+ if EPISODE_ID_RE.fullmatch(entry.name)
2022
+ ) if episodes_root.is_dir() and not episodes_root.is_symlink() else []
2023
+ if episodes:
2024
+ selected = []
2025
+ for episode in episodes:
2026
+ if EPISODE_ID_RE.fullmatch(episode) is None:
2027
+ raise ValueError("episode must use an EP001-style identifier")
2028
+ if episode not in available:
2029
+ raise FileNotFoundError(f"episode directory not found: {episode}")
2030
+ if episode not in selected:
2031
+ selected.append(episode)
2032
+ else:
2033
+ selected = available
2034
+ if not selected:
2035
+ raise FileNotFoundError("no episode directory to export")
2036
+ if destination.exists():
2037
+ if not overwrite:
2038
+ raise FileExistsError(f"export destination already exists: {destination}")
2039
+ _require_replaceable_export(destination)
2040
+
2041
+ project = json.loads((root / PROJECT_FILE).read_text(encoding="utf-8"))
2042
+ destination.parent.mkdir(parents=True, exist_ok=True)
2043
+ temporary = destination.parent / f".{destination.name}.{uuid.uuid4().hex}.tmp"
2044
+ temporary.mkdir()
2045
+ try:
2046
+ exported: list[dict[str, Any]] = []
2047
+ members: list[dict[str, str]] = []
2048
+ episodes_name = str(layout["roots"]["episodes"])
2049
+ for episode in selected:
2050
+ target = temporary / episodes_name / episode
2051
+ target.mkdir(parents=True)
2052
+ files, missing = _export_episode(
2053
+ episodes_root / episode, target, include_media=include_media
2054
+ )
2055
+ for entry in files:
2056
+ members.append(
2057
+ {
2058
+ "path": f"{episodes_name}/{episode}/{entry['path']}",
2059
+ "sha256": entry["sha256"],
2060
+ }
2061
+ )
2062
+ exported.append(
2063
+ {"episode": episode, "files": files, "missing_documents": missing}
2064
+ )
2065
+ config_digest = _export_regular_file(
2066
+ root / PROJECT_FILE, temporary / PROJECT_FILE
2067
+ )
2068
+ members.append({"path": PROJECT_FILE, "sha256": config_digest})
2069
+ manifest = {
2070
+ "schema_version": EXPORT_SCHEMA,
2071
+ "kind": "creator_export",
2072
+ "asserts_approval": False,
2073
+ "project_id": project.get("project_id"),
2074
+ "title": project.get("title"),
2075
+ "created_at": utc_now(),
2076
+ "episodes": exported,
2077
+ "selection": {
2078
+ "episodes": "all" if not episodes else list(selected),
2079
+ "available_episodes": list(available),
2080
+ "include_media": bool(include_media),
2081
+ },
2082
+ "excluded": _export_exclusions(root, episodes_name),
2083
+ "windows_unsafe_paths": sorted(
2084
+ entry["path"]
2085
+ for entry in members
2086
+ if not _is_portable_windows_path(entry["path"])
2087
+ ),
2088
+ }
2089
+ atomic_json(temporary / "manifest.json", manifest)
2090
+ checksum_members = ["manifest.json", *(entry["path"] for entry in members)]
2091
+ checksums = "".join(
2092
+ f"{sha256_file(temporary / relative)} {relative}\n"
2093
+ for relative in sorted(checksum_members)
2094
+ )
2095
+ _atomic_bytes(temporary / "checksums.sha256", checksums.encode("utf-8"))
2096
+ _replace_directory(temporary, destination)
2097
+ finally:
2098
+ if temporary.exists():
2099
+ shutil.rmtree(temporary)
2100
+ return {
2101
+ "kind": "creator_export",
2102
+ "out": str(destination),
2103
+ "episodes": [entry["episode"] for entry in exported],
2104
+ "files": [entry["path"] for entry in members],
2105
+ "excluded": manifest["excluded"],
2106
+ "include_media": bool(include_media),
2107
+ "missing_documents": {
2108
+ entry["episode"]: entry["missing_documents"]
2109
+ for entry in exported
2110
+ if entry["missing_documents"]
2111
+ },
2112
+ "windows_unsafe_paths": manifest["windows_unsafe_paths"],
2113
+ }
2114
+
2115
+
1671
2116
  def verify_delivery_package(root: Path, *, episode: str) -> dict[str, Any]:
1672
2117
  if EPISODE_ID_RE.fullmatch(episode) is None:
1673
2118
  raise ValueError("episode must use an EP001-style identifier")
@@ -1825,6 +2270,8 @@ def build_parser() -> argparse.ArgumentParser:
1825
2270
  init.add_argument("--language", default="zh-CN")
1826
2271
  init.add_argument("--prompt-language", default=DEFAULT_PROMPT_LANGUAGE)
1827
2272
  init.add_argument("--aspect-ratio", default="9:16")
2273
+ init.add_argument("--episode-count", type=int)
2274
+ init.add_argument("--target-seconds", type=float, dest="target_seconds_per_episode")
1828
2275
 
1829
2276
  status = commands.add_parser("status", help="Print a creator-safe project summary.")
1830
2277
  status.add_argument("path", nargs="?", default=".")
@@ -1891,6 +2338,29 @@ def build_parser() -> argparse.ArgumentParser:
1891
2338
  verify = commands.add_parser("verify", help="Re-check a delivered package checksums.")
1892
2339
  verify.add_argument("path")
1893
2340
  verify.add_argument("--episode", required=True)
2341
+
2342
+ export = commands.add_parser(
2343
+ "export",
2344
+ help="Copy the current creator documents and produced media for handover.",
2345
+ )
2346
+ export.add_argument("path")
2347
+ export.add_argument("--out", required=True, type=Path)
2348
+ export.add_argument(
2349
+ "--episode",
2350
+ action="append",
2351
+ dest="episodes",
2352
+ help="Export only this episode; repeat for several. Default: every episode.",
2353
+ )
2354
+ export.add_argument(
2355
+ "--no-media",
2356
+ action="store_true",
2357
+ help=f"Skip 剧集/<EP>/{EXPORT_MEDIA_DIRECTORY}/ and export text only.",
2358
+ )
2359
+ export.add_argument(
2360
+ "--overwrite",
2361
+ action="store_true",
2362
+ help="Replace an existing export directory at --out.",
2363
+ )
1894
2364
  return parser
1895
2365
 
1896
2366
 
@@ -1904,6 +2374,8 @@ def main(argv: list[str] | None = None) -> int:
1904
2374
  language=args.language,
1905
2375
  prompt_language=args.prompt_language,
1906
2376
  aspect_ratio=args.aspect_ratio,
2377
+ episode_count=args.episode_count,
2378
+ target_seconds_per_episode=args.target_seconds_per_episode,
1907
2379
  )
1908
2380
  elif args.command == "status":
1909
2381
  result = project_status(Path(args.path))
@@ -1947,6 +2419,14 @@ def main(argv: list[str] | None = None) -> int:
1947
2419
  includes=args.includes,
1948
2420
  omissions=_parse_omissions(args.omissions),
1949
2421
  )
2422
+ elif args.command == "export":
2423
+ result = build_creator_export(
2424
+ Path(args.path),
2425
+ out=args.out,
2426
+ episodes=args.episodes,
2427
+ include_media=not args.no_media,
2428
+ overwrite=args.overwrite,
2429
+ )
1950
2430
  else:
1951
2431
  result = verify_delivery_package(Path(args.path), episode=args.episode)
1952
2432
  if result["status"] != "verified":