@lemoncat7/dsh-partner 2.5.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (653) hide show
  1. package/README.md +4 -0
  2. package/docs/THIRD_PARTY_LICENSES.md +13 -0
  3. package/docs/attachment-storage.md +18 -0
  4. package/docs/director-skills.md +24 -0
  5. package/docs/long-task-continuation.md +1 -1
  6. package/docs/releases/2.6.0.md +11 -0
  7. package/docs/releases/2.7.1.md +10 -0
  8. package/lib/agent-runtime.d.ts +3 -1
  9. package/lib/agent-runtime.d.ts.map +1 -1
  10. package/lib/agent-runtime.js +35 -11
  11. package/lib/agent-runtime.js.map +1 -1
  12. package/lib/api/features/attachments-api.d.ts.map +1 -1
  13. package/lib/api/features/attachments-api.js +31 -1
  14. package/lib/api/features/attachments-api.js.map +1 -1
  15. package/lib/api.d.ts +1 -1
  16. package/lib/api.d.ts.map +1 -1
  17. package/lib/api.js +1 -1
  18. package/lib/api.js.map +1 -1
  19. package/lib/attachments/grouped-storage.d.ts +5 -0
  20. package/lib/attachments/grouped-storage.d.ts.map +1 -0
  21. package/lib/attachments/grouped-storage.js +47 -0
  22. package/lib/attachments/grouped-storage.js.map +1 -0
  23. package/lib/attachments/service.d.ts +17 -0
  24. package/lib/attachments/service.d.ts.map +1 -1
  25. package/lib/attachments/service.js +100 -8
  26. package/lib/attachments/service.js.map +1 -1
  27. package/lib/attachments/shared-storage.d.ts +7 -0
  28. package/lib/attachments/shared-storage.d.ts.map +1 -0
  29. package/lib/attachments/shared-storage.js +44 -0
  30. package/lib/attachments/shared-storage.js.map +1 -0
  31. package/lib/attachments/storage-admin.d.ts +6 -0
  32. package/lib/attachments/storage-admin.d.ts.map +1 -0
  33. package/lib/attachments/storage-admin.js +43 -0
  34. package/lib/attachments/storage-admin.js.map +1 -0
  35. package/lib/attachments/storage-types.d.ts +33 -0
  36. package/lib/attachments/storage-types.d.ts.map +1 -0
  37. package/lib/attachments/storage-types.js +2 -0
  38. package/lib/attachments/storage-types.js.map +1 -0
  39. package/lib/attachments/tool.d.ts.map +1 -1
  40. package/lib/attachments/tool.js +16 -5
  41. package/lib/attachments/tool.js.map +1 -1
  42. package/lib/channels/delivery-policy.d.ts.map +1 -1
  43. package/lib/channels/delivery-policy.js +6 -4
  44. package/lib/channels/delivery-policy.js.map +1 -1
  45. package/lib/channels/direct/message-format.js +294 -157
  46. package/lib/channels/direct/message-format.js.map +4 -4
  47. package/lib/channels/manager.d.ts +6 -3
  48. package/lib/channels/manager.d.ts.map +1 -1
  49. package/lib/channels/manager.js +64 -44
  50. package/lib/channels/manager.js.map +1 -1
  51. package/lib/channels/weixin/api.d.ts.map +1 -1
  52. package/lib/channels/weixin/api.js +7 -2
  53. package/lib/channels/weixin/api.js.map +1 -1
  54. package/lib/channels/weixin/errors.d.ts +14 -0
  55. package/lib/channels/weixin/errors.d.ts.map +1 -0
  56. package/lib/channels/weixin/errors.js +29 -0
  57. package/lib/channels/weixin/errors.js.map +1 -0
  58. package/lib/channels/weixin/poll.d.ts +14 -0
  59. package/lib/channels/weixin/poll.d.ts.map +1 -0
  60. package/lib/channels/weixin/poll.js +50 -0
  61. package/lib/channels/weixin/poll.js.map +1 -0
  62. package/lib/client-controller.d.ts.map +1 -1
  63. package/lib/client-controller.js +3 -3
  64. package/lib/client-controller.js.map +1 -1
  65. package/lib/client.d.ts.map +1 -1
  66. package/lib/client.js +983 -575
  67. package/lib/client.js.map +4 -4
  68. package/lib/collaboration/composition.d.ts.map +1 -1
  69. package/lib/collaboration/composition.js +16 -6
  70. package/lib/collaboration/composition.js.map +1 -1
  71. package/lib/collaboration/domain.d.ts +6 -0
  72. package/lib/collaboration/domain.d.ts.map +1 -1
  73. package/lib/collaboration/domain.js.map +1 -1
  74. package/lib/collaboration/service.d.ts.map +1 -1
  75. package/lib/collaboration/service.js +33 -3
  76. package/lib/collaboration/service.js.map +1 -1
  77. package/lib/concern-notification.d.ts.map +1 -1
  78. package/lib/concern-notification.js +2 -1
  79. package/lib/concern-notification.js.map +1 -1
  80. package/lib/concern-tool.js +1 -1
  81. package/lib/concern-tool.js.map +1 -1
  82. package/lib/execution/reply-progress.d.ts.map +1 -1
  83. package/lib/execution/reply-progress.js +4 -2
  84. package/lib/execution/reply-progress.js.map +1 -1
  85. package/lib/execution/service.d.ts +1 -1
  86. package/lib/execution/service.d.ts.map +1 -1
  87. package/lib/execution/service.js +4 -7
  88. package/lib/execution/service.js.map +1 -1
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +3 -1
  91. package/lib/index.js.map +1 -1
  92. package/lib/memory-reflection.js +2 -2
  93. package/lib/memory-reflection.js.map +1 -1
  94. package/lib/message-source.d.ts +25 -0
  95. package/lib/message-source.d.ts.map +1 -0
  96. package/lib/message-source.js +8 -0
  97. package/lib/message-source.js.map +1 -0
  98. package/lib/observation-loop.js +4 -4
  99. package/lib/observation-loop.js.map +1 -1
  100. package/lib/pendant/reader.js +1 -1
  101. package/lib/pendant/reader.js.map +1 -1
  102. package/lib/pendant/renderer.d.ts.map +1 -1
  103. package/lib/pendant/renderer.js +18 -2
  104. package/lib/pendant/renderer.js.map +1 -1
  105. package/lib/pendant/settings-panel.js +1 -1
  106. package/lib/pendant/settings-panel.js.map +1 -1
  107. package/lib/pendant/startup.d.ts +4 -0
  108. package/lib/pendant/startup.d.ts.map +1 -0
  109. package/lib/pendant/startup.js +64 -0
  110. package/lib/pendant/startup.js.map +1 -0
  111. package/lib/pendant/widget.d.ts.map +1 -1
  112. package/lib/pendant/widget.js +2 -1
  113. package/lib/pendant/widget.js.map +1 -1
  114. package/lib/pendant-renderer.js +169 -169
  115. package/lib/persona/service.js +1 -1
  116. package/lib/persona/service.js.map +1 -1
  117. package/lib/requirements/attachments.d.ts +7 -0
  118. package/lib/requirements/attachments.d.ts.map +1 -0
  119. package/lib/requirements/attachments.js +26 -0
  120. package/lib/requirements/attachments.js.map +1 -0
  121. package/lib/requirements/domain.d.ts +1 -1
  122. package/lib/requirements/domain.d.ts.map +1 -1
  123. package/lib/requirements/service.d.ts.map +1 -1
  124. package/lib/requirements/service.js +4 -1
  125. package/lib/requirements/service.js.map +1 -1
  126. package/lib/scheduler/channel-origin.d.ts +7 -0
  127. package/lib/scheduler/channel-origin.d.ts.map +1 -0
  128. package/lib/scheduler/channel-origin.js +27 -0
  129. package/lib/scheduler/channel-origin.js.map +1 -0
  130. package/lib/scheduler/continuations.d.ts +2 -2
  131. package/lib/scheduler/continuations.d.ts.map +1 -1
  132. package/lib/scheduler/continuations.js +28 -8
  133. package/lib/scheduler/continuations.js.map +1 -1
  134. package/lib/scheduler/domain.d.ts +13 -0
  135. package/lib/scheduler/domain.d.ts.map +1 -1
  136. package/lib/scheduler/final-reply.d.ts +13 -0
  137. package/lib/scheduler/final-reply.d.ts.map +1 -0
  138. package/lib/scheduler/final-reply.js +21 -0
  139. package/lib/scheduler/final-reply.js.map +1 -0
  140. package/lib/skills/builtin-resources.d.ts +6 -0
  141. package/lib/skills/builtin-resources.d.ts.map +1 -0
  142. package/lib/skills/builtin-resources.js +47 -0
  143. package/lib/skills/builtin-resources.js.map +1 -0
  144. package/lib/skills/builtin.d.ts +2 -5
  145. package/lib/skills/builtin.d.ts.map +1 -1
  146. package/lib/skills/builtin.js +6 -0
  147. package/lib/skills/builtin.js.map +1 -1
  148. package/lib/skills/director-builtins.d.ts +8 -0
  149. package/lib/skills/director-builtins.d.ts.map +1 -0
  150. package/lib/skills/director-builtins.js +65 -0
  151. package/lib/skills/director-builtins.js.map +1 -0
  152. package/lib/skills/drama-builtin.d.ts +3 -0
  153. package/lib/skills/drama-builtin.d.ts.map +1 -0
  154. package/lib/skills/drama-builtin.js +49 -0
  155. package/lib/skills/drama-builtin.js.map +1 -0
  156. package/lib/skills/long-task-continuation.d.ts +2 -2
  157. package/lib/skills/long-task-continuation.d.ts.map +1 -1
  158. package/lib/skills/long-task-continuation.js +2 -2
  159. package/lib/skills/service.d.ts.map +1 -1
  160. package/lib/skills/service.js +8 -3
  161. package/lib/skills/service.js.map +1 -1
  162. package/lib/skills/viora-canvas-builtin.d.ts +3 -0
  163. package/lib/skills/viora-canvas-builtin.d.ts.map +1 -0
  164. package/lib/skills/viora-canvas-builtin.js +19 -0
  165. package/lib/skills/viora-canvas-builtin.js.map +1 -0
  166. package/lib/storage/migrate-layout.d.ts.map +1 -1
  167. package/lib/storage/migrate-layout.js +13 -4
  168. package/lib/storage/migrate-layout.js.map +1 -1
  169. package/lib/tasks/context.js +1 -0
  170. package/lib/tasks/context.js.map +1 -1
  171. package/lib/tasks/continuation.d.ts.map +1 -1
  172. package/lib/tasks/continuation.js +3 -0
  173. package/lib/tasks/continuation.js.map +1 -1
  174. package/lib/tasks/domain.d.ts +2 -0
  175. package/lib/tasks/domain.d.ts.map +1 -1
  176. package/lib/tasks/service.d.ts +2 -1
  177. package/lib/tasks/service.d.ts.map +1 -1
  178. package/lib/tasks/service.js +15 -1
  179. package/lib/tasks/service.js.map +1 -1
  180. package/lib/ui/attachment-storage.d.ts +2 -0
  181. package/lib/ui/attachment-storage.d.ts.map +1 -0
  182. package/lib/ui/attachment-storage.js +59 -0
  183. package/lib/ui/attachment-storage.js.map +1 -0
  184. package/lib/ui/capability-editor.js +1 -1
  185. package/lib/ui/capability-editor.js.map +1 -1
  186. package/lib/ui/companion-access-panel.js +1 -1
  187. package/lib/ui/companion-access-panel.js.map +1 -1
  188. package/lib/ui/companion-create.js +1 -1
  189. package/lib/ui/companion-create.js.map +1 -1
  190. package/lib/ui/companion-picker.js +1 -1
  191. package/lib/ui/companion-picker.js.map +1 -1
  192. package/lib/ui/identity-editor.js +1 -1
  193. package/lib/ui/identity-editor.js.map +1 -1
  194. package/lib/ui/mcp-capabilities.js +1 -1
  195. package/lib/ui/mcp-capabilities.js.map +1 -1
  196. package/lib/ui/mcp-panel.js +1 -1
  197. package/lib/ui/mcp-panel.js.map +1 -1
  198. package/lib/ui/memory/concern-board.js +2 -2
  199. package/lib/ui/memory/concern-board.js.map +1 -1
  200. package/lib/ui/memory/concern-deletion.d.ts +1 -1
  201. package/lib/ui/memory/concern-deletion.d.ts.map +1 -1
  202. package/lib/ui/memory/concern-deletion.js +9 -4
  203. package/lib/ui/memory/concern-deletion.js.map +1 -1
  204. package/lib/ui/memory/concern-panel.js +1 -1
  205. package/lib/ui/memory/concern-panel.js.map +1 -1
  206. package/lib/ui/memory/memory-library.js +1 -1
  207. package/lib/ui/memory/memory-library.js.map +1 -1
  208. package/lib/ui/memory/memory-panel.js +1 -1
  209. package/lib/ui/memory/memory-panel.js.map +1 -1
  210. package/lib/ui/partner-components.js +1 -1
  211. package/lib/ui/partner-components.js.map +1 -1
  212. package/lib/ui/requirement-board.js +1 -1
  213. package/lib/ui/requirement-board.js.map +1 -1
  214. package/lib/ui/requirement-dialog.js +1 -1
  215. package/lib/ui/requirement-dialog.js.map +1 -1
  216. package/lib/ui/requirement-tasks-panel.js +1 -1
  217. package/lib/ui/requirement-tasks-panel.js.map +1 -1
  218. package/lib/ui/schedule-panel.js +1 -1
  219. package/lib/ui/schedule-panel.js.map +1 -1
  220. package/lib/ui/skills-panel.js +1 -1
  221. package/lib/ui/skills-panel.js.map +1 -1
  222. package/lib/ui/storage-settings.d.ts.map +1 -1
  223. package/lib/ui/storage-settings.js +2 -1
  224. package/lib/ui/storage-settings.js.map +1 -1
  225. package/lib/ui/task-board-stage.js +1 -1
  226. package/lib/ui/task-board-stage.js.map +1 -1
  227. package/lib/ui/workspace-components.js +1 -1
  228. package/lib/ui/workspace-components.js.map +1 -1
  229. package/lib/ui/workspace-navigation.d.ts +16 -16
  230. package/lib/ui/workspace-navigation.js +1 -1
  231. package/lib/ui/workspace-navigation.js.map +1 -1
  232. package/package.json +53 -41
  233. package/resources/skills/cinematic-director/manifest.json +205 -0
  234. package/resources/skills/cinematic-director/upstream/CHANGELOG.md +213 -0
  235. package/resources/skills/cinematic-director/upstream/CONTRIBUTING.md +316 -0
  236. package/resources/skills/cinematic-director/upstream/LICENSE +21 -0
  237. package/resources/skills/cinematic-director/upstream/README.md +507 -0
  238. package/resources/skills/cinematic-director/upstream/SKILL.md +420 -0
  239. package/resources/skills/cinematic-director/upstream/assets/beat-sheet-template.md +97 -0
  240. package/resources/skills/cinematic-director/upstream/assets/director-book-template.md +254 -0
  241. package/resources/skills/cinematic-director/upstream/assets/edit-timeline-template.md +147 -0
  242. package/resources/skills/cinematic-director/upstream/assets/keyframe-prompt-template.md +334 -0
  243. package/resources/skills/cinematic-director/upstream/assets/qc-checklist.md +153 -0
  244. package/resources/skills/cinematic-director/upstream/assets/shot-plan-template.md +95 -0
  245. package/resources/skills/cinematic-director/upstream/assets/sound-plan-template.md +181 -0
  246. package/resources/skills/cinematic-director/upstream/assets/video-prompt-template.md +305 -0
  247. package/resources/skills/cinematic-director/upstream/evals/run.md +69 -0
  248. package/resources/skills/cinematic-director/upstream/references/ai-video-tool-adapters.md +527 -0
  249. package/resources/skills/cinematic-director/upstream/references/blocking-and-staging.md +424 -0
  250. package/resources/skills/cinematic-director/upstream/references/cinematic-language.md +595 -0
  251. package/resources/skills/cinematic-director/upstream/references/continuity-bible.md +410 -0
  252. package/resources/skills/cinematic-director/upstream/references/director_styles/01_spielberg.md +192 -0
  253. package/resources/skills/cinematic-director/upstream/references/director_styles/02_hitchcock.md +193 -0
  254. package/resources/skills/cinematic-director/upstream/references/director_styles/03_kubrick.md +196 -0
  255. package/resources/skills/cinematic-director/upstream/references/director_styles/04_kurosawa.md +196 -0
  256. package/resources/skills/cinematic-director/upstream/references/director_styles/05_scorsese.md +195 -0
  257. package/resources/skills/cinematic-director/upstream/references/director_styles/06_fellini.md +197 -0
  258. package/resources/skills/cinematic-director/upstream/references/director_styles/07_bergman.md +197 -0
  259. package/resources/skills/cinematic-director/upstream/references/director_styles/08_tarkovsky.md +184 -0
  260. package/resources/skills/cinematic-director/upstream/references/director_styles/09_wong_kar_wai.md +189 -0
  261. package/resources/skills/cinematic-director/upstream/references/director_styles/10_nolan.md +185 -0
  262. package/resources/skills/cinematic-director/upstream/references/director_styles/11_villeneuve.md +188 -0
  263. package/resources/skills/cinematic-director/upstream/references/director_styles/12_fincher.md +185 -0
  264. package/resources/skills/cinematic-director/upstream/references/director_styles/13_refn.md +186 -0
  265. package/resources/skills/cinematic-director/upstream/references/director_styles/14_bi_gan.md +188 -0
  266. package/resources/skills/cinematic-director/upstream/references/director_styles/15_zhang_yimou.md +192 -0
  267. package/resources/skills/cinematic-director/upstream/references/director_styles/16_hou_hsiao_hsien.md +192 -0
  268. package/resources/skills/cinematic-director/upstream/references/director_styles/17_park_chan_wook.md +192 -0
  269. package/resources/skills/cinematic-director/upstream/references/director_styles/18_malick.md +191 -0
  270. package/resources/skills/cinematic-director/upstream/references/director_styles/19_michael_mann.md +192 -0
  271. package/resources/skills/cinematic-director/upstream/references/director_styles/20_coen_brothers.md +194 -0
  272. package/resources/skills/cinematic-director/upstream/references/director_styles/README.md +89 -0
  273. package/resources/skills/cinematic-director/upstream/references/director_styles/example_comparisons.md +140 -0
  274. package/resources/skills/cinematic-director/upstream/references/editing-and-assembly.md +263 -0
  275. package/resources/skills/cinematic-director/upstream/references/failure-modes.md +594 -0
  276. package/resources/skills/cinematic-director/upstream/references/genre-playbooks.md +407 -0
  277. package/resources/skills/cinematic-director/upstream/references/image-model-adapters.md +290 -0
  278. package/resources/skills/cinematic-director/upstream/references/lighting-and-color.md +349 -0
  279. package/resources/skills/cinematic-director/upstream/references/product-and-macro.md +207 -0
  280. package/resources/skills/cinematic-director/upstream/references/production-workflow.md +238 -0
  281. package/resources/skills/cinematic-director/upstream/references/prompt-lexicon.md +602 -0
  282. package/resources/skills/cinematic-director/upstream/references/sound-and-dialogue.md +364 -0
  283. package/resources/skills/director-skills/manifest.json +541 -0
  284. package/resources/skills/director-skills/upstream/CHANGELOG.md +32 -0
  285. package/resources/skills/director-skills/upstream/LICENSE +21 -0
  286. package/resources/skills/director-skills/upstream/MANIFEST.md +366 -0
  287. package/resources/skills/director-skills/upstream/README.md +251 -0
  288. package/resources/skills/director-skills/upstream/RESEARCH_PROVENANCE.md +37 -0
  289. package/resources/skills/director-skills/upstream/SKILL.md +114 -0
  290. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/SKILL.md +81 -0
  291. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/checklists/continuity_checklist.md +8 -0
  292. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/examples/character_continuity_update.md +9 -0
  293. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/examples/location_continuity_update.md +9 -0
  294. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/continuity_system.md +21 -0
  295. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/golden_image_workflow.md +29 -0
  296. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/identity_anchors.md +14 -0
  297. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/visual_bible_system.md +15 -0
  298. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/character_profile.md +15 -0
  299. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/continuity_checklist.md +11 -0
  300. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/costume_profile.md +12 -0
  301. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/location_profile.md +14 -0
  302. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/prop_profile.md +13 -0
  303. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/visual_style_bible.md +15 -0
  304. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/tests/continuity_tests.md +6 -0
  305. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/SKILL.md +80 -0
  306. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/checklists/image_prompt_quality_checklist.md +10 -0
  307. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/examples/shot_list_to_image_prompts.md +4 -0
  308. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/image_prompt_components.md +22 -0
  309. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/lighting_and_photorealism.md +19 -0
  310. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/negative_prompt_patterns.md +18 -0
  311. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/prompt_anatomy.md +18 -0
  312. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/character_reference_sheet_prompt.md +5 -0
  313. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/cinematic_still_prompt.md +3 -0
  314. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/keyframe_prompt.md +6 -0
  315. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/location_reference_prompt.md +3 -0
  316. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/prop_prompt.md +3 -0
  317. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/tests/image_prompt_completeness_tests.md +5 -0
  318. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/SKILL.md +84 -0
  319. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/checklists/video_prompt_quality_checklist.md +9 -0
  320. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/examples/image_prompt_to_video_prompt.md +3 -0
  321. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/ai_film_production_pipeline.md +21 -0
  322. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/audio_motion_timing.md +16 -0
  323. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/camera_lens_movement.md +26 -0
  324. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/negative_prompt_patterns.md +18 -0
  325. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/prompt_anatomy.md +18 -0
  326. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/video_prompt_components.md +25 -0
  327. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/image_to_video_prompt.md +5 -0
  328. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/text_to_video_prompt.md +5 -0
  329. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/video_beat_prompt.md +9 -0
  330. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/tests/video_prompt_completeness_tests.md +5 -0
  331. package/resources/skills/director-skills/upstream/skills/model-adaptation/SKILL.md +82 -0
  332. package/resources/skills/director-skills/upstream/skills/model-adaptation/checklists/model_export_checklist.md +9 -0
  333. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/failed_model_transfer_diagnosis.md +5 -0
  334. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/image_prompt_translation.md +10 -0
  335. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/same_scene_across_models.md +9 -0
  336. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/universal_prompt_to_model_specific_exports.md +14 -0
  337. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/video_prompt_translation.md +9 -0
  338. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/failure_modes_by_model.md +13 -0
  339. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/image_model_profiles.md +226 -0
  340. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/model_capability_matrix.md +24 -0
  341. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/model_update_protocol.md +10 -0
  342. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/parameter_compatibility.md +20 -0
  343. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/prompt_translation_rules.md +18 -0
  344. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/video_model_profiles.md +251 -0
  345. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/model_comparison_table.md +4 -0
  346. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/model_specific_prompt_export.md +21 -0
  347. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/multi_model_export_package.md +18 -0
  348. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/prompt_translation_report.md +13 -0
  349. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/universal_prompt_brief.md +25 -0
  350. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/model_routing_tests.md +8 -0
  351. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/prompt_translation_tests.md +7 -0
  352. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/research_provenance_tests.md +6 -0
  353. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/unknown_capability_tests.md +7 -0
  354. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/SKILL.md +82 -0
  355. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/checklists/diagnostic_checklist.md +8 -0
  356. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/checklists/release_quality_checklist.md +9 -0
  357. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/examples/failed_output_to_revised_prompt.md +10 -0
  358. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/examples/final_prompt_package_export.md +10 -0
  359. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/failure_modes.md +13 -0
  360. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/iteration_diagnostics.md +17 -0
  361. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/revision_loops.md +17 -0
  362. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/story_voice_release_qc.md +30 -0
  363. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/failed_output_intake.md +13 -0
  364. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/final_production_package.md +11 -0
  365. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/prompt_revision_report.md +17 -0
  366. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/tests/prompt_quality_checklist_tests.md +6 -0
  367. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/SKILL.md +81 -0
  368. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/checklists/scene_quality_checklist.md +8 -0
  369. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/examples/scene_to_screenplay_excerpt.md +18 -0
  370. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/dialogue_and_subtext.md +15 -0
  371. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/fountain_workflow.md +29 -0
  372. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/scene_construction.md +20 -0
  373. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/screenplay_format.md +18 -0
  374. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/beat_sheet.md +6 -0
  375. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/dialogue_pass.md +9 -0
  376. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/fountain_scene.md +25 -0
  377. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/screenplay_scene.md +19 -0
  378. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/tests/screenplay_scene_tests.md +6 -0
  379. package/resources/skills/director-skills/upstream/skills/short-film-development/SKILL.md +83 -0
  380. package/resources/skills/director-skills/upstream/skills/short-film-development/checklists/concept_quality_checklist.md +8 -0
  381. package/resources/skills/director-skills/upstream/skills/short-film-development/examples/idea_to_logline.md +13 -0
  382. package/resources/skills/director-skills/upstream/skills/short-film-development/examples/logline_to_outline.md +10 -0
  383. package/resources/skills/director-skills/upstream/skills/short-film-development/references/ai_storytelling_workflow.md +46 -0
  384. package/resources/skills/director-skills/upstream/skills/short-film-development/references/continuity_handoff.md +8 -0
  385. package/resources/skills/director-skills/upstream/skills/short-film-development/references/screenplay_structure.md +19 -0
  386. package/resources/skills/director-skills/upstream/skills/short-film-development/references/story_development_workflow.md +23 -0
  387. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/beat_sheet.md +7 -0
  388. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/idea_intake.md +16 -0
  389. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/logline.md +10 -0
  390. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/short_film_treatment.md +23 -0
  391. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/short_story_plan.md +22 -0
  392. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/story_premise.md +19 -0
  393. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/story_revision_audit.md +22 -0
  394. package/resources/skills/director-skills/upstream/skills/short-film-development/tests/output_format_tests.md +3 -0
  395. package/resources/skills/director-skills/upstream/skills/short-film-development/tests/routing_tests.md +7 -0
  396. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/SKILL.md +77 -0
  397. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/checklists/shotlist_quality_checklist.md +8 -0
  398. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/examples/screenplay_scene_to_shot_list.md +8 -0
  399. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/camera_plan.md +11 -0
  400. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/production_breakdown.md +11 -0
  401. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/shotlist_schema.md +21 -0
  402. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/asset_list.md +7 -0
  403. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/camera_plan.md +10 -0
  404. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/scene_breakdown.md +16 -0
  405. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/shot_list.md +5 -0
  406. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/tests/screenplay_to_shotlist_tests.md +5 -0
  407. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/SKILL.md +77 -0
  408. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/checklists/style_quality_checklist.md +8 -0
  409. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/examples/genre_to_visual_direction.md +15 -0
  410. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/camera_lens_movement.md +26 -0
  411. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/cinematic_language.md +32 -0
  412. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/genre_style_tone.md +17 -0
  413. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/lighting_and_photorealism.md +19 -0
  414. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/templates/visual_style_bible.md +33 -0
  415. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/tests/style_translation_tests.md +5 -0
  416. package/resources/skills/director-skills/upstream/tests/master_router_tests.md +80 -0
  417. package/resources/skills/drama-production/manifest.json +933 -0
  418. package/resources/skills/drama-production/upstream/LICENSE +21 -0
  419. package/resources/skills/drama-production/upstream/skills/short-drama/SKILL.md +153 -0
  420. package/resources/skills/drama-production/upstream/skills/short-drama/agents/openai.yaml +4 -0
  421. package/resources/skills/drama-production/upstream/skills/short-drama/assets/creator-decision.example.jsonl +4 -0
  422. package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/app.js +1263 -0
  423. package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/index.html +114 -0
  424. package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/styles.css +297 -0
  425. package/resources/skills/drama-production/upstream/skills/short-drama/assets/production-observation.example.jsonl +2 -0
  426. package/resources/skills/drama-production/upstream/skills/short-drama/assets/project-template/short-drama.json +36 -0
  427. package/resources/skills/drama-production/upstream/skills/short-drama/assets/reference-observation.example.jsonl +2 -0
  428. package/resources/skills/drama-production/upstream/skills/short-drama/references/audience-reveal.md +21 -0
  429. package/resources/skills/drama-production/upstream/skills/short-drama/references/contract-and-ownership.md +45 -0
  430. package/resources/skills/drama-production/upstream/skills/short-drama/references/creator-documents.md +230 -0
  431. package/resources/skills/drama-production/upstream/skills/short-drama/references/creator-workflow.md +98 -0
  432. 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
  433. 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
  434. 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
  435. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//345/256/236/346/213/215.md +75 -0
  436. 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
  437. 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
  438. package/resources/skills/drama-production/upstream/skills/short-drama/references/knowhow-index.md +85 -0
  439. package/resources/skills/drama-production/upstream/skills/short-drama/references/look-development.md +51 -0
  440. package/resources/skills/drama-production/upstream/skills/short-drama/references/pickup-and-alternate.md +24 -0
  441. package/resources/skills/drama-production/upstream/skills/short-drama/references/production-form-profiles.md +126 -0
  442. package/resources/skills/drama-production/upstream/skills/short-drama/references/reference-roles.md +53 -0
  443. package/resources/skills/drama-production/upstream/skills/short-drama/references/runtime-preflight.md +24 -0
  444. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/creator_markdown_check.py +1413 -0
  445. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/dashboard_server.py +1798 -0
  446. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/project_tool.py +2454 -0
  447. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/selftest.py +287 -0
  448. package/resources/skills/drama-production/upstream/skills/short-drama-assets/SKILL.md +76 -0
  449. package/resources/skills/drama-production/upstream/skills/short-drama-assets/agents/openai.yaml +4 -0
  450. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/character-look.example.jsonl +3 -0
  451. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/continuity.example.jsonl +4 -0
  452. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/decisions.example.jsonl +5 -0
  453. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/location-view.example.jsonl +3 -0
  454. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/occurrences.example.jsonl +5 -0
  455. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/prop-state.example.jsonl +3 -0
  456. package/resources/skills/drama-production/upstream/skills/short-drama-assets/examples/minimal/characters.jsonl +2 -0
  457. package/resources/skills/drama-production/upstream/skills/short-drama-assets/examples/minimal/looks.jsonl +2 -0
  458. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/asset-review-checklist.md +82 -0
  459. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/character-and-look.md +190 -0
  460. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/continuity-delta.md +120 -0
  461. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/continuity-lock.md +122 -0
  462. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/identity-vs-variant.md +140 -0
  463. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/location-and-view.md +84 -0
  464. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/occurrence-extraction.md +108 -0
  465. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/prop-and-state.md +126 -0
  466. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/stage-contract.md +43 -0
  467. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/voice-direction.md +179 -0
  468. package/resources/skills/drama-production/upstream/skills/short-drama-assets/scripts/asset_check.py +267 -0
  469. package/resources/skills/drama-production/upstream/skills/short-drama-assets/scripts/selftest.py +89 -0
  470. package/resources/skills/drama-production/upstream/skills/short-drama-develop/SKILL.md +157 -0
  471. package/resources/skills/drama-production/upstream/skills/short-drama-develop/agents/openai.yaml +4 -0
  472. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -0
  473. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/creative-brief.md +120 -0
  474. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/episode-map.jsonl +1 -0
  475. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/story-engine.md +181 -0
  476. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/adaptation-craft.md +219 -0
  477. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/creative-reference-intake.md +57 -0
  478. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/director-brief-craft.md +113 -0
  479. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/episode-design.md +509 -0
  480. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-and-hook-playbook.md +240 -0
  481. 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
  482. 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
  483. 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
  484. 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
  485. 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
  486. 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
  487. 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
  488. 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
  489. 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
  490. 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
  491. 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
  492. 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
  493. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards.md +92 -0
  494. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/mechanism-loop.md +282 -0
  495. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/multi-episode-intake.md +128 -0
  496. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/premise-devices.md +211 -0
  497. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/reveal-reversal-payoff.md +99 -0
  498. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/serial-character-and-memory.md +293 -0
  499. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/stage-contract.md +88 -0
  500. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/story-craft.md +359 -0
  501. package/resources/skills/drama-production/upstream/skills/short-drama-develop/scripts/episode_intake.py +699 -0
  502. package/resources/skills/drama-production/upstream/skills/short-drama-develop/scripts/selftest.py +47 -0
  503. package/resources/skills/drama-production/upstream/skills/short-drama-edit/SKILL.md +146 -0
  504. package/resources/skills/drama-production/upstream/skills/short-drama-edit/agents/openai.yaml +4 -0
  505. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/README.md +60 -0
  506. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/package.json +15 -0
  507. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  508. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  509. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  510. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  511. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  512. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  513. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  514. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  515. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/cut-craft.md +38 -0
  516. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/delivery-verify.md +29 -0
  517. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/generated-footage.md +54 -0
  518. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/sound-and-subtitles.md +60 -0
  519. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/stage-contract.md +51 -0
  520. package/resources/skills/drama-production/upstream/skills/short-drama-edit/scripts/edit_tool.py +1156 -0
  521. package/resources/skills/drama-production/upstream/skills/short-drama-edit/scripts/selftest.py +520 -0
  522. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/SKILL.md +73 -0
  523. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/agents/openai.yaml +4 -0
  524. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/image-prompt-spec.jsonl.md +187 -0
  525. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/image-prompts.md +42 -0
  526. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/lookdev-frame-spec.jsonl.md +128 -0
  527. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/lookdev-prompts.md +34 -0
  528. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/examples/minimal-image-prompt-specs.jsonl +2 -0
  529. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/character-and-look.md +153 -0
  530. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/common-recipe.md +241 -0
  531. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/edit-and-revision.md +110 -0
  532. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/location-plate.md +151 -0
  533. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/look-and-state-variant.md +97 -0
  534. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/lookdev-frame.md +34 -0
  535. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/production-sheet-recipes.md +177 -0
  536. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/prop-plate.md +137 -0
  537. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/review-and-fixtures.md +92 -0
  538. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/stage-contract.md +32 -0
  539. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/scripts/image_prompt_check.py +443 -0
  540. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/scripts/selftest.py +95 -0
  541. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/SKILL.md +245 -0
  542. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/agents/openai.yaml +4 -0
  543. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +3 -0
  544. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/adaptation-triage.md +116 -0
  545. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/adaptation-value.md +124 -0
  546. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +155 -0
  547. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/chapter-extraction.md +144 -0
  548. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/stage-contract.md +67 -0
  549. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/scripts/novel_index.py +651 -0
  550. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/scripts/selftest.py +51 -0
  551. package/resources/skills/drama-production/upstream/skills/short-drama-produce/SKILL.md +183 -0
  552. package/resources/skills/drama-production/upstream/skills/short-drama-produce/agents/openai.yaml +4 -0
  553. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/adapter-contract.md +219 -0
  554. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/gpt-image-2.md +24 -0
  555. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-h3-video.md +86 -0
  556. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-music.md +25 -0
  557. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  558. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/seedance.md +64 -0
  559. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/fixture_adapter.py +77 -0
  560. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/production_tool.py +2205 -0
  561. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/provider_adapters.py +1747 -0
  562. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/selftest.py +135 -0
  563. package/resources/skills/drama-production/upstream/skills/short-drama-review/SKILL.md +118 -0
  564. package/resources/skills/drama-production/upstream/skills/short-drama-review/agents/openai.yaml +4 -0
  565. package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/finding-template.jsonl +2 -0
  566. package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/supersession-decision.example.json +42 -0
  567. package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/verdict-template.json +32 -0
  568. package/resources/skills/drama-production/upstream/skills/short-drama-review/examples/minimal-findings.jsonl +2 -0
  569. package/resources/skills/drama-production/upstream/skills/short-drama-review/examples/minimal-verdict.json +34 -0
  570. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/anti-template-repair.md +109 -0
  571. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/production-quality-gates.md +276 -0
  572. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/project-calibration.md +83 -0
  573. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/review-method.md +81 -0
  574. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-assets-prompts.md +111 -0
  575. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-source-analysis.md +108 -0
  576. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-story-script.md +264 -0
  577. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-visual-motion.md +184 -0
  578. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/stage-contract.md +28 -0
  579. package/resources/skills/drama-production/upstream/skills/short-drama-review/scripts/review_check.py +325 -0
  580. package/resources/skills/drama-production/upstream/skills/short-drama-review/scripts/selftest.py +87 -0
  581. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/SKILL.md +175 -0
  582. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/agents/openai.yaml +4 -0
  583. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/coverage-audition.example.jsonl +2 -0
  584. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/coverage-template.json +49 -0
  585. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/keyframe-prompts.md +36 -0
  586. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/keyframe-template.jsonl +2 -0
  587. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/revision-lineage.fragment.json +12 -0
  588. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/scene-visual-plan.example.jsonl +2 -0
  589. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/shot-template.jsonl +2 -0
  590. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/blocking-playbooks.md +216 -0
  591. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/comic-keyframe-lexicon.md +156 -0
  592. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/coverage-audition.md +34 -0
  593. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/keyframe-craft.md +174 -0
  594. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  595. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/production-shot-grammar.md +279 -0
  596. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/review-and-fixtures.md +36 -0
  597. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/scene-visual-plan.md +30 -0
  598. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +155 -0
  599. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/shot-craft.md +266 -0
  600. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/shot-revision-identity.md +25 -0
  601. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/stage-contract.md +57 -0
  602. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/scripts/selftest.py +191 -0
  603. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/scripts/storyboard_check.py +824 -0
  604. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/SKILL.md +154 -0
  605. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/agents/openai.yaml +4 -0
  606. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/coverage-scope.fragment.json +21 -0
  607. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/delivery-container.jsonl.md +101 -0
  608. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +217 -0
  609. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/motion-terminal.example.jsonl +2 -0
  610. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/music-spec.jsonl.md +31 -0
  611. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/performance.fragment.json +38 -0
  612. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/video-prompts.md +67 -0
  613. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/examples/minimal-music-specs.jsonl +2 -0
  614. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/camera-audio-continuity.md +259 -0
  615. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/delivery-profile.md +273 -0
  616. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/generability.md +93 -0
  617. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/minimax-h3.md +253 -0
  618. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/motion-recipe.md +315 -0
  619. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/performance-action-timing.md +209 -0
  620. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/production-prompt-grammar.md +304 -0
  621. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/review-and-fixtures.md +139 -0
  622. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/seedance-2.0.md +50 -0
  623. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/seedance-2.5.md +122 -0
  624. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/stage-contract.md +60 -0
  625. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/target-model-profile.md +116 -0
  626. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/container_check.py +329 -0
  627. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/motion_timing_check.py +450 -0
  628. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/music_spec_check.py +327 -0
  629. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/selftest.py +82 -0
  630. package/resources/skills/drama-production/upstream/skills/short-drama-write/SKILL.md +99 -0
  631. package/resources/skills/drama-production/upstream/skills/short-drama-write/agents/openai.yaml +4 -0
  632. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/beats.jsonl +2 -0
  633. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/episode-card-standalone.json +120 -0
  634. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/episode-card.json +55 -0
  635. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/screenplay.md +36 -0
  636. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/voice-record-sheet.jsonl.md +89 -0
  637. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/dialogue-craft.md +526 -0
  638. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/production-format-dialect.md +273 -0
  639. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/scene-handoff-capsule.md +25 -0
  640. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/scene-sound-dramaturgy.md +43 -0
  641. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/screenplay-format.md +266 -0
  642. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/script-craft.md +745 -0
  643. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/stage-contract.md +36 -0
  644. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/substitutable-realization.md +107 -0
  645. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/duration_estimate.py +309 -0
  646. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/screenplay_index.py +1091 -0
  647. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/selftest.py +229 -0
  648. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/voice_sheet_check.py +416 -0
  649. package/resources/skills/viora-canvas-production/manifest.json +23 -0
  650. package/resources/skills/viora-canvas-production/references/canvas-contract.md +50 -0
  651. package/resources/skills/viora-canvas-production/references/creative-production.md +50 -0
  652. package/resources/skills/viora-canvas-production/references/generation-delivery.md +36 -0
  653. 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())