@oh-story/dsh 0.1.8 → 0.1.9

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 (272) hide show
  1. package/README.md +11 -11
  2. package/lib/client.js +35 -14
  3. package/lib/client.js.map +2 -2
  4. package/lib/drama/manifest.json +165 -69
  5. package/lib/drama/skills/short-drama/SKILL.md +5 -1
  6. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +126 -9
  7. package/lib/drama/skills/short-drama/scripts/project_tool.py +11 -0
  8. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +18 -0
  9. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +10 -1
  10. package/lib/drama/skills/short-drama-edit/SKILL.md +148 -0
  11. package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
  12. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
  13. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
  14. package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  15. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  16. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  17. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  18. package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  19. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  20. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  21. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  22. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +81 -0
  23. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +58 -0
  24. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +96 -0
  25. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +113 -0
  26. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +55 -0
  27. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1041 -0
  28. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +456 -0
  29. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
  30. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
  31. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
  32. package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
  33. package/lib/drama/skills/short-drama-produce/SKILL.md +24 -3
  34. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +28 -0
  35. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +24 -5
  36. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
  37. package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
  38. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
  39. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +57 -2
  40. package/lib/drama/skills/short-drama-review/SKILL.md +3 -0
  41. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +20 -0
  42. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +36 -0
  43. package/lib/drama/skills/short-drama-storyboard/SKILL.md +7 -0
  44. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
  45. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +39 -0
  46. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
  47. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -1
  48. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +101 -6
  49. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +7 -1
  50. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +35 -0
  51. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +3 -1
  52. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +13 -3
  53. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +35 -4
  54. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -1
  55. package/lib/drama/skills/short-drama-write/SKILL.md +13 -2
  56. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
  57. package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
  58. package/lib/index.js +4 -2
  59. package/lib/index.js.map +2 -2
  60. package/lib/novel-to-game/VERSION +1 -1
  61. package/lib/novel-to-game/manifest.json +28 -28
  62. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +6 -27
  63. package/lib/novel-to-game/skills/game-build/SKILL.md +12 -29
  64. package/lib/novel-to-game/skills/game-concept/SKILL.md +10 -26
  65. package/lib/novel-to-game/skills/game-qa/SKILL.md +5 -15
  66. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +1 -1
  67. package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -28
  68. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +1 -2
  69. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +6 -9
  70. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +6 -11
  71. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -4
  72. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +1 -3
  73. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +3 -5
  74. package/lib/oh-story/manifest.json +292 -172
  75. package/lib/oh-story/roles/character-designer.md +1 -1
  76. package/lib/oh-story/roles/narrative-writer.md +56 -242
  77. package/lib/oh-story/roles/story-architect.md +1 -1
  78. package/lib/oh-story/skills/story/SKILL.md +1 -1
  79. package/lib/oh-story/skills/story/VERSION +1 -1
  80. package/lib/oh-story/skills/story/references/author-memory.md +10 -8
  81. package/lib/oh-story/skills/story-deslop/SKILL.md +20 -182
  82. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +19 -47
  83. package/lib/oh-story/skills/story-deslop/references/author-memory.md +10 -8
  84. package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
  85. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
  86. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
  87. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
  88. package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
  89. package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
  90. package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
  91. package/lib/oh-story/skills/story-import/SKILL.md +2 -2
  92. package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
  93. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
  94. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +2 -29
  95. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +176 -12
  96. package/lib/oh-story/skills/story-long-analyze/SKILL.md +1 -1
  97. package/lib/oh-story/skills/story-long-write/SKILL.md +20 -193
  98. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +19 -47
  99. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +62 -7
  100. package/lib/oh-story/skills/story-long-write/references/author-memory.md +10 -8
  101. package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
  102. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
  103. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +6 -4
  104. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +2 -2
  105. package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
  106. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
  107. package/lib/oh-story/skills/story-long-write/references/project-files.md +108 -0
  108. package/lib/oh-story/skills/story-long-write/references/reference-index.md +87 -0
  109. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
  110. package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
  111. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  112. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
  113. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
  114. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +2 -29
  115. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +51 -52
  116. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +8 -7
  117. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +36 -15
  118. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
  119. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +69 -2
  120. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +410 -0
  121. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
  122. package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
  123. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +176 -12
  124. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
  125. package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
  126. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +283 -0
  127. package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
  128. package/lib/oh-story/skills/story-review/SKILL.md +13 -3
  129. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +19 -47
  130. package/lib/oh-story/skills/story-review/references/author-memory.md +10 -8
  131. package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
  132. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +6 -4
  133. package/lib/oh-story/skills/story-review/references/review-quality.md +2 -2
  134. package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
  135. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
  136. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +2 -29
  137. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
  138. package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
  139. package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
  140. package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
  141. package/lib/oh-story/skills/story-setup/SKILL.md +17 -10
  142. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +19 -47
  143. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
  144. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
  145. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +6 -4
  146. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
  147. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
  148. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
  149. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  150. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
  151. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +69 -2
  152. package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
  153. package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
  154. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
  155. package/lib/oh-story/skills/story-short-write/SKILL.md +18 -186
  156. package/lib/oh-story/skills/story-short-write/references/author-memory.md +10 -8
  157. package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
  158. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
  159. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +6 -4
  160. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +2 -2
  161. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
  162. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +3 -3
  163. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +3 -3
  164. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +3 -3
  165. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
  166. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +4 -4
  167. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
  168. package/lib/oh-story/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +3 -3
  169. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
  170. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
  171. package/lib/oh-story/skills/story-short-write/references/reference-index.md +53 -0
  172. package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
  173. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
  174. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
  175. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
  176. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
  177. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +77 -0
  178. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
  179. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
  180. package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
  181. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
  182. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
  183. package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
  184. package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
  185. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  186. package/lib/types/client/index.d.ts.map +1 -1
  187. package/lib/types/client/production-prompts.d.ts.map +1 -1
  188. package/lib/types/client/production-runtime.d.ts +17 -0
  189. package/lib/types/client/production-runtime.d.ts.map +1 -1
  190. package/lib/types/skill-provider.d.ts.map +1 -1
  191. package/lib/video-recap/CHANGELOG.md +31 -1
  192. package/lib/video-recap/manifest.json +162 -162
  193. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
  194. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
  195. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
  196. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
  197. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
  198. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
  199. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
  200. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
  201. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
  202. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
  203. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
  204. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
  205. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
  206. package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
  207. package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
  208. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
  209. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
  210. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
  211. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
  212. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
  213. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
  214. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
  215. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
  216. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
  217. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
  218. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
  219. package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
  220. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
  221. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
  222. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
  223. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
  224. package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
  225. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
  226. package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
  227. package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
  228. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
  229. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
  230. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
  231. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
  232. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
  233. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
  234. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
  235. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
  236. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
  237. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
  238. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
  239. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
  240. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
  241. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
  242. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
  243. package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
  244. package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
  245. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
  246. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
  247. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
  248. package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
  249. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
  250. package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
  251. package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
  252. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
  253. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
  254. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
  255. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
  256. package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
  257. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
  258. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
  259. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
  260. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
  261. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
  262. package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
  263. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
  264. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
  265. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
  266. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
  267. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
  268. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
  269. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
  270. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
  271. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
  272. package/package.json +41 -41
