@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
|
@@ -1,61 +1,29 @@
|
|
|
1
1
|
"""Load and format research, consolidation, and substrate context."""
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
|
-
|
|
5
|
-
import importlib.util
|
|
6
|
-
|
|
7
|
-
|
|
8
4
|
import json
|
|
9
|
-
|
|
10
|
-
|
|
11
5
|
import re
|
|
12
|
-
|
|
13
6
|
from pathlib import Path
|
|
14
7
|
|
|
15
8
|
from lib import CONFIG
|
|
16
9
|
|
|
17
|
-
from lib import log
|
|
18
|
-
|
|
19
|
-
try:
|
|
20
|
-
from deslop_qc import analyze_deslop_qc
|
|
21
|
-
except ModuleNotFoundError:
|
|
22
|
-
_deslop_qc_path = Path(__file__).with_name("deslop_qc.py")
|
|
23
|
-
_deslop_qc_spec = importlib.util.spec_from_file_location(
|
|
24
|
-
"deslop_qc", _deslop_qc_path
|
|
25
|
-
)
|
|
26
|
-
if _deslop_qc_spec is None or _deslop_qc_spec.loader is None:
|
|
27
|
-
raise
|
|
28
|
-
_deslop_qc_module = importlib.util.module_from_spec(_deslop_qc_spec)
|
|
29
|
-
_deslop_qc_spec.loader.exec_module(_deslop_qc_module)
|
|
30
|
-
analyze_deslop_qc = _deslop_qc_module.analyze_deslop_qc
|
|
31
|
-
|
|
32
10
|
|
|
33
11
|
def _load_background_research(work_dir):
|
|
34
|
-
"""Load the agent-authored background_research.json
|
|
35
|
-
|
|
36
|
-
This file is the single highest-leverage quality input (character names,
|
|
37
|
-
relationships, plot context). It used to be documented but never read by
|
|
38
|
-
any code, so researched story knowledge never reached the writing brief.
|
|
39
|
-
"""
|
|
12
|
+
"""Load the agent-authored background_research.json ({} when absent)."""
|
|
40
13
|
path = Path(work_dir) / "background_research.json"
|
|
41
14
|
if not path.exists():
|
|
42
15
|
return {}
|
|
43
|
-
|
|
44
|
-
data = json.loads(path.read_text(encoding="utf-8"))
|
|
45
|
-
except (json.JSONDecodeError, OSError) as exc:
|
|
46
|
-
log(f"警告: background_research.json 读取失败,忽略: {exc}")
|
|
47
|
-
return {}
|
|
48
|
-
return data if isinstance(data, dict) else {}
|
|
16
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
49
17
|
|
|
50
18
|
|
|
51
19
|
def _clip_text(text, limit):
|
|
52
|
-
|
|
53
|
-
return
|
|
20
|
+
"""Collapse whitespace and clip. Research fields are optional, so None clips to ""."""
|
|
21
|
+
return re.sub(r"\s+", " ", str(text or "")).strip()[:limit]
|
|
54
22
|
|
|
55
23
|
|
|
56
24
|
def _format_background_research(research, limit=1800):
|
|
57
25
|
"""Render background_research.json into a bounded Story-context brief section."""
|
|
58
|
-
if not
|
|
26
|
+
if not research:
|
|
59
27
|
return []
|
|
60
28
|
lines = [
|
|
61
29
|
"## Story context (from background_research.json)",
|
|
@@ -72,50 +40,33 @@ def _format_background_research(research, limit=1800):
|
|
|
72
40
|
if value:
|
|
73
41
|
lines.append(f"- {label}: {value}")
|
|
74
42
|
|
|
75
|
-
characters = research.get("characters")
|
|
76
|
-
if
|
|
43
|
+
characters = research.get("characters", {})
|
|
44
|
+
if characters:
|
|
77
45
|
lines.append("- Characters:")
|
|
78
46
|
for name, desc in list(characters.items())[:12]:
|
|
79
|
-
|
|
80
|
-
clean_desc = _clip_text(desc, 160)
|
|
81
|
-
if clean_name:
|
|
82
|
-
lines.append(f" - {clean_name}: {clean_desc}")
|
|
47
|
+
lines.append(f" - {_clip_text(name, 60)}: {_clip_text(desc, 160)}")
|
|
83
48
|
|
|
84
|
-
details = research.get("character_details")
|
|
85
|
-
if
|
|
49
|
+
details = research.get("character_details", {})
|
|
50
|
+
if details:
|
|
86
51
|
lines.append("- Character details:")
|
|
87
52
|
for name, info in list(details.items())[:8]:
|
|
88
|
-
if not isinstance(info, dict):
|
|
89
|
-
continue
|
|
90
53
|
bits = []
|
|
91
|
-
aliases = info.get("aliases")
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
clean_aliases = [alias for alias in clean_aliases if alias]
|
|
95
|
-
if clean_aliases:
|
|
96
|
-
bits.append("别名 " + "/".join(clean_aliases))
|
|
54
|
+
aliases = [_clip_text(alias, 40) for alias in info.get("aliases", [])[:4]]
|
|
55
|
+
if aliases:
|
|
56
|
+
bits.append("别名 " + "/".join(aliases))
|
|
97
57
|
role = _clip_text(info.get("role"), 80)
|
|
98
58
|
if role:
|
|
99
59
|
bits.append(role)
|
|
100
|
-
rels = info.get("relationships")
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
lines.append(f" - {clean_name}: {'; '.join(bits)}")
|
|
109
|
-
|
|
110
|
-
arcs = research.get("plot_arcs")
|
|
111
|
-
if isinstance(arcs, list) and arcs:
|
|
60
|
+
rels = [_clip_text(rel, 80) for rel in info.get("relationships", [])[:4]]
|
|
61
|
+
if rels:
|
|
62
|
+
bits.append(";".join(rels))
|
|
63
|
+
if bits:
|
|
64
|
+
lines.append(f" - {_clip_text(name, 60)}: {'; '.join(bits)}")
|
|
65
|
+
|
|
66
|
+
arcs = research.get("plot_arcs", [])
|
|
67
|
+
if arcs:
|
|
112
68
|
lines.append("- Plot arcs:")
|
|
113
69
|
for arc in arcs[:8]:
|
|
114
|
-
if not isinstance(arc, dict):
|
|
115
|
-
value = _clip_text(arc, 180)
|
|
116
|
-
if value:
|
|
117
|
-
lines.append(f" - {value}")
|
|
118
|
-
continue
|
|
119
70
|
name = _clip_text(arc.get("name"), 80)
|
|
120
71
|
desc = _clip_text(arc.get("description"), 180)
|
|
121
72
|
status = _clip_text(arc.get("status"), 40)
|
|
@@ -123,15 +74,10 @@ def _format_background_research(research, limit=1800):
|
|
|
123
74
|
tail = f" [{status}]" if status else ""
|
|
124
75
|
lines.append(f" - {name}: {desc}{tail}".rstrip())
|
|
125
76
|
|
|
126
|
-
notes = research.get("cultural_notes")
|
|
127
|
-
if
|
|
77
|
+
notes = research.get("cultural_notes", [])
|
|
78
|
+
if notes:
|
|
128
79
|
lines.append("- Cultural notes:")
|
|
129
80
|
for note in notes[:6]:
|
|
130
|
-
if not isinstance(note, dict):
|
|
131
|
-
value = _clip_text(note, 160)
|
|
132
|
-
if value:
|
|
133
|
-
lines.append(f" - {value}")
|
|
134
|
-
continue
|
|
135
81
|
item = _clip_text(note.get("item"), 80)
|
|
136
82
|
expl = _clip_text(note.get("explanation"), 160)
|
|
137
83
|
if item and expl:
|
|
@@ -150,7 +96,8 @@ def _format_background_research(research, limit=1800):
|
|
|
150
96
|
if len(text) <= limit:
|
|
151
97
|
return lines
|
|
152
98
|
clipped = text[:limit].rsplit("\n", 1)[0].rstrip()
|
|
153
|
-
return
|
|
99
|
+
return [
|
|
100
|
+
*clipped.splitlines(),
|
|
154
101
|
"",
|
|
155
102
|
"[Story context clipped to keep ASR/visual evidence in context]",
|
|
156
103
|
"",
|
|
@@ -169,16 +116,9 @@ def assess_understanding_substrate(
|
|
|
169
116
|
so it must not grade as "rich"; otherwise the sparse-substrate warning, the research
|
|
170
117
|
directive, and the density relief never fire for exactly that cold-narration case.
|
|
171
118
|
"""
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
1 for s in scenes if isinstance(s, dict) and s.get("frame_facts")
|
|
176
|
-
)
|
|
177
|
-
desc_lens = [
|
|
178
|
-
len(str(s.get("description", "")).strip())
|
|
179
|
-
for s in scenes
|
|
180
|
-
if isinstance(s, dict)
|
|
181
|
-
]
|
|
119
|
+
asr_chars = sum(len(seg["text"]) for seg in asr_result)
|
|
120
|
+
scenes_with_facts = sum(1 for s in scenes_analysis if s.get("frame_facts"))
|
|
121
|
+
desc_lens = [len(s["description"]) for s in scenes_analysis]
|
|
182
122
|
avg_desc = sum(desc_lens) // len(desc_lens) if desc_lens else 0
|
|
183
123
|
|
|
184
124
|
has_asr = asr_chars >= 20
|
|
@@ -193,7 +133,7 @@ def assess_understanding_substrate(
|
|
|
193
133
|
return {
|
|
194
134
|
"level": level,
|
|
195
135
|
"asr_chars": asr_chars,
|
|
196
|
-
"scene_count": len(
|
|
136
|
+
"scene_count": len(scenes_analysis),
|
|
197
137
|
"scenes_with_frame_facts": scenes_with_facts,
|
|
198
138
|
"avg_description_len": avg_desc,
|
|
199
139
|
"has_story_context": bool(has_story_context),
|
|
@@ -202,7 +142,7 @@ def assess_understanding_substrate(
|
|
|
202
142
|
|
|
203
143
|
def _format_substrate_warning(assessment):
|
|
204
144
|
"""Render a loud brief banner when the understanding substrate is weak."""
|
|
205
|
-
if
|
|
145
|
+
if assessment["level"] == "rich":
|
|
206
146
|
return []
|
|
207
147
|
if assessment["level"] == "empty":
|
|
208
148
|
head = "⚠️ UNDERSTANDING SUBSTRATE IS EMPTY — narration will be generic guesswork unless you fix this first."
|
|
@@ -236,8 +176,8 @@ def _format_asr_chunks_for_brief(chunks, max_chunks=24):
|
|
|
236
176
|
"",
|
|
237
177
|
]
|
|
238
178
|
for chunk in chunks[:max_chunks]:
|
|
239
|
-
scene_ids = ",".join(str(sid) for sid in chunk
|
|
240
|
-
text =
|
|
179
|
+
scene_ids = ",".join(str(sid) for sid in chunk["scene_ids"]) or "n/a"
|
|
180
|
+
text = chunk["text"]
|
|
241
181
|
if len(text) > 900:
|
|
242
182
|
text = text[:897] + "..."
|
|
243
183
|
lines.extend(
|
|
@@ -266,29 +206,27 @@ def _format_timeline_fusion_for_brief(fusion, max_items=40):
|
|
|
266
206
|
"",
|
|
267
207
|
]
|
|
268
208
|
for item in fusion[:max_items]:
|
|
269
|
-
start, end = item
|
|
270
|
-
slots = item.get("narration_slots") or []
|
|
209
|
+
start, end = item["time_range"]
|
|
271
210
|
slot_text = (
|
|
272
211
|
", ".join(
|
|
273
212
|
f"{slot['start']:.1f}-{slot['end']:.1f}s/{slot['char_budget']}字"
|
|
274
|
-
for slot in
|
|
213
|
+
for slot in item["narration_slots"][:4]
|
|
275
214
|
)
|
|
276
215
|
or "none"
|
|
277
216
|
)
|
|
278
|
-
dialogue = item.get("dialogue_segments") or []
|
|
279
217
|
dialogue_text = (
|
|
280
218
|
"; ".join(
|
|
281
|
-
f"{seg['start']:.1f}-{seg['end']:.1f}s {seg
|
|
282
|
-
for seg in
|
|
283
|
-
if seg
|
|
219
|
+
f"{seg['start']:.1f}-{seg['end']:.1f}s {seg['text'][:80]}"
|
|
220
|
+
for seg in item["dialogue_segments"][:3]
|
|
221
|
+
if seg["text"]
|
|
284
222
|
)
|
|
285
223
|
or "none"
|
|
286
224
|
)
|
|
287
225
|
lines.extend(
|
|
288
226
|
[
|
|
289
|
-
f"### Fusion scene {item
|
|
290
|
-
f"- Visual: {item
|
|
291
|
-
f"- Dialogue overlap: {item
|
|
227
|
+
f"### Fusion scene {item['scene_id']}: {start:.1f}-{end:.1f}s ({item['recommended_mode']})",
|
|
228
|
+
f"- Visual: {item['visual_description']}",
|
|
229
|
+
f"- Dialogue overlap: {item['dialogue_overlap_seconds']:.1f}s | {dialogue_text}",
|
|
292
230
|
f"- Narration slots: {slot_text}",
|
|
293
231
|
"",
|
|
294
232
|
]
|
|
@@ -302,7 +240,7 @@ def _format_timeline_fusion_for_brief(fusion, max_items=40):
|
|
|
302
240
|
|
|
303
241
|
|
|
304
242
|
def _consolidation_model():
|
|
305
|
-
return CONFIG
|
|
243
|
+
return CONFIG["vlm_model"]
|
|
306
244
|
|
|
307
245
|
|
|
308
246
|
def _clean_asr_prompt_fingerprint():
|
|
@@ -332,70 +270,77 @@ def _index_prompt_fingerprint():
|
|
|
332
270
|
return hashlib.md5(prompt.encode("utf-8")).hexdigest()
|
|
333
271
|
|
|
334
272
|
|
|
335
|
-
def _load_consolidation(work_dir, scenes_analysis
|
|
336
|
-
"""Load consolidate.py's understanding_index.json only when provenance matches
|
|
273
|
+
def _load_consolidation(work_dir, scenes_analysis):
|
|
274
|
+
"""Load consolidate.py's understanding_index.json only when its provenance matches
|
|
275
|
+
the current vlm_analysis.json, model and index prompt ({} otherwise)."""
|
|
337
276
|
work_dir = Path(work_dir)
|
|
338
277
|
path = work_dir / "understanding_index.json"
|
|
339
278
|
meta_path = work_dir / "understanding_index.json.meta.json"
|
|
340
279
|
if not path.exists() or not meta_path.exists():
|
|
341
280
|
return {}
|
|
342
281
|
try:
|
|
343
|
-
data = json.loads(path.read_text(encoding="utf-8"))
|
|
344
282
|
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
345
|
-
|
|
283
|
+
source_md5 = hashlib.md5(
|
|
284
|
+
(work_dir / "vlm_analysis.json").read_bytes()
|
|
285
|
+
).hexdigest()
|
|
286
|
+
except (OSError, json.JSONDecodeError):
|
|
346
287
|
return {}
|
|
347
|
-
if not isinstance(
|
|
288
|
+
if not isinstance(meta, dict):
|
|
348
289
|
return {}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
290
|
+
expected = {
|
|
291
|
+
"source_md5": source_md5,
|
|
292
|
+
"model": _consolidation_model(),
|
|
293
|
+
"prompt_md5": _index_prompt_fingerprint(),
|
|
294
|
+
}
|
|
295
|
+
if scenes_analysis:
|
|
296
|
+
expected["scene_count"] = len(scenes_analysis)
|
|
297
|
+
if not meta.items() >= expected.items():
|
|
353
298
|
return {}
|
|
354
|
-
|
|
299
|
+
try:
|
|
300
|
+
index = json.loads(path.read_text(encoding="utf-8"))
|
|
301
|
+
except (OSError, json.JSONDecodeError):
|
|
355
302
|
return {}
|
|
356
|
-
|
|
357
|
-
|
|
303
|
+
if not isinstance(index, dict) or not all(
|
|
304
|
+
isinstance(index.get(key), list)
|
|
305
|
+
for key in ("characters", "relationships", "plot_points", "entities")
|
|
306
|
+
):
|
|
358
307
|
return {}
|
|
359
|
-
if
|
|
308
|
+
if not all(isinstance(item, dict) for item in index["characters"]):
|
|
360
309
|
return {}
|
|
361
|
-
if
|
|
310
|
+
if not all(isinstance(item, dict) for item in index["relationships"]):
|
|
362
311
|
return {}
|
|
363
|
-
return
|
|
312
|
+
return index
|
|
364
313
|
|
|
365
314
|
|
|
366
315
|
def _format_consolidation(index):
|
|
367
|
-
"""Render consolidate.py's understanding_index.json into a compact brief section.
|
|
316
|
+
"""Render consolidate.py's understanding_index.json into a compact brief section.
|
|
317
|
+
|
|
318
|
+
plot_points/entities items are model output and may be bare strings or objects."""
|
|
368
319
|
if not index:
|
|
369
320
|
return []
|
|
370
321
|
lines = ["## Understanding index (from consolidate.py)", ""]
|
|
371
|
-
chars = index
|
|
322
|
+
chars = index["characters"]
|
|
372
323
|
if chars:
|
|
373
324
|
lines.append("- Characters:")
|
|
374
325
|
for c in chars:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
else:
|
|
380
|
-
lines.append(f" - {c}")
|
|
381
|
-
rels = index.get("relationships") or []
|
|
326
|
+
lines.append(
|
|
327
|
+
f" - {c.get('name', '?')}: {str(c.get('description', '')).strip()}"
|
|
328
|
+
)
|
|
329
|
+
rels = index["relationships"]
|
|
382
330
|
if rels:
|
|
383
331
|
lines.append("- Relationships:")
|
|
384
332
|
for r in rels:
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
else:
|
|
390
|
-
lines.append(f" - {r}")
|
|
391
|
-
plot = index.get("plot_points") or []
|
|
333
|
+
lines.append(
|
|
334
|
+
f" - {r.get('a', '?')} — {r.get('relation', '?')} — {r.get('b', '?')}"
|
|
335
|
+
)
|
|
336
|
+
plot = index["plot_points"]
|
|
392
337
|
if plot:
|
|
393
338
|
lines.append("- Plot spine:")
|
|
394
339
|
lines.extend(
|
|
395
340
|
f" {i + 1}. {p.get('text', p) if isinstance(p, dict) else p}"
|
|
396
341
|
for i, p in enumerate(plot)
|
|
397
342
|
)
|
|
398
|
-
ents = index
|
|
343
|
+
ents = index["entities"]
|
|
399
344
|
if ents:
|
|
400
345
|
ent_names = [str(e.get("name", e) if isinstance(e, dict) else e) for e in ents]
|
|
401
346
|
lines.append(f"- Entities: {', '.join(ent_names)}")
|