@oh-story/dsh 0.1.9 → 0.1.11

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 (529) hide show
  1. package/README.md +14 -14
  2. package/lib/client.js +209 -73
  3. package/lib/client.js.map +3 -3
  4. package/lib/drama/manifest.json +248 -168
  5. package/lib/drama/skills/short-drama/SKILL.md +16 -4
  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/creator_markdown_check.py +14 -0
  10. package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
  11. package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
  12. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
  13. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +10 -0
  14. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +6 -9
  15. package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
  16. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  17. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
  18. package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
  19. package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
  20. package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
  21. package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
  22. package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
  23. package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
  24. 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
  25. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
  26. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
  27. 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
  28. package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
  29. package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
  30. package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
  31. package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
  32. package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
  33. package/lib/drama/skills/short-drama-edit/SKILL.md +65 -44
  34. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
  35. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
  36. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
  37. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
  38. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
  39. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
  40. package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
  41. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
  42. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
  43. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
  44. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
  45. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
  46. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
  47. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
  48. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
  49. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
  50. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +6 -4
  51. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +26 -67
  52. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +31 -56
  53. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +42 -78
  54. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +44 -92
  55. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +14 -16
  56. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1272 -125
  57. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +113 -9
  58. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
  59. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
  60. package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
  61. package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
  62. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
  63. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
  64. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
  65. package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
  66. package/lib/drama/skills/short-drama-produce/SKILL.md +5 -1
  67. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +14 -20
  68. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +8 -26
  69. package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  70. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +142 -1
  71. package/lib/drama/skills/short-drama-review/SKILL.md +4 -5
  72. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +6 -19
  73. package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
  74. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
  75. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
  76. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +24 -35
  77. package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
  78. package/lib/drama/skills/short-drama-storyboard/SKILL.md +23 -11
  79. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
  80. package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  81. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
  82. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +139 -34
  83. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +5 -3
  84. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +15 -12
  85. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +29 -0
  86. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
  87. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
  88. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +52 -39
  89. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -21
  90. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +9 -23
  91. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +14 -26
  92. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
  93. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +2 -3
  94. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +34 -9
  95. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -2
  96. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
  97. package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
  98. package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
  99. package/lib/drama/skills/short-drama-write/SKILL.md +10 -7
  100. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
  101. package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
  102. package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
  103. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
  104. package/lib/index.js +346 -98
  105. package/lib/index.js.map +4 -4
  106. package/lib/novel-to-game/README.md +299 -59
  107. package/lib/novel-to-game/README_ZH.md +299 -56
  108. package/lib/novel-to-game/VERSION +1 -1
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
  118. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
  119. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
  120. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
  121. package/lib/novel-to-game/manifest.json +77 -87
  122. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +7 -12
  123. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
  124. package/lib/novel-to-game/skills/game-build/SKILL.md +13 -14
  125. package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
  126. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
  127. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
  128. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
  129. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
  130. package/lib/novel-to-game/skills/game-concept/SKILL.md +13 -15
  131. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
  132. package/lib/novel-to-game/skills/game-qa/SKILL.md +6 -7
  133. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +9 -5
  134. package/lib/novel-to-game/skills/game-world-design/SKILL.md +12 -16
  135. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
  136. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
  137. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
  138. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +4 -10
  139. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
  140. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +9 -21
  141. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -4
  142. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +5 -8
  143. package/lib/oh-story/manifest.json +607 -327
  144. package/lib/oh-story/roles/chapter-extractor.md +177 -237
  145. package/lib/oh-story/roles/character-designer.md +1 -2
  146. package/lib/oh-story/roles/consistency-checker.md +15 -13
  147. package/lib/oh-story/roles/narrative-writer.md +36 -38
  148. package/lib/oh-story/roles/story-architect.md +9 -61
  149. package/lib/oh-story/roles/story-explorer.md +20 -125
  150. package/lib/oh-story/roles/story-researcher.md +0 -1
  151. package/lib/oh-story/skills/story/SKILL.md +15 -9
  152. package/lib/oh-story/skills/story/VERSION +1 -1
  153. package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
  154. package/lib/oh-story/skills/story/references/author-memory.md +46 -94
  155. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
  156. package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
  157. package/lib/oh-story/skills/story-deslop/SKILL.md +59 -73
  158. package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
  159. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +12 -11
  160. package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
  161. package/lib/oh-story/skills/story-deslop/references/author-memory.md +46 -94
  162. package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
  163. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +22 -5
  164. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
  165. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
  166. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
  167. package/lib/oh-story/skills/story-import/SKILL.md +64 -584
  168. package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
  169. package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
  170. package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
  171. package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
  172. package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
  173. package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
  174. package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
  175. package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
  176. package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
  177. package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
  178. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +98 -92
  179. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
  180. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
  181. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +15 -11
  182. package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
  183. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +132 -27
  184. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +478 -15
  185. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +182 -23
  186. package/lib/oh-story/skills/story-long-analyze/SKILL.md +81 -270
  187. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +252 -0
  188. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +3 -3
  189. package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
  190. package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
  191. package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
  192. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +138 -64
  193. package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
  194. package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
  195. package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
  196. package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
  197. package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
  198. package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
  199. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +55 -128
  200. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +6 -6
  201. package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
  202. package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
  203. package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
  204. package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
  205. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1145 -0
  206. package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
  207. package/lib/oh-story/skills/story-long-scan/SKILL.md +45 -194
  208. package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
  209. package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
  210. package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
  211. package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
  212. package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
  213. package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
  214. package/lib/oh-story/skills/story-long-write/SKILL.md +49 -81
  215. package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
  216. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +12 -11
  217. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +24 -202
  218. package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
  219. package/lib/oh-story/skills/story-long-write/references/author-memory.md +46 -94
  220. package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
  221. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +7 -11
  222. package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
  223. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
  224. package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
  225. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +4 -0
  226. package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
  227. package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
  228. package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
  229. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  230. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +7 -15
  231. package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
  232. package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
  233. package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
  234. package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
  235. package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
  236. package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
  237. package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
  238. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
  239. package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
  240. package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
  241. package/lib/oh-story/skills/story-long-write/references/project-files.md +19 -13
  242. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +13 -5
  243. package/lib/oh-story/skills/story-long-write/references/reference-index.md +5 -6
  244. package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
  245. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
  246. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  247. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +4 -4
  248. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
  249. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +15 -11
  250. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +81 -121
  251. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +41 -139
  252. package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
  253. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +57 -67
  254. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -253
  255. package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
  256. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +6 -4
  257. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +19 -65
  258. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
  259. package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
  260. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +226 -25
  261. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
  262. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +132 -27
  263. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
  264. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +7 -4
  265. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +304 -46
  266. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +478 -15
  267. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +182 -23
  268. package/lib/oh-story/skills/story-review/SKILL.md +31 -302
  269. package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
  270. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +12 -11
  271. package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
  272. package/lib/oh-story/skills/story-review/references/author-memory.md +46 -94
  273. package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
  274. package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
  275. package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
  276. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +4 -0
  277. package/lib/oh-story/skills/story-review/references/review-quality.md +6 -11
  278. package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
  279. package/lib/oh-story/skills/story-review/references/solo.md +52 -0
  280. package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
  281. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
  282. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +15 -11
  283. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
  284. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
  285. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +478 -15
  286. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +182 -23
  287. package/lib/oh-story/skills/story-setup/SKILL.md +71 -452
  288. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
  289. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +12 -11
  290. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
  291. package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
  292. package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
  293. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +22 -5
  294. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +4 -0
  295. package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
  296. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
  297. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
  298. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  299. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
  300. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
  301. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
  302. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
  303. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
  304. package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
  305. package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
  306. package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
  307. package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
  308. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
  309. package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
  310. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
  311. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  312. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +4 -4
  313. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +19 -65
  314. package/lib/oh-story/skills/story-setup/references/diagnostics.md +4 -4
  315. package/lib/oh-story/skills/story-short-analyze/SKILL.md +56 -110
  316. package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
  317. package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
  318. package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
  319. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
  320. package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
  321. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
  322. package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
  323. package/lib/oh-story/skills/story-short-analyze/references/stage4-6-reversal-summary.md +611 -0
  324. package/lib/oh-story/skills/story-short-scan/SKILL.md +35 -65
  325. package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
  326. package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
  327. package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
  328. package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
  329. package/lib/oh-story/skills/story-short-write/SKILL.md +23 -175
  330. package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
  331. package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
  332. package/lib/oh-story/skills/story-short-write/references/author-memory.md +46 -94
  333. package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
  334. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
  335. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +4 -0
  336. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
  337. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
  338. package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
  339. package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -47
  340. package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
  341. package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
  342. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
  343. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
  344. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
  345. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
  346. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
  347. package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
  348. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +21 -13
  349. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +116 -0
  350. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +99 -0
  351. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
  352. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
  353. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
  354. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
  355. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  356. package/lib/types/client/file-activity.d.ts +19 -1
  357. package/lib/types/client/file-activity.d.ts.map +1 -1
  358. package/lib/types/client/index.d.ts.map +1 -1
  359. package/lib/types/client/production-prompts.d.ts +7 -0
  360. package/lib/types/client/production-prompts.d.ts.map +1 -1
  361. package/lib/types/client/production-runtime.d.ts +16 -0
  362. package/lib/types/client/production-runtime.d.ts.map +1 -1
  363. package/lib/types/client/video-studio.d.ts.map +1 -1
  364. package/lib/types/drama-adapters.d.ts +5 -3
  365. package/lib/types/drama-adapters.d.ts.map +1 -1
  366. package/lib/types/native-hooks.d.ts +53 -1
  367. package/lib/types/native-hooks.d.ts.map +1 -1
  368. package/lib/types/role-identity.d.ts +17 -0
  369. package/lib/types/role-identity.d.ts.map +1 -0
  370. package/lib/types/role-tool.d.ts.map +1 -1
  371. package/lib/types/skill-provider.d.ts.map +1 -1
  372. package/lib/types/video-project.d.ts.map +1 -1
  373. package/lib/types/workspace-route.d.ts.map +1 -1
  374. package/lib/video-recap/CHANGELOG.md +60 -1
  375. package/lib/video-recap/README.en.md +348 -137
  376. package/lib/video-recap/README.md +340 -136
  377. package/lib/video-recap/manifest.json +406 -251
  378. package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
  379. package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
  380. package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
  381. package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
  382. package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
  383. package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
  384. package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
  385. package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
  386. package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
  387. package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
  388. package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
  389. package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
  390. package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
  391. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
  392. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
  393. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
  394. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
  395. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
  396. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
  397. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
  398. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
  399. package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
  400. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
  401. package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
  402. package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
  403. package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
  404. package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
  405. package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
  406. package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
  407. package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
  408. package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
  409. package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
  410. package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
  411. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
  412. package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
  413. package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
  414. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
  415. package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
  416. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
  417. package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
  418. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
  419. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
  420. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
  421. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
  422. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
  423. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
  424. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
  425. package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
  426. package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
  427. package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
  428. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
  429. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
  430. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
  431. package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
  432. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
  433. package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
  434. package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
  435. package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
  436. package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
  437. package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
  438. package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
  439. package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
  440. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
  441. package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
  442. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
  443. package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
  444. package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
  445. package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
  446. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
  447. package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
  448. package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
  449. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
  450. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
  451. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
  452. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
  453. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
  454. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
  455. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
  456. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
  457. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
  458. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
  459. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
  460. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
  461. package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
  462. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
  463. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
  464. package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
  465. package/lib/video-recap/skills/video-script/SKILL.md +16 -13
  466. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
  467. package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
  468. package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
  469. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
  470. package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
  471. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
  472. package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
  473. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
  474. package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
  475. package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
  476. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
  477. package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
  478. package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
  479. package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
  480. package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
  481. package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
  482. package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
  483. package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
  484. package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
  485. package/lib/video-recap/skills/{video-script/scripts/agent_brief.py → video-understanding/scripts/briefing/builder.py} +38 -35
  486. package/lib/video-recap/skills/video-understanding/scripts/{brief_context.py → briefing/context.py} +4 -35
  487. package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
  488. package/lib/video-recap/skills/{video-script/scripts/brief_timeline.py → video-understanding/scripts/briefing/timeline.py} +5 -6
  489. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
  490. package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
  491. package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
  492. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
  493. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
  494. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
  495. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
  496. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
  497. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
  498. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
  499. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
  500. package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
  501. package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
  502. package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
  503. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
  504. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
  505. package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
  506. package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
  507. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
  508. package/package.json +45 -45
  509. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -58
  510. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
  511. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -38
  512. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -530
  513. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -559
  514. package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
  515. package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
  516. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +0 -678
  517. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
  518. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
  519. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
  520. package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
  521. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
  522. package/lib/video-recap/skills/video-script/scripts/brief_context.py +0 -348
  523. package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
  524. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +0 -399
  525. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
  526. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +0 -327
  527. /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
  528. /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
  529. /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
