@oh-story/dsh 0.1.3 → 0.1.5

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 (524) hide show
  1. package/README.md +58 -12
  2. package/lib/client.js +2970 -537
  3. package/lib/client.js.map +4 -4
  4. package/lib/drama/manifest.json +218 -178
  5. package/lib/drama/skills/short-drama/SKILL.md +110 -117
  6. package/lib/drama/skills/short-drama/references/audience-reveal.md +14 -67
  7. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +26 -77
  8. package/lib/drama/skills/short-drama/references/creator-documents.md +204 -0
  9. package/lib/drama/skills/short-drama/references/creator-workflow.md +73 -63
  10. package/lib/drama/skills/short-drama/references/knowhow-index.md +5 -0
  11. package/lib/drama/skills/short-drama/references/look-development.md +2 -3
  12. package/lib/drama/skills/short-drama/references/pickup-and-alternate.md +14 -48
  13. package/lib/drama/skills/short-drama/references/production-form-profiles.md +1 -1
  14. package/lib/drama/skills/short-drama/references/reference-roles.md +32 -135
  15. package/lib/drama/skills/short-drama/references/runtime-preflight.md +16 -19
  16. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +1002 -0
  17. package/lib/drama/skills/short-drama/scripts/project_tool.py +485 -5
  18. package/lib/drama/skills/short-drama-assets/SKILL.md +59 -170
  19. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +18 -25
  20. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +13 -15
  21. package/lib/drama/skills/short-drama-assets/references/continuity-delta.md +11 -14
  22. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +116 -0
  23. package/lib/drama/skills/short-drama-assets/references/identity-vs-variant.md +6 -12
  24. package/lib/drama/skills/short-drama-assets/references/occurrence-extraction.md +8 -11
  25. package/lib/drama/skills/short-drama-assets/references/prop-and-state.md +21 -2
  26. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +12 -105
  27. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +12 -32
  28. package/lib/drama/skills/short-drama-assets/scripts/asset_check.py +1 -1
  29. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +45 -155
  30. package/lib/drama/skills/short-drama-image-prompts/references/character-and-look.md +12 -13
  31. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +12 -13
  32. package/lib/drama/skills/short-drama-image-prompts/references/edit-and-revision.md +39 -51
  33. package/lib/drama/skills/short-drama-image-prompts/references/location-plate.md +3 -4
  34. package/lib/drama/skills/short-drama-image-prompts/references/look-and-state-variant.md +15 -14
  35. package/lib/drama/skills/short-drama-image-prompts/references/lookdev-frame.md +11 -15
  36. package/lib/drama/skills/short-drama-image-prompts/references/production-sheet-recipes.md +1 -1
  37. package/lib/drama/skills/short-drama-image-prompts/references/prop-plate.md +4 -7
  38. package/lib/drama/skills/short-drama-image-prompts/references/review-and-fixtures.md +23 -23
  39. package/lib/drama/skills/short-drama-image-prompts/references/stage-contract.md +8 -65
  40. package/lib/drama/skills/short-drama-image-prompts/scripts/image_prompt_check.py +8 -8
  41. package/lib/drama/skills/short-drama-image-prompts/scripts/selftest.py +4 -2
  42. package/lib/drama/skills/short-drama-produce/SKILL.md +67 -15
  43. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +65 -8
  44. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +85 -0
  45. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +27 -10
  46. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +362 -3
  47. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +627 -40
  48. package/lib/drama/skills/short-drama-produce/scripts/selftest.py +5 -4
  49. package/lib/drama/skills/short-drama-review/SKILL.md +61 -123
  50. package/lib/drama/skills/short-drama-review/references/anti-template-repair.md +10 -15
  51. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +15 -16
  52. package/lib/drama/skills/short-drama-review/references/project-calibration.md +7 -9
  53. package/lib/drama/skills/short-drama-review/references/review-method.md +8 -9
  54. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +9 -13
  55. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +104 -4
  56. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +27 -19
  57. package/lib/drama/skills/short-drama-review/references/stage-contract.md +4 -63
  58. package/lib/drama/skills/short-drama-review/scripts/review_check.py +1 -1
  59. package/lib/drama/skills/short-drama-storyboard/SKILL.md +116 -238
  60. package/lib/drama/skills/short-drama-storyboard/assets/shot-template.jsonl +1 -1
  61. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +9 -34
  62. package/lib/drama/skills/short-drama-storyboard/references/comic-keyframe-lexicon.md +156 -0
  63. package/lib/drama/skills/short-drama-storyboard/references/coverage-audition.md +5 -10
  64. package/lib/drama/skills/short-drama-storyboard/references/keyframe-craft.md +12 -7
  65. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +19 -4
  66. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +27 -62
  67. package/lib/drama/skills/short-drama-storyboard/references/scene-visual-plan.md +16 -25
  68. package/lib/drama/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +21 -23
  69. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +8 -10
  70. package/lib/drama/skills/short-drama-storyboard/references/shot-revision-identity.md +9 -13
  71. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +16 -71
  72. package/lib/drama/skills/short-drama-storyboard/scripts/storyboard_check.py +1 -1
  73. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +123 -230
  74. package/lib/drama/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +6 -1
  75. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +17 -1
  76. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +10 -8
  77. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +24 -6
  78. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +2 -2
  79. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +154 -0
  80. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +35 -27
  81. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +8 -11
  82. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +13 -10
  83. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +34 -63
  84. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +41 -0
  85. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +66 -0
  86. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +17 -73
  87. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +115 -0
  88. package/lib/drama/skills/short-drama-video-prompts/scripts/container_check.py +1 -1
  89. package/lib/drama/skills/short-drama-video-prompts/scripts/music_spec_check.py +1 -1
  90. package/lib/drama/skills/short-drama-write/SKILL.md +74 -268
  91. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +7 -0
  92. package/lib/drama/skills/short-drama-write/references/scene-handoff-capsule.md +17 -43
  93. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +15 -15
  94. package/lib/drama/skills/short-drama-write/references/script-craft.md +208 -21
  95. package/lib/drama/skills/short-drama-write/references/stage-contract.md +11 -70
  96. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +1 -1
  97. package/lib/drama/skills/short-drama-write/scripts/screenplay_index.py +2 -2
  98. package/lib/drama/skills/short-drama-write/scripts/voice_sheet_check.py +1 -1
  99. package/lib/index.js +971 -46
  100. package/lib/index.js.map +4 -4
  101. package/lib/novel-to-game/LICENSE +21 -0
  102. package/lib/novel-to-game/README.md +180 -0
  103. package/lib/novel-to-game/README_ZH.md +178 -0
  104. package/lib/novel-to-game/VERSION +1 -0
  105. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +217 -0
  106. package/lib/novel-to-game/examples/jin-ping-mei/build/app/.vercelignore +3 -0
  107. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +130 -0
  108. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  118. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  119. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  120. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  121. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  122. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  123. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  124. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  125. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  126. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  127. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  128. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  129. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  130. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  131. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  132. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  133. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  134. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  135. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  136. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  137. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  138. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  139. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  140. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  141. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  142. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  143. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  144. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  145. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  146. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  147. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  148. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  149. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +1105 -0
  150. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +2539 -0
  151. package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +18 -0
  152. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +147 -0
  153. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +332 -0
  154. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +7472 -0
  155. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +13077 -0
  156. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +3028 -0
  157. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +32 -0
  158. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +10 -0
  159. package/lib/novel-to-game/examples/jin-ping-mei/build/app/vercel.json +23 -0
  160. package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +161 -0
  161. package/lib/novel-to-game/examples/jin-ping-mei/example.json +12 -0
  162. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +31 -0
  163. package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +99 -0
  164. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +41 -0
  165. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +129 -0
  166. package/lib/novel-to-game/manifest.json +508 -0
  167. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +73 -0
  168. package/lib/novel-to-game/skills/game-art-direction/agents/openai.yaml +4 -0
  169. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +119 -0
  170. package/lib/novel-to-game/skills/game-build/SKILL.md +84 -0
  171. package/lib/novel-to-game/skills/game-build/agents/openai.yaml +4 -0
  172. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +109 -0
  173. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +56 -0
  174. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +81 -0
  175. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +37 -0
  176. package/lib/novel-to-game/skills/game-concept/SKILL.md +72 -0
  177. package/lib/novel-to-game/skills/game-concept/agents/openai.yaml +4 -0
  178. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +94 -0
  179. package/lib/novel-to-game/skills/game-qa/SKILL.md +50 -0
  180. package/lib/novel-to-game/skills/game-qa/agents/openai.yaml +4 -0
  181. package/lib/novel-to-game/skills/game-qa/references/qa-contract.md +66 -0
  182. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +45 -0
  183. package/lib/novel-to-game/skills/game-world-design/SKILL.md +63 -0
  184. package/lib/novel-to-game/skills/game-world-design/agents/openai.yaml +4 -0
  185. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +59 -0
  186. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +53 -0
  187. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +84 -0
  188. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +56 -0
  189. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +124 -0
  190. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +77 -0
  191. package/lib/novel-to-game/skills/novel-game-analyze/agents/openai.yaml +4 -0
  192. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +65 -0
  193. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +71 -0
  194. package/lib/novel-to-game/skills/novel-to-game/agents/openai.yaml +4 -0
  195. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +42 -0
  196. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +51 -0
  197. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +65 -0
  198. package/lib/oh-story/manifest.json +419 -309
  199. package/lib/oh-story/roles/character-designer.md +4 -4
  200. package/lib/oh-story/roles/consistency-checker.md +1 -1
  201. package/lib/oh-story/roles/narrative-writer.md +41 -42
  202. package/lib/oh-story/roles/story-architect.md +19 -23
  203. package/lib/oh-story/roles/story-explorer.md +8 -5
  204. package/lib/oh-story/skills/browser-cdp/SKILL.md +1 -1
  205. package/lib/oh-story/skills/story/SKILL.md +16 -9
  206. package/lib/oh-story/skills/story/VERSION +1 -1
  207. package/lib/oh-story/skills/story/references/author-memory.md +163 -0
  208. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +871 -0
  209. package/lib/oh-story/skills/story-cover/SKILL.md +1 -1
  210. package/lib/oh-story/skills/story-deslop/SKILL.md +10 -5
  211. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +2 -1
  212. package/lib/oh-story/skills/story-deslop/references/author-memory.md +163 -0
  213. package/lib/oh-story/skills/story-deslop/references/banned-words.md +6 -4
  214. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +871 -0
  215. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +64 -1
  216. package/lib/oh-story/skills/story-import/SKILL.md +21 -8
  217. package/lib/oh-story/skills/story-import/references/format-and-structure.md +9 -10
  218. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +2 -2
  219. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +1 -1
  220. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +14 -6
  221. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +251 -0
  222. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +98 -9
  223. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +246 -0
  224. package/lib/oh-story/skills/story-long-analyze/SKILL.md +4 -4
  225. package/lib/oh-story/skills/story-long-scan/SKILL.md +1 -1
  226. package/lib/oh-story/skills/story-long-write/SKILL.md +35 -21
  227. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +2 -1
  228. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +7 -3
  229. package/lib/oh-story/skills/story-long-write/references/author-memory.md +163 -0
  230. package/lib/oh-story/skills/story-long-write/references/banned-words.md +6 -4
  231. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +6 -6
  232. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  233. package/lib/oh-story/skills/story-long-write/references/genre-readers.md +1 -1
  234. package/lib/oh-story/skills/{story-setup/references/agent-references/hooks-chapter.md → story-long-write/references/long-chapter-hooks.md} +5 -5
  235. package/lib/oh-story/skills/story-long-write/references/{quality-checklist.md → long-chapter-quality.md} +2 -1
  236. package/lib/oh-story/skills/story-long-write/references/long-format.md +42 -0
  237. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-catalog.md → story-long-write/references/long-genre-catalog.md} +11 -97
  238. package/lib/oh-story/skills/story-long-write/references/{genre-core-mechanics.md → long-genre-mechanics.md} +1 -1
  239. package/lib/oh-story/skills/story-long-write/references/{reversal-toolkit.md → long-reversal.md} +18 -32
  240. package/lib/oh-story/skills/story-long-write/references/{hooks-suspense.md → long-suspense.md} +6 -6
  241. package/lib/oh-story/skills/story-long-write/references/opening-design.md +1 -1
  242. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +1 -1
  243. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  244. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +14 -6
  245. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +32 -27
  246. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +5 -4
  247. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +2 -2
  248. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +17 -11
  249. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +54 -54
  250. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +871 -0
  251. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +64 -1
  252. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +251 -0
  253. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +303 -0
  254. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +98 -9
  255. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +246 -0
  256. package/lib/oh-story/skills/story-review/SKILL.md +26 -17
  257. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +2 -1
  258. package/lib/oh-story/skills/story-review/references/author-memory.md +163 -0
  259. package/lib/oh-story/skills/story-review/references/banned-words.md +6 -4
  260. package/lib/oh-story/skills/story-review/references/{quality-checklist.md → review-quality.md} +22 -21
  261. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +14 -6
  262. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +871 -0
  263. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +64 -1
  264. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +98 -9
  265. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +246 -0
  266. package/lib/oh-story/skills/story-setup/SKILL.md +85 -41
  267. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +72 -0
  268. package/lib/oh-story/skills/story-setup/references/agent-references/agent-reference-profiles.md +55 -0
  269. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +2 -1
  270. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +6 -4
  271. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +9 -10
  272. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  273. package/lib/oh-story/skills/story-setup/references/agent-references/genre-readers.md +1 -1
  274. package/lib/oh-story/skills/{story-short-analyze/references/hooks-chapter.md → story-setup/references/agent-references/long-chapter-hooks.md} +5 -5
  275. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +209 -0
  276. package/lib/oh-story/skills/{story-long-write/references/genre-catalog.md → story-setup/references/agent-references/long-genre-catalog.md} +11 -97
  277. package/lib/oh-story/skills/story-setup/references/agent-references/{genre-core-mechanics.md → long-genre-mechanics.md} +1 -1
  278. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +40 -0
  279. package/lib/oh-story/skills/{story-short-write/references/reversal-toolkit.md → story-setup/references/agent-references/long-reversal.md} +18 -32
  280. package/lib/oh-story/skills/{story-short-write/references/hooks-suspense.md → story-setup/references/agent-references/long-suspense.md} +6 -6
  281. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +1 -1
  282. package/lib/oh-story/skills/story-setup/references/agent-references/{emotional-methods.md → short-emotional-methods.md} +4 -4
  283. package/lib/oh-story/skills/{story-long-write/references/genre-writing-formulas.md → story-setup/references/agent-references/short-genre-formulas.md} +32 -44
  284. package/lib/oh-story/skills/story-setup/references/agent-references/short-quality.md +39 -0
  285. package/lib/oh-story/skills/story-setup/references/agent-references/{reversal-toolkit.md → short-reversal.md} +29 -23
  286. package/lib/oh-story/skills/story-setup/references/agent-references/{hooks-suspense.md → short-suspense.md} +31 -31
  287. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  288. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +55 -55
  289. package/lib/oh-story/skills/story-short-analyze/SKILL.md +28 -28
  290. package/lib/oh-story/skills/story-short-analyze/references/{character-basics.md → analysis-character-basics.md} +1 -1
  291. package/lib/oh-story/skills/story-short-analyze/references/{character-design-methods.md → analysis-character-design.md} +1 -1
  292. package/lib/oh-story/skills/story-short-analyze/references/{character-relations.md → analysis-character-relations.md} +1 -1
  293. package/lib/oh-story/skills/story-short-analyze/references/{hooks-paragraph.md → analysis-paragraph-hooks.md} +1 -1
  294. package/lib/oh-story/skills/story-short-analyze/references/{genre-readers.md → analysis-reader-profile.md} +2 -2
  295. package/lib/oh-story/skills/story-short-analyze/references/analysis-report-style.md +45 -0
  296. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-genres.md +65 -0
  297. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-hooks.md +77 -0
  298. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-mechanics.md +82 -0
  299. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-patterns.md +50 -0
  300. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-suspense.md +98 -0
  301. package/lib/oh-story/skills/story-short-analyze/references/{genre-writing-techniques.md → analysis-writing-techniques.md} +21 -19
  302. package/lib/oh-story/skills/story-short-analyze/references/deconstruction-examples.md +1 -1
  303. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +21 -28
  304. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +1 -1
  305. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +17 -17
  306. package/lib/oh-story/skills/story-short-analyze/references/{quality-checklist.md → source-story-quality.md} +28 -49
  307. package/lib/oh-story/skills/story-short-analyze/references/zhihu-style.md +2 -2
  308. package/lib/oh-story/skills/story-short-scan/SKILL.md +1 -1
  309. package/lib/oh-story/skills/story-short-write/SKILL.md +78 -55
  310. package/lib/oh-story/skills/story-short-write/references/author-memory.md +163 -0
  311. package/lib/oh-story/skills/story-short-write/references/banned-words.md +6 -4
  312. package/lib/oh-story/skills/story-short-write/references/cross-book-recall.md +3 -3
  313. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +4 -4
  314. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +3 -3
  315. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +2 -2
  316. 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
  317. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +6 -6
  318. 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
  319. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +4 -4
  320. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +7 -7
  321. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/262/231/351/233/225/350/204/221/346/264/236.md +5 -5
  322. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +4 -4
  323. 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
  324. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +32 -44
  325. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +20 -18
  326. package/lib/oh-story/skills/story-short-write/references/output-contract.md +1 -1
  327. package/lib/oh-story/skills/story-short-write/references/short-craft.md +18 -18
  328. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +1 -1
  329. package/lib/oh-story/skills/story-short-write/references/short-format.md +38 -8
  330. package/lib/oh-story/skills/story-short-write/references/{quality-checklist.md → short-prose-quality.md} +22 -21
  331. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +355 -0
  332. package/lib/oh-story/skills/{story-short-analyze/references/hooks-suspense.md → story-short-write/references/short-suspense.md} +31 -31
  333. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +2 -2
  334. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +31 -22
  335. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +871 -0
  336. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +64 -1
  337. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +212 -0
  338. package/lib/oh-story/skills/story-short-write/scripts/check-phase2-contract.js +363 -0
  339. package/lib/types/client/drama-production-view.d.ts +34 -0
  340. package/lib/types/client/drama-production-view.d.ts.map +1 -0
  341. package/lib/types/client/drama-production.d.ts +92 -0
  342. package/lib/types/client/drama-production.d.ts.map +1 -0
  343. package/lib/types/client/file-activity.d.ts +9 -2
  344. package/lib/types/client/file-activity.d.ts.map +1 -1
  345. package/lib/types/client/index.d.ts +1 -1
  346. package/lib/types/client/index.d.ts.map +1 -1
  347. package/lib/types/client/production-intents.d.ts +9 -0
  348. package/lib/types/client/production-intents.d.ts.map +1 -0
  349. package/lib/types/client/production-prompts.d.ts +9 -0
  350. package/lib/types/client/production-prompts.d.ts.map +1 -0
  351. package/lib/types/client/production-runtime.d.ts +58 -0
  352. package/lib/types/client/production-runtime.d.ts.map +1 -0
  353. package/lib/types/client/video-studio.d.ts +41 -0
  354. package/lib/types/client/video-studio.d.ts.map +1 -0
  355. package/lib/types/client/workbench-ui.d.ts +5 -0
  356. package/lib/types/client/workbench-ui.d.ts.map +1 -0
  357. package/lib/types/game-verification.d.ts +16 -0
  358. package/lib/types/game-verification.d.ts.map +1 -0
  359. package/lib/types/index.d.ts +5 -2
  360. package/lib/types/index.d.ts.map +1 -1
  361. package/lib/types/production-intent.d.ts +21 -0
  362. package/lib/types/production-intent.d.ts.map +1 -0
  363. package/lib/types/production-tool.d.ts +5 -0
  364. package/lib/types/production-tool.d.ts.map +1 -0
  365. package/lib/types/reference-tool.d.ts +6 -0
  366. package/lib/types/reference-tool.d.ts.map +1 -0
  367. package/lib/types/role-provider.d.ts.map +1 -1
  368. package/lib/types/role-tool.d.ts +3 -1
  369. package/lib/types/role-tool.d.ts.map +1 -1
  370. package/lib/types/skill-provider.d.ts +7 -0
  371. package/lib/types/skill-provider.d.ts.map +1 -1
  372. package/lib/types/video-project.d.ts +42 -0
  373. package/lib/types/video-project.d.ts.map +1 -0
  374. package/lib/types/workspace-request-trust.d.ts +6 -0
  375. package/lib/types/workspace-request-trust.d.ts.map +1 -1
  376. package/lib/types/workspace-route.d.ts +20 -0
  377. package/lib/types/workspace-route.d.ts.map +1 -1
  378. package/lib/video-recap/CHANGELOG.md +282 -0
  379. package/lib/video-recap/LICENSE +21 -0
  380. package/lib/video-recap/README.en.md +257 -0
  381. package/lib/video-recap/README.md +255 -0
  382. package/lib/video-recap/manifest.json +684 -0
  383. package/lib/video-recap/skills/video-assemble/SKILL.md +97 -0
  384. package/lib/video-recap/skills/video-assemble/references/jianying/LICENSE.duo-video +21 -0
  385. package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +15 -0
  386. package/lib/video-recap/skills/video-assemble/references/jianying/empty_draft_meta_info.json +69 -0
  387. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +84 -0
  388. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +98 -0
  389. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +204 -0
  390. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_material_video.json +83 -0
  391. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_meta_material_value.json +23 -0
  392. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_project_info.json +133 -0
  393. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_segment.json +70 -0
  394. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_text_styles.json +30 -0
  395. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_audio.json +41 -0
  396. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_text.json +109 -0
  397. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +88 -0
  398. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +345 -0
  399. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +34 -0
  400. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +305 -0
  401. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +94 -0
  402. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +205 -0
  403. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +555 -0
  404. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +261 -0
  405. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +767 -0
  406. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +111 -0
  407. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +24 -0
  408. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +109 -0
  409. package/lib/video-recap/skills/video-assemble/scripts/jianying_templates.py +37 -0
  410. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +191 -0
  411. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +75 -0
  412. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +394 -0
  413. package/lib/video-recap/skills/video-assemble/scripts/lib.py +239 -0
  414. package/lib/video-recap/skills/video-assemble/scripts/media.py +243 -0
  415. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +477 -0
  416. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +46 -0
  417. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +486 -0
  418. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +376 -0
  419. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +88 -0
  420. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +248 -0
  421. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +89 -0
  422. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +512 -0
  423. package/lib/video-recap/skills/video-cut/SKILL.md +90 -0
  424. package/lib/video-recap/skills/video-cut/scripts/cut.py +67 -0
  425. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +320 -0
  426. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +545 -0
  427. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +348 -0
  428. package/lib/video-recap/skills/video-cut/scripts/lib.py +77 -0
  429. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +341 -0
  430. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +249 -0
  431. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +745 -0
  432. package/lib/video-recap/skills/video-recap/SKILL.md +216 -0
  433. package/lib/video-recap/skills/video-recap/references/config-playbook.md +61 -0
  434. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +225 -0
  435. package/lib/video-recap/skills/video-recap/references/data-schema.md +534 -0
  436. package/lib/video-recap/skills/video-recap/references/research-guide.md +74 -0
  437. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +23 -0
  438. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +251 -0
  439. package/lib/video-recap/skills/video-recap/scripts/doctor.py +483 -0
  440. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +737 -0
  441. package/lib/video-recap/skills/video-recap/scripts/lib.py +242 -0
  442. package/lib/video-recap/skills/video-recap/scripts/materials.py +457 -0
  443. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +95 -0
  444. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +18 -0
  445. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_contract.py +9 -0
  446. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +371 -0
  447. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +201 -0
  448. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +227 -0
  449. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +362 -0
  450. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +129 -0
  451. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +338 -0
  452. package/lib/video-recap/skills/video-recap/scripts/recap.py +9 -0
  453. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +140 -0
  454. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +550 -0
  455. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +77 -0
  456. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +654 -0
  457. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +335 -0
  458. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +217 -0
  459. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +795 -0
  460. package/lib/video-recap/skills/video-script/SKILL.md +248 -0
  461. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +225 -0
  462. package/lib/video-recap/skills/video-script/references/research-guide.md +75 -0
  463. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +399 -0
  464. package/lib/video-recap/skills/video-script/scripts/agent_text.py +410 -0
  465. package/lib/video-recap/skills/video-script/scripts/brief_context.py +403 -0
  466. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +332 -0
  467. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +442 -0
  468. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +237 -0
  469. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +492 -0
  470. package/lib/video-recap/skills/video-script/scripts/lib.py +327 -0
  471. package/lib/video-recap/skills/video-script/scripts/narration.py +12 -0
  472. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +678 -0
  473. package/lib/video-recap/skills/video-script/scripts/review.py +42 -0
  474. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +262 -0
  475. package/lib/video-recap/skills/video-script/scripts/review_response.py +761 -0
  476. package/lib/video-recap/skills/video-script/scripts/review_runner.py +200 -0
  477. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +204 -0
  478. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +199 -0
  479. package/lib/video-recap/skills/video-script/scripts/validate.py +164 -0
  480. package/lib/video-recap/skills/video-understanding/SKILL.md +75 -0
  481. package/lib/video-recap/skills/video-understanding/references/data-schema.md +299 -0
  482. package/lib/video-recap/skills/video-understanding/references/prompt-templates.md +37 -0
  483. package/lib/video-recap/skills/video-understanding/references/research-guide.md +74 -0
  484. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +399 -0
  485. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +410 -0
  486. package/lib/video-recap/skills/video-understanding/scripts/asr.py +260 -0
  487. package/lib/video-recap/skills/video-understanding/scripts/brief.py +12 -0
  488. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +403 -0
  489. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +332 -0
  490. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +442 -0
  491. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +647 -0
  492. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +237 -0
  493. package/lib/video-recap/skills/video-understanding/scripts/detect.py +513 -0
  494. package/lib/video-recap/skills/video-understanding/scripts/extract.py +62 -0
  495. package/lib/video-recap/skills/video-understanding/scripts/lib.py +470 -0
  496. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +678 -0
  497. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +204 -0
  498. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +507 -0
  499. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +199 -0
  500. package/lib/video-recap/skills/video-understanding/scripts/understand.py +9 -0
  501. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +216 -0
  502. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +327 -0
  503. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +367 -0
  504. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +162 -0
  505. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +705 -0
  506. package/lib/video-recap/skills/video-voiceover/SKILL.md +81 -0
  507. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +637 -0
  508. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +109 -0
  509. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +421 -0
  510. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +113 -0
  511. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +782 -0
  512. package/package.json +57 -41
  513. package/lib/drama/skills/short-drama/references/lifecycle-commands.md +0 -130
  514. package/lib/oh-story/skills/story-deslop/scripts/check-outline-copy.js +0 -241
  515. package/lib/oh-story/skills/story-long-write/references/format-and-structure.md +0 -169
  516. package/lib/oh-story/skills/story-setup/references/agent-references/genre-writing-formulas.md +0 -444
  517. package/lib/oh-story/skills/story-setup/references/agent-references/quality-checklist.md +0 -247
  518. package/lib/oh-story/skills/story-short-analyze/references/anti-ai-writing.md +0 -582
  519. package/lib/oh-story/skills/story-short-analyze/references/banned-words.md +0 -147
  520. package/lib/oh-story/skills/story-short-analyze/references/genre-catalog.md +0 -490
  521. package/lib/oh-story/skills/story-short-analyze/references/genre-core-mechanics.md +0 -425
  522. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-formulas.md +0 -453
  523. /package/lib/oh-story/skills/{story-long-write/references/hooks-chapter.md → story-setup/references/agent-references/short-chapter-hooks.md} +0 -0
  524. /package/lib/oh-story/skills/{story-long-write/references/hooks-paragraph.md → story-setup/references/agent-references/short-paragraph-hooks.md} +0 -0
