@oh-story/dsh 0.1.10 → 0.1.12

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 (444) hide show
  1. package/README.md +11 -11
  2. package/lib/client.js +7 -3
  3. package/lib/client.js.map +2 -2
  4. package/lib/drama/manifest.json +212 -142
  5. package/lib/drama/skills/short-drama/SKILL.md +14 -2
  6. package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +3 -0
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +3 -0
  8. package/lib/drama/skills/short-drama/references/knowhow-index.md +15 -0
  9. package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
  10. package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
  11. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
  12. package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
  13. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  14. package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
  15. package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
  16. package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
  17. package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
  18. package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
  19. package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
  20. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +1 -1
  21. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
  22. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
  23. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/207/215/347/224/237/347/251/277/350/266/212.md +51 -0
  24. package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
  25. package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
  26. package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
  27. package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
  28. package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
  29. package/lib/drama/skills/short-drama-edit/SKILL.md +43 -20
  30. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
  31. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
  32. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
  33. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
  34. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
  35. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
  36. package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
  37. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
  38. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
  39. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
  40. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
  41. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
  42. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
  43. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
  44. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
  45. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
  46. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +4 -2
  47. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +3 -1
  48. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +6 -2
  49. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +12 -6
  50. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +13 -8
  51. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +2 -0
  52. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1153 -121
  53. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +49 -9
  54. package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
  55. package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
  56. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
  57. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
  58. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
  59. package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
  60. package/lib/drama/skills/short-drama-review/SKILL.md +2 -2
  61. package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
  62. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
  63. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
  64. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +15 -0
  65. package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
  66. package/lib/drama/skills/short-drama-storyboard/SKILL.md +13 -5
  67. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
  68. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
  69. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +82 -14
  70. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +4 -2
  71. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +5 -3
  72. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +12 -0
  73. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
  74. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
  75. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +40 -1
  76. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +20 -14
  77. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +1 -1
  78. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +8 -7
  79. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
  80. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
  81. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
  82. package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
  83. package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
  84. package/lib/drama/skills/short-drama-write/SKILL.md +8 -3
  85. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
  86. package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
  87. package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
  88. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
  89. package/lib/index.js +71 -21
  90. package/lib/index.js.map +3 -3
  91. package/lib/oh-story/manifest.json +545 -310
  92. package/lib/oh-story/roles/chapter-extractor.md +11 -11
  93. package/lib/oh-story/roles/character-designer.md +1 -2
  94. package/lib/oh-story/roles/consistency-checker.md +11 -9
  95. package/lib/oh-story/roles/narrative-writer.md +24 -25
  96. package/lib/oh-story/roles/story-architect.md +9 -61
  97. package/lib/oh-story/roles/story-explorer.md +20 -125
  98. package/lib/oh-story/roles/story-researcher.md +0 -1
  99. package/lib/oh-story/skills/story/SKILL.md +6 -6
  100. package/lib/oh-story/skills/story/VERSION +1 -1
  101. package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
  102. package/lib/oh-story/skills/story/references/author-memory.md +40 -118
  103. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +3 -3
  104. package/lib/oh-story/skills/story-deslop/SKILL.md +24 -25
  105. package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
  106. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +1 -1
  107. package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
  108. package/lib/oh-story/skills/story-deslop/references/author-memory.md +40 -118
  109. package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
  110. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +21 -4
  111. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
  112. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +3 -3
  113. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
  114. package/lib/oh-story/skills/story-import/SKILL.md +63 -579
  115. package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
  116. package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
  117. package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
  118. package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
  119. package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
  120. package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
  121. package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
  122. package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
  123. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +95 -91
  124. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
  125. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
  126. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +11 -9
  127. package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
  128. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +101 -14
  129. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +278 -23
  130. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +179 -23
  131. package/lib/oh-story/skills/story-long-analyze/SKILL.md +28 -50
  132. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +12 -5
  133. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +1 -1
  134. package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
  135. package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
  136. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +21 -55
  137. package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
  138. package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
  139. package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
  140. package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
  141. package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
  142. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +17 -0
  143. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +1 -1
  144. package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
  145. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +47 -8
  146. package/lib/oh-story/skills/story-long-scan/SKILL.md +41 -193
  147. package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
  148. package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
  149. package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
  150. package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
  151. package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
  152. package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
  153. package/lib/oh-story/skills/story-long-write/SKILL.md +32 -73
  154. package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
  155. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +1 -1
  156. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +18 -201
  157. package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
  158. package/lib/oh-story/skills/story-long-write/references/author-memory.md +40 -118
  159. package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
  160. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +6 -10
  161. package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
  162. package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
  163. package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
  164. package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
  165. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  166. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +3 -2
  167. package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
  168. package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
  169. package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
  170. package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
  171. package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
  172. package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
  173. package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
  174. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
  175. package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
  176. package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
  177. package/lib/oh-story/skills/story-long-write/references/project-files.md +10 -6
  178. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +1 -1
  179. package/lib/oh-story/skills/story-long-write/references/reference-index.md +4 -4
  180. package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
  181. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
  182. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  183. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +4 -4
  184. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
  185. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +11 -9
  186. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +33 -44
  187. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +18 -51
  188. package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
  189. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +53 -67
  190. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -251
  191. package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
  192. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +5 -3
  193. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +18 -56
  194. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +3 -3
  195. package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
  196. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +96 -16
  197. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
  198. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +101 -14
  199. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
  200. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +2 -1
  201. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +250 -50
  202. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +278 -23
  203. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +179 -23
  204. package/lib/oh-story/skills/story-review/SKILL.md +19 -273
  205. package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
  206. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +1 -1
  207. package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
  208. package/lib/oh-story/skills/story-review/references/author-memory.md +40 -118
  209. package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
  210. package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
  211. package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
  212. package/lib/oh-story/skills/story-review/references/review-quality.md +2 -1
  213. package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
  214. package/lib/oh-story/skills/story-review/references/solo.md +52 -0
  215. package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
  216. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
  217. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +11 -9
  218. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +3 -3
  219. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
  220. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +278 -23
  221. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +179 -23
  222. package/lib/oh-story/skills/story-setup/SKILL.md +69 -458
  223. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
  224. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +1 -1
  225. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
  226. package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
  227. package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
  228. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +21 -4
  229. package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
  230. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
  231. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
  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/long-genre-catalog.md +1 -1
  234. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
  235. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
  236. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
  237. package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
  238. package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
  239. package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
  240. package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
  241. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
  242. package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
  243. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
  244. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  245. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +4 -4
  246. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +18 -56
  247. package/lib/oh-story/skills/story-setup/references/diagnostics.md +3 -3
  248. package/lib/oh-story/skills/story-short-analyze/SKILL.md +37 -110
  249. package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
  250. package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
  251. package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
  252. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
  253. package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
  254. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
  255. package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
  256. package/lib/oh-story/skills/story-short-analyze/references/{material-decomposition.md → stage4-6-reversal-summary.md} +303 -374
  257. package/lib/oh-story/skills/story-short-scan/SKILL.md +30 -64
  258. package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
  259. package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
  260. package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
  261. package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
  262. package/lib/oh-story/skills/story-short-write/SKILL.md +23 -34
  263. package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
  264. package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
  265. package/lib/oh-story/skills/story-short-write/references/author-memory.md +40 -118
  266. package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
  267. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
  268. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
  269. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
  270. package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
  271. package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -49
  272. package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
  273. package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
  274. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
  275. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
  276. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
  277. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
  278. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
  279. package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
  280. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +19 -12
  281. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +22 -32
  282. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +84 -17
  283. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
  284. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +3 -3
  285. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
  286. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
  287. package/lib/types/client/video-studio.d.ts.map +1 -1
  288. package/lib/types/native-hooks.d.ts +2 -2
  289. package/lib/types/native-hooks.d.ts.map +1 -1
  290. package/lib/types/role-tool.d.ts.map +1 -1
  291. package/lib/types/skill-provider.d.ts.map +1 -1
  292. package/lib/types/video-project.d.ts.map +1 -1
  293. package/lib/types/workspace-route.d.ts.map +1 -1
  294. package/lib/video-recap/CHANGELOG.md +60 -1
  295. package/lib/video-recap/README.en.md +348 -137
  296. package/lib/video-recap/README.md +340 -136
  297. package/lib/video-recap/manifest.json +406 -251
  298. package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
  299. package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
  300. package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
  301. package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
  302. package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
  303. package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
  304. package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
  305. package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
  306. package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
  307. package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
  308. package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
  309. package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
  310. package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
  311. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
  312. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
  313. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
  314. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
  315. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
  316. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
  317. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
  318. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
  319. package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
  320. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
  321. package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
  322. package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
  323. package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
  324. package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
  325. package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
  326. package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
  327. package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
  328. package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
  329. package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
  330. package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
  331. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
  332. package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
  333. package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
  334. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
  335. package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
  336. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
  337. package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
  338. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
  339. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
  340. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
  341. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
  342. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
  343. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
  344. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
  345. package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
  346. package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
  347. package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
  348. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
  349. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
  350. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
  351. package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
  352. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
  353. package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
  354. package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
  355. package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
  356. package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
  357. package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
  358. package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
  359. package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
  360. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
  361. package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
  362. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
  363. package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
  364. package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
  365. package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
  366. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
  367. package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
  368. package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
  369. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
  370. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
  371. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
  372. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
  373. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
  374. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
  375. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
  376. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
  377. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
  378. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
  379. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
  380. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
  381. package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
  382. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
  383. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
  384. package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
  385. package/lib/video-recap/skills/video-script/SKILL.md +16 -13
  386. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
  387. package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
  388. package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
  389. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
  390. package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
  391. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
  392. package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
  393. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
  394. package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
  395. package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
  396. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
  397. package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
  398. package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
  399. package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
  400. package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
  401. package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
  402. package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
  403. package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
  404. package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
  405. package/lib/video-recap/skills/video-understanding/scripts/{agent_brief.py → briefing/builder.py} +38 -35
  406. package/lib/video-recap/skills/{video-script/scripts/brief_context.py → video-understanding/scripts/briefing/context.py} +4 -35
  407. package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
  408. package/lib/video-recap/skills/video-understanding/scripts/{brief_timeline.py → briefing/timeline.py} +5 -6
  409. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
  410. package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
  411. package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
  412. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
  413. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
  414. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
  415. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
  416. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
  417. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
  418. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
  419. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
  420. package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
  421. package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
  422. package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
  423. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
  424. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
  425. package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
  426. package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
  427. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
  428. package/package.json +41 -41
  429. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -538
  430. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -603
  431. package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
  432. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
  433. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
  434. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
  435. package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
  436. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
  437. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +0 -399
  438. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +0 -327
  439. package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
  440. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +0 -348
  441. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
  442. /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
  443. /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
  444. /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