@@ -4,8 +4,10 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  import argparse
7
+ import hashlib
7
8
  import importlib.util
8
9
  import json
10
+ import re
9
11
  import shutil
10
12
  import subprocess
11
13
  import sys
@@ -30,6 +32,9 @@ for _name in dir(core):
30
32
 
31
33
  CHAPTER_CHECK_SCHEMA = "story-chapter-check/v1"
32
34
  CHAPTER_ERROR_SCHEMA = "story-chapter-error/v1"
35
+ # 书内工作目录:分组 segment、writer prompt 留档、逐章事务 JSON 的唯一落点。
36
+ # 与 build_writer_prompt.py 同一口径;提交成功后整目录删除,失败时原样保留供重跑。
37
+ WORK_ROOT = (".story", "work")
33
38
 
34
39
 
35
40
  class CliArgumentError(ValueError):
@@ -72,72 +77,142 @@ def _read_json_object(path: Path, label: str) -> dict[str, Any]:
72
77
  return value
73
78
 
74
79
 
75
- def _project_files(project: Path, chapter: int) -> tuple[Path, Path, int]:
76
- root = project.resolve()
77
- outline = find_chapter_file(root / "大纲", chapter, outline=True)
78
- body = find_chapter_file(root / "正文", chapter, outline=False)
79
- try:
80
- target = target_from_outline(outline.read_text(encoding="utf-8"))
81
- except (OSError, UnicodeError) as exc:
82
- raise WordcountError(f"unable to read outline: {exc}") from exc
83
- return outline, body, target
80
+ def chapter_work_dir(project: Path, chapter: int) -> Path:
81
+ width = max(3, len(str(chapter)))
82
+ return project.resolve().joinpath(*WORK_ROOT, f"第{chapter:0{width}d}章")
83
+
84
+
85
+ def _remove_chapter_work_dir(project: Path, chapter: int) -> str | None:
86
+ """Delete this chapter's scratch directory after a successful commit.
87
+
88
+ Only the per-chapter directory is removed; `.story/work` and `.story` are
89
+ pruned only when empty, so book-level author memory under `.story/` stays.
90
+ """
91
+ path = chapter_work_dir(project, chapter)
92
+ if path.is_symlink():
93
+ path.unlink()
94
+ elif path.is_dir():
95
+ shutil.rmtree(path)
96
+ else:
97
+ return None
98
+ for parent in (path.parent, path.parent.parent):
99
+ try:
100
+ parent.rmdir()
101
+ except OSError:
102
+ break
103
+ return "/".join((*WORK_ROOT, path.name))
84
104
 
