@oh-story/dsh 0.1.10 → 0.1.11
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 +7 -7
- package/lib/client.js +7 -3
- package/lib/client.js.map +2 -2
- package/lib/drama/manifest.json +212 -142
- package/lib/drama/skills/short-drama/SKILL.md +14 -2
- package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +3 -0
- package/lib/drama/skills/short-drama/references/creator-documents.md +3 -0
- package/lib/drama/skills/short-drama/references/knowhow-index.md +15 -0
- package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
- package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
- package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
- package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
- package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
- package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
- package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
- package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
- package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
- package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
- package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +1 -1
- package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards//351/207/215/347/224/237/347/251/277/350/266/212.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
- package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
- package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
- package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
- package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
- package/lib/drama/skills/short-drama-edit/SKILL.md +43 -20
- package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
- package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +4 -2
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +3 -1
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +6 -2
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +12 -6
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +13 -8
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1153 -121
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +49 -9
- package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
- package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
- package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
- package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
- package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
- package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-review/SKILL.md +2 -2
- package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
- package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
- package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +15 -0
- package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +13 -5
- package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
- package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +82 -14
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +4 -2
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +5 -3
- package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +12 -0
- package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
- package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +40 -1
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +20 -14
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +8 -7
- package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
- package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
- package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
- package/lib/drama/skills/short-drama-write/SKILL.md +8 -3
- package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
- package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
- package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
- package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
- package/lib/index.js +71 -21
- package/lib/index.js.map +3 -3
- package/lib/oh-story/manifest.json +545 -310
- package/lib/oh-story/roles/chapter-extractor.md +11 -11
- package/lib/oh-story/roles/character-designer.md +1 -2
- package/lib/oh-story/roles/consistency-checker.md +11 -9
- package/lib/oh-story/roles/narrative-writer.md +24 -25
- package/lib/oh-story/roles/story-architect.md +9 -61
- package/lib/oh-story/roles/story-explorer.md +20 -125
- package/lib/oh-story/roles/story-researcher.md +0 -1
- package/lib/oh-story/skills/story/SKILL.md +6 -6
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story/references/author-memory.md +40 -118
- package/lib/oh-story/skills/story/scripts/author_memory_commit.py +3 -3
- package/lib/oh-story/skills/story-deslop/SKILL.md +24 -25
- package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +1 -1
- package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +40 -118
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +21 -4
- package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +3 -3
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-import/SKILL.md +63 -579
- package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
- package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
- package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
- package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
- package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
- package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
- package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
- package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
- package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +95 -91
- package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
- package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +11 -9
- package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +101 -14
- package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +278 -23
- package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +179 -23
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +28 -50
- package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +12 -5
- package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +1 -1
- package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
- package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
- package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +21 -55
- package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +17 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +1 -1
- package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +47 -8
- package/lib/oh-story/skills/story-long-scan/SKILL.md +41 -193
- package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
- package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
- package/lib/oh-story/skills/story-long-write/SKILL.md +32 -73
- package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +18 -201
- package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +40 -118
- package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +6 -10
- package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
- package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
- package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
- package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +3 -2
- package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
- package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
- package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
- package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
- package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/project-files.md +10 -6
- package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +4 -4
- package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
- package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
- 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 +4 -4
- package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +11 -9
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +33 -44
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +18 -51
- package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
- package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +53 -67
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -251
- package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +5 -3
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +18 -56
- package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +3 -3
- package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +96 -16
- package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +101 -14
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +2 -1
- package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +250 -50
- package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +278 -23
- package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +179 -23
- package/lib/oh-story/skills/story-review/SKILL.md +19 -273
- package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +1 -1
- package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-review/references/author-memory.md +40 -118
- package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
- package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-review/references/review-quality.md +2 -1
- package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
- package/lib/oh-story/skills/story-review/references/solo.md +52 -0
- package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +11 -9
- package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +3 -3
- package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +278 -23
- package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +179 -23
- package/lib/oh-story/skills/story-setup/SKILL.md +69 -458
- package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +21 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
- package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
- package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
- package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
- package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
- package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
- 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 +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +18 -56
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +3 -3
- package/lib/oh-story/skills/story-short-analyze/SKILL.md +37 -110
- package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
- package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
- package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
- package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
- package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
- package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
- package/lib/oh-story/skills/story-short-analyze/references/{material-decomposition.md → stage4-6-reversal-summary.md} +303 -374
- package/lib/oh-story/skills/story-short-scan/SKILL.md +30 -64
- package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
- package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
- package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
- package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
- package/lib/oh-story/skills/story-short-write/SKILL.md +23 -34
- package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
- package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +40 -118
- package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
- package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
- package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -49
- package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
- package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
- package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
- package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +19 -12
- package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +22 -32
- package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +84 -17
- package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
- package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +3 -3
- package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
- package/lib/types/client/video-studio.d.ts.map +1 -1
- package/lib/types/native-hooks.d.ts +2 -2
- package/lib/types/native-hooks.d.ts.map +1 -1
- package/lib/types/role-tool.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/types/video-project.d.ts.map +1 -1
- package/lib/types/workspace-route.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +60 -1
- package/lib/video-recap/README.en.md +348 -137
- package/lib/video-recap/README.md +340 -136
- package/lib/video-recap/manifest.json +406 -251
- package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
- package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
- package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
- package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
- package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
- package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
- package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
- package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
- package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
- package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
- package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
- package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
- package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
- package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
- package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
- package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
- package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
- package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
- package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
- package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
- package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
- package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
- package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
- package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
- package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
- package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
- package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
- package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
- package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
- package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
- package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
- package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
- package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
- package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
- package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
- package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
- package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
- package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
- package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
- package/lib/video-recap/skills/video-script/SKILL.md +16 -13
- package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
- package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
- package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
- package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
- package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
- package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
- package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
- package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
- package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
- package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
- package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
- package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
- package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
- package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
- package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
- package/lib/video-recap/skills/video-understanding/scripts/{agent_brief.py → briefing/builder.py} +38 -35
- package/lib/video-recap/skills/{video-script/scripts/brief_context.py → video-understanding/scripts/briefing/context.py} +4 -35
- package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
- package/lib/video-recap/skills/video-understanding/scripts/{brief_timeline.py → briefing/timeline.py} +5 -6
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
- package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
- package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
- package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
- package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
- package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
- package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
- package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
- package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
- package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
- package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
- package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
- package/package.json +1 -1
- package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -538
- package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -603
- package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
- package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
- package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
- package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
- package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
- package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
- package/lib/video-recap/skills/video-script/scripts/agent_brief.py +0 -399
- package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +0 -327
- package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
- package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +0 -348
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
- /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
- /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
- /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
|
@@ -26,8 +26,71 @@ _FRONTMATTER_KEY_RE = re.compile(r"^[A-Za-z_\u3400-\u9FFF][^:\n]{0,80}:[ \t]*(?:
|
|
|
26
26
|
_LEADING_BLANK_RE = re.compile(r"^[\u0009\u0020\u3000]*$")
|
|
27
27
|
_ATX_HEADING_RE = re.compile(r"^[\u0009\u0020]{0,3}#{1,6}[\u0009\u0020]+\S")
|
|
28
28
|
_POSITIVE_INTEGER_RE = re.compile(r"^[1-9]\d*$")
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# 细纲「字数目标」「字数范围」「字数口径」的唯一写法语法;check-outline-contract.js 逐字镜像同一组正则,
|
|
30
|
+
# scripts/test-storyctl.py 用同一批写法同时跑两边,保证细纲验收过了、章节检查不会再拒。
|
|
31
|
+
# 值里一个数字都没有的目标/范围行(「字数目标:按卷规划」)是说明文字,不参与取值。
|
|
32
|
+
_NUMBER = r"([1-9][0-9]{0,2}(?:[,,][0-9]{3})+|[1-9][0-9]*)"
|
|
33
|
+
_NOTE = r"(?:[((][^()()]*[))])?"
|
|
34
|
+
_TARGET_VALUE_RE = re.compile(rf"^(?:约|大约)?[ \t]*{_NUMBER}[ \t]*字?(?:左右)?[ \t]*{_NOTE}$")
|
|
35
|
+
_RANGE_VALUE_RE = re.compile(
|
|
36
|
+
rf"^{_NUMBER}[ \t]*字?[ \t]*(?:-|~|~|—|–|-|至|到)[ \t]*{_NUMBER}[ \t]*字?[ \t]*{_NOTE}$"
|
|
37
|
+
)
|
|
38
|
+
_METRIC_VALUE_RE = re.compile(rf"^([A-Za-z0-9_-]+)[ \t]*{_NOTE}$")
|
|
39
|
+
_HAS_DIGIT_RE = re.compile(r"[0-90-9]")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _field_values(text: str, label: str) -> list[str]:
|
|
43
|
+
pattern = re.compile(
|
|
44
|
+
rf"^[ \t>]*(?:[-*+][ \t]*)?(?:\*\*)?{label}(?:\*\*)?[ \t]*[::][ \t]*(.*?)[ \t]*$", re.MULTILINE
|
|
45
|
+
)
|
|
46
|
+
return [value.replace("**", "").strip() for value in pattern.findall(text)]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _numeric_field_values(text: str, label: str) -> list[str]:
|
|
50
|
+
return [raw for raw in _field_values(text, label) if _HAS_DIGIT_RE.search(raw)]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _number(raw: str) -> int:
|
|
54
|
+
return int(re.sub(r"[,,]", "", raw))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def parse_target_field(text: str) -> dict[str, Any]:
|
|
58
|
+
"""Return {"status": ok|missing|invalid|conflict, "value": int|None, "raw": [...]}."""
|
|
59
|
+
raws = _numeric_field_values(normalize_newlines(text).lstrip("\ufeff"), "字数目标")
|
|
60
|
+
if not raws:
|
|
61
|
+
return {"status": "missing", "value": None, "raw": raws}
|
|
62
|
+
matches = [_TARGET_VALUE_RE.fullmatch(raw) for raw in raws]
|
|
63
|
+
if not all(matches):
|
|
64
|
+
return {"status": "invalid", "value": None, "raw": raws}
|
|
65
|
+
values = sorted({_number(match.group(1)) for match in matches if match})
|
|
66
|
+
if len(values) != 1:
|
|
67
|
+
return {"status": "conflict", "value": None, "raw": raws}
|
|
68
|
+
return {"status": "ok", "value": values[0], "raw": raws}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def parse_range_field(text: str) -> dict[str, Any]:
|
|
72
|
+
"""Optional author range line 「字数范围:2000-2600」; same status vocabulary as parse_target_field."""
|
|
73
|
+
raws = _numeric_field_values(normalize_newlines(text).lstrip("\ufeff"), "字数范围")
|
|
74
|
+
if not raws:
|
|
75
|
+
return {"status": "missing", "value": None, "raw": raws}
|
|
76
|
+
matches = [_RANGE_VALUE_RE.fullmatch(raw) for raw in raws]
|
|
77
|
+
if not all(matches):
|
|
78
|
+
return {"status": "invalid", "value": None, "raw": raws}
|
|
79
|
+
values = sorted({(_number(match.group(1)), _number(match.group(2))) for match in matches if match})
|
|
80
|
+
if len(values) != 1:
|
|
81
|
+
return {"status": "invalid", "value": None, "raw": raws}
|
|
82
|
+
low, high = values[0]
|
|
83
|
+
if low > high:
|
|
84
|
+
return {"status": "invalid", "value": None, "raw": raws}
|
|
85
|
+
return {"status": "ok", "value": {"min": low, "max": high}, "raw": raws}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def parse_metric_field(text: str) -> dict[str, Any]:
|
|
89
|
+
"""「字数口径:visible_chars_v1(按可见字符)」;每行都得是口径名(可带括号备注),且只能是 METRIC。"""
|
|
90
|
+
raws = _field_values(normalize_newlines(text).lstrip("\ufeff"), "字数口径")
|
|
91
|
+
matches = [_METRIC_VALUE_RE.fullmatch(raw) for raw in raws]
|
|
92
|
+
ok = bool(raws) and all(matches) and {match.group(1) for match in matches if match} == {METRIC}
|
|
93
|
+
return {"ok": ok, "raw": raws}
|
|
31
94
|
|
|
32
95
|
|
|
33
96
|
class WordcountError(ValueError):
|
|
@@ -80,8 +143,26 @@ def parse_target(value: Any) -> int:
|
|
|
80
143
|
return target
|
|
81
144
|
|
|
82
145
|
|
|
83
|
-
def
|
|
146
|
+
def normalize_author_range(value: Any) -> dict[str, int] | None:
|
|
147
|
+
"""作者明确给出的上下限;None 表示沿用默认 ±12% / ±15% 双层带。"""
|
|
148
|
+
if value is None:
|
|
149
|
+
return None
|
|
150
|
+
require(isinstance(value, dict) and set(value) == {"min", "max"}, "author range must be {min, max}")
|
|
151
|
+
low, high = value["min"], value["max"]
|
|
152
|
+
require(
|
|
153
|
+
all(isinstance(item, int) and not isinstance(item, bool) and item >= 1 for item in (low, high)),
|
|
154
|
+
"author range bounds must be positive integers",
|
|
155
|
+
)
|
|
156
|
+
require(low <= high, "author range min must not exceed max")
|
|
157
|
+
return {"min": low, "max": high}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def compute_wordcount_bands(value: Any, author_range: Any = None) -> dict[str, dict[str, int]]:
|
|
84
161
|
target = parse_target(value)
|
|
162
|
+
author = normalize_author_range(author_range)
|
|
163
|
+
if author is not None:
|
|
164
|
+
# 作者给定区间原样优先:内部带与用户带都取它,不再有 borderline。
|
|
165
|
+
return {"internal": dict(author), "user": dict(author)}
|
|
85
166
|
return {
|
|
86
167
|
"internal": {"min": (target * 88 + 99) // 100, "max": target * 112 // 100},
|
|
87
168
|
"user": {"min": (target * 85 + 99) // 100, "max": target * 115 // 100},
|
|
@@ -95,7 +176,7 @@ def invalid_wordcount_result(
|
|
|
95
176
|
return {
|
|
96
177
|
"schema": WORDCOUNT_SCHEMA, "metric": METRIC, "chapter": chapter, "case_id": case_id,
|
|
97
178
|
"target": target, "actual": actual, "internal_band": None, "user_band": None,
|
|
98
|
-
"signed_error_pct": None, "absolute_error_pct": None,
|
|
179
|
+
"signed_error_pct": None, "absolute_error_pct": None, "band_source": None,
|
|
99
180
|
"status": "invalid", "invalid_reason": reason,
|
|
100
181
|
}
|
|
101
182
|
|
|
@@ -115,19 +196,23 @@ def measure_wordcount(value: str, *, chapter: Any = None, case_id: Any = None) -
|
|
|
115
196
|
|
|
116
197
|
|
|
117
198
|
def evaluate_wordcount(
|
|
118
|
-
value: str, target_value: Any, *, chapter: Any = None, case_id: Any = None,
|
|
199
|
+
value: str, target_value: Any, *, chapter: Any = None, case_id: Any = None, author_range: Any = None,
|
|
119
200
|
) -> dict[str, Any]:
|
|
120
201
|
try:
|
|
121
202
|
target = parse_target(target_value)
|
|
122
203
|
except (TypeError, ValueError):
|
|
123
204
|
return invalid_wordcount_result("INVALID_TARGET", chapter=chapter, case_id=case_id)
|
|
205
|
+
try:
|
|
206
|
+
author = normalize_author_range(author_range)
|
|
207
|
+
except WordcountError:
|
|
208
|
+
return invalid_wordcount_result("INVALID_RANGE", chapter=chapter, case_id=case_id, target=target)
|
|
124
209
|
try:
|
|
125
210
|
actual = count_visible_chars(value)
|
|
126
211
|
except (TypeError, ValueError):
|
|
127
212
|
return invalid_wordcount_result("INVALID_BODY", chapter=chapter, case_id=case_id, target=target)
|
|
128
213
|
if actual == 0:
|
|
129
214
|
return invalid_wordcount_result("EMPTY_BODY", chapter=chapter, case_id=case_id, target=target, actual=actual)
|
|
130
|
-
bands = compute_wordcount_bands(target)
|
|
215
|
+
bands = compute_wordcount_bands(target, author)
|
|
131
216
|
internal_pass = bands["internal"]["min"] <= actual <= bands["internal"]["max"]
|
|
132
217
|
user_pass = bands["user"]["min"] <= actual <= bands["user"]["max"]
|
|
133
218
|
status = "internal_pass" if internal_pass else (
|
|
@@ -140,19 +225,22 @@ def evaluate_wordcount(
|
|
|
140
225
|
"internal_band": {**bands["internal"], "status": "pass" if internal_pass else "fail"},
|
|
141
226
|
"user_band": {**bands["user"], "status": "pass" if user_pass else "fail"},
|
|
142
227
|
"signed_error_pct": signed_error, "absolute_error_pct": abs(signed_error),
|
|
228
|
+
"band_source": "author" if author else "default",
|
|
143
229
|
"status": status, "invalid_reason": None,
|
|
144
230
|
}
|
|
145
231
|
|
|
146
232
|
|
|
147
233
|
def checkpoint_wordcount(
|
|
148
|
-
value: str, target_value: Any, *, chapter: Any = None, case_id: Any = None,
|
|
234
|
+
value: str, target_value: Any, *, chapter: Any = None, case_id: Any = None, author_range: Any = None,
|
|
149
235
|
) -> dict[str, Any]:
|
|
150
236
|
target = parse_target(target_value)
|
|
151
237
|
actual = count_visible_chars(value)
|
|
152
|
-
|
|
238
|
+
author = normalize_author_range(author_range)
|
|
239
|
+
user = compute_wordcount_bands(target, author)["user"]
|
|
153
240
|
return {
|
|
154
241
|
"schema": CHECKPOINT_SCHEMA, "metric": METRIC, "chapter": chapter, "case_id": case_id,
|
|
155
242
|
"target": target, "actual": actual, "user_band": user,
|
|
243
|
+
"band_source": "author" if author else "default",
|
|
156
244
|
"remaining_user_range": {
|
|
157
245
|
"min": max(0, user["min"] - actual),
|
|
158
246
|
"max": max(0, user["max"] - actual),
|
|
@@ -160,15 +248,30 @@ def checkpoint_wordcount(
|
|
|
160
248
|
}
|
|
161
249
|
|
|
162
250
|
|
|
251
|
+
_FIELD_ERRORS = {
|
|
252
|
+
"missing": "is missing",
|
|
253
|
+
"invalid": "is not a recognizable number (write e.g. 2300 字 / 约 2300 / 2,300)",
|
|
254
|
+
"conflict": "has conflicting values",
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
163
258
|
def target_from_outline(value: str) -> int:
|
|
164
259
|
text = normalize_newlines(value)
|
|
165
260
|
if text.startswith("\ufeff"):
|
|
166
261
|
text = text[1:]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
require(
|
|
170
|
-
|
|
171
|
-
|
|
262
|
+
parsed = parse_target_field(text)
|
|
263
|
+
require(parsed["status"] == "ok", f"字数目标 {_FIELD_ERRORS.get(parsed['status'], '')}: {parsed['raw']}")
|
|
264
|
+
require(parse_metric_field(text)["ok"], f"字数口径 must be {METRIC}")
|
|
265
|
+
return parse_target(parsed["value"])
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def range_from_outline(value: str) -> dict[str, int] | None:
|
|
269
|
+
"""细纲可选行「字数范围:2000-2600」;没写返回 None,写了但写不对就报错而不是静默回落默认带。"""
|
|
270
|
+
parsed = parse_range_field(value)
|
|
271
|
+
if parsed["status"] == "missing":
|
|
272
|
+
return None
|
|
273
|
+
require(parsed["status"] == "ok", f"字数范围 must be one min-max pair with min <= max: {parsed['raw']}")
|
|
274
|
+
return parsed["value"]
|
|
172
275
|
|
|
173
276
|
|
|
174
277
|
def _chapter_number_from_name(name: str, *, outline: bool) -> int | None:
|
|
@@ -192,18 +295,40 @@ def find_chapter_file(directory: Path, chapter: int, *, outline: bool) -> Path:
|
|
|
192
295
|
return matches[0]
|
|
193
296
|
|
|
194
297
|
|
|
195
|
-
def
|
|
298
|
+
def read_chapter_outline(project: Path, chapter: int) -> tuple[Path, str]:
|
|
299
|
+
outline_path = find_chapter_file(project.resolve() / "大纲", chapter, outline=True)
|
|
300
|
+
try:
|
|
301
|
+
return outline_path, outline_path.read_text(encoding="utf-8")
|
|
302
|
+
except (OSError, UnicodeError) as exc:
|
|
303
|
+
raise WordcountError(f"unable to read outline: {exc}") from exc
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def effective_author_range(outline: str, author_range: Any = None) -> dict[str, int] | None:
|
|
307
|
+
"""命令行给了上下限就以它为准(细纲「字数范围」写错也不阻断);否则读细纲。"""
|
|
308
|
+
return normalize_author_range(author_range) if author_range is not None else range_from_outline(outline)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def read_project_chapter(
|
|
312
|
+
project: Path, chapter: int, author_range: Any = None,
|
|
313
|
+
) -> tuple[Path, Path, int, dict[str, int] | None]:
|
|
314
|
+
"""定位本章细纲与正文,读出字数目标和生效的作者字数范围。"""
|
|
315
|
+
outline_path, outline = read_chapter_outline(project, chapter)
|
|
316
|
+
body_path = find_chapter_file(project.resolve() / "正文", chapter, outline=False)
|
|
317
|
+
return outline_path, body_path, target_from_outline(outline), effective_author_range(outline, author_range)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def build_project_wordcount_record(
|
|
321
|
+
project: Path, chapter: int, *, resolution: str, author_range: Any = None,
|
|
322
|
+
) -> dict[str, Any]:
|
|
323
|
+
"""author_range=None 时读细纲「字数范围」;显式传入(命令行 --min-chars/--max-chars)时以它为准。"""
|
|
196
324
|
require(resolution in RESOLUTIONS, f"unsupported wordcount resolution: {resolution}")
|
|
197
|
-
|
|
198
|
-
outline_path = find_chapter_file(root / "大纲", chapter, outline=True)
|
|
199
|
-
body_path = find_chapter_file(root / "正文", chapter, outline=False)
|
|
325
|
+
_, body_path, target, author = read_project_chapter(project, chapter, author_range)
|
|
200
326
|
try:
|
|
201
|
-
target = target_from_outline(outline_path.read_text(encoding="utf-8"))
|
|
202
327
|
body_bytes = body_path.read_bytes()
|
|
203
328
|
body = body_bytes.decode("utf-8")
|
|
204
329
|
except (OSError, UnicodeError) as exc:
|
|
205
330
|
raise WordcountError(f"unable to read chapter files: {exc}") from exc
|
|
206
|
-
result = evaluate_wordcount(body, target, chapter=chapter)
|
|
331
|
+
result = evaluate_wordcount(body, target, chapter=chapter, author_range=author)
|
|
207
332
|
require(result["status"] != "invalid", f"invalid chapter wordcount: {result['invalid_reason']}")
|
|
208
333
|
in_user_band = result["status"] in {"internal_pass", "borderline"}
|
|
209
334
|
require(
|
|
@@ -211,7 +336,7 @@ def build_project_wordcount_record(project: Path, chapter: int, *, resolution: s
|
|
|
211
336
|
or (resolution == "accepted_current_length" and not in_user_band),
|
|
212
337
|
"wordcount resolution does not match the current length",
|
|
213
338
|
)
|
|
214
|
-
|
|
339
|
+
record = {
|
|
215
340
|
"metric": METRIC,
|
|
216
341
|
"target": result["target"],
|
|
217
342
|
"actual": result["actual"],
|
|
@@ -219,19 +344,26 @@ def build_project_wordcount_record(project: Path, chapter: int, *, resolution: s
|
|
|
219
344
|
"resolution": resolution,
|
|
220
345
|
"body_sha256": hashlib.sha256(body_bytes).hexdigest(),
|
|
221
346
|
}
|
|
347
|
+
if author is not None:
|
|
348
|
+
record["author_range"] = dict(author)
|
|
349
|
+
return record
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
_RECORD_FIELDS = {"metric", "target", "actual", "status", "resolution", "body_sha256"}
|
|
222
353
|
|
|
223
354
|
|
|
224
355
|
def normalize_wordcount_record(value: object) -> dict[str, Any]:
|
|
225
356
|
require(isinstance(value, dict), "wordcount record must be an object")
|
|
226
357
|
require(
|
|
227
|
-
set(value)
|
|
358
|
+
set(value) in (_RECORD_FIELDS, _RECORD_FIELDS | {"author_range"}),
|
|
228
359
|
"wordcount record fields are invalid",
|
|
229
360
|
)
|
|
230
361
|
require(value.get("metric") == METRIC, "wordcount metric is unsupported")
|
|
231
362
|
target = parse_target(value.get("target"))
|
|
363
|
+
author = normalize_author_range(value.get("author_range"))
|
|
232
364
|
actual = value.get("actual")
|
|
233
365
|
require(isinstance(actual, int) and not isinstance(actual, bool) and actual >= 1, "wordcount actual is invalid")
|
|
234
|
-
expected = evaluate_wordcount("字" * actual, target)
|
|
366
|
+
expected = evaluate_wordcount("字" * actual, target, author_range=author)
|
|
235
367
|
require(value.get("status") == expected["status"], "wordcount status does not match target and actual")
|
|
236
368
|
resolution = value.get("resolution")
|
|
237
369
|
require(resolution in RESOLUTIONS, "wordcount resolution is unsupported")
|
|
@@ -242,8 +374,32 @@ def normalize_wordcount_record(value: object) -> dict[str, Any]:
|
|
|
242
374
|
return dict(value)
|
|
243
375
|
|
|
244
376
|
|
|
377
|
+
def wordcount_record_drift(project: Path, chapter: int, value: object) -> list[str]:
|
|
378
|
+
"""已提交的字数记录对当前文件过期了哪几项:只核对记录钉住的正文字节与字数目标。
|
|
379
|
+
|
|
380
|
+
作者事后在细纲补/改「字数范围」只影响下次计数的判定带,不让已提交的记录过期。
|
|
381
|
+
"""
|
|
382
|
+
record = normalize_wordcount_record(value)
|
|
383
|
+
root = project.resolve()
|
|
384
|
+
outline_path = find_chapter_file(root / "大纲", chapter, outline=True)
|
|
385
|
+
body_path = find_chapter_file(root / "正文", chapter, outline=False)
|
|
386
|
+
try:
|
|
387
|
+
outline = outline_path.read_text(encoding="utf-8")
|
|
388
|
+
body_bytes = body_path.read_bytes()
|
|
389
|
+
except (OSError, UnicodeError) as exc:
|
|
390
|
+
raise WordcountError(f"unable to read chapter files: {exc}") from exc
|
|
391
|
+
drift = []
|
|
392
|
+
if hashlib.sha256(body_bytes).hexdigest() != record["body_sha256"]:
|
|
393
|
+
drift.append("正文")
|
|
394
|
+
if target_from_outline(outline) != record["target"]:
|
|
395
|
+
drift.append("字数目标")
|
|
396
|
+
return drift
|
|
397
|
+
|
|
398
|
+
|
|
245
399
|
def validate_current_wordcount_record(project: Path, chapter: int, value: object) -> dict[str, Any]:
|
|
246
400
|
record = normalize_wordcount_record(value)
|
|
247
|
-
current = build_project_wordcount_record(
|
|
401
|
+
current = build_project_wordcount_record(
|
|
402
|
+
project, chapter, resolution=record["resolution"], author_range=record.get("author_range"),
|
|
403
|
+
)
|
|
248
404
|
require(record == current, "wordcount record is stale for the current body or target")
|
|
249
405
|
return record
|
|
@@ -12,7 +12,7 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
12
12
|
|
|
13
13
|
> Agent 兼容性:只检查当前运行时 canonical 目录。运行时不支持项目 agent 或找不到文件时降级 solo/direct,并报告 `Fallback: project custom agents unavailable -> solo`。ZCode 3.3.4 不提供项目 custom agents,直接按此规则降级,不扫描其他 CLI 的 agent 目录。
|
|
14
14
|
>
|
|
15
|
-
> Spawn 版本提示(不阻断 spawn):先读取项目根 `.story-deployed` 的 `agents_version`。与本版 `agents_version:
|
|
15
|
+
> Spawn 版本提示(不阻断 spawn):先读取项目根 `.story-deployed` 的 `agents_version`。与本版 `agents_version: 34` 不一致时(标记缺失、字段缺失/非整数、小于或大于 34)照常按文件存在性检查并 spawn,同时报告 `Notice: agents bundle 版本不匹配(项目 {N},本版 34)` 并提示重新运行 `/story-setup` 后新开会话;大于 34 时额外提示先更新 oh-story-claudecode,不要用本地旧版 setup 降级覆盖。只有 agent 文件缺失、或运行时不暴露 custom agent 时才降级 solo/direct。
|
|
16
16
|
|
|
17
17
|
## 分析边界
|
|
18
18
|
|
|
@@ -27,6 +27,20 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
27
27
|
|
|
28
28
|
作者读到的一切——停下来提问、进度、拆完汇报、出错说明,以及 `快速预览.md`、`拆文报告.md`、人物关系图——按 [references/author-facing.md](references/author-facing.md) 写:大白话讲书、讲章、讲读者和作者能怎么用;不出现脚本名、命令、字段名、状态值、批次编号、内部文件名、质量指标名和证据分级字母;编号只和名称一起出现;需要作者拿主意时给一个问题、推荐选项和默认值;工程细节默认不写,确需时只在末尾留一行技术备注。脚本输出带 `author_message` 时转述它,不贴 JSON 或错误码。
|
|
29
29
|
|
|
30
|
+
## 按时刻读
|
|
31
|
+
|
|
32
|
+
拆文按阶段分成几个时刻。进入一个时刻只读下表这一行的文件;上一时刻的操作说明不必留在上下文里,时刻之间只靠落盘产物和 `_progress.md` 的阶段状态交接(续跑、换新对话都从这里接上)。[references/author-facing.md](references/author-facing.md) 每个时刻都按需用。
|
|
33
|
+
|
|
34
|
+
| 时刻 | 读 | 交接 |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| Phase 1–2、Stage 0–1 开头三章 | 本文件 + [stage1-golden-chapters.md](references/stage1-golden-chapters.md);原文变了或章号对不上时加 [index-rebuild.md](references/index-rebuild.md) | 黄金三章与快速预览落盘,标 `stage1` |
|
|
37
|
+
| Stage 2 逐批提取 | [pipeline-ops.md](references/pipeline-ops.md);子代理不可用、自己写批次时加 [stage2-extraction.md](references/stage2-extraction.md) | 全部摘要落盘,标 `stage2` |
|
|
38
|
+
| Stage 3 剧情与节奏 | [synthesis-inputs.md](references/synthesis-inputs.md) + [stage3-plot-rhythm.md](references/stage3-plot-rhythm.md);打桥段标签时查 [deconstruction-notes.md](references/deconstruction-notes.md)「桥段词表」 | 节奏与情绪模块落盘,标 `stage3` |
|
|
39
|
+
| Stage 4 角色与设定 | [synthesis-inputs.md](references/synthesis-inputs.md) + [stage4-characters-settings.md](references/stage4-characters-settings.md) | 角色与设定落盘,标 `stage4` |
|
|
40
|
+
| Stage 5 主报告 | [synthesis-inputs.md](references/synthesis-inputs.md) + [stage5-report.md](references/stage5-report.md) | 报告落盘,标 `stage5` |
|
|
41
|
+
| Stage 6 文风 | [style-profile-generator.md](references/style-profile-generator.md)(它再指向文风协议) | `文风.md` 落盘,标 `stage6` |
|
|
42
|
+
| 全部拆完 | [final-checks.md](references/final-checks.md) | 按 author-facing「全部拆完」汇报 |
|
|
43
|
+
|
|
30
44
|
## Phase 1:确认对象并检查目录
|
|
31
45
|
|
|
32
46
|
没有书名或原文时询问书名、平台和原文路径;已有完整成果直接使用时不强制索要原文。已有目录先运行只读检查器:
|
|
@@ -69,7 +83,7 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
69
83
|
| 5 主报告 | 权威底层结果 | 拆文报告、完整概要 | 文件存在、阶段状态完成 |
|
|
70
84
|
| 6 文风 | 既有资料、样本或索引定点原文 | `文风.md` | 文件存在、阶段状态完成 |
|
|
71
85
|
|
|
72
|
-
用户未要求一次跑完时,Stage 1 后按 author-facing.md
|
|
86
|
+
用户未要求一次跑完时,Stage 1 后按 author-facing.md「开头三章拆完、停下来问」询问是否继续;要求一次跑完、多本书一起拆或由导入自动续跑时不停下询问。Stage 2 默认有限并行(每轮 3 批),不请作者选派发方式;作者问起或明确要求时再按 author-facing「作者问起怎么拆」解释并切换(见 pipeline-ops「执行与提交一个批次」)。续跑不重复 Stage 0/1。Stage 3–5 不重读原文。Stage 6 可按索引定点读取 4–6 段原文锚点,但不重扫全书。
|
|
73
87
|
|
|
74
88
|
## Stage 0:机械章节索引
|
|
75
89
|
|
|
@@ -85,65 +99,31 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
85
99
|
chapter,source_chapter,volume,title,start_line,end_line,char_count,source_locator,status,chapter_sha256,source_sha256,parser_version
|
|
86
100
|
```
|
|
87
101
|
|
|
88
|
-
只按 LF 计物理行。支持楔子、序章、第0
|
|
102
|
+
只按 LF 计物理行。支持楔子、序章、第0章、任意正文起始章、番外、后记、中文大数、英文章号、多卷重置和卷章组合。目录与正文标题重复时先剔掉目录块;落表前校验章号连续、无重复和边界有效,其中特殊章独立编号,正文允许从任意首章开始。原文变化先拒绝;脚本因原文变化、章号对不上而停下并返回 `author_message` 时,读 [references/index-rebuild.md](references/index-rebuild.md),把说明和选项转告作者(默认推荐按旧章号继续)。
|
|
89
103
|
|
|
90
|
-
|
|
104
|
+
`概要.md` 初稿只按章节标题、卷段结构和抽样开头/结尾写,模板见 [references/stage1-golden-chapters.md](references/stage1-golden-chapters.md)。
|
|
91
105
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```text
|
|
95
|
-
"{PYTHON}" "{story-long-analyze skill 根}/scripts/manage_analysis_run.py" plan --root "{拆文目录}" --intent continue
|
|
96
|
-
```
|
|
106
|
+
## Stage 1:黄金三章
|
|
97
107
|
|
|
98
|
-
|
|
108
|
+
按索引读前三章原文,同一次阅读写三份单章深度拆解、可选 `_style-sample.txt`,再写 `快速预览.md`(模板在 author-facing.md「快速预览.md」);深度拆解与文风样本模板见 [references/stage1-golden-chapters.md](references/stage1-golden-chapters.md)。黄金三章与快速预览落盘后运行 `manage_analysis_run.py mark-stage --stage stage1`,再按上方规则停下来问或继续。
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
- `raw-original`:只按索引读取计划范围,一次产生紧凑逐章事实和跨章观察;
|
|
103
|
-
- `existing-results`:只读 `source_files` 列出的摘要或黄金三章,不得打开原文。完整旧项目增强只产批次观察;本批含摘要缺口时必须为每章生成紧凑章块。
|
|
104
|
-
|
|
105
|
-
`chapter-extractor` 输出字段为:概要、因果、关键行动、局面结果、涉及人物、信息变化、状态变化、三维节奏、章尾钩子、证据,以及情节点列表(原文块每章 10–20 个、长章最多 30 个,格式见 output-templates)。不得另做章节卡表。派发只给 `source_locator`、字数、输出文件路径和上一批缓存路径:子代理自己读原文、把完整结果写进 `_analysis_cache/输入-{批次ID}.md`,只回一行回执;主会话不转贴原文、不读这份输入,直接提交(细则见 pipeline-ops「执行与提交一个批次」)。
|
|
106
|
-
|
|
107
|
-
提交:
|
|
108
|
-
|
|
109
|
-
```text
|
|
110
|
-
"{PYTHON}" "{story-long-analyze skill 根}/scripts/manage_analysis_run.py" commit --root "{拆文目录}" --input "{拆文目录}/_analysis_cache/输入-RAW-4-6.md" --batch-id "RAW-4-6" --range-sha256 "{plan 输出值}" --source-file "{plan 来源}"
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
提交脚本先完整校验(原文块每章情节点不足 10 或超过 30 即整批拒收),再按“缓存 → 缺失摘要 → `_progress.md`”写入。摘要投影逐个情节点固定主题、基调和类型枚举,映射不上时主题/基调写“其他”;保留“关键事件”“情节点”“涉及”“基调”等旧消费者字段。任何已有摘要均不覆盖,结果里的 `kept_existing_summary_chapters` 列出被保留的章。
|
|
114
|
-
|
|
115
|
-
### 拆分与恢复
|
|
116
|
-
|
|
117
|
-
```text
|
|
118
|
-
"{PYTHON}" "{story-long-analyze skill 根}/scripts/manage_analysis_run.py" split --root "{拆文目录}" --batch-id "RAW-4-6"
|
|
119
|
-
"{PYTHON}" "{story-long-analyze skill 根}/scripts/manage_analysis_run.py" repair-progress --root "{拆文目录}"
|
|
120
|
-
```
|
|
110
|
+
## Stage 2:计划、提取、提交
|
|
121
111
|
|
|
122
|
-
|
|
112
|
+
按 [references/pipeline-ops.md](references/pipeline-ops.md) 执行:`manage_analysis_run.py plan` 出只读计划(用户明确增强用 `--intent enhance`,逐批加 `--next`,只拆一段加 `--chapters 起-止`),每批派一个 `chapter-extractor`,只照抄计划里这一批的字段;子代理自己读原文、把结果写进 `_analysis_cache/输入-{批次ID}.md`、只回一行回执,主会话不转贴原文、不读这份输入,直接 `commit`。批次过大或连续失败用 `split`,中断用 `repair-progress`。计划不再有批次、全部摘要落盘后运行 `manage_analysis_run.py mark-stage --stage stage2`。
|
|
123
113
|
|
|
124
114
|
## Stage 3:剧情、双时间线与三维节奏
|
|
125
115
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
剧情点按“起始目标与阻碍 → 改变局面的选择/行动/外部事件 → 局面变化与得失 → 后续影响”合并。事件发生与信息披露分开;同一事实的异常、线索、解释、确认属于一条披露路径。全局只保留约 8–15 个主线或关键转折节点。
|
|
129
|
-
|
|
130
|
-
三维节奏分别说明:事件推进 1–5 及状态变化,读者情绪类型/强度 1–5 及触发,篇幅展开度 1–3 及展开/压缩/省略/反复的作用。情绪机制写完整卡,至少覆盖最强三个,值得复现的不设数量上限;其余保留索引。完整卡以 `读者想看什么`、`情绪链`、`戏剧单元`、`可替换项`、`不可照搬` 五个字段齐全为准,字段名按 `references/output-templates.md` 的 EM 卡表原样使用;缺任一项即无法登记灵感库。铺垫、成立条件、关键触发物、复现步骤、失效情形等按需增列。索引节标题用字面量 `## 其他机制索引`。
|
|
131
|
-
|
|
132
|
-
`剧情/情绪模块.md` 与 `剧情/节奏.md` 都落盘后,用 `manage_analysis_run.py mark-stage --stage stage3 --output "剧情/节奏.md"` 标记。命令会同时检查两份必需产物;阶段没有 receipt 或依赖 hash。
|
|
116
|
+
按 [references/stage3-plot-rhythm.md](references/stage3-plot-rhythm.md) 生成剧情单元、故事线、`剧情/节奏.md` 与 `剧情/情绪模块.md`,取料用 `digest`(见 synthesis-inputs.md)。两份权威文件都落盘后运行 `manage_analysis_run.py mark-stage --stage stage3 --output "剧情/节奏.md"`。
|
|
133
117
|
|
|
134
118
|
## Stage 4:角色、设定与关系
|
|
135
119
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
关系图只从 `角色/角色关系.md` 生成:`"{PYTHON}" "{story-long-analyze skill 根}/scripts/render_relation_chart.py" --root "{拆文目录}" --png`。主产物是 `人物关系图/人物关系图.md`(Mermaid + 文字清单,任何 Markdown 查看器都显示中文);PNG 只在找到能显示图中全部文字的字体时生成(表情等符号不画进图片)。字体不够就不出图,不得自行改画拼音或首字母版,把脚本的 `author_message` 转告作者。
|
|
139
|
-
|
|
140
|
-
至少一份角色档案和一份设定文件落盘后运行 `manage_analysis_run.py mark-stage --stage stage4`;缺任一类文件时不得标完成。
|
|
120
|
+
按 [references/stage4-characters-settings.md](references/stage4-characters-settings.md) 生成角色档案、设定和 `角色/角色关系.md`,关系图只从该文件用 `render_relation_chart.py` 生成。至少一份角色档案和一份设定文件落盘后运行 `manage_analysis_run.py mark-stage --stage stage4`;缺任一类文件时不得标完成。
|
|
141
121
|
|
|
142
122
|
## Stage 5:主报告
|
|
143
123
|
|
|
144
|
-
报告按 author-facing.md「拆文报告.md」写:拆到哪、核心发现、读者在追什么、故事怎么推进、人物与关系、读者与角色的信息差、节奏、核心机制、可借鉴套路、不建议模仿、文风一句话、还不确定的地方。生成新报告前运行 `manage_analysis_run.py mark-stage --stage stage5 --prepare
|
|
124
|
+
报告按 author-facing.md「拆文报告.md」写:拆到哪、核心发现、读者在追什么、故事怎么推进、人物与关系、读者与角色的信息差、节奏、核心机制、可借鉴套路、不建议模仿、文风一句话、还不确定的地方。生成新报告前运行 `manage_analysis_run.py mark-stage --stage stage5 --prepare`,新报告与完整概要落盘后再运行 `manage_analysis_run.py mark-stage --stage stage5`(细则见 [references/stage5-report.md](references/stage5-report.md))。报告只综合底层结果,不再次阅读全文。
|
|
145
125
|
|
|
146
|
-
如项目存在 `选题决策.md
|
|
126
|
+
如项目存在 `选题决策.md`,只回填仍标记“待拆文验证”且题材匹配的项。没有「推荐选题」一节(只扫了榜)就跳过回填,不算无效;有推荐选题但缺少当前契约必需的“能爆的原因”等字段时返回 `invalid_topic_decision_contract`,提示重跑 `story-long-scan` Phase 5;文件不存在不影响拆文。
|
|
147
127
|
|
|
148
128
|
## Stage 6:文风与单独重建
|
|
149
129
|
|
|
@@ -153,10 +133,8 @@ Stage 3–5 用 `manage_analysis_run.py digest` 取料:先读全书跨章观
|
|
|
153
133
|
|
|
154
134
|
用户提出「灵感库 / 提炼灵感 / 跨书灵感聚合 / 更新灵感库」时加载 [references/inspiration-library.md](references/inspiration-library.md)。复用 Stage 3 的 EM 机制卡:`inspiration_index.py register-atoms` 机械登记原子灵感索引(无 IA 文件),再按该文档做单书合并与带受控标签的跨书聚合;卡内只用 `书名/EM-xxx` 裸 ID,禁路径引用。缺情绪模块的书先走上方按需增强,不在灵感层代拆。单书拆文不自动入库。
|
|
155
135
|
|
|
156
|
-
##
|
|
136
|
+
## 状态与旧项目
|
|
157
137
|
|
|
158
138
|
运行状态只有 `_progress.md` 受管区;既有 `schema_version: 2` 原值保留;`chapter_index.csv` 是机械索引;缓存是恢复证据。有阶段记录后,受管区的 `最终状态` 由脚本按 Stage 3–6 的阶段状态写出(都完成为 `completed`,否则 `pending`);旧项目沿用自己原有的 `最终状态` 行,全部完成时由脚本改为 `completed`,不写第二行,会话 hooks 靠它判断拆文是否完成,不要手改。不得创建运行计划、checkpoint、逐批 JSON receipt 或 Stage receipt。
|
|
159
139
|
|
|
160
|
-
全部完成后按 author-facing.md
|
|
161
|
-
|
|
162
|
-
详细命令和恢复顺序见 [references/pipeline-ops.md](references/pipeline-ops.md)。输出模板见 [references/output-templates.md](references/output-templates.md),素材聚合方法见 [references/material-decomposition.md](references/material-decomposition.md)。联合验收用 [references/semantic-acceptance-fixtures.md](references/semantic-acceptance-fixtures.md) 的六项 0–2 分表,真实模型结果至少 10/12 且无硬失败才算语义通过。
|
|
140
|
+
全部完成后按 [references/final-checks.md](references/final-checks.md) 做收尾检查,再按 author-facing.md「全部拆完」向作者汇报。
|
|
@@ -22,14 +22,21 @@
|
|
|
22
22
|
一句话判断:{开篇靠什么抓人、主角怎么立住,一两句}。
|
|
23
23
|
最值得学的一点:{最有借鉴价值的写法,一句}。
|
|
24
24
|
|
|
25
|
-
要继续把后面 {剩余章数} 章也拆完吗?我建议{继续 /
|
|
25
|
+
要继续把后面 {剩余章数} 章也拆完吗?我建议{继续 / 只拆第 X–Y 章这一段 / 到此为止},因为{理由}。
|
|
26
|
+
继续的话我每次同时拆三段,比一段段按顺序拆快两三倍,段和段之间的人物称呼、没收尾的剧情在最后汇总时对齐。
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
不回复的话我按「{推荐选项}」处理。
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 作者问起怎么拆
|
|
32
|
+
|
|
33
|
+
拆法默认替作者定好(每次同时拆三段),不主动列选项。作者问能不能更快、更稳,或说想换拆法时,才这样解释,作者选了就换:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
有三种拆法:
|
|
28
37
|
1. 一段接一段按顺序拆:前后章的人物称呼、没收尾的剧情都接得最稳;最慢。
|
|
29
|
-
2.
|
|
38
|
+
2. 每次同时拆三段(现在用的):快两三倍;段和段之间偶尔要等全部拆完汇总时再对齐人物和剧情。
|
|
30
39
|
3. 能同时拆多少就拆多少:最快;前后接续全靠最后汇总时对齐,汇总会多花些工夫。电脑或账号扛不住时我会自动放慢。
|
|
31
|
-
|
|
32
|
-
不回复的话我按「{推荐选项}」、每次同时拆三段处理。
|
|
33
40
|
```
|
|
34
41
|
|
|
35
42
|
### 拆的过程中报进度
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 收尾检查与维护者回归
|
|
2
|
+
|
|
3
|
+
全部拆完(或本次要拆的一段拆完)的时刻读本文件的「收尾检查」;「维护者回归」只在改动本 skill 时用。
|
|
4
|
+
|
|
5
|
+
## 收尾检查
|
|
6
|
+
|
|
7
|
+
检查全部通过后,按 [author-facing.md](author-facing.md)「全部拆完」给作者汇报;下面这些检查结果不写进汇报。
|
|
8
|
+
|
|
9
|
+
- 再运行检查器和计划器;完整项目应 `direct_use`,继续意图应无待处理批次;
|
|
10
|
+
- 对比受保护路径 hash:旧 `章节/`、`剧情/`、`角色/`、`设定/`、`文风.md`、`拆文报告.md` 不得被增强或恢复流程改写;
|
|
11
|
+
- 允许变化的旧项目文件只有 `_progress.md` 受管状态区和 `_analysis_cache/` 新证据;
|
|
12
|
+
- 检查旧 `schema_version` 原值;
|
|
13
|
+
- 检查新投影的情节点序列、主题、基调、类型和“涉及”字段能被导入与写作流程读取。
|
|
14
|
+
|
|
15
|
+
## 维护者回归
|
|
16
|
+
|
|
17
|
+
改动本 skill 后必须回归:黄金三章、逐章摘要、情绪模块、节奏、角色、设定、文风、导入、对标和写作仍可用;旧完整项目直接使用;部分项目只补精确缺章;混存项目报告来源但不重拆;增强/恢复不改旧产物与原 schema。报告未执行的真实模型或跨平台检查,不得用静态 fixture 冒充。
|
|
18
|
+
|
|
19
|
+
联合验收用 [semantic-acceptance-fixtures.md](semantic-acceptance-fixtures.md) 的六项 0–2 分表,真实模型结果至少 10/12 且无硬失败才算语义通过。
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# 原文变化与章号对不上
|
|
2
|
+
|
|
3
|
+
`build_chapter_index.py` 因原文变化非零退出,或返回 `chapter_mapping_ambiguous`、`index_would_shrink` 时读本文件;把脚本的 `author_message` 按 [author-facing.md](author-facing.md)「停下来或出错时」转告作者。
|
|
4
|
+
|
|
5
|
+
同源索引直接复用且不重写。原文变化时先非零退出;人工确认后加 `--rebuild`。重建沿用已有索引的章号口径:已有索引第 1 章是正式章节、而原文开头有楔子/序章/第0章(即上次用了 `--fold-prologue`),自动照样并入,无需再加参数;重建出的前面各章与已有索引对不上(`chapter_mapping_ambiguous:position=N` 或 `index_would_shrink`)时不写索引,返回 `author_message`。追加新章时旧章 hash 保持稳定,输出仅列出新增或内容变化的 `pending_chapters`。改动已拆章节的原文只会让覆盖它的批次缓存失效、下次计划重读该批;已有摘要不刷新——要刷新哪章就删掉哪章的 `章节/第N章_摘要.md` 和覆盖它的 `_analysis_cache/批次-*.md` 再续跑(只删摘要会从缓存原样补回)。旧成果按旧章号命名,写新索引前逐一核对:旧摘要一律参与;黄金三章在 `_progress.md` 来自旧版(有「章节边界」表,或没有运行状态受管区)时参与,本次 Stage 1 按索引写的不参与。有旧版「章节边界」表时逐章定位:起始行正好是某章标题行就直接认定;否则按标题找(统一全半角,去掉章号前缀和「(求收藏)」「【二合一】」这类尾注,允许包含关系;标题只有章号时比章号),多个候选取离旧起始行最近的;全部对上才放行;没有表时,原文首章不是第 1 章就无法确认。对不上时不写索引,返回 `chapter_mapping_ambiguous` 和 `author_message`;`plan` 遇到已建好的错位索引同样拒绝。作者的三个选择:
|
|
6
|
+
|
|
7
|
+
1. **按旧章号继续(推荐)**:`build_chapter_index.py ... --fold-prologue`,开头的楔子/序章/引子/前言/第0章并进第一个正式章节,编号回到旧版口径,旧成果原样复用;输出里的 `folded_into_first_chapter` 列出被并入的章节标签(如「楔子」「第0章」)。楔子内容不会单独拆,要在报告「还不确定的地方」说明。
|
|
8
|
+
2. **楔子单独成章**:把除 `原文/` 外所有按旧章号写的产物——`章节/`、`剧情/`、`角色/`、`设定/`、`人物关系图/`、`快速预览.md`、`概要.md`、`拆文报告.md`、`文风.md`、`_analysis_cache/批次-*.md` 和 `_progress.md`——挪进 `_analysis_cache/legacy/旧章号/`(挪走,不删除),再从 Stage 0 按新章号重拆。旧库只拆到黄金三章时代价最小。
|
|
9
|
+
3. **换一个新目录整本重拆**。
|
|
10
|
+
|
|
11
|
+
`chapter_index.csv` 已按错误口径建好时,先删掉它(纯机械章节表,可随时重建),再按选择重建;`--rebuild` 只沿用已有索引的口径,不能用来改口径。
|