@oh-story/dsh 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/lib/client.js +237 -84
- package/lib/client.js.map +3 -3
- package/lib/drama/manifest.json +183 -77
- package/lib/drama/skills/short-drama/SKILL.md +5 -1
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +140 -9
- package/lib/drama/skills/short-drama/scripts/project_tool.py +11 -0
- package/lib/drama/skills/short-drama-assets/references/character-and-look.md +28 -0
- package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +8 -2
- package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
- package/lib/drama/skills/short-drama-edit/SKILL.md +146 -0
- package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +38 -0
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +29 -0
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +54 -0
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +60 -0
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +51 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1156 -0
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +520 -0
- package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
- package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
- package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
- package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
- package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
- package/lib/drama/skills/short-drama-produce/SKILL.md +29 -4
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +22 -0
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +13 -12
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
- package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
- package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
- package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +199 -3
- package/lib/drama/skills/short-drama-review/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +7 -0
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +10 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +12 -1
- package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
- package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +78 -2
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +12 -5
- package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +17 -0
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +76 -7
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +5 -2
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +21 -0
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +6 -17
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +12 -3
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +61 -5
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -3
- package/lib/drama/skills/short-drama-write/SKILL.md +11 -2
- package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
- package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
- package/lib/index.js +290 -90
- package/lib/index.js.map +4 -4
- package/lib/novel-to-game/README.md +299 -59
- package/lib/novel-to-game/README_ZH.md +299 -56
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
- package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
- package/lib/novel-to-game/manifest.json +77 -87
- package/lib/novel-to-game/skills/game-art-direction/SKILL.md +9 -35
- package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
- package/lib/novel-to-game/skills/game-build/SKILL.md +17 -35
- package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
- package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
- package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
- package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
- package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
- package/lib/novel-to-game/skills/game-concept/SKILL.md +16 -34
- package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
- package/lib/novel-to-game/skills/game-qa/SKILL.md +9 -20
- package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +10 -6
- package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -32
- package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
- package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
- package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
- package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +9 -18
- package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
- package/lib/novel-to-game/skills/novel-to-game/SKILL.md +10 -27
- package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -6
- package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +6 -11
- package/lib/oh-story/manifest.json +419 -254
- package/lib/oh-story/roles/chapter-extractor.md +177 -237
- package/lib/oh-story/roles/character-designer.md +1 -1
- package/lib/oh-story/roles/consistency-checker.md +4 -4
- package/lib/oh-story/roles/narrative-writer.md +56 -243
- package/lib/oh-story/roles/story-architect.md +6 -6
- package/lib/oh-story/roles/story-explorer.md +11 -11
- package/lib/oh-story/skills/story/SKILL.md +13 -7
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory.md +61 -29
- package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
- package/lib/oh-story/skills/story-deslop/SKILL.md +54 -229
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +29 -56
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +61 -29
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
- package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-import/SKILL.md +78 -82
- package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
- package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
- package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
- package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +10 -8
- package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +9 -34
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +197 -15
- package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +210 -2
- package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +3 -0
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +91 -258
- package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +245 -0
- package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +2 -2
- package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
- package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +113 -105
- package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +169 -125
- package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +172 -64
- package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +42 -132
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +5 -5
- package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1106 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
- package/lib/oh-story/skills/story-long-scan/SKILL.md +4 -1
- package/lib/oh-story/skills/story-long-write/SKILL.md +52 -216
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +29 -56
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +72 -12
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +61 -29
- package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
- package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
- package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +10 -4
- package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +6 -15
- package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
- package/lib/oh-story/skills/story-long-write/references/project-files.md +110 -0
- package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +12 -4
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +86 -0
- package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
- package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +9 -34
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +88 -118
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +55 -119
- package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +15 -11
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +73 -54
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +63 -4
- package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +531 -0
- package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +197 -15
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
- package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +285 -0
- package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +58 -0
- package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +210 -2
- package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +3 -0
- package/lib/oh-story/skills/story-review/SKILL.md +80 -87
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +29 -56
- package/lib/oh-story/skills/story-review/references/author-memory.md +61 -29
- package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +10 -4
- package/lib/oh-story/skills/story-review/references/review-quality.md +6 -12
- package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +9 -34
- package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +210 -2
- package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +3 -0
- package/lib/oh-story/skills/story-setup/SKILL.md +58 -43
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +29 -56
- package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +10 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +63 -4
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
- package/lib/oh-story/skills/story-short-analyze/SKILL.md +22 -3
- package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +5 -1
- package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
- package/lib/oh-story/skills/story-short-scan/SKILL.md +5 -1
- package/lib/oh-story/skills/story-short-write/SKILL.md +15 -324
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +61 -29
- package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
- package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +10 -4
- package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
- 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
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +3 -3
- 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
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +4 -4
- package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
- 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
- package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +55 -0
- package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
- package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
- package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
- package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +78 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +126 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +32 -0
- package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
- package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
- package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
- package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/file-activity.d.ts +19 -1
- package/lib/types/client/file-activity.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/production-prompts.d.ts +7 -0
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +33 -0
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/drama-adapters.d.ts +5 -3
- package/lib/types/drama-adapters.d.ts.map +1 -1
- package/lib/types/native-hooks.d.ts +52 -0
- package/lib/types/native-hooks.d.ts.map +1 -1
- package/lib/types/role-identity.d.ts +17 -0
- package/lib/types/role-identity.d.ts.map +1 -0
- package/lib/types/role-tool.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +31 -1
- package/lib/video-recap/manifest.json +162 -162
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
- package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
- package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
- package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
- package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
- package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
- package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
- package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
- package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
- package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
- package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
- package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
- package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
- package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
- package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
- package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
- package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
- package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
- package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
- package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
- package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
- package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
- package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
- package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
- package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
- package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
- package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
- package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
- package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
- package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
- package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
- package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
- package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
- package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
- package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
- package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
- package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
- package/package.json +45 -45
- package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -59
- package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
- package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -42
- package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""卷纲取段器 —— 让「不整读卷纲」成立。
|
|
3
|
+
|
|
4
|
+
按声明作用域选取卷级、单元级和批次材料;未声明的旧段保守纳入并告警。
|
|
5
|
+
|
|
6
|
+
段头约定(每个 ## / ### / #### 标题的**下一行非空行**):
|
|
7
|
+
|
|
8
|
+
> 作用域:卷级常任
|
|
9
|
+
> 作用域:单元级 D1-03
|
|
10
|
+
> 作用域:批次底稿 D1-03|状态:在用
|
|
11
|
+
> 作用域:批次底稿 D1-02|状态:已退役
|
|
12
|
+
|
|
13
|
+
退役行(行内覆盖):以 `⊘` 起头的列表行 / 表格行,默认过滤。
|
|
14
|
+
|
|
15
|
+
用法:
|
|
16
|
+
outline_view.py --toc 卷纲.md
|
|
17
|
+
outline_view.py --unit D1-03 卷纲.md
|
|
18
|
+
outline_view.py --contract 卷纲.md
|
|
19
|
+
outline_view.py --check 卷纲.md
|
|
20
|
+
任意子命令加 --history 连退役行一起输出
|
|
21
|
+
"""
|
|
22
|
+
import argparse
|
|
23
|
+
import io
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
HEADING = re.compile(r"^(#{2,4})\s+(.*\S)\s*$")
|
|
28
|
+
SCOPE = re.compile(r"^>\s*作用域[::]\s*(.+?)\s*$")
|
|
29
|
+
RETIRED_LINE = re.compile(r"^\s*(?:[-*]\s*)?⊘|^\s*\|\s*⊘")
|
|
30
|
+
# 单元块内不该出现的全卷字样——出现即说明这条常任规则放错了地方
|
|
31
|
+
LEAK = re.compile(r"全卷常任|全程生效|全卷生效|本单元起常任|自 ?[LD]\d+-\d+ ?起生效")
|
|
32
|
+
# 指向卷级的指针行不算泄漏:单元块可以引用常任规则,不可以复述它
|
|
33
|
+
POINTER = re.compile(r"全卷常任裁定|已提为卷级|不复读|规则本身见|见「全卷")
|
|
34
|
+
UNIT_IN_TITLE = re.compile(r"(?<![\w-])([LD]\d+-\d+|U\d+)(?![\w-])")
|
|
35
|
+
# 批次底稿里出现跨章祈使=这条约束在写作期还有效,而写作档不给底稿 → 必须下沉
|
|
36
|
+
IMPERATIVE = re.compile(r"全程不许|一律不许|往后任何章|此后不再|往后每|从此不|终局前不")
|
|
37
|
+
CH_RANGE = re.compile(r"章节范围[::]\s*第\s*(\d+)\s*[-–—~至]\s*(\d+)\s*章")
|
|
38
|
+
ARC_ROW = re.compile(r"^\|\s*(\d+)\s*\|")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def read(path):
|
|
42
|
+
with io.open(path, encoding="utf-8-sig") as handle:
|
|
43
|
+
return handle.read()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Section:
|
|
47
|
+
__slots__ = ("level", "title", "start", "end", "scope", "kind",
|
|
48
|
+
"unit", "status", "lines")
|
|
49
|
+
|
|
50
|
+
def __init__(self, level, title, start):
|
|
51
|
+
self.level = level
|
|
52
|
+
self.title = title
|
|
53
|
+
self.start = start # 标题行号(0 基)
|
|
54
|
+
self.end = None
|
|
55
|
+
self.scope = None # 原始作用域串,None = 未声明
|
|
56
|
+
self.kind = None # 卷级常任 / 单元级 / 批次底稿
|
|
57
|
+
self.unit = None
|
|
58
|
+
self.status = "在用"
|
|
59
|
+
self.lines = []
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse(text):
|
|
63
|
+
lines = text.split("\n")
|
|
64
|
+
sections = []
|
|
65
|
+
fence = None
|
|
66
|
+
for index, line in enumerate(lines):
|
|
67
|
+
marker = re.match(r"^\s{0,3}(`{3,}|~{3,})", line)
|
|
68
|
+
if marker:
|
|
69
|
+
value = marker.group(1)
|
|
70
|
+
if fence is None:
|
|
71
|
+
fence = value
|
|
72
|
+
elif value[0] == fence[0] and len(value) >= len(fence):
|
|
73
|
+
fence = None
|
|
74
|
+
continue
|
|
75
|
+
if fence:
|
|
76
|
+
continue
|
|
77
|
+
match = HEADING.match(line)
|
|
78
|
+
if match:
|
|
79
|
+
if sections:
|
|
80
|
+
sections[-1].end = index
|
|
81
|
+
sections.append(Section(len(match.group(1)), match.group(2), index))
|
|
82
|
+
if sections:
|
|
83
|
+
sections[-1].end = len(lines)
|
|
84
|
+
for section in sections:
|
|
85
|
+
section.lines = lines[section.start:section.end]
|
|
86
|
+
for candidate in section.lines[1:]:
|
|
87
|
+
if not candidate.strip():
|
|
88
|
+
continue
|
|
89
|
+
scope_match = SCOPE.match(candidate)
|
|
90
|
+
if scope_match:
|
|
91
|
+
section.scope = scope_match.group(1)
|
|
92
|
+
break
|
|
93
|
+
if section.scope:
|
|
94
|
+
head = section.scope.split("|")[0].strip()
|
|
95
|
+
if head == "卷级常任":
|
|
96
|
+
section.kind = "卷级常任"
|
|
97
|
+
elif re.fullmatch(r"单元级(?:\s+[A-Za-z][\w-]*)?", head):
|
|
98
|
+
section.kind = "单元级"
|
|
99
|
+
elif re.fullmatch(r"批次底稿(?:\s+[A-Za-z][\w-]*)?", head):
|
|
100
|
+
section.kind = "批次底稿"
|
|
101
|
+
found = re.search(r"^(?:单元级|批次底稿)\s+([A-Za-z][\w-]*)$", head)
|
|
102
|
+
if found:
|
|
103
|
+
section.unit = found.group(1)
|
|
104
|
+
if "已退役" in section.scope:
|
|
105
|
+
section.status = "已退役"
|
|
106
|
+
if section.unit is None:
|
|
107
|
+
found = re.search(r"剧情单元\s+([A-Za-z][\w-]*)", section.title) or UNIT_IN_TITLE.search(section.title)
|
|
108
|
+
if found:
|
|
109
|
+
section.unit = found.group(1)
|
|
110
|
+
if section.unit is None and section.scope is None:
|
|
111
|
+
for line in section.lines[1:]:
|
|
112
|
+
field = re.match(r"^\s*[-*+]\s*单元ID\s*[::]\s*([A-Za-z][\w-]*)\s*$", line.replace("**", ""))
|
|
113
|
+
if field:
|
|
114
|
+
section.unit = field.group(1)
|
|
115
|
+
break
|
|
116
|
+
return lines, sections
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def strip_retired(block_lines, keep_history):
|
|
120
|
+
if keep_history:
|
|
121
|
+
return block_lines
|
|
122
|
+
return [line for line in block_lines if not RETIRED_LINE.match(line)]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def unit_chapter_range(sections, unit):
|
|
126
|
+
for section in sections:
|
|
127
|
+
if section.unit == unit and section.kind == "单元级" and "剧情单元" in section.title:
|
|
128
|
+
for line in section.lines:
|
|
129
|
+
found = CH_RANGE.search(line)
|
|
130
|
+
if found:
|
|
131
|
+
return int(found.group(1)), int(found.group(2))
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def slice_arc(section, span):
|
|
136
|
+
"""情绪弧线这类逐章表:只留本单元章区间的行,表头与说明照留。"""
|
|
137
|
+
if span is None:
|
|
138
|
+
return section.lines
|
|
139
|
+
low, high = span
|
|
140
|
+
kept = []
|
|
141
|
+
for line in section.lines:
|
|
142
|
+
row = ARC_ROW.match(line)
|
|
143
|
+
if row and not (low <= int(row.group(1)) <= high):
|
|
144
|
+
continue
|
|
145
|
+
kept.append(line)
|
|
146
|
+
return kept
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def emit(sections, chosen, span, keep_history, note, preamble):
|
|
150
|
+
out = ["\n".join(strip_retired(preamble, keep_history)).rstrip()] if preamble else []
|
|
151
|
+
for section in sections:
|
|
152
|
+
if section not in chosen:
|
|
153
|
+
continue
|
|
154
|
+
body = section.lines
|
|
155
|
+
if "情绪弧线" in section.title or "排班" in section.title:
|
|
156
|
+
body = slice_arc(section, span)
|
|
157
|
+
body = strip_retired(body, keep_history)
|
|
158
|
+
out.append("\n".join(body).rstrip())
|
|
159
|
+
text = "\n\n".join(out)
|
|
160
|
+
sys.stdout.write(text + "\n")
|
|
161
|
+
if note:
|
|
162
|
+
sys.stderr.write(note + "\n")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def cmd_toc(sections):
|
|
166
|
+
print("%-3s %-6s %-9s %-8s %-46s %s" %
|
|
167
|
+
("#", "行", "作用域", "单元", "段", "字符"))
|
|
168
|
+
for number, section in enumerate(sections, 1):
|
|
169
|
+
size = sum(len(line) + 1 for line in section.lines)
|
|
170
|
+
print("%-3d %-6d %-9s %-8s %-46s %d" %
|
|
171
|
+
(number, section.start + 1, section.kind or "**未声明**",
|
|
172
|
+
(section.unit or "-") + ("(退)" if section.status == "已退役" else ""),
|
|
173
|
+
section.title[:44], size))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def cmd_check(sections, strict=False):
|
|
177
|
+
problems = []
|
|
178
|
+
warnings = []
|
|
179
|
+
for section in sections:
|
|
180
|
+
if section.kind == "批次底稿":
|
|
181
|
+
for offset, line in enumerate(section.lines):
|
|
182
|
+
if IMPERATIVE.search(line):
|
|
183
|
+
warnings.append(
|
|
184
|
+
("W1", "第%d行" % (section.start + offset + 1),
|
|
185
|
+
"批次底稿里有跨章祈使,写作档不给底稿——确认已下沉到单元级段或细纲:%s"
|
|
186
|
+
% line.strip()[:52]))
|
|
187
|
+
for section in sections:
|
|
188
|
+
where = "第%d行「%s」" % (section.start + 1, section.title[:36])
|
|
189
|
+
if section.scope is None:
|
|
190
|
+
(problems if strict else warnings).append(("E1" if strict else "W2", where, "缺「> 作用域:」声明行;旧段保守纳入"))
|
|
191
|
+
continue
|
|
192
|
+
if section.kind is None:
|
|
193
|
+
problems.append(("E2", where, "作用域值不认识:%s" % section.scope))
|
|
194
|
+
if section.kind in ("单元级", "批次底稿") and section.unit is None:
|
|
195
|
+
problems.append(("E3", where, "单元级/批次底稿段未标单元ID"))
|
|
196
|
+
if section.kind in ("单元级", "批次底稿"):
|
|
197
|
+
for offset, line in enumerate(section.lines):
|
|
198
|
+
if LEAK.search(line) and not POINTER.search(line):
|
|
199
|
+
problems.append(
|
|
200
|
+
("E4", "第%d行" % (section.start + offset + 1),
|
|
201
|
+
"单元块内出现全卷字样,常任规则放错了位置:%s"
|
|
202
|
+
% line.strip()[:56]))
|
|
203
|
+
for code, where, message in problems + warnings:
|
|
204
|
+
print("%s %s %s" % (code, where, message))
|
|
205
|
+
print("---")
|
|
206
|
+
print("段 %d / 问题 %d / 告警 %d(告警不阻断,逐条人工判断)"
|
|
207
|
+
% (len(sections), len(problems), len(warnings)))
|
|
208
|
+
return 1 if problems else 0
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def main():
|
|
212
|
+
parser = argparse.ArgumentParser(add_help=True)
|
|
213
|
+
parser.add_argument("path")
|
|
214
|
+
parser.add_argument("--toc", action="store_true")
|
|
215
|
+
parser.add_argument("--unit")
|
|
216
|
+
parser.add_argument("--contract", action="store_true")
|
|
217
|
+
parser.add_argument("--check", action="store_true")
|
|
218
|
+
parser.add_argument("--history", action="store_true")
|
|
219
|
+
parser.add_argument("--strict", action="store_true", help="新建卷纲校验:缺作用域也视为错误")
|
|
220
|
+
# 排纲底稿现在放在卷纲外的 大纲/排纲底稿_{单元ID}.md,默认输出就是写作要的闭包。
|
|
221
|
+
# --stage outline 只为老卷纲保留:它们的供给自查等底稿还写在卷纲里(作用域:批次底稿)。
|
|
222
|
+
parser.add_argument("--stage", choices=["outline", "write"], default="write",
|
|
223
|
+
help="write(默认)=卷级常任+单元级;"
|
|
224
|
+
"outline=另带老卷纲里仍在用的批次底稿段")
|
|
225
|
+
args = parser.parse_args()
|
|
226
|
+
|
|
227
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
228
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
229
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
lines, sections = parse(read(args.path))
|
|
233
|
+
except (OSError, UnicodeError) as error:
|
|
234
|
+
sys.stderr.write(f"无法读取卷纲:{error}\n")
|
|
235
|
+
return 2
|
|
236
|
+
if not sections:
|
|
237
|
+
sys.stderr.write("卷纲里没有 ## 段,路径对不对?\n")
|
|
238
|
+
return 2
|
|
239
|
+
|
|
240
|
+
if args.toc:
|
|
241
|
+
cmd_toc(sections)
|
|
242
|
+
return 0
|
|
243
|
+
if args.check:
|
|
244
|
+
return cmd_check(sections, args.strict)
|
|
245
|
+
|
|
246
|
+
invalid = [s for s in sections if s.scope and
|
|
247
|
+
(s.kind is None or (s.kind != "卷级常任" and s.unit is None))]
|
|
248
|
+
if invalid:
|
|
249
|
+
sys.stderr.write("作用域声明无效,先运行 --check 修正\n")
|
|
250
|
+
return 1
|
|
251
|
+
active = [s for s in sections if args.history or s.status != "已退役"]
|
|
252
|
+
permanent = [s for s in active if s.kind == "卷级常任"]
|
|
253
|
+
undeclared = [s for s in active if s.scope is None]
|
|
254
|
+
note = ("%d 段未声明作用域,已保守纳入;用 --check 查看" % len(undeclared)) if undeclared else None
|
|
255
|
+
preamble = lines[:sections[0].start]
|
|
256
|
+
if args.contract:
|
|
257
|
+
emit(sections, set(permanent + undeclared), None, args.history, note, preamble)
|
|
258
|
+
return 0
|
|
259
|
+
|
|
260
|
+
if args.unit:
|
|
261
|
+
unit = args.unit
|
|
262
|
+
owned = [s for s in active if s.unit == unit]
|
|
263
|
+
if not owned:
|
|
264
|
+
sys.stderr.write(
|
|
265
|
+
"找不到单元 %s —— 不静默降级。核对单元ID,或先补卷纲。\n" % unit)
|
|
266
|
+
return 1
|
|
267
|
+
if args.stage == "write":
|
|
268
|
+
# 写作档不给批次底稿:它是排纲期的工作底稿,其中有写作期约束力的条目
|
|
269
|
+
# 必须已经下沉到单元级段或细纲(见 artifact-protocols.md 段位契约纪律④)
|
|
270
|
+
live = [s for s in owned if s.kind == "单元级"]
|
|
271
|
+
else:
|
|
272
|
+
live = [s for s in owned
|
|
273
|
+
if s.kind == "单元级"
|
|
274
|
+
or s.kind == "批次底稿"]
|
|
275
|
+
chosen = set(permanent + live + undeclared)
|
|
276
|
+
span = unit_chapter_range(active, unit)
|
|
277
|
+
emit(sections, chosen, span, args.history, note, preamble)
|
|
278
|
+
return 0
|
|
279
|
+
|
|
280
|
+
parser.print_help()
|
|
281
|
+
return 2
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
if __name__ == "__main__":
|
|
285
|
+
sys.exit(main())
|
|
@@ -30,6 +30,9 @@ for _name in dir(core):
|
|
|
30
30
|
|
|
31
31
|
CHAPTER_CHECK_SCHEMA = "story-chapter-check/v1"
|
|
32
32
|
CHAPTER_ERROR_SCHEMA = "story-chapter-error/v1"
|
|
33
|
+
# 书内工作目录:分组 segment、writer prompt 留档、逐章事务 JSON 的唯一落点。
|
|
34
|
+
# 与 build_writer_prompt.py 同一口径;提交成功后整目录删除,失败时原样保留供重跑。
|
|
35
|
+
WORK_ROOT = (".story", "work")
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
class CliArgumentError(ValueError):
|
|
@@ -72,6 +75,32 @@ def _read_json_object(path: Path, label: str) -> dict[str, Any]:
|
|
|
72
75
|
return value
|
|
73
76
|
|
|
74
77
|
|
|
78
|
+
def chapter_work_dir(project: Path, chapter: int) -> Path:
|
|
79
|
+
width = max(3, len(str(chapter)))
|
|
80
|
+
return project.resolve().joinpath(*WORK_ROOT, f"第{chapter:0{width}d}章")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _remove_chapter_work_dir(project: Path, chapter: int) -> str | None:
|
|
84
|
+
"""Delete this chapter's scratch directory after a successful commit.
|
|
85
|
+
|
|
86
|
+
Only the per-chapter directory is removed; `.story/work` and `.story` are
|
|
87
|
+
pruned only when empty, so book-level author memory under `.story/` stays.
|
|
88
|
+
"""
|
|
89
|
+
path = chapter_work_dir(project, chapter)
|
|
90
|
+
if path.is_symlink():
|
|
91
|
+
path.unlink()
|
|
92
|
+
elif path.is_dir():
|
|
93
|
+
shutil.rmtree(path)
|
|
94
|
+
else:
|
|
95
|
+
return None
|
|
96
|
+
for parent in (path.parent, path.parent.parent):
|
|
97
|
+
try:
|
|
98
|
+
parent.rmdir()
|
|
99
|
+
except OSError:
|
|
100
|
+
break
|
|
101
|
+
return "/".join((*WORK_ROOT, path.name))
|
|
102
|
+
|
|
103
|
+
|
|
75
104
|
def _project_files(project: Path, chapter: int) -> tuple[Path, Path, int]:
|
|
76
105
|
root = project.resolve()
|
|
77
106
|
outline = find_chapter_file(root / "大纲", chapter, outline=True)
|
|
@@ -200,6 +229,23 @@ def chapter_check(project: Path, chapter: int) -> dict[str, Any]:
|
|
|
200
229
|
}
|
|
201
230
|
|
|
202
231
|
|
|
232
|
+
def fix_punctuation(project: Path, chapter: int) -> bool:
|
|
233
|
+
"""Run the deterministic punctuation normalizer on the chapter body in place.
|
|
234
|
+
|
|
235
|
+
Folding it into `chapter check --fix-punctuation` lets the parent flow close a
|
|
236
|
+
chapter with one call instead of running each cleanup script separately.
|
|
237
|
+
"""
|
|
238
|
+
_, body, _ = _project_files(project, chapter)
|
|
239
|
+
node = shutil.which("node")
|
|
240
|
+
script = Path(__file__).with_name("normalize-punctuation.js")
|
|
241
|
+
if node is None or not script.is_file():
|
|
242
|
+
return False # chapter check reports TOOL_UNAVAILABLE as a blocking finding
|
|
243
|
+
before = body.read_bytes()
|
|
244
|
+
subprocess.run([node, str(script), str(body)], text=True, encoding="utf-8",
|
|
245
|
+
capture_output=True, check=False)
|
|
246
|
+
return body.read_bytes() != before
|
|
247
|
+
|
|
248
|
+
|
|
203
249
|
def chapter_commit(project: Path, chapter: int, input_path: Path, *, accept_current_length: bool) -> dict[str, Any]:
|
|
204
250
|
checked = chapter_check(project, chapter)
|
|
205
251
|
require(checked["quality"]["status"] == "pass", "blocking quality findings must be fixed before commit")
|
|
@@ -220,6 +266,12 @@ def chapter_commit(project: Path, chapter: int, input_path: Path, *, accept_curr
|
|
|
220
266
|
checked["tracking_committed"] = state["last_committed_chapter"] >= chapter
|
|
221
267
|
checked["next_chapter_started"] = state["last_committed_chapter"] > chapter
|
|
222
268
|
checked["wordcount"] = state["wordcount_records"].get(str(chapter))
|
|
269
|
+
# The commit is already durable; a cleanup failure is reported, never turned into a failed commit.
|
|
270
|
+
try:
|
|
271
|
+
checked["work_dir_removed"] = _remove_chapter_work_dir(project, chapter)
|
|
272
|
+
except OSError as exc:
|
|
273
|
+
checked["work_dir_removed"] = None
|
|
274
|
+
checked["work_dir_cleanup_error"] = str(exc)
|
|
223
275
|
return checked
|
|
224
276
|
|
|
225
277
|
|
|
@@ -243,6 +295,9 @@ def _build_parser() -> StructuredArgumentParser:
|
|
|
243
295
|
subparser.add_argument("--chapter", type=int, required=True)
|
|
244
296
|
if command != "check":
|
|
245
297
|
subparser.add_argument("--input", type=Path, required=True)
|
|
298
|
+
else:
|
|
299
|
+
subparser.add_argument("--fix-punctuation", action="store_true",
|
|
300
|
+
help="先就地整理正文标点,再做检查")
|
|
246
301
|
return parser
|
|
247
302
|
|
|
248
303
|
|
|
@@ -286,7 +341,10 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
286
341
|
return _wordcount_command(args)
|
|
287
342
|
try:
|
|
288
343
|
if args.chapter_command == "check":
|
|
344
|
+
fixed = fix_punctuation(args.project, args.chapter) if args.fix_punctuation else None
|
|
289
345
|
result = chapter_check(args.project, args.chapter)
|
|
346
|
+
if fixed is not None:
|
|
347
|
+
result["punctuation_fixed"] = fixed
|
|
290
348
|
else:
|
|
291
349
|
result = chapter_commit(
|
|
292
350
|
args.project, args.chapter, args.input,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Book-local only: never inherit an unrelated workspace or another book's choices.
|
|
7
|
+
function loadStyleWhitelist(file) {
|
|
8
|
+
const parent = path.dirname(path.resolve(file));
|
|
9
|
+
const book = path.basename(parent) === '正文' ? path.dirname(parent) : parent;
|
|
10
|
+
try {
|
|
11
|
+
return fs.readFileSync(path.join(book, '.deslop-whitelist'), 'utf8')
|
|
12
|
+
.split(/\r?\n/).map(line => line.trim())
|
|
13
|
+
.filter(line => line && !line.startsWith('#'))
|
|
14
|
+
.sort((a, b) => b.length - a.length);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error.code === 'ENOENT') return [];
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function styleSpans(text, whitelist) {
|
|
22
|
+
const spans = [];
|
|
23
|
+
for (const literal of whitelist) {
|
|
24
|
+
for (let at = text.indexOf(literal); at !== -1; at = text.indexOf(literal, at + 1)) {
|
|
25
|
+
spans.push([at, at + literal.length]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return spans;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function maskStyleText(text, whitelist) {
|
|
32
|
+
const chars = text.split('');
|
|
33
|
+
for (const [start, end] of styleSpans(text, whitelist)) {
|
|
34
|
+
chars.fill('?', start, end);
|
|
35
|
+
}
|
|
36
|
+
return chars.join('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { loadStyleWhitelist, styleSpans, maskStyleText };
|