@oh-story/dsh 0.1.8 → 0.1.10

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 (360) hide show
  1. package/README.md +14 -14
  2. package/lib/client.js +237 -84
  3. package/lib/client.js.map +3 -3
  4. package/lib/drama/manifest.json +183 -77
  5. package/lib/drama/skills/short-drama/SKILL.md +5 -1
  6. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +140 -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 +28 -0
  9. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +8 -2
  10. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
  11. package/lib/drama/skills/short-drama-edit/SKILL.md +146 -0
  12. package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
  13. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
  14. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
  15. package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  16. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  17. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  18. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  19. package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  20. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  21. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  22. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  23. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +38 -0
  24. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +29 -0
  25. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +54 -0
  26. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +60 -0
  27. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +51 -0
  28. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1156 -0
  29. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +520 -0
  30. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
  31. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
  32. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
  33. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
  34. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
  35. package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
  36. package/lib/drama/skills/short-drama-produce/SKILL.md +29 -4
  37. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +22 -0
  38. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +13 -12
  39. package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  40. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
  41. package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
  42. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
  43. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +199 -3
  44. package/lib/drama/skills/short-drama-review/SKILL.md +2 -0
  45. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +7 -0
  46. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +10 -0
  47. package/lib/drama/skills/short-drama-storyboard/SKILL.md +12 -1
  48. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
  49. package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  50. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +78 -2
  51. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
  52. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +12 -5
  53. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +17 -0
  54. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +76 -7
  55. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +5 -2
  56. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +21 -0
  57. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +6 -17
  58. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +12 -3
  59. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +61 -5
  60. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -3
  61. package/lib/drama/skills/short-drama-write/SKILL.md +11 -2
  62. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
  63. package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
  64. package/lib/index.js +290 -90
  65. package/lib/index.js.map +4 -4
  66. package/lib/novel-to-game/README.md +299 -59
  67. package/lib/novel-to-game/README_ZH.md +299 -56
  68. package/lib/novel-to-game/VERSION +1 -1
  69. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
  70. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
  71. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
  72. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
  73. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
  74. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
  75. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
  76. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
  77. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
  78. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
  79. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
  80. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
  81. package/lib/novel-to-game/manifest.json +77 -87
  82. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +9 -35
  83. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
  84. package/lib/novel-to-game/skills/game-build/SKILL.md +17 -35
  85. package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
  86. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
  87. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
  88. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
  89. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
  90. package/lib/novel-to-game/skills/game-concept/SKILL.md +16 -34
  91. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
  92. package/lib/novel-to-game/skills/game-qa/SKILL.md +9 -20
  93. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +10 -6
  94. package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -32
  95. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
  96. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
  97. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
  98. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +9 -18
  99. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
  100. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +10 -27
  101. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -6
  102. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +6 -11
  103. package/lib/oh-story/manifest.json +419 -254
  104. package/lib/oh-story/roles/chapter-extractor.md +177 -237
  105. package/lib/oh-story/roles/character-designer.md +1 -1
  106. package/lib/oh-story/roles/consistency-checker.md +4 -4
  107. package/lib/oh-story/roles/narrative-writer.md +56 -243
  108. package/lib/oh-story/roles/story-architect.md +6 -6
  109. package/lib/oh-story/roles/story-explorer.md +11 -11
  110. package/lib/oh-story/skills/story/SKILL.md +13 -7
  111. package/lib/oh-story/skills/story/VERSION +1 -1
  112. package/lib/oh-story/skills/story/references/author-memory.md +61 -29
  113. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
  114. package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
  115. package/lib/oh-story/skills/story-deslop/SKILL.md +54 -229
  116. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +29 -56
  117. package/lib/oh-story/skills/story-deslop/references/author-memory.md +61 -29
  118. package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
  119. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
  120. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
  121. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
  122. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
  123. package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
  124. package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
  125. package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
  126. package/lib/oh-story/skills/story-import/SKILL.md +78 -82
  127. package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
  128. package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
  129. package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
  130. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +10 -8
  131. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
  132. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +9 -34
  133. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +197 -15
  134. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +210 -2
  135. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +3 -0
  136. package/lib/oh-story/skills/story-long-analyze/SKILL.md +91 -258
  137. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +245 -0
  138. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +2 -2
  139. package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
  140. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +113 -105
  141. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +169 -125
  142. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +172 -64
  143. package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
  144. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +42 -132
  145. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +5 -5
  146. package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
  147. package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
  148. package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
  149. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1106 -0
  150. package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
  151. package/lib/oh-story/skills/story-long-scan/SKILL.md +4 -1
  152. package/lib/oh-story/skills/story-long-write/SKILL.md +52 -216
  153. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +29 -56
  154. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +72 -12
  155. package/lib/oh-story/skills/story-long-write/references/author-memory.md +61 -29
  156. package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
  157. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
  158. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
  159. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +10 -4
  160. package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
  161. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +6 -15
  162. package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
  163. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
  164. package/lib/oh-story/skills/story-long-write/references/project-files.md +110 -0
  165. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +12 -4
  166. package/lib/oh-story/skills/story-long-write/references/reference-index.md +86 -0
  167. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
  168. package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
  169. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  170. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
  171. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
  172. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +9 -34
  173. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +88 -118
  174. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +55 -119
  175. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +15 -11
  176. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +73 -54
  177. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
  178. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +63 -4
  179. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
  180. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +531 -0
  181. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
  182. package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
  183. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +197 -15
  184. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
  185. package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
  186. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +285 -0
  187. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +58 -0
  188. package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
  189. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +210 -2
  190. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +3 -0
  191. package/lib/oh-story/skills/story-review/SKILL.md +80 -87
  192. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +29 -56
  193. package/lib/oh-story/skills/story-review/references/author-memory.md +61 -29
  194. package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
  195. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +10 -4
  196. package/lib/oh-story/skills/story-review/references/review-quality.md +6 -12
  197. package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
  198. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
  199. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +9 -34
  200. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
  201. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
  202. package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
  203. package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
  204. package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
  205. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +210 -2
  206. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +3 -0
  207. package/lib/oh-story/skills/story-setup/SKILL.md +58 -43
  208. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +29 -56
  209. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
  210. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
  211. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +10 -4
  212. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
  213. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
  214. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +1 -1
  215. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
  216. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
  217. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  218. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
  219. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +63 -4
  220. package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
  221. package/lib/oh-story/skills/story-short-analyze/SKILL.md +22 -3
  222. package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
  223. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +5 -1
  224. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
  225. package/lib/oh-story/skills/story-short-scan/SKILL.md +5 -1
  226. package/lib/oh-story/skills/story-short-write/SKILL.md +15 -324
  227. package/lib/oh-story/skills/story-short-write/references/author-memory.md +61 -29
  228. package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
  229. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
  230. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +10 -4
  231. 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
  232. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
  233. 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
  234. 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
  235. 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
  236. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
  237. 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
  238. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
  239. 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
  240. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
  241. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
  242. package/lib/oh-story/skills/story-short-write/references/reference-index.md +55 -0
  243. package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
  244. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
  245. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
  246. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
  247. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
  248. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +78 -0
  249. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +126 -0
  250. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +32 -0
  251. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
  252. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
  253. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
  254. package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
  255. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
  256. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
  257. package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
  258. package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
  259. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  260. package/lib/types/client/file-activity.d.ts +19 -1
  261. package/lib/types/client/file-activity.d.ts.map +1 -1
  262. package/lib/types/client/index.d.ts.map +1 -1
  263. package/lib/types/client/production-prompts.d.ts +7 -0
  264. package/lib/types/client/production-prompts.d.ts.map +1 -1
  265. package/lib/types/client/production-runtime.d.ts +33 -0
  266. package/lib/types/client/production-runtime.d.ts.map +1 -1
  267. package/lib/types/drama-adapters.d.ts +5 -3
  268. package/lib/types/drama-adapters.d.ts.map +1 -1
  269. package/lib/types/native-hooks.d.ts +52 -0
  270. package/lib/types/native-hooks.d.ts.map +1 -1
  271. package/lib/types/role-identity.d.ts +17 -0
  272. package/lib/types/role-identity.d.ts.map +1 -0
  273. package/lib/types/role-tool.d.ts.map +1 -1
  274. package/lib/types/skill-provider.d.ts.map +1 -1
  275. package/lib/video-recap/CHANGELOG.md +31 -1
  276. package/lib/video-recap/manifest.json +162 -162
  277. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
  278. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
  279. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
  280. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
  281. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
  282. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
  283. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
  284. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
  285. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
  286. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
  287. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
  288. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
  289. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
  290. package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
  291. package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
  292. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
  293. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
  294. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
  295. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
  296. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
  297. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
  298. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
  299. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
  300. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
  301. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
  302. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
  303. package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
  304. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
  305. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
  306. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
  307. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
  308. package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
  309. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
  310. package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
  311. package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
  312. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
  313. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
  314. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
  315. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
  316. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
  317. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
  318. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
  319. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
  320. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
  321. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
  322. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
  323. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
  324. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
  325. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
  326. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
  327. package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
  328. package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
  329. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
  330. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
  331. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
  332. package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
  333. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
  334. package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
  335. package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
  336. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
  337. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
  338. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
  339. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
  340. package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
  341. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
  342. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
  343. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
  344. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
  345. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
  346. package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
  347. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
  348. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
  349. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
  350. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
  351. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
  352. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
  353. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
  354. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
  355. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
  356. package/package.json +45 -45
  357. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -59
  358. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
  359. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -42
  360. package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