@@ -22,7 +22,7 @@ const path = require('path')
22
22
  const FIELDS = [
23
23
  '核心事件', '字数目标', '字数口径', '阶段位置', '单元ID/位置', '目标情绪',
24
24
  '主角目标/关键选择', '章节定位', '本章结构公式', '章首钩子', '爽点',
25
- '本章禁止提前释放', '契约风险',
25
+ '本章标价', '闭环状态', '本章禁止提前释放', '写手自由区', '契约风险',
26
26
  ]
27
27
  const SUBSECTIONS = ['内容概括', '情节安排', '人物关系和出场顺序', '情节细化']
28
28
  const FIVE_ACT = ['起因', '发展', '转折', '高潮', '结尾']
@@ -46,11 +46,11 @@ function readUtf8(file) {
46
46
  }
47
47
  }
48
48
 
49
- function makeCheck(id, ok, file, evidence, expected, repair) {
49
+ function makeCheck(id, ok, file, evidence, expected, repair, severity = 'blocking') {
50
50
  return {
51
51
  id,
52
52
  ok,
53
- severity: 'blocking',
53
+ severity,
54
54
  file,
55
55
  evidence,
56
56
  expected,
@@ -65,7 +65,47 @@ function parseTableRow(line) {
65
65
  return trimmed.slice(1, -1).split('|').map((cell) => cell.replace(/\*\*/g, '').replace(/`/g, '').trim())
66
66
  }
67
67
 
68
- function verify(file) {
68
+ function resolveBookRoot(file, projectRoot) {
69
+ if (projectRoot) return path.resolve(projectRoot)
70
+ const parent = path.dirname(path.resolve(file))
71
+ if (path.basename(parent) === '大纲') return path.dirname(parent)
72
+ return null
73
+ }
74
+
75
+ // 项目引用须写目录路径;裸文件名可能指向 skill reference,不猜测其归属。
76
+ const REF_DIR_PREFIX = /^(?:设定|大纲|追踪|正文|读者笔记|对标)\//
77
+
78
+ function checkSettingRefs(text, name, file, projectRoot) {
79
+ const bookRoot = resolveBookRoot(file, projectRoot)
80
+ if (!bookRoot) {
81
+ return makeCheck('outline.setting-refs-exist', true, name, '未能定位书目录(细纲不在 大纲/ 下且未传 --project),跳过', '细纲引用的项目内文件真实存在', '无需修复。')
82
+ }
83
+ const refs = new Set()
84
+ const pattern = /`([^`\n]+?\.md)[^`\n]*`/g
85
+ let match
86
+ while ((match = pattern.exec(text)) !== null) {
87
+ const token = match[1].trim().replace(/\\/g, '/')
88
+ if (token.includes('{') || token.includes('}')) continue
89
+ if (REF_DIR_PREFIX.test(token) || /^\.{1,2}\//.test(token)) refs.add(token)
90
+ }
91
+ const missing = []
92
+ for (const token of refs) {
93
+ const base = /^\.{1,2}\//.test(token) ? path.dirname(path.resolve(file)) : bookRoot
94
+ try {
95
+ if (!fs.statSync(path.resolve(base, token)).isFile()) missing.push(token)
96
+ } catch { missing.push(token) }
97
+ }
98
+ return makeCheck(
99
+ 'outline.setting-refs-exist',
100
+ missing.length === 0,
101
+ name,
102
+ missing.length ? `引用的路径不是可用文件:${missing.join('、')}` : `${refs.size} 处项目内引用全部存在`,
103
+ '细纲引用的每个项目内 .md 文件都真实存在——不许把槽位指向查无实据的权威文件',
104
+ '修正引用路径;或按「创作自主权分级」先把缺的设定落档(B 级直接补、C 级进《供给单》提案),再在细纲引用。'
105
+ )
106
+ }
107
+
108
+ function verify(file, projectRoot = null) {
69
109
  const name = path.basename(file)
70
110
  const read = readUtf8(file)
71
111
  const checks = []
@@ -133,21 +173,82 @@ function verify(file) {
133
173
  let header = null
134
174
  for (const line of lines) {
135
175
  const cells = parseTableRow(line)
136
- if (cells && cells.length === 4 && PLOT_HEADER_FIRST.test(cells[0])) {
176
+ // 四列是旧表(# / 情节点 / 功能标签 / 执行边界),五列是现行模板(中间多一列「分辨率」)。
177
+ // 两种都收:功能标签固定在第 3 列,执行边界一律取最后一列,旧细纲不因加列而失效。
178
+ if (cells && (cells.length === 4 || cells.length === 5) && PLOT_HEADER_FIRST.test(cells[0])) {
137
179
  header = cells
138
180
  break
139
181
  }
140
182
  }
141
- const headerOk = Boolean(header) && header[2].includes('功能标签') && header[3].includes('执行边界')
183
+ const headerOk = Boolean(header)
184
+ && header[2].includes('功能标签')
185
+ && header[header.length - 1].includes('执行边界')
186
+ && (header.length === 4 || header[3].includes('分辨率'))
142
187
  checks.push(makeCheck(
143
188
  'outline.plotpoint-table',
144
189
  headerOk,
145
190
  name,
146
- header ? `表头:${header.join(' | ')}` : '未找到 | # | 情节点 | 功能标签 | 执行边界 | 表头',
147
- '情节细化使用四列表格:# / 情节点(谁做了什么) / 功能标签 / 执行边界',
148
- '只把情节点序列改成四列表格,逐点补功能标签与执行边界;不增删情节点本身。'
191
+ header ? `表头:${header.join(' | ')}` : '未找到 | # | 情节点 | 功能标签 | [分辨率] | 执行边界 | 表头',
192
+ '情节细化使用五列表格:# / 情节点(谁做了什么) / 功能标签 / 分辨率 / 执行边界(四列旧表仍收,但新建细纲按五列模板走)',
193
+ '只把情节点序列改成五列表格,逐点补功能标签、分辨率与执行边界;不增删情节点本身。'
149
194
  ))
150
195
 
196
+ // 「放」半边:五列表的执行边界必须至少一个点解除限制(模板既有要求),
197
+ // 全是禁令的情节点表实测会把整章压成同一个温度(015 章十一个点全「不许」)。
198
+ if (headerOk && header.length === 5) {
199
+ const rows = []
200
+ for (const line of lines) {
201
+ const cells = parseTableRow(line)
202
+ if (cells && cells.length === 5 && /^\d+$/.test(cells[0])) rows.push(cells)
203
+ }
204
+ if (rows.length) {
205
+ const released = rows.filter((cells) => {
206
+ const match = cells[4].match(/(?:^|[。;;\s])放\s*[::]([^]*?)(?=(?:禁|放)\s*[::]|$)/)
207
+ if (!match) return false
208
+ const value = match[1].replace(/[\s。;;,,、\[\]【】]/g, '')
209
+ return value.length > 0 && !/^(?:无|暂无|待补充|待定|无缺口)$/.test(value)
210
+ })
211
+ checks.push(makeCheck(
212
+ 'outline.plotpoint-release',
213
+ released.length > 0,
214
+ name,
215
+ `${rows.length} 个情节点里 ${released.length} 个写了「放」`,
216
+ '执行边界写成「禁+放」两半,每章至少一个点写了「放」——放行是解除限制,不是义务',
217
+ '给最该出彩的一两个点补「放」半边(谁可以出声、谁可以失态、允不允许当场标价),不改情节点本身。'
218
+ ))
219
+ if (released.length > 0 && rows.length >= 3 && released.length * 3 < rows.length) {
220
+ checks.push(makeCheck(
221
+ 'outline.plotpoint-release-ratio',
222
+ false,
223
+ name,
224
+ `${rows.length} 个情节点里只有 ${released.length} 个写了「放」(不足三分之一)`,
225
+ '建议至少三分之一的情节点写「放」半边,避免整章只剩禁令',
226
+ '按人工判断酌情补「放」;本项不阻断。',
227
+ 'advisory'
228
+ ))
229
+ }
230
+ }
231
+ }
232
+
233
+ // 章级「禁止提前释放」只写本章特有的三五条;条目过多多半是把卷级常任禁忌逐章复读了。
234
+ const releaseMatch = text.match(/^\s*[-*+]\s*\*{0,2}本章禁止提前释放\*{0,2}\s*[::]\s*(.*)$/m)
235
+ if (releaseMatch) {
236
+ const items = releaseMatch[1].split(/[、;;,,\/]/).map((s) => s.trim()).filter(Boolean)
237
+ if (items.length > 5) {
238
+ checks.push(makeCheck(
239
+ 'outline.release-brevity',
240
+ false,
241
+ name,
242
+ `本章禁止提前释放列了 ${items.length} 条`,
243
+ '章级只写本章特有的三五条;卷级常任禁忌写在卷纲「本卷禁碰的终局底牌」,不逐章复读',
244
+ '把与卷纲常任重复的条目删掉,只留本章特有;本项不阻断。',
245
+ 'advisory'
246
+ ))
247
+ }
248
+ }
249
+
250
+ checks.push(checkSettingRefs(text, name, file, projectRoot))
251
+
151
252
  const targetMatch = text.match(/字数目标\s*[::]\s*(?:约\s*)?([\d,,]+)/)
152
253
  const target = targetMatch ? Number(targetMatch[1].replace(/[,,]/g, '')) : null
153
254
  const caliberOk = new RegExp(`字数口径\\s*[::]\\s*${CALIBER}`).test(text)
@@ -164,7 +265,8 @@ function verify(file) {
164
265
  }
165
266
 
166
267
  function report(file, checks) {
167
- const failures = checks.filter((check) => !check.ok)
268
+ const failures = checks.filter((check) => !check.ok && check.severity === 'blocking')
269
+ const advisories = checks.filter((check) => !check.ok && check.severity === 'advisory')
168
270
  return {
169
271
  schema_version: 1,
170
272
  verifier: 'story-long-write.outline-contract',
@@ -172,6 +274,7 @@ function report(file, checks) {
172
274
  ok: failures.length === 0,
173
275
  checks,
174
276
  failures,
277
+ advisories,
175
278
  repair_scope: failures.map((failure) => ({
176
279
  id: failure.id,
177
280
  file: failure.file,
@@ -200,13 +303,65 @@ function resolveChapter(project, chapter) {
200
303
  return { file: path.join(dir, hit) }
201
304
  }
202
305
 
306
+ // --supply:批末验证《供给单》已落卷纲——定位单元卡块,确认其中有「供给自查」小节。
307
+ function verifySupply(volumeFile, unitId) {
308
+ const read = readUtf8(volumeFile)
309
+ if (!read.ok) {
310
+ return { schema_version: 1, verifier: 'story-long-write.outline-supply', file: path.resolve(volumeFile), unit: unitId, ok: false, evidence: read.error || '卷纲文件为空' }
311
+ }
312
+ // 只检查目标单元内的标题,不把正文提及或代码示例当成小节。
313
+ let fence = null
314
+ const lines = read.text.split(/\r?\n/).map((line) => {
315
+ const marker = line.match(/^\s{0,3}(`{3,}|~{3,})/)
316
+ if (marker) {
317
+ if (!fence) fence = marker[1]
318
+ else if (marker[1][0] === fence[0] && marker[1].length >= fence.length) fence = null
319
+ return ''
320
+ }
321
+ return fence ? '' : line.replace(/\*\*/g, '')
322
+ })
323
+ const headings = []
324
+ for (let index = 0; index < lines.length; index++) {
325
+ const heading = lines[index].match(/^(#{1,6})\s+(.+)$/)
326
+ if (heading) headings.push({ index, level: heading[1].length, title: heading[2] })
327
+ }
328
+ const card = headings.find((heading) => {
329
+ const id = heading.title.match(/剧情单元\s+([A-Za-z][\w-]*)/)
330
+ if (id) return id[1] === unitId
331
+ const end = headings.find((next) => next.index > heading.index)?.index ?? lines.length
332
+ return lines.slice(heading.index + 1, end).some((line) => {
333
+ const field = line.match(/^\s*[-*+]\s*单元ID\s*[::]\s*([A-Za-z][\w-]*)\s*$/)
334
+ return field?.[1] === unitId
335
+ })
336
+ })
337
+ const end = card ? (headings.find((next) => next.index > card.index && next.level <= card.level)?.index ?? lines.length) : 0
338
+ const ok = Boolean(card) && headings.some((heading) => heading.index > card.index && heading.index < end && /^供给自查(?:\s|[((]|$)/.test(heading.title))
339
+ if (!card) {
340
+ return { schema_version: 1, verifier: 'story-long-write.outline-supply', file: path.resolve(volumeFile), unit: unitId, ok: false, evidence: `卷纲中未找到剧情单元 ${unitId}` }
341
+ }
342
+ return {
343
+ schema_version: 1,
344
+ verifier: 'story-long-write.outline-supply',
345
+ file: path.resolve(volumeFile),
346
+ unit: unitId,
347
+ ok,
348
+ evidence: ok ? '单元卡含「供给自查」小节' : `剧情单元 ${unitId} 的卡内没有「供给自查」小节——每批出细纲前须产出《供给单》(含「无缺口」情形),见 workflow-setup.md「按剧情批出细纲」步骤 3`,
349
+ }
350
+ }
351
+
203
352
  function parseArgs(argv) {
204
353
  const files = []
205
354
  let project = null
206
355
  let chapter = null
356
+ let supply = null
207
357
  for (let index = 0; index < argv.length; index++) {
208
358
  const arg = argv[index]
209
359
  if (arg === '--json') continue
360
+ if (arg === '--supply') {
361
+ if (index + 2 >= argv.length) return null
362
+ supply = { volumeFile: argv[++index], unitId: argv[++index] }
363
+ continue
364
+ }
210
365
  if (arg === '--project' || arg === '--chapter') {
211
366
  if (index + 1 >= argv.length || argv[index + 1].startsWith('--')) return null
212
367
  const value = argv[++index]
@@ -217,6 +372,10 @@ function parseArgs(argv) {
217
372
  if (arg.startsWith('--')) return null
218
373
  files.push(arg)
219
374
  }
375
+ if (supply) {
376
+ if (project || chapter || files.length) return null
377
+ return { supply }
378
+ }
220
379
  if (project || chapter) {
221
380
  if (!project || !chapter || files.length || !/^\d+$/.test(chapter)) return null
222
381
  return { project, chapter }
@@ -228,9 +387,14 @@ function parseArgs(argv) {
228
387
  function main(argv) {
229
388
  const parsed = parseArgs(argv)
230
389
  if (!parsed) {
231
- process.stderr.write('用法: node scripts/check-outline-contract.js --json <细纲路径...> | --json --project <书目录> --chapter N\n')
390
+ process.stderr.write('用法: node scripts/check-outline-contract.js --json <细纲路径...> | --json --project <书目录> --chapter N | --json --supply <卷纲路径> <单元ID>\n')
232
391
  return 2
233
392
  }
393
+ if (parsed.supply) {
394
+ const supplyReport = verifySupply(parsed.supply.volumeFile, parsed.supply.unitId)
395
+ process.stdout.write(`${JSON.stringify(supplyReport, null, 2)}\n`)
396
+ return supplyReport.ok ? 0 : 1
397
+ }
234
398
  let targets = parsed.files
235
399
  if (!targets) {
236
400
  const resolved = resolveChapter(parsed.project, parsed.chapter)
@@ -240,7 +404,7 @@ function main(argv) {
240
404
  }
241
405
  targets = [resolved.file]
242
406
  }
243
- const reports = targets.map((file) => verify(file))
407
+ const reports = targets.map((file) => verify(file, parsed.project || null))
244
408
  const ok = reports.every((entry) => entry.ok)
245
409
  process.stdout.write(`${JSON.stringify(reports.length === 1 ? reports[0] : reports, null, 2)}\n`)
246
410
  return ok ? 0 : 1
@@ -31,8 +31,9 @@
31
31
  * 收尾复扫用 `正文/第XXX章_*.md` 这类通配传多章时,多出来的正文不能被当成细纲吞掉
32
32
  * ——那会让首个文件比错对象、其余文件根本不检,静默退 0 报「干净」。
33
33
  *
34
- * 退出码:0 = 干净或无法判定(缺细纲/非分章正文);1 = 有重合待复核。
35
- * 无发现时完全静默,不污染上下文。
34
+ * 退出码:0 = 干净或未自动发现细纲而跳过;1 = 有重合待复核;
35
+ * 2 = 参数错误、输入不可读(含已发现的细纲)或非预期异常。
36
+ * 干净时静默;自动发现不到细纲时明确报告跳过,避免与「已检查且干净」混淆。
36
37
  */
37
38
 
38
39
  'use strict'
@@ -42,19 +43,59 @@ const path = require('path')
42
43
  const MIN_RUN = 16 // 判定阈值:连续重合 >15 字,即 >=16
43
44
  const REPORT_TOP = 8 // 最多列出的片段数
44
45
 
45
- function read(p) {
46
+ function readTextFile(p) {
46
47
  try {
47
- return fs.readFileSync(p, 'utf8').replace(/^/, '')
48
- } catch {
49
- return null
48
+ const stat = fs.statSync(p)
49
+ if (!stat.isFile()) return { ok: false, reason: '不是普通文件' }
50
+ return { ok: true, text: fs.readFileSync(p, 'utf8').replace(/^/, '') }
51
+ } catch (error) {
52
+ if (error && error.code === 'ENOENT') return { ok: false, reason: '不存在' }
53
+ if (error && error.code) return { ok: false, reason: `无法读取(${error.code})` }
54
+ return { ok: false, reason: `无法读取:${error instanceof Error ? error.message : String(error)}` }
50
55
  }
51
56
  }
52
57
 
58
+ function reportInputError(kind, file, reason) {
59
+ process.stderr.write(`错误: 无法读取${kind} "${file}":${reason}。\n`)
60
+ }
61
+
62
+ function reportUsageError(message) {
63
+ process.stderr.write(`错误: ${message}。\n`)
64
+ process.stderr.write('用法: node check-outline-copy.js [--outline <细纲路径>] <正文路径...>\n')
65
+ return 2
66
+ }
67
+
53
68
  /** 只留汉字——剥掉标点/加粗/【】后比对,防止细纲标注造成假阴性 */
54
69
  function hanOnly(s) {
55
70
  return s.replace(/[^一-鿿]/g, '')
56
71
  }
57
72
 
73
+ // 写后回填的完成记录不是誊抄来源。标题块包含子标题;字段块止于下一字段或标题。
74
+ function stripCompletionRecord(outline) {
75
+ let headingLevel = 0
76
+ let inField = false
77
+ return outline.split('\n').filter((line) => {
78
+ const heading = line.match(/^ {0,3}(#{1,6})[\t ]+(.*)$/)
79
+ if (headingLevel) {
80
+ if (!heading || heading[1].length > headingLevel) return false
81
+ headingLevel = 0
82
+ }
83
+ if (inField) {
84
+ if (!heading && !/^[-*+][\t ]+/.test(line)) return false
85
+ inField = false
86
+ }
87
+ if (heading && heading[2].includes('实际完成情况')) {
88
+ headingLevel = heading[1].length
89
+ return false
90
+ }
91
+ if (/^[-*+][\t ]+(?:\*\*|__)?实际完成情况[^::]*[::]/.test(line)) {
92
+ inField = true
93
+ return false
94
+ }
95
+ return true
96
+ }).join('\n')
97
+ }
98
+
58
99
  /**
59
100
  * 抽出细纲「复沓锚句」字段下的原话,一行一条。
60
101
  * 只认这一个字段,不扫情节点序列——锚句集中在固定区块,情节点保持只写「要发生什么」。
@@ -129,7 +170,9 @@ function findOutline(proseFile) {
129
170
  const fm = file.match(/^细纲_第0*(\d+)章.*\.md$/)
130
171
  if (fm && fm[1] === chapter) return path.join(dir, file)
131
172
  }
132
- } catch {}
173
+ } catch (error) {
174
+ if (error.code !== 'ENOENT') throw error
175
+ }
133
176
  return null
134
177
  }
135
178
 
@@ -137,34 +180,56 @@ function main() {
137
180
  const proseFiles = []
138
181
  let explicitOutline = null
139
182
  const argv = process.argv.slice(2)
183
+ let positionalOnly = false
140
184
  for (let k = 0; k < argv.length; k++) {
141
- if (argv[k] === '--outline') explicitOutline = argv[++k] || null
142
- else proseFiles.push(argv[k])
143
- }
144
- if (!proseFiles.length) {
145
- process.stderr.write('用法: node check-outline-copy.js [--outline <细纲路径>] <正文路径...>\n')
146
- return 0
185
+ const arg = argv[k]
186
+ if (!positionalOnly && arg === '--') {
187
+ positionalOnly = true
188
+ } else if (!positionalOnly && arg === '--outline') {
189
+ if (explicitOutline !== null) return reportUsageError('--outline 不能重复指定')
190
+ const value = argv[k + 1]
191
+ if (!value || value.startsWith('--')) return reportUsageError('--outline 缺少路径')
192
+ explicitOutline = value
193
+ k++
194
+ } else if (!positionalOnly && arg.startsWith('--')) {
195
+ return reportUsageError(`未知选项: ${arg}`)
196
+ } else {
197
+ proseFiles.push(arg)
198
+ }
147
199
  }
148
- // 逐个文件独立判定;任一文件有重合即整体退 1
200
+ if (!proseFiles.length) return reportUsageError('缺少正文路径')
201
+
202
+ // 逐个文件独立判定;输入错误优先于重合发现
149
203
  let status = 0
150
204
  for (const proseFile of proseFiles) {
151
- if (checkOne(proseFile, explicitOutline)) status = 1
205
+ status = Math.max(status, checkOne(proseFile, explicitOutline))
152
206
  }
153
207
  return status
154
208
  }
155
209
 
156
210
  function checkOne(proseFile, explicitOutline) {
157
- const prose = read(proseFile)
158
- if (prose === null) return 0
211
+ const proseResult = readTextFile(proseFile)
212
+ if (!proseResult.ok) {
213
+ reportInputError('正文', proseFile, proseResult.reason)
214
+ return 2
215
+ }
216
+ const prose = proseResult.text
159
217
 
160
218
  const outlineFile = explicitOutline || findOutline(proseFile)
161
- if (!outlineFile) return 0
162
- const outline = read(outlineFile)
163
- if (outline === null) return 0
219
+ if (!outlineFile) {
220
+ process.stdout.write(`细纲照搬检测:跳过 "${proseFile}"(未自动发现细纲)。\n`)
221
+ return 0
222
+ }
223
+ const outlineResult = readTextFile(outlineFile)
224
+ if (!outlineResult.ok) {
225
+ reportInputError(explicitOutline ? '显式细纲' : '自动发现的细纲', outlineFile, outlineResult.reason)
226
+ return 2
227
+ }
228
+ const outline = outlineResult.text
164
229
 
165
230
  // 正文去掉标题行后比对
166
231
  const P = hanOnly(prose.replace(/^#.*$/gm, ''))
167
- const O = hanOnly(outline)
232
+ const O = hanOnly(stripCompletionRecord(outline))
168
233
  if (P.length < MIN_RUN || O.length < MIN_RUN) return 0
169
234
 
170
235
  // 复沓锚句列出的原话允许逐字落地,命中后计入豁免、不判誊抄
@@ -236,6 +301,8 @@ function checkOne(proseFile, explicitOutline) {
236
301
 
237
302
  try {
238
303
  process.exit(main())
239
- } catch {
240
- process.exit(0)
304
+ } catch (error) {
305
+ const message = error instanceof Error ? error.message : String(error)
306
+ process.stderr.write(`错误: 细纲照搬检测异常:${message}。\n`)
307
+ process.exit(2)
241
308
  }
@@ -2,6 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  const fs = require('fs');
5
+ const { loadStyleWhitelist, styleSpans } = require('./style-whitelist.js');
5
6
  const path = require('path');
6
7
 
7
8
  const USAGE = `Usage: node normalize-punctuation.js [--check] [--quote-mode keep|ascii|yan] <file...>
@@ -9,6 +10,7 @@ const USAGE = `Usage: node normalize-punctuation.js [--check] [--quote-mode keep
9
10
  Normalize正文 punctuation deterministically:
10
11
  - replace ellipses, em dashes, and double hyphens with Chinese punctuation
11
12
  - remove markdown divider lines (---) from正文
13
+ - preserve pause punctuation covered by book-local .deslop-whitelist literals
12
14
  - keep quote style by default; convert quotes only when explicitly requested
13
15
  `;
14
16
 
@@ -61,7 +63,10 @@ for (const file of options.files) {
61
63
  continue;
62
64
  }
63
65
 
64
- const result = normalizeDocument(input, options.quoteMode);
66
+ let whitelist;
67
+ try { whitelist = loadStyleWhitelist(fullPath); }
68
+ catch (error) { die(`${file}: unable to read .deslop-whitelist (${error.message})`); }
69
+ const result = normalizeDocument(input, options.quoteMode, whitelist);
65
70
  totalFindings += result.findings.length;
66
71
 
67
72
  if (options.check) {
@@ -94,7 +99,7 @@ function die(message) {
94
99
  process.exit(2);
95
100
  }
96
101
 
97
- function normalizeDocument(input, quoteMode) {
102
+ function normalizeDocument(input, quoteMode, whitelist) {
98
103
  const { lines, endings } = splitLinesKeepingEndings(input);
99
104
 
100
105
  const findings = [];
@@ -159,7 +164,7 @@ function normalizeDocument(input, quoteMode) {
159
164
  }
160
165
 
161
166
  const commentOpenBefore = commentOpen;
162
- const punctuationResult = normalizePausePunctuation(line, lineNo, commentOpen);
167
+ const punctuationResult = normalizePausePunctuation(line, lineNo, commentOpen, whitelist);
163
168
  findings.push(...punctuationResult.findings);
164
169
  line = punctuationResult.line;
165
170
  commentOpen = punctuationResult.commentOpen;
@@ -237,7 +242,7 @@ function isClosingFence(line, fence) {
237
242
  // 一遍归一化留不干净,再跑一遍还会改已定稿的正文;所以反复归一化到不动点。
238
243
  // 每遍至少把一个 `…/./—/-` 换成非停顿字符,字符数严格递减,必然收敛。
239
244
  // findings 只留第一遍:同一处不重复计数,column 也仍然是原行的偏移。
240
- function normalizePausePunctuation(line, lineNo, commentOpen) {
245
+ function normalizePausePunctuation(line, lineNo, commentOpen, whitelist) {
241
246
  let current = line;
242
247
  let findings = null;
243
248
  let commentOpenAfter = commentOpen;
@@ -245,7 +250,7 @@ function normalizePausePunctuation(line, lineNo, commentOpen) {
245
250
  for (;;) {
246
251
  const comments = htmlCommentSpans(current, commentOpen);
247
252
  commentOpenAfter = comments.open;
248
- const pass = normalizePausePunctuationPass(current, lineNo, comments.spans);
253
+ const pass = normalizePausePunctuationPass(current, lineNo, comments.spans.concat(styleSpans(current, whitelist)));
249
254
  if (findings === null) findings = pass.findings;
250
255
  if (pass.line === current) break;
251
256
  current = pass.line;