@oh-story/dsh 0.1.0

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 (488) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/cordis.patch.yml +3 -0
  4. package/lib/client.js +1201 -0
  5. package/lib/client.js.map +7 -0
  6. package/lib/drama/LICENSE.upstream +21 -0
  7. package/lib/drama/manifest.json +1012 -0
  8. package/lib/drama/skills/short-drama/SKILL.md +153 -0
  9. package/lib/drama/skills/short-drama/agents/openai.yaml +4 -0
  10. package/lib/drama/skills/short-drama/assets/creator-decision.example.jsonl +4 -0
  11. package/lib/drama/skills/short-drama/assets/production-observation.example.jsonl +2 -0
  12. package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +36 -0
  13. package/lib/drama/skills/short-drama/assets/reference-observation.example.jsonl +2 -0
  14. package/lib/drama/skills/short-drama/references/audience-reveal.md +74 -0
  15. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +94 -0
  16. package/lib/drama/skills/short-drama/references/creator-workflow.md +82 -0
  17. package/lib/drama/skills/short-drama/references/form-cards/Q/347/211/210/350/241/250/350/276/276.md +73 -0
  18. package/lib/drama/skills/short-drama/references/form-cards//344/272/214/347/273/264/345/212/250/346/200/201/346/274/253.md +74 -0
  19. package/lib/drama/skills/short-drama/references/form-cards//345/233/275/346/274/253/344/272/214/346/254/241/345/205/203.md +79 -0
  20. package/lib/drama/skills/short-drama/references/form-cards//345/256/236/346/213/215.md +75 -0
  21. package/lib/drama/skills/short-drama/references/form-cards//346/260/264/345/242/250/347/254/224/350/247/246.md +85 -0
  22. package/lib/drama/skills/short-drama/references/form-cards//351/243/216/346/240/274/345/214/226/344/270/211/347/273/264.md +72 -0
  23. package/lib/drama/skills/short-drama/references/knowhow-index.md +80 -0
  24. package/lib/drama/skills/short-drama/references/lifecycle-commands.md +130 -0
  25. package/lib/drama/skills/short-drama/references/look-development.md +52 -0
  26. package/lib/drama/skills/short-drama/references/pickup-and-alternate.md +58 -0
  27. package/lib/drama/skills/short-drama/references/production-form-profiles.md +126 -0
  28. package/lib/drama/skills/short-drama/references/reference-roles.md +142 -0
  29. package/lib/drama/skills/short-drama/references/runtime-preflight.md +27 -0
  30. package/lib/drama/skills/short-drama/scripts/project_tool.py +1963 -0
  31. package/lib/drama/skills/short-drama/scripts/selftest.py +287 -0
  32. package/lib/drama/skills/short-drama-assets/SKILL.md +187 -0
  33. package/lib/drama/skills/short-drama-assets/agents/openai.yaml +4 -0
  34. package/lib/drama/skills/short-drama-assets/assets/character-look.example.jsonl +3 -0
  35. package/lib/drama/skills/short-drama-assets/assets/continuity.example.jsonl +4 -0
  36. package/lib/drama/skills/short-drama-assets/assets/decisions.example.jsonl +5 -0
  37. package/lib/drama/skills/short-drama-assets/assets/location-view.example.jsonl +3 -0
  38. package/lib/drama/skills/short-drama-assets/assets/occurrences.example.jsonl +5 -0
  39. package/lib/drama/skills/short-drama-assets/assets/prop-state.example.jsonl +3 -0
  40. package/lib/drama/skills/short-drama-assets/examples/minimal/characters.jsonl +2 -0
  41. package/lib/drama/skills/short-drama-assets/examples/minimal/looks.jsonl +2 -0
  42. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +89 -0
  43. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +164 -0
  44. package/lib/drama/skills/short-drama-assets/references/continuity-delta.md +123 -0
  45. package/lib/drama/skills/short-drama-assets/references/identity-vs-variant.md +146 -0
  46. package/lib/drama/skills/short-drama-assets/references/location-and-view.md +84 -0
  47. package/lib/drama/skills/short-drama-assets/references/occurrence-extraction.md +111 -0
  48. package/lib/drama/skills/short-drama-assets/references/prop-and-state.md +107 -0
  49. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +136 -0
  50. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +171 -0
  51. package/lib/drama/skills/short-drama-assets/scripts/asset_check.py +267 -0
  52. package/lib/drama/skills/short-drama-assets/scripts/selftest.py +89 -0
  53. package/lib/drama/skills/short-drama-develop/SKILL.md +157 -0
  54. package/lib/drama/skills/short-drama-develop/agents/openai.yaml +4 -0
  55. package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -0
  56. package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +120 -0
  57. package/lib/drama/skills/short-drama-develop/assets/episode-map.jsonl +1 -0
  58. package/lib/drama/skills/short-drama-develop/assets/story-engine.md +181 -0
  59. package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +219 -0
  60. package/lib/drama/skills/short-drama-develop/references/creative-reference-intake.md +57 -0
  61. package/lib/drama/skills/short-drama-develop/references/director-brief-craft.md +113 -0
  62. package/lib/drama/skills/short-drama-develop/references/episode-design.md +509 -0
  63. package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +240 -0
  64. package/lib/drama/skills/short-drama-develop/references/genre-cards//344/272/262/345/255/220/351/232/220/347/247/230.md +112 -0
  65. package/lib/drama/skills/short-drama-develop/references/genre-cards//344/273/231/344/276/240/344/277/256/347/234/237.md +51 -0
  66. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/212/250/344/275/234/344/273/273/345/212/241.md +51 -0
  67. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/217/244/350/243/205/346/235/203/350/260/213.md +51 -0
  68. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +51 -0
  69. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/256/266/345/272/255/345/205/263/347/263/273.md +51 -0
  70. package/lib/drama/skills/short-drama-develop/references/genre-cards//346/202/254/347/226/221/350/247/204/345/210/231.md +51 -0
  71. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/224/237/346/264/273/346/265/201.md +51 -0
  72. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/240/264/351/225/234/351/207/215/345/234/206.md +109 -0
  73. package/lib/drama/skills/short-drama-develop/references/genre-cards//350/201/214/345/234/272/345/226/234/345/211/247.md +51 -0
  74. package/lib/drama/skills/short-drama-develop/references/genre-cards//350/261/252/351/227/250/345/251/232/346/201/213.md +51 -0
  75. package/lib/drama/skills/short-drama-develop/references/genre-cards//350/272/253/344/273/275/351/224/231/344/275/215.md +51 -0
  76. package/lib/drama/skills/short-drama-develop/references/genre-cards.md +92 -0
  77. package/lib/drama/skills/short-drama-develop/references/mechanism-loop.md +282 -0
  78. package/lib/drama/skills/short-drama-develop/references/multi-episode-intake.md +128 -0
  79. package/lib/drama/skills/short-drama-develop/references/premise-devices.md +211 -0
  80. package/lib/drama/skills/short-drama-develop/references/reveal-reversal-payoff.md +99 -0
  81. package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +293 -0
  82. package/lib/drama/skills/short-drama-develop/references/stage-contract.md +88 -0
  83. package/lib/drama/skills/short-drama-develop/references/story-craft.md +359 -0
  84. package/lib/drama/skills/short-drama-develop/scripts/episode_intake.py +699 -0
  85. package/lib/drama/skills/short-drama-develop/scripts/selftest.py +47 -0
  86. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +181 -0
  87. package/lib/drama/skills/short-drama-image-prompts/agents/openai.yaml +4 -0
  88. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompt-spec.jsonl.md +187 -0
  89. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +37 -0
  90. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-frame-spec.jsonl.md +128 -0
  91. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +31 -0
  92. package/lib/drama/skills/short-drama-image-prompts/examples/minimal-image-prompt-specs.jsonl +2 -0
  93. package/lib/drama/skills/short-drama-image-prompts/references/character-and-look.md +154 -0
  94. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +231 -0
  95. package/lib/drama/skills/short-drama-image-prompts/references/edit-and-revision.md +122 -0
  96. package/lib/drama/skills/short-drama-image-prompts/references/location-plate.md +152 -0
  97. package/lib/drama/skills/short-drama-image-prompts/references/look-and-state-variant.md +96 -0
  98. package/lib/drama/skills/short-drama-image-prompts/references/lookdev-frame.md +38 -0
  99. package/lib/drama/skills/short-drama-image-prompts/references/production-sheet-recipes.md +177 -0
  100. package/lib/drama/skills/short-drama-image-prompts/references/prop-plate.md +140 -0
  101. package/lib/drama/skills/short-drama-image-prompts/references/review-and-fixtures.md +92 -0
  102. package/lib/drama/skills/short-drama-image-prompts/references/stage-contract.md +89 -0
  103. package/lib/drama/skills/short-drama-image-prompts/scripts/image_prompt_check.py +443 -0
  104. package/lib/drama/skills/short-drama-image-prompts/scripts/selftest.py +93 -0
  105. package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +245 -0
  106. package/lib/drama/skills/short-drama-novel-analyze/agents/openai.yaml +4 -0
  107. package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +3 -0
  108. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +116 -0
  109. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +124 -0
  110. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +155 -0
  111. package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +141 -0
  112. package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +67 -0
  113. package/lib/drama/skills/short-drama-novel-analyze/scripts/novel_index.py +651 -0
  114. package/lib/drama/skills/short-drama-novel-analyze/scripts/selftest.py +51 -0
  115. package/lib/drama/skills/short-drama-produce/SKILL.md +103 -0
  116. package/lib/drama/skills/short-drama-produce/agents/openai.yaml +4 -0
  117. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +140 -0
  118. package/lib/drama/skills/short-drama-produce/references/providers/gpt-image-2.md +24 -0
  119. package/lib/drama/skills/short-drama-produce/references/providers/minimax-music.md +25 -0
  120. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +40 -0
  121. package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +60 -0
  122. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +1635 -0
  123. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +964 -0
  124. package/lib/drama/skills/short-drama-produce/scripts/selftest.py +134 -0
  125. package/lib/drama/skills/short-drama-review/SKILL.md +178 -0
  126. package/lib/drama/skills/short-drama-review/agents/openai.yaml +4 -0
  127. package/lib/drama/skills/short-drama-review/assets/finding-template.jsonl +2 -0
  128. package/lib/drama/skills/short-drama-review/assets/supersession-decision.example.json +42 -0
  129. package/lib/drama/skills/short-drama-review/assets/verdict-template.json +32 -0
  130. package/lib/drama/skills/short-drama-review/examples/minimal-findings.jsonl +2 -0
  131. package/lib/drama/skills/short-drama-review/examples/minimal-verdict.json +34 -0
  132. package/lib/drama/skills/short-drama-review/references/anti-template-repair.md +114 -0
  133. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +270 -0
  134. package/lib/drama/skills/short-drama-review/references/project-calibration.md +85 -0
  135. package/lib/drama/skills/short-drama-review/references/review-method.md +82 -0
  136. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +115 -0
  137. package/lib/drama/skills/short-drama-review/references/rubric-source-analysis.md +108 -0
  138. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +164 -0
  139. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +166 -0
  140. package/lib/drama/skills/short-drama-review/references/stage-contract.md +87 -0
  141. package/lib/drama/skills/short-drama-review/scripts/review_check.py +325 -0
  142. package/lib/drama/skills/short-drama-review/scripts/selftest.py +87 -0
  143. package/lib/drama/skills/short-drama-storyboard/SKILL.md +264 -0
  144. package/lib/drama/skills/short-drama-storyboard/agents/openai.yaml +4 -0
  145. package/lib/drama/skills/short-drama-storyboard/assets/coverage-audition.example.jsonl +2 -0
  146. package/lib/drama/skills/short-drama-storyboard/assets/coverage-template.json +49 -0
  147. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +32 -0
  148. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-template.jsonl +2 -0
  149. package/lib/drama/skills/short-drama-storyboard/assets/revision-lineage.fragment.json +12 -0
  150. package/lib/drama/skills/short-drama-storyboard/assets/scene-visual-plan.example.jsonl +2 -0
  151. package/lib/drama/skills/short-drama-storyboard/assets/shot-template.jsonl +2 -0
  152. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +241 -0
  153. package/lib/drama/skills/short-drama-storyboard/references/coverage-audition.md +39 -0
  154. package/lib/drama/skills/short-drama-storyboard/references/keyframe-craft.md +169 -0
  155. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +264 -0
  156. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +70 -0
  157. package/lib/drama/skills/short-drama-storyboard/references/scene-visual-plan.md +39 -0
  158. package/lib/drama/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +157 -0
  159. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +192 -0
  160. package/lib/drama/skills/short-drama-storyboard/references/shot-revision-identity.md +29 -0
  161. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +110 -0
  162. package/lib/drama/skills/short-drama-storyboard/scripts/selftest.py +191 -0
  163. package/lib/drama/skills/short-drama-storyboard/scripts/storyboard_check.py +824 -0
  164. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +245 -0
  165. package/lib/drama/skills/short-drama-video-prompts/agents/openai.yaml +4 -0
  166. package/lib/drama/skills/short-drama-video-prompts/assets/coverage-scope.fragment.json +21 -0
  167. package/lib/drama/skills/short-drama-video-prompts/assets/delivery-container.jsonl.md +101 -0
  168. package/lib/drama/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +212 -0
  169. package/lib/drama/skills/short-drama-video-prompts/assets/motion-terminal.example.jsonl +2 -0
  170. package/lib/drama/skills/short-drama-video-prompts/assets/music-spec.jsonl.md +31 -0
  171. package/lib/drama/skills/short-drama-video-prompts/assets/performance.fragment.json +38 -0
  172. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +46 -0
  173. package/lib/drama/skills/short-drama-video-prompts/examples/minimal-music-specs.jsonl +2 -0
  174. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +240 -0
  175. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +255 -0
  176. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +93 -0
  177. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +304 -0
  178. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +191 -0
  179. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +312 -0
  180. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +168 -0
  181. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +110 -0
  182. package/lib/drama/skills/short-drama-video-prompts/scripts/container_check.py +329 -0
  183. package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +450 -0
  184. package/lib/drama/skills/short-drama-video-prompts/scripts/music_spec_check.py +327 -0
  185. package/lib/drama/skills/short-drama-video-prompts/scripts/selftest.py +82 -0
  186. package/lib/drama/skills/short-drama-write/SKILL.md +284 -0
  187. package/lib/drama/skills/short-drama-write/agents/openai.yaml +4 -0
  188. package/lib/drama/skills/short-drama-write/assets/beats.jsonl +2 -0
  189. package/lib/drama/skills/short-drama-write/assets/episode-card-standalone.json +120 -0
  190. package/lib/drama/skills/short-drama-write/assets/episode-card.json +55 -0
  191. package/lib/drama/skills/short-drama-write/assets/screenplay.md +36 -0
  192. package/lib/drama/skills/short-drama-write/assets/voice-record-sheet.jsonl.md +89 -0
  193. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +519 -0
  194. package/lib/drama/skills/short-drama-write/references/production-format-dialect.md +273 -0
  195. package/lib/drama/skills/short-drama-write/references/scene-handoff-capsule.md +51 -0
  196. package/lib/drama/skills/short-drama-write/references/scene-sound-dramaturgy.md +43 -0
  197. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +260 -0
  198. package/lib/drama/skills/short-drama-write/references/script-craft.md +558 -0
  199. package/lib/drama/skills/short-drama-write/references/stage-contract.md +94 -0
  200. package/lib/drama/skills/short-drama-write/references/substitutable-realization.md +107 -0
  201. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +309 -0
  202. package/lib/drama/skills/short-drama-write/scripts/screenplay_index.py +1091 -0
  203. package/lib/drama/skills/short-drama-write/scripts/selftest.py +229 -0
  204. package/lib/drama/skills/short-drama-write/scripts/voice_sheet_check.py +416 -0
  205. package/lib/index.js +756 -0
  206. package/lib/index.js.map +7 -0
  207. package/lib/oh-story/LICENSE.upstream +21 -0
  208. package/lib/oh-story/manifest.json +1351 -0
  209. package/lib/oh-story/roles/chapter-extractor.md +314 -0
  210. package/lib/oh-story/roles/character-designer.md +169 -0
  211. package/lib/oh-story/roles/consistency-checker.md +190 -0
  212. package/lib/oh-story/roles/narrative-writer.md +294 -0
  213. package/lib/oh-story/roles/story-architect.md +195 -0
  214. package/lib/oh-story/roles/story-explorer.md +364 -0
  215. package/lib/oh-story/roles/story-researcher.md +304 -0
  216. package/lib/oh-story/skills/browser-cdp/SKILL.md +175 -0
  217. package/lib/oh-story/skills/story/SKILL.md +116 -0
  218. package/lib/oh-story/skills/story/VERSION +1 -0
  219. package/lib/oh-story/skills/story-cover/SKILL.md +327 -0
  220. package/lib/oh-story/skills/story-cover/references/cover-styles.md +210 -0
  221. package/lib/oh-story/skills/story-deslop/SKILL.md +445 -0
  222. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +582 -0
  223. package/lib/oh-story/skills/story-deslop/references/banned-words.md +147 -0
  224. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +1371 -0
  225. package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +330 -0
  226. package/lib/oh-story/skills/story-deslop/scripts/check-outline-copy.js +241 -0
  227. package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +410 -0
  228. package/lib/oh-story/skills/story-import/SKILL.md +634 -0
  229. package/lib/oh-story/skills/story-import/references/character-state-reverse.md +82 -0
  230. package/lib/oh-story/skills/story-import/references/format-and-structure.md +169 -0
  231. package/lib/oh-story/skills/story-import/references/length-routing.md +134 -0
  232. package/lib/oh-story/skills/story-import/references/state-tracking.md +108 -0
  233. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +442 -0
  234. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +175 -0
  235. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +161 -0
  236. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +1139 -0
  237. package/lib/oh-story/skills/story-long-analyze/SKILL.md +329 -0
  238. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +270 -0
  239. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +530 -0
  240. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +559 -0
  241. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +80 -0
  242. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +149 -0
  243. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +133 -0
  244. package/lib/oh-story/skills/story-long-scan/SKILL.md +342 -0
  245. package/lib/oh-story/skills/story-long-scan/references/genre-trends.md +197 -0
  246. package/lib/oh-story/skills/story-long-scan/references/publishing-guide.md +114 -0
  247. package/lib/oh-story/skills/story-long-scan/references/reader-profiling.md +105 -0
  248. package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +207 -0
  249. package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +78 -0
  250. package/lib/oh-story/skills/story-long-write/SKILL.md +327 -0
  251. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +582 -0
  252. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +302 -0
  253. package/lib/oh-story/skills/story-long-write/references/banned-words.md +147 -0
  254. package/lib/oh-story/skills/story-long-write/references/character-basics.md +445 -0
  255. package/lib/oh-story/skills/story-long-write/references/character-design-methods.md +428 -0
  256. package/lib/oh-story/skills/story-long-write/references/character-relations.md +385 -0
  257. package/lib/oh-story/skills/story-long-write/references/commercial-core-methods.md +502 -0
  258. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +62 -0
  259. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +282 -0
  260. package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +426 -0
  261. package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +209 -0
  262. package/lib/oh-story/skills/story-long-write/references/female-audience-writing.md +188 -0
  263. package/lib/oh-story/skills/story-long-write/references/format-and-structure.md +169 -0
  264. package/lib/oh-story/skills/story-long-write/references/genre-catalog.md +480 -0
  265. package/lib/oh-story/skills/story-long-write/references/genre-core-mechanics.md +415 -0
  266. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//344/270/234/346/226/271/344/273/231/344/276/240.md +51 -0
  267. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//344/274/240/347/273/237/347/216/204/345/271/273.md +51 -0
  268. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/216/206/345/217/262/345/217/244/344/273/243.md +51 -0
  269. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/216/206/345/217/262/350/204/221/346/264/236.md +51 -0
  270. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/217/214/347/224/267/344/270/273.md +53 -0
  271. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/217/244/350/250/200/350/204/221/346/264/236.md +51 -0
  272. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/217/244/351/243/216/344/270/226/346/203/205.md +51 -0
  273. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/245/263/351/242/221/346/202/254/347/226/221.md +51 -0
  274. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/245/263/351/242/221/347/247/215/347/224/260.md +51 -0
  275. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/256/253/346/226/227/345/256/205/346/226/227.md +51 -0
  276. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/271/264/344/273/243.md +53 -0
  277. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//345/277/253/347/251/277.md +51 -0
  278. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/202/254/347/226/221/347/201/265/345/274/202.md +51 -0
  279. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/202/254/347/226/221/350/204/221/346/264/236.md +51 -0
  280. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/210/230/347/245/236/350/265/230/345/251/277.md +51 -0
  281. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/212/227/346/210/230/350/260/215/346/210/230.md +51 -0
  282. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/230/237/345/205/211/347/222/200/347/222/250.md +51 -0
  283. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/260/221/345/233/275/350/250/200/346/203/205.md +51 -0
  284. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//346/270/270/346/210/217/344/275/223/350/202/262.md +51 -0
  285. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//347/216/204/345/271/273/350/204/221/346/264/236.md +53 -0
  286. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//347/216/204/345/271/273/350/250/200/346/203/205.md +51 -0
  287. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//347/216/260/350/250/200/350/204/221/346/264/236.md +51 -0
  288. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//347/247/221/345/271/273/346/234/253/344/270/226.md +51 -0
  289. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//350/201/214/345/234/272/345/251/232/346/201/213.md +51 -0
  290. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//350/245/277/346/226/271/345/245/207/345/271/273.md +51 -0
  291. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//350/261/252/351/227/250/346/200/273/350/243/201.md +53 -0
  292. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/344/277/256/347/234/237.md +51 -0
  293. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/346/227/245/345/270/270.md +53 -0
  294. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/347/247/215/347/224/260.md +51 -0
  295. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/350/204/221/346/264/236.md +53 -0
  296. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/351/253/230/346/255/246.md +53 -0
  297. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards//351/235/222/346/230/245/347/224/234/345/256/240.md +51 -0
  298. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +91 -0
  299. package/lib/oh-story/skills/story-long-write/references/genre-readers.md +235 -0
  300. package/lib/oh-story/skills/story-long-write/references/genre-writing-formulas.md +444 -0
  301. package/lib/oh-story/skills/story-long-write/references/hooks-chapter.md +225 -0
  302. package/lib/oh-story/skills/story-long-write/references/hooks-paragraph.md +146 -0
  303. package/lib/oh-story/skills/story-long-write/references/hooks-suspense.md +352 -0
  304. package/lib/oh-story/skills/story-long-write/references/opening-design.md +349 -0
  305. package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +457 -0
  306. package/lib/oh-story/skills/story-long-write/references/outline-methods.md +287 -0
  307. package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +389 -0
  308. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +326 -0
  309. package/lib/oh-story/skills/story-long-write/references/plot-core-methods.md +522 -0
  310. package/lib/oh-story/skills/story-long-write/references/plot-emotion-system.md +354 -0
  311. package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +711 -0
  312. package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +622 -0
  313. package/lib/oh-story/skills/story-long-write/references/quality-checklist.md +184 -0
  314. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +109 -0
  315. package/lib/oh-story/skills/story-long-write/references/reversal-toolkit.md +349 -0
  316. package/lib/oh-story/skills/story-long-write/references/state-tracking.md +108 -0
  317. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +409 -0
  318. package/lib/oh-story/skills/story-long-write/references/style-craft.md +347 -0
  319. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +510 -0
  320. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +161 -0
  321. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +140 -0
  322. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +171 -0
  323. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +99 -0
  324. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +284 -0
  325. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +421 -0
  326. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +1371 -0
  327. package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +330 -0
  328. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +241 -0
  329. package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +410 -0
  330. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +1139 -0
  331. package/lib/oh-story/skills/story-review/SKILL.md +483 -0
  332. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +582 -0
  333. package/lib/oh-story/skills/story-review/references/banned-words.md +147 -0
  334. package/lib/oh-story/skills/story-review/references/character-relations.md +385 -0
  335. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +282 -0
  336. package/lib/oh-story/skills/story-review/references/plot-core-methods.md +522 -0
  337. package/lib/oh-story/skills/story-review/references/quality-checklist.md +250 -0
  338. package/lib/oh-story/skills/story-review/references/quality-rubric.md +55 -0
  339. package/lib/oh-story/skills/story-review/references/rubrics/fanqie.md +18 -0
  340. package/lib/oh-story/skills/story-review/references/rubrics/qidian.md +19 -0
  341. package/lib/oh-story/skills/story-review/references/rubrics/zhihu.md +20 -0
  342. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +161 -0
  343. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +1371 -0
  344. package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +330 -0
  345. package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +410 -0
  346. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +1139 -0
  347. package/lib/oh-story/skills/story-setup/SKILL.md +495 -0
  348. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +582 -0
  349. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +147 -0
  350. package/lib/oh-story/skills/story-setup/references/agent-references/character-basics.md +445 -0
  351. package/lib/oh-story/skills/story-setup/references/agent-references/character-design-methods.md +428 -0
  352. package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +385 -0
  353. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +282 -0
  354. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +426 -0
  355. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-methods.md +179 -0
  356. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +169 -0
  357. package/lib/oh-story/skills/story-setup/references/agent-references/genre-catalog.md +480 -0
  358. package/lib/oh-story/skills/story-setup/references/agent-references/genre-core-mechanics.md +415 -0
  359. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//344/270/234/346/226/271/344/273/231/344/276/240.md +51 -0
  360. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//344/274/240/347/273/237/347/216/204/345/271/273.md +51 -0
  361. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/216/206/345/217/262/345/217/244/344/273/243.md +51 -0
  362. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/216/206/345/217/262/350/204/221/346/264/236.md +51 -0
  363. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/217/214/347/224/267/344/270/273.md +53 -0
  364. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/217/244/350/250/200/350/204/221/346/264/236.md +51 -0
  365. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/217/244/351/243/216/344/270/226/346/203/205.md +51 -0
  366. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/245/263/351/242/221/346/202/254/347/226/221.md +51 -0
  367. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/245/263/351/242/221/347/247/215/347/224/260.md +51 -0
  368. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/256/253/346/226/227/345/256/205/346/226/227.md +51 -0
  369. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/271/264/344/273/243.md +53 -0
  370. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//345/277/253/347/251/277.md +51 -0
  371. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/202/254/347/226/221/347/201/265/345/274/202.md +51 -0
  372. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/202/254/347/226/221/350/204/221/346/264/236.md +51 -0
  373. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/210/230/347/245/236/350/265/230/345/251/277.md +51 -0
  374. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/212/227/346/210/230/350/260/215/346/210/230.md +51 -0
  375. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/230/237/345/205/211/347/222/200/347/222/250.md +51 -0
  376. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/260/221/345/233/275/350/250/200/346/203/205.md +51 -0
  377. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//346/270/270/346/210/217/344/275/223/350/202/262.md +51 -0
  378. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//347/216/204/345/271/273/350/204/221/346/264/236.md +53 -0
  379. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//347/216/204/345/271/273/350/250/200/346/203/205.md +51 -0
  380. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//347/216/260/350/250/200/350/204/221/346/264/236.md +51 -0
  381. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//347/247/221/345/271/273/346/234/253/344/270/226.md +51 -0
  382. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//350/201/214/345/234/272/345/251/232/346/201/213.md +51 -0
  383. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//350/245/277/346/226/271/345/245/207/345/271/273.md +51 -0
  384. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//350/261/252/351/227/250/346/200/273/350/243/201.md +53 -0
  385. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/344/277/256/347/234/237.md +51 -0
  386. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/346/227/245/345/270/270.md +53 -0
  387. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/347/247/215/347/224/260.md +51 -0
  388. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/350/204/221/346/264/236.md +53 -0
  389. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/351/253/230/346/255/246.md +53 -0
  390. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards//351/235/222/346/230/245/347/224/234/345/256/240.md +51 -0
  391. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +91 -0
  392. package/lib/oh-story/skills/story-setup/references/agent-references/genre-readers.md +235 -0
  393. package/lib/oh-story/skills/story-setup/references/agent-references/genre-writing-formulas.md +444 -0
  394. package/lib/oh-story/skills/story-setup/references/agent-references/hooks-chapter.md +225 -0
  395. package/lib/oh-story/skills/story-setup/references/agent-references/hooks-suspense.md +352 -0
  396. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +349 -0
  397. package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +457 -0
  398. package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +287 -0
  399. package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +389 -0
  400. package/lib/oh-story/skills/story-setup/references/agent-references/plot-core-methods.md +522 -0
  401. package/lib/oh-story/skills/story-setup/references/agent-references/quality-checklist.md +247 -0
  402. package/lib/oh-story/skills/story-setup/references/agent-references/reversal-toolkit.md +349 -0
  403. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +409 -0
  404. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +510 -0
  405. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +421 -0
  406. package/lib/oh-story/skills/story-short-analyze/SKILL.md +278 -0
  407. package/lib/oh-story/skills/story-short-analyze/references/anti-ai-writing.md +582 -0
  408. package/lib/oh-story/skills/story-short-analyze/references/banned-words.md +147 -0
  409. package/lib/oh-story/skills/story-short-analyze/references/character-basics.md +453 -0
  410. package/lib/oh-story/skills/story-short-analyze/references/character-design-methods.md +438 -0
  411. package/lib/oh-story/skills/story-short-analyze/references/character-relations.md +395 -0
  412. package/lib/oh-story/skills/story-short-analyze/references/deconstruction-examples.md +79 -0
  413. package/lib/oh-story/skills/story-short-analyze/references/genre-catalog.md +490 -0
  414. package/lib/oh-story/skills/story-short-analyze/references/genre-core-mechanics.md +425 -0
  415. package/lib/oh-story/skills/story-short-analyze/references/genre-readers.md +245 -0
  416. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-formulas.md +453 -0
  417. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-techniques.md +475 -0
  418. package/lib/oh-story/skills/story-short-analyze/references/hooks-chapter.md +225 -0
  419. package/lib/oh-story/skills/story-short-analyze/references/hooks-paragraph.md +146 -0
  420. package/lib/oh-story/skills/story-short-analyze/references/hooks-suspense.md +352 -0
  421. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +685 -0
  422. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +165 -0
  423. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +561 -0
  424. package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +242 -0
  425. package/lib/oh-story/skills/story-short-analyze/references/real-market-data.md +90 -0
  426. package/lib/oh-story/skills/story-short-analyze/references/zhihu-style.md +54 -0
  427. package/lib/oh-story/skills/story-short-scan/SKILL.md +212 -0
  428. package/lib/oh-story/skills/story-short-scan/references/real-market-data.md +90 -0
  429. package/lib/oh-story/skills/story-short-write/SKILL.md +433 -0
  430. package/lib/oh-story/skills/story-short-write/references/banned-words.md +147 -0
  431. package/lib/oh-story/skills/story-short-write/references/cross-book-recall.md +62 -0
  432. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +282 -0
  433. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +179 -0
  434. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +159 -0
  435. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +156 -0
  436. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +163 -0
  437. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +167 -0
  438. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +178 -0
  439. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +166 -0
  440. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +159 -0
  441. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/262/231/351/233/225/350/204/221/346/264/236.md +151 -0
  442. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +153 -0
  443. 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 +210 -0
  444. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +444 -0
  445. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +488 -0
  446. package/lib/oh-story/skills/story-short-write/references/hooks-chapter.md +225 -0
  447. package/lib/oh-story/skills/story-short-write/references/hooks-paragraph.md +146 -0
  448. package/lib/oh-story/skills/story-short-write/references/hooks-suspense.md +352 -0
  449. package/lib/oh-story/skills/story-short-write/references/output-contract.md +165 -0
  450. package/lib/oh-story/skills/story-short-write/references/quality-checklist.md +222 -0
  451. package/lib/oh-story/skills/story-short-write/references/reversal-toolkit.md +349 -0
  452. package/lib/oh-story/skills/story-short-write/references/short-craft.md +185 -0
  453. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +133 -0
  454. package/lib/oh-story/skills/story-short-write/references/short-format.md +167 -0
  455. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +35 -0
  456. package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +158 -0
  457. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +153 -0
  458. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +1371 -0
  459. package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +330 -0
  460. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +241 -0
  461. package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +410 -0
  462. package/lib/types/client/file-activity.d.ts +30 -0
  463. package/lib/types/client/file-activity.d.ts.map +1 -0
  464. package/lib/types/client/file-tree.d.ts +20 -0
  465. package/lib/types/client/file-tree.d.ts.map +1 -0
  466. package/lib/types/client/index.d.ts +12 -0
  467. package/lib/types/client/index.d.ts.map +1 -0
  468. package/lib/types/client/jsonl-preview.d.ts +15 -0
  469. package/lib/types/client/jsonl-preview.d.ts.map +1 -0
  470. package/lib/types/client/markdown-preview.d.ts +38 -0
  471. package/lib/types/client/markdown-preview.d.ts.map +1 -0
  472. package/lib/types/index.d.ts +25 -0
  473. package/lib/types/index.d.ts.map +1 -0
  474. package/lib/types/native-hooks.d.ts +16 -0
  475. package/lib/types/native-hooks.d.ts.map +1 -0
  476. package/lib/types/role-provider.d.ts +6 -0
  477. package/lib/types/role-provider.d.ts.map +1 -0
  478. package/lib/types/role-tool.d.ts +10 -0
  479. package/lib/types/role-tool.d.ts.map +1 -0
  480. package/lib/types/skill-provider.d.ts +15 -0
  481. package/lib/types/skill-provider.d.ts.map +1 -0
  482. package/lib/types/workspace-request-trust.d.ts +13 -0
  483. package/lib/types/workspace-request-trust.d.ts.map +1 -0
  484. package/lib/types/workspace-route.d.ts +10 -0
  485. package/lib/types/workspace-route.d.ts.map +1 -0
  486. package/lib/types/workspace-security.d.ts +9 -0
  487. package/lib/types/workspace-security.d.ts.map +1 -0
  488. package/package.json +107 -0
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env python3
2
+ """Offline self-test for screenplay indexing and voice-sheet projection."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import json
8
+ import sys
9
+ import tempfile
10
+ from pathlib import Path
11
+
12
+ from duration_estimate import StaleIndex, estimate, index_blocks
13
+ from screenplay_index import build_index
14
+ from voice_sheet_check import check
15
+
16
+ MINIMUM_PYTHON = (3, 9)
17
+ if sys.version_info < MINIMUM_PYTHON:
18
+ raise SystemExit("selftest.py requires Python 3.9 or newer")
19
+
20
+
21
+ def require(condition: bool, message: str) -> None:
22
+ if not condition:
23
+ raise AssertionError(message)
24
+
25
+
26
+ def codes(result: dict[str, object]) -> set[str]:
27
+ findings = result["findings"]
28
+ assert isinstance(findings, list)
29
+ return {finding["code"] for finding in findings}
30
+
31
+
32
+ def measure_text(body: str, project: dict[str, object] | None = None) -> dict[str, object]:
33
+ """Index a screenplay the documented way, then estimate from that index.
34
+
35
+ The estimate has no reader of its own. Going through ``build_index`` here is
36
+ the point of the test, not overhead: it is what stops this script and the
37
+ index from holding two opinions about the same line.
38
+ """
39
+ with tempfile.TemporaryDirectory() as directory:
40
+ root = Path(directory)
41
+ screenplay = root / "screenplay.md"
42
+ index_path = root / "screenplay-index.jsonl"
43
+ screenplay.write_text(
44
+ "# EP001\n\n## EP001-SC001 内 · 房间 · 日\n\n" + body, encoding="utf-8"
45
+ )
46
+ build_index(
47
+ screenplay,
48
+ index_path,
49
+ source_ref="剧集/EP001/screenplay.md",
50
+ speakers=["甲"],
51
+ )
52
+ records = [
53
+ json.loads(line)
54
+ for line in index_path.read_text(encoding="utf-8").splitlines()
55
+ if line.strip()
56
+ ]
57
+ source = screenplay.read_bytes()
58
+ blocks, review = index_blocks(records, source)
59
+ return estimate(source, blocks, project, review)
60
+
61
+
62
+ def test_duration_reports_counts_without_rates() -> None:
63
+ """No declared rates must yield no seconds -- never a default guess."""
64
+ report = measure_text("甲:一二三四五。\n\n他把门关上。\n")
65
+ require(report["seconds"] is None, "seconds must stay unknown without rates")
66
+ require(report["counts"]["dialogue_characters"] == 6, "spoken characters counted")
67
+ require(report["counts"]["action_paragraphs"] == 1, "action paragraph counted")
68
+
69
+
70
+ def test_duration_uses_the_projects_own_rates() -> None:
71
+ project = {
72
+ "format": {
73
+ "target_seconds_per_episode": 10,
74
+ "pacing": {
75
+ "spoken_characters_per_second": 3.0,
76
+ "seconds_per_action_paragraph": 2.0,
77
+ },
78
+ }
79
+ }
80
+ report = measure_text("甲:一二三四五六\n\n他把门关上。\n", project)
81
+ # Punctuation counts as spoken time because it stands for the pause it
82
+ # creates; here the line is six bare characters: 6 / 3.0 + 1 * 2.0 == 4.0.
83
+ require(report["seconds"] == 4.0, f"expected 4.0s, got {report['seconds']}")
84
+ require(report["delta_seconds"] == -6.0, "delta is reported against the target")
85
+
86
+
87
+ def test_production_tags_are_not_performed_time() -> None:
88
+ report = measure_text("[画面文字] 账号后台:粉丝 2\n\n他把门关上。\n")
89
+ require(report["counts"]["production_tag_lines"] == 1, "tag counted separately")
90
+ require(report["counts"]["action_paragraphs"] == 1, "tag is not an action")
91
+
92
+
93
+ def test_voice_over_is_performed_time() -> None:
94
+ """[VO] is a line delivered off-camera, not an instruction to a later stage."""
95
+ report = measure_text("[VO] 甲:一二三四五。\n")
96
+ require(report["counts"]["dialogue_lines"] == 1, "[VO] is a spoken line")
97
+ require(report["counts"]["dialogue_characters"] == 6, "[VO] characters are timed")
98
+ require(report["counts"]["production_tag_lines"] == 0, "[VO] is not a mute tag")
99
+
100
+
101
+ def test_one_action_paragraph_is_one_paragraph_however_many_lines() -> None:
102
+ """The format contract lets an action paragraph run several lines."""
103
+ report = measure_text("他站起来,\n走到窗边,\n把窗帘拉开。\n")
104
+ require(
105
+ report["counts"]["action_paragraphs"] == 1,
106
+ f"expected 1 paragraph, got {report['counts']['action_paragraphs']}",
107
+ )
108
+
109
+
110
+ def test_comments_are_not_production_content() -> None:
111
+ """A Markdown comment is a note to the writer, not a line to perform."""
112
+ report = measure_text("<!-- 待确认:这一段的转场是否保留。 -->\n")
113
+ require(report["counts"]["dialogue_lines"] == 0, "a comment is not dialogue")
114
+ require(report["counts"]["dialogue_characters"] == 0, "a comment is not spoken")
115
+ require(report["counts"]["action_paragraphs"] == 0, "a comment is not an action")
116
+
117
+
118
+ def test_an_index_from_other_bytes_is_refused() -> None:
119
+ """Spans from a stale index land on text it never classified."""
120
+ stale = [
121
+ {
122
+ "record_type": "screenplay_index_meta",
123
+ "source_byte_length": 999_999,
124
+ }
125
+ ]
126
+ try:
127
+ index_blocks(stale, b"short")
128
+ except StaleIndex:
129
+ return
130
+ raise AssertionError("a stale index must be refused, not measured")
131
+
132
+
133
+ def main() -> int:
134
+ with tempfile.TemporaryDirectory() as directory:
135
+ root = Path(directory)
136
+ screenplay = root / "screenplay.md"
137
+ index_path = root / "screenplay-index.jsonl"
138
+ screenplay.write_text(
139
+ "# EP001\n\n## EP001-SC001 内 · 客厅 · 夜\n\n陈予安推开门。\n\n陈予安:我回来了。\n",
140
+ encoding="utf-8",
141
+ )
142
+ summary = build_index(
143
+ screenplay,
144
+ index_path,
145
+ source_ref="剧集/EP001/screenplay.md",
146
+ speakers=["陈予安"],
147
+ )
148
+ require(summary["review_status"] == "clean", "valid screenplay index")
149
+ index_bytes = index_path.read_bytes()
150
+ records = [
151
+ json.loads(line) for line in index_bytes.decode("utf-8").splitlines()
152
+ ]
153
+ dialogue = next(record for record in records if record.get("kind") == "dialogue")
154
+
155
+ header = {
156
+ "record_type": "sources",
157
+ "schema_version": "1.0.0",
158
+ "sources": {
159
+ "screenplay-index": {
160
+ "owner": "short-drama-write",
161
+ "artifact": "剧集/EP001/screenplay-index.jsonl",
162
+ "hash": hashlib.sha256(index_bytes).hexdigest(),
163
+ }
164
+ },
165
+ }
166
+ line = {
167
+ "line_id": "LINE-001",
168
+ "channel": "sync",
169
+ "speaker_display": "陈予安",
170
+ "line_text": "我回来了。",
171
+ "source_ref": {"src": "screenplay-index", "record_id": dialogue["block_id"]},
172
+ }
173
+ sheet = [header, line]
174
+ result = check(sheet, records, screenplay.read_bytes())
175
+ require(result["status"] == "pass", "faithful voice sheet")
176
+ require(result["lines"] == 1, "the sources header is not a voice line")
177
+
178
+ # A sheet written before the compact form still resolves; real projects
179
+ # hold both spellings and neither is rewritten.
180
+ expanded = [
181
+ dict(
182
+ line,
183
+ source_ref={
184
+ "owner": "short-drama-write",
185
+ "artifact": "剧集/EP001/screenplay-index.jsonl",
186
+ "hash": hashlib.sha256(index_bytes).hexdigest(),
187
+ "record_id": dialogue["block_id"],
188
+ },
189
+ )
190
+ ]
191
+ require(
192
+ check(expanded, records, screenplay.read_bytes())["status"] == "pass",
193
+ "expanded voice sheet",
194
+ )
195
+
196
+ undeclared = [header, dict(line, source_ref={"src": "screenplay", "record_id": "X"})]
197
+ require(
198
+ "VOICE_SOURCE_REF_UNDECLARED"
199
+ in codes(check(undeclared, records, screenplay.read_bytes())),
200
+ "src without a sources entry was not detected",
201
+ )
202
+
203
+ unbound = [header, dict(line, source_ref={"record_id": dialogue["block_id"]})]
204
+ require(
205
+ "VOICE_SOURCE_REF_MISSING"
206
+ in codes(check(unbound, records, screenplay.read_bytes())),
207
+ "source_ref naming no upstream snapshot was not detected",
208
+ )
209
+
210
+ changed = [header, dict(line, line_text="我走了。")]
211
+ require(
212
+ check(changed, records, screenplay.read_bytes())["status"] == "fail",
213
+ "changed voice line was not detected",
214
+ )
215
+
216
+ test_duration_reports_counts_without_rates()
217
+ test_duration_uses_the_projects_own_rates()
218
+ test_production_tags_are_not_performed_time()
219
+ test_voice_over_is_performed_time()
220
+ test_one_action_paragraph_is_one_paragraph_however_many_lines()
221
+ test_comments_are_not_production_content()
222
+ test_an_index_from_other_bytes_is_refused()
223
+
224
+ print("14 self-tests passed")
225
+ return 0
226
+
227
+
228
+ if __name__ == "__main__":
229
+ raise SystemExit(main())
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env python3
2
+ """Prove a voice record sheet is still a projection of the screenplay.
3
+
4
+ The sheet exists to be carried into a recording session, which is exactly the
5
+ moment nobody can check it against the script. A line edited in the sheet, or a
6
+ script revised after the sheet was built, both read as a perfectly ordinary
7
+ sheet — so the comparison has to be mechanical: resolve each line's block in
8
+ the derived index, slice those exact bytes out of the screenplay, and compare.
9
+
10
+ The script reads accepted creator files and writes nothing.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import re
18
+ import sys
19
+ from pathlib import Path
20
+ from collections.abc import Mapping
21
+ from typing import Any, NamedTuple
22
+
23
+
24
+ # Creators run these scripts on whatever interpreter their machine provides, so
25
+ # an unsupported version must say so instead of failing inside an import.
26
+ MINIMUM_PYTHON = (3, 9)
27
+ if sys.version_info < MINIMUM_PYTHON:
28
+ raise SystemExit(
29
+ "short-drama needs Python {}.{} or newer; this interpreter is {}.{}".format(
30
+ *MINIMUM_PYTHON, sys.version_info.major, sys.version_info.minor
31
+ )
32
+ )
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # REFERENCE RESOLVER -- reference implementation.
36
+ #
37
+ # Each skill checker carries its own copy of this block. The suite has no shared
38
+ # library on purpose: a skill must stay runnable after copying only its own
39
+ # directory, so duplicating these few lines across skills is the correct shape.
40
+ # Copy the block verbatim; do not import it.
41
+ # ---------------------------------------------------------------------------
42
+
43
+ SOURCES_RECORD_TYPE = "sources"
44
+ SOURCES_SCHEMA_VERSION = "1.0.0"
45
+
46
+
47
+ class ResolvedRef(NamedTuple):
48
+ """An upstream reference with its snapshot resolved, whichever form it used."""
49
+
50
+ owner: str
51
+ artifact: str
52
+ record_id: str | None
53
+ field: str | None
54
+ authority: str | None
55
+
56
+
57
+ class RefFinding(NamedTuple):
58
+ """A structural defect in a reference object."""
59
+
60
+ code: str
61
+ location: str
62
+ detail: str
63
+
64
+
65
+ def load_sources(document: Any) -> dict[str, dict[str, Any]]:
66
+ """Return the ``sources`` declaration of a parsed file, or ``{}`` if absent.
67
+
68
+ Accepts a parsed ``.json`` document (a dict) or the parsed record list of a
69
+ ``.jsonl`` file, whose declaration lives on the first record.
70
+ """
71
+ if isinstance(document, list):
72
+ document = document[0] if document else None
73
+ if not isinstance(document, dict):
74
+ return {}
75
+ declared = document.get("sources")
76
+ if not isinstance(declared, dict):
77
+ return {}
78
+ return {key: value for key, value in declared.items() if isinstance(value, dict)}
79
+
80
+
81
+ def resolve_ref(
82
+ ref: Any, sources: dict[str, dict[str, Any]], location: str
83
+ ) -> tuple[ResolvedRef | None, RefFinding | None]:
84
+ """Resolve a reference object written in either the compact or expanded form."""
85
+ if not isinstance(ref, dict):
86
+ return None, RefFinding("REF_IS_NOT_AN_OBJECT", location, f"got {type(ref).__name__}")
87
+ src = ref.get("src")
88
+ if isinstance(src, str):
89
+ entry = sources.get(src)
90
+ if entry is None:
91
+ return None, RefFinding(
92
+ "REF_SRC_IS_NOT_DECLARED", location, f"src {src!r} has no sources entry"
93
+ )
94
+ owner, artifact = entry.get("owner"), entry.get("artifact")
95
+ if not (isinstance(owner, str) and isinstance(artifact, str)):
96
+ return None, RefFinding(
97
+ "SOURCE_ENTRY_IS_INCOMPLETE",
98
+ location,
99
+ f"sources[{src!r}] needs owner/artifact",
100
+ )
101
+ elif all(isinstance(ref.get(key), str) for key in ("owner", "artifact")):
102
+ owner, artifact = ref["owner"], ref["artifact"]
103
+ else:
104
+ return None, RefFinding(
105
+ "REF_HAS_NO_UPSTREAM_BINDING", location, "needs src, or owner+artifact"
106
+ )
107
+ optional = {
108
+ key: ref[key]
109
+ for key in ("record_id", "field", "authority")
110
+ if isinstance(ref.get(key), str)
111
+ }
112
+ return (
113
+ ResolvedRef(
114
+ owner,
115
+ artifact,
116
+ optional.get("record_id"),
117
+ optional.get("field"),
118
+ optional.get("authority"),
119
+ ),
120
+ None,
121
+ )
122
+
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # END REFERENCE RESOLVER
126
+ # ---------------------------------------------------------------------------
127
+
128
+ SCHEMA_VERSION = "1.0.0"
129
+ CHANNELS = {"sync", "dubbed", "VO", "OS"}
130
+ # `[VO]` and `[OS]` are spoken lines that happen to be delivered off-camera, and
131
+ # the index already records them with a `tag` and a `speaker`. Projecting only
132
+ # `dialogue` blocks made the VO and OS channels above unreachable, so an episode
133
+ # that carries its interiority in voice-over reported a clean sheet while the
134
+ # recording list was missing every one of those lines.
135
+ VOICED_TAGS = {"VO", "OS"}
136
+ # `[VO] 角色:台词` — the tag is stripped before the line grammar is applied.
137
+ VOICE_TAG_PREFIX = re.compile(r"^\[(?:VO|OS)\]\s*")
138
+
139
+
140
+ def _is_voiced(block: Mapping[str, Any]) -> bool:
141
+ kind = block.get("kind")
142
+ if kind == "dialogue":
143
+ return True
144
+ return kind == "production_tag" and block.get("tag") in VOICED_TAGS
145
+ # The resolver speaks the suite-wide reference vocabulary; this checker reports
146
+ # in its own.
147
+ REF_FINDING_CODES = {
148
+ "REF_IS_NOT_AN_OBJECT": "VOICE_SOURCE_REF_MISSING",
149
+ "REF_HAS_NO_UPSTREAM_BINDING": "VOICE_SOURCE_REF_MISSING",
150
+ "REF_SRC_IS_NOT_DECLARED": "VOICE_SOURCE_REF_UNDECLARED",
151
+ "SOURCE_ENTRY_IS_INCOMPLETE": "VOICE_SOURCE_REF_UNDECLARED",
152
+ }
153
+ # `角色(可表演提示):台词` — the cue is optional and never part of the line.
154
+ DIALOGUE = re.compile(
155
+ r"^(?P<speaker>[^((::]+)(?:[((](?P<cue>[^))]*)[))])?\s*[::]\s*(?P<line>.*)$",
156
+ re.DOTALL,
157
+ )
158
+
159
+
160
+ class CheckError(ValueError):
161
+ """The inputs cannot be checked at all, as opposed to failing a check."""
162
+
163
+
164
+ def _load_jsonl(path: Path) -> list[dict[str, Any]]:
165
+ records: list[dict[str, Any]] = []
166
+ try:
167
+ text = path.read_text(encoding="utf-8")
168
+ except (OSError, UnicodeError) as error:
169
+ raise CheckError(f"unreadable JSONL: {path}") from error
170
+ for number, line in enumerate(text.splitlines(), start=1):
171
+ if not line.strip():
172
+ continue
173
+ try:
174
+ record = json.loads(line)
175
+ except json.JSONDecodeError as error:
176
+ raise CheckError(f"invalid JSONL at {path.name}:{number}") from error
177
+ if not isinstance(record, dict):
178
+ raise CheckError(f"JSONL needs one object per line: {path.name}:{number}")
179
+ records.append(record)
180
+ return records
181
+
182
+
183
+ def _finding(code: str, message: str, **detail: Any) -> dict[str, Any]:
184
+ return {"code": code, "message": message, **detail}
185
+
186
+
187
+ def _blocks_by_id(index: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
188
+ return {
189
+ record["block_id"]: record
190
+ for record in index
191
+ if record.get("record_type") == "block"
192
+ and isinstance(record.get("block_id"), str)
193
+ }
194
+
195
+
196
+ def check(
197
+ sheet: list[dict[str, Any]],
198
+ index: list[dict[str, Any]],
199
+ screenplay: bytes,
200
+ ) -> dict[str, Any]:
201
+ findings: list[dict[str, Any]] = []
202
+ blocks = _blocks_by_id(index)
203
+ seen: set[str] = set()
204
+ # The first record declares the upstream snapshots this sheet references; the
205
+ # rest are voice lines.
206
+ sources = load_sources(sheet)
207
+ lines = [record for record in sheet if record.get("record_type") != SOURCES_RECORD_TYPE]
208
+
209
+ for record in lines:
210
+ line_id = record.get("line_id")
211
+ if not isinstance(line_id, str):
212
+ findings.append(_finding("VOICE_LINE_HAS_NO_ID", "a line record has no id"))
213
+ continue
214
+ if line_id in seen:
215
+ findings.append(
216
+ _finding("VOICE_LINE_ID_REPEATS", "line_id must be unique", line_id=line_id)
217
+ )
218
+ continue
219
+ seen.add(line_id)
220
+
221
+ channel = record.get("channel")
222
+ if channel not in CHANNELS:
223
+ findings.append(
224
+ _finding(
225
+ "VOICE_CHANNEL_INVALID",
226
+ "channel must be sync, dubbed, VO, or OS",
227
+ line_id=line_id,
228
+ channel=channel,
229
+ )
230
+ )
231
+
232
+ resolved, defect = resolve_ref(record.get("source_ref"), sources, line_id)
233
+ if defect is not None:
234
+ findings.append(
235
+ _finding(
236
+ REF_FINDING_CODES[defect.code],
237
+ f"source_ref does not name an upstream snapshot: {defect.detail}",
238
+ line_id=line_id,
239
+ )
240
+ )
241
+ continue
242
+ if resolved is None or resolved.record_id is None:
243
+ findings.append(
244
+ _finding(
245
+ "VOICE_SOURCE_REF_MISSING",
246
+ "a line must bind the screenplay block it projects",
247
+ line_id=line_id,
248
+ )
249
+ )
250
+ continue
251
+ record_id = resolved.record_id
252
+ block = blocks.get(record_id)
253
+ if block is None:
254
+ findings.append(
255
+ _finding(
256
+ "VOICE_SOURCE_REF_UNRESOLVABLE",
257
+ "source_ref names a block that is not in the index",
258
+ line_id=line_id,
259
+ record_id=record_id,
260
+ )
261
+ )
262
+ continue
263
+ if not _is_voiced(block):
264
+ findings.append(
265
+ _finding(
266
+ "VOICE_SOURCE_IS_NOT_DIALOGUE",
267
+ "a voice line must project a spoken block: dialogue, [VO] or [OS]",
268
+ line_id=line_id,
269
+ record_id=record_id,
270
+ kind=block.get("kind"),
271
+ )
272
+ )
273
+ continue
274
+
275
+ start, end = block.get("byte_start"), block.get("byte_end")
276
+ if (
277
+ not isinstance(start, int)
278
+ or not isinstance(end, int)
279
+ or not 0 <= start < end <= len(screenplay)
280
+ ):
281
+ findings.append(
282
+ _finding(
283
+ "VOICE_BLOCK_SPAN_INVALID",
284
+ "the indexed block span does not fit this screenplay",
285
+ line_id=line_id,
286
+ record_id=record_id,
287
+ )
288
+ )
289
+ continue
290
+ # The channel decides how the line is booked, and off-camera and
291
+ # on-camera carry completely different room for change. It was checked
292
+ # against the enum and never against the block it projects, so a [VO]
293
+ # line could be booked `sync` and go to the booth under lip-sync
294
+ # constraints. `_is_voiced` already reads the block's tag; this compares.
295
+ expected = block.get("tag") if block.get("kind") == "production_tag" else "on_camera"
296
+ if expected in VOICED_TAGS and channel not in {expected, "dubbed"}:
297
+ findings.append(
298
+ _finding(
299
+ "VOICE_CHANNEL_DISAGREES_WITH_BLOCK",
300
+ "an off-camera block must be booked on its own channel",
301
+ line_id=line_id,
302
+ record_id=record_id,
303
+ channel=channel,
304
+ tag=expected,
305
+ )
306
+ )
307
+ elif expected == "on_camera" and channel in VOICED_TAGS:
308
+ findings.append(
309
+ _finding(
310
+ "VOICE_CHANNEL_DISAGREES_WITH_BLOCK",
311
+ "an on-camera dialogue block must not be booked as VO or OS",
312
+ line_id=line_id,
313
+ record_id=record_id,
314
+ channel=channel,
315
+ )
316
+ )
317
+
318
+ raw = screenplay[start:end]
319
+ try:
320
+ decoded = raw.decode("utf-8")
321
+ except UnicodeDecodeError:
322
+ # A span that begins or ends mid-character is a stale offset. It was
323
+ # crashing the whole check instead of reporting the one line.
324
+ findings.append(
325
+ _finding(
326
+ "VOICE_BLOCK_SPAN_INVALID",
327
+ "the indexed block span does not start and end on characters",
328
+ line_id=line_id,
329
+ record_id=record_id,
330
+ )
331
+ )
332
+ continue
333
+ body = VOICE_TAG_PREFIX.sub("", decoded.strip())
334
+ match = DIALOGUE.match(body)
335
+ if match is None:
336
+ findings.append(
337
+ _finding(
338
+ "VOICE_BLOCK_IS_UNPARSEABLE",
339
+ "the dialogue block does not use the documented line grammar",
340
+ line_id=line_id,
341
+ record_id=record_id,
342
+ )
343
+ )
344
+ continue
345
+ if record.get("line_text") != match.group("line"):
346
+ findings.append(
347
+ _finding(
348
+ "VOICE_LINE_TEXT_DIVERGED",
349
+ "line_text is not the screenplay wording; change the screenplay",
350
+ line_id=line_id,
351
+ record_id=record_id,
352
+ )
353
+ )
354
+ indexed_speaker = block.get("speaker")
355
+ if isinstance(indexed_speaker, str) and record.get("speaker_display") not in (
356
+ None,
357
+ indexed_speaker,
358
+ ):
359
+ findings.append(
360
+ _finding(
361
+ "VOICE_SPEAKER_DIVERGED",
362
+ "speaker_display disagrees with the indexed speaker",
363
+ line_id=line_id,
364
+ record_id=record_id,
365
+ )
366
+ )
367
+
368
+ dialogue_blocks = {
369
+ block_id for block_id, block in blocks.items() if _is_voiced(block)
370
+ }
371
+ projected: set[str] = set()
372
+ for record in lines:
373
+ covered, _defect = resolve_ref(record.get("source_ref"), sources, "")
374
+ if covered is not None and covered.record_id is not None:
375
+ projected.add(covered.record_id)
376
+ # Reported, never a finding: a sheet may legitimately cover one actor or one
377
+ # scene, so an incomplete sheet is a scope decision, not a defect.
378
+ uncovered = sorted(dialogue_blocks - projected)
379
+
380
+ return {
381
+ "schema_version": SCHEMA_VERSION,
382
+ "lines": len(lines),
383
+ "dialogue_blocks": len(dialogue_blocks),
384
+ "uncovered_dialogue_blocks": uncovered,
385
+ "findings": findings,
386
+ "status": "pass" if not findings else "fail",
387
+ }
388
+
389
+
390
+ def build_parser() -> argparse.ArgumentParser:
391
+ parser = argparse.ArgumentParser(
392
+ description="Check a voice record sheet against its screenplay and index."
393
+ )
394
+ parser.add_argument("sheet", type=Path, help="the voice record sheet JSONL")
395
+ parser.add_argument("--index", type=Path, required=True)
396
+ parser.add_argument("--screenplay", type=Path, required=True)
397
+ return parser
398
+
399
+
400
+ def main(argv: list[str] | None = None) -> int:
401
+ args = build_parser().parse_args(argv)
402
+ try:
403
+ result = check(
404
+ _load_jsonl(args.sheet),
405
+ _load_jsonl(args.index),
406
+ args.screenplay.read_bytes(),
407
+ )
408
+ except (CheckError, OSError) as error:
409
+ print(f"{type(error).__name__}: {error}", file=sys.stderr)
410
+ return 2
411
+ print(json.dumps(result, ensure_ascii=False, sort_keys=True))
412
+ return 0 if result["status"] == "pass" else 1
413
+
414
+
415
+ if __name__ == "__main__":
416
+ raise SystemExit(main())