@lingjingai/scriptctl 0.34.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. package/README.md +30 -5
  2. package/changes/0.35.0.md +22 -0
  3. package/changes/0.36.0.md +22 -0
  4. package/changes/unreleased.md +3 -5
  5. package/dist/cli.d.ts +2 -1
  6. package/dist/cli.js +56 -128
  7. package/dist/cli.js.map +1 -1
  8. package/dist/common.d.ts +0 -16
  9. package/dist/common.js +2 -342
  10. package/dist/common.js.map +1 -1
  11. package/dist/domain/ingest/apply-plan.d.ts +23 -0
  12. package/dist/domain/ingest/apply-plan.js +210 -0
  13. package/dist/domain/ingest/apply-plan.js.map +1 -0
  14. package/dist/domain/ingest/assembler.d.ts +57 -0
  15. package/dist/domain/ingest/assembler.js +181 -0
  16. package/dist/domain/ingest/assembler.js.map +1 -0
  17. package/dist/domain/ingest/chunker.d.ts +9 -0
  18. package/dist/domain/ingest/chunker.js +38 -0
  19. package/dist/domain/ingest/chunker.js.map +1 -0
  20. package/dist/domain/ingest/evidence.d.ts +5 -0
  21. package/dist/domain/ingest/evidence.js +66 -0
  22. package/dist/domain/ingest/evidence.js.map +1 -0
  23. package/dist/domain/ingest/govern.d.ts +3 -0
  24. package/dist/domain/ingest/govern.js +127 -0
  25. package/dist/domain/ingest/govern.js.map +1 -0
  26. package/dist/domain/ingest/jsonl-parser.d.ts +6 -0
  27. package/dist/domain/ingest/jsonl-parser.js +187 -0
  28. package/dist/domain/ingest/jsonl-parser.js.map +1 -0
  29. package/dist/domain/ingest/markdown-parser.d.ts +6 -0
  30. package/dist/domain/ingest/markdown-parser.js +186 -0
  31. package/dist/domain/ingest/markdown-parser.js.map +1 -0
  32. package/dist/domain/ingest/records.d.ts +71 -0
  33. package/dist/domain/ingest/records.js +9 -0
  34. package/dist/domain/ingest/records.js.map +1 -0
  35. package/dist/domain/ingest/renumber.d.ts +14 -0
  36. package/dist/domain/ingest/renumber.js +130 -0
  37. package/dist/domain/ingest/renumber.js.map +1 -0
  38. package/dist/domain/ingest/script-v3.d.ts +94 -0
  39. package/dist/domain/ingest/script-v3.js +7 -0
  40. package/dist/domain/ingest/script-v3.js.map +1 -0
  41. package/dist/domain/ingest/state-reconcile.d.ts +11 -0
  42. package/dist/domain/ingest/state-reconcile.js +90 -0
  43. package/dist/domain/ingest/state-reconcile.js.map +1 -0
  44. package/dist/domain/ingest/validation.d.ts +11 -0
  45. package/dist/domain/ingest/validation.js +168 -0
  46. package/dist/domain/ingest/validation.js.map +1 -0
  47. package/dist/domain/ingest/video-aggregate.d.ts +23 -0
  48. package/dist/domain/ingest/video-aggregate.js +89 -0
  49. package/dist/domain/ingest/video-aggregate.js.map +1 -0
  50. package/dist/domain/ingest/video-apply.d.ts +37 -0
  51. package/dist/domain/ingest/video-apply.js +236 -0
  52. package/dist/domain/ingest/video-apply.js.map +1 -0
  53. package/dist/domain/ingest/video-md-parser.d.ts +2 -0
  54. package/dist/domain/ingest/video-md-parser.js +233 -0
  55. package/dist/domain/ingest/video-md-parser.js.map +1 -0
  56. package/dist/domain/ingest/video-records.d.ts +56 -0
  57. package/dist/domain/ingest/video-records.js +9 -0
  58. package/dist/domain/ingest/video-records.js.map +1 -0
  59. package/dist/domain/loom/index.d.ts +2 -1
  60. package/dist/domain/loom/index.js +12 -10
  61. package/dist/domain/loom/index.js.map +1 -1
  62. package/dist/domain/loom/types.d.ts +3 -2
  63. package/dist/domain/script/lookups.d.ts +3 -2
  64. package/dist/domain/script/lookups.js +25 -8
  65. package/dist/domain/script/lookups.js.map +1 -1
  66. package/dist/domain/script/patch/apply.js +2 -2
  67. package/dist/domain/script/patch/apply.js.map +1 -1
  68. package/dist/domain/script/patch/helpers.d.ts +3 -7
  69. package/dist/domain/script/patch/helpers.js +23 -96
  70. package/dist/domain/script/patch/helpers.js.map +1 -1
  71. package/dist/domain/script/patch/ops-action.d.ts +0 -2
  72. package/dist/domain/script/patch/ops-action.js +34 -100
  73. package/dist/domain/script/patch/ops-action.js.map +1 -1
  74. package/dist/domain/script/patch/ops-asset.d.ts +1 -0
  75. package/dist/domain/script/patch/ops-asset.js +102 -155
  76. package/dist/domain/script/patch/ops-asset.js.map +1 -1
  77. package/dist/domain/script/patch/ops-dialogue.d.ts +1 -4
  78. package/dist/domain/script/patch/ops-dialogue.js +43 -220
  79. package/dist/domain/script/patch/ops-dialogue.js.map +1 -1
  80. package/dist/domain/script/patch/ops-extend.js +8 -2
  81. package/dist/domain/script/patch/ops-extend.js.map +1 -1
  82. package/dist/domain/script/patch/ops-meta.js.map +1 -1
  83. package/dist/domain/script/patch/ops-scene.js +26 -39
  84. package/dist/domain/script/patch/ops-scene.js.map +1 -1
  85. package/dist/domain/script/patch/ops-state.d.ts +3 -3
  86. package/dist/domain/script/patch/ops-state.js +33 -49
  87. package/dist/domain/script/patch/ops-state.js.map +1 -1
  88. package/dist/domain/script/patch/registry.js +9 -13
  89. package/dist/domain/script/patch/registry.js.map +1 -1
  90. package/dist/domain/script/refs.js +23 -111
  91. package/dist/domain/script/refs.js.map +1 -1
  92. package/dist/domain/script/scene-refs.d.ts +3 -0
  93. package/dist/domain/script/scene-refs.js +35 -0
  94. package/dist/domain/script/scene-refs.js.map +1 -0
  95. package/dist/domain/script/schema.js +17 -21
  96. package/dist/domain/script/schema.js.map +1 -1
  97. package/dist/domain/script/text-util.d.ts +7 -0
  98. package/dist/domain/script/text-util.js +91 -0
  99. package/dist/domain/script/text-util.js.map +1 -0
  100. package/dist/help-text.js +145 -414
  101. package/dist/help-text.js.map +1 -1
  102. package/dist/infra/converters.js +1 -1
  103. package/dist/infra/converters.js.map +1 -1
  104. package/dist/infra/episode-title.d.ts +4 -0
  105. package/dist/infra/episode-title.js +129 -0
  106. package/dist/infra/episode-title.js.map +1 -0
  107. package/dist/infra/ingest/review-renderer.d.ts +1 -0
  108. package/dist/infra/ingest/review-renderer.js +431 -0
  109. package/dist/infra/ingest/review-renderer.js.map +1 -0
  110. package/dist/infra/ingest/source-resolver.d.ts +21 -0
  111. package/dist/infra/ingest/source-resolver.js +51 -0
  112. package/dist/infra/ingest/source-resolver.js.map +1 -0
  113. package/dist/infra/ingest/text-reader.d.ts +3 -0
  114. package/dist/infra/ingest/text-reader.js +27 -0
  115. package/dist/infra/ingest/text-reader.js.map +1 -0
  116. package/dist/infra/ingest/video-discovery.d.ts +8 -0
  117. package/dist/infra/ingest/video-discovery.js +60 -0
  118. package/dist/infra/ingest/video-discovery.js.map +1 -0
  119. package/dist/infra/ingest/workspace-store.d.ts +18 -0
  120. package/dist/infra/ingest/workspace-store.js +70 -0
  121. package/dist/infra/ingest/workspace-store.js.map +1 -0
  122. package/dist/infra/llm/anthropic-model.d.ts +11 -0
  123. package/dist/infra/llm/anthropic-model.js +109 -0
  124. package/dist/infra/llm/anthropic-model.js.map +1 -0
  125. package/dist/infra/llm/gemini-upload.d.ts +9 -0
  126. package/dist/infra/llm/gemini-upload.js +94 -0
  127. package/dist/infra/llm/gemini-upload.js.map +1 -0
  128. package/dist/infra/llm/gemini-video-model.d.ts +47 -0
  129. package/dist/infra/llm/gemini-video-model.js +167 -0
  130. package/dist/infra/llm/gemini-video-model.js.map +1 -0
  131. package/dist/infra/llm/openai-compatible-model.d.ts +13 -0
  132. package/dist/infra/llm/openai-compatible-model.js +143 -0
  133. package/dist/infra/llm/openai-compatible-model.js.map +1 -0
  134. package/dist/infra/llm/proxy.d.ts +1 -0
  135. package/dist/infra/llm/proxy.js +30 -0
  136. package/dist/infra/llm/proxy.js.map +1 -0
  137. package/dist/infra/llm/retry.d.ts +5 -0
  138. package/dist/infra/llm/retry.js +20 -0
  139. package/dist/infra/llm/retry.js.map +1 -0
  140. package/dist/llm/composition.d.ts +5 -0
  141. package/dist/llm/composition.js +5 -0
  142. package/dist/llm/composition.js.map +1 -0
  143. package/dist/llm/config.d.ts +26 -0
  144. package/dist/llm/config.js +201 -0
  145. package/dist/llm/config.js.map +1 -0
  146. package/dist/llm/registry.d.ts +44 -0
  147. package/dist/llm/registry.js +48 -0
  148. package/dist/llm/registry.js.map +1 -0
  149. package/dist/llm/router.d.ts +23 -0
  150. package/dist/llm/router.js +0 -0
  151. package/dist/llm/router.js.map +1 -0
  152. package/dist/llm/tasks/ingest/asset-curate.d.ts +13 -0
  153. package/dist/llm/tasks/ingest/asset-curate.js +45 -0
  154. package/dist/llm/tasks/ingest/asset-curate.js.map +1 -0
  155. package/dist/llm/tasks/ingest/asset-extract.d.ts +12 -0
  156. package/dist/llm/tasks/ingest/asset-extract.js +51 -0
  157. package/dist/llm/tasks/ingest/asset-extract.js.map +1 -0
  158. package/dist/llm/tasks/ingest/asset-govern.d.ts +22 -0
  159. package/dist/llm/tasks/ingest/asset-govern.js +143 -0
  160. package/dist/llm/tasks/ingest/asset-govern.js.map +1 -0
  161. package/dist/llm/tasks/ingest/mention-resolve.d.ts +9 -0
  162. package/dist/llm/tasks/ingest/mention-resolve.js +39 -0
  163. package/dist/llm/tasks/ingest/mention-resolve.js.map +1 -0
  164. package/dist/llm/tasks/ingest/plot-speaker.d.ts +11 -0
  165. package/dist/llm/tasks/ingest/plot-speaker.js +72 -0
  166. package/dist/llm/tasks/ingest/plot-speaker.js.map +1 -0
  167. package/dist/llm/tasks/ingest/roster-merge.d.ts +46 -0
  168. package/dist/llm/tasks/ingest/roster-merge.js +235 -0
  169. package/dist/llm/tasks/ingest/roster-merge.js.map +1 -0
  170. package/dist/llm/tasks/ingest/state-merge.d.ts +10 -0
  171. package/dist/llm/tasks/ingest/state-merge.js +84 -0
  172. package/dist/llm/tasks/ingest/state-merge.js.map +1 -0
  173. package/dist/llm/tasks/ingest/state-reconcile.d.ts +8 -0
  174. package/dist/llm/tasks/ingest/state-reconcile.js +44 -0
  175. package/dist/llm/tasks/ingest/state-reconcile.js.map +1 -0
  176. package/dist/llm/tasks/ingest/text-normalize.d.ts +10 -0
  177. package/dist/llm/tasks/ingest/text-normalize.js +89 -0
  178. package/dist/llm/tasks/ingest/text-normalize.js.map +1 -0
  179. package/dist/llm/tasks/ingest/video-correct.d.ts +30 -0
  180. package/dist/llm/tasks/ingest/video-correct.js +144 -0
  181. package/dist/llm/tasks/ingest/video-correct.js.map +1 -0
  182. package/dist/llm/tasks/ingest/video-transcribe.d.ts +4 -0
  183. package/dist/llm/tasks/ingest/video-transcribe.js +105 -0
  184. package/dist/llm/tasks/ingest/video-transcribe.js.map +1 -0
  185. package/dist/llm/tasks/schemas.d.ts +5 -0
  186. package/dist/llm/tasks/schemas.js +107 -0
  187. package/dist/llm/tasks/schemas.js.map +1 -0
  188. package/dist/llm/types.d.ts +97 -0
  189. package/dist/llm/types.js +13 -0
  190. package/dist/llm/types.js.map +1 -0
  191. package/dist/usecases/doctor.d.ts +1 -1
  192. package/dist/usecases/doctor.js +36 -9
  193. package/dist/usecases/doctor.js.map +1 -1
  194. package/dist/usecases/ingest/command.d.ts +5 -0
  195. package/dist/usecases/ingest/command.js +57 -0
  196. package/dist/usecases/ingest/command.js.map +1 -0
  197. package/dist/usecases/ingest/fanout.d.ts +10 -0
  198. package/dist/usecases/ingest/fanout.js +23 -0
  199. package/dist/usecases/ingest/fanout.js.map +1 -0
  200. package/dist/usecases/ingest/pass4-mention-resolve.d.ts +11 -0
  201. package/dist/usecases/ingest/pass4-mention-resolve.js +52 -0
  202. package/dist/usecases/ingest/pass4-mention-resolve.js.map +1 -0
  203. package/dist/usecases/ingest/pass5-extract.d.ts +7 -0
  204. package/dist/usecases/ingest/pass5-extract.js +50 -0
  205. package/dist/usecases/ingest/pass5-extract.js.map +1 -0
  206. package/dist/usecases/ingest/pass5-govern.d.ts +13 -0
  207. package/dist/usecases/ingest/pass5-govern.js +155 -0
  208. package/dist/usecases/ingest/pass5-govern.js.map +1 -0
  209. package/dist/usecases/ingest/pipeline.d.ts +28 -0
  210. package/dist/usecases/ingest/pipeline.js +144 -0
  211. package/dist/usecases/ingest/pipeline.js.map +1 -0
  212. package/dist/usecases/ingest/publish-command.d.ts +4 -0
  213. package/dist/usecases/ingest/publish-command.js +77 -0
  214. package/dist/usecases/ingest/publish-command.js.map +1 -0
  215. package/dist/usecases/ingest/status.d.ts +39 -0
  216. package/dist/usecases/ingest/status.js +155 -0
  217. package/dist/usecases/ingest/status.js.map +1 -0
  218. package/dist/usecases/ingest/video-pass3.d.ts +26 -0
  219. package/dist/usecases/ingest/video-pass3.js +0 -0
  220. package/dist/usecases/ingest/video-pass3.js.map +1 -0
  221. package/dist/usecases/ingest/video-pipeline.d.ts +32 -0
  222. package/dist/usecases/ingest/video-pipeline.js +202 -0
  223. package/dist/usecases/ingest/video-pipeline.js.map +1 -0
  224. package/dist/usecases/ingest/view-command.d.ts +4 -0
  225. package/dist/usecases/ingest/view-command.js +51 -0
  226. package/dist/usecases/ingest/view-command.js.map +1 -0
  227. package/dist/usecases/script/actions.js +2 -2
  228. package/dist/usecases/script/actions.js.map +1 -1
  229. package/dist/usecases/script/actor.js.map +1 -1
  230. package/dist/usecases/script/actors.js +1 -0
  231. package/dist/usecases/script/actors.js.map +1 -1
  232. package/dist/usecases/script/add-actor.js.map +1 -1
  233. package/dist/usecases/script/add-episode.js.map +1 -1
  234. package/dist/usecases/script/add-location.js.map +1 -1
  235. package/dist/usecases/script/add-prop.js.map +1 -1
  236. package/dist/usecases/script/alias.js.map +1 -1
  237. package/dist/usecases/script/assets.js +2 -1
  238. package/dist/usecases/script/assets.js.map +1 -1
  239. package/dist/usecases/script/context.js.map +1 -1
  240. package/dist/usecases/script/create.js +3 -6
  241. package/dist/usecases/script/create.js.map +1 -1
  242. package/dist/usecases/script/delete.js.map +1 -1
  243. package/dist/usecases/script/describe.js.map +1 -1
  244. package/dist/usecases/script/dialogue.js +6 -13
  245. package/dist/usecases/script/dialogue.js.map +1 -1
  246. package/dist/usecases/script/do.js +1 -1
  247. package/dist/usecases/script/do.js.map +1 -1
  248. package/dist/usecases/script/episodes.js.map +1 -1
  249. package/dist/usecases/script/{add-speaker.d.ts → importance.d.ts} +1 -1
  250. package/dist/usecases/script/importance.js +17 -0
  251. package/dist/usecases/script/importance.js.map +1 -0
  252. package/dist/usecases/script/insert.js.map +1 -1
  253. package/dist/usecases/script/issues.js.map +1 -1
  254. package/dist/usecases/script/lib.d.ts +19 -6
  255. package/dist/usecases/script/lib.js +173 -99
  256. package/dist/usecases/script/lib.js.map +1 -1
  257. package/dist/usecases/script/locations.js +1 -0
  258. package/dist/usecases/script/locations.js.map +1 -1
  259. package/dist/usecases/script/merge.js.map +1 -1
  260. package/dist/usecases/script/move.js.map +1 -1
  261. package/dist/usecases/script/props.js +1 -0
  262. package/dist/usecases/script/props.js.map +1 -1
  263. package/dist/usecases/script/refs.js.map +1 -1
  264. package/dist/usecases/script/rename.js.map +1 -1
  265. package/dist/usecases/script/replace.js.map +1 -1
  266. package/dist/usecases/script/role.js.map +1 -1
  267. package/dist/usecases/script/scenes.js +8 -15
  268. package/dist/usecases/script/scenes.js.map +1 -1
  269. package/dist/usecases/script/script-patch.js +1 -1
  270. package/dist/usecases/script/script-patch.js.map +1 -1
  271. package/dist/usecases/script/script-validate.js +1 -1
  272. package/dist/usecases/script/script-validate.js.map +1 -1
  273. package/dist/usecases/script/session.d.ts +4 -4
  274. package/dist/usecases/script/session.js +57 -78
  275. package/dist/usecases/script/session.js.map +1 -1
  276. package/dist/usecases/script/split.js.map +1 -1
  277. package/dist/usecases/script/state-add.js.map +1 -1
  278. package/dist/usecases/script/state-delete.js.map +1 -1
  279. package/dist/usecases/script/state-rename.js.map +1 -1
  280. package/dist/usecases/script/states.js +58 -13
  281. package/dist/usecases/script/states.js.map +1 -1
  282. package/dist/usecases/script/summary.js +8 -0
  283. package/dist/usecases/script/summary.js.map +1 -1
  284. package/dist/usecases/script/synopsis-generate.d.ts +4 -5
  285. package/dist/usecases/script/synopsis-generate.js +30 -46
  286. package/dist/usecases/script/synopsis-generate.js.map +1 -1
  287. package/dist/usecases/script/transition.js.map +1 -1
  288. package/dist/usecases/script/type.js.map +1 -1
  289. package/dist/usecases/script/worldview.js.map +1 -1
  290. package/package.json +11 -3
  291. package/scripts/install-skill.mjs +120 -0
  292. package/skills/scriptctl/SKILL.md +293 -0
  293. package/skills/scriptctl/references/atomic-write-workflow.md +117 -0
  294. package/skills/scriptctl/references/ingest-workflow.md +85 -0
  295. package/skills/scriptctl/references/state-reference-repair.md +59 -0
  296. package/dist/domain/direct/runner.d.ts +0 -25
  297. package/dist/domain/direct/runner.js +0 -88
  298. package/dist/domain/direct/runner.js.map +0 -1
  299. package/dist/domain/direct/stage.d.ts +0 -108
  300. package/dist/domain/direct/stage.js +0 -134
  301. package/dist/domain/direct/stage.js.map +0 -1
  302. package/dist/domain/direct/stages/asset-curation.d.ts +0 -2
  303. package/dist/domain/direct/stages/asset-curation.js +0 -10
  304. package/dist/domain/direct/stages/asset-curation.js.map +0 -1
  305. package/dist/domain/direct/stages/batch-extract.d.ts +0 -2
  306. package/dist/domain/direct/stages/batch-extract.js +0 -12
  307. package/dist/domain/direct/stages/batch-extract.js.map +0 -1
  308. package/dist/domain/direct/stages/batch-plan.d.ts +0 -2
  309. package/dist/domain/direct/stages/batch-plan.js +0 -9
  310. package/dist/domain/direct/stages/batch-plan.js.map +0 -1
  311. package/dist/domain/direct/stages/episode-merge.d.ts +0 -2
  312. package/dist/domain/direct/stages/episode-merge.js +0 -11
  313. package/dist/domain/direct/stages/episode-merge.js.map +0 -1
  314. package/dist/domain/direct/stages/episode-plan.d.ts +0 -2
  315. package/dist/domain/direct/stages/episode-plan.js +0 -9
  316. package/dist/domain/direct/stages/episode-plan.js.map +0 -1
  317. package/dist/domain/direct/stages/episode-synopsis.d.ts +0 -2
  318. package/dist/domain/direct/stages/episode-synopsis.js +0 -12
  319. package/dist/domain/direct/stages/episode-synopsis.js.map +0 -1
  320. package/dist/domain/direct/stages/episode-titles.d.ts +0 -2
  321. package/dist/domain/direct/stages/episode-titles.js +0 -12
  322. package/dist/domain/direct/stages/episode-titles.js.map +0 -1
  323. package/dist/domain/direct/stages/index.d.ts +0 -19
  324. package/dist/domain/direct/stages/index.js +0 -41
  325. package/dist/domain/direct/stages/index.js.map +0 -1
  326. package/dist/domain/direct/stages/metadata.d.ts +0 -2
  327. package/dist/domain/direct/stages/metadata.js +0 -11
  328. package/dist/domain/direct/stages/metadata.js.map +0 -1
  329. package/dist/domain/direct/stages/script-merge.d.ts +0 -2
  330. package/dist/domain/direct/stages/script-merge.js +0 -15
  331. package/dist/domain/direct/stages/script-merge.js.map +0 -1
  332. package/dist/domain/direct/stages/script-synopsis.d.ts +0 -2
  333. package/dist/domain/direct/stages/script-synopsis.js +0 -12
  334. package/dist/domain/direct/stages/script-synopsis.js.map +0 -1
  335. package/dist/domain/direct/stages/source-prepare.d.ts +0 -2
  336. package/dist/domain/direct/stages/source-prepare.js +0 -9
  337. package/dist/domain/direct/stages/source-prepare.js.map +0 -1
  338. package/dist/domain/direct/stages/state-binding.d.ts +0 -2
  339. package/dist/domain/direct/stages/state-binding.js +0 -9
  340. package/dist/domain/direct/stages/state-binding.js.map +0 -1
  341. package/dist/domain/direct/stages/state-curation.d.ts +0 -2
  342. package/dist/domain/direct/stages/state-curation.js +0 -10
  343. package/dist/domain/direct/stages/state-curation.js.map +0 -1
  344. package/dist/domain/direct/stages/validate.d.ts +0 -2
  345. package/dist/domain/direct/stages/validate.js +0 -10
  346. package/dist/domain/direct/stages/validate.js.map +0 -1
  347. package/dist/domain/direct-core.d.ts +0 -313
  348. package/dist/domain/direct-core.js +0 -8516
  349. package/dist/domain/direct-core.js.map +0 -1
  350. package/dist/domain/script/validate.d.ts +0 -22
  351. package/dist/domain/script/validate.js +0 -997
  352. package/dist/domain/script/validate.js.map +0 -1
  353. package/dist/infra/providers.d.ts +0 -97
  354. package/dist/infra/providers.js +0 -1396
  355. package/dist/infra/providers.js.map +0 -1
  356. package/dist/usecases/direct.d.ts +0 -27
  357. package/dist/usecases/direct.js +0 -4172
  358. package/dist/usecases/direct.js.map +0 -1
  359. package/dist/usecases/parse.d.ts +0 -15
  360. package/dist/usecases/parse.js +0 -429
  361. package/dist/usecases/parse.js.map +0 -1
  362. package/dist/usecases/script/add-speaker.js +0 -22
  363. package/dist/usecases/script/add-speaker.js.map +0 -1
  364. package/dist/usecases/script/export.d.ts +0 -3
  365. package/dist/usecases/script/export.js +0 -111
  366. package/dist/usecases/script/export.js.map +0 -1
  367. package/dist/usecases/script/overlap.d.ts +0 -3
  368. package/dist/usecases/script/overlap.js +0 -21
  369. package/dist/usecases/script/overlap.js.map +0 -1
  370. package/dist/usecases/script/speakers.d.ts +0 -3
  371. package/dist/usecases/script/speakers.js +0 -28
  372. package/dist/usecases/script/speakers.js.map +0 -1
  373. package/dist/usecases/script/state-change.d.ts +0 -3
  374. package/dist/usecases/script/state-change.js +0 -24
  375. package/dist/usecases/script/state-change.js.map +0 -1