85
105
 
86
- def _json_findings(output: str) -> list[dict[str, Any]]:
106
+ def _project_files(
107
+ project: Path, chapter: int, author_range: Any = None,
108
+ ) -> tuple[Path, Path, int, dict[str, int] | None]:
109
+ return read_project_chapter(project, chapter, author_range)
110
+
111
+
112
+ def _json_findings(output: str) -> list[dict[str, Any]] | None:
113
+ """检测器 --json 输出里的 findings;输出不是预期 JSON 时返回 None(按工具故障处理,不当作无问题)。"""
87
114
  try:
88
- value = json.loads(output or "{}")
115
+ value = json.loads(output)
89
116
  except json.JSONDecodeError:
90
- return []
91
- findings = value.get("findings", []) if isinstance(value, dict) else []
92
- return findings if isinstance(findings, list) else []
117
+ return None
118
+ findings = value.get("findings") if isinstance(value, dict) else None
119
+ if not isinstance(findings, list) or not all(isinstance(row, dict) for row in findings):
120
+ return None
121
+ return findings
122
+
123
+
124
+ NODE_REQUIRED = (
125
+ "质量检查(AI 句式、退化、标点、细纲照搬)要用 Node.js 运行,本机找不到 node。"
126
+ "安装 Node.js 18 或更高版本并确认 `node --version` 可用后重跑本命令;装好之前本章不能提交。"
127
+ )
128
+
129
+
130
+ def _semantic_advisories(advisories: list[dict[str, Any]]) -> int:
131
+ # check-ai-patterns 给每条 finding 标 review=semantic|mechanical,退化检测器的在入口统一标 mechanical;
132
+ # 其余来源没标的按语义类算,宁多勿漏。
133
+ return sum(1 for row in advisories if row.get("review") != "mechanical")
134
+
135
+
136
+ # 与 hooks(JS core DESLOP_SKIP_MARKER / codex py / bash guard)同一语法:注释内只认 ASCII 空格/Tab,
137
+ # 冒号全角半角都认;裸写不在注释里的不算。
138
+ DESLOP_SKIP = re.compile(r"<!--[ \t]*去味[ \t]*(:|:)[ \t]*跳过[ \t]*-->")
139
+
140
+
141
+ def deslop_skipped(body: Path) -> bool:
142
+ """作者显式说本章不去味时,标题行下的 <!-- 去味:跳过 --> 豁免 AI 句式;与 hooks 同一个首 6 行窗口。"""
143
+ head = re.split(r"\r?\n", body.read_text(encoding="utf-8-sig"))[:6]
144
+ return bool(DESLOP_SKIP.search("\n".join(head)))
93
145
 
