@lingjingai/scriptctl 0.34.0 → 0.35.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.
@@ -0,0 +1,114 @@
1
+ # 从零原子写 — 你自己写正文,scriptctl 只搭骨架
2
+
3
+ 适用:**没有现成剧本文本**,从零写(手里只有题材 / 灵感 / 集纲)。
4
+
5
+ 核心理念:**写手是你**。你自己想好每一句正文,用原子能力把它灌进结构化剧本里。scriptctl 负责「合法的骨架 + 引用完整性 + 校验」,不负责「写得好不好」。
6
+
7
+ ---
8
+
9
+ ## 端到端流程
10
+
11
+ ```
12
+ create(空白剧本)
13
+ → worldview 设世界观
14
+ → add-actor / add-location / add-prop / add-speaker 注册资产(+ state-add 给外观档位)
15
+ → add-episode 建分集
16
+ → synopsis 写整本 / 分集梗概
17
+ → insert <ep> --location 建场景
18
+ → insert <ep/scn> --type ... --content "正文" [--emotion "情绪"] 逐 action 灌正文
19
+ → state-change / transition / context / dialogue 关系与状态层
20
+ → validate 收尾自查
21
+ ```
22
+
23
+ 资产/分集/场景的 id **自动按序分配**:第一个 actor 是 `act_001`、location 是 `loc_001`、prop 是 `prp_001`、episode 是 `ep_001`、它下面第一场是 `scn_001`……所以你能在后续命令里直接引用这些可预测的 id(也可以 `--id` 显式指定)。
24
+
25
+ ---
26
+
27
+ ## 两种写法
28
+
29
+ ### A. 增量(少量、边想边写):逐条原子 verb
30
+
31
+ ```bash
32
+ scriptctl create --title "夏夜便利店"
33
+ scriptctl worldview 现代
34
+ scriptctl add-actor 林夏 --role 主角 --description "高三女生,沉默寡言"
35
+ scriptctl add-actor 陈默 --role 配角 --description "便利店夜班店员"
36
+ scriptctl add-location 便利店 --description "24h 便利店,冷白光"
37
+ scriptctl add-episode --title "第一集:相遇"
38
+ scriptctl synopsis "雨夜便利店里,林夏与夜班店员陈默因一场意外相遇。"
39
+ scriptctl synopsis ep_001 "林夏雨夜进入便利店,发现陈默似乎认识她。"
40
+ scriptctl insert ep_001 --location loc_001 --time night --space interior # → scn_001
41
+ scriptctl insert ep_001/scn_001 --type action --content "雨夜,林夏推门进店,浑身湿透。" --emotion "狼狈"
42
+ scriptctl insert ep_001/scn_001 --type dialogue --content "欢迎光临。" --actor act_002
43
+ scriptctl insert ep_001/scn_001 --type inner_thought --content "又是他。" --actor act_001
44
+ scriptctl validate
45
+ ```
46
+
47
+ ### B. 整集 / 整本(推荐):一段 `do` 动词脚本,一个事务
48
+
49
+ 一集几十个 action 不要敲几十条命令,也**不要手撸 patch JSON**。写一段 `do` 脚本——**每行就是一条你会敲的命令**(去掉开头的 `scriptctl`),按依赖顺序排好(先建资产、再 `add-episode`、再 `insert` 场景、最后 `insert` action),一次落地。同一段 `do` 内按顺序执行,因此 `add-episode` 产出的 `ep_001` 能被靠后的 `insert` 直接引用。
50
+
51
+ ```bash
52
+ scriptctl create --title "夏夜便利店" # 先有空白剧本
53
+ scriptctl do ep01.txt # 默认 dry-run:内存里跑 + 校验,不落库
54
+ scriptctl do ep01.txt --apply # 确认后真写
55
+ ```
56
+
57
+ `ep01.txt` 形如(每行一条动词,`#` 注释、空行随意):
58
+
59
+ ```
60
+ worldview 现代
61
+ add-actor 林夏 --role 主角 --description "高三女生,沉默寡言"
62
+ add-actor 陈默 --role 配角 --description "便利店夜班店员"
63
+ add-location 便利店 --description "24h 便利店,冷白光"
64
+ add-episode --title "第一集:相遇"
65
+ synopsis "雨夜便利店里,林夏与夜班店员陈默因一场意外相遇。"
66
+ synopsis ep_001 "林夏雨夜进入便利店,发现陈默似乎认识她。"
67
+ insert ep_001 --location loc_001 --time night --space interior
68
+ insert ep_001/scn_001 --type action --content "雨夜,林夏推门进店。" --emotion "狼狈"
69
+ insert ep_001/scn_001 --type dialogue --content "欢迎光临。" --actor act_002
70
+ ```
71
+
72
+ 动词与单条命令完全一致(参数见 `scriptctl <verb> --help`)。`do` 也读 stdin:`… | scriptctl do -`。
73
+
74
+ > 机器/程序化生成的批量(如 jq 变换查询结果)才用 JSON:`scriptctl actions … --json | jq '[…ops…]' | scriptctl patch -`。手写一律 `do`。
75
+
76
+ ---
77
+
78
+ ## 写到哪里去(落库)
79
+
80
+ `create` 和所有编辑 verb 共用同一套目标解析,四选一:
81
+
82
+ | 目标 | 怎么走 | 说明 |
83
+ |---|---|---|
84
+ | **本地文件** | `--script-path <file>` | 🖥️ **本地电脑首选**。直接读写这一个 JSON,无 store、无 revision。洗稿/对标/转绘、试验都走它 |
85
+ | **DB(最终剧本)** | 不加目标 flag(沙箱默认),或 `--project-group-no <no>` | ☁️ **沙箱首选**。每个 op / patch 直接写进 DB 的新 revision,边写边入库,不需要单独的「发布」步骤 |
86
+ | **direct 中间稿** | `--draft` | 写进 `script.initial.json`;想要「先攒后审再入库」就走这条,最后 `scriptctl direct export` 落库 |
87
+
88
+ - 🖥️ **本地**:`scriptctl create --script-path new.json` 起本,之后每条编辑都带同一个 `--script-path new.json`。
89
+ - ☁️ **沙箱**:直接对 DB 写(`create` → 编辑,不加 flag),写完就在库里;想导出前人工自查就 `create --draft` → 全程 `--draft` 编辑 → `direct export`。
90
+
91
+ ### 两份 script.json(洗稿 / 对标 / 转绘)
92
+
93
+ 同时开着原剧与新剧时,一次只作用于 `--script-path` 指的那份:从原剧读(`summary`/`actions`(带 timestamp)/`states`(带弧线)/`scenes`(带时段)),往新剧写(`create` → `do --apply`)。换文件 = 换 `--script-path` 的值。详见 SKILL.md「两个 script.json」。
94
+
95
+ ---
96
+
97
+ ## 校验语义(重要)
98
+
99
+ 「没填满」不拦路,「结构坏了」才拦:
100
+
101
+ - **只提醒(warning,不影响 `validate` 通过)**:空剧本、空集、空场、场景没地点、资产缺描述。所以你可以先把骨架搭出来、逐步填,中途 `validate` 也能过。
102
+ - **硬错误(拦截)**:悬挂引用、重复 id、非法枚举、缺 id/name、scene_id 不递增、非人类注册成 actor —— 这些会破坏 DB 图结构,必须修。
103
+
104
+ 随时 `scriptctl validate` 看整体,`scriptctl issues --severity warning` 专看「还没填」的清单。
105
+
106
+ ---
107
+
108
+ ## 提醒
109
+
110
+ - 资产 state 判定(哪些进 `states[]`)见 SKILL.md「资产 state 判定」。
111
+ - 非人类发声源(系统/广播/道具音)用 `add-speaker --kind system|broadcast|prop|...`,**不要**注册成 actor。
112
+ - 角色名保持规范:别在名字里塞状态注解(`林夏(受伤)`会被拒),外观档位用 `state-add`。
113
+ - action 情绪放 `emotion` 字段:插入时用 `insert ... --emotion "紧张"`,修改时用 `emotion <ep/scn#idx> "紧张"` / `emotion <ep/scn#idx> --clear`,不要把 `[紧张]` 这类标记塞进 content。
114
+ - 参数 / 退出码以 `scriptctl <cmd> --help` 为准。
@@ -0,0 +1,72 @@
1
+ # direct workflow — 已有素材直转
2
+
3
+ 适用:已有完整剧本 / 分镜(docx / pdf / xlsx / md / json)需要结构化入库。
4
+
5
+ ## 流程
6
+
7
+ ```
8
+ direct init <file> → direct review → [<edit verb> --draft / patch --draft] → direct validate → direct export
9
+ ```
10
+
11
+ | 步骤 | 命令 | 说明 |
12
+ |---|---|---|
13
+ | 1. 解析 | `scriptctl direct init --source-path <file>` | 把素材切批、抽取到 `script.initial.json` |
14
+ | 2. 检查 | `scriptctl direct review`(总览)/ `scriptctl direct review --episode 1,15,30`(源文↔抽取对照) | 无参看 curation 决策 + issues(含 batch 抽取失败);带 `--episode` 看按集对照视图 |
15
+ | 3. 修中间稿 | `scriptctl <verb> ... --draft` 单点改,或 `scriptctl patch <file> --draft` 批量 | 加 `--draft` 即指向 `script.initial.json`,不用写路径。改 type / actor / 命名 / 状态等 |
16
+ | 4. 校验 | `scriptctl direct validate` | 跑结构校验 |
17
+ | 5. 入库 | `scriptctl direct export` | 落最终剧本到 DB(只卡 validation passed) |
18
+
19
+ > 编辑 / 查询中间稿:所有顶层 verb(`replace` / `rename` / `actions` / `episodes` / `patch` / ...)都认 `--draft`。例如 `scriptctl actions --in ep_001 --draft`、`scriptctl rename actor:act_001 "陈墨" --draft`。
20
+
21
+ ## 梗概产物
22
+
23
+ direct 流程完成后,最终剧本会带有整本和分集梗概。查看方式:
24
+
25
+ - `scriptctl summary`:看整本 synopsis / theme / logline / style / main_characters
26
+ - `scriptctl episodes`:看每集 synopsis
27
+
28
+ ## 状态 / 续跑 / 重跑(关键)
29
+
30
+ init **可续跑**:已成功的批次跳过(看结果文件在不在),失败的批次留下错误标记、**默认不自动重试**(避免内容过滤这类硬失败无限重打)。状态全部从磁盘派生 —— 清掉 `run_state.json` 也不丢,重跑只会动你点名的集 / 批次,绝不碰其它集。
31
+
32
+ | 想做什么 | 命令 |
33
+ |---|---|
34
+ | 看每集状态(done / ERROR / pending,错误集标出原因 + 批次 key) | `scriptctl direct status` |
35
+ | 续跑(只补从没跑过的批次) | `scriptctl direct init --source-path <file>`(同参数再跑一次) |
36
+ | 只重跑某集 | `scriptctl direct init ... --episodes 27` |
37
+ | 只重跑某些批次 | `scriptctl direct init ... --batches bat_0063,bat_0065` |
38
+ | 重跑所有错误批次 | `scriptctl direct init ... --retry-errors` |
39
+ | 全部重来 | `scriptctl direct init ... --all` |
40
+
41
+ 某集持续 error(典型:内容过滤 `PROVIDER_CONTENT_FILTERED`)的标准处理:`direct status` 定位 → `--episodes <N>` 重跑;仍失败就软化源文本对应那一段再 `--episodes <N>`,或先入库其余集、在最终剧本里用 patch 工具补这一集。🔴 **不要清 `run_state.json` 或手工往 `batch_results/` 塞文件** —— 用 `direct status` + 选择性重跑即可,错误批次默认粘性不会被自动重试。
42
+
43
+ ## export 前的质量自查(强烈建议,非强制)
44
+
45
+ `direct export` 只卡 validation —— passed 就能导出,没有 review 签字闸门。但抽取质量靠 validation 兜不住(它只看结构),导出前**强烈建议**自己走一遍:
46
+
47
+ 1. `direct review`:把 issues 清零(结构性问题 + batch 抽取失败),看 curation 决策是否合理
48
+ 2. 抽样 ≥3 集(开头 / 中段 / 结尾;双语 / 特殊场次必查)跑 `direct review --episode <N>` 做下面的语义 checklist
49
+
50
+ ## review checklist
51
+
52
+ **对白 / 动作**
53
+ - 双语 / gloss / 旁注没被错抽进 content
54
+ - 角色注解前缀(`Sloane(画外音):` 这类)没被当 content
55
+ - 字幕 / 音效 / 屏幕字归 `type=action`;系统 / 广播 / 道具发声归 dialogue + speaker
56
+ - dialogue / inner_thought / action 三型分得对
57
+ - 没漏抽 / 没拆碎 / 没合并对白
58
+ - emotion 进 emotion 字段,不进 content
59
+
60
+ **场景 / 角色**
61
+ - 场景顺序、每场角色与原文一致
62
+ - location_name / location_state 与原文场号标题对应
63
+
64
+ **资产**
65
+ - actors / locations / props 是跨场景可复用的生产资产;单场临时物留在 action 文本
66
+ - 名字是规范实体名(不夹括号 / 状态 / 情绪 / 身份注解)
67
+ - 群体词(众人 / 群众 / 围观者)不进 actors
68
+ - description 不能 LLM 凭空编造
69
+
70
+ ## 阈值
71
+
72
+ 抽样集每集 ≤1 问题 → `--draft` 改掉后即可 export;≥2 个 → `scriptctl direct init --episodes <N>` 重跑该集(或 `--batches <key>` 重跑具体批次)。
@@ -0,0 +1,72 @@
1
+ # State 引用修复核验
2
+
3
+ 适用:合并 state、删除 state,或修 `STATE_NOT_FOUND` / 状态引用错连。重点不是只改 `states[]`,而是确认引用该状态的场和动作都被修好。
4
+
5
+ ## 操作语义
6
+
7
+ scriptctl 没有单独的 `state-merge` 命令;合并 state 用“删除旧 state 并替换引用”表达:
8
+
9
+ ```bash
10
+ scriptctl delete actor:act_001/st_old --strategy replace --replacement st_keep
11
+ ```
12
+
13
+ 删除且不保留引用:
14
+
15
+ ```bash
16
+ scriptctl delete actor:act_001/st_old --strategy remove
17
+ ```
18
+
19
+ 等价别名:`state-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
+ 输出里 `scene_initial_state` 是场景 context 引用,形如 `ep_001/scn_003.context.actors[0]`;`state_change_from` / `state_change_to` 是 action 的 `state_changes` 引用。
30
+
31
+ 2. 合并时用 `replace`,不要先裸删 state:
32
+
33
+ ```bash
34
+ scriptctl delete actor:act_001/st_old --strategy replace --replacement st_keep
35
+ ```
36
+
37
+ 期望结果:
38
+ - `scriptctl refs actor:act_001/st_old` 为空。
39
+ - `scriptctl refs actor:act_001/st_keep` 包含原来应该保留的场景初始状态和 action 状态变化引用。
40
+ - `scriptctl validate` 通过。
41
+
42
+ 3. 真删除时用 `remove`:
43
+
44
+ ```bash
45
+ scriptctl delete actor:act_001/st_old --strategy remove
46
+ ```
47
+
48
+ 期望结果:
49
+ - 场景 context 中引用该 state 的 ref 被保留,但 `state_id` 变成 `null`。
50
+ - 涉及该 state 的 action `state_changes` 被移除,避免留下半截 from/to。
51
+ - `scriptctl refs actor:act_001/st_old` 为空,`scriptctl validate` 通过。
52
+
53
+ 4. 如果只想清某场的初始状态,不要删 state:
54
+
55
+ ```bash
56
+ scriptctl context ep_001/scn_003 actor:act_001 --clear
57
+ ```
58
+
59
+ 如果要移除该角色在某场的引用:
60
+
61
+ ```bash
62
+ scriptctl context ep_001/scn_003 actor:act_001 --remove
63
+ ```
64
+
65
+ ## 状态引用散在两层
66
+
67
+ 改状态引用时两层都要覆盖,`replace`/`remove` 会自动处理,核验时也按这两层看:
68
+
69
+ - 场景初始状态:`scene.context.{actors|locations|props}[].state_id`。
70
+ - 动作状态变化:`action.state_changes[].from_state_id` / `to_state_id`。
71
+
72
+ `replace` 把两层里指向旧 state 的引用全改成 replacement 再删旧定义;`remove` 把 context ref 的 `state_id` 清成 `null` 并删掉碰到旧 state 的 action state_changes。replacement 只能是**同一资产**上已存在的 state_id。改完用 `scriptctl refs actor:<id>/<st>` 复查为空、`scriptctl validate` 通过。