@@ -0,0 +1,286 @@
1
+ """Resolve a ``recap_project.json`` binding into the settings stage skills already accept.
2
+
3
+ Only video-recap reads the library. A bound subtitle style becomes ``SUBTITLE_*`` env for
4
+ video-assemble, a bound voice becomes the voiceover provider / voice arguments, a bound
5
+ BGM becomes ``BGM_PATH``. Stage skills keep seeing concrete paths and values; they never
6
+ learn about templates. Anything the caller already set explicitly must agree with the
7
+ binding — a silent override would make the delivered file disagree with the project.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+
15
+ import library as library_lib
16
+
17
+ PROJECT_FILE = "recap_project.json"
18
+ PROJECT_SCHEMA = "video-recap.project.v1"
19
+ PROJECT_KEYS = {"schema", "name", "library", "bindings", "notes"}
20
+ BINDING_KINDS = ("subtitle_style", "packaging", "voice", "bgm")
21
+
22
+
23
+ class BindingError(SystemExit):
24
+ """A project binding that cannot be applied; exits before any stage runs."""
25
+
26
+ def __init__(self, message):
27
+ super().__init__(f"项目绑定无法使用: {message}")
28
+
29
+
30
+ def project_path(value) -> Path:
31
+ path = Path(value).expanduser().resolve()
32
+ if path.is_dir():
33
+ path = path / PROJECT_FILE
34
+ if not path.is_file():
35
+ raise BindingError(f"找不到 {path}")
36
+ return path
37
+
38
+
39
+ def _value(params, key):
40
+ node = params.get(key)
41
+ return node.get("value") if isinstance(node, dict) else None
42
+
43
+
44
+ def _record(entry):
45
+ return json.loads(Path(entry["record"]).read_text(encoding="utf-8"))
46
+
47
+
48
+ def _subtitle_env(template, record, resources):
49
+ params = record["params"]
50
+ canvas = record["canvas"]
51
+ env = {
52
+ "SUBTITLE_PLAY_RES_X": canvas["width"],
53
+ "SUBTITLE_PLAY_RES_Y": canvas["height"],
54
+ "SUBTITLE_FONT_SIZE": _value(params, "size_px"),
55
+ "SUBTITLE_OUTLINE": _value(params, "outline_px"),
56
+ "SUBTITLE_SHADOW": _value(params, "shadow_px"),
57
+ "SUBTITLE_PRIMARY_COLOR": _value(params, "primary_color"),
58
+ "SUBTITLE_OUTLINE_COLOR": _value(params, "outline_color"),
59
+ "SUBTITLE_MAX_CHARS": _value(params, "max_chars"),
60
+ "SUBTITLE_MAX_LINES": _value(params, "max_lines"),
61
+ }
62
+ font = params["font"]
63
+ if font.get("resource"):
64
+ resource = resources[font["resource"]]
65
+ env["SUBTITLE_FONT_NAME"] = (_record(resource).get("font") or {}).get("family")
66
+ env["SUBTITLE_FONT_FILE"] = resource["files"][0]["path"]
67
+ else:
68
+ env["SUBTITLE_FONT_NAME"] = font["family"]
69
+ band = _value(params, "band")
70
+ if band:
71
+ env["SUBTITLE_ALIGNMENT"] = 2
72
+ env["SUBTITLE_MARGIN_V"] = canvas["height"] - band["y_bot"]
73
+ return {k: str(v) for k, v in env.items() if v is not None}
74
+
75
+
76
+ def _voice_updates(resource, args):
77
+ record = _record(resource)
78
+ voice = record.get("voice") or {}
79
+ provider = voice.get("provider")
80
+ consent = (record.get("consent") or {}).get("status")
81
+ if resource["files"] and consent == "denied":
82
+ raise BindingError(f"音色 {resource['id']} 的参考音频声音授权为 denied")
83
+ if args.tts_provider not in ("auto", provider):
84
+ raise BindingError(f"音色 {resource['id']} 属于 {provider},与 --tts-provider {args.tts_provider} 冲突")
85
+ updates, env = {"tts_provider": provider}, {}
86
+ if provider == "mimo-tts":
87
+ if resource["files"]:
88
+ updates["voice_ref"] = resource["files"][0]["path"]
89
+ else:
90
+ updates["mimo_tts_voice"] = voice.get("voice_id")
91
+ elif provider == "fish-audio":
92
+ env["FISH_TTS_REFERENCE_ID"] = voice.get("voice_id")
93
+ else:
94
+ env["INDEX_TTS_VOICE"] = voice.get("voice_id")
95
+ return updates, env
96
+
97
+
98
+ def _same_setting(key, current, value):
99
+ if key.endswith(("_PATH", "_FILE", "VOICE_REF")):
100
+ return Path(current).expanduser().resolve() == Path(value).expanduser().resolve()
101
+ try:
102
+ return float(current) == float(value)
103
+ except ValueError:
104
+ return current == value
105
+
106
+
107
+ def _check_env_conflicts(env, environ):
108
+ for key, value in env.items():
109
+ current = environ.get(key, "").strip()
110
+ if current and not _same_setting(key, current, value):
111
+ raise BindingError(f"{key}={current} 与项目绑定的 {value} 冲突;删掉其中一处")
112
+
113
+
114
+ def _check_arg_conflicts(updates, args):
115
+ for key, value in updates.items():
116
+ current = getattr(args, key, None)
117
+ if key == "tts_provider" or current in (None, ""):
118
+ continue
119
+ if str(current) != str(value):
120
+ raise BindingError(f"--{key.replace('_', '-')} {current} 与项目绑定的 {value} 冲突")
121
+
122
+
123
+ def resolve_project(value, args, environ=None, *, include_voice=True) -> dict:
124
+ """Load and validate a project; return what to apply plus the lock's project block."""
125
+ environ = os.environ if environ is None else environ
126
+ path = project_path(value)
127
+ try:
128
+ project = json.loads(path.read_text(encoding="utf-8"))
129
+ except ValueError as exc:
130
+ raise BindingError(f"{path} 无法解析: {exc}") from None
131
+ if not isinstance(project, dict) or project.get("schema") != PROJECT_SCHEMA:
132
+ raise BindingError(f"{path} 的 schema 必须是 {PROJECT_SCHEMA}")
133
+ unknown = sorted(set(project) - PROJECT_KEYS)
134
+ bindings = project.get("bindings") or {}
135
+ unknown += sorted(f"bindings.{k}" for k in set(bindings) - set(BINDING_KINDS))
136
+ if unknown:
137
+ raise BindingError(f"{path} 有未知字段: {', '.join(unknown)}")
138
+ library_root = (path.parent / str(project.get("library", ""))).resolve()
139
+ if not (library_root / "library.json").is_file():
140
+ raise BindingError(f"库目录无效(缺少 library.json): {library_root}")
141
+ index, report = library_lib.scan_library(library_root)
142
+ resources = {r["id"]: r for r in index["resources"]}
143
+ templates = {f"{t['id']}@v{t['version']}": t for t in index["templates"]}
144
+ errors = {}
145
+ for issue in report.errors:
146
+ errors.setdefault(issue["path"], issue["message"])
147
+
148
+ def require_valid(entry, label):
149
+ rel = Path(entry["record"]).relative_to(library_root).as_posix()
150
+ if rel in errors:
151
+ raise BindingError(f"{label}: {errors[rel]}(先运行 library.py check)")
152
+
153
+ def template(kind):
154
+ ref = bindings.get(kind)
155
+ if not ref:
156
+ return None
157
+ entry = templates.get(ref)
158
+ if entry is None or entry["kind"] != kind:
159
+ raise BindingError(f"{kind} 模板 {ref} 不存在或无效(先运行 library.py check)")
160
+ if entry["status"] != "adopted":
161
+ raise BindingError(f"{kind} 模板 {ref} 的状态是 {entry['status']},只有 adopted 能绑定")
162
+ require_valid(entry, ref)
163
+ for _, rid in entry["refs"]:
164
+ if rid not in resources:
165
+ raise BindingError(f"{ref} 引用的资源 {rid} 不存在或无效(先运行 library.py check)")
166
+ require_valid(resources[rid], f"{ref} 引用的资源 {rid}")
167
+ return entry
168
+
169
+ def resource(kind):
170
+ rid = bindings.get(kind)
171
+ if not rid:
172
+ return None
173
+ entry = resources.get(rid)
174
+ if entry is None or entry["kind"] != kind:
175
+ raise BindingError(f"{kind} 资源 {rid} 不存在或无效(先运行 library.py check)")
176
+ require_valid(entry, f"{kind} 资源 {rid}")
177
+ return entry
178
+
179
+ env, updates, used_templates = {}, {}, []
180
+ subtitle = template("subtitle_style")
181
+ if subtitle:
182
+ env.update(_subtitle_env(subtitle, _record(subtitle), resources))
183
+ used_templates.append({"role": "subtitle_style", "id": subtitle["id"],
184
+ "version": subtitle["version"], "status": subtitle["status"],
185
+ "canvas": subtitle["canvas"]})
186
+ packaging = template("packaging")
187
+ if packaging:
188
+ used_templates.append({"role": "packaging", "id": packaging["id"],
189
+ "version": packaging["version"], "status": packaging["status"],
190
+ "canvas": packaging["canvas"]})
191
+ voice = resource("voice") if include_voice else None
192
+ if voice:
193
+ voice_updates, voice_env = _voice_updates(voice, args)
194
+ updates.update(voice_updates)
195
+ env.update(voice_env)
196
+ bgm = resource("bgm")
197
+ if bgm and getattr(args, "audio_mode", "narration") == "adopted-packet-copy":
198
+ raise BindingError("adopted-packet-copy 冻结原音轨,不能再绑定 BGM;去掉 bindings.bgm 或换声音模式")
199
+ if bgm:
200
+ env["BGM_PATH"] = bgm["files"][0]["path"]
201
+ material_env = environ.get("VIDEO_RECAP_MATERIAL_LIBRARY_DIR", "").strip()
202
+ if (material_env and not getattr(args, "material_library_dir", None)
203
+ and Path(material_env).expanduser().resolve() != library_root):
204
+ raise BindingError(
205
+ f"VIDEO_RECAP_MATERIAL_LIBRARY_DIR={material_env} 与项目的库 {library_root} 不是同一个目录;"
206
+ "素材库与资源库共用根目录,删掉其中一处"
207
+ )
208
+ _check_env_conflicts(env, environ)
209
+ _check_env_conflicts(
210
+ {name: updates[key] for key, name in (("mimo_tts_voice", "MIMO_TTS_VOICE"), ("voice_ref", "VOICE_REF"))
211
+ if key in updates},
212
+ environ,
213
+ )
214
+ _check_arg_conflicts(updates, args)
215
+ return {
216
+ "path": str(path),
217
+ "name": project.get("name", ""),
218
+ "library": str(library_root),
219
+ "env": env,
220
+ "arg_updates": updates,
221
+ "templates": used_templates,
222
+ "resources": [],
223
+ "packaging": packaging and {"record": _record(packaging), "resources": resources},
224
+ }
225
+
226
+
227
+ def apply_project(resolved, args, environ=None) -> None:
228
+ """Write the resolved settings where the stage skills will read them."""
229
+ environ = os.environ if environ is None else environ
230
+ for key, value in resolved["env"].items():
231
+ environ[key] = value
232
+ bound = set()
233
+ for key, value in resolved["arg_updates"].items():
234
+ if getattr(args, key, None) != value:
235
+ bound.add(key)
236
+ setattr(args, key, value)
237
+ if not getattr(args, "material_library_dir", None):
238
+ args.material_library_dir = resolved["library"]
239
+ bound.add("material_library_dir")
240
+ args._bound_from_project = frozenset(bound)
241
+ args.resolved_project = resolved
242
+
243
+
244
+ def check_canvas(resolved, width, height) -> None:
245
+ """Templates only apply to the canvas they were calibrated on."""
246
+ for template in resolved["templates"]:
247
+ canvas = template["canvas"]
248
+ if (canvas["width"], canvas["height"]) != (width, height):
249
+ raise BindingError(
250
+ f"{template['role']} 模板 {template['id']}@v{template['version']} 按 "
251
+ f"{canvas['width']}x{canvas['height']} 校准,成片画布是 {width}x{height};换一个同画幅的模板"
252
+ )
253
+
254
+
255
+ PACKAGING_LAYERS = "packaging_layers.json"
256
+ _WRITTEN_BY = "video-recap --project"
257
+
258
+
259
+ def sync_packaging_layers(work_dir, resolved) -> None:
260
+ """Write the bound packaging template's layers for video-assemble, or retire our old copy.
261
+
262
+ A caller-authored ``packaging_layers.json`` (no ``written_by`` marker) is left alone.
263
+ """
264
+ path = Path(work_dir) / PACKAGING_LAYERS
265
+ packaging = (resolved or {}).get("packaging")
266
+ if not packaging:
267
+ if path.exists():
268
+ try:
269
+ plan = json.loads(path.read_text(encoding="utf-8"))
270
+ except ValueError:
271
+ plan = None
272
+ ours = isinstance(plan, dict) and plan.get("written_by") == _WRITTEN_BY
273
+ if ours:
274
+ path.unlink()
275
+ return
276
+ record, resources = packaging["record"], packaging["resources"]
277
+ layers = [{"name": layer["name"],
278
+ "path": resources[layer["image"]["resource"]]["files"][0]["path"],
279
+ "rect": layer["rect"]}
280
+ for layer in record["params"]["layers"]]
281
+ path.write_text(json.dumps({
282
+ "written_by": _WRITTEN_BY,
283
+ "template": {"id": record["id"], "version": record["version"]},
284
+ "canvas": record["canvas"],
285
+ "layers": layers,
286
+ }, ensure_ascii=False, indent=2), encoding="utf-8")
@@ -0,0 +1 @@
1
+ """MiMo QC subpackage."""
@@ -4,7 +4,7 @@ import importlib.util
4
4
  from pathlib import Path