@@ -0,0 +1,293 @@
1
+ ---
2
+ name: scriptctl
3
+ description: "剧本 script.json 的读写 + 入库 CLI。转剧本:ingest(txt/md/docx 或视频,自动分流,产出 workspace/script.json)→ view 自查 → publish 入库。读:集/场/action/资产/状态/校验/时间戳/出场统计。写:台词/类型/归属/scene-ref 场景造型/内联发声源/importance/资产。选择器批量精修(读写共享 --in/谓词)、动词脚本 do(异构批量一事务)、结构调整(插/删/移/分/合)。ingest status 从工作区真实文件报进度。沙箱里默认读写项目 DB;本地电脑默认 --script-path 直接读写本地 script.json。剧本阶段做任何剧本相关读写、以及洗稿/对标/转绘里操作两个 script.json 之前都应加载。正文由你用原子能力写入。"
4
+ version: "0.19.0"
5
+ scriptctl_version: "0.36.0"
6
+ author: "official"
7
+ ---
8
+
9
+ # scriptctl
10
+
11
+ `scriptctl` 读写结构化剧本 `script.json`(Schema v3:资产 人物/场景/道具 + 分集→场景→action + 场景级 cast/造型 + 内联发声源 + 引用完整性 + 校验),并把外部素材**转成剧本、入库**。**每次调用只作用于一份剧本**(由目标 flag 选定),所以「换剧本 = 换目标」。
12
+
13
+ 两条主线:
14
+ - **转剧本入库**:`ingest`(抽取,产出 `workspace/script.json`)→ `view`(自查)→ `publish`(入库)。见 [references/ingest-workflow.md](references/ingest-workflow.md)。
15
+ - **读写既有剧本**:查询 + 原子写 + 选择器批量 + `do`。
16
+
17
+ ## 先定目标:作用在哪份剧本上
18
+
19
+ 除 `ingest`/`view`/`publish` 外,每条读写命令都要知道读写哪份剧本,从下面选一个(互斥):
20
+
21
+ | 目标 | flag | 用在 |
22
+ |---|---|---|
23
+ | **项目 DB(最终剧本)** | `--remote [--project-group-no <no>]` | ☁️ **沙箱首选**。每次写进 DB 新 revision,边写边入库 |
24
+ | **本地文件** | `--script-path <file>` | 🖥️ **本地电脑首选**。直接读写这一个 JSON,无 store、无 revision。洗稿/对标/转绘、试验、离线全走它 |
25
+ | 本地约定 store | `--local` | `SCRIPTCTL_OUTPUT_DIR` 下的约定 store |
26
+
27
+ - ☁️ **沙箱里**:后端注入 `SANDBOX_PROJECT_GROUP_NO`,**裸命令自动指向该项目 DB**(自动 remote),无需任何 flag。
28
+ - 🖥️ **本地电脑上**:几乎每条命令都带 `--script-path <file>`。下文示例为简洁常省略它,实际本地使用请补上。
29
+ - **不给任何目标、又不在沙箱**:CLI 拒绝猜测并报 `SCRIPT_NO_TARGET`。选一个目标。
30
+ - `--script-path`(本地文件)与 `--remote`/`--local`/`--project-group-no`(store)**互斥**,混用报 `TARGET_FLAG_CONFLICT`。
31
+ - `--workspace-path <dir>` 只给 `ingest`/`view`/`publish`/`ingest status` 用,指 ingest 工作区(默认 `workspace`),不是剧本目标。
32
+
33
+ ### 两个 script.json:洗稿 / 对标 / 转绘
34
+
35
+ 洗稿、对标仿写、转绘(换地区/换壳)里常常**同时开着原剧和新剧两份 script.json**:从原剧读结构与节奏,往新剧写。因为每条命令只作用于 `--script-path` 指的那一份,**换文件就是换这个 flag 的值**:
36
+
37
+ ```bash
38
+ ORIG=原剧/script.json
39
+ NEW=新剧/script.json
40
+
41
+ # 读原剧(了解要对标什么)
42
+ scriptctl summary --script-path "$ORIG"
43
+ scriptctl actions --in ep_001 --script-path "$ORIG" # 带 timestamp 列,看节奏
44
+ scriptctl states act_001 --script-path "$ORIG" # 看人物状态弧线
45
+
46
+ # 建/写新剧(把改写后的正文灌进去)
47
+ scriptctl create --title "新剧名" --script-path "$NEW"
48
+ scriptctl do ep01.txt --apply --script-path "$NEW"
49
+
50
+ # 逐场对照:同一集在两份里并排看
51
+ scriptctl scenes --in ep_003 --script-path "$ORIG"
52
+ scriptctl scenes --in ep_003 --script-path "$NEW"
53
+ ```
54
+
55
+ - 一次只碰一份。要「从 A 抄到 B」就是:`--script-path A` 读出来 → 你决定改法 → `--script-path B` 写进去。
56
+ - 事件级对齐:原剧 `actions` 的 `timestamp` 列 + `scenes` 的 `t=t_start..t_end` 给出每段的时间跨度;对标时按这个跨度切新剧的段落。
57
+ - 人物状态弧线:`states <actor>` 每个状态末尾的 `episodes=<区间>` 告诉你某个造型/形态在原剧哪些集出现,转绘时据此规划新剧的状态。
58
+
59
+ ## 能干什么
60
+
61
+ ### 读(永远只读、永远安全)
62
+
63
+ | 想做什么 | 命令 |
64
+ |---|---|
65
+ | **了解剧情 / 全貌**:synopsis/worldview/style/logline/theme/main_characters + 标题/集数/场数/资产数("这剧本是关于什么"先跑这个) | `scriptctl summary` |
66
+ | 每集字数 / 场数 / action 数 **+ 每集剧情梗概** | `scriptctl episodes`(`--min-chars 1000` 找长集) |
67
+ | 看某集的所有场(含 `t=t_start..t_end` 时段) | `scriptctl scenes --in ep_007` |
68
+ | 看某场的资产 + action 数 | `scriptctl scenes --in ep_007/scn_005` |
69
+ | **按正文搜某句话定位 action**(最常用) | `scriptctl actions --grep "<片段>"` |
70
+ | 列某集所有 action(TSV 含 **timestamp** / speaker / 正文 / 情绪) | `scriptctl actions --in ep_001` |
71
+ | 看单条 action 正文 | `scriptctl actions --in ep_001/scn_001#3` |
72
+ | action 多维过滤 | `scriptctl actions --in ep_001 --type dialogue --actor act_001 --has speaker` |
73
+ | 某角色出现的所有场 | `scriptctl scenes --has-actor act_001`(另有 `--has-location`/`--has-prop`) |
74
+ | **列某资产的所有 state + 状态弧线**(改造型前必查) | `scriptctl states act_001`(裸 id 即可,也认 `actor:act_001`) |
75
+ | 资产**出场统计**(几场/几集,压成集区间) | `scriptctl actors --counts` / `locations --counts` / `props --counts` |
76
+ | 某资产 / 状态 / speaker 反查 | `scriptctl refs actor:act_001`(`refs spk_001` 反查发声源) |
77
+ | 当前校验问题 / 跑校验 | `scriptctl issues --severity error` / `scriptctl validate` |
78
+
79
+ **读侧要点:**
80
+ - `actions` TSV 列:`addr timestamp type speaker actor emotion content`。`timestamp` 是该 action 的 `extend.timestamp`(集内 `M:SS`,无则空)。事件跨度 = 首末 action 时间戳之差。`speaker` 列是 v3 内联发声源(actor 或 system/broadcast/offscreen/group)。
81
+ - `scenes` 行尾 `t=<t_start>..<t_end>`:该场首/末带时间戳 action 的时间。
82
+ - `states <asset>`:接受裸 id(`act_001`/`loc_001`/`prp_001`);每个状态末尾 `episodes=<区间> (N eps, M scenes)` 是该状态的**集弧线**(按 scene 造型引用统计);从不被引用的状态显示 `episodes=none`。
83
+ - `--counts`:追加 `scenes=N episodes_count=M episodes=<区间>`。默认不带,保持精简。
84
+
85
+ ### 写(单点:第一个位置参数是 address。多点:见「选择器批量」)
86
+
87
+ | 想做什么 | 命令 |
88
+ |---|---|
89
+ | 改 action 正文(替换/删一段) | `scriptctl replace <ep/scn#idx> --from X --to Y`(加 `--regex`;`--to` 省略即删) |
90
+ | 改 action type | `scriptctl type <ep/scn#idx> <dialogue\|action\|inner_thought\|transition>` |
91
+ | 改 action 归属角色(内联发声源) | `scriptctl actor <ep/scn#idx> <act_id\|none>` |
92
+ | 改/清 action 情绪 | `scriptctl emotion <ep/scn#idx> "紧张"` / `--clear` |
93
+ | **把某行设成对白 + 定内联发声源** | `scriptctl dialogue <ep/scn#idx> --actor act_001`(角色)/ `--kind <system\|broadcast\|offscreen\|group> [--label 广播]`(非角色声源)。同时把 type 翻成 dialogue |
94
+ | **设/改场景里某资产的造型(state)** | `scriptctl scene-ref <ep/scn> <actor\|location\|prop>:<id> --state <state_id>`(`--state none` 上场但无造型;`--clear` 保留引用清造型;`--remove` 整个移出场景)。location 单值(set 即替换) |
95
+ | 改资产名/描述/别名/role | `scriptctl rename actor:<id> "X"` / `describe` / `alias --add X` / `role <主角\|配角>` |
96
+ | **设资产重要度**(决定下游是否生成视觉资产+状态跟踪) | `scriptctl importance <actor\|location\|prop>:<id> <featured\|background>`(featured=主角/配角;background=龙套/背景,下游跳过) |
97
+ | 给资产加 state | `scriptctl state-add actor:<id> "震惊"` |
98
+ | **改/删某个 state** | `scriptctl state-rename actor:<id>/<st_id> "新名"` / `scriptctl state-delete actor:<id>/<st_id> --strategy remove`(引用修复核验见 [references/state-reference-repair.md](references/state-reference-repair.md)) |
99
+ | 挂自由维度键值 | `scriptctl extend <addr> --key <名> --value <JSON>`(如给 action 挂 `timestamp`) |
100
+ | 合并资产/场景 | `scriptctl merge actor:<src> --into actor:<dst>` |
101
+ | 删 action/scene/资产 | `scriptctl delete <addr>`(资产要 `--strategy replace\|remove`) |
102
+ | 插 action/scene | `scriptctl insert <ep/scn> --type X --content X [--emotion X] [--actor <id>]` / `insert <ep> --location <id>` |
103
+ | 移动/分场 | `scriptctl move <addr> <to>` / `split <ep/scn> --at <idx>` |
104
+ | 设整本/分集剧情梗概 | `scriptctl synopsis "整本梗概"` / `scriptctl synopsis ep_001 "本集梗概"`(`synopsis generate` 用 LLM 批量生成) |
105
+ | 设世界观 | `scriptctl worldview <现代\|古代历史\|...>` |
106
+
107
+ > **v3 变化(相对旧版)**:① 发声源不再是独立实体——`add-speaker`/`speakers` 没了,改成**对白行内联**(`dialogue --actor/--kind`、`actor <at> <id>`)。② 造型不再挂在 action 上——`state-change` 没了,改成**挂在场景级 cast 引用**上(`scene-ref <ep/scn> <kind:id> --state`)。③ `context` 命令改名 `scene-ref`。④ 新增 `importance`。
108
+
109
+ ### 选择器批量(一条命令扫一片 —— 读写共享同一套选择语法)
110
+
111
+ 把单点写动词的「address」换成一个**选择**,它就对**所有命中的 action** 一次性改。支持选择器的写动词:`replace / type / actor / emotion / transition`。
112
+
113
+ - **选范围 `--in`**:`ep_003..ep_007`(集区间)/ `ep/scn` 区间 / 逗号多区间 / `'*'`(全本)。
114
+ - **加谓词**(和 `actions` 查询**完全一样**的 flag,AND 组合):`--type` `--actor` `--grep /re/` `--has speaker|transition`。
115
+ - 🔴 **护栏:多点写默认 dry-run**(回显命中数 + blast-radius,不落盘),加 **`--apply`** 才真写。单点(给 address)仍直写。
116
+
117
+ ```bash
118
+ # 清全本所有 action 开头的 [xxx] 标记:一条正则扫全本
119
+ scriptctl replace --in '*' --regex --from '^\s*\[[^\]]*\]\s*' --to '' # 先看命中
120
+ scriptctl replace --in '*' --regex --from '^\s*\[[^\]]*\]\s*' --to '' --apply # 落地
121
+ # 第3-7集里某角色的对白统一改归属(谓词直达,无需先查地址)
122
+ scriptctl actor --in ep_003..ep_007 --actor act_001 act_009 --apply
123
+ # 第3-7集里某角色的对白统一标情绪
124
+ scriptctl emotion --in ep_003..ep_007 --actor act_001 "紧张" --apply
125
+ ```
126
+
127
+ > 先 discover 后 mutate 用**同一套 flag**:`scriptctl actions --in … --actor …` 查出来是哪些,换个动词就改哪些。`actions --json` 带结构化 `rows[]`,`actions … --format addr` 直接吐裸地址流喂 `xargs`。scene-ref/importance/dialogue 是**单点**动词,不进选择器批量。
128
+
129
+ ### 异构批量 `do`(一组各不相同的改动 = 一段动词脚本 = 一个事务)
130
+
131
+ 每行就是一条**去掉 `scriptctl` 的命令**,`#` 注释、空行随意;整段全成或全不成。比手写 patch JSON 省心(动词原样,不猜字段名)。
132
+
133
+ ```bash
134
+ scriptctl do edits.txt # 默认 dry-run(预演 + 校验,不写)
135
+ scriptctl do edits.txt --apply # 一次性原子落盘
136
+ cat edits.txt <<'EOF'
137
+ replace ep_001/scn_001#3 --from "台下掌声雷动" --to ""
138
+ dialogue ep_001/scn_002#1 --actor act_001
139
+ scene-ref ep_001/scn_001 actor:act_001 --state st_calm
140
+ merge actor:act_005 --into actor:act_001
141
+ insert ep_001/scn_006 --type action --content "暗夜,街道空无一人。"
142
+ EOF
143
+ ```
144
+
145
+ `do` 也读 stdin(`scriptctl do -`)。结构性动词(insert/delete/move/split/merge)**只能**走单点或 `do`,不进选择器批量(索引会漂移)。目标 flag(如 `--script-path`)加在 `do` 命令本身上,整段脚本共用同一目标。
146
+
147
+ ### 建(从零起一本剧本,不要 address,第一个位置参数是名字)
148
+
149
+ | 想做什么 | 命令 |
150
+ |---|---|
151
+ | 建空白剧本(v3 骨架) | `scriptctl create [--title X]` |
152
+ | 加人物 | `scriptctl add-actor "林夏" [--role 主角\|配角] [--description X] [--alias X]` |
153
+ | 加场景地点 | `scriptctl add-location "便利店" [--description X]` |
154
+ | 加道具 | `scriptctl add-prop "怀表" [--description X]` |
155
+ | 加分集 | `scriptctl add-episode [--title X]` |
156
+
157
+ id 自动按序分配(`act_001` / `loc_001` / `prp_001` / `ep_001` / `scn_001`…)。建完资产与集,用 `insert` 建场景、灌正文(正文 = 你想好文字用原子能力写入)。整本怎么搭见 [references/atomic-write-workflow.md](references/atomic-write-workflow.md)。
158
+
159
+ ### Address 格式(决定 verb 作用对象,自动分发)
160
+
161
+ | 形态 | 类型 | 例 |
162
+ |---|---|---|
163
+ | `ep_001/scn_001#3` | action | `replace` / `type` / `actor` / `emotion` / `dialogue` |
164
+ | `ep_001/scn_001` | scene | `scene-ref`(配 `<kind:id>`)/ `split` |
165
+ | `actor:act_001` / `location:loc_001` / `prop:prp_001` | asset | `rename` / `importance` / `merge` / `delete` |
166
+ | `actor:act_001/st_001` | asset state | `state-rename` / `state-delete` |
167
+ | `ep_001` | episode | `insert`(scene)|
168
+ | `spk_001` | speaker 实体 | `refs` / `delete` |
169
+
170
+ > `states` 查询额外接受裸资产 id(`act_001`/`loc_001`/`prp_001`),按前缀推断种类。其它 verb 仍要完整 address。
171
+
172
+ ## 经典最短路径
173
+
174
+ **改某句台词**:grep 找地址 → replace 落字。
175
+ ```bash
176
+ scriptctl actions --grep "台下掌声雷动"
177
+ # → ep_001/scn_001#3 0:14 action ... 台下掌声雷动。
178
+ scriptctl replace ep_001/scn_001#3 --from ",台下掌声雷动" --to ""
179
+ ```
180
+ `--to` 省略即删除。一个 action 内多次命中默认拒绝,把 `--from` 写更长更唯一,或显式 `--all`。
181
+
182
+ **改某行归属 / 定发声源**:
183
+ ```bash
184
+ scriptctl dialogue ep_001/scn_003#5 --actor act_002 # 这行是 act_002 说的
185
+ scriptctl dialogue ep_001/scn_003#7 --kind broadcast --label 广播 # 非角色声源
186
+ ```
187
+
188
+ **设场景里某人的造型**:
189
+ ```bash
190
+ scriptctl states act_001 # 先看有哪些 state
191
+ scriptctl scene-ref ep_001/scn_003 actor:act_001 --state st_injured
192
+ ```
193
+
194
+ **整理重复角色**:
195
+ ```bash
196
+ scriptctl refs actor:act_005 # 先看 act_005 出现在哪
197
+ scriptctl merge actor:act_005 --into actor:act_001
198
+ ```
199
+
200
+ **标龙套(下游省算力)**:
201
+ ```bash
202
+ scriptctl actors --counts # 看谁出场少
203
+ scriptctl importance actor:act_042 background
204
+ ```
205
+
206
+ **修校验问题**:
207
+ ```bash
208
+ scriptctl issues --severity error
209
+ # → STATE_NOT_MATERIALIZED at ep_001/scn_003 actor:act_001
210
+ scriptctl states act_001 # 确认合法 state_id
211
+ scriptctl scene-ref ep_001/scn_003 actor:act_001 --state st_calm
212
+ ```
213
+
214
+ **批量精修(同构一片 → 选择器;异构多点 → `do`)**:
215
+ ```bash
216
+ scriptctl replace --in ep_003..ep_007 --from 陈总 --to 陈墨 --all # 同构:默认 dry-run
217
+ scriptctl replace --in ep_003..ep_007 --from 陈总 --to 陈墨 --all --apply # --apply 落地
218
+ scriptctl do fixes.txt --apply # 异构:一段动词脚本一事务
219
+ ```
220
+
221
+ ## 转剧本入库:ingest → view → publish
222
+
223
+ 有外部素材(剧本文本 / 分镜 / 小说 / 视频)要变成入库剧本时,走这条线。**统一入口是 `ingest`,自动分流文本/视频**。完整细节 + 复杂场景见 [references/ingest-workflow.md](references/ingest-workflow.md)。
224
+
225
+ | 项目状态 | 走 |
226
+ |---|---|
227
+ | 没剧本文本,从零写(题材/灵感/集纲在手) | `create` + `add-*` + `insert` + `do`(正文你自己写),然后 `publish` |
228
+ | 已有剧本/分镜/小说文本(txt/md/docx) | `scriptctl ingest --source-path <file>` → `view` 自查 → `publish` |
229
+ | 有视频(正片文件或分集目录) | `scriptctl ingest --source-path <file\|dir>` → `view` 点击定位复核 → `publish` |
230
+
231
+ 三步:
232
+
233
+ ```bash
234
+ # 1) 抽取:产出 workspace/script.json(不入库)。文本/视频看后缀自动分流。
235
+ scriptctl ingest --source-path uploads/剧本.docx # 文本
236
+ scriptctl ingest --source-path uploads/正片/ # 视频分集目录(需 GEMINI_API_KEY)
237
+
238
+ # 2) 看进度 / 自查
239
+ scriptctl ingest status # 从工作区真实文件报进度(见下)
240
+ scriptctl view # 生成 review.html(视频版可点击定位)
241
+ scriptctl summary --script-path workspace/script.json # 抽完后当普通剧本读/改(注意目标是本地文件)
242
+
243
+ # 3) 入库:校验 v3 通过后写进目标 store
244
+ scriptctl publish # 沙箱:自动写项目 DB 新 revision
245
+ scriptctl publish --local # 本地:写 SCRIPTCTL_OUTPUT_DIR
246
+ ```
247
+
248
+ - 🔴 **ingest 不入库**——它只产出 `workspace/script.json`。**入库是 `publish` 这独立一步**(沙箱里转完必须 publish,否则后端/前端看不到剧本)。
249
+ - 抽取产物在 `workspace/` 下(默认目录,可 `--workspace-path` 改)。抽完后可用所有读写动词加 `--script-path workspace/script.json` 精修,再 `publish`;或 publish 入库后再对 DB(remote)精修。
250
+ - publish 冲突(`SCRIPT_REVISION_CONFLICT`)= DB 被别的 revision 改过,重新拉取再 publish。
251
+
252
+ ### `ingest status`:进度按真实文件判定
253
+
254
+ ```bash
255
+ scriptctl ingest status # 人读:kind/state/各 pass 进度
256
+ scriptctl ingest status --json # 机器:.status = {kind,state,phase,passes[],units,validation,errors[]}
257
+ ```
258
+ - `state`:`empty`(没开始)/ `incomplete`(在跑或中断、无硬错,重跑续)/ `failed`(有失败单元或校验没过)/ `ready`(script.json 已生成且 v3 校验通过)。
259
+ - 进度**纯从工作区文件推导**(没有独立状态机文件,不会和真实状态不同步)。它报的是"管线事实",不代表"已人工复核"——复核是你的完成门,另算。
260
+
261
+ ### 🔴 遇到错误怎么办(抖动是常态,重跑即续)
262
+
263
+ 大规模转剧本时,模型抖动 / 限流 / 单集超时 / 偶发解析失败是**正常现象**,不是 bug。护栏与恢复:
264
+
265
+ - **`*_INCOMPLETE`(NORMALIZE / TRANSCRIBE / CORRECT / SPEAKER)= 可续的抖动**:部分单元失败,工作区留下 `passN/*.error.json` sidecar。**直接重跑 `scriptctl ingest`(同 source、同 workspace)即可**——它从 checkpoint 续,只补失败单元,成功的不重跑。跑完 `ingest status` 看还剩几个失败,多跑几次直到收敛。
266
+ - 🔴 **不要**清工作区、不要删 checkpoint、不要手塞/手改中间文件来"绕过"。重跑是唯一正解。
267
+ - **`INGEST_SOURCE_MISMATCH` = 换源了**(工作区是别的 source 建的):换一个新的 `--workspace-path`,别 `--force` 硬覆盖。
268
+ - **`INGEST_VALIDATION_FAILED` = 最终 v3 校验没过**:看 `workspace/validation.json` 里的具体报错,用读写动词修 `--script-path workspace/script.json` 再校验/publish。
269
+ - **持续 401/403/404 或模型报错**:多半是网关/密钥/模型路由的环境问题(不是抖动、重跑也不好)。如实告诉用户,别反复空跑、别编造成功。视频转需要 `GEMINI_API_KEY`。
270
+ - 支持的输入:txt/md/docx + 视频(文件或分集目录);其它(pdf/json/xlsx)会被拒。
271
+
272
+ ## 资产 state 判定(`state-add` / `scene-ref --state` 通用规则)
273
+
274
+ 只有**持久、可复用、需独立生成视觉资产的外观变体**才进 `states[]`。身份/年龄/职业/情绪/动作/姿态/临时伤病/混合状态**不**算状态,放 description 或 action 文本。
275
+
276
+ **默认状态(default)**:每个有 `states[]` 的资产必须有且仅有一个 `state_id: "default"`(最常见常态外观,保留可读 state_name)。`ingest` 会自动把「默认/最常见」那档 canonical 成 `default`;手动 `state-add` 给原先无状态的资产加第一个 state 后,若校验报 `DEFAULT_STATE_MISSING`,把最常见那档的 state_id 设成 `default`。
277
+
278
+ 合并/删除 state 时,必须确认引用它的场景 `scene-ref --state` 已被正确修复;操作见 [references/state-reference-repair.md](references/state-reference-repair.md)。
279
+
280
+ ## 通用边界
281
+
282
+ - 🔴 **不要加 `--json`**。默认列表/查询输出是给 agent 读的 TSV/行式文本(地址单独成列),比 JSON 更省 token、更好决策。除非真要 parse 字段喂给下一步程序(`actions --json` 有结构化 `rows[]`,`ingest status --json` 有 `.status`,或 `--format addr` 取裸地址流)。
283
+ - 🔴 **多点写默认 dry-run**:`replace/type/actor/emotion/transition` 一旦带 `--in`/谓词就是预演(回显命中数,不落盘),确认后加 `--apply` 才真写。单点(给 address)直写。
284
+ - 🔴 **手写批量走 `do`,不要手撸 patch JSON**。`scriptctl patch <file|->` 是**机器/jq 管道**接口(`… --json | jq '[…]' | scriptctl patch -`);人/agent 手写一律用 `do`。需要 op schema 用 `scriptctl patch --schema [<op>]`。
285
+ - 🔴 **本地用记得带 `--script-path <file>`**;洗稿/对标/转绘里两份剧本就是两个不同的 `--script-path`,一次只碰一份。
286
+ - 🔴 **沙箱里转完剧本要 `publish`**:`ingest` 只产 `workspace/script.json`,不 publish 后端/前端看不到。
287
+ - 只有 `ingest` 会请求大模型;`ingest status`、`view`、`publish`、`summary/actions` 等查询、`synopsis/replace/patch/do` 等读写命令**不打模型**。
288
+ - 多态 verb(`delete`/`merge`/`move`/`describe`/`rename`/`insert`/`scene-ref`)按 address 格式分发;flag 用错 kind 会直接报错(不静默忽略)。
289
+ - 互斥 flag(`scene-ref` 的 --state/--clear/--remove;`dialogue` 的 --actor/--kind;`transition` 的 --process+--contrast/--clear)只能传一个,多传报 `*_FLAG_CONFLICT`。
290
+ - `mock` provider 仅测试,禁止作为交付;provider 失败不降级 mock。
291
+ - 工具失败排查根因,不手工拼装 JSON 绕过校验。
292
+
293
+ 参数/默认值/退出码以 `scriptctl <cmd> --help` 为准。功能变更用 `scriptctl changelog` 查。
@@ -0,0 +1,117 @@
1
+ # 从零原子写 — 你自己写正文,scriptctl 只搭骨架
2
+
3
+ 适用:**没有现成剧本文本**,从零写(手里只有题材 / 灵感 / 集纲)。
4
+
5
+ 核心理念:**写手是你**。你自己想好每一句正文,用原子能力把它灌进结构化剧本里。scriptctl 负责「合法的骨架 + 引用完整性 + 校验」,不负责「写得好不好」。
6
+
7
+ ---
8
+
9
+ ## 端到端流程
10
+
11
+ ```
12
+ create(空白剧本)
13
+ → worldview 设世界观
14
+ → add-actor / add-location / add-prop 注册资产(+ state-add 给外观档位)
15
+ → add-episode 建分集
16
+ → synopsis 写整本 / 分集梗概
17
+ → insert <ep> --location 建场景
18
+ → insert <ep/scn> --type ... --content "正文" [--emotion "情绪"] [--actor <id>] 逐 action 灌正文
19
+ → dialogue / scene-ref / transition / importance 发声源、场景造型、重要度
20
+ → validate 收尾自查
21
+ → publish 入库(沙箱必做)
22
+ ```
23
+
24
+ > v3 里发声源是**对白行内联**(`dialogue <at> --actor/--kind`,或 `insert --actor`),没有独立的 speaker 实体命令;造型挂在**场景 cast 引用**上(`scene-ref <ep/scn> <kind:id> --state`),没有 action 级 state-change。
25
+
26
+ 资产/分集/场景的 id **自动按序分配**:第一个 actor 是 `act_001`、location 是 `loc_001`、prop 是 `prp_001`、episode 是 `ep_001`、它下面第一场是 `scn_001`……所以你能在后续命令里直接引用这些可预测的 id(也可以 `--id` 显式指定)。
27
+
28
+ ---
29
+
30
+ ## 两种写法
31
+
32
+ ### A. 增量(少量、边想边写):逐条原子 verb
33
+
34
+ ```bash
35
+ scriptctl create --title "夏夜便利店"
36
+ scriptctl worldview 现代
37
+ scriptctl add-actor 林夏 --role 主角 --description "高三女生,沉默寡言"
38
+ scriptctl add-actor 陈默 --role 配角 --description "便利店夜班店员"
39
+ scriptctl add-location 便利店 --description "24h 便利店,冷白光"
40
+ scriptctl add-episode --title "第一集:相遇"
41
+ scriptctl synopsis "雨夜便利店里,林夏与夜班店员陈默因一场意外相遇。"
42
+ scriptctl synopsis ep_001 "林夏雨夜进入便利店,发现陈默似乎认识她。"
43
+ scriptctl insert ep_001 --location loc_001 --time night --space interior # → scn_001
44
+ scriptctl insert ep_001/scn_001 --type action --content "雨夜,林夏推门进店,浑身湿透。" --emotion "狼狈"
45
+ scriptctl insert ep_001/scn_001 --type dialogue --content "欢迎光临。" --actor act_002
46
+ scriptctl insert ep_001/scn_001 --type inner_thought --content "又是他。" --actor act_001
47
+ scriptctl validate
48
+ ```
49
+
50
+ ### B. 整集 / 整本(推荐):一段 `do` 动词脚本,一个事务
51
+
52
+ 一集几十个 action 不要敲几十条命令,也**不要手撸 patch JSON**。写一段 `do` 脚本——**每行就是一条你会敲的命令**(去掉开头的 `scriptctl`),按依赖顺序排好(先建资产、再 `add-episode`、再 `insert` 场景、最后 `insert` action),一次落地。同一段 `do` 内按顺序执行,因此 `add-episode` 产出的 `ep_001` 能被靠后的 `insert` 直接引用。
53
+
54
+ ```bash
55
+ scriptctl create --title "夏夜便利店" # 先有空白剧本
56
+ scriptctl do ep01.txt # 默认 dry-run:内存里跑 + 校验,不落库
57
+ scriptctl do ep01.txt --apply # 确认后真写
58
+ ```
59
+
60
+ `ep01.txt` 形如(每行一条动词,`#` 注释、空行随意):
61
+
62
+ ```
63
+ worldview 现代
64
+ add-actor 林夏 --role 主角 --description "高三女生,沉默寡言"
65
+ add-actor 陈默 --role 配角 --description "便利店夜班店员"
66
+ add-location 便利店 --description "24h 便利店,冷白光"
67
+ add-episode --title "第一集:相遇"
68
+ synopsis "雨夜便利店里,林夏与夜班店员陈默因一场意外相遇。"
69
+ synopsis ep_001 "林夏雨夜进入便利店,发现陈默似乎认识她。"
70
+ insert ep_001 --location loc_001 --time night --space interior
71
+ insert ep_001/scn_001 --type action --content "雨夜,林夏推门进店。" --emotion "狼狈"
72
+ insert ep_001/scn_001 --type dialogue --content "欢迎光临。" --actor act_002
73
+ ```
74
+
75
+ 动词与单条命令完全一致(参数见 `scriptctl <verb> --help`)。`do` 也读 stdin:`… | scriptctl do -`。
76
+
77
+ > 机器/程序化生成的批量(如 jq 变换查询结果)才用 JSON:`scriptctl actions … --json | jq '[…ops…]' | scriptctl patch -`。手写一律 `do`。
78
+
79
+ ---
80
+
81
+ ## 写到哪里去(落库)
82
+
83
+ `create` 和所有编辑 verb 共用同一套目标解析,三选一:
84
+
85
+ | 目标 | 怎么走 | 说明 |
86
+ |---|---|---|
87
+ | **本地文件** | `--script-path <file>` | 🖥️ **本地电脑首选**。直接读写这一个 JSON,无 store、无 revision。洗稿/对标/转绘、试验都走它 |
88
+ | **DB(项目组)** | `--remote`(沙箱裸命令自动 remote),或 `--project-group-no <no>` | ☁️ **沙箱首选**。每个编辑 op 直接写进 DB 的新 revision,边写边入库 |
89
+ | **本地约定 store** | `--local` | 写 `SCRIPTCTL_OUTPUT_DIR` 下的约定 store |
90
+
91
+ - 🖥️ **本地**:`scriptctl create --script-path new.json` 起本,之后每条编辑都带同一个 `--script-path new.json`;想入库时用 `scriptctl publish`(读某个 `workspace/script.json` 写 store)或直接对 DB 写。
92
+ - ☁️ **沙箱**:直接对 DB 写(`create` → 编辑,不加 flag = 自动 remote),写完就在库里。想先本地攒好再入库,就 `create --script-path draft.json` → 全程 `--script-path draft.json` 编辑,最后把它放进 `workspace/` 用 `scriptctl publish` 入库。
93
+
94
+ ### 两份 script.json(洗稿 / 对标 / 转绘)
95
+
96
+ 同时开着原剧与新剧时,一次只作用于 `--script-path` 指的那份:从原剧读(`summary`/`actions`(带 timestamp)/`states`(带弧线)/`scenes`(带时段)),往新剧写(`create` → `do --apply`)。换文件 = 换 `--script-path` 的值。详见 SKILL.md「两个 script.json」。
97
+
98
+ ---
99
+
100
+ ## 校验语义(重要)
101
+
102
+ 「没填满」不拦路,「结构坏了」才拦:
103
+
104
+ - **只提醒(warning,不影响 `validate` 通过)**:空剧本、空集、空场、场景没地点、资产缺描述。所以你可以先把骨架搭出来、逐步填,中途 `validate` 也能过。
105
+ - **硬错误(拦截)**:悬挂引用、重复 id、非法枚举、缺 id/name、scene_id 不递增、非人类注册成 actor —— 这些会破坏 DB 图结构,必须修。
106
+
107
+ 随时 `scriptctl validate` 看整体,`scriptctl issues --severity warning` 专看「还没填」的清单。
108
+
109
+ ---
110
+
111
+ ## 提醒
112
+
113
+ - 资产 state 判定(哪些进 `states[]`)见 SKILL.md「资产 state 判定」。
114
+ - 非角色发声源(系统/广播/画外/群体)用 `dialogue <at> --kind system|broadcast|offscreen|group [--label ...]`(对白行内联),**不要**注册成 actor。
115
+ - 角色名保持规范:别在名字里塞状态注解(`林夏(受伤)`会被拒),外观档位用 `state-add`。
116
+ - action 情绪放 `emotion` 字段:插入时用 `insert ... --emotion "紧张"`,修改时用 `emotion <ep/scn#idx> "紧张"` / `emotion <ep/scn#idx> --clear`,不要把 `[紧张]` 这类标记塞进 content。
117
+ - 参数 / 退出码以 `scriptctl <cmd> --help` 为准。
@@ -0,0 +1,85 @@
1
+ # ingest workflow — 素材直转入库(文本 / 视频)
2
+
3
+ 适用:有外部素材要**转成 v3 剧本并入库**。统一入口 `scriptctl ingest`,看 source 后缀**自动分流**文本 / 视频;两条管线在同一个 govern 收尾汇合,产出**完全相同的 v3 `script.json`**。
4
+
5
+ ```
6
+ ingest --source-path <file|dir> → view / ingest status → publish
7
+ (抽取,产 workspace/script.json) (自查/看进度) (校验+入库)
8
+ ```
9
+
10
+ - 支持输入:**文本** txt / md / docx;**视频** 单个正片文件或**分集目录**(需 `GEMINI_API_KEY`)。pdf/json/xlsx 会被拒。
11
+ - `ingest` **不入库**:只产 `workspace/script.json`(默认工作区 `workspace`,可 `--workspace-path` 改)。入库是独立的 `publish`。
12
+ - 产物随后就是普通剧本:任何读写动词加 `--script-path workspace/script.json` 即可精修。
13
+
14
+ ## 一、抽取 `ingest`
15
+
16
+ ```bash
17
+ # 文本
18
+ scriptctl ingest --source-path uploads/剧本.docx
19
+ # 视频(分集目录,文件名带集号;单文件也行)
20
+ scriptctl ingest --source-path uploads/正片/
21
+ ```
22
+
23
+ 常用 flag:
24
+ - `--workspace-path <dir>`:工作区根(manifest + 各 pass 目录 + 产物)。默认 `workspace`。
25
+ - `--concurrency <n>`:文本/归并 fanout 并发(文本默认 80,视频默认 30)。
26
+ - `--video-concurrency <n>`:视频上传+转录并发。默认 10。
27
+ - `--fps <n>`:视频采样帧率(>1 解锁亚秒级时间码)。默认 3。
28
+ - `--force`:**同源**重跑,清掉产物重来(**不能换源**)。
29
+
30
+ 管线(了解即可,产物都在 `workspace/`):
31
+ - **文本**:pass1 切块 → pass2 归一化(廉价模型出 markdown,确定性解析,**逐块 checkpoint**)→ pass3 合并 → 组装 `script.initial.json` → pass5 govern(资产 recall/refine → 合并/canonical/裁剪)→ `script.json`(v3 校验)。
32
+ - **视频**:pass1 Gemini 转录 → pass2 解析 → pass3 跨集花名册归并 + 状态归并(**贵,`roster.json`/`context.merged.md` 会复用**)→ pass4 二次画面校正 → pass5 纯剧情发声源审计 → pass6 apply + 汇入同一 govern 收尾。时间戳挂在 `extend.timestamp`。
33
+
34
+ ## 二、看进度 / 自查
35
+
36
+ ```bash
37
+ scriptctl ingest status # 人读:kind / state / 各 pass 进度(transcribe 40/57 (2 failed) ...)
38
+ scriptctl ingest status --json # 机器:.status = {kind,state,phase,passes[],units,validation,errors[]}
39
+ scriptctl view # 生成 workspace/review.html(视频版可点击定位到画面)
40
+ scriptctl summary --script-path workspace/script.json # 抽完当普通剧本读
41
+ ```
42
+
43
+ `state`:`empty`(没开始)/ `incomplete`(在跑或中断、无硬错,重跑续)/ `failed`(有失败单元或校验没过)/ `ready`(`script.json` 生成且 v3 校验通过)。进度**纯从工作区真实文件推导**,不看任何独立状态机文件——不会出现"状态文件说完了但其实没完"。它报的是**管线事实**,不代表"已人工复核"(复核是你的完成门,另算)。
44
+
45
+ ## 三、🔴 抖动与重跑(大规模转剧本的常态)
46
+
47
+ 模型抖动 / 限流 / 单集超时 / 偶发解析失败在几十集体量下**是正常现象,不是 bug**。护栏与恢复:
48
+
49
+ | 报错 | 含义 | 怎么办 |
50
+ |---|---|---|
51
+ | `INGEST_NORMALIZE_INCOMPLETE`(文本)<br>`INGEST_TRANSCRIBE_/CORRECT_/SPEAKER_INCOMPLETE`(视频) | 部分单元失败,留下 `passN/*.error.json` sidecar | **直接重跑 `scriptctl ingest`(同 source、同 workspace)**——从 checkpoint 续,只补失败单元。跑完 `ingest status` 看还剩几个,多跑几次到收敛 |
52
+ | `INGEST_SOURCE_MISMATCH` | 工作区是别的 source 建的 | 换一个新的 `--workspace-path`,**别 `--force` 硬覆盖** |
53
+ | `INGEST_VALIDATION_FAILED` | 最终 v3 校验没过 | 看 `workspace/validation.json` 的具体报错,用读写动词修 `--script-path workspace/script.json` 再 `publish` |
54
+ | 持续 401/403/404 / 模型路由错 | 网关/密钥/模型环境问题(不是抖动,重跑也不好) | 如实告诉用户,别反复空跑、别编造成功。视频需 `GEMINI_API_KEY` |
55
+
56
+ 🔴 **绝不**:清工作区、删 checkpoint、手塞/手改中间文件来"绕过"失败。重跑 = 唯一正解;成功单元不会重跑,贵的花名册归并(`roster.json`)也会复用。
57
+
58
+ ## 四、入库 `publish`
59
+
60
+ ```bash
61
+ scriptctl publish # 沙箱:自动写项目 DB 新 revision(SANDBOX_PROJECT_GROUP_NO 已注入)
62
+ scriptctl publish --local # 本地:写 SCRIPTCTL_OUTPUT_DIR
63
+ scriptctl publish --remote --project-group-no 123 # 显式指定项目组
64
+ ```
65
+
66
+ - `publish` 读 `workspace/script.json`,**再跑一遍 v3 校验**,通过才写 store。
67
+ - 沙箱里转完**必须 publish**,否则后端/前端看不到剧本。
68
+ - `SCRIPT_REVISION_CONFLICT` = DB 被别的 revision 改过:重新拉取当前剧本、合并你的改动,再 publish。
69
+ - publish 幂等:同一份 script 重复 publish 不会产生新 revision(按内容 sha 去重)。
70
+
71
+ ## 五、复杂场景怎么接
72
+
73
+ - **转完先精修再入库**:`ingest` → 用 `--script-path workspace/script.json` 跑读写动词修(改归属、并角色、标龙套、补造型……)→ `publish`。
74
+ - **入库后再改**:`publish` 之后直接对 DB(沙箱裸命令 = remote)继续精修,每次改都是新 revision。
75
+ - **视频复核**:`view` 出的 `review.html` 视频版支持点击台词跳到对应画面时间码,逐场核对发声源/造型。
76
+ - **超大剧集(几十集)**:并发默认已调好;失败就重跑到 `ingest status` 显示 `ready`。花名册归并只跑一次并复用,重跑很快。
77
+ - **归并质量**:跨集角色归并靠文字锚点,相似角色可能误合/合过头。用 `roster.review.md`(视频)核对归并决策;发现误合,入库后用 `merge` 拆分/改名修正。
78
+
79
+ ## 六、抽完后的读写
80
+
81
+ 抽取产物就是标准 v3 剧本,用 SKILL.md 里所有读写能力精修(目标记得指 `--script-path workspace/script.json`,或 publish 后指 DB):
82
+ - `summary` / `episodes` 看整本 + 分集梗概;`actions --in <ep>` 带 timestamp 看节奏。
83
+ - `states <actor>` 看造型弧线;`actors --counts` 看出场频次决定 `importance`。
84
+ - 归属/造型修正:`dialogue <at> --actor/--kind`、`scene-ref <ep/scn> <kind:id> --state`。
85
+ - 梗概/世界观:`synopsis` / `synopsis generate`(LLM 批量)/ `worldview`。
@@ -0,0 +1,59 @@
1
+ # State 引用修复核验(v3)
2
+
3
+ 适用:合并 state、删除 state,或修 `STATE_NOT_MATERIALIZED` / 状态引用错连。重点不是只改 `states[]`,而是确认**引用该状态的场景 cast 引用**都被修好。
4
+
5
+ > v3 里造型(state)只挂在**场景级 cast 引用**上(`scene.actors[]` / `props[]` / `location` 的 `state_id`),**没有** action 级 `state_changes`。所以引用只有一层,比 v2 简单。
6
+
7
+ ## 操作语义
8
+
9
+ scriptctl 没有单独的 `state-merge` 命令;合并 state = "删旧 state 并替换引用":
10
+
11
+ ```bash
12
+ # 合并:把 st_old 的引用全指向 st_keep,再删 st_old(replacement 必须是同一资产已存在的 state_id)
13
+ scriptctl state-delete actor:act_001/st_old --strategy replace --replacement st_keep
14
+
15
+ # 真删除:删 st_old,引用它的场景 ref 的 state_id 被清掉
16
+ scriptctl state-delete actor:act_001/st_old --strategy remove
17
+ ```
18
+
19
+ (`delete actor:act_001/st_old --strategy ...` 是等价写法。)
20
+
21
+ ## 必查流程
22
+
23
+ 1. **操作前反查旧 state**,看谁在引用它:
24
+
25
+ ```bash
26
+ scriptctl refs actor:act_001/st_old
27
+ ```
28
+
29
+ 输出里是各场景 cast 引用(形如 `ep_001/scn_003.actors[0].state_id`)。
30
+
31
+ 2. **合并用 `replace`**,不要先裸删:
32
+
33
+ ```bash
34
+ scriptctl state-delete actor:act_001/st_old --strategy replace --replacement st_keep
35
+ ```
36
+
37
+ 期望:`refs actor:act_001/st_old` 为空;`refs actor:act_001/st_keep` 含原来该保留的场景引用;`scriptctl validate` 通过。
38
+
39
+ 3. **真删除用 `remove`**:
40
+
41
+ ```bash
42
+ scriptctl state-delete actor:act_001/st_old --strategy remove
43
+ ```
44
+
45
+ 期望:引用该 state 的场景 ref 被保留、但 `state_id` 被清(角色仍在场,只是没造型);`refs actor:act_001/st_old` 为空;`validate` 通过。
46
+
47
+ 4. **只想清某场的造型**(不删 state):
48
+
49
+ ```bash
50
+ scriptctl scene-ref ep_001/scn_003 actor:act_001 --clear # 保留在场,清造型
51
+ scriptctl scene-ref ep_001/scn_003 actor:act_001 --remove # 整个移出该场
52
+ ```
53
+
54
+ ## 核验要点
55
+
56
+ - 引用只有一层:`scene.{actors|props}[].state_id` 和 `scene.location.state_id`。`replace`/`remove` 会自动处理这一层。
57
+ - replacement 只能是**同一资产**上已存在的 state_id。
58
+ - 改完固定两步复查:`scriptctl refs actor:<id>/<st>` 为空 + `scriptctl validate` 通过。
59
+ - `STATE_NOT_MATERIALIZED`:某场引用了一个该资产上不存在的 state_id。用 `scriptctl states <asset>` 看合法 state_id,再 `scene-ref <ep/scn> <kind:id> --state <合法 id>` 修正。
@@ -1,25 +0,0 @@
1
- import { type Stage, type StageContext } from "./stage.js";
2
- export interface RunStagesOptions {
3
- force?: boolean;
4
- bypassInternalCaches?: boolean;
5
- only?: Set<string>;
6
- persistScriptAfterStages?: boolean;
7
- }
8
- /**
9
- * Run the pipeline in order. A stage whose output artifact already exists and
10
- * validates is skipped (resume), unless `force` is set or it isn't in `only`.
11
- * Stages with `output: null` always run (they decide their own per-unit reuse).
12
- *
13
- * The StageIncomplete sentinel (raised by episode-synopsis when some episode
14
- * still has unextracted batches) is NOT swallowed — it propagates so the
15
- * orchestrator can emit the INIT INCOMPLETE report + EXIT_RUNTIME, exactly as
16
- * the former god-command did with its early return.
17
- */
18
- export declare function runStages(ctx: StageContext, stages?: readonly Stage[], opts?: RunStagesOptions): Promise<void>;
19
- /**
20
- * Run exactly one stage by name against an existing workspace. Each stage
21
- * rehydrates the prior artifacts it needs from disk (ensure* helpers), so a
22
- * single step can run standalone — `direct run metadata` re-extracts metadata
23
- * for a workspace whose script.initial.json already exists, etc.
24
- */
25
- export declare function runOne(ctx: StageContext, stageName: string): Promise<Stage>;