@@ -3,25 +3,40 @@
3
3
 
4
4
  The language model supplies compact semantic JSON. This tool validates and
5
5
  merges that input in memory, renders every derived view, then atomically writes
6
- ``_tracking-state.json`` last as the single commit point. One book project has
7
- one serial writer; concurrent commits are intentionally unsupported.
6
+ ``_tracking-state.json`` last as the single commit point. Per-project locking
7
+ serializes concurrent writers before revision and wordcount checks.
8
8
  """
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
12
  import argparse
13
13
  import copy
14
+ import importlib.util
14
15
  import json
15
16
  import os
16
17
  import re
17
18
  import stat
18
19
  import sys
19
20
  import tempfile
21
+ import time
20
22
  import unicodedata
23
+ from contextlib import contextmanager
21
24
  from pathlib import Path
22
25
  from typing import Any
23
26
 
24
27
 
28
+ _WORDCOUNT_CORE_PATH = Path(__file__).with_name("wordcount_core.py")
29
+ if not _WORDCOUNT_CORE_PATH.is_file(): # pragma: no cover - broken deployment
30
+ raise RuntimeError("TOOL_UNAVAILABLE: wordcount_core.py")
31
+ _WORDCOUNT_CORE_SPEC = importlib.util.spec_from_file_location(
32
+ "story_wordcount_core", _WORDCOUNT_CORE_PATH
33
+ )
34
+ if _WORDCOUNT_CORE_SPEC is None or _WORDCOUNT_CORE_SPEC.loader is None: # pragma: no cover
35
+ raise RuntimeError("unable to load wordcount core")
36
+ wordcount_core = importlib.util.module_from_spec(_WORDCOUNT_CORE_SPEC)
37
+ _WORDCOUNT_CORE_SPEC.loader.exec_module(wordcount_core)
38
+
39
+
25
40
  INPUT_SCHEMA_VERSION = 1
26
41
  TRACKING_SCHEMA_VERSION = 4
27
42
  DELTA_TARGET_BYTES = 1536
@@ -74,6 +89,13 @@ def require(condition: bool, message: str) -> None:
74
89
  raise TrackingError(message)
75
90
 
76
91
 
92
+ def wordcount_value(function: Any, *args: Any, **kwargs: Any) -> Any:
93
+ try:
94
+ return function(*args, **kwargs)
95
+ except wordcount_core.WordcountError as exc:
96
+ raise TrackingError(str(exc)) from exc
97
+
98
+
77
99
  def as_mapping(value: object, label: str) -> dict[str, Any]:
78
100
  require(isinstance(value, dict), f"{label} must be a JSON object")
79
101
  return value
@@ -188,6 +210,34 @@ def state_path(project: Path) -> Path:
188
210
  return tracking_root(project) / "_tracking-state.json"
189
211
 
190
212
 
213
+ @contextmanager
214
+ def project_write_lock(project: Path, *, timeout_seconds: float = 10.0):
215
+ tracking = tracking_root(project)
216
+ tracking.mkdir(parents=True, exist_ok=True)
217
+ path = tracking / ".tracking-commit.lock"
218
+ deadline = time.monotonic() + timeout_seconds
219
+ descriptor: int | None = None
220
+ while descriptor is None:
221
+ try:
222
+ descriptor = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
223
+ except FileExistsError:
224
+ if time.monotonic() >= deadline:
225
+ raise TrackingError(
226
+ "tracking commit lock is busy or stale; retry, or remove 追踪/.tracking-commit.lock after confirming no commit is running"
227
+ )
228
+ time.sleep(0.05)
229
+ try:
230
+ os.write(descriptor, f"pid={os.getpid()}\n".encode("ascii"))
231
+ os.fsync(descriptor)
232
+ yield
233
+ finally:
234
+ os.close(descriptor)
235
+ try:
236
+ path.unlink()
237
+ except FileNotFoundError:
238
+ pass
239
+
240
+
191
241
  def delta_path(tracking: Path, chapter: int) -> Path:
192
242
  width = max(3, len(str(chapter)))
193
243
  return tracking / "逐章记录" / f"第{chapter:0{width}d}章.md"
@@ -735,6 +785,18 @@ def render_delta(chapter: int, title: str, delta: dict[str, Any], core_names: se
735
785
  return payload
736
786
 
737
787
 
788
+ def normalize_wordcount_records(value: object, last_chapter: int) -> dict[str, dict[str, Any]]:
789
+ records = as_mapping(value, "tracking state.wordcount_records")
790
+ normalized: dict[str, dict[str, Any]] = {}
791
+ for raw_chapter, raw_record in records.items():
792
+ require(isinstance(raw_chapter, str) and re.fullmatch(r"[1-9]\d*", raw_chapter) is not None,
793
+ "wordcount record chapter key is invalid")
794
+ chapter = int(raw_chapter)
795
+ require(chapter <= last_chapter, "wordcount record exceeds last committed chapter")
796
+ normalized[raw_chapter] = wordcount_value(wordcount_core.normalize_wordcount_record, raw_record)
797
+ return normalized
798
+
799
+
738
800
  def normalize_state(document: object) -> dict[str, Any]:
739
801
  root = as_mapping(document, "tracking state")
740
802
  require_known_keys(
@@ -742,6 +804,7 @@ def normalize_state(document: object) -> dict[str, Any]:
742
804
  {
743
805
  "schema_version", "book_title", "last_committed_chapter", "imported_through_chapter",
744
806
  "state_revision", "context", "characters", "foreshadow", "timeline",
807
+ "wordcount_records",
745
808
  },
746
809
  "tracking state",
747
810
  )
@@ -766,16 +829,19 @@ def normalize_state(document: object) -> dict[str, Any]:
766
829
  if last_chapter == 0:
767
830
  require(not foreshadow, "a chapter-0 project cannot have planted foreshadow facts")
768
831
  require(not timeline, "a chapter-0 project cannot have established timeline facts")
832
+ state_revision = as_int(root.get("state_revision"), "tracking state.state_revision")
833
+ wordcount_records = normalize_wordcount_records(root.get("wordcount_records", {}), last_chapter)
769
834
  return {
770
835
  "schema_version": TRACKING_SCHEMA_VERSION,
771
836
  "book_title": clean_text(root.get("book_title"), "tracking state.book_title", max_bytes=240),
772
837
  "last_committed_chapter": last_chapter,
773
838
  "imported_through_chapter": imported_through,
774
- "state_revision": as_int(root.get("state_revision"), "tracking state.state_revision"),
839
+ "state_revision": state_revision,
775
840
  "context": context,
776
841
  "characters": characters,
777
842
  "foreshadow": foreshadow,
778
843
  "timeline": timeline,
844
+ "wordcount_records": wordcount_records,
779
845
  }
780
846
 
781
847
 
@@ -789,7 +855,10 @@ def normalize_initial_document(document: object) -> dict[str, Any]:
789
855
  root = as_mapping(document, "init input")
790
856
  require_known_keys(
791
857
  root,
792
- {"schema_version", "book_title", "last_chapter", "context", "character_snapshots", "foreshadow", "timeline_events"},
858
+ {
859
+ "schema_version", "book_title", "last_chapter", "context", "character_snapshots",
860
+ "foreshadow", "timeline_events",
861
+ },
793
862
  "init input",
794
863
  )
795
864
  require(root.get("schema_version") == INPUT_SCHEMA_VERSION, "init input schema_version is unsupported")
@@ -826,17 +895,18 @@ def normalize_initial_document(document: object) -> dict[str, Any]:
826
895
  "characters": snapshots,
827
896
  "foreshadow": foreshadow,
828
897
  "timeline": timeline,
898
+ "wordcount_records": {},
829
899
  }
830
900
  )
831
901
 
832
902
 
833
- def normalize_transaction(state: dict[str, Any], document: object) -> dict[str, Any]:
903
+ def normalize_transaction(project: Path, state: dict[str, Any], document: object) -> dict[str, Any]:
834
904
  root = as_mapping(document, "transaction")
835
905
  require_known_keys(
836
906
  root,
837
907
  {
838
908
  "schema_version", "mode", "chapter", "chapter_title", "expected_state_revision",
839
- "delta", "context", "character_snapshots",
909
+ "delta", "context", "character_snapshots", "wordcount",
840
910
  },
841
911
  "transaction",
842
912
  )
@@ -845,6 +915,7 @@ def normalize_transaction(state: dict[str, Any], document: object) -> dict[str,
845
915
  require(mode in {"append", "revision"}, "mode must be append or revision")
846
916
  chapter = as_int(root.get("chapter"), "chapter", minimum=1)
847
917
  expected_revision = as_int(root.get("expected_state_revision"), "expected_state_revision")
918
+ wordcount_input = root.get("wordcount")
848
919
  require(expected_revision == state["state_revision"], "tracking state changed since this transaction was prepared")
849
920
  last = state["last_committed_chapter"]
850
921
  if mode == "append":
@@ -864,6 +935,11 @@ def normalize_transaction(state: dict[str, Any], document: object) -> dict[str,
864
935
  snapshots=snapshots,
865
936
  existing_core_names=existing_names,
866
937
  )
938
+ wordcount = None
939
+ if wordcount_input is not None:
940
+ wordcount = wordcount_value(
941
+ wordcount_core.validate_current_wordcount_record, project, chapter, wordcount_input
942
+ )
867
943
  return {
868
944
  "mode": mode,
869
945
  "chapter": chapter,
@@ -871,6 +947,7 @@ def normalize_transaction(state: dict[str, Any], document: object) -> dict[str,
871
947
  "delta": delta,
872
948
  "context": context,
873
949
  "snapshots": snapshots,
950
+ "wordcount": wordcount,
874
951
  }
875
952
 
876
953
 
@@ -891,6 +968,8 @@ def merge_transaction(state: dict[str, Any], transaction: dict[str, Any]) -> dic
891
968
  next_state["last_committed_chapter"] = chapter
892
969
  next_state["state_revision"] += 1
893
970
  next_state["characters"].update(transaction["snapshots"])
971
+ if transaction["wordcount"] is not None:
972
+ next_state["wordcount_records"][str(chapter)] = transaction["wordcount"]
894
973
 
895
974
  next_context = transaction["context"]
896
975
  # 退役说的是「从此刻起离开当前状态」,只有 append 的逐章记录代表此刻;
@@ -1013,7 +1092,7 @@ def warn_sizes(views: dict[str, str], delta_payload: str | None = None) -> None:
1013
1092
  )
1014
1093
 
1015
1094
 
1016
- def initialize(project: Path, document: object) -> dict[str, Any]:
1095
+ def _initialize_locked(project: Path, document: object) -> dict[str, Any]:
1017
1096
  tracking = tracking_root(project)
1018
1097
  require(not state_path(project).exists(), "tracking state already exists; init never overwrites project state")
1019
1098
  state = normalize_initial_document(document)
@@ -1036,11 +1115,16 @@ def initialize(project: Path, document: object) -> dict[str, Any]:
1036
1115
  return state
1037
1116
 
1038
1117
 
1039
- def apply_transaction(project: Path, document: object) -> dict[str, Any]:
1118
+ def initialize(project: Path, document: object) -> dict[str, Any]:
1119
+ with project_write_lock(project):
1120
+ return _initialize_locked(project, document)
1121
+
1122
+
1123
+ def _apply_transaction_locked(project: Path, document: object) -> dict[str, Any]:
1040
1124
  tracking = tracking_root(project)
1041
1125
  require_no_retired_tracking_paths(tracking)
1042
1126
  state = load_state(project)
1043
- transaction = normalize_transaction(state, document)
1127
+ transaction = normalize_transaction(project, state, document)
1044
1128
  next_state = merge_transaction(state, transaction)
1045
1129
 
1046
1130
  delta_payload = render_delta(
@@ -1067,6 +1151,11 @@ def apply_transaction(project: Path, document: object) -> dict[str, Any]:
1067
1151
  return next_state
1068
1152
 
1069
1153
 
1154
+ def apply_transaction(project: Path, document: object) -> dict[str, Any]:
1155
+ with project_write_lock(project):
1156
+ return _apply_transaction_locked(project, document)
1157
+
1158
+
1070
1159
  def check_project(project: Path) -> dict[str, Any]:
1071
1160
  tracking = tracking_root(project)
1072
1161
  require_no_retired_tracking_paths(tracking)
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env python3
2
+ """Small deterministic wordcount core shared by storyctl and tracking."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import re
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+
12
+ WORDCOUNT_SCHEMA = "story-wordcount-result/v1"
13
+ MEASUREMENT_SCHEMA = "story-wordcount-measurement/v1"
14
+ CHECKPOINT_SCHEMA = "story-wordcount-checkpoint/v1"
15
+ METRIC = "visible_chars_v1"
16
+ RESOLUTIONS = frozenset({"within_user_band", "accepted_current_length"})
17
+
18
+ _WHITE_SPACE_CODEPOINTS = frozenset(
19
+ [*range(0x0009, 0x000E)]
20
+ + [
21
+ 0x0020, 0x0085, 0x00A0, 0x1680, *range(0x2000, 0x200B),
22
+ 0x2028, 0x2029, 0x202F, 0x205F, 0x3000,
23
+ ]
24
+ )
25
+ _FRONTMATTER_KEY_RE = re.compile(r"^[A-Za-z_\u3400-\u9FFF][^:\n]{0,80}:[ \t]*(?:.*)$")
26
+ _LEADING_BLANK_RE = re.compile(r"^[\u0009\u0020\u3000]*$")
27
+ _ATX_HEADING_RE = re.compile(r"^[\u0009\u0020]{0,3}#{1,6}[\u0009\u0020]+\S")
28
+ _POSITIVE_INTEGER_RE = re.compile(r"^[1-9]\d*$")
29
+ _TARGET_LINE_RE = re.compile(r"^[ \t>*-]*字数目标[ \t]*[::][ \t]*([1-9]\d*)[ \t]*(?:字)?[ \t]*$", re.MULTILINE)
30
+ _METRIC_LINE_RE = re.compile(r"^[ \t>*-]*字数口径[ \t]*[::][ \t]*([A-Za-z0-9_-]+)[ \t]*$", re.MULTILINE)
31
+
32
+
33
+ class WordcountError(ValueError):
34
+ """Expected deterministic wordcount contract failure."""
35
+
36
+
37
+ def require(condition: bool, message: str) -> None:
38
+ if not condition:
39
+ raise WordcountError(message)
40
+
41
+
42
+ def normalize_newlines(value: str) -> str:
43
+ return value.replace("\r\n", "\n").replace("\r", "\n")
44
+
45
+
46
+ def strip_recognizable_frontmatter(value: str) -> str:
47
+ lines = value.split("\n")
48
+ if not lines or lines[0] != "---":
49
+ return value
50
+ closing = next((index for index in range(1, min(len(lines), 201)) if lines[index] in {"---", "..."}), -1)
51
+ if closing < 2 or not any(_FRONTMATTER_KEY_RE.match(line) for line in lines[1:closing]):
52
+ return value
53
+ return "\n".join(lines[closing + 1 :])
54
+
55
+
56
+ def visible_body(value: str) -> str:
57
+ if not isinstance(value, str):
58
+ raise TypeError("body must be a string")
59
+ text = normalize_newlines(value)
60
+ if text.startswith("\ufeff"):
61
+ text = text[1:]
62
+ lines = strip_recognizable_frontmatter(text).split("\n")
63
+ while lines and _LEADING_BLANK_RE.match(lines[0]):
64
+ lines.pop(0)
65
+ if lines and _ATX_HEADING_RE.match(lines[0]):
66
+ lines.pop(0)
67
+ return "\n".join(lines)
68
+
69
+
70
+ def count_visible_chars(value: str) -> int:
71
+ return sum(ord(character) not in _WHITE_SPACE_CODEPOINTS for character in visible_body(value))
72
+
73
+
74
+ def parse_target(value: Any) -> int:
75
+ raw = str(value if value is not None else "")
76
+ if not _POSITIVE_INTEGER_RE.fullmatch(raw):
77
+ raise WordcountError("target must be a positive integer")
78
+ target = int(raw)
79
+ require(target <= 9_007_199_254_740_991, "target exceeds Number.MAX_SAFE_INTEGER")
80
+ return target
81
+
82
+
83
+ def compute_wordcount_bands(value: Any) -> dict[str, dict[str, int]]:
84
+ target = parse_target(value)
85
+ return {
86
+ "internal": {"min": (target * 88 + 99) // 100, "max": target * 112 // 100},
87
+ "user": {"min": (target * 85 + 99) // 100, "max": target * 115 // 100},
88
+ }
89
+
90
+
91
+ def invalid_wordcount_result(
92
+ reason: str, *, chapter: Any = None, case_id: Any = None,
93
+ target: int | None = None, actual: int | None = None,
94
+ ) -> dict[str, Any]:
95
+ return {
96
+ "schema": WORDCOUNT_SCHEMA, "metric": METRIC, "chapter": chapter, "case_id": case_id,
97
+ "target": target, "actual": actual, "internal_band": None, "user_band": None,
98
+ "signed_error_pct": None, "absolute_error_pct": None,
99
+ "status": "invalid", "invalid_reason": reason,
100
+ }
101
+
102
+
103
+ def measure_wordcount(value: str, *, chapter: Any = None, case_id: Any = None) -> dict[str, Any]:
104
+ try:
105
+ actual = count_visible_chars(value)
106
+ except (TypeError, ValueError):
107
+ return {
108
+ "schema": MEASUREMENT_SCHEMA, "metric": METRIC, "chapter": chapter,
109
+ "case_id": case_id, "actual": None, "status": "invalid", "invalid_reason": "INVALID_BODY",
110
+ }
111
+ return {
112
+ "schema": MEASUREMENT_SCHEMA, "metric": METRIC, "chapter": chapter,
113
+ "case_id": case_id, "actual": actual, "status": "measured", "invalid_reason": None,
114
+ }
115
+
116
+
117
+ def evaluate_wordcount(
118
+ value: str, target_value: Any, *, chapter: Any = None, case_id: Any = None,
119
+ ) -> dict[str, Any]:
120
+ try:
121
+ target = parse_target(target_value)
122
+ except (TypeError, ValueError):
123
+ return invalid_wordcount_result("INVALID_TARGET", chapter=chapter, case_id=case_id)
124
+ try:
125
+ actual = count_visible_chars(value)
126
+ except (TypeError, ValueError):
127
+ return invalid_wordcount_result("INVALID_BODY", chapter=chapter, case_id=case_id, target=target)
128
+ if actual == 0:
129
+ return invalid_wordcount_result("EMPTY_BODY", chapter=chapter, case_id=case_id, target=target, actual=actual)
130
+ bands = compute_wordcount_bands(target)
131
+ internal_pass = bands["internal"]["min"] <= actual <= bands["internal"]["max"]
132
+ user_pass = bands["user"]["min"] <= actual <= bands["user"]["max"]
133
+ status = "internal_pass" if internal_pass else (
134
+ "borderline" if user_pass else ("under" if actual < bands["user"]["min"] else "over")
135
+ )
136
+ signed_error = (actual - target) / target
137
+ return {
138
+ "schema": WORDCOUNT_SCHEMA, "metric": METRIC, "chapter": chapter, "case_id": case_id,
139
+ "target": target, "actual": actual,
140
+ "internal_band": {**bands["internal"], "status": "pass" if internal_pass else "fail"},
141
+ "user_band": {**bands["user"], "status": "pass" if user_pass else "fail"},
142
+ "signed_error_pct": signed_error, "absolute_error_pct": abs(signed_error),
143
+ "status": status, "invalid_reason": None,
144
+ }
145
+
146
+
147
+ def checkpoint_wordcount(
148
+ value: str, target_value: Any, *, chapter: Any = None, case_id: Any = None,
149
+ ) -> dict[str, Any]:
150
+ target = parse_target(target_value)
151
+ actual = count_visible_chars(value)
152
+ user = compute_wordcount_bands(target)["user"]
153
+ return {
154
+ "schema": CHECKPOINT_SCHEMA, "metric": METRIC, "chapter": chapter, "case_id": case_id,
155
+ "target": target, "actual": actual, "user_band": user,
156
+ "remaining_user_range": {
157
+ "min": max(0, user["min"] - actual),
158
+ "max": max(0, user["max"] - actual),
159
+ },
160
+ }
161
+
162
+
163
+ def target_from_outline(value: str) -> int:
164
+ text = normalize_newlines(value)
165
+ if text.startswith("\ufeff"):
166
+ text = text[1:]
167
+ targets = list(dict.fromkeys(_TARGET_LINE_RE.findall(text)))
168
+ metrics = list(dict.fromkeys(_METRIC_LINE_RE.findall(text)))
169
+ require(len(targets) == 1, "字数目标 must appear exactly once with one value")
170
+ require(metrics == [METRIC], f"字数口径 must appear exactly once as {METRIC}")
171
+ return parse_target(targets[0])
172
+
173
+
174
+ def _chapter_number_from_name(name: str, *, outline: bool) -> int | None:
175
+ pattern = r"^细纲_第0*(\d+)章.*\.md$" if outline else r"^第0*(\d+)章(?:[_\-  ].*)?\.md$"
176
+ match = re.match(pattern, name)
177
+ return int(match.group(1)) if match else None
178
+
179
+
180
+ def find_chapter_file(directory: Path, chapter: int, *, outline: bool) -> Path:
181
+ require(isinstance(chapter, int) and not isinstance(chapter, bool) and chapter >= 1, "chapter must be >= 1")
182
+ require(directory.is_dir(), f"chapter directory is missing: {directory}")
183
+ matches = sorted(
184
+ path for path in directory.iterdir()
185
+ if path.is_file() and _chapter_number_from_name(path.name, outline=outline) == chapter
186
+ )
187
+ label = "outline" if outline else "body"
188
+ require(len(matches) == 1, f"chapter {chapter} must have exactly one {label} file")
189
+ return matches[0]
190
+
191
+
192
+ def build_project_wordcount_record(project: Path, chapter: int, *, resolution: str) -> dict[str, Any]:
193
+ require(resolution in RESOLUTIONS, f"unsupported wordcount resolution: {resolution}")
194
+ root = project.resolve()
195
+ outline_path = find_chapter_file(root / "大纲", chapter, outline=True)
196
+ body_path = find_chapter_file(root / "正文", chapter, outline=False)
197
+ try:
198
+ target = target_from_outline(outline_path.read_text(encoding="utf-8"))
199
+ body_bytes = body_path.read_bytes()
200
+ body = body_bytes.decode("utf-8")
201
+ except (OSError, UnicodeError) as exc:
202
+ raise WordcountError(f"unable to read chapter files: {exc}") from exc
203
+ result = evaluate_wordcount(body, target, chapter=chapter)
204
+ require(result["status"] != "invalid", f"invalid chapter wordcount: {result['invalid_reason']}")
205
+ in_user_band = result["status"] in {"internal_pass", "borderline"}
206
+ require(
207
+ (resolution == "within_user_band" and in_user_band)
208
+ or (resolution == "accepted_current_length" and not in_user_band),
209
+ "wordcount resolution does not match the current length",
210
+ )
211
+ return {
212
+ "metric": METRIC,
213
+ "target": result["target"],
214
+ "actual": result["actual"],
215
+ "status": result["status"],
216
+ "resolution": resolution,
217
+ "body_sha256": hashlib.sha256(body_bytes).hexdigest(),
218
+ }
219
+
220
+
221
+ def normalize_wordcount_record(value: object) -> dict[str, Any]:
222
+ require(isinstance(value, dict), "wordcount record must be an object")
223
+ require(
224
+ set(value) == {"metric", "target", "actual", "status", "resolution", "body_sha256"},
225
+ "wordcount record fields are invalid",
226
+ )
227
+ require(value.get("metric") == METRIC, "wordcount metric is unsupported")
228
+ target = parse_target(value.get("target"))
229
+ actual = value.get("actual")
230
+ require(isinstance(actual, int) and not isinstance(actual, bool) and actual >= 1, "wordcount actual is invalid")
231
+ expected = evaluate_wordcount("字" * actual, target)
232
+ require(value.get("status") == expected["status"], "wordcount status does not match target and actual")
233
+ resolution = value.get("resolution")
234
+ require(resolution in RESOLUTIONS, "wordcount resolution is unsupported")
235
+ in_user_band = expected["status"] in {"internal_pass", "borderline"}
236
+ require((resolution == "within_user_band") == in_user_band, "wordcount resolution does not match status")
237
+ digest = value.get("body_sha256")
238
+ require(isinstance(digest, str) and re.fullmatch(r"[0-9a-f]{64}", digest) is not None, "body_sha256 is invalid")
239
+ return dict(value)
240
+
241
+
242
+ def validate_current_wordcount_record(project: Path, chapter: int, value: object) -> dict[str, Any]:
243
+ record = normalize_wordcount_record(value)
244
+ current = build_project_wordcount_record(project, chapter, resolution=record["resolution"])
245
+ require(record == current, "wordcount record is stale for the current body or target")
246
+ return record
@@ -2,7 +2,7 @@
2
2
  name: story-long-analyze
3
3
  version: 1.0.0
4
4
  description: "长篇网文拆文。深度拆解爆款长篇小说的黄金三章、人设架构、爽点设计、节奏控制。单一深度拆解管道:跑完黄金三章(Stage 1)后产出快速预览报告并询问是否继续全量拆解,确认后从 Stage 2 续跑逐章摘要、聚合分析、设定关系、汇总报告,全程产物落盘 拆文库/{书名}/。触发方式:/story-long-analyze、/长篇拆文、「帮我拆这本书」「拆这本书」「分析黄金三章」「深度拆解」「完整拆解」「系统拆解」或提供小说文本文件路径——全部进入同一管道。"
5
- metadata: {"openclaw":{"source":"https://github.com/worldwonderer/oh-story-claudecode"}}
5
+ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudecode"}}
6
6
  ---
7
7
  # story-long-analyze:长篇网文拆文
8
8
 
@@ -12,9 +12,9 @@ metadata: {"openclaw":{"source":"https://github.com/worldwonderer/oh-story-claud
12
12
 
13
13
  ---
14
14
 
15
- > Agent 兼容性:检查专业 agent 是否可用时,按 `.claude/agents/{agent}.md` → `.opencode/agents/{agent}.md` → `.codex/agents/{agent}.toml` 的顺序查找。Codex 原生子代理调用优先使用同名 `agent_type`;如果当前 Codex 运行时返回 `unknown agent_type` 或未暴露 custom-agent registry,必须降级为 solo/direct。检测到 `.zcode/` 时同样直接 solo/direct,因为 ZCode 3.3.4 不执行项目 custom agents;报告 `Fallback: project custom agents unavailable -> solo`。Claude/OpenCode 兼容面保留 `subagent_type`。
15
+ > Agent 兼容性:只检查当前运行时的 canonical 目录:Claude `.claude/agents/{agent}.md`、OpenCode `.opencode/agents/{agent}.md`、Codex `.codex/agents/{agent}.toml`、Antigravity `.agents/agents/agent-name/agent.md`(`agent-name` 为目标 agent 名),不得因其他端文件存在而误判。Codex 使用同名 `agent_type`;Antigravity 使用 `invoke_subagent` + `TypeName`。对应运行时未暴露 custom-agent registry / `invoke_subagent` 或返回未知 agent 时,必须降级 solo/direct。检测到 `.zcode/` 时同样直接 solo/direct,因为 ZCode 3.3.4 不执行项目 custom agents;报告 `Fallback: project custom agents unavailable -> solo`。Claude/OpenCode 兼容面保留 `subagent_type`。
16
16
  >
17
- > Spawn 版本提示(不阻断 spawn):先读取项目根 `.story-deployed` 的 `agents_version`。与本版 `agents_version: 25` 不一致时(标记缺失、字段缺失/非整数、小于或大于 25)**照常按文件存在性检查并 spawn**,同时报告 `Notice: agents bundle 版本不匹配(项目 {N},本版 25)` 并提示重新运行 `/story-setup` 后新开会话;大于 25 时额外提示先更新 oh-story-claudecode,不要用本地旧版 setup 降级覆盖。只有 agent 文件缺失、或运行时不暴露 custom agent 时才降级 solo/direct,报告 `Fallback: ... -> solo`。
17
+ > Spawn 版本提示(不阻断 spawn):先读取项目根 `.story-deployed` 的 `agents_version`。与本版 `agents_version: 29` 不一致时(标记缺失、字段缺失/非整数、小于或大于 29)**照常按文件存在性检查并 spawn**,同时报告 `Notice: agents bundle 版本不匹配(项目 {N},本版 29)` 并提示重新运行 `/story-setup` 后新开会话;大于 29 时额外提示先更新 oh-story-claudecode,不要用本地旧版 setup 降级覆盖。只有 agent 文件缺失、或运行时不暴露 custom agent 时才降级 solo/direct,报告 `Fallback: ... -> solo`。
18
18
 
19
19
  ## 拆解边界声明(主线程同样适用)
20
20
 
@@ -267,7 +267,7 @@ Agent(
267
267
 
268
268
  以下任一情况,Stage 2 自动退回串行模式,由主线程逐章处理(质量不受影响,只是改为串行、速度略慢)。**两条路径的要求是同一份**:串行时概要写法、情节点白描、原文引用精选规则和输出自检都按 [output-templates.md](references/output-templates.md)「Stage 2 章节摘要+情节点」执行;上面的机械硬检查串行同样要跑。串行没有 sonnet 升级重试这条路——硬检查命中时由主线程按失败项重写本章摘要 1 次,仍不过按 `⚠️ 跳过` 记入 `_progress.md` 「失败记录」表。
269
269
 
270
- - **agent 未部署**:agent 目录(优先 `.claude/agents/`,其次 `.opencode/agents/`,再检查 `.codex/agents/`)下的 `chapter-extractor.md` 或 `.codex/agents/chapter-extractor.toml` 不存在。`.claude/agents/` 通常不随仓库提交,应重新运行 `/story-setup` 完成当前适配器部署,不跨 Skill 读取模板源。
270
+ - **agent 未部署**:当前运行时的 canonical agent 目录未找到 `chapter-extractor.md` 或 Codex 同名 TOML。项目 agent 通常不随写作仓库预置,应重新运行 `/story-setup` 完成当前适配器部署,不跨 Skill 读取模板源。
271
271
  - **环境不支持 spawn 子代理**:本 skill 正运行在某个子代理上下文中,无法再起下一层 agent。
272
272
 
273
273
  ### Stage 2 收尾:合并章节摘要(_章节摘要汇总.md)
@@ -2,7 +2,7 @@
2
2
  name: story-long-scan
3
3
  version: 1.0.0
4
4
  description: "长篇网文扫榜。分析起点、番茄、晋江等平台排行榜数据,提炼市场趋势与热门题材。触发方式:/story-long-scan、/长篇扫榜、「长篇什么火」「起点排行」。"
5
- metadata: {"openclaw":{"source":"https://github.com/worldwonderer/oh-story-claudecode"}}
5
+ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudecode"}}
6
6
  ---
7
7
  # story-long-scan:长篇网文扫榜
8
8