5
5
 
6
6
  _SPEC = importlib.util.spec_from_file_location(
7
- "video_recap_mimo_qc_lib", Path(__file__).with_name("lib.py")
7
+ "video_recap_mimo_qc_lib", Path(__file__).resolve().parents[1] / "lib.py"
8
8
  )
9
9
  _LIB = importlib.util.module_from_spec(_SPEC)
10
10
  _SPEC.loader.exec_module(_LIB)
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import hashlib
6
5
  import json
7
6
  import os
8
7
  from pathlib import Path
@@ -10,7 +9,7 @@ from typing import Any
10
9
  from collections.abc import Mapping, Sequence
11
10
 
12
11
  import qc_contract
13
- from mimo_qc_client import DEFAULT_CONFIG
12
+ from qc.mimo_client import DEFAULT_CONFIG
14
13
 
15
14
  _JSON_ARTIFACTS = (
16
15
  "narration.json",
@@ -48,14 +47,6 @@ _OPTIONAL_VISUAL_METADATA = (
48
47
  )
49
48
 
50
49
 
51
- def _stable_json(value: Any) -> str:
52
- return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
53
-
54
-
55
- def _fingerprint_value(value: Any) -> str:
56
- return hashlib.sha256(_stable_json(value).encode("utf-8")).hexdigest()
57
-
58
-
59
50
  def _load_json(path: Path) -> Any:
60
51
  return json.loads(path.read_text(encoding="utf-8"))
61
52
 
@@ -75,7 +66,7 @@ def _summarize(
75
66
  ) -> Any:
76
67
  """Keep request/report evidence bounded while retaining useful structure."""
77
68
  if depth >= 4:
78
- return {"type": type(value).__name__, "fingerprint": _fingerprint_value(value)}
69
+ return {"type": type(value).__name__, "omitted": True}
79
70
  if isinstance(value, Mapping):
80
71
  out = {
81
72
  str(key): _summarize(
@@ -110,11 +101,12 @@ def _collect_file(work_dir: Path, name: str) -> dict[str, Any] | None:
110
101
  kind, summary = "json", _summarize(_load_json(path))
111
102
  else:
112
103
  kind, summary = "text", _summarize(_read_text_sample(path))
104
+ stat = path.stat()
113
105
  return {
114
106
  "path": name,
115
107
  "kind": kind,
116
- "bytes": path.stat().st_size,
117
- "fingerprint": qc_contract.artifact_fingerprint(path),
108
+ "bytes": stat.st_size,
109
+ "mtime_ns": stat.st_mtime_ns,
118
110
  "summary": summary,
119
111
  }
120
112
 
@@ -128,21 +120,12 @@ def _collect_multi_source_asr(work_dir: Path) -> dict[str, Any]:
128
120
  manifest_path = work_dir / "multi_source_manifest.json"
129
121
  if not manifest_path.is_file():
130
122
  return {}
131
- root = work_dir.resolve(strict=False)
132
123
  collected = {}
133
124
  for source in _load_json(manifest_path)["sources"]:
134
- relative_dir = source["source_work_dir"]
135
- source_dir = (root / relative_dir).resolve(strict=False)
136
- if not source_dir.is_relative_to(root):
137
- continue
138
- name = _first_existing(source_dir, _SOURCE_ASR_ARTIFACTS)
125
+ relative_dir = source["source_work_dir"] # recap writes sources/<source_id>
126
+ name = _first_existing(work_dir / relative_dir, _SOURCE_ASR_ARTIFACTS)
139
127
  if name is not None:
140
- evidence_path = (source_dir / name).resolve(strict=False)
141
- if not evidence_path.is_relative_to(root):
142
- continue
143
- collected[source["source_id"]] = _collect_file(
144
- root, str(evidence_path.relative_to(root))
145
- )
128
+ collected[source["source_id"]] = _collect_file(work_dir, f"{relative_dir}/{name}")
146
129
  return collected
147
130
 
148
131
 
@@ -151,55 +134,38 @@ def _resolve_candidate(work_dir: Path, candidate: str | Path) -> Path:
151
134
  return path if path.is_absolute() else work_dir / path
152
135
 
153
136
 
154
- def _final_output_candidates(
137
+ def _final_output_path(
155
138
  work_dir: Path, final_output: str | Path | None
156
- ) -> list[tuple[Path, str]]:
157
- raw: list[str | Path] = []
158
- if final_output:
159
- raw.append(final_output)
160
- manifest_path = work_dir / "assembly_manifest.json"
161
- if manifest_path.is_file():
162
- raw.append(_load_json(manifest_path)["final_output"])
163
- raw.extend(("output.mp4", "recap.mp4", "final.mp4"))
164
- seen: set[str] = set()
165
- result = []
166
- for item in raw:
167
- path = _resolve_candidate(work_dir, item)
168
- key = str(path.resolve(strict=False))
169
- if key not in seen:
170
- seen.add(key)
171
- result.append((path, str(item)))
172
- return result
139
+ ) -> tuple[Path, str] | None:
140
+ """(path, display) of the final output: the caller's, else assembly_manifest.final_output
141
+ (video-assemble always writes it); None before the assembler has run."""
142
+ if final_output is None:
143
+ manifest_path = work_dir / "assembly_manifest.json"
144
+ if not manifest_path.is_file():
145
+ return None
146
+ final_output = _load_json(manifest_path)["final_output"]
147
+ return _resolve_candidate(work_dir, final_output), str(final_output)
173
148
 
174
149
 
175
150
  def _final_output_metadata(
176
151
  work_dir: Path, final_output: str | Path | None = None
177
152
  ) -> dict[str, Any]:
178
- outputs = []
179
- for path, display in _final_output_candidates(work_dir, final_output):
180
- item: dict[str, Any] = {"path": display, "exists": path.is_file()}
181
- if item["exists"]:
182
- item.update(
183
- {
184
- "bytes": path.stat().st_size,
185
- "fingerprint": qc_contract.artifact_fingerprint(path),
186
- }
187
- )
188
- outputs.append(item)
189
- return {"candidates": outputs}
153
+ resolved = _final_output_path(work_dir, final_output)
154
+ if resolved is None:
155
+ return {"candidates": []}
156
+ path, display = resolved
157
+ item: dict[str, Any] = {"path": display, "exists": path.is_file()}
158
+ if item["exists"]:
159
+ stat = path.stat()
160
+ item.update({"bytes": stat.st_size, "mtime_ns": stat.st_mtime_ns})
161
+ return {"candidates": [item]}
190
162
 
191
163
 
192
164
  def _existing_final_output(
193
165
  work_dir: Path, final_output: str | Path | None
194
166
  ) -> Path | None:
195
- return next(
196
- (
197
- path
198
- for path, _display in _final_output_candidates(work_dir, final_output)
199
- if path.is_file()
200
- ),
201
- None,
202
- )
167
+ resolved = _final_output_path(work_dir, final_output)
168
+ return resolved[0] if resolved is not None and resolved[0].is_file() else None
203
169
 
204
170
 
205
171
  def collect_evidence(
@@ -243,7 +209,6 @@ def collect_evidence(
243
209
  }
244
210
  if not evidence["source_asr"]:
245
211
  evidence["source_asr"] = _collect_multi_source_asr(root)
246
- evidence["fingerprint"] = _fingerprint_value(_cache_evidence(evidence))
247
212
  # The evidence goes into the MiMo request as well as the persisted report, so it is
248
213
  # redacted here, before either.
249
214
  return qc_contract.redact_secrets(evidence)
@@ -251,19 +216,21 @@ def collect_evidence(
251
216
 
252
217
  def _cache_file_group(group: Mapping[str, Any]) -> dict[str, Any]:
253
218
  return {
254
- name: {key: item[key] for key in ("kind", "bytes", "fingerprint")}
219
+ name: {key: item[key] for key in ("kind", "bytes", "mtime_ns")}
255
220
  for name, item in sorted(group.items())
256
221
  }
257
222
 
258
223
 
259
224
  def _cache_evidence(evidence: Mapping[str, Any]) -> dict[str, Any]:
225
+ """The per-file identities ({kind, bytes, mtime_ns}) a cached stage report was built
226
+ from; work_dir is excluded so moving the directory is still a cache hit."""
260
227
  return {
261
228
  "artifacts": _cache_file_group(evidence["artifacts"]),
262
229
  "source_asr": _cache_file_group(evidence["source_asr"]),
263
230
  "generated_subtitles": _cache_file_group(evidence["generated_subtitles"]),
264
231
  "visual_metadata": _cache_file_group(evidence["visual_metadata"]),
265
232
  "final_output": [
266
- {key: item[key] for key in ("exists", "bytes", "fingerprint") if key in item}
233
+ {key: item[key] for key in ("exists", "bytes", "mtime_ns") if key in item}
267
234
  for item in evidence["final_output"]["candidates"]
268
235
  ],
269
236
  }
@@ -8,9 +8,9 @@ from typing import Any
8
8
  from collections.abc import Mapping
9
9
 
10
10
  import qc_contract
11
- from mimo_qc_evidence import _fingerprint_value, _summarize, safe_mimo_config
12
- from mimo_qc_payload import _strip_json_fence
13
- from mimo_qc_contract import (
11
+ from qc.mimo_evidence import _summarize, safe_mimo_config
12
+ from qc.mimo_payload import _strip_json_fence
13
+ from qc.mimo_contract import (
14
14
  ARTIFACT_NAME,
15
15
  DEFAULT_STAGE,
16
16
  MAX_MESSAGE_CHARS,
@@ -146,10 +146,6 @@ def normalize_observations(
146
146
  **(raw_evidence if isinstance(raw_evidence, Mapping) else {}),
147
147
  "model": model_used,
148
148
  "config": cfg,
149
- "fingerprint": {
150
- "evidence": payload["evidence_fingerprint"],
151
- "observation": _fingerprint_value(observation),
152
- },
153
149
  }
154
150
  location = obs.get("location")
155
151
  findings.append(
@@ -172,10 +168,6 @@ def normalize_observations(
172
168
  location=location if isinstance(location, Mapping) else {},
173
169
  evidence=evidence,
174
170
  model_used=model_used,
175
- artifact_fingerprints={
176
- "payload": payload["payload_fingerprint"],
177
- "evidence": payload["evidence_fingerprint"],
178
- },
179
171
  next_action="human_review",
180
172
  decision_reason=message,
181
173
  )
@@ -6,8 +6,8 @@ import json
6
6
  from typing import Any
7
7
  from collections.abc import Mapping, Sequence
8
8
 
9
- from mimo_qc_evidence import _fingerprint_value, safe_mimo_config
10
- from mimo_qc_contract import ARTIFACT_NAME, DEFAULT_STAGE
9
+ from qc.mimo_evidence import safe_mimo_config
10
+ from qc.mimo_contract import ARTIFACT_NAME, DEFAULT_STAGE
11
11
 
12
12
 
13
13
  def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str, Any]:
@@ -16,12 +16,11 @@ def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str,
16
16
  ``collect_evidence`` already bounds every artifact independently. The additional
17
17
  summarization here therefore needs enough depth to retain the narration/ASR/TTS
18
18
  scalars nested inside those summaries. A shallow second pass used to replace the
19
- values with fingerprints, which made live QC invent missing-script and failed-TTS
19
+ values with opaque placeholders, which made live QC invent missing-script and failed-TTS
20
20
  findings from evidence it could no longer read.
21
21
  """
22
22
  semantic = dict(evidence)
23
23
  semantic.pop("work_dir", None)
24
- semantic.pop("fingerprint", None)
25
24
  semantic["evidence_roles"] = {
26
25
  "narration.json": (
27
26
  "Planned recap voiceover on the OUTPUT timeline; judge its factual and temporal "
@@ -104,7 +103,7 @@ def build_payload(
104
103
  "instructions": (
105
104
  "你是 video-recap 的建议性质量审阅器。结合解说、剪辑计划、字幕/ASR、TTS、"
106
105
  "组装元数据和抽样画面,指出语义或审美问题。只返回主观观察,不做自动修复,"
107
- "只根据可见的实际字段判断,不得从文件字节数、指纹、截断或省略标记推断内容缺失;"
106
+ "只根据可见的实际字段判断,不得从文件字节数、截断或省略标记推断内容缺失;"
108
107
  "source_asr 是源素材原声证据,不是生成后 TTS 的逐字转录;generated_subtitles 是本轮生成字幕,"
109
108
  "二者角色不可混淆,解说改写与源台词不一致本身不是问题;"
110
109
  "空 failures 表示没有失败,storyboard 的 labels_burned 仅表示诊断图上的时间标签,"
@@ -131,9 +130,7 @@ def build_payload(
131
130
  '"semantic|aesthetic|sampled","evidence":{...}}]}。最多 12 条。'
132
131
  ),
133
132
  "evidence": _semantic_evidence(evidence, stage=stage),
134
- "evidence_fingerprint": evidence["fingerprint"],
135
133
  }
136
- payload["payload_fingerprint"] = _fingerprint_value(payload)
137
134
  return payload
138
135
 
139
136