@@ -18,11 +18,13 @@
18
18
  const fs = require('fs')
19
19
  const path = require('path')
20
20
 
21
- // 权威模板:references/workflow-setup.md「细纲(全书每章)」
21
+ // 权威模板:references/workflow-setup.md「排纲自查与细纲」里的细纲模板
22
+ // 必填只留有证据或事故撑腰的核心字段;阶段位置、章节定位、结构公式、本章标价、闭环状态、
23
+ // 写手自由区、契约风险、镜头准入、信息差触发点与「分辨率」列是可选的阅读体验字段——
24
+ // 写了照样消费,没写不拦(#383 隔离实验:只补目标情绪与主角目标就复现了补满全部字段的收益)。
22
25
  const FIELDS = [
23
- '核心事件', '字数目标', '字数口径', '阶段位置', '单元ID/位置', '目标情绪',
24
- '主角目标/关键选择', '章节定位', '本章结构公式', '章首钩子', '爽点',
25
- '本章禁止提前释放', '契约风险',
26
+ '核心事件', '字数目标', '字数口径', '单元ID/位置', '目标情绪',
27
+ '主角目标/关键选择', '章首钩子', '爽点', '本章禁止提前释放',
26
28
  ]
27
29
  const SUBSECTIONS = ['内容概括', '情节安排', '人物关系和出场顺序', '情节细化']