94
146
 
95
147
  def check_blocking_quality(outline: Path, body: Path) -> dict[str, Any]:
148
+ """Run the deterministic prose checks.
149
+
150
+ status: pass(无 blocking)/ fail(有 blocking 正文问题)/ unavailable(检测工具缺失或崩溃,
151
+ 不是正文问题,见 tool_errors)。
152
+ """
96
153
  node = shutil.which("node")
97
154
  if node is None:
98
155
  return {
99
- "status": "fail",
100
- "blocking_findings": [{"type": "TOOL_UNAVAILABLE", "message": "node is required for quality checks"}],
156
+ "status": "unavailable",
157
+ "blocking_findings": [],
101
158
  "advisories": [],
159
+ "semantic_advisories": 0,
160
+ "tool_errors": [{"type": "TOOL_UNAVAILABLE", "tool": "node", "message": NODE_REQUIRED}],
102
161
  }
103
162
  root = Path(__file__).parent
163
+ exempt = deslop_skipped(body)
104
164
  blocking: list[dict[str, Any]] = []
105
165
  advisories: list[dict[str, Any]] = []
166
+ tool_errors: list[dict[str, Any]] = []
106
167
  for name, script in (
107
168
  ("ai-pattern", "check-ai-patterns.js"),
108
169
  ("degeneration", "check-degeneration.js"),
109
170
  ):
110
171
  path = root / script
111
172
  if not path.is_file():
112
- blocking.append({"type": "TOOL_UNAVAILABLE", "message": f"missing {script}"})
173
+ tool_errors.append({"type": "TOOL_UNAVAILABLE", "tool": script, "message": f"missing {script}"})
113
174
  continue
114
175
  completed = subprocess.run(
115
176
  [node, str(path), "--check", "--json", "--fail-on=blocking", str(body)],
116
177
  text=True, encoding="utf-8", capture_output=True, check=False,
117
178
  )
118
179
  findings = _json_findings(completed.stdout)
180
+ if findings is None or completed.returncode not in {0, 1}:
181
+ tool_errors.append({"type": "TOOL_ERROR", "tool": script, "source": name,
182
+ "message": completed.stderr.strip() or f"{script} exited {completed.returncode} without JSON findings"})
183
+ continue
119
184
  for finding in findings:
120
185
  row = {"source": name, **finding}
