@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.
- package/README.md +9 -0
- package/changes/0.35.0.md +22 -0
- package/changes/unreleased.md +3 -5
- package/dist/bin.js +0 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/domain/direct-core.js +43 -45
- package/dist/domain/direct-core.js.map +1 -1
- package/dist/help-text.js +31 -7
- package/dist/help-text.js.map +1 -1
- package/dist/usecases/script/actions.js +1 -1
- package/dist/usecases/script/actions.js.map +1 -1
- package/dist/usecases/script/actors.js +1 -0
- package/dist/usecases/script/actors.js.map +1 -1
- package/dist/usecases/script/assets.js +2 -1
- package/dist/usecases/script/assets.js.map +1 -1
- package/dist/usecases/script/lib.d.ts +11 -0
- package/dist/usecases/script/lib.js +93 -3
- package/dist/usecases/script/lib.js.map +1 -1
- package/dist/usecases/script/locations.js +1 -0
- package/dist/usecases/script/locations.js.map +1 -1
- package/dist/usecases/script/props.js +1 -0
- package/dist/usecases/script/props.js.map +1 -1
- package/dist/usecases/script/states.js +58 -13
- package/dist/usecases/script/states.js.map +1 -1
- package/dist/usecases/script/summary.js +8 -0
- package/dist/usecases/script/summary.js.map +1 -1
- package/package.json +17 -14
- package/scripts/install-skill.mjs +120 -0
- package/skills/scriptctl/SKILL.md +273 -0
- package/skills/scriptctl/references/atomic-write-workflow.md +114 -0
- package/skills/scriptctl/references/direct-workflow.md +72 -0
- package/skills/scriptctl/references/state-reference-repair.md +72 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"props.js","sourceRoot":"","sources":["../../../src/usecases/script/props.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,OAAO,GAWR,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAIL,eAAe,GAGhB,MAAM,iCAAiC,CAAC;AAuBzC,OAAO,EAKL,KAAK,GACN,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,iBAAiB,GAElB,MAAM,cAAc,CAAC;AACtB,OAAO,EACwS,WAAW,EAAqI,gBAAgB,GAC9c,MAAM,UAAU,CAAC;AAElB,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAU;IAC3C,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE;QACrD,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;QAC5B,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE;QAChC,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"props.js","sourceRoot":"","sources":["../../../src/usecases/script/props.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,OAAO,GAWR,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAIL,eAAe,GAGhB,MAAM,iCAAiC,CAAC;AAuBzC,OAAO,EAKL,KAAK,GACN,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,iBAAiB,GAElB,MAAM,cAAc,CAAC;AACtB,OAAO,EACwS,WAAW,EAAqI,gBAAgB,GAC9c,MAAM,UAAU,CAAC;AAElB,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAU;IAC3C,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE;QACrD,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;QAC5B,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE;QAChC,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;KAChC,CAAC,CAAC;IACH,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -1,41 +1,70 @@
|
|
|
1
1
|
import { CliError, EXIT_INPUT, EXIT_OK, EXIT_USAGE, } from "../../common.js";
|
|
2
2
|
import { parseAssetTarget } from "../../domain/script/address.js";
|
|
3
|
+
import { collectStateRefs } from "../../domain/script/refs.js";
|
|
4
|
+
import { ASSET_ID_PREFIX } from "../../domain/script/lookups.js";
|
|
3
5
|
import { asList, strOf } from "../../domain/script/shared.js";
|
|
4
6
|
import { loadScriptForEdit } from "./session.js";
|
|
5
|
-
import { buildReport } from "./lib.js";
|
|
7
|
+
import { buildReport, compactEpisodeRanges } from "./lib.js";
|
|
6
8
|
const ASSET_KEYS = {
|
|
7
9
|
actor: ["actors", "actor_id", "actor_name"],
|
|
8
10
|
location: ["locations", "location_id", "location_name"],
|
|
9
11
|
prop: ["props", "prop_id", "prop_name"],
|
|
10
12
|
};
|
|
13
|
+
// Reverse of ASSET_ID_PREFIX: an id prefix (act / loc / prp) → asset kind. Lets
|
|
14
|
+
// `states act_001` resolve the kind from the id alone, so callers holding a bare
|
|
15
|
+
// id from another query never need to re-tag it as `actor:act_001`.
|
|
16
|
+
const PREFIX_TO_KIND = new Map(Object.entries(ASSET_ID_PREFIX).map(([kind, prefix]) => [prefix, kind]));
|
|
17
|
+
// Resolve the states address to [kind, id]. Accepts both the tagged asset form
|
|
18
|
+
// (`actor:act_001`) and a bare id (`act_001`), inferring the kind from the id
|
|
19
|
+
// prefix in the bare case.
|
|
20
|
+
function resolveStatesTarget(raw) {
|
|
21
|
+
if (raw.includes(":")) {
|
|
22
|
+
const [kind, id] = parseAssetTarget(raw);
|
|
23
|
+
return [kind, id];
|
|
24
|
+
}
|
|
25
|
+
const prefix = raw.includes("_") ? raw.slice(0, raw.indexOf("_")) : "";
|
|
26
|
+
const kind = PREFIX_TO_KIND.get(prefix);
|
|
27
|
+
if (!kind) {
|
|
28
|
+
throw new CliError("VERB BLOCKED: states address invalid", "states address invalid.", {
|
|
29
|
+
exitCode: EXIT_USAGE,
|
|
30
|
+
required: ["<asset-addr>: act_001 / loc_001 / prp_001 (or actor:act_001)"],
|
|
31
|
+
received: [raw],
|
|
32
|
+
nextSteps: ["Pass an asset id whose prefix is act_ / loc_ / prp_, or tag it as actor|location|prop:<id>."],
|
|
33
|
+
errorCode: "STATE_ADDR_INVALID",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return [kind, raw];
|
|
37
|
+
}
|
|
11
38
|
// `states <asset-addr>` — the read half of the state address space. Lists every
|
|
12
|
-
// state on one asset with its state_id ↔ name ↔ description,
|
|
13
|
-
// (
|
|
39
|
+
// state on one asset with its state_id ↔ name ↔ description, plus the episode
|
|
40
|
+
// arc (which episodes / how many scenes each state is bound in), so editing a
|
|
41
|
+
// state or reading a character's visual arc never requires grepping raw JSON.
|
|
14
42
|
export async function commandStates(opts) {
|
|
15
43
|
const args = asList(opts["_args"]);
|
|
16
44
|
if (args.length < 1) {
|
|
17
45
|
throw new CliError("VERB BLOCKED: states address missing", "states address missing.", {
|
|
18
46
|
exitCode: EXIT_USAGE,
|
|
19
|
-
required: ["<asset-addr> (
|
|
47
|
+
required: ["<asset-addr> (act_001 / loc_001 / prp_001, or actor:act_001)"],
|
|
20
48
|
received: ["<empty>"],
|
|
21
|
-
nextSteps: ["Example: scriptctl states
|
|
49
|
+
nextSteps: ["Example: scriptctl states act_001"],
|
|
22
50
|
errorCode: "ARGS_MISSING",
|
|
23
51
|
});
|
|
24
52
|
}
|
|
25
|
-
// A state address (actor:act_001/st_010) would otherwise be
|
|
26
|
-
//
|
|
27
|
-
//
|
|
53
|
+
// A state address (actor:act_001/st_010 or act_001/st_010) would otherwise be
|
|
54
|
+
// mis-parsed as an asset id and mis-report "asset not found". Catch the
|
|
55
|
+
// trailing /state_id here and point at the right shape.
|
|
28
56
|
const raw = strOf(args[0]);
|
|
29
|
-
if (raw.includes("
|
|
57
|
+
if (raw.includes("/")) {
|
|
58
|
+
const assetPart = raw.slice(0, raw.indexOf("/"));
|
|
30
59
|
throw new CliError("VERB BLOCKED: states wants an asset address", "states wants an asset address.", {
|
|
31
60
|
exitCode: EXIT_USAGE,
|
|
32
|
-
required: ["<asset-addr> without a /state_id (e.g.
|
|
61
|
+
required: ["<asset-addr> without a /state_id (e.g. act_001)"],
|
|
33
62
|
received: [args[0]],
|
|
34
|
-
nextSteps: [`Drop the /state_id: scriptctl states ${
|
|
63
|
+
nextSteps: [`Drop the /state_id: scriptctl states ${assetPart}`],
|
|
35
64
|
errorCode: "STATE_ADDR_NOT_ASSET",
|
|
36
65
|
});
|
|
37
66
|
}
|
|
38
|
-
const [kind, id] =
|
|
67
|
+
const [kind, id] = resolveStatesTarget(raw);
|
|
39
68
|
const [collection, idKey, nameKey] = ASSET_KEYS[kind];
|
|
40
69
|
const session = await loadScriptForEdit(opts);
|
|
41
70
|
const asset = asList(session.script[collection]).find((a) => strOf(a[idKey]) === id);
|
|
@@ -72,7 +101,23 @@ export async function commandStates(opts) {
|
|
|
72
101
|
flags.push("DUP_NAME");
|
|
73
102
|
const descPart = desc ? `desc="${desc}"` : "description=missing";
|
|
74
103
|
const flagPart = flags.length ? ` [${flags.join(",")}]` : "";
|
|
75
|
-
|
|
104
|
+
// Episode arc: where this state is actually bound (scene context +
|
|
105
|
+
// action state_changes). collectStateRefs returns refs whose `location`
|
|
106
|
+
// starts with `ep_NNN/scn_NNN`; fold those into episode ranges + scene count.
|
|
107
|
+
const refs = collectStateRefs(session.script, kind, id, sid);
|
|
108
|
+
const episodes = new Set();
|
|
109
|
+
const scenes = new Set();
|
|
110
|
+
for (const ref of refs) {
|
|
111
|
+
const m = /^(ep_\d+)\/(scn_\d+)/.exec(strOf(ref["location"]));
|
|
112
|
+
if (!m)
|
|
113
|
+
continue;
|
|
114
|
+
episodes.add(m[1]);
|
|
115
|
+
scenes.add(`${m[1]}/${m[2]}`);
|
|
116
|
+
}
|
|
117
|
+
const arc = episodes.size > 0
|
|
118
|
+
? `episodes=${compactEpisodeRanges(episodes)} (${episodes.size} eps, ${scenes.size} scenes)`
|
|
119
|
+
: "episodes=none";
|
|
120
|
+
lines.push(`${kind}:${id}/${sid} ${sname} ${descPart} ${arc}${flagPart}`);
|
|
76
121
|
}
|
|
77
122
|
if (lines.length === 0)
|
|
78
123
|
lines.push("(no states)");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"states.js","sourceRoot":"","sources":["../../../src/usecases/script/states.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,OAAO,EACP,UAAU,GAEX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,MAAM,EAAa,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"states.js","sourceRoot":"","sources":["../../../src/usecases/script/states.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,14 @@ export async function commandSummary(opts) {
|
|
|
22
22
|
`props: ${asList(script["props"]).length}`,
|
|
23
23
|
`speakers: ${asList(script["speakers"]).length}`,
|
|
24
24
|
];
|
|
25
|
+
const worldview = strOf(script["worldview"]).trim();
|
|
26
|
+
if (worldview) {
|
|
27
|
+
const worldviewRaw = strOf(script["worldview_raw"]).trim();
|
|
28
|
+
lines.push(worldviewRaw && worldviewRaw !== worldview ? `worldview: ${worldview} (${worldviewRaw})` : `worldview: ${worldview}`);
|
|
29
|
+
}
|
|
30
|
+
const style = strOf(script["style"]).trim();
|
|
31
|
+
if (style)
|
|
32
|
+
lines.push(`style: ${style}`);
|
|
25
33
|
const logline = strOf(script["logline"]).trim();
|
|
26
34
|
if (logline)
|
|
27
35
|
lines.push(`logline: ${logline}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../../src/usecases/script/summary.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../../src/usecases/script/summary.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingjingai/scriptctl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "剧本阶段统一 CLI:直转外部素材到 script.json,以及当前最终剧本的读取、校验、原子编辑与批量 patch 精修。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,23 +11,13 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
13
|
"changes",
|
|
14
|
+
"skills",
|
|
15
|
+
"scripts/install-skill.mjs",
|
|
14
16
|
"README.md"
|
|
15
17
|
],
|
|
16
18
|
"engines": {
|
|
17
19
|
"node": ">=20"
|
|
18
20
|
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsc",
|
|
21
|
-
"clean": "rm -rf dist",
|
|
22
|
-
"prepublishOnly": "pnpm run clean && pnpm run build",
|
|
23
|
-
"pretest": "pnpm run build",
|
|
24
|
-
"test": "vitest run",
|
|
25
|
-
"pretest:coverage": "pnpm run build",
|
|
26
|
-
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov --report-dir=coverage vitest run",
|
|
27
|
-
"test:watch": "vitest",
|
|
28
|
-
"typecheck": "tsc --noEmit",
|
|
29
|
-
"surface:snapshot": "node scripts/scriptctl-surface-snapshot.mjs"
|
|
30
|
-
},
|
|
31
21
|
"dependencies": {
|
|
32
22
|
"@anthropic-ai/sdk": "^0.32.1",
|
|
33
23
|
"commander": "^12.1.0",
|
|
@@ -51,5 +41,18 @@
|
|
|
51
41
|
"license": "MIT",
|
|
52
42
|
"publishConfig": {
|
|
53
43
|
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc",
|
|
47
|
+
"clean": "rm -rf dist",
|
|
48
|
+
"postinstall": "node scripts/install-skill.mjs",
|
|
49
|
+
"install-skill": "node scripts/install-skill.mjs",
|
|
50
|
+
"pretest": "pnpm run build",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"pretest:coverage": "pnpm run build",
|
|
53
|
+
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov --report-dir=coverage vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"surface:snapshot": "node scripts/scriptctl-surface-snapshot.mjs"
|
|
54
57
|
}
|
|
55
|
-
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// postinstall: 把打包进 npm 包的 scriptctl skill 装到用户根目录下的三处 skill 目录
|
|
3
|
+
// (Claude Code / agents / codex),并把「本次安装对应的 scriptctl 版本号」戳进每份
|
|
4
|
+
// SKILL.md 的 frontmatter + 一个 .scriptctl-version 标记文件里。
|
|
5
|
+
//
|
|
6
|
+
// 设计要点:
|
|
7
|
+
// - 永不让安装失败:全程 try/catch,任何异常只打印警告并以 0 退出(skill 装不上不该
|
|
8
|
+
// 拖垮 `npm i -g scriptctl` 本身)。
|
|
9
|
+
// - 开发态跳过:源码检出(存在 src/)不是「已发布的包」,跳过,免得 dev `pnpm install`
|
|
10
|
+
// 覆盖你本地手改的 skill。
|
|
11
|
+
// - 可关闭:SCRIPTCTL_SKIP_SKILL_INSTALL=1 或 SCRIPTCTL_SKILL_INSTALL=0。
|
|
12
|
+
// - 幂等:每次把目标 <dir>/scriptctl 清空重写,只管这一个子目录,不碰旁边的其它 skill。
|
|
13
|
+
// - 手动补装:node <pkg>/scripts/install-skill.mjs(同一逻辑,可随时重跑)。
|
|
14
|
+
|
|
15
|
+
import * as fs from "node:fs";
|
|
16
|
+
import * as os from "node:os";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
|
|
20
|
+
const SKILL_NAME = "scriptctl";
|
|
21
|
+
// 三处目标 skill 根目录(相对用户家目录)。每处装成 <root>/scriptctl 子目录。
|
|
22
|
+
const TARGET_SKILL_ROOTS = [
|
|
23
|
+
path.join(".claude", "skills"),
|
|
24
|
+
path.join(".agents", "skills"),
|
|
25
|
+
path.join(".codex", "skills"),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function log(msg) {
|
|
29
|
+
process.stdout.write(`[scriptctl] ${msg}\n`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function optedOut() {
|
|
33
|
+
const skip = process.env.SCRIPTCTL_SKIP_SKILL_INSTALL;
|
|
34
|
+
const flag = process.env.SCRIPTCTL_SKILL_INSTALL;
|
|
35
|
+
return skip === "1" || skip === "true" || flag === "0" || flag === "false";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// package root = 本脚本上一级的上一级(<root>/scripts/install-skill.mjs)。
|
|
39
|
+
function packageRoot() {
|
|
40
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readVersion(root) {
|
|
44
|
+
try {
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
|
|
46
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
47
|
+
} catch {
|
|
48
|
+
return "0.0.0";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 把源 SKILL.md 里的 `scriptctl_version:` frontmatter 值改成实际安装的版本,让装好的
|
|
53
|
+
// skill 一眼看清对应的是哪个 scriptctl。没有该行时不硬插(保持 frontmatter 干净)。
|
|
54
|
+
function stampVersion(skillMdPath, version) {
|
|
55
|
+
try {
|
|
56
|
+
const text = fs.readFileSync(skillMdPath, "utf-8");
|
|
57
|
+
if (!/^scriptctl_version:/m.test(text)) return;
|
|
58
|
+
const stamped = text.replace(/^scriptctl_version:.*$/m, `scriptctl_version: "${version}"`);
|
|
59
|
+
fs.writeFileSync(skillMdPath, stamped, "utf-8");
|
|
60
|
+
} catch {
|
|
61
|
+
/* 戳版本失败不影响 skill 可用 */
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function installOne(skillSrc, homeDir, skillRootRel, version) {
|
|
66
|
+
const target = path.join(homeDir, skillRootRel, SKILL_NAME);
|
|
67
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
68
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
69
|
+
fs.cpSync(skillSrc, target, { recursive: true });
|
|
70
|
+
stampVersion(path.join(target, "SKILL.md"), version);
|
|
71
|
+
fs.writeFileSync(path.join(target, ".scriptctl-version"), `${version}\n`, "utf-8");
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function main() {
|
|
76
|
+
if (optedOut()) {
|
|
77
|
+
log("SCRIPTCTL_SKIP_SKILL_INSTALL set — skipping skill install.");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const root = packageRoot();
|
|
82
|
+
|
|
83
|
+
// 开发态守卫:已发布的包不含 src/(package.json files 未包含它)。存在 src/ = 源码
|
|
84
|
+
// 检出,跳过,免得覆盖开发者本地 skill。
|
|
85
|
+
if (fs.existsSync(path.join(root, "src"))) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const skillSrc = path.join(root, "skills", SKILL_NAME);
|
|
90
|
+
if (!fs.existsSync(path.join(skillSrc, "SKILL.md"))) {
|
|
91
|
+
log(`skill source not found at ${skillSrc} — skipping.`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const homeDir = os.homedir();
|
|
96
|
+
if (!homeDir) {
|
|
97
|
+
log("home directory unresolved — skipping skill install.");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const version = readVersion(root);
|
|
102
|
+
const installed = [];
|
|
103
|
+
for (const rel of TARGET_SKILL_ROOTS) {
|
|
104
|
+
try {
|
|
105
|
+
installed.push(installOne(skillSrc, homeDir, rel, version));
|
|
106
|
+
} catch (exc) {
|
|
107
|
+
log(`could not install skill into ${rel}: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (installed.length > 0) {
|
|
111
|
+
log(`installed ${SKILL_NAME} skill (v${version}) → ${installed.join(", ")}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
main();
|
|
117
|
+
} catch (exc) {
|
|
118
|
+
// 兜底:任何未预期异常都不该让 npm install 失败。
|
|
119
|
+
log(`skill install skipped: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
120
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scriptctl
|
|
3
|
+
description: "剧本 script.json 的读写 CLI:从零创建(create + add-actor/location/prop/episode)、查询(集/场/action/资产/状态/校验/时间戳/出场统计)、原子编辑(台词/类型/归属/state/资产)、选择器批量精修(一条命令扫一片,读写共享 --in/谓词)、动词脚本 do(异构批量一事务)、结构调整(插/删/移/分/合)、入库导出。本地电脑上默认直接读写本地 script.json(--script-path);沙箱里默认读写项目 DB。剧本阶段做任何剧本相关读写、以及洗稿/对标/转绘里操作原剧与新剧两个 script.json 之前都应加载。正文由你用原子能力写入。"
|
|
4
|
+
version: "0.18.0"
|
|
5
|
+
scriptctl_version: "0.35.0"
|
|
6
|
+
author: "official"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# scriptctl
|
|
10
|
+
|
|
11
|
+
`scriptctl` 读写结构化剧本 `script.json`:资产(人物/场景/道具/发声源)、分集 → 场景 → action、状态与引用完整性、校验。**每次调用只作用于一份剧本**(由目标 flag 选定),所以「换剧本 = 换目标」。
|
|
12
|
+
|
|
13
|
+
## 先定目标:作用在哪份剧本上
|
|
14
|
+
|
|
15
|
+
`scriptctl` 每条命令都要知道读写哪份剧本,四选一(互斥):
|
|
16
|
+
|
|
17
|
+
| 目标 | flag | 用在 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **本地文件** | `--script-path <file>` | 🖥️ **本地电脑首选**。直接读写这一个 JSON,无 store、无 revision。洗稿/对标/转绘、试验、离线全走它 |
|
|
20
|
+
| direct 中间稿 | `--draft` | 指向 `script.initial.json`(direct 抽取产物);不用写路径 |
|
|
21
|
+
| 项目 DB(最终剧本) | `--remote` / `--project-group-no <no>` | ☁️ **沙箱首选**。每次写进 DB 新 revision,边写边入库 |
|
|
22
|
+
| 本地约定 store | `--local` | `output/` 目录下的约定 store |
|
|
23
|
+
|
|
24
|
+
- 🖥️ **本地电脑上**:几乎每条命令都带 `--script-path <file>`。下文示例为简洁常省略它,实际本地使用请补上。
|
|
25
|
+
- ☁️ **沙箱里**:`SANDBOX_PROJECT_GROUP_NO` 已注入时,裸命令自动指向该项目 DB,无需 flag。
|
|
26
|
+
- 不给任何目标、又不在沙箱:CLI 拒绝猜测并报错。选一个目标。
|
|
27
|
+
|
|
28
|
+
### 两个 script.json:洗稿 / 对标 / 转绘
|
|
29
|
+
|
|
30
|
+
洗稿、对标仿写、转绘(换地区/换壳)里常常**同时开着原剧和新剧两份 script.json**:从原剧读结构与节奏,往新剧写。因为每条命令只作用于 `--script-path` 指的那一份,**换文件就是换这个 flag 的值**:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ORIG=原剧/script.json
|
|
34
|
+
NEW=新剧/script.json
|
|
35
|
+
|
|
36
|
+
# 读原剧(了解要对标什么)
|
|
37
|
+
scriptctl summary --script-path "$ORIG"
|
|
38
|
+
scriptctl actions --in ep_001 --script-path "$ORIG" # 带 timestamp 列,看节奏
|
|
39
|
+
scriptctl states act_001 --script-path "$ORIG" # 看人物状态弧线
|
|
40
|
+
|
|
41
|
+
# 建/写新剧(把改写后的正文灌进去)
|
|
42
|
+
scriptctl create --title "新剧名" --script-path "$NEW"
|
|
43
|
+
scriptctl do ep01.txt --apply --script-path "$NEW"
|
|
44
|
+
|
|
45
|
+
# 逐场对照:同一集在两份里并排看
|
|
46
|
+
scriptctl scenes --in ep_003 --script-path "$ORIG"
|
|
47
|
+
scriptctl scenes --in ep_003 --script-path "$NEW"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- 一次只碰一份。要「从 A 抄到 B」就是:`--script-path A` 读出来 → 你决定改法 → `--script-path B` 写进去。
|
|
51
|
+
- 事件级对齐:原剧 `actions` 的 `timestamp` 列 + `scenes` 的 `t=t_start..t_end` 给出每段的时间跨度;对标时按这个跨度切新剧的段落。
|
|
52
|
+
- 人物状态弧线:`states <actor>` 每个状态末尾的 `episodes=<区间>` 告诉你某个造型/形态在原剧哪些集出现,转绘时据此规划新剧的状态。
|
|
53
|
+
|
|
54
|
+
## 能干什么
|
|
55
|
+
|
|
56
|
+
### 读(永远只读、永远安全)
|
|
57
|
+
|
|
58
|
+
| 想做什么 | 命令 |
|
|
59
|
+
|---|---|
|
|
60
|
+
| **了解剧情 / 全貌**:讲什么(synopsis)、世界观(worldview)、风格(style)、一句话(logline)、主旨(theme)、主角(main_characters)+ 标题 / 集数 / 场数 / 资产数("这剧本是关于什么的"先跑这个) | `scriptctl summary` |
|
|
61
|
+
| 每集字数 / 场数 / action 数 **+ 每集剧情梗概** | `scriptctl episodes` |
|
|
62
|
+
| 找字数超 1000 的集 | `scriptctl episodes --min-chars 1000` |
|
|
63
|
+
| 看某集的所有场(含 `t=t_start..t_end` 时段) | `scriptctl scenes --in ep_007` |
|
|
64
|
+
| 看某场的资产 + action 数 | `scriptctl scenes --in ep_007/scn_005` |
|
|
65
|
+
| **按正文搜某句话定位 action**(最常用) | `scriptctl actions --grep "<片段>"` |
|
|
66
|
+
| 列某集所有 action(TSV 含 **timestamp** / 正文 / 情绪) | `scriptctl actions --in ep_001` |
|
|
67
|
+
| 看单条 action 正文 | `scriptctl actions --in ep_001/scn_001#3` |
|
|
68
|
+
| action 多维过滤 | `scriptctl actions --in ep_001 --type dialogue --actor act_001` |
|
|
69
|
+
| 某角色出现的所有场 | `scriptctl scenes --has-actor act_001` |
|
|
70
|
+
| **列某资产的所有 state + 状态弧线**(拿 state_id↔名↔描述↔出现在哪些集,改 state 前必查) | `scriptctl states act_001`(裸 id 即可,也认 `actor:act_001`) |
|
|
71
|
+
| 资产**出场统计**(几场 / 几集,压成集区间) | `scriptctl actors --counts` / `locations --counts` / `props --counts` |
|
|
72
|
+
| 某资产 / 状态 / speaker 反查 | `scriptctl refs actor:act_001` |
|
|
73
|
+
| 当前校验问题 | `scriptctl issues --severity error` |
|
|
74
|
+
| 跑校验 | `scriptctl validate` |
|
|
75
|
+
|
|
76
|
+
**读侧新特性(0.35.0 起)**——洗稿/对标/转绘的核心读法:
|
|
77
|
+
|
|
78
|
+
- `actions` TSV 列:`addr timestamp type delivery speaker actor emotion content`。`timestamp` 是该 action 的 `extend.timestamp`(集内 `M:SS`,无则空)。事件跨度 = 首末 action 时间戳之差。
|
|
79
|
+
- `scenes` 行尾 `t=<t_start>..<t_end>`:该场首/末带时间戳 action 的时间。
|
|
80
|
+
- `states <asset>`:接受裸 id(`act_001`/`loc_001`/`prp_001`);每个状态末尾 `episodes=<区间> (N eps, M scenes)` 是该状态的**集弧线**(scene context + action state_changes 里被绑定的位置)。从不绑定的状态显示 `episodes=none`。
|
|
81
|
+
- `actors|locations|props|assets --counts`:追加 `scenes=N episodes_count=M episodes=<区间>`(按 scene context 出场统计)。默认不带 `--counts`,保持输出精简。
|
|
82
|
+
- `summary`:多吐 `worldview`(有 `worldview_raw` 且不同则括号附带)与 `style`。
|
|
83
|
+
|
|
84
|
+
### 写(单点:第一个位置参数是 address。多点:见下「选择器批量」)
|
|
85
|
+
|
|
86
|
+
| 想做什么 | 命令 |
|
|
87
|
+
|---|---|
|
|
88
|
+
| 改 action 正文(替换 / 删一段) | `scriptctl replace <ep/scn#idx> --from X --to Y`(加 `--regex` 走正则) |
|
|
89
|
+
| 改 action type | `scriptctl type <ep/scn#idx> <dialogue\|action\|inner_thought>` |
|
|
90
|
+
| 改 action 归属角色 | `scriptctl actor <ep/scn#idx> <act_id\|none>` |
|
|
91
|
+
| 改 / 清 action 情绪 | `scriptctl emotion <ep/scn#idx> "紧张"` / `scriptctl emotion <ep/scn#idx> --clear` |
|
|
92
|
+
| 加 state 变化 | `scriptctl state-change <ep/scn#idx> actor:<id> --to <state>` |
|
|
93
|
+
| 清 state 变化 | `scriptctl state-change <ep/scn#idx> actor:<id> --clear` |
|
|
94
|
+
| action 转对白 | `scriptctl dialogue <ep/scn#idx> --speakers spk_001` |
|
|
95
|
+
| 改资产名 / 描述 / 别名 / role | `scriptctl rename actor:<id> "X"` / `describe` / `alias --add X` / `role <主角\|配角>` |
|
|
96
|
+
| 给资产加 state | `scriptctl state-add actor:<id> "震惊"` |
|
|
97
|
+
| **改 / 删某个 state** | `scriptctl rename actor:<id>/<st_id> "新名"` / `scriptctl delete actor:<id>/<st_id> --strategy remove`(地址带 `/st_id` 即作用于 state;`state-rename` / `state-delete` 是等价别名;合并/删除 state 的引用修复核验见 [references/state-reference-repair.md](references/state-reference-repair.md)) |
|
|
98
|
+
| 改 scene 资产 ref | `scriptctl context <ep/scn> actor:<id> --state st_X \| --clear \| --remove` |
|
|
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]` / `insert <ep> --location <id>` |
|
|
103
|
+
| 移动 / 分场 | `scriptctl move <addr> <to>` / `split <ep/scn> --at <idx>` |
|
|
104
|
+
| 设整本 / 分集剧情梗概 | `scriptctl synopsis "整本梗概"` / `scriptctl synopsis ep_001 "本集梗概"` |
|
|
105
|
+
| 设世界观 | `scriptctl worldview <现代\|古代历史\|...>` |
|
|
106
|
+
| 新建 speaker | `scriptctl add-speaker --kind X --name Y` |
|
|
107
|
+
|
|
108
|
+
### 选择器批量(一条命令扫一片 —— 读写共享同一套选择语法)
|
|
109
|
+
|
|
110
|
+
把单点写动词的「address」换成一个**选择**,它就对**所有命中的 action** 一次性改。`replace / type / actor / emotion / state-change / transition` 都支持。
|
|
111
|
+
|
|
112
|
+
- **选范围 `--in`**:`ep_003..ep_007`(集区间)/ `ep/scn` 区间 / 逗号多区间 / `'*'`(全本)。
|
|
113
|
+
- **加谓词**(和 `actions` 查询**完全一样**的 flag,AND 组合):`--type` `--actor` `--speaker` `--grep /re/` `--has state-changes|transition|lines`。
|
|
114
|
+
- 🔴 **护栏:多点写默认 dry-run**(回显命中数 + blast-radius,不落盘),加 **`--apply`** 才真写。单点(给 address)仍直写。
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 清全本所有 action 开头的 [xxx] 标记:一条正则扫全本
|
|
118
|
+
scriptctl replace --in '*' --regex --from '^\s*\[[^\]]*\]\s*' --to '' # 先看命中
|
|
119
|
+
scriptctl replace --in '*' --regex --from '^\s*\[[^\]]*\]\s*' --to '' --apply # 落地
|
|
120
|
+
# 第3-7集里某 speaker 的对白统一改归属(谓词直达,无需先查地址)
|
|
121
|
+
scriptctl actor --in ep_003..ep_007 --speaker spk_001 act_009 --apply
|
|
122
|
+
# 第3-7集里某 speaker 的对白统一标情绪
|
|
123
|
+
scriptctl emotion --in ep_003..ep_007 --speaker spk_001 "紧张" --apply
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
> 先 discover 后 mutate 用**同一套 flag**:`scriptctl actions --in … --speaker …` 查出来是哪些,换个动词就改哪些。`actions --json` 带结构化 `rows[]`(含独立 `addr` / `timestamp` / `emotion` 字段),`actions … --format addr` 直接吐裸地址流喂 `xargs`。
|
|
127
|
+
|
|
128
|
+
### 异构批量 `do`(一组各不相同的改动 = 一段动词脚本 = 一个事务)
|
|
129
|
+
|
|
130
|
+
每行就是一条**去掉 `scriptctl` 的命令**,`#` 注释、空行随意;整段全成或全不成。比手写 patch JSON 省心(动词原样,不猜字段名)。
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
scriptctl do edits.txt # 默认 dry-run(预演 + 校验,不写)
|
|
134
|
+
scriptctl do edits.txt --apply # 一次性原子落盘
|
|
135
|
+
cat edits.txt <<'EOF'
|
|
136
|
+
replace ep_001/scn_001#3 --from "台下掌声雷动" --to ""
|
|
137
|
+
type ep_001/scn_002#1 dialogue
|
|
138
|
+
merge actor:act_005 --into actor:act_001
|
|
139
|
+
insert ep_001/scn_006 --type action --content "暗夜,街道空无一人。"
|
|
140
|
+
emotion ep_001/scn_001#3 --clear
|
|
141
|
+
EOF
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`do` 也读 stdin(`scriptctl do -`)。结构性动词(insert/delete/move/split/merge)**只能**走单点或 `do`,不进选择器批量(索引会漂移)。目标 flag(如 `--script-path`)加在 `do` 命令本身上,整段脚本共用同一目标。
|
|
145
|
+
|
|
146
|
+
### 建(从零起一本剧本,不要 address,第一个位置参数是名字)
|
|
147
|
+
|
|
148
|
+
| 想做什么 | 命令 |
|
|
149
|
+
|---|---|
|
|
150
|
+
| 建空白剧本(schema v2 骨架) | `scriptctl create [--title X]` |
|
|
151
|
+
| 加人物 | `scriptctl add-actor "林夏" [--role 主角\|配角] [--description X] [--alias X]` |
|
|
152
|
+
| 加场景地点 | `scriptctl add-location "便利店" [--description X]` |
|
|
153
|
+
| 加道具 | `scriptctl add-prop "怀表" [--description X]` |
|
|
154
|
+
| 加分集 | `scriptctl add-episode [--title X]` |
|
|
155
|
+
|
|
156
|
+
id 自动按序分配(`act_001` / `loc_001` / `prp_001` / `ep_001` / `scn_001`…),后续命令直接引用。建完资产与集,用 `insert` 建场景、灌正文。整本怎么搭见 [references/atomic-write-workflow.md](references/atomic-write-workflow.md)。
|
|
157
|
+
|
|
158
|
+
### Address 格式(决定 verb 作用对象,自动分发)
|
|
159
|
+
|
|
160
|
+
| 形态 | 类型 |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `ep_001/scn_001#3` | action |
|
|
163
|
+
| `ep_001/scn_001` | scene |
|
|
164
|
+
| `actor:act_001` / `location:loc_001` / `prop:prp_001` | asset |
|
|
165
|
+
| `actor:act_001/st_001` | asset state |
|
|
166
|
+
| `ep_001` | episode |
|
|
167
|
+
| `spk_001` | speaker |
|
|
168
|
+
|
|
169
|
+
> `states` 查询额外接受裸资产 id(`act_001` / `loc_001` / `prp_001`),按前缀推断种类。其它 verb 仍要完整 address。
|
|
170
|
+
|
|
171
|
+
## 经典最短路径
|
|
172
|
+
|
|
173
|
+
**改某句台词**:grep 找地址 → replace 落字。
|
|
174
|
+
```bash
|
|
175
|
+
scriptctl actions --grep "台下掌声雷动"
|
|
176
|
+
# → ep_001/scn_001#3 0:14 action ... 台下掌声雷动。
|
|
177
|
+
scriptctl replace ep_001/scn_001#3 --from ",台下掌声雷动" --to ""
|
|
178
|
+
```
|
|
179
|
+
`--to` 省略即删除。一个 action 内多次命中默认拒绝,要么把 `--from` 写更长更唯一,要么显式 `--all`。
|
|
180
|
+
|
|
181
|
+
**改某角色名 + 补别名**:
|
|
182
|
+
```bash
|
|
183
|
+
scriptctl rename actor:act_001 "陈墨"
|
|
184
|
+
scriptctl alias actor:act_001 --add "陈总" --add "老板"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**整理重复角色**:
|
|
188
|
+
```bash
|
|
189
|
+
scriptctl refs actor:act_005 # 先看 act_005 出现在哪
|
|
190
|
+
scriptctl merge actor:act_005 --into actor:act_001
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**读人物状态弧线(转绘/对标常用)**:
|
|
194
|
+
```bash
|
|
195
|
+
scriptctl states act_001
|
|
196
|
+
# → actor:act_001/st_001 京北队球衣 desc="…" episodes=ep_001..ep_017,ep_022..ep_044 (40 eps, 43 scenes)
|
|
197
|
+
# → actor:act_001/st_002 石膏轮椅装 desc="…" episodes=ep_018..ep_021 (4 eps, 4 scenes)
|
|
198
|
+
scriptctl actors --counts # 每个人物出场几场/几集
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**写 / 改梗概**:
|
|
202
|
+
```bash
|
|
203
|
+
scriptctl summary # 先看整本 worldview/style/synopsis/logline/theme
|
|
204
|
+
scriptctl episodes # 每集列表含 synopsis
|
|
205
|
+
scriptctl synopsis "一段整本剧情梗概。"
|
|
206
|
+
scriptctl synopsis ep_001 "第一集剧情梗概。"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**插一场过渡 + 起头 action**:
|
|
210
|
+
```bash
|
|
211
|
+
scriptctl insert ep_001 --after ep_001/scn_005 --location loc_002 --time night --space exterior
|
|
212
|
+
scriptctl insert ep_001/scn_006 --type action --content "暗夜,街道空无一人。" --emotion "紧张"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**修校验问题**:
|
|
216
|
+
```bash
|
|
217
|
+
scriptctl issues --severity error
|
|
218
|
+
# → STATE_NOT_FOUND at ep_001/scn_003#5 actor:act_001
|
|
219
|
+
scriptctl actions --in ep_001/scn_003 --has state-changes
|
|
220
|
+
scriptctl state-change ep_001/scn_003#5 actor:act_001 --to st_calm --from st_default
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**批量精修(同构一片 → 选择器;异构多点 → `do`)**:
|
|
224
|
+
```bash
|
|
225
|
+
scriptctl replace --in ep_003..ep_007 --from 陈总 --to 陈墨 --all # 同构:默认 dry-run
|
|
226
|
+
scriptctl replace --in ep_003..ep_007 --from 陈总 --to 陈墨 --all --apply # --apply 落地
|
|
227
|
+
scriptctl do fixes.txt --apply # 异构:一段动词脚本一事务
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**从零写一集(你自己写正文,`do` 批量灌)**:
|
|
231
|
+
```bash
|
|
232
|
+
scriptctl create --title "夏夜便利店" # 空白剧本
|
|
233
|
+
scriptctl do ep01.txt --apply # 每行一条建词/insert:add-actor → add-location → add-episode → insert
|
|
234
|
+
```
|
|
235
|
+
完整骨架与写法见 [references/atomic-write-workflow.md](references/atomic-write-workflow.md)。
|
|
236
|
+
|
|
237
|
+
## 三种入库起点(按项目状态选)
|
|
238
|
+
|
|
239
|
+
| 项目状态 | 走 | 细节 |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| 没剧本文本,从零写(题材 / 灵感 / 集纲在手) | `create` + `add-*` + `insert` + `do`,正文你自己写 | [references/atomic-write-workflow.md](references/atomic-write-workflow.md) |
|
|
242
|
+
| 已有完整剧本 / 分镜素材(docx / pdf / xlsx / md / json) | `scriptctl direct ...`(LLM 抽取) | [references/direct-workflow.md](references/direct-workflow.md) |
|
|
243
|
+
| 只有正文 md、还没资产 md | `scriptctl parse <dir> --extract-assets`(LLM 提资产) | 只写 `人物.md`/`场景.md`/`道具.md`/`发声源.md`/`元信息.md`,不生成 script.json、不发布;复核后再跑普通 parse |
|
|
244
|
+
| 自己手写好一个 md 工作区 | `scriptctl parse <dir> [--publish]`(确定性组装,无 LLM) | `scriptctl parse --spec` 看 md 语法 |
|
|
245
|
+
|
|
246
|
+
写正文 = 你(agent)想好正文,用上面「建 / 写」的原子能力灌进剧本。
|
|
247
|
+
|
|
248
|
+
`direct export` 只卡 validation:passed 才放行。但**强烈建议**导出前用 `direct review` 自查(curation/issues 总览 + `--episode N` 源文↔抽取对照),细节看 direct-workflow.md。
|
|
249
|
+
|
|
250
|
+
`direct init` 可续跑:成功批次跳过、失败批次粘性不自动重试。`scriptctl direct status` 看每集状态,`direct init --episodes <N> / --batches <key> / --retry-errors / --all` 选择性重跑(只动点名的,不碰其它集)。某集内容过滤就 `--episodes <N>` 重跑或软化源文本,不要清 run_state / 手塞文件。
|
|
251
|
+
|
|
252
|
+
## 资产 state 判定(`state-add` 通用规则)
|
|
253
|
+
|
|
254
|
+
只有**持久、可复用、需独立生成视觉资产的外观变体**才进 `states[]`。身份 / 年龄 / 职业 / 情绪 / 动作 / 姿态 / 临时伤病 / 混合状态**不**算状态,放 description 或 action 文本。
|
|
255
|
+
|
|
256
|
+
**默认状态(default)**:每个有 `states[]` 的资产必须有且仅有一个 asset-scoped 的 `state_id: "default"`(代表最常见的常态外观,保留可读的 state_name)。`direct`/`parse` 会自动把「默认/最常见」那一档 canonical 成 `default`;手动 `state-add` 给一个原先无状态的资产加第一个 state 后,若校验报 `DEFAULT_STATE_MISSING`,说明缺默认态——把最常见那档的 state_id 设成 `default`(改 raw JSON 或重跑 direct state-curation)。
|
|
257
|
+
|
|
258
|
+
合并或删除 state 时,必须确认引用该 state 的场景 context 和 action state_changes 已被正确修复;操作与实现路径见 [references/state-reference-repair.md](references/state-reference-repair.md)。
|
|
259
|
+
|
|
260
|
+
## 通用边界
|
|
261
|
+
|
|
262
|
+
- 🔴 **不要加 `--json`**。默认列表 / 查询输出是给 agent 读的 TSV / 行式文本(地址单独成列),比 JSON 更省 token、更好决策。除非真的要 parse 字段喂给下一步程序(那时 `actions --json` 有结构化 `rows[]`,或用 `--format addr` 取裸地址流)。
|
|
263
|
+
- 🔴 **多点写默认 dry-run**:`replace/type/actor/emotion/state-change/transition` 一旦带 `--in`/谓词就是预演(回显命中数,不落盘),确认后加 `--apply` 才真写。单点(给 address)直写、不用 `--apply`。
|
|
264
|
+
- 🔴 **手写批量走 `do`,不要手撸 patch JSON**。`scriptctl patch <file|->` 降级为**机器/jq 管道**的接口(`… --json | jq '[…]' | scriptctl patch -`);人/agent 手写一律用 `do`(动词原样,无翻译)。需要 op schema 用 `scriptctl patch --schema [<op>]`。
|
|
265
|
+
- 🔴 **本地用记得带 `--script-path <file>`**;洗稿/对标/转绘里两份剧本就是两个不同的 `--script-path`,一次只碰一份。
|
|
266
|
+
- 只有 `direct init` 和 `parse --extract-assets` 会请求大模型;普通 `parse`、`summary/episodes/actions` 查询、`synopsis/replace/patch/do` 等 script.json 读写命令不打模型。
|
|
267
|
+
- 修 direct 中间稿(`script.initial.json`):所有读写 verb 加 `--draft` 即可,不用知道文件路径;不加 `--draft` 默认操作 DB 里的最终剧本。`--draft` 与 `--script-path`/`--project-group-no` 互斥。
|
|
268
|
+
- 多态 verb(`delete` / `merge` / `move` / `describe` / `rename` / `insert`)按 address 格式分发;flag 用错 kind 会直接报错(不会静默忽略)。
|
|
269
|
+
- 互斥 flag(`context` 的 --state/--clear/--remove;`state-change` 的 --to/--clear;`transition` 的 --process+--contrast/--clear)只能传一个,多传会报 `*_FLAG_CONFLICT`。
|
|
270
|
+
- `mock` provider 仅测试,禁止作为交付;provider 失败不降级 mock。
|
|
271
|
+
- 工具失败排查根因,不手工拼装 JSON 绕过校验。
|
|
272
|
+
|
|
273
|
+
参数 / 默认值 / 退出码以 `scriptctl <cmd> --help` 为准。功能变更用 `scriptctl changelog` 查。
|