28
30
  const FIVE_ACT = ['起因', '发展', '转折', '高潮', '结尾']
@@ -46,11 +48,11 @@ function readUtf8(file) {
46
48
  }
47
49
  }
48
50
 
49
- function makeCheck(id, ok, file, evidence, expected, repair) {
51
+ function makeCheck(id, ok, file, evidence, expected, repair, severity = 'blocking') {
50
52
  return {
51
53
  id,
52
54
  ok,
53
- severity: 'blocking',
55
+ severity,
54
56
  file,
55
57
  evidence,
56
58
  expected,
@@ -65,7 +67,47 @@ function parseTableRow(line) {
65
67
  return trimmed.slice(1, -1).split('|').map((cell) => cell.replace(/\*\*/g, '').replace(/`/g, '').trim())
66
68
  }
67
69
 
68
- function verify(file) {
70
+ function resolveBookRoot(file, projectRoot) {
71
+ if (projectRoot) return path.resolve(projectRoot)
72
+ const parent = path.dirname(path.resolve(file))
73
+ if (path.basename(parent) === '大纲') return path.dirname(parent)
74
+ return null
75
+ }
76
+
77
+ // 项目引用须写目录路径;裸文件名可能指向 skill reference,不猜测其归属。
78
+ const REF_DIR_PREFIX = /^(?:设定|大纲|追踪|正文|读者笔记|对标)\//
79
+
80
+ function checkSettingRefs(text, name, file, projectRoot) {
81
+ const bookRoot = resolveBookRoot(file, projectRoot)
82
+ if (!bookRoot) {
83
+ return makeCheck('outline.setting-refs-exist', true, name, '未能定位书目录(细纲不在 大纲/ 下且未传 --project),跳过', '细纲引用的项目内文件真实存在', '无需修复。')
84
+ }
85
+ const refs = new Set()
86
+ const pattern = /`([^`\n]+?\.md)[^`\n]*`/g
87
+ let match
88
+ while ((match = pattern.exec(text)) !== null) {
89
+ const token = match[1].trim().replace(/\\/g, '/')
90
+ if (token.includes('{') || token.includes('}')) continue
91
+ if (REF_DIR_PREFIX.test(token) || /^\.{1,2}\//.test(token)) refs.add(token)
92
+ }
93
+ const missing = []
94
+ for (const token of refs) {
95
+ const base = /^\.{1,2}\//.test(token) ? path.dirname(path.resolve(file)) : bookRoot
96
+ try {
97
+ if (!fs.statSync(path.resolve(base, token)).isFile()) missing.push(token)
98
+ } catch { missing.push(token) }
99
+ }
100
+ return makeCheck(
101
+ 'outline.setting-refs-exist',
102
+ missing.length === 0,
103
+ name,
104
+ missing.length ? `引用的路径不是可用文件:${missing.join('、')}` : `${refs.size} 处项目内引用全部存在`,
105
+ '细纲引用的每个项目内 .md 文件都真实存在——不许把槽位指向查无实据的权威文件',
106
+ '修正引用路径;或按新增物三级先把缺的设定落档(「写了要报」直接补并登记、「先问作者」进排纲底稿的供给自查提案),再在细纲引用。'
107
+ )
108
+ }
109
+
110
+ function verify(file, projectRoot = null) {
69
111
  const name = path.basename(file)
70
112
  const read = readUtf8(file)
71
113
  const checks = []
@@ -133,21 +175,82 @@ function verify(file) {
133
175
  let header = null
134
176
  for (const line of lines) {
135
177
  const cells = parseTableRow(line)
136
- if (cells && cells.length === 4 && PLOT_HEADER_FIRST.test(cells[0])) {
178
+ // 现行模板默认四列(# / 情节点 / 功能标签 / 执行边界(禁+放)),需要时在功能标签后加「分辨率」列成五列。
179
+ // 两种都收:功能标签固定在第 3 列,执行边界一律取最后一列。
180
+ if (cells && (cells.length === 4 || cells.length === 5) && PLOT_HEADER_FIRST.test(cells[0])) {
137
181
  header = cells
138
182
  break
139
183
  }
140
184
  }
141
- const headerOk = Boolean(header) && header[2].includes('功能标签') && header[3].includes('执行边界')
185
+ const headerOk = Boolean(header)
186
+ && header[2].includes('功能标签')
187
+ && header[header.length - 1].includes('执行边界')
188
+ && (header.length === 4 || header[3].includes('分辨率'))
142
189
  checks.push(makeCheck(
143
190
  'outline.plotpoint-table',
144
191
  headerOk,
145
192
  name,
146
- header ? `表头:${header.join(' | ')}` : '未找到 | # | 情节点 | 功能标签 | 执行边界 | 表头',
147
- '情节细化使用四列表格:# / 情节点(谁做了什么) / 功能标签 / 执行边界',
148
- '只把情节点序列改成四列表格,逐点补功能标签与执行边界;不增删情节点本身。'
193
+ header ? `表头:${header.join(' | ')}` : '未找到 | # | 情节点 | 功能标签 | [分辨率] | 执行边界 | 表头',
194
+ '情节细化使用表格:# / 情节点(谁做了什么) / 功能标签 / 执行边界(禁+放);需要时可在功能标签后加「分辨率」列',
195
+ '只把情节点序列改成这张表,逐点补功能标签与执行边界;不增删情节点本身。'
149
196
  ))
150
197
 
198
+ // 「放」半边:五列表或表头写明「禁+放」的四列表,执行边界必须至少一个点解除限制,
199
+ // 全是禁令的情节点表实测会把整章压成同一个温度(015 章十一个点全「不许」)。表头没写「禁+放」的四列表不追溯。
200
+ if (headerOk && (header.length === 5 || /放/.test(header[header.length - 1]))) {
201
+ const rows = []
202
+ for (const line of lines) {
203
+ const cells = parseTableRow(line)
204
+ if (cells && cells.length === header.length && /^\d+$/.test(cells[0])) rows.push(cells)
205
+ }
206
+ if (rows.length) {
207
+ const released = rows.filter((cells) => {
208
+ const match = cells[cells.length - 1].match(/(?:^|[。;;\s])放\s*[::]([^]*?)(?=(?:禁|放)\s*[::]|$)/)
209
+ if (!match) return false
210
+ const value = match[1].replace(/[\s。;;,,、\[\]【】]/g, '')
211
+ return value.length > 0 && !/^(?:无|暂无|待补充|待定|无缺口)$/.test(value)
212
+ })
213
+ checks.push(makeCheck(
214
+ 'outline.plotpoint-release',
215
+ released.length > 0,
216
+ name,
217
+ `${rows.length} 个情节点里 ${released.length} 个写了「放」`,
218
+ '执行边界写成「禁+放」两半,每章至少一个点写了「放」——放行是解除限制,不是义务',
219
+ '给最该出彩的一两个点补「放」半边(谁可以出声、谁可以失态、允不允许当场标价),不改情节点本身。'
220
+ ))
221
+ if (released.length > 0 && rows.length >= 3 && released.length * 3 < rows.length) {
222
+ checks.push(makeCheck(
223
+ 'outline.plotpoint-release-ratio',
224
+ false,
225
+ name,
226
+ `${rows.length} 个情节点里只有 ${released.length} 个写了「放」(不足三分之一)`,
227
+ '建议至少三分之一的情节点写「放」半边,避免整章只剩禁令',
228
+ '按人工判断酌情补「放」;本项不阻断。',
229
+ 'advisory'
230
+ ))
231
+ }
232
+ }
233
+ }
234
+
235
+ // 章级「禁止提前释放」只写本章特有的三五条;条目过多多半是把卷级常任禁忌逐章复读了。
236
+ const releaseMatch = text.match(/^\s*[-*+]\s*\*{0,2}本章禁止提前释放\*{0,2}\s*[::]\s*(.*)$/m)
237
+ if (releaseMatch) {
238
+ const items = releaseMatch[1].split(/[、;;,,\/]/).map((s) => s.trim()).filter(Boolean)
239
+ if (items.length > 5) {
240
+ checks.push(makeCheck(
241
+ 'outline.release-brevity',
242
+ false,
243
+ name,
244
+ `本章禁止提前释放列了 ${items.length} 条`,
245
+ '章级只写本章特有的三五条;卷级常任禁忌写在卷纲「本卷禁碰的终局底牌」,不逐章复读',
246
+ '把与卷纲常任重复的条目删掉,只留本章特有;本项不阻断。',
247
+ 'advisory'
248
+ ))
249
+ }
250
+ }
251
+
252
+ checks.push(checkSettingRefs(text, name, file, projectRoot))
253
+
151
254
  const targetMatch = text.match(/字数目标\s*[::]\s*(?:约\s*)?([\d,,]+)/)
152
255
  const target = targetMatch ? Number(targetMatch[1].replace(/[,,]/g, '')) : null
153
256
  const caliberOk = new RegExp(`字数口径\\s*[::]\\s*${CALIBER}`).test(text)
@@ -164,7 +267,8 @@ function verify(file) {
164
267
  }
165
268
 
166
269
  function report(file, checks) {
167
- const failures = checks.filter((check) => !check.ok)
270
+ const failures = checks.filter((check) => !check.ok && check.severity === 'blocking')
271
+ const advisories = checks.filter((check) => !check.ok && check.severity === 'advisory')
168
272
  return {
169
273
  schema_version: 1,
170
274
  verifier: 'story-long-write.outline-contract',
@@ -172,6 +276,7 @@ function report(file, checks) {
172
276
  ok: failures.length === 0,
173
277
  checks,
174
278
  failures,
279
+ advisories,
175
280
  repair_scope: failures.map((failure) => ({
176
281
  id: failure.id,
177
282
  file: failure.file,
@@ -200,13 +305,81 @@ function resolveChapter(project, chapter) {
200
305
  return { file: path.join(dir, hit) }
201
306
  }
202
307
 
308
+ // --supply:批末验证供给自查已落盘。新书写在卷纲旁的 排纲底稿_{单元ID}.md;老卷纲写在单元卡块内。
309
+ function hasSupplyHeading(text) {
310
+ return text.split(/\r?\n/).some((line) => {
311
+ const heading = line.replace(/\*\*/g, '').match(/^#{1,6}\s+(.+)$/)
312
+ return Boolean(heading) && /^供给自查(?:\s|[((]|$)/.test(heading[1])
313
+ })
314
+ }
315
+
316
+ function verifySupply(volumeFile, unitId) {
317
+ const draftFile = path.join(path.dirname(volumeFile), `排纲底稿_${unitId}.md`)
318
+ const draft = readUtf8(draftFile)
319
+ if (draft.ok) {
320
+ const ok = hasSupplyHeading(draft.text)
321
+ return {
322
+ schema_version: 1, verifier: 'story-long-write.outline-supply', file: path.resolve(draftFile), unit: unitId, ok,
323
+ evidence: ok ? '排纲底稿含「供给自查」小节' : `${path.basename(draftFile)} 里没有「供给自查」小节——每批出细纲前须做供给自查(含「无缺口」情形),见 workflow-setup.md「排纲自查与细纲」步骤 3`,
324
+ }
325
+ }
326
+ const read = readUtf8(volumeFile)
327
+ if (!read.ok) {
328
+ return { schema_version: 1, verifier: 'story-long-write.outline-supply', file: path.resolve(volumeFile), unit: unitId, ok: false, evidence: read.error || '卷纲文件为空' }
329
+ }
330
+ // 只检查目标单元内的标题,不把正文提及或代码示例当成小节。
331
+ let fence = null
332
+ const lines = read.text.split(/\r?\n/).map((line) => {
333
+ const marker = line.match(/^\s{0,3}(`{3,}|~{3,})/)
334
+ if (marker) {
335
+ if (!fence) fence = marker[1]
336
+ else if (marker[1][0] === fence[0] && marker[1].length >= fence.length) fence = null
337
+ return ''
338
+ }
339
+ return fence ? '' : line.replace(/\*\*/g, '')
340
+ })
341
+ const headings = []
342
+ for (let index = 0; index < lines.length; index++) {
343
+ const heading = lines[index].match(/^(#{1,6})\s+(.+)$/)
344
+ if (heading) headings.push({ index, level: heading[1].length, title: heading[2] })
345
+ }
346
+ const card = headings.find((heading) => {
347
+ const id = heading.title.match(/剧情单元\s+([A-Za-z][\w-]*)/)
348
+ if (id) return id[1] === unitId
349
+ const end = headings.find((next) => next.index > heading.index)?.index ?? lines.length
350
+ return lines.slice(heading.index + 1, end).some((line) => {
351
+ const field = line.match(/^\s*[-*+]\s*单元ID\s*[::]\s*([A-Za-z][\w-]*)\s*$/)
352
+ return field?.[1] === unitId
353
+ })
354
+ })
355
+ const end = card ? (headings.find((next) => next.index > card.index && next.level <= card.level)?.index ?? lines.length) : 0
356
+ const ok = Boolean(card) && headings.some((heading) => heading.index > card.index && heading.index < end && /^供给自查(?:\s|[((]|$)/.test(heading.title))
357
+ if (!card) {
358
+ return { schema_version: 1, verifier: 'story-long-write.outline-supply', file: path.resolve(volumeFile), unit: unitId, ok: false, evidence: `卷纲中未找到剧情单元 ${unitId}` }
359
+ }
360
+ return {
361
+ schema_version: 1,
362
+ verifier: 'story-long-write.outline-supply',
363
+ file: path.resolve(volumeFile),
364
+ unit: unitId,
365
+ ok,
366
+ evidence: ok ? '单元卡含「供给自查」小节' : `没有 排纲底稿_${unitId}.md,剧情单元 ${unitId} 的卡内也没有「供给自查」小节——每批出细纲前须做供给自查(含「无缺口」情形),见 workflow-setup.md「排纲自查与细纲」步骤 3`,
367
+ }
368
+ }
369
+
203
370
  function parseArgs(argv) {
204
371
  const files = []
205
372
  let project = null
206
373
  let chapter = null
374
+ let supply = null
207
375
  for (let index = 0; index < argv.length; index++) {
208
376
  const arg = argv[index]
209
377
  if (arg === '--json') continue
378
+ if (arg === '--supply') {
379
+ if (index + 2 >= argv.length) return null
380
+ supply = { volumeFile: argv[++index], unitId: argv[++index] }
381
+ continue
382
+ }
210
383
  if (arg === '--project' || arg === '--chapter') {
211
384
  if (index + 1 >= argv.length || argv[index + 1].startsWith('--')) return null
212
385
  const value = argv[++index]
@@ -217,6 +390,10 @@ function parseArgs(argv) {
217
390
  if (arg.startsWith('--')) return null
218
391
  files.push(arg)
219
392
  }
393
+ if (supply) {
394
+ if (project || chapter || files.length) return null
395
+ return { supply }
396
+ }
220
397
  if (project || chapter) {
221
398
  if (!project || !chapter || files.length || !/^\d+$/.test(chapter)) return null
222
399
  return { project, chapter }
@@ -228,9 +405,14 @@ function parseArgs(argv) {
228
405
  function main(argv) {
229
406
  const parsed = parseArgs(argv)
230
407
  if (!parsed) {
231
- process.stderr.write('用法: node scripts/check-outline-contract.js --json <细纲路径...> | --json --project <书目录> --chapter N\n')
408
+ process.stderr.write('用法: node scripts/check-outline-contract.js --json <细纲路径...> | --json --project <书目录> --chapter N | --json --supply <卷纲路径> <单元ID>\n')
232
409
  return 2
233
410
  }
411
+ if (parsed.supply) {
412
+ const supplyReport = verifySupply(parsed.supply.volumeFile, parsed.supply.unitId)
413
+ process.stdout.write(`${JSON.stringify(supplyReport, null, 2)}\n`)
414
+ return supplyReport.ok ? 0 : 1
415
+ }
234
416
  let targets = parsed.files
235
417
  if (!targets) {
236
418
  const resolved = resolveChapter(parsed.project, parsed.chapter)
@@ -240,7 +422,7 @@ function main(argv) {
240
422
  }
241
423
  targets = [resolved.file]
242
424
  }
243
- const reports = targets.map((file) => verify(file))
425
+ const reports = targets.map((file) => verify(file, parsed.project || null))
244
426
  const ok = reports.every((entry) => entry.ok)
245
427
  process.stdout.write(`${JSON.stringify(reports.length === 1 ? reports[0] : reports, null, 2)}\n`)
246
428
  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;