186
+ if name == "degeneration":
187
+ # 退化类(复读、截断、拒绝语、工程词)不是 AI 味,不计入触发去 AI 味审查的语义条数。
188
+ row.setdefault("review", "mechanical")
189
+ if exempt and name == "ai-pattern" and finding.get("severity") == "blocking":
190
+ # 豁免只管 AI 句式;退化类(复读、截断、拒绝语、工程词)照常必须修。
191
+ advisories.append({**row, "severity": "advisory", "exempted": "去味:跳过"})
192
+ continue
121
193
  (blocking if finding.get("severity") == "blocking" else advisories).append(row)
122
- if completed.returncode not in {0, 1}:
123
- blocking.append({"type": "TOOL_ERROR", "source": name, "message": completed.stderr.strip()})
124
194
 
125
195
  punctuation = root / "normalize-punctuation.js"
126
196
  if not punctuation.is_file():
127
- blocking.append({"type": "TOOL_UNAVAILABLE", "message": "missing normalize-punctuation.js"})
197
+ tool_errors.append({"type": "TOOL_UNAVAILABLE", "tool": punctuation.name,
198
+ "message": "missing normalize-punctuation.js"})
128
199
  else:
129
200
  completed = subprocess.run(
130
201
  [node, str(punctuation), "--check", str(body)],
131
202
  text=True, encoding="utf-8", capture_output=True, check=False,
132
203
  )
133
- if completed.returncode != 0:
204
+ if completed.returncode == 1:
134
205
  blocking.append(
135
206
  {"type": "PUNCTUATION_NOT_NORMALIZED", "message": (completed.stdout or completed.stderr).strip()}
136
207
  )
208
+ elif completed.returncode != 0:
209
+ tool_errors.append({"type": "TOOL_ERROR", "tool": punctuation.name,
210
+ "message": completed.stderr.strip() or f"exited {completed.returncode}"})
137
211
 
138
212
  outline_copy = root / "check-outline-copy.js"
139
213
  if not outline_copy.is_file():
140
- blocking.append({"type": "TOOL_UNAVAILABLE", "message": "missing check-outline-copy.js"})
214
+ tool_errors.append({"type": "TOOL_UNAVAILABLE", "tool": outline_copy.name,
215
+ "message": "missing check-outline-copy.js"})
141
216
  else:
142
217
  completed = subprocess.run(
143
218
  [node, str(outline_copy), "--outline", str(outline), str(body)],
@@ -145,28 +220,72 @@ def check_blocking_quality(outline: Path, body: Path) -> dict[str, Any]:
145
220
  )
146
221
  if completed.returncode != 0:
147
222
  advisories.append(
148
- {"source": "outline-copy", "type": "OUTLINE_COPY_REVIEW", "message": completed.stdout.strip()}
223
+ {"source": "outline-copy", "type": "OUTLINE_COPY_REVIEW", "review": "semantic",
224
+ "message": completed.stdout.strip()}
149
225
  )
150
226
  return {
151
- "status": "fail" if blocking else "pass",
227
+ "status": "unavailable" if tool_errors else ("fail" if blocking else "pass"),
152
228
  "blocking_findings": blocking,
153
229
  "advisories": advisories,
230
+ "semantic_advisories": _semantic_advisories(advisories),
231
+ "tool_errors": tool_errors,
154
232
  }
155
233
 
156
234
 
157
- def chapter_check(project: Path, chapter: int) -> dict[str, Any]:
158
- outline, body_path, target = _project_files(project, chapter)
235
+ # chapter check 顶层 status 与退出码:一眼分清「能提交」「要作者定」「正文要改」「工具坏了」。
236
+ CHECK_EXIT_CODES = {"ready": 0, "needs_decision": 0, "blocked": 1, "invalid": 1, "tool_unavailable": 3}
237
+ ERROR_EXIT_CODES = {"TOOL_UNAVAILABLE": 3}
238
+ ACCEPT_FLOOR_RATIO = 0.5
239
+ BASELINE_FILE = "over_length_baseline.json"
240
+
241
+
242
+ class ChapterRefused(WordcountError):
243
+ def __init__(self, code: str, message: str) -> None:
244
+ super().__init__(message)
245
+ self.code = code
246
+
247
+
248
+ def _check_status(length: dict[str, Any], quality: dict[str, Any]) -> str:
249
+ if quality["status"] == "unavailable":
250
+ return "tool_unavailable"
251
+ if quality["status"] != "pass":
252
+ return "blocked"
253
+ if length["status"] == "invalid":
254
+ return "invalid"
255
+ return "ready" if length["status"] in {"internal_pass", "borderline"} else "needs_decision"
256
+
257
+
258
+ def _body_digest(path: Path) -> str:
259
+ return hashlib.sha256(path.read_bytes()).hexdigest()
260
+
261
+
262
+ def _record_over_baseline(project: Path, chapter: int, body_path: Path, actual: int) -> None:
263
+ """第一次看到 over 时记下原稿指纹:接受超长前要证明那一次净删压缩真的做过。"""
264
+ path = chapter_work_dir(project, chapter) / BASELINE_FILE
265
+ try:
266
+ previous = json.loads(path.read_text(encoding="utf-8"))
267
+ except (OSError, UnicodeError, json.JSONDecodeError):
268
+ previous = None
269
+ # 已有基线且这次更短:是压缩后的复检,保留基线;更长则是重写出的新稿,重新记。
270
+ if isinstance(previous, dict) and isinstance(previous.get("actual"), int) and actual <= previous["actual"]:
271
+ return
272
+ path.parent.mkdir(parents=True, exist_ok=True)
273
+ path.write_text(json.dumps({"body_sha256": _body_digest(body_path), "actual": actual}), encoding="utf-8")
274
+
275
+
276
+ def chapter_check(project: Path, chapter: int, author_range: Any = None) -> dict[str, Any]:
277
+ outline, body_path, target, effective_range = _project_files(project, chapter, author_range)
159
278
  try:
160
279
  body = body_path.read_text(encoding="utf-8")
161
280
  except (OSError, UnicodeError) as exc:
162
281
  raise WordcountError(f"unable to read body: {exc}") from exc
163
- length = evaluate_wordcount(body, target, chapter=chapter)
282
+ length = evaluate_wordcount(body, target, chapter=chapter, author_range=effective_range)
164
283
  quality = check_blocking_quality(outline, body_path)
165
- length_ok = length["status"] in {"internal_pass", "borderline"}
166
- if quality["status"] != "pass" or length["status"] == "invalid":
167
- actions: list[str] = []
168
- elif length_ok:
284
+ status = _check_status(length, quality)
285
+ if status == "ready":
169
286
  actions = ["commit"]
287
+ elif status != "needs_decision":
288
+ actions: list[str] = []
170
289
  elif length["status"] == "over":
171
290
  actions = ["compress-once", "accept-current-length", "revise-outline-or-target", "discard"]
172
291
  else:
@@ -187,8 +306,10 @@ def chapter_check(project: Path, chapter: int) -> dict[str, Any]:
187
306
  "max": actual - length["user_band"]["min"],
188
307
  },
189
308
  }
309
+ _record_over_baseline(project, chapter, body_path, actual)
190
310
  return {
191
311
  "schema": CHAPTER_CHECK_SCHEMA,
312
+ "status": status,
192
313
  "chapter": chapter,
193
314
  "length": length,
194
315
  "quality": quality,
@@ -200,26 +321,108 @@ def chapter_check(project: Path, chapter: int) -> dict[str, Any]:
200
321
  }
201
322
 
202
323
 
