@lemoncat7/dsh-partner 2.4.2 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/docs/THIRD_PARTY_LICENSES.md +13 -0
- package/docs/attachment-storage.md +18 -0
- package/docs/director-skills.md +24 -0
- package/docs/long-task-continuation.md +51 -0
- package/docs/releases/2.5.0.md +13 -0
- package/docs/releases/2.6.0.md +11 -0
- package/docs/releases/2.7.0.md +21 -0
- package/lib/agent-runtime.d.ts +9 -1
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +117 -17
- package/lib/agent-runtime.js.map +1 -1
- package/lib/api/features/attachments-api.d.ts.map +1 -1
- package/lib/api/features/attachments-api.js +31 -1
- package/lib/api/features/attachments-api.js.map +1 -1
- package/lib/api.d.ts +1 -1
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +1 -1
- package/lib/api.js.map +1 -1
- package/lib/attachments/grouped-storage.d.ts +5 -0
- package/lib/attachments/grouped-storage.d.ts.map +1 -0
- package/lib/attachments/grouped-storage.js +47 -0
- package/lib/attachments/grouped-storage.js.map +1 -0
- package/lib/attachments/service.d.ts +17 -0
- package/lib/attachments/service.d.ts.map +1 -1
- package/lib/attachments/service.js +100 -8
- package/lib/attachments/service.js.map +1 -1
- package/lib/attachments/shared-storage.d.ts +7 -0
- package/lib/attachments/shared-storage.d.ts.map +1 -0
- package/lib/attachments/shared-storage.js +44 -0
- package/lib/attachments/shared-storage.js.map +1 -0
- package/lib/attachments/storage-admin.d.ts +6 -0
- package/lib/attachments/storage-admin.d.ts.map +1 -0
- package/lib/attachments/storage-admin.js +43 -0
- package/lib/attachments/storage-admin.js.map +1 -0
- package/lib/attachments/storage-types.d.ts +33 -0
- package/lib/attachments/storage-types.d.ts.map +1 -0
- package/lib/attachments/storage-types.js +2 -0
- package/lib/attachments/storage-types.js.map +1 -0
- package/lib/attachments/tool.d.ts.map +1 -1
- package/lib/attachments/tool.js +16 -5
- package/lib/attachments/tool.js.map +1 -1
- package/lib/channels/delivery-policy.d.ts.map +1 -1
- package/lib/channels/delivery-policy.js +7 -4
- package/lib/channels/delivery-policy.js.map +1 -1
- package/lib/channels/direct/message-format.js +418 -281
- package/lib/channels/direct/message-format.js.map +4 -4
- package/lib/channels/manager.d.ts +7 -3
- package/lib/channels/manager.d.ts.map +1 -1
- package/lib/channels/manager.js +73 -38
- package/lib/channels/manager.js.map +1 -1
- package/lib/channels/weixin/api.d.ts.map +1 -1
- package/lib/channels/weixin/api.js +7 -2
- package/lib/channels/weixin/api.js.map +1 -1
- package/lib/channels/weixin/errors.d.ts +14 -0
- package/lib/channels/weixin/errors.d.ts.map +1 -0
- package/lib/channels/weixin/errors.js +29 -0
- package/lib/channels/weixin/errors.js.map +1 -0
- package/lib/channels/weixin/poll.d.ts +14 -0
- package/lib/channels/weixin/poll.d.ts.map +1 -0
- package/lib/channels/weixin/poll.js +50 -0
- package/lib/channels/weixin/poll.js.map +1 -0
- package/lib/client-api.d.ts +1 -23
- package/lib/client-api.d.ts.map +1 -1
- package/lib/client-api.js.map +1 -1
- package/lib/client-controller.d.ts.map +1 -1
- package/lib/client-controller.js +3 -3
- package/lib/client-controller.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +1078 -679
- package/lib/client.js.map +4 -4
- package/lib/collaboration/composition.d.ts.map +1 -1
- package/lib/collaboration/composition.js +32 -8
- package/lib/collaboration/composition.js.map +1 -1
- package/lib/collaboration/domain.d.ts +8 -0
- package/lib/collaboration/domain.d.ts.map +1 -1
- package/lib/collaboration/domain.js.map +1 -1
- package/lib/collaboration/service.d.ts.map +1 -1
- package/lib/collaboration/service.js +38 -5
- package/lib/collaboration/service.js.map +1 -1
- package/lib/concern-notification.d.ts.map +1 -1
- package/lib/concern-notification.js +2 -1
- package/lib/concern-notification.js.map +1 -1
- package/lib/concern-tool.js +1 -1
- package/lib/concern-tool.js.map +1 -1
- package/lib/execution/reply-progress.d.ts.map +1 -1
- package/lib/execution/reply-progress.js +4 -2
- package/lib/execution/reply-progress.js.map +1 -1
- package/lib/execution/service.d.ts +1 -1
- package/lib/execution/service.d.ts.map +1 -1
- package/lib/execution/service.js +4 -7
- package/lib/execution/service.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -1
- package/lib/index.js.map +1 -1
- package/lib/memory-reflection.js +2 -2
- package/lib/memory-reflection.js.map +1 -1
- package/lib/message-source.d.ts +25 -0
- package/lib/message-source.d.ts.map +1 -0
- package/lib/message-source.js +8 -0
- package/lib/message-source.js.map +1 -0
- package/lib/observation-loop.js +4 -4
- package/lib/observation-loop.js.map +1 -1
- package/lib/pendant/reader.js +1 -1
- package/lib/pendant/reader.js.map +1 -1
- package/lib/pendant/settings-panel.js +1 -1
- package/lib/pendant/settings-panel.js.map +1 -1
- package/lib/persona/service.js +1 -1
- package/lib/persona/service.js.map +1 -1
- package/lib/requirements/attachments.d.ts +7 -0
- package/lib/requirements/attachments.d.ts.map +1 -0
- package/lib/requirements/attachments.js +26 -0
- package/lib/requirements/attachments.js.map +1 -0
- package/lib/requirements/domain.d.ts +1 -1
- package/lib/requirements/domain.d.ts.map +1 -1
- package/lib/requirements/service.d.ts.map +1 -1
- package/lib/requirements/service.js +4 -1
- package/lib/requirements/service.js.map +1 -1
- package/lib/scheduler/channel-origin.d.ts +7 -0
- package/lib/scheduler/channel-origin.d.ts.map +1 -0
- package/lib/scheduler/channel-origin.js +27 -0
- package/lib/scheduler/channel-origin.js.map +1 -0
- package/lib/scheduler/continuations.d.ts +29 -0
- package/lib/scheduler/continuations.d.ts.map +1 -0
- package/lib/scheduler/continuations.js +286 -0
- package/lib/scheduler/continuations.js.map +1 -0
- package/lib/scheduler/domain.d.ts +40 -0
- package/lib/scheduler/domain.d.ts.map +1 -1
- package/lib/scheduler/final-reply.d.ts +13 -0
- package/lib/scheduler/final-reply.d.ts.map +1 -0
- package/lib/scheduler/final-reply.js +21 -0
- package/lib/scheduler/final-reply.js.map +1 -0
- package/lib/scheduler/service.d.ts +2 -0
- package/lib/scheduler/service.d.ts.map +1 -1
- package/lib/scheduler/service.js +30 -1
- package/lib/scheduler/service.js.map +1 -1
- package/lib/scheduler/wakeup-context.d.ts +7 -0
- package/lib/scheduler/wakeup-context.d.ts.map +1 -0
- package/lib/scheduler/wakeup-context.js +14 -0
- package/lib/scheduler/wakeup-context.js.map +1 -0
- package/lib/scheduler/wakeup.d.ts +11 -0
- package/lib/scheduler/wakeup.d.ts.map +1 -0
- package/lib/scheduler/wakeup.js +52 -0
- package/lib/scheduler/wakeup.js.map +1 -0
- package/lib/skills/builtin-resources.d.ts +6 -0
- package/lib/skills/builtin-resources.d.ts.map +1 -0
- package/lib/skills/builtin-resources.js +47 -0
- package/lib/skills/builtin-resources.js.map +1 -0
- package/lib/skills/builtin.d.ts +2 -5
- package/lib/skills/builtin.d.ts.map +1 -1
- package/lib/skills/builtin.js +11 -0
- package/lib/skills/builtin.js.map +1 -1
- package/lib/skills/director-builtins.d.ts +8 -0
- package/lib/skills/director-builtins.d.ts.map +1 -0
- package/lib/skills/director-builtins.js +65 -0
- package/lib/skills/director-builtins.js.map +1 -0
- package/lib/skills/drama-builtin.d.ts +3 -0
- package/lib/skills/drama-builtin.d.ts.map +1 -0
- package/lib/skills/drama-builtin.js +49 -0
- package/lib/skills/drama-builtin.js.map +1 -0
- package/lib/skills/long-task-continuation.d.ts +4 -0
- package/lib/skills/long-task-continuation.d.ts.map +1 -0
- package/lib/skills/long-task-continuation.js +43 -0
- package/lib/skills/long-task-continuation.js.map +1 -0
- package/lib/skills/service.d.ts.map +1 -1
- package/lib/skills/service.js +8 -3
- package/lib/skills/service.js.map +1 -1
- package/lib/skills/viora-canvas-builtin.d.ts +3 -0
- package/lib/skills/viora-canvas-builtin.d.ts.map +1 -0
- package/lib/skills/viora-canvas-builtin.js +19 -0
- package/lib/skills/viora-canvas-builtin.js.map +1 -0
- package/lib/storage/migrate-layout.d.ts.map +1 -1
- package/lib/storage/migrate-layout.js +13 -4
- package/lib/storage/migrate-layout.js.map +1 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +2 -0
- package/lib/store.js.map +1 -1
- package/lib/tasks/context.js +1 -0
- package/lib/tasks/context.js.map +1 -1
- package/lib/tasks/continuation-yield.d.ts +4 -0
- package/lib/tasks/continuation-yield.d.ts.map +1 -0
- package/lib/tasks/continuation-yield.js +27 -0
- package/lib/tasks/continuation-yield.js.map +1 -0
- package/lib/tasks/continuation.d.ts +13 -0
- package/lib/tasks/continuation.d.ts.map +1 -0
- package/lib/tasks/continuation.js +106 -0
- package/lib/tasks/continuation.js.map +1 -0
- package/lib/tasks/domain.d.ts +2 -0
- package/lib/tasks/domain.d.ts.map +1 -1
- package/lib/tasks/scheduling.d.ts.map +1 -1
- package/lib/tasks/scheduling.js +7 -0
- package/lib/tasks/scheduling.js.map +1 -1
- package/lib/tasks/service.d.ts +2 -1
- package/lib/tasks/service.d.ts.map +1 -1
- package/lib/tasks/service.js +19 -1
- package/lib/tasks/service.js.map +1 -1
- package/lib/ui/attachment-storage.d.ts +2 -0
- package/lib/ui/attachment-storage.d.ts.map +1 -0
- package/lib/ui/attachment-storage.js +59 -0
- package/lib/ui/attachment-storage.js.map +1 -0
- package/lib/ui/capability-editor.js +1 -1
- package/lib/ui/capability-editor.js.map +1 -1
- package/lib/ui/companion-access-panel.js +1 -1
- package/lib/ui/companion-access-panel.js.map +1 -1
- package/lib/ui/companion-create.js +1 -1
- package/lib/ui/companion-create.js.map +1 -1
- package/lib/ui/companion-picker.js +1 -1
- package/lib/ui/companion-picker.js.map +1 -1
- package/lib/ui/identity-editor.js +1 -1
- package/lib/ui/identity-editor.js.map +1 -1
- package/lib/ui/mcp-capabilities.js +1 -1
- package/lib/ui/mcp-capabilities.js.map +1 -1
- package/lib/ui/mcp-panel.js +1 -1
- package/lib/ui/mcp-panel.js.map +1 -1
- package/lib/ui/memory/concern-board.js +2 -2
- package/lib/ui/memory/concern-board.js.map +1 -1
- package/lib/ui/memory/concern-deletion.d.ts +1 -1
- package/lib/ui/memory/concern-deletion.d.ts.map +1 -1
- package/lib/ui/memory/concern-deletion.js +9 -4
- package/lib/ui/memory/concern-deletion.js.map +1 -1
- package/lib/ui/memory/concern-panel.js +1 -1
- package/lib/ui/memory/concern-panel.js.map +1 -1
- package/lib/ui/memory/memory-library.js +1 -1
- package/lib/ui/memory/memory-library.js.map +1 -1
- package/lib/ui/memory/memory-panel.js +1 -1
- package/lib/ui/memory/memory-panel.js.map +1 -1
- package/lib/ui/partner-components.js +1 -1
- package/lib/ui/partner-components.js.map +1 -1
- package/lib/ui/requirement-board.js +1 -1
- package/lib/ui/requirement-board.js.map +1 -1
- package/lib/ui/requirement-dialog.js +1 -1
- package/lib/ui/requirement-dialog.js.map +1 -1
- package/lib/ui/requirement-tasks-panel.js +1 -1
- package/lib/ui/requirement-tasks-panel.js.map +1 -1
- package/lib/ui/schedule-panel.d.ts.map +1 -1
- package/lib/ui/schedule-panel.js +20 -4
- package/lib/ui/schedule-panel.js.map +1 -1
- package/lib/ui/skills-panel.js +1 -1
- package/lib/ui/skills-panel.js.map +1 -1
- package/lib/ui/storage-settings.d.ts.map +1 -1
- package/lib/ui/storage-settings.js +2 -1
- package/lib/ui/storage-settings.js.map +1 -1
- package/lib/ui/task-board-stage.js +1 -1
- package/lib/ui/task-board-stage.js.map +1 -1
- package/lib/ui/workspace-components.js +1 -1
- package/lib/ui/workspace-components.js.map +1 -1
- package/lib/ui/workspace-navigation.d.ts +16 -16
- package/lib/ui/workspace-navigation.js +1 -1
- package/lib/ui/workspace-navigation.js.map +1 -1
- package/package.json +55 -41
- package/resources/skills/cinematic-director/manifest.json +205 -0
- package/resources/skills/cinematic-director/upstream/CHANGELOG.md +213 -0
- package/resources/skills/cinematic-director/upstream/CONTRIBUTING.md +316 -0
- package/resources/skills/cinematic-director/upstream/LICENSE +21 -0
- package/resources/skills/cinematic-director/upstream/README.md +507 -0
- package/resources/skills/cinematic-director/upstream/SKILL.md +420 -0
- package/resources/skills/cinematic-director/upstream/assets/beat-sheet-template.md +97 -0
- package/resources/skills/cinematic-director/upstream/assets/director-book-template.md +254 -0
- package/resources/skills/cinematic-director/upstream/assets/edit-timeline-template.md +147 -0
- package/resources/skills/cinematic-director/upstream/assets/keyframe-prompt-template.md +334 -0
- package/resources/skills/cinematic-director/upstream/assets/qc-checklist.md +153 -0
- package/resources/skills/cinematic-director/upstream/assets/shot-plan-template.md +95 -0
- package/resources/skills/cinematic-director/upstream/assets/sound-plan-template.md +181 -0
- package/resources/skills/cinematic-director/upstream/assets/video-prompt-template.md +305 -0
- package/resources/skills/cinematic-director/upstream/evals/run.md +69 -0
- package/resources/skills/cinematic-director/upstream/references/ai-video-tool-adapters.md +527 -0
- package/resources/skills/cinematic-director/upstream/references/blocking-and-staging.md +424 -0
- package/resources/skills/cinematic-director/upstream/references/cinematic-language.md +595 -0
- package/resources/skills/cinematic-director/upstream/references/continuity-bible.md +410 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/01_spielberg.md +192 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/02_hitchcock.md +193 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/03_kubrick.md +196 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/04_kurosawa.md +196 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/05_scorsese.md +195 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/06_fellini.md +197 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/07_bergman.md +197 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/08_tarkovsky.md +184 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/09_wong_kar_wai.md +189 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/10_nolan.md +185 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/11_villeneuve.md +188 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/12_fincher.md +185 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/13_refn.md +186 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/14_bi_gan.md +188 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/15_zhang_yimou.md +192 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/16_hou_hsiao_hsien.md +192 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/17_park_chan_wook.md +192 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/18_malick.md +191 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/19_michael_mann.md +192 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/20_coen_brothers.md +194 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/README.md +89 -0
- package/resources/skills/cinematic-director/upstream/references/director_styles/example_comparisons.md +140 -0
- package/resources/skills/cinematic-director/upstream/references/editing-and-assembly.md +263 -0
- package/resources/skills/cinematic-director/upstream/references/failure-modes.md +594 -0
- package/resources/skills/cinematic-director/upstream/references/genre-playbooks.md +407 -0
- package/resources/skills/cinematic-director/upstream/references/image-model-adapters.md +290 -0
- package/resources/skills/cinematic-director/upstream/references/lighting-and-color.md +349 -0
- package/resources/skills/cinematic-director/upstream/references/product-and-macro.md +207 -0
- package/resources/skills/cinematic-director/upstream/references/production-workflow.md +238 -0
- package/resources/skills/cinematic-director/upstream/references/prompt-lexicon.md +602 -0
- package/resources/skills/cinematic-director/upstream/references/sound-and-dialogue.md +364 -0
- package/resources/skills/director-skills/manifest.json +541 -0
- package/resources/skills/director-skills/upstream/CHANGELOG.md +32 -0
- package/resources/skills/director-skills/upstream/LICENSE +21 -0
- package/resources/skills/director-skills/upstream/MANIFEST.md +366 -0
- package/resources/skills/director-skills/upstream/README.md +251 -0
- package/resources/skills/director-skills/upstream/RESEARCH_PROVENANCE.md +37 -0
- package/resources/skills/director-skills/upstream/SKILL.md +114 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/SKILL.md +81 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/checklists/continuity_checklist.md +8 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/examples/character_continuity_update.md +9 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/examples/location_continuity_update.md +9 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/continuity_system.md +21 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/golden_image_workflow.md +29 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/identity_anchors.md +14 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/visual_bible_system.md +15 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/character_profile.md +15 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/continuity_checklist.md +11 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/costume_profile.md +12 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/location_profile.md +14 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/prop_profile.md +13 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/visual_style_bible.md +15 -0
- package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/tests/continuity_tests.md +6 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/SKILL.md +80 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/checklists/image_prompt_quality_checklist.md +10 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/examples/shot_list_to_image_prompts.md +4 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/image_prompt_components.md +22 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/lighting_and_photorealism.md +19 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/negative_prompt_patterns.md +18 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/prompt_anatomy.md +18 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/character_reference_sheet_prompt.md +5 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/cinematic_still_prompt.md +3 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/keyframe_prompt.md +6 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/location_reference_prompt.md +3 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/prop_prompt.md +3 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/tests/image_prompt_completeness_tests.md +5 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/SKILL.md +84 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/checklists/video_prompt_quality_checklist.md +9 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/examples/image_prompt_to_video_prompt.md +3 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/ai_film_production_pipeline.md +21 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/audio_motion_timing.md +16 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/camera_lens_movement.md +26 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/negative_prompt_patterns.md +18 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/prompt_anatomy.md +18 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/video_prompt_components.md +25 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/image_to_video_prompt.md +5 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/text_to_video_prompt.md +5 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/video_beat_prompt.md +9 -0
- package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/tests/video_prompt_completeness_tests.md +5 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/SKILL.md +82 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/checklists/model_export_checklist.md +9 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/failed_model_transfer_diagnosis.md +5 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/image_prompt_translation.md +10 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/same_scene_across_models.md +9 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/universal_prompt_to_model_specific_exports.md +14 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/video_prompt_translation.md +9 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/failure_modes_by_model.md +13 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/image_model_profiles.md +226 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/model_capability_matrix.md +24 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/model_update_protocol.md +10 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/parameter_compatibility.md +20 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/prompt_translation_rules.md +18 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/references/video_model_profiles.md +251 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/model_comparison_table.md +4 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/model_specific_prompt_export.md +21 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/multi_model_export_package.md +18 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/prompt_translation_report.md +13 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/universal_prompt_brief.md +25 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/model_routing_tests.md +8 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/prompt_translation_tests.md +7 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/research_provenance_tests.md +6 -0
- package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/unknown_capability_tests.md +7 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/SKILL.md +82 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/checklists/diagnostic_checklist.md +8 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/checklists/release_quality_checklist.md +9 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/examples/failed_output_to_revised_prompt.md +10 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/examples/final_prompt_package_export.md +10 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/failure_modes.md +13 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/iteration_diagnostics.md +17 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/revision_loops.md +17 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/story_voice_release_qc.md +30 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/failed_output_intake.md +13 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/final_production_package.md +11 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/prompt_revision_report.md +17 -0
- package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/tests/prompt_quality_checklist_tests.md +6 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/SKILL.md +81 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/checklists/scene_quality_checklist.md +8 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/examples/scene_to_screenplay_excerpt.md +18 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/dialogue_and_subtext.md +15 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/fountain_workflow.md +29 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/scene_construction.md +20 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/screenplay_format.md +18 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/beat_sheet.md +6 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/dialogue_pass.md +9 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/fountain_scene.md +25 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/screenplay_scene.md +19 -0
- package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/tests/screenplay_scene_tests.md +6 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/SKILL.md +83 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/checklists/concept_quality_checklist.md +8 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/examples/idea_to_logline.md +13 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/examples/logline_to_outline.md +10 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/references/ai_storytelling_workflow.md +46 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/references/continuity_handoff.md +8 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/references/screenplay_structure.md +19 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/references/story_development_workflow.md +23 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/beat_sheet.md +7 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/idea_intake.md +16 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/logline.md +10 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/short_film_treatment.md +23 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/short_story_plan.md +22 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/story_premise.md +19 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/templates/story_revision_audit.md +22 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/tests/output_format_tests.md +3 -0
- package/resources/skills/director-skills/upstream/skills/short-film-development/tests/routing_tests.md +7 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/SKILL.md +77 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/checklists/shotlist_quality_checklist.md +8 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/examples/screenplay_scene_to_shot_list.md +8 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/camera_plan.md +11 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/production_breakdown.md +11 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/shotlist_schema.md +21 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/asset_list.md +7 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/camera_plan.md +10 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/scene_breakdown.md +16 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/shot_list.md +5 -0
- package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/tests/screenplay_to_shotlist_tests.md +5 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/SKILL.md +77 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/checklists/style_quality_checklist.md +8 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/examples/genre_to_visual_direction.md +15 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/camera_lens_movement.md +26 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/cinematic_language.md +32 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/genre_style_tone.md +17 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/lighting_and_photorealism.md +19 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/templates/visual_style_bible.md +33 -0
- package/resources/skills/director-skills/upstream/skills/style-cinematography-director/tests/style_translation_tests.md +5 -0
- package/resources/skills/director-skills/upstream/tests/master_router_tests.md +80 -0
- package/resources/skills/drama-production/manifest.json +933 -0
- package/resources/skills/drama-production/upstream/LICENSE +21 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/SKILL.md +153 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/creator-decision.example.jsonl +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/app.js +1263 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/index.html +114 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/styles.css +297 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/production-observation.example.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/project-template/short-drama.json +36 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/assets/reference-observation.example.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/audience-reveal.md +21 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/contract-and-ownership.md +45 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/creator-documents.md +230 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/creator-workflow.md +98 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards/Q/347/211/210/350/241/250/350/276/276.md +73 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//344/272/214/347/273/264/345/212/250/346/200/201/346/274/253.md +74 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//345/233/275/346/274/253/344/272/214/346/254/241/345/205/203.md +79 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//345/256/236/346/213/215.md +75 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//346/260/264/345/242/250/347/254/224/350/247/246.md +85 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//351/243/216/346/240/274/345/214/226/344/270/211/347/273/264.md +72 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/knowhow-index.md +85 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/look-development.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/pickup-and-alternate.md +24 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/production-form-profiles.md +126 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/reference-roles.md +53 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/references/runtime-preflight.md +24 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/scripts/creator_markdown_check.py +1413 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/scripts/dashboard_server.py +1798 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/scripts/project_tool.py +2454 -0
- package/resources/skills/drama-production/upstream/skills/short-drama/scripts/selftest.py +287 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/SKILL.md +76 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/character-look.example.jsonl +3 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/continuity.example.jsonl +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/decisions.example.jsonl +5 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/location-view.example.jsonl +3 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/occurrences.example.jsonl +5 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/prop-state.example.jsonl +3 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/examples/minimal/characters.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/examples/minimal/looks.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/asset-review-checklist.md +82 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/character-and-look.md +190 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/continuity-delta.md +120 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/continuity-lock.md +122 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/identity-vs-variant.md +140 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/location-and-view.md +84 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/occurrence-extraction.md +108 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/prop-and-state.md +126 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/stage-contract.md +43 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/voice-direction.md +179 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/scripts/asset_check.py +267 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-assets/scripts/selftest.py +89 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/SKILL.md +157 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/creative-brief.md +120 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/episode-map.jsonl +1 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/story-engine.md +181 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/adaptation-craft.md +219 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/creative-reference-intake.md +57 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/director-brief-craft.md +113 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/episode-design.md +509 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-and-hook-playbook.md +240 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//344/272/262/345/255/220/351/232/220/347/247/230.md +112 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//344/273/231/344/276/240/344/277/256/347/234/237.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/212/250/344/275/234/344/273/273/345/212/241.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/217/244/350/243/205/346/235/203/350/260/213.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/256/266/345/272/255/345/205/263/347/263/273.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//346/202/254/347/226/221/350/247/204/345/210/231.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//347/224/237/346/264/273/346/265/201.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//347/240/264/351/225/234/351/207/215/345/234/206.md +109 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//350/201/214/345/234/272/345/226/234/345/211/247.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//350/261/252/351/227/250/345/251/232/346/201/213.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//350/272/253/344/273/275/351/224/231/344/275/215.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards.md +92 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/mechanism-loop.md +282 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/multi-episode-intake.md +128 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/premise-devices.md +211 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/reveal-reversal-payoff.md +99 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/serial-character-and-memory.md +293 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/stage-contract.md +88 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/story-craft.md +359 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/scripts/episode_intake.py +699 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-develop/scripts/selftest.py +47 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/SKILL.md +146 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/README.md +60 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/package.json +15 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/cut-craft.md +38 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/delivery-verify.md +29 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/generated-footage.md +54 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/sound-and-subtitles.md +60 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/stage-contract.md +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/scripts/edit_tool.py +1156 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-edit/scripts/selftest.py +520 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/SKILL.md +73 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/image-prompt-spec.jsonl.md +187 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/image-prompts.md +42 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/lookdev-frame-spec.jsonl.md +128 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/lookdev-prompts.md +34 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/examples/minimal-image-prompt-specs.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/character-and-look.md +153 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/common-recipe.md +241 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/edit-and-revision.md +110 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/location-plate.md +151 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/look-and-state-variant.md +97 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/lookdev-frame.md +34 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/production-sheet-recipes.md +177 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/prop-plate.md +137 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/review-and-fixtures.md +92 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/stage-contract.md +32 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/scripts/image_prompt_check.py +443 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/scripts/selftest.py +95 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/SKILL.md +245 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +3 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/adaptation-triage.md +116 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/adaptation-value.md +124 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +155 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/chapter-extraction.md +144 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/stage-contract.md +67 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/scripts/novel_index.py +651 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/scripts/selftest.py +51 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/SKILL.md +183 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/adapter-contract.md +219 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/gpt-image-2.md +24 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-h3-video.md +86 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-music.md +25 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/seedance.md +64 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/fixture_adapter.py +77 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/production_tool.py +2205 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/provider_adapters.py +1747 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/selftest.py +135 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/SKILL.md +118 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/finding-template.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/supersession-decision.example.json +42 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/verdict-template.json +32 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/examples/minimal-findings.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/examples/minimal-verdict.json +34 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/anti-template-repair.md +109 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/production-quality-gates.md +276 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/project-calibration.md +83 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/review-method.md +81 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-assets-prompts.md +111 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-source-analysis.md +108 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-story-script.md +264 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-visual-motion.md +184 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/references/stage-contract.md +28 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/scripts/review_check.py +325 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-review/scripts/selftest.py +87 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/SKILL.md +175 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/coverage-audition.example.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/coverage-template.json +49 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/keyframe-prompts.md +36 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/keyframe-template.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/revision-lineage.fragment.json +12 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/scene-visual-plan.example.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/shot-template.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/blocking-playbooks.md +216 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/comic-keyframe-lexicon.md +156 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/coverage-audition.md +34 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/keyframe-craft.md +174 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/production-shot-grammar.md +279 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/review-and-fixtures.md +36 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/scene-visual-plan.md +30 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +155 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/shot-craft.md +266 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/shot-revision-identity.md +25 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/stage-contract.md +57 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/scripts/selftest.py +191 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/scripts/storyboard_check.py +824 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/SKILL.md +154 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/coverage-scope.fragment.json +21 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/delivery-container.jsonl.md +101 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +217 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/motion-terminal.example.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/music-spec.jsonl.md +31 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/performance.fragment.json +38 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/video-prompts.md +67 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/examples/minimal-music-specs.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/camera-audio-continuity.md +259 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/delivery-profile.md +273 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/generability.md +93 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/minimax-h3.md +253 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/motion-recipe.md +315 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/performance-action-timing.md +209 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/production-prompt-grammar.md +304 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/review-and-fixtures.md +139 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/seedance-2.0.md +50 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/seedance-2.5.md +122 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/stage-contract.md +60 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/target-model-profile.md +116 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/container_check.py +329 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/motion_timing_check.py +450 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/music_spec_check.py +327 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/selftest.py +82 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/SKILL.md +99 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/agents/openai.yaml +4 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/beats.jsonl +2 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/episode-card-standalone.json +120 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/episode-card.json +55 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/screenplay.md +36 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/voice-record-sheet.jsonl.md +89 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/dialogue-craft.md +526 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/production-format-dialect.md +273 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/scene-handoff-capsule.md +25 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/scene-sound-dramaturgy.md +43 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/screenplay-format.md +266 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/script-craft.md +745 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/stage-contract.md +36 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/references/substitutable-realization.md +107 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/duration_estimate.py +309 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/screenplay_index.py +1091 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/selftest.py +229 -0
- package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/voice_sheet_check.py +416 -0
- package/resources/skills/viora-canvas-production/manifest.json +23 -0
- package/resources/skills/viora-canvas-production/references/canvas-contract.md +50 -0
- package/resources/skills/viora-canvas-production/references/creative-production.md +50 -0
- package/resources/skills/viora-canvas-production/references/generation-delivery.md +36 -0
- package/resources/skills/viora-canvas-production/references/production-ledger.md +25 -0
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Build a byte-accurate, derived index for a short-drama Markdown screenplay.
|
|
3
|
+
|
|
4
|
+
The indexer recognizes only the screenplay grammar documented by the write
|
|
5
|
+
skill. It never rewrites the screenplay and never guesses through a
|
|
6
|
+
split/merge revision.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
from collections import defaultdict
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, NamedTuple
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Creators run these scripts on whatever interpreter their machine provides, so
|
|
23
|
+
# an unsupported version must say so instead of failing inside an import.
|
|
24
|
+
MINIMUM_PYTHON = (3, 9)
|
|
25
|
+
if sys.version_info < MINIMUM_PYTHON:
|
|
26
|
+
raise SystemExit(
|
|
27
|
+
"short-drama needs Python {}.{} or newer; this interpreter is {}.{}".format(
|
|
28
|
+
*MINIMUM_PYTHON, sys.version_info.major, sys.version_info.minor
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# REFERENCE RESOLVER -- reference implementation.
|
|
34
|
+
#
|
|
35
|
+
# Each skill checker carries its own copy of this block. The suite has no shared
|
|
36
|
+
# library on purpose: a skill must stay runnable after copying only its own
|
|
37
|
+
# directory, so duplicating these few lines across skills is the correct shape.
|
|
38
|
+
# Copy the block verbatim; do not import it.
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
SOURCES_RECORD_TYPE = "sources"
|
|
42
|
+
SOURCES_SCHEMA_VERSION = "1.0.0"
|
|
43
|
+
SOURCES_KEY = "sources"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ResolvedRef(NamedTuple):
|
|
47
|
+
"""An upstream reference with its snapshot resolved, whichever form it used."""
|
|
48
|
+
|
|
49
|
+
owner: str
|
|
50
|
+
artifact: str
|
|
51
|
+
record_id: str | None
|
|
52
|
+
field: str | None
|
|
53
|
+
authority: str | None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class RefFinding(NamedTuple):
|
|
57
|
+
"""A structural defect in a reference object."""
|
|
58
|
+
|
|
59
|
+
code: str
|
|
60
|
+
location: str
|
|
61
|
+
detail: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def load_sources(document: Any) -> dict[str, dict[str, Any]]:
|
|
65
|
+
"""Return the ``sources`` declaration of a parsed file, or ``{}`` if absent.
|
|
66
|
+
|
|
67
|
+
Accepts a parsed ``.json`` document (a dict) or the parsed record list of a
|
|
68
|
+
``.jsonl`` file, whose declaration lives on the first record.
|
|
69
|
+
"""
|
|
70
|
+
if isinstance(document, list):
|
|
71
|
+
document = document[0] if document else None
|
|
72
|
+
if not isinstance(document, dict):
|
|
73
|
+
return {}
|
|
74
|
+
declared = document.get("sources")
|
|
75
|
+
if not isinstance(declared, dict):
|
|
76
|
+
return {}
|
|
77
|
+
return {key: value for key, value in declared.items() if isinstance(value, dict)}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def resolve_ref(
|
|
81
|
+
ref: Any, sources: dict[str, dict[str, Any]], location: str
|
|
82
|
+
) -> tuple[ResolvedRef | None, RefFinding | None]:
|
|
83
|
+
"""Resolve a reference object written in either the compact or expanded form."""
|
|
84
|
+
if not isinstance(ref, dict):
|
|
85
|
+
return None, RefFinding("REF_IS_NOT_AN_OBJECT", location, f"got {type(ref).__name__}")
|
|
86
|
+
src = ref.get("src")
|
|
87
|
+
if isinstance(src, str):
|
|
88
|
+
entry = sources.get(src)
|
|
89
|
+
if entry is None:
|
|
90
|
+
return None, RefFinding(
|
|
91
|
+
"REF_SRC_IS_NOT_DECLARED", location, f"src {src!r} has no sources entry"
|
|
92
|
+
)
|
|
93
|
+
owner, artifact = entry.get("owner"), entry.get("artifact")
|
|
94
|
+
if not (isinstance(owner, str) and isinstance(artifact, str)):
|
|
95
|
+
return None, RefFinding(
|
|
96
|
+
"SOURCE_ENTRY_IS_INCOMPLETE",
|
|
97
|
+
location,
|
|
98
|
+
f"sources[{src!r}] needs owner/artifact",
|
|
99
|
+
)
|
|
100
|
+
elif all(isinstance(ref.get(key), str) for key in ("owner", "artifact")):
|
|
101
|
+
owner, artifact = ref["owner"], ref["artifact"]
|
|
102
|
+
else:
|
|
103
|
+
return None, RefFinding(
|
|
104
|
+
"REF_HAS_NO_UPSTREAM_BINDING", location, "needs src, or owner+artifact"
|
|
105
|
+
)
|
|
106
|
+
optional = {
|
|
107
|
+
key: ref[key]
|
|
108
|
+
for key in ("record_id", "field", "authority")
|
|
109
|
+
if isinstance(ref.get(key), str)
|
|
110
|
+
}
|
|
111
|
+
return (
|
|
112
|
+
ResolvedRef(
|
|
113
|
+
owner,
|
|
114
|
+
artifact,
|
|
115
|
+
optional.get("record_id"),
|
|
116
|
+
optional.get("field"),
|
|
117
|
+
optional.get("authority"),
|
|
118
|
+
),
|
|
119
|
+
None,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
# END REFERENCE RESOLVER
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
SCHEMA_VERSION = "1.0.0"
|
|
128
|
+
|
|
129
|
+
# An index declares its upstream screenplay once, on the meta record, and every
|
|
130
|
+
# reference in the file names it by key. A rebuild against a previous index adds
|
|
131
|
+
# a second key only when the previous screenplay is a different snapshot.
|
|
132
|
+
SCREENPLAY_SOURCE_KEY = "screenplay"
|
|
133
|
+
PREVIOUS_SCREENPLAY_SOURCE_KEY = "previous-screenplay"
|
|
134
|
+
|
|
135
|
+
SUPPORTED_TAGS = ("VO", "OS", "SFX", "画面文字", "连续性", "转场")
|
|
136
|
+
KIND_CODES = {
|
|
137
|
+
"scene_heading": "H",
|
|
138
|
+
"action": "A",
|
|
139
|
+
"dialogue": "D",
|
|
140
|
+
"production_tag": "P",
|
|
141
|
+
"comment": "C",
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Three digits through EP999, then unpadded — the same single spelling per
|
|
145
|
+
# episode number the lifecycle tool enforces on `episodes/<EP>/`. A fixed three
|
|
146
|
+
# digits would have made EP1000 a legal directory but an illegal heading.
|
|
147
|
+
_EPISODE = r"EP(?:[0-9]{3}|[1-9][0-9]{3,})"
|
|
148
|
+
EPISODE_HEADING_RE = re.compile(rf"^# (?P<episode>{_EPISODE})(?: .+)?$")
|
|
149
|
+
SCENE_HEADING_RE = re.compile(
|
|
150
|
+
rf"^## (?P<scene>{_EPISODE}-SC[0-9]{{3}}) "
|
|
151
|
+
r"(?P<space>内外|内|外) · (?P<location>\S(?:.*\S)?) · "
|
|
152
|
+
r"(?P<time>\S(?:.*\S)?)$"
|
|
153
|
+
)
|
|
154
|
+
DIALOGUE_RE = re.compile(
|
|
155
|
+
r"^(?P<speaker>[^\s:():\[\]#]{1,40})"
|
|
156
|
+
r"(?:((?P<cue>[^()\r\n]+)))?:(?P<text>\S[\s\S]*)$"
|
|
157
|
+
)
|
|
158
|
+
ASCII_DIALOGUE_RE = re.compile(
|
|
159
|
+
r"^[^\s:():\[\]#]{1,40}(?:([^()\r\n]+))?:\s*\S"
|
|
160
|
+
)
|
|
161
|
+
TAG_RE = re.compile(r"^\[(?P<tag>VO|OS|SFX|画面文字|连续性|转场)\]\s*(?P<body>\S[\s\S]*)$")
|
|
162
|
+
ANY_TAG_RE = re.compile(r"^\[(?P<tag>[^\]\r\n]+)\]")
|
|
163
|
+
# Production dialects write tags with full-width brackets (【特写】/【闪回】).
|
|
164
|
+
# Without this the paragraph matches nothing and is emitted as a plain action
|
|
165
|
+
# block with no issue, so the owner never learns the source needs
|
|
166
|
+
# normalization. The block is still emitted (see below) because block IDs are
|
|
167
|
+
# the durable cross-artifact reference; only the diagnosis is added.
|
|
168
|
+
# The lookahead keeps dialect dialogue on the dialogue path. The dialect writes
|
|
169
|
+
# the performance cue with ASCII parens and a full-width colon
|
|
170
|
+
# (【角色名】(表演括注):台词, production-format-dialect.md), so both paren
|
|
171
|
+
# styles must be exempted here.
|
|
172
|
+
FULLWIDTH_TAG_RE = re.compile(
|
|
173
|
+
r"^【(?P<tag>[^】\r\n]+)】"
|
|
174
|
+
r"(?!\s*(?:([^()\r\n]*)|\([^()\r\n]*\))?\s*[::])"
|
|
175
|
+
)
|
|
176
|
+
MALFORMED_TAG_RE = re.compile(r"^\[(?:VO|OS|SFX|画面文字|连续性|转场)(?:\s|:|:)")
|
|
177
|
+
VOICE_TAG_BODY_RE = re.compile(r"^(?P<speaker>[^\s:():\[\]#]{1,40}):(?P<text>\S[\s\S]*)$")
|
|
178
|
+
BLOCK_ID_RE = re.compile(r"^BLK-(?P<scope>.+)-(?P<code>[HADPC])(?P<number>\d+)$")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _newline_style(data: bytes) -> str:
|
|
182
|
+
crlf = data.count(b"\r\n")
|
|
183
|
+
bare_lf = data.count(b"\n") - crlf
|
|
184
|
+
bare_cr = data.count(b"\r") - crlf
|
|
185
|
+
styles = sum(bool(count) for count in (crlf, bare_lf, bare_cr))
|
|
186
|
+
if styles > 1:
|
|
187
|
+
return "mixed"
|
|
188
|
+
if crlf:
|
|
189
|
+
return "crlf"
|
|
190
|
+
if bare_lf:
|
|
191
|
+
return "lf"
|
|
192
|
+
if bare_cr:
|
|
193
|
+
return "cr"
|
|
194
|
+
return "none"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _line_table(data: bytes) -> list[dict[str, Any]]:
|
|
198
|
+
raw_lines = data.splitlines(keepends=True)
|
|
199
|
+
if not raw_lines and data == b"":
|
|
200
|
+
return []
|
|
201
|
+
if raw_lines and sum(map(len, raw_lines)) < len(data):
|
|
202
|
+
raw_lines.append(data[sum(map(len, raw_lines)) :])
|
|
203
|
+
lines: list[dict[str, Any]] = []
|
|
204
|
+
offset = 0
|
|
205
|
+
for number, raw in enumerate(raw_lines, 1):
|
|
206
|
+
if raw.endswith(b"\r\n"):
|
|
207
|
+
content = raw[:-2]
|
|
208
|
+
elif raw.endswith((b"\n", b"\r")):
|
|
209
|
+
content = raw[:-1]
|
|
210
|
+
else:
|
|
211
|
+
content = raw
|
|
212
|
+
lines.append(
|
|
213
|
+
{
|
|
214
|
+
"number": number,
|
|
215
|
+
"start": offset,
|
|
216
|
+
"content_end": offset + len(content),
|
|
217
|
+
"end": offset + len(raw),
|
|
218
|
+
"raw": raw,
|
|
219
|
+
"content": content,
|
|
220
|
+
"text": content.decode("utf-8"),
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
offset += len(raw)
|
|
224
|
+
return lines
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _span(
|
|
228
|
+
data: bytes,
|
|
229
|
+
lines: list[dict[str, Any]],
|
|
230
|
+
start_index: int,
|
|
231
|
+
end_index: int,
|
|
232
|
+
) -> dict[str, Any]:
|
|
233
|
+
start = lines[start_index]["start"]
|
|
234
|
+
end = lines[end_index]["content_end"]
|
|
235
|
+
raw = data[start:end]
|
|
236
|
+
return {
|
|
237
|
+
"byte_start": start,
|
|
238
|
+
"byte_end": end,
|
|
239
|
+
"line_start": lines[start_index]["number"],
|
|
240
|
+
"line_end": lines[end_index]["number"],
|
|
241
|
+
"_raw": raw,
|
|
242
|
+
"_text": raw.decode("utf-8"),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _issue(
|
|
247
|
+
span: dict[str, Any],
|
|
248
|
+
code: str,
|
|
249
|
+
message: str,
|
|
250
|
+
) -> dict[str, Any]:
|
|
251
|
+
return {
|
|
252
|
+
**{key: span[key] for key in ("byte_start", "byte_end", "line_start", "line_end")},
|
|
253
|
+
"issue_code": code,
|
|
254
|
+
"severity": "review_required",
|
|
255
|
+
"message": message,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _looks_like_markdown(text: str) -> bool:
|
|
260
|
+
stripped = text.lstrip()
|
|
261
|
+
return bool(
|
|
262
|
+
stripped.startswith(("#", "```", "~~~", ">", "- ", "* ", "+ "))
|
|
263
|
+
or re.match(r"\d+[.)]\s", stripped)
|
|
264
|
+
or re.fullmatch(r"[-*_]{3,}", stripped)
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _parse_screenplay(
|
|
269
|
+
data: bytes,
|
|
270
|
+
speaker_names: frozenset[str],
|
|
271
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
272
|
+
# Decode once up front so invalid UTF-8 fails before any output is written.
|
|
273
|
+
data.decode("utf-8")
|
|
274
|
+
lines = _line_table(data)
|
|
275
|
+
blocks: list[dict[str, Any]] = []
|
|
276
|
+
issues: list[dict[str, Any]] = []
|
|
277
|
+
episode_id: str | None = None
|
|
278
|
+
scene_id: str | None = None
|
|
279
|
+
index = 0
|
|
280
|
+
|
|
281
|
+
def append_block(kind: str, span: dict[str, Any], **fields: Any) -> None:
|
|
282
|
+
blocks.append(
|
|
283
|
+
{
|
|
284
|
+
"kind": kind,
|
|
285
|
+
"episode_id": episode_id,
|
|
286
|
+
"scene_id": scene_id,
|
|
287
|
+
**span,
|
|
288
|
+
**fields,
|
|
289
|
+
"_order": len(blocks),
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
while index < len(lines):
|
|
294
|
+
text = lines[index]["text"]
|
|
295
|
+
stripped = text.strip()
|
|
296
|
+
if not stripped:
|
|
297
|
+
index += 1
|
|
298
|
+
continue
|
|
299
|
+
|
|
300
|
+
if stripped.startswith("<!--"):
|
|
301
|
+
end_index = index
|
|
302
|
+
while end_index < len(lines) and "-->" not in lines[end_index]["text"]:
|
|
303
|
+
end_index += 1
|
|
304
|
+
if end_index >= len(lines):
|
|
305
|
+
span = _span(data, lines, index, len(lines) - 1)
|
|
306
|
+
issues.append(_issue(span, "malformed_comment", "Markdown 注释缺少闭合的 -->。"))
|
|
307
|
+
break
|
|
308
|
+
span = _span(data, lines, index, end_index)
|
|
309
|
+
if not span["_text"].strip().endswith("-->"):
|
|
310
|
+
issues.append(
|
|
311
|
+
_issue(span, "mixed_comment_content", "注释闭合符后还有内容,不能机械分块。")
|
|
312
|
+
)
|
|
313
|
+
else:
|
|
314
|
+
append_block("comment", span, production=False)
|
|
315
|
+
index = end_index + 1
|
|
316
|
+
continue
|
|
317
|
+
|
|
318
|
+
if stripped.startswith("##"):
|
|
319
|
+
span = _span(data, lines, index, index)
|
|
320
|
+
match = SCENE_HEADING_RE.fullmatch(stripped)
|
|
321
|
+
if not match:
|
|
322
|
+
scene_id = None
|
|
323
|
+
issues.append(
|
|
324
|
+
_issue(
|
|
325
|
+
span,
|
|
326
|
+
"invalid_scene_heading",
|
|
327
|
+
"场景标题必须为 ## <EP001-SC001> <内|外|内外> · <地点> · <时间/天气>。",
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
else:
|
|
331
|
+
scene_id = match.group("scene")
|
|
332
|
+
heading_episode = scene_id.split("-", 1)[0]
|
|
333
|
+
if episode_id and episode_id != heading_episode:
|
|
334
|
+
issues.append(
|
|
335
|
+
_issue(span, "episode_scene_mismatch", "场景 ID 与当前集标题的集 ID 不一致。")
|
|
336
|
+
)
|
|
337
|
+
scene_id = None
|
|
338
|
+
else:
|
|
339
|
+
episode_id = heading_episode
|
|
340
|
+
append_block(
|
|
341
|
+
"scene_heading",
|
|
342
|
+
span,
|
|
343
|
+
space=match.group("space"),
|
|
344
|
+
location=match.group("location"),
|
|
345
|
+
time_weather=match.group("time"),
|
|
346
|
+
)
|
|
347
|
+
index += 1
|
|
348
|
+
continue
|
|
349
|
+
|
|
350
|
+
if stripped.startswith("#"):
|
|
351
|
+
span = _span(data, lines, index, index)
|
|
352
|
+
match = EPISODE_HEADING_RE.fullmatch(stripped)
|
|
353
|
+
if match:
|
|
354
|
+
episode_id = match.group("episode")
|
|
355
|
+
scene_id = None
|
|
356
|
+
else:
|
|
357
|
+
scene_id = None
|
|
358
|
+
issues.append(_issue(span, "invalid_episode_heading", "集标题必须以 # EP001 开头(EP 加三位数字,超过 EP999 后不补零)。"))
|
|
359
|
+
index += 1
|
|
360
|
+
continue
|
|
361
|
+
|
|
362
|
+
end_index = index
|
|
363
|
+
while end_index + 1 < len(lines):
|
|
364
|
+
next_stripped = lines[end_index + 1]["text"].strip()
|
|
365
|
+
if not next_stripped or next_stripped.startswith(("#", "<!--")):
|
|
366
|
+
break
|
|
367
|
+
end_index += 1
|
|
368
|
+
span = _span(data, lines, index, end_index)
|
|
369
|
+
paragraph = span["_text"].strip()
|
|
370
|
+
|
|
371
|
+
if scene_id is None:
|
|
372
|
+
issues.append(
|
|
373
|
+
_issue(span, "content_outside_scene", "动作、对白和生产标签只能位于合法场景标题之后。")
|
|
374
|
+
)
|
|
375
|
+
index = end_index + 1
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
paragraph_lines = [line.strip() for line in paragraph.splitlines() if line.strip()]
|
|
379
|
+
if len(paragraph_lines) > 1:
|
|
380
|
+
later_dialogues = [
|
|
381
|
+
match
|
|
382
|
+
for line in paragraph_lines[1:]
|
|
383
|
+
if (match := DIALOGUE_RE.fullmatch(line)) is not None
|
|
384
|
+
]
|
|
385
|
+
if any(match.group("speaker") in speaker_names for match in later_dialogues) or any(
|
|
386
|
+
ASCII_DIALOGUE_RE.match(line)
|
|
387
|
+
or TAG_RE.fullmatch(line)
|
|
388
|
+
or ANY_TAG_RE.match(line)
|
|
389
|
+
or MALFORMED_TAG_RE.match(line)
|
|
390
|
+
for line in paragraph_lines[1:]
|
|
391
|
+
):
|
|
392
|
+
issues.append(
|
|
393
|
+
_issue(
|
|
394
|
+
span,
|
|
395
|
+
"missing_block_separator",
|
|
396
|
+
"动作、对白与生产标签之间需要空行,不能把不同语法块合成动作段落。",
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
index = end_index + 1
|
|
400
|
+
continue
|
|
401
|
+
if later_dialogues:
|
|
402
|
+
issues.append(
|
|
403
|
+
_issue(
|
|
404
|
+
span,
|
|
405
|
+
"ambiguous_dialogue_or_action",
|
|
406
|
+
"冒号前缀不在本次说话者清单中;由 write owner 判断是动作、画面文字还是对白。",
|
|
407
|
+
)
|
|
408
|
+
)
|
|
409
|
+
index = end_index + 1
|
|
410
|
+
continue
|
|
411
|
+
|
|
412
|
+
tag_match = TAG_RE.fullmatch(paragraph)
|
|
413
|
+
if tag_match:
|
|
414
|
+
tag = tag_match.group("tag")
|
|
415
|
+
body = tag_match.group("body")
|
|
416
|
+
fields: dict[str, Any] = {"tag": tag, "production": True}
|
|
417
|
+
if tag in {"VO", "OS"}:
|
|
418
|
+
voice_match = VOICE_TAG_BODY_RE.fullmatch(body)
|
|
419
|
+
if not voice_match:
|
|
420
|
+
issues.append(
|
|
421
|
+
_issue(span, "invalid_voice_tag_syntax", f"[{tag}] 必须明确说话者并使用全角冒号。")
|
|
422
|
+
)
|
|
423
|
+
index = end_index + 1
|
|
424
|
+
continue
|
|
425
|
+
fields["speaker"] = voice_match.group("speaker")
|
|
426
|
+
append_block("production_tag", span, **fields)
|
|
427
|
+
index = end_index + 1
|
|
428
|
+
continue
|
|
429
|
+
|
|
430
|
+
any_tag = ANY_TAG_RE.match(paragraph)
|
|
431
|
+
if any_tag:
|
|
432
|
+
issues.append(
|
|
433
|
+
_issue(
|
|
434
|
+
span,
|
|
435
|
+
"unsupported_production_tag",
|
|
436
|
+
f"不支持生产标签 [{any_tag.group('tag')}];仅支持 {', '.join(SUPPORTED_TAGS)}。",
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
index = end_index + 1
|
|
440
|
+
continue
|
|
441
|
+
if MALFORMED_TAG_RE.match(paragraph):
|
|
442
|
+
issues.append(_issue(span, "malformed_production_tag", "生产标签缺少闭合的 ]。"))
|
|
443
|
+
index = end_index + 1
|
|
444
|
+
continue
|
|
445
|
+
|
|
446
|
+
dialogue_match = DIALOGUE_RE.fullmatch(paragraph)
|
|
447
|
+
if dialogue_match:
|
|
448
|
+
if dialogue_match.group("speaker") not in speaker_names:
|
|
449
|
+
issues.append(
|
|
450
|
+
_issue(
|
|
451
|
+
span,
|
|
452
|
+
"ambiguous_dialogue_or_action",
|
|
453
|
+
"冒号前缀不在本次说话者清单中;由 write owner 判断是动作、画面文字还是对白。",
|
|
454
|
+
)
|
|
455
|
+
)
|
|
456
|
+
index = end_index + 1
|
|
457
|
+
continue
|
|
458
|
+
append_block(
|
|
459
|
+
"dialogue",
|
|
460
|
+
span,
|
|
461
|
+
speaker=dialogue_match.group("speaker"),
|
|
462
|
+
performance_cue=dialogue_match.group("cue"),
|
|
463
|
+
production=False,
|
|
464
|
+
)
|
|
465
|
+
index = end_index + 1
|
|
466
|
+
continue
|
|
467
|
+
if ASCII_DIALOGUE_RE.match(paragraph):
|
|
468
|
+
issues.append(
|
|
469
|
+
_issue(span, "invalid_dialogue_syntax", "对白说话者后必须使用全角冒号 :。")
|
|
470
|
+
)
|
|
471
|
+
index = end_index + 1
|
|
472
|
+
continue
|
|
473
|
+
if _looks_like_markdown(paragraph):
|
|
474
|
+
issues.append(
|
|
475
|
+
_issue(span, "unsupported_markdown", "该 Markdown 结构不属于受支持的剧本块。")
|
|
476
|
+
)
|
|
477
|
+
index = end_index + 1
|
|
478
|
+
continue
|
|
479
|
+
|
|
480
|
+
# Diagnosed last, so only paragraphs that would already be plain action
|
|
481
|
+
# are reported. Anything a dialect writes with a colon (【音效:…】,
|
|
482
|
+
# 【角色名】(括注):台词) has been claimed by the dialogue paths above
|
|
483
|
+
# and keeps main's disposition, and the block is still emitted here, so
|
|
484
|
+
# no block ID moves in either direction.
|
|
485
|
+
fullwidth_tag = FULLWIDTH_TAG_RE.match(paragraph)
|
|
486
|
+
if fullwidth_tag:
|
|
487
|
+
issues.append(
|
|
488
|
+
_issue(
|
|
489
|
+
span,
|
|
490
|
+
"unsupported_production_tag",
|
|
491
|
+
f"【{fullwidth_tag.group('tag')}】 是生产方言写法,不是本套件的生产标签;"
|
|
492
|
+
f"受支持的标签是半角 [ ] 加 {', '.join(SUPPORTED_TAGS)}。"
|
|
493
|
+
"先经规范化入口判断它应当成为动作、画面文字还是转场,再发布。",
|
|
494
|
+
)
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
append_block("action", span, production=False)
|
|
498
|
+
index = end_index + 1
|
|
499
|
+
|
|
500
|
+
return blocks, issues
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _read_jsonl(path: Path) -> list[dict[str, Any]]:
|
|
504
|
+
records: list[dict[str, Any]] = []
|
|
505
|
+
for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
|
506
|
+
if not line.strip():
|
|
507
|
+
continue
|
|
508
|
+
try:
|
|
509
|
+
value = json.loads(line)
|
|
510
|
+
except json.JSONDecodeError as error:
|
|
511
|
+
raise ValueError(f"{path.name}:{line_number}: invalid JSONL: {error.msg}") from error
|
|
512
|
+
if not isinstance(value, dict):
|
|
513
|
+
raise ValueError(f"{path.name}:{line_number}: JSONL record must be an object")
|
|
514
|
+
records.append(value)
|
|
515
|
+
return records
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _load_previous(
|
|
519
|
+
records: list[dict[str, Any]],
|
|
520
|
+
previous_source_path: Path,
|
|
521
|
+
) -> list[dict[str, Any]]:
|
|
522
|
+
# Without the previous screenplay's bytes a block's identity is its content
|
|
523
|
+
# digest and nothing more: exact-content reuse still holds, split/merge
|
|
524
|
+
# detection does not. A rewritten body block then loses its ID instead of
|
|
525
|
+
# exception by design — they are reused on a stable scene_id, so an edited
|
|
526
|
+
# heading keeps its ID; the scene is the same scene, and the blocks under it
|
|
527
|
+
# are what carry the content.
|
|
528
|
+
source_data = previous_source_path.read_bytes()
|
|
529
|
+
sources = load_sources(records)
|
|
530
|
+
body = [record for record in records if record.get("record_type") != SOURCES_RECORD_TYPE]
|
|
531
|
+
if not body or body[0].get("record_type") != "screenplay_index_meta":
|
|
532
|
+
raise ValueError("previous index is missing screenplay_index_meta")
|
|
533
|
+
# The previous index must name the screenplay it was built from. It no
|
|
534
|
+
# longer carries that screenplay's bytes, so a resume can confirm identity
|
|
535
|
+
# but not that the file is unchanged; a screenplay edited between runs is
|
|
536
|
+
# caught by re-parsing below, not by a stored digest.
|
|
537
|
+
resolved, _defect = resolve_ref(body[0].get("source_ref"), sources, "previous_index")
|
|
538
|
+
if resolved is None:
|
|
539
|
+
raise ValueError("previous index does not name its screenplay")
|
|
540
|
+
previous_speakers = previous_index_speakers(records)
|
|
541
|
+
parsed_blocks, _ = _parse_screenplay(source_data, previous_speakers)
|
|
542
|
+
parsed_by_span = {
|
|
543
|
+
(block["byte_start"], block["byte_end"], block["kind"]): block
|
|
544
|
+
for block in parsed_blocks
|
|
545
|
+
}
|
|
546
|
+
previous: list[dict[str, Any]] = []
|
|
547
|
+
seen_ids: set[str] = set()
|
|
548
|
+
for record in records:
|
|
549
|
+
if record.get("record_type") != "block":
|
|
550
|
+
continue
|
|
551
|
+
block_id = record.get("block_id")
|
|
552
|
+
if not isinstance(block_id, str) or not BLOCK_ID_RE.fullmatch(block_id):
|
|
553
|
+
raise ValueError("previous index contains an invalid block_id")
|
|
554
|
+
if block_id in seen_ids:
|
|
555
|
+
raise ValueError("previous index contains duplicate block_id values")
|
|
556
|
+
seen_ids.add(block_id)
|
|
557
|
+
byte_start = record.get("byte_start")
|
|
558
|
+
byte_end = record.get("byte_end")
|
|
559
|
+
kind = record.get("kind")
|
|
560
|
+
if (
|
|
561
|
+
not isinstance(byte_start, int)
|
|
562
|
+
or not isinstance(byte_end, int)
|
|
563
|
+
or isinstance(byte_start, bool)
|
|
564
|
+
or isinstance(byte_end, bool)
|
|
565
|
+
or not 0 <= byte_start < byte_end <= len(source_data)
|
|
566
|
+
or kind not in KIND_CODES
|
|
567
|
+
):
|
|
568
|
+
raise ValueError(f"previous block {block_id} does not resolve against previous source")
|
|
569
|
+
parsed = parsed_by_span.get((byte_start, byte_end, kind))
|
|
570
|
+
if parsed is None:
|
|
571
|
+
raise ValueError(f"previous block {block_id} does not resolve against previous source")
|
|
572
|
+
previous.append({**record, "_text": parsed["_text"], "_order": parsed["_order"]})
|
|
573
|
+
return previous
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def previous_index_speakers(records: list[dict[str, Any]]) -> frozenset[str]:
|
|
577
|
+
"""Recover the dialogue speaker roster an earlier index already resolved."""
|
|
578
|
+
return frozenset(
|
|
579
|
+
speaker.strip()
|
|
580
|
+
for record in records
|
|
581
|
+
if record.get("record_type") == "block"
|
|
582
|
+
and record.get("kind") == "dialogue"
|
|
583
|
+
and isinstance((speaker := record.get("speaker")), str)
|
|
584
|
+
and speaker.strip()
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _fingerprint(block: dict[str, Any]) -> tuple[Any, ...]:
|
|
589
|
+
# Content identity comes from the prose, re-read from the previous screenplay.
|
|
590
|
+
# No digest is stored in the index, so a rebuild without those bytes has
|
|
591
|
+
# nothing to compare and renumbers by position.
|
|
592
|
+
return (
|
|
593
|
+
block["kind"],
|
|
594
|
+
block.get("episode_id"),
|
|
595
|
+
block.get("scene_id"),
|
|
596
|
+
_normalized(block["_text"]),
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _normalized(text: str) -> str:
|
|
601
|
+
return re.sub(r"\s+", "", text)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def _mark_revision_mappings(
|
|
605
|
+
current: list[dict[str, Any]],
|
|
606
|
+
previous: list[dict[str, Any]],
|
|
607
|
+
) -> list[dict[str, Any]]:
|
|
608
|
+
matched_current: set[int] = set()
|
|
609
|
+
matched_previous: set[int] = set()
|
|
610
|
+
|
|
611
|
+
old_headings: dict[tuple[str, str], list[int]] = defaultdict(list)
|
|
612
|
+
new_headings: dict[tuple[str, str], list[int]] = defaultdict(list)
|
|
613
|
+
for index, block in enumerate(previous):
|
|
614
|
+
if block["kind"] == "scene_heading" and block.get("scene_id"):
|
|
615
|
+
old_headings[(block["kind"], block["scene_id"])].append(index)
|
|
616
|
+
for index, block in enumerate(current):
|
|
617
|
+
if block["kind"] == "scene_heading" and block.get("scene_id"):
|
|
618
|
+
new_headings[(block["kind"], block["scene_id"])].append(index)
|
|
619
|
+
for key, new_indices in new_headings.items():
|
|
620
|
+
old_indices = old_headings.get(key, [])
|
|
621
|
+
if len(old_indices) == len(new_indices) == 1:
|
|
622
|
+
old_index, new_index = old_indices[0], new_indices[0]
|
|
623
|
+
current[new_index]["block_id"] = previous[old_index]["block_id"]
|
|
624
|
+
current[new_index]["mapping"] = {
|
|
625
|
+
"status": "reused",
|
|
626
|
+
"reason": "stable_scene_id",
|
|
627
|
+
"previous_block_ids": [previous[old_index]["block_id"]],
|
|
628
|
+
}
|
|
629
|
+
matched_current.add(new_index)
|
|
630
|
+
matched_previous.add(old_index)
|
|
631
|
+
|
|
632
|
+
old_groups: dict[tuple[Any, ...], list[int]] = defaultdict(list)
|
|
633
|
+
new_groups: dict[tuple[Any, ...], list[int]] = defaultdict(list)
|
|
634
|
+
for index, block in enumerate(previous):
|
|
635
|
+
if index not in matched_previous:
|
|
636
|
+
old_groups[_fingerprint(block)].append(index)
|
|
637
|
+
for index, block in enumerate(current):
|
|
638
|
+
if index not in matched_current:
|
|
639
|
+
new_groups[_fingerprint(block)].append(index)
|
|
640
|
+
duplicate_requests: list[dict[str, Any]] = []
|
|
641
|
+
for fingerprint, new_indices in new_groups.items():
|
|
642
|
+
old_indices = old_groups.get(fingerprint, [])
|
|
643
|
+
pairs: list[tuple[int, int]] = []
|
|
644
|
+
if len(old_indices) == len(new_indices) == 1:
|
|
645
|
+
pairs = [(old_indices[0], new_indices[0])]
|
|
646
|
+
elif len(old_indices) == len(new_indices) and old_indices:
|
|
647
|
+
# Reuse repeated identical blocks only when their exact spans did not move.
|
|
648
|
+
# Once a duplicate group moves, occurrence identity is ambiguous.
|
|
649
|
+
by_start = {previous[old]["byte_start"]: old for old in old_indices}
|
|
650
|
+
if all(current[new]["byte_start"] in by_start for new in new_indices):
|
|
651
|
+
pairs = [(by_start[current[new]["byte_start"]], new) for new in new_indices]
|
|
652
|
+
for old_index, new_index in pairs:
|
|
653
|
+
current[new_index]["block_id"] = previous[old_index]["block_id"]
|
|
654
|
+
current[new_index]["mapping"] = {
|
|
655
|
+
"status": "reused",
|
|
656
|
+
"reason": "exact_content",
|
|
657
|
+
"previous_block_ids": [previous[old_index]["block_id"]],
|
|
658
|
+
}
|
|
659
|
+
matched_current.add(new_index)
|
|
660
|
+
matched_previous.add(old_index)
|
|
661
|
+
|
|
662
|
+
if old_indices and new_indices and not pairs:
|
|
663
|
+
duplicate_requests.append(
|
|
664
|
+
{
|
|
665
|
+
"reason": "duplicate_exact_match",
|
|
666
|
+
"previous_indices": old_indices,
|
|
667
|
+
"current_indices": new_indices,
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
matched_current.update(new_indices)
|
|
671
|
+
matched_previous.update(old_indices)
|
|
672
|
+
|
|
673
|
+
return duplicate_requests + _find_split_merge_requests(
|
|
674
|
+
current, previous, matched_current, matched_previous
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
def _find_split_merge_requests(
|
|
679
|
+
current: list[dict[str, Any]],
|
|
680
|
+
previous: list[dict[str, Any]],
|
|
681
|
+
matched_current: set[int],
|
|
682
|
+
matched_previous: set[int],
|
|
683
|
+
) -> list[dict[str, Any]]:
|
|
684
|
+
requests: list[dict[str, Any]] = []
|
|
685
|
+
claimed_new: set[int] = set()
|
|
686
|
+
claimed_old: set[int] = set()
|
|
687
|
+
|
|
688
|
+
def compatible(blocks: list[dict[str, Any]]) -> bool:
|
|
689
|
+
return bool(blocks) and all(
|
|
690
|
+
block["kind"] == blocks[0]["kind"]
|
|
691
|
+
and block.get("scene_id") == blocks[0].get("scene_id")
|
|
692
|
+
and block["kind"] in {"action", "dialogue", "production_tag", "comment"}
|
|
693
|
+
for block in blocks
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
for old_index, old in enumerate(previous):
|
|
697
|
+
if old_index in matched_previous:
|
|
698
|
+
continue
|
|
699
|
+
old_text = _normalized(old["_text"])
|
|
700
|
+
for start in range(len(current)):
|
|
701
|
+
new_indices: list[int] = []
|
|
702
|
+
new_parts: list[str] = []
|
|
703
|
+
for item in range(start, len(current)):
|
|
704
|
+
if item in matched_current or item in claimed_new:
|
|
705
|
+
break
|
|
706
|
+
block = current[item]
|
|
707
|
+
if not compatible([old, block]):
|
|
708
|
+
break
|
|
709
|
+
new_indices.append(item)
|
|
710
|
+
new_parts.append(_normalized(block["_text"]))
|
|
711
|
+
combined = "".join(new_parts)
|
|
712
|
+
if not old_text.startswith(combined):
|
|
713
|
+
break
|
|
714
|
+
if len(new_indices) < 2:
|
|
715
|
+
continue
|
|
716
|
+
if combined != old_text:
|
|
717
|
+
continue
|
|
718
|
+
requests.append(
|
|
719
|
+
{
|
|
720
|
+
"reason": "split",
|
|
721
|
+
"previous_indices": [old_index],
|
|
722
|
+
"current_indices": new_indices,
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
claimed_old.add(old_index)
|
|
726
|
+
claimed_new.update(new_indices)
|
|
727
|
+
break
|
|
728
|
+
if old_index in claimed_old:
|
|
729
|
+
break
|
|
730
|
+
|
|
731
|
+
for new_index, new in enumerate(current):
|
|
732
|
+
if new_index in matched_current or new_index in claimed_new:
|
|
733
|
+
continue
|
|
734
|
+
new_text = _normalized(new["_text"])
|
|
735
|
+
for start in range(len(previous)):
|
|
736
|
+
old_indices: list[int] = []
|
|
737
|
+
old_parts: list[str] = []
|
|
738
|
+
for item in range(start, len(previous)):
|
|
739
|
+
if item in matched_previous or item in claimed_old:
|
|
740
|
+
break
|
|
741
|
+
block = previous[item]
|
|
742
|
+
if not compatible([new, block]):
|
|
743
|
+
break
|
|
744
|
+
old_indices.append(item)
|
|
745
|
+
old_parts.append(_normalized(block["_text"]))
|
|
746
|
+
combined = "".join(old_parts)
|
|
747
|
+
if not new_text.startswith(combined):
|
|
748
|
+
break
|
|
749
|
+
if len(old_indices) < 2:
|
|
750
|
+
continue
|
|
751
|
+
if combined != new_text:
|
|
752
|
+
continue
|
|
753
|
+
requests.append(
|
|
754
|
+
{
|
|
755
|
+
"reason": "merge",
|
|
756
|
+
"previous_indices": old_indices,
|
|
757
|
+
"current_indices": [new_index],
|
|
758
|
+
}
|
|
759
|
+
)
|
|
760
|
+
claimed_old.update(old_indices)
|
|
761
|
+
claimed_new.add(new_index)
|
|
762
|
+
break
|
|
763
|
+
if new_index in claimed_new:
|
|
764
|
+
break
|
|
765
|
+
return requests
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
def _scope(block: dict[str, Any]) -> str:
|
|
769
|
+
if block.get("scene_id"):
|
|
770
|
+
return str(block["scene_id"])
|
|
771
|
+
if block.get("episode_id"):
|
|
772
|
+
return f"{block['episode_id']}-GLOBAL"
|
|
773
|
+
return "GLOBAL"
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
def _assign_new_ids(
|
|
777
|
+
current: list[dict[str, Any]],
|
|
778
|
+
previous: list[dict[str, Any]],
|
|
779
|
+
high_water: dict[str, int] | None = None,
|
|
780
|
+
) -> dict[str, int]:
|
|
781
|
+
"""Give every unmatched block an ID no earlier revision has used.
|
|
782
|
+
|
|
783
|
+
Retirement has to outlive the revision that retired an ID. Seeding the
|
|
784
|
+
counter from the previous index alone makes it one generation deep: once
|
|
785
|
+
every block of one kind in a scene is gone, the counter restarts at zero and
|
|
786
|
+
the next new block is handed a retired ID. A downstream artifact still
|
|
787
|
+
citing it resolves cleanly and silently means something else -- which is
|
|
788
|
+
exactly what resolving cleanly is supposed to rule out.
|
|
789
|
+
|
|
790
|
+
So the highest number ever issued per (scene, kind) rides along in the index
|
|
791
|
+
meta and is carried forward here.
|
|
792
|
+
"""
|
|
793
|
+
|
|
794
|
+
reserved = {str(block["block_id"]) for block in previous}
|
|
795
|
+
counters: dict[tuple[str, str], int] = defaultdict(int)
|
|
796
|
+
for name, number in (high_water or {}).items():
|
|
797
|
+
scope, _, code = name.rpartition("|")
|
|
798
|
+
if scope and isinstance(number, int):
|
|
799
|
+
counters[(scope, code)] = max(counters[(scope, code)], number)
|
|
800
|
+
for block_id in reserved:
|
|
801
|
+
match = BLOCK_ID_RE.fullmatch(block_id)
|
|
802
|
+
assert match
|
|
803
|
+
key = (match.group("scope"), match.group("code"))
|
|
804
|
+
counters[key] = max(counters[key], int(match.group("number")))
|
|
805
|
+
for block in current:
|
|
806
|
+
if "block_id" in block:
|
|
807
|
+
continue
|
|
808
|
+
scope = _scope(block)
|
|
809
|
+
code = KIND_CODES[block["kind"]]
|
|
810
|
+
key = (scope, code)
|
|
811
|
+
while True:
|
|
812
|
+
counters[key] += 1
|
|
813
|
+
candidate = f"BLK-{scope}-{code}{counters[key]:02d}"
|
|
814
|
+
if candidate not in reserved:
|
|
815
|
+
break
|
|
816
|
+
reserved.add(candidate)
|
|
817
|
+
block["block_id"] = candidate
|
|
818
|
+
block["mapping"] = {
|
|
819
|
+
"status": "new",
|
|
820
|
+
"reason": "no_previous_exact_match",
|
|
821
|
+
"previous_block_ids": [],
|
|
822
|
+
}
|
|
823
|
+
# Every ID this revision kept also counts, so a block reused from the
|
|
824
|
+
# previous index cannot be reissued after it later disappears.
|
|
825
|
+
for block in current:
|
|
826
|
+
match = BLOCK_ID_RE.fullmatch(str(block.get("block_id", "")))
|
|
827
|
+
if match:
|
|
828
|
+
key = (match.group("scope"), match.group("code"))
|
|
829
|
+
counters[key] = max(counters[key], int(match.group("number")))
|
|
830
|
+
return {f"{scope}|{code}": number for (scope, code), number in counters.items()}
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def _public_block(block: dict[str, Any], source_ref: dict[str, str]) -> dict[str, Any]:
|
|
834
|
+
omitted = {"_raw", "_text", "_order"}
|
|
835
|
+
return {
|
|
836
|
+
"record_type": "block",
|
|
837
|
+
"schema_version": SCHEMA_VERSION,
|
|
838
|
+
**{key: value for key, value in block.items() if key not in omitted},
|
|
839
|
+
"source_ref": source_ref,
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
def _atomic_jsonl(path: Path, records: list[dict[str, Any]]) -> None:
|
|
844
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
845
|
+
body = "".join(
|
|
846
|
+
json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n"
|
|
847
|
+
for record in records
|
|
848
|
+
).encode("utf-8")
|
|
849
|
+
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
|
|
850
|
+
temporary = Path(temporary_name)
|
|
851
|
+
try:
|
|
852
|
+
with os.fdopen(descriptor, "wb") as handle:
|
|
853
|
+
handle.write(body)
|
|
854
|
+
handle.flush()
|
|
855
|
+
os.fsync(handle.fileno())
|
|
856
|
+
os.replace(temporary, path)
|
|
857
|
+
finally:
|
|
858
|
+
temporary.unlink(missing_ok=True)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def _portable_source_ref(source: Path, source_ref: str | None) -> str:
|
|
862
|
+
value = source_ref or source.name
|
|
863
|
+
candidate = Path(value)
|
|
864
|
+
if candidate.is_absolute() or ".." in candidate.parts or "://" in value:
|
|
865
|
+
raise ValueError("source_ref must be a portable project-relative path")
|
|
866
|
+
return candidate.as_posix()
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def build_index(
|
|
870
|
+
source_path: str | Path,
|
|
871
|
+
output_path: str | Path,
|
|
872
|
+
*,
|
|
873
|
+
previous_index_path: str | Path | None = None,
|
|
874
|
+
previous_source_path: str | Path | None = None,
|
|
875
|
+
source_ref: str | None = None,
|
|
876
|
+
authority: str = "accepted",
|
|
877
|
+
speakers: list[str] | tuple[str, ...] | set[str] | frozenset[str] | None = None,
|
|
878
|
+
no_previous: bool = False,
|
|
879
|
+
) -> dict[str, Any]:
|
|
880
|
+
"""Parse ``source_path`` and atomically publish its derived JSONL index."""
|
|
881
|
+
source = Path(source_path)
|
|
882
|
+
output = Path(output_path)
|
|
883
|
+
if source.resolve() == output.resolve():
|
|
884
|
+
raise ValueError("output path must not be the screenplay source path")
|
|
885
|
+
if (previous_index_path is None) != (previous_source_path is None):
|
|
886
|
+
raise ValueError("--previous-index and --previous-source must be supplied together")
|
|
887
|
+
# Rebuilding over an existing index without naming a previous version
|
|
888
|
+
# renumbers every block from scratch. Surviving text keeps whatever ID its
|
|
889
|
+
# position now yields, so a rewritten block can reclaim the retired ID and
|
|
890
|
+
# every downstream reference to it silently resolves to different content --
|
|
891
|
+
# the exact failure the content-stable path exists to prevent. Refuse, and
|
|
892
|
+
# name the file, rather than produce that index quietly.
|
|
893
|
+
if previous_index_path is None and not no_previous and output.exists():
|
|
894
|
+
raise ValueError(
|
|
895
|
+
f"{output} already holds an index; pass --previous-index {output} "
|
|
896
|
+
f"--previous-source <the screenplay before this revision> to keep block "
|
|
897
|
+
f"IDs stable by content, or --no-previous to renumber from scratch"
|
|
898
|
+
)
|
|
899
|
+
if authority not in {"accepted", "candidate"}:
|
|
900
|
+
raise ValueError("authority must be accepted or candidate")
|
|
901
|
+
raw_speakers = tuple(speakers or ())
|
|
902
|
+
if any(not isinstance(speaker, str) or not speaker.strip() for speaker in raw_speakers):
|
|
903
|
+
raise ValueError("speaker names must be non-empty text")
|
|
904
|
+
speaker_names = frozenset(speaker.strip() for speaker in raw_speakers)
|
|
905
|
+
if any(
|
|
906
|
+
len(speaker) > 40
|
|
907
|
+
or DIALOGUE_RE.fullmatch(f"{speaker}:占位") is None
|
|
908
|
+
for speaker in speaker_names
|
|
909
|
+
):
|
|
910
|
+
raise ValueError("speaker names must match the screenplay dialogue label grammar")
|
|
911
|
+
|
|
912
|
+
source_data = source.read_bytes()
|
|
913
|
+
declared_sources: dict[str, dict[str, str]] = {
|
|
914
|
+
SCREENPLAY_SOURCE_KEY: {
|
|
915
|
+
"owner": "short-drama-write",
|
|
916
|
+
"artifact": _portable_source_ref(source, source_ref),
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
source_artifact_ref: dict[str, str] = {"src": SCREENPLAY_SOURCE_KEY}
|
|
920
|
+
if authority == "candidate":
|
|
921
|
+
source_artifact_ref["authority"] = "candidate"
|
|
922
|
+
previous_records: list[dict[str, Any]] = []
|
|
923
|
+
if previous_index_path is not None:
|
|
924
|
+
previous_records = _read_jsonl(Path(previous_index_path))
|
|
925
|
+
# Union, not replacement: a reviser who adds one new speaker passes only
|
|
926
|
+
# that name, and dropping the rest would turn every other dialogue line
|
|
927
|
+
# back into an ambiguous block and change its ID.
|
|
928
|
+
speaker_names |= previous_index_speakers(previous_records)
|
|
929
|
+
current, source_issues = _parse_screenplay(source_data, speaker_names)
|
|
930
|
+
previous: list[dict[str, Any]] = []
|
|
931
|
+
raw_requests: list[dict[str, Any]] = []
|
|
932
|
+
previous_source_ref: dict[str, str] | None = None
|
|
933
|
+
# Carried from the previous index so a retired ID stays retired even after
|
|
934
|
+
# every block that was using it has gone.
|
|
935
|
+
previous_high_water: dict[str, int] = {}
|
|
936
|
+
for record in previous_records:
|
|
937
|
+
if record.get("record_type") == "screenplay_index_meta":
|
|
938
|
+
declared = record.get("block_id_high_water")
|
|
939
|
+
if isinstance(declared, dict):
|
|
940
|
+
previous_high_water = {
|
|
941
|
+
str(name): number
|
|
942
|
+
for name, number in declared.items()
|
|
943
|
+
if isinstance(number, int) and not isinstance(number, bool)
|
|
944
|
+
}
|
|
945
|
+
break
|
|
946
|
+
if previous_index_path is not None and previous_source_path is not None:
|
|
947
|
+
previous_source = Path(previous_source_path)
|
|
948
|
+
previous_snapshot = {
|
|
949
|
+
"owner": "short-drama-write",
|
|
950
|
+
"artifact": _portable_source_ref(previous_source, source_ref),
|
|
951
|
+
}
|
|
952
|
+
previous_key = SCREENPLAY_SOURCE_KEY
|
|
953
|
+
if previous_snapshot != declared_sources[SCREENPLAY_SOURCE_KEY]:
|
|
954
|
+
previous_key = PREVIOUS_SCREENPLAY_SOURCE_KEY
|
|
955
|
+
declared_sources[previous_key] = previous_snapshot
|
|
956
|
+
previous_source_ref = {"src": previous_key}
|
|
957
|
+
if authority == "candidate":
|
|
958
|
+
previous_source_ref["authority"] = "candidate"
|
|
959
|
+
previous = _load_previous(previous_records, previous_source)
|
|
960
|
+
raw_requests = _mark_revision_mappings(current, previous)
|
|
961
|
+
block_id_high_water = _assign_new_ids(
|
|
962
|
+
current, previous, previous_high_water
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
review_requests: list[dict[str, Any]] = []
|
|
966
|
+
for number, request in enumerate(raw_requests, 1):
|
|
967
|
+
old_ids = [previous[index]["block_id"] for index in request["previous_indices"]]
|
|
968
|
+
new_ids = [current[index]["block_id"] for index in request["current_indices"]]
|
|
969
|
+
for index in request["current_indices"]:
|
|
970
|
+
current[index]["mapping"] = {
|
|
971
|
+
"status": "unresolved",
|
|
972
|
+
"reason": request["reason"],
|
|
973
|
+
"previous_block_ids": old_ids,
|
|
974
|
+
}
|
|
975
|
+
review_requests.append(
|
|
976
|
+
{
|
|
977
|
+
"record_type": "mapping_review_request",
|
|
978
|
+
"schema_version": SCHEMA_VERSION,
|
|
979
|
+
"request_id": f"MAP-REVIEW-{number:03d}",
|
|
980
|
+
"reason": request["reason"],
|
|
981
|
+
"status": "unresolved",
|
|
982
|
+
"previous_block_ids": old_ids,
|
|
983
|
+
"current_block_ids": new_ids,
|
|
984
|
+
"instruction": "创作者或 write owner 必须显式选择重映射;索引器不会猜测。",
|
|
985
|
+
}
|
|
986
|
+
)
|
|
987
|
+
|
|
988
|
+
issue_records = [
|
|
989
|
+
{
|
|
990
|
+
"record_type": "source_issue",
|
|
991
|
+
"schema_version": SCHEMA_VERSION,
|
|
992
|
+
"issue_id": f"SOURCE-ISSUE-{number:03d}",
|
|
993
|
+
**issue,
|
|
994
|
+
"source_ref": source_artifact_ref,
|
|
995
|
+
}
|
|
996
|
+
for number, issue in enumerate(source_issues, 1)
|
|
997
|
+
]
|
|
998
|
+
review_status = "review_required" if issue_records or review_requests else "clean"
|
|
999
|
+
meta = {
|
|
1000
|
+
"record_type": "screenplay_index_meta",
|
|
1001
|
+
"schema_version": SCHEMA_VERSION,
|
|
1002
|
+
"source_ref": source_artifact_ref,
|
|
1003
|
+
"source_byte_length": len(source_data),
|
|
1004
|
+
"newline_style": _newline_style(source_data),
|
|
1005
|
+
"previous_source_ref": previous_source_ref,
|
|
1006
|
+
"block_count": len(current),
|
|
1007
|
+
"source_issue_count": len(issue_records),
|
|
1008
|
+
"mapping_review_count": len(review_requests),
|
|
1009
|
+
"review_status": review_status,
|
|
1010
|
+
# The highest block number ever issued per scene and kind. Carried
|
|
1011
|
+
# forward so a retired ID stays retired after the blocks that were
|
|
1012
|
+
# using it are gone.
|
|
1013
|
+
"block_id_high_water": dict(sorted(block_id_high_water.items())),
|
|
1014
|
+
SOURCES_KEY: declared_sources,
|
|
1015
|
+
}
|
|
1016
|
+
records = [meta]
|
|
1017
|
+
records.extend(_public_block(block, source_artifact_ref) for block in current)
|
|
1018
|
+
records.extend(issue_records)
|
|
1019
|
+
records.extend(review_requests)
|
|
1020
|
+
_atomic_jsonl(output, records)
|
|
1021
|
+
return {
|
|
1022
|
+
"output": str(output),
|
|
1023
|
+
"block_count": len(current),
|
|
1024
|
+
"source_issue_count": len(issue_records),
|
|
1025
|
+
"mapping_review_count": len(review_requests),
|
|
1026
|
+
"review_status": review_status,
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
def _parser() -> argparse.ArgumentParser:
|
|
1031
|
+
parser = argparse.ArgumentParser(
|
|
1032
|
+
description="Build a byte-accurate derived screenplay-index.jsonl without editing screenplay.md."
|
|
1033
|
+
)
|
|
1034
|
+
parser.add_argument("source", type=Path, help="UTF-8 Markdown screenplay source")
|
|
1035
|
+
parser.add_argument("--output", type=Path, help="output JSONL path")
|
|
1036
|
+
parser.add_argument("--previous-index", type=Path)
|
|
1037
|
+
parser.add_argument("--previous-source", type=Path)
|
|
1038
|
+
parser.add_argument(
|
|
1039
|
+
"--no-previous",
|
|
1040
|
+
action="store_true",
|
|
1041
|
+
help="renumber block IDs from scratch, abandoning the IDs downstream artifacts cite",
|
|
1042
|
+
)
|
|
1043
|
+
parser.add_argument(
|
|
1044
|
+
"--source-ref",
|
|
1045
|
+
help="portable project-relative source reference; defaults to the source basename",
|
|
1046
|
+
)
|
|
1047
|
+
parser.add_argument(
|
|
1048
|
+
"--authority",
|
|
1049
|
+
choices=("accepted", "candidate"),
|
|
1050
|
+
default="accepted",
|
|
1051
|
+
help="mark source refs candidate for a provisional normalization preview",
|
|
1052
|
+
)
|
|
1053
|
+
parser.add_argument(
|
|
1054
|
+
"--speaker",
|
|
1055
|
+
action="append",
|
|
1056
|
+
default=[],
|
|
1057
|
+
help="repeat for each agent-resolved dialogue speaker label",
|
|
1058
|
+
)
|
|
1059
|
+
parser.add_argument(
|
|
1060
|
+
"--fail-on-review",
|
|
1061
|
+
action="store_true",
|
|
1062
|
+
help="return exit code 2 after writing when source or mapping review is required",
|
|
1063
|
+
)
|
|
1064
|
+
return parser
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
def main(argv: list[str] | None = None) -> int:
|
|
1068
|
+
args = _parser().parse_args(argv)
|
|
1069
|
+
output = args.output or args.source.with_name("screenplay-index.jsonl")
|
|
1070
|
+
try:
|
|
1071
|
+
summary = build_index(
|
|
1072
|
+
args.source,
|
|
1073
|
+
output,
|
|
1074
|
+
previous_index_path=args.previous_index,
|
|
1075
|
+
previous_source_path=args.previous_source,
|
|
1076
|
+
source_ref=args.source_ref,
|
|
1077
|
+
authority=args.authority,
|
|
1078
|
+
speakers=args.speaker,
|
|
1079
|
+
no_previous=args.no_previous,
|
|
1080
|
+
)
|
|
1081
|
+
except (OSError, UnicodeDecodeError, ValueError) as error:
|
|
1082
|
+
print(json.dumps({"error": str(error)}, ensure_ascii=True), file=sys.stderr)
|
|
1083
|
+
return 1
|
|
1084
|
+
print(json.dumps(summary, ensure_ascii=True, sort_keys=True))
|
|
1085
|
+
if args.fail_on_review and summary["review_status"] != "clean":
|
|
1086
|
+
return 2
|
|
1087
|
+
return 0
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
if __name__ == "__main__":
|
|
1091
|
+
raise SystemExit(main())
|