203
- def chapter_commit(project: Path, chapter: int, input_path: Path, *, accept_current_length: bool) -> dict[str, Any]:
204
- checked = chapter_check(project, chapter)
205
- require(checked["quality"]["status"] == "pass", "blocking quality findings must be fixed before commit")
324
+ def fix_punctuation(project: Path, chapter: int, author_range: Any = None) -> bool:
325
+ """Run the deterministic punctuation normalizer on the chapter body in place.
326
+
327
+ Folding it into `chapter check --fix-punctuation` lets the parent flow close a
328
+ chapter with one call instead of running each cleanup script separately.
329
+ """
330
+ _, body, _, _ = _project_files(project, chapter, author_range)
331
+ node = shutil.which("node")
332
+ script = Path(__file__).with_name("normalize-punctuation.js")
333
+ if node is None or not script.is_file():
334
+ return False # chapter check reports the missing tool in quality.tool_errors
335
+ before = body.read_bytes()
336
+ subprocess.run([node, str(script), str(body)], text=True, encoding="utf-8",
337
+ capture_output=True, check=False)
338
+ return body.read_bytes() != before
339
+
340
+
341
+ def _require_acceptable_length(
342
+ project: Path, chapter: int, checked: dict[str, Any], *, force: bool, author_range: Any = None,
343
+ ) -> None:
344
+ """接受当前长度的两道底线:欠长不低于目标一半;超长先做过一次净删压缩(删掉原稿到作者范围上限
345
+ 差额的至少一半)。作者明确拍板用 --force 越过。"""
346
+ if force:
347
+ return
348
+ length = checked["length"]
349
+ if length["status"] == "under" and length["actual"] < length["target"] * ACCEPT_FLOOR_RATIO:
350
+ raise ChapterRefused(
351
+ "BELOW_ACCEPT_FLOOR",
352
+ f"本章 {length['actual']} 字,不到字数目标 {length['target']} 的一半,不能直接按当前长度接受;"
353
+ "先补细纲或改字数目标,作者明确要这个长度时加 --force",
354
+ )
355
+ if length["status"] == "over":
356
+ path = chapter_work_dir(project, chapter) / BASELINE_FILE
357
+ try:
358
+ baseline = json.loads(path.read_text(encoding="utf-8"))
359
+ except (OSError, UnicodeError, json.JSONDecodeError):
360
+ baseline = None
361
+ _, body_path, _, _ = _project_files(project, chapter, author_range)
362
+ valid = isinstance(baseline, dict) and isinstance(baseline.get("actual"), int)
363
+ # 压缩要真删:至少删掉原稿到作者字数范围上限差额的一半(至少 1 字),删一两个字不算做过。
364
+ ceiling = None
365
+ if valid:
366
+ gap = baseline["actual"] - length["user_band"]["max"]
367
+ ceiling = baseline["actual"] - max(1, (gap + 1) // 2)
368
+ compressed = (
369
+ valid
370
+ and baseline.get("body_sha256") != _body_digest(body_path)
371
+ and length["actual"] <= ceiling
372
+ )
373
+ if not compressed:
374
+ detail = (f"(原稿 {baseline['actual']} 字,压缩后要不超过 {ceiling} 字,现在 {length['actual']} 字)"
375
+ if valid else "")
376
+ raise ChapterRefused(
377
+ "COMPRESSION_REQUIRED",
378
+ f"超长章节先按 compress-once 做一次净删压缩并重跑 chapter check{detail},仍超长再接受当前长度;"
379
+ "作者明确不压缩时加 --force",
380
+ )
381
+
382
+
383
+ def chapter_commit(
384
+ project: Path, chapter: int, input_path: Path, *, accept_current_length: bool,
385
+ author_range: Any = None, force: bool = False,
386
+ ) -> dict[str, Any]:
387
+ checked = chapter_check(project, chapter, author_range)
388
+ if checked["status"] == "tool_unavailable":
389
+ raise ChapterRefused(
390
+ "TOOL_UNAVAILABLE",
391
+ ";".join(item["message"] for item in checked["quality"]["tool_errors"]) or NODE_REQUIRED,
392
+ )
393
+ if checked["quality"]["status"] != "pass":
394
+ raise ChapterRefused("QUALITY_BLOCKED", "blocking quality findings must be fixed before commit")
206
395
  length_status = checked["length"]["status"]
207
396
  in_user_band = length_status in {"internal_pass", "borderline"}
208
397
  if accept_current_length:
209
398
  require(length_status in {"under", "over"}, "accept-current-length requires a valid out-of-band chapter")
399
+ _require_acceptable_length(project, chapter, checked, force=force, author_range=author_range)
210
400
  resolution = "accepted_current_length"
211
401
  else:
212
- require(in_user_band, "chapter length is outside the user band; use accept-current-length or revise it")
402
+ if not in_user_band:
403
+ raise ChapterRefused(
404
+ "LENGTH_OUT_OF_BAND",
405
+ "chapter length is outside the user band; use accept-current-length or revise it",
406
+ )
213
407
  resolution = "within_user_band"
214
408
  document = _read_json_object(input_path, "tracking transaction")
215
409
  require(document.get("chapter") == chapter, "tracking transaction chapter does not match command")
216
410
  require("wordcount" not in document, "tracking transaction must not provide wordcount")
217
- document["wordcount"] = build_project_wordcount_record(project, chapter, resolution=resolution)
411
+ document["wordcount"] = build_project_wordcount_record(
412
+ project, chapter, resolution=resolution, author_range=author_range,
413
+ )
218
414
  tracking = _tracking_module()
219
415
  state = _tracking_call(tracking.apply_transaction, project, document)
220
416
  checked["tracking_committed"] = state["last_committed_chapter"] >= chapter
221
417
  checked["next_chapter_started"] = state["last_committed_chapter"] > chapter
222
418
  checked["wordcount"] = state["wordcount_records"].get(str(chapter))
419
+ checked["mode"] = document.get("mode")
420
+ # The commit is already durable; a cleanup failure is reported, never turned into a failed commit.
421
+ try:
422
+ checked["work_dir_removed"] = _remove_chapter_work_dir(project, chapter)
423
+ except OSError as exc:
424
+ checked["work_dir_removed"] = None
425
+ checked["work_dir_cleanup_error"] = str(exc)
223
426
  return checked
224
427
 
225
428
 
@@ -234,19 +437,47 @@ def _build_parser() -> StructuredArgumentParser:
234
437
  subparser.add_argument("--chapter")
235
438
  subparser.add_argument("--case-id")
236
439
  if command != "measure":
237
- subparser.add_argument("--target", required=True)
440
+ subparser.add_argument("--target", required=command == "check")
441
+ _add_range_arguments(subparser)
442
+ if command == "checkpoint":
443
+ subparser.add_argument("--project", type=Path,
444
+ help="书目录:与 --chapter 一起从细纲读字数目标(未给 --target 时)和「字数范围」")
238
445
  chapter = commands.add_parser("chapter")
239
446
  chapter_commands = chapter.add_subparsers(dest="chapter_command", required=True)
240
447
  for command in ("check", "commit", "accept-current-length"):
241
448
  subparser = chapter_commands.add_parser(command)
242
449
  subparser.add_argument("--project", type=Path, required=True)
243
450
  subparser.add_argument("--chapter", type=int, required=True)
451
+ _add_range_arguments(subparser)
244
452
  if command != "check":
245
453
  subparser.add_argument("--input", type=Path, required=True)
454
+ else:
455
+ subparser.add_argument("--fix-punctuation", action="store_true",
456
+ help="先就地整理正文标点,再做检查")
457
+ if command == "accept-current-length":
458
+ subparser.add_argument("--force", action="store_true",
459
+ help="作者明确拍板:越过欠长一半下限与超长先压缩一次的要求")
246
460
  return parser
247
461
 
248
462
 
249
- def _wordcount_command(args: argparse.Namespace) -> int:
463
+ def _add_range_arguments(subparser: argparse.ArgumentParser) -> None:
464
+ subparser.add_argument("--min-chars", type=int, help="作者本轮给定的字数下限(须与 --max-chars 同给),优先于细纲字数范围与默认 ±15%%")
465
+ subparser.add_argument("--max-chars", type=int, help="作者本轮给定的字数上限")
466
+
467
+
468
+ def _cli_author_range(args: argparse.Namespace) -> dict[str, int] | None:
469
+ low, high = getattr(args, "min_chars", None), getattr(args, "max_chars", None)
470
+ if low is None and high is None:
471
+ return None
472
+ if low is None or high is None:
473
+ raise CliArgumentError("--min-chars and --max-chars must be given together")
474
+ try:
475
+ return normalize_author_range({"min": low, "max": high})
476
+ except WordcountError as exc:
477
+ raise CliArgumentError(str(exc)) from exc
478
+
479
+
480
+ def _wordcount_command(args: argparse.Namespace, author_range: dict[str, int] | None) -> int:
250
481
  try:
251
482
  body = Path(args.file).read_text(encoding="utf-8")
252
483
  except (OSError, UnicodeError):
@@ -261,12 +492,31 @@ def _wordcount_command(args: argparse.Namespace) -> int:
261
492
  if args.wordcount_command == "measure":
262
493
  result = measure_wordcount(body, chapter=args.chapter, case_id=args.case_id)
263
494
  elif args.wordcount_command == "checkpoint":
495
+ target = args.target
496
+ if args.project is not None:
497
+ # 与 chapter check 同一优先级:命令行上下限 > 细纲「字数范围」 > 默认带。
498
+ try:
499
+ chapter = int(args.chapter or "")
500
+ _, outline = read_chapter_outline(args.project, chapter)
501
+ if target is None:
502
+ target = target_from_outline(outline)
503
+ author_range = effective_author_range(outline, author_range)
504
+ except (ValueError, WordcountError):
505
+ _json_line(invalid_wordcount_result("INVALID_OUTLINE", chapter=args.chapter, case_id=args.case_id))
506
+ return 2
507
+ if target is None:
508
+ _json_line(invalid_wordcount_result("INVALID_ARGUMENT", chapter=args.chapter, case_id="--target or --project is required"))
509
+ return 2
264
510
  try:
265
- result = checkpoint_wordcount(body, args.target, chapter=args.chapter, case_id=args.case_id)
511
+ result = checkpoint_wordcount(
512
+ body, target, chapter=args.chapter, case_id=args.case_id, author_range=author_range,
513
+ )
266
514
  except WordcountError:
267
515
  result = invalid_wordcount_result("INVALID_TARGET", chapter=args.chapter, case_id=args.case_id)
268
516
  else:
269
- result = evaluate_wordcount(body, args.target, chapter=args.chapter, case_id=args.case_id)
517
+ result = evaluate_wordcount(
518
+ body, args.target, chapter=args.chapter, case_id=args.case_id, author_range=author_range,
519
+ )
270
520
  _json_line(result)
271
521
  return 2 if result.get("status") == "invalid" else 0
272
522
 
@@ -275,28 +525,36 @@ def main(argv: Sequence[str] | None = None) -> int:
275
525
  raw = list(argv) if argv is not None else sys.argv[1:]
276
526
  try:
277
527
  args = _build_parser().parse_args(raw)
528
+ author_range = _cli_author_range(args)
278
529
  except CliArgumentError as exc:
279
530
  payload = (
280
- {"schema": CHAPTER_ERROR_SCHEMA, "error_code": "INVALID_ARGUMENT", "message": str(exc)}
531
+ {"schema": CHAPTER_ERROR_SCHEMA, "status": "error", "error_code": "INVALID_ARGUMENT", "message": str(exc)}
281
532
  if raw[:1] == ["chapter"] else invalid_wordcount_result("INVALID_ARGUMENT", case_id=str(exc))
282
533
  )
283
534
  _json_line(payload)
284
535
  return 2
285
536
  if args.command == "wordcount":
286
- return _wordcount_command(args)
537
+ return _wordcount_command(args, author_range)
287
538
  try:
288
539
  if args.chapter_command == "check":
289
- result = chapter_check(args.project, args.chapter)
540
+ fixed = fix_punctuation(args.project, args.chapter, author_range) if args.fix_punctuation else None
541
+ result = chapter_check(args.project, args.chapter, author_range)
542
+ if fixed is not None:
543
+ result["punctuation_fixed"] = fixed
290
544
  else:
291
545
  result = chapter_commit(
292
546
  args.project, args.chapter, args.input,
293
547
  accept_current_length=args.chapter_command == "accept-current-length",
548
+ author_range=author_range, force=getattr(args, "force", False),
294
549
  )
295
550
  except (WordcountError, OSError, UnicodeError) as exc:
296
- _json_line({"schema": CHAPTER_ERROR_SCHEMA, "error_code": "CHECK_FAILED", "message": str(exc)})
297
- return 2
551
+ code = exc.code if isinstance(exc, ChapterRefused) else "CHECK_FAILED"
552
+ _json_line({"schema": CHAPTER_ERROR_SCHEMA, "status": "error", "error_code": code, "message": str(exc)})
553
+ return ERROR_EXIT_CODES.get(code, 2)
298
554
  _json_line(result)
299
- return 2 if result["quality"]["status"] != "pass" else 0
555
+ if args.chapter_command != "check":
556
+ return 0
557
+ return CHECK_EXIT_CODES[result["status"]]
300
558
 
301
559
 
302
560
  if __name__ == "__main__":