@lingjingai/scriptctl 0.1.0 → 0.3.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 +72 -0
- package/dist/cli.js +309 -396
- package/dist/cli.js.map +1 -1
- package/dist/common.d.ts +9 -0
- package/dist/common.js.map +1 -1
- package/dist/domain/asset-registry.d.ts +141 -0
- package/dist/domain/asset-registry.js +318 -0
- package/dist/domain/asset-registry.js.map +1 -0
- package/dist/domain/collision-detector.d.ts +83 -0
- package/dist/domain/collision-detector.js +248 -0
- package/dist/domain/collision-detector.js.map +1 -0
- package/dist/domain/direct-core.d.ts +13 -1
- package/dist/domain/direct-core.js +19 -6
- package/dist/domain/direct-core.js.map +1 -1
- package/dist/domain/script-core.d.ts +11 -0
- package/dist/domain/script-core.js +34 -19
- package/dist/domain/script-core.js.map +1 -1
- package/dist/help-text.js +336 -4
- package/dist/help-text.js.map +1 -1
- package/dist/infra/converters.js +21 -7
- package/dist/infra/converters.js.map +1 -1
- package/dist/infra/default-writing-prompt.d.ts +31 -0
- package/dist/infra/default-writing-prompt.js +50 -0
- package/dist/infra/default-writing-prompt.js.map +1 -0
- package/dist/infra/default-writing-prompt.md +115 -0
- package/dist/infra/gemini-writer.d.ts +107 -0
- package/dist/infra/gemini-writer.js +207 -0
- package/dist/infra/gemini-writer.js.map +1 -0
- package/dist/infra/providers.d.ts +36 -0
- package/dist/infra/providers.js +186 -2
- package/dist/infra/providers.js.map +1 -1
- package/dist/output.js +26 -9
- package/dist/output.js.map +1 -1
- package/dist/usecases/episode.d.ts +48 -0
- package/dist/usecases/episode.js +1209 -0
- package/dist/usecases/episode.js.map +1 -0
- package/dist/usecases/script.d.ts +6 -2
- package/dist/usecases/script.js +49 -5
- package/dist/usecases/script.js.map +1 -1
- package/package.json +9 -5
package/dist/cli.js
CHANGED
|
@@ -1,433 +1,346 @@
|
|
|
1
|
+
import { Command, CommanderError } from "commander";
|
|
1
2
|
import { CliError, EXIT_OK, EXIT_USAGE } from "./common.js";
|
|
2
3
|
import { getHelp } from "./help-text.js";
|
|
3
4
|
import { loadLocalEnv } from "./infra/env.js";
|
|
4
5
|
import { emit, emitError } from "./output.js";
|
|
5
6
|
import { commandInit, commandInspect, commandValidate, } from "./usecases/direct.js";
|
|
6
7
|
import { commandDoctor } from "./usecases/doctor.js";
|
|
8
|
+
import { commandEpisodeBatch, commandEpisodeDraft, commandEpisodeInit, commandEpisodeMerge, commandEpisodePush, commandEpisodeSpec, commandEpisodeWorkspace, } from "./usecases/episode.js";
|
|
7
9
|
import { commandExport, commandScriptAction, commandScriptContext, commandScriptDialogue, commandScriptInspect, commandScriptPatch, commandScriptSpeaker, commandScriptState, commandScriptValidate, } from "./usecases/script.js";
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"script.
|
|
12
|
-
"script.state.describe",
|
|
13
|
-
"script.state.refs",
|
|
14
|
-
"script.
|
|
15
|
-
"script.state.
|
|
16
|
-
"script.
|
|
17
|
-
"script.
|
|
18
|
-
"script.
|
|
19
|
-
"
|
|
20
|
-
"script.action.transition-set",
|
|
21
|
-
"script.action.transition-clear",
|
|
22
|
-
"script.speaker.add",
|
|
23
|
-
"script.dialogue.speakers",
|
|
24
|
-
"script.dialogue.overlap",
|
|
10
|
+
const KNOWN_COMMANDS = new Set([
|
|
11
|
+
"doctor",
|
|
12
|
+
"direct", "direct.init", "direct.validate", "direct.inspect", "direct.export",
|
|
13
|
+
"script", "script.inspect", "script.validate", "script.patch",
|
|
14
|
+
"script.state", "script.state.add", "script.state.rename", "script.state.describe",
|
|
15
|
+
"script.state.refs", "script.state.delete-plan", "script.state.delete-apply",
|
|
16
|
+
"script.context", "script.context.set", "script.context.clear",
|
|
17
|
+
"script.action", "script.action.state-change", "script.action.state-remove",
|
|
18
|
+
"script.action.transition-set", "script.action.transition-clear",
|
|
19
|
+
"script.speaker", "script.speaker.add",
|
|
20
|
+
"script.dialogue", "script.dialogue.speakers", "script.dialogue.overlap",
|
|
21
|
+
"episode", "episode.init", "episode.draft", "episode.batch", "episode.workspace", "episode.merge", "episode.push", "episode.spec",
|
|
25
22
|
]);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
opts[name] = true;
|
|
52
|
-
i += 1;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
let value;
|
|
56
|
-
if (eqIdx >= 0) {
|
|
57
|
-
value = token.slice(eqIdx + 1);
|
|
58
|
-
i += 1;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
if (i + 1 >= argv.length) {
|
|
62
|
-
throw new CliError("USAGE ERROR: Invalid arguments", "Invalid arguments.", {
|
|
63
|
-
exitCode: EXIT_USAGE,
|
|
64
|
-
required: [`Run: scriptctl ${command.join(" ")} --help`],
|
|
65
|
-
received: [`option --${rawName} requires a value`],
|
|
66
|
-
nextSteps: [`Run: scriptctl ${command.join(" ")} --help`],
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
value = argv[i + 1];
|
|
70
|
-
i += 2;
|
|
71
|
-
}
|
|
72
|
-
if (arraySet.has(name)) {
|
|
73
|
-
if (!Array.isArray(opts[name]))
|
|
74
|
-
opts[name] = [];
|
|
75
|
-
opts[name].push(value);
|
|
76
|
-
}
|
|
77
|
-
else if (valueSet.has(name)) {
|
|
78
|
-
opts[name] = value;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// Allow unknown options for forward-compat but record verbatim
|
|
82
|
-
opts[name] = value;
|
|
83
|
-
}
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
positionals.push(token);
|
|
87
|
-
i += 1;
|
|
23
|
+
// Groups are command nodes that exist only to host subcommands. Extra non-flag tokens
|
|
24
|
+
// after a group path indicate an unknown subcommand (e.g. `direct nope` → error).
|
|
25
|
+
// Leaves are real commands that can accept positional arguments (e.g. `episode draft <n>`).
|
|
26
|
+
const GROUP_COMMANDS = new Set([
|
|
27
|
+
"direct",
|
|
28
|
+
"script",
|
|
29
|
+
"script.state",
|
|
30
|
+
"script.context",
|
|
31
|
+
"script.action",
|
|
32
|
+
"script.speaker",
|
|
33
|
+
"script.dialogue",
|
|
34
|
+
"episode",
|
|
35
|
+
]);
|
|
36
|
+
function extractLeadingPath(args) {
|
|
37
|
+
// Collect leading non-flag tokens, then take only as many as form the longest known
|
|
38
|
+
// command prefix. Trailing tokens are treated as either:
|
|
39
|
+
// - positional arguments (when the matched node is a leaf, e.g. `episode draft 1`)
|
|
40
|
+
// - unknown subcommands (when the matched node is a group, e.g. `direct nope`)
|
|
41
|
+
const allNonFlag = [];
|
|
42
|
+
for (const tok of args) {
|
|
43
|
+
if (tok.startsWith("-"))
|
|
44
|
+
break;
|
|
45
|
+
allNonFlag.push(tok);
|
|
46
|
+
if (allNonFlag.length >= 5)
|
|
47
|
+
break;
|
|
88
48
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!(required in opts)) {
|
|
94
|
-
throw new CliError("USAGE ERROR: Invalid arguments", "Invalid arguments.", {
|
|
95
|
-
exitCode: EXIT_USAGE,
|
|
96
|
-
required: [`Run: scriptctl ${command.join(" ")} --help`],
|
|
97
|
-
received: [`missing --${required.replace(/_/g, "-")}`],
|
|
98
|
-
nextSteps: [`Run: scriptctl ${command.join(" ")} --help`],
|
|
99
|
-
});
|
|
100
|
-
}
|
|
49
|
+
const matchLen = longestKnownPrefix(allNonFlag);
|
|
50
|
+
if (matchLen === 0) {
|
|
51
|
+
// No known command at all — return up to 3 tokens for a readable error.
|
|
52
|
+
return allNonFlag.slice(0, 3);
|
|
101
53
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
54
|
+
const matchedKey = allNonFlag.slice(0, matchLen).join(".");
|
|
55
|
+
const isGroup = GROUP_COMMANDS.has(matchedKey);
|
|
56
|
+
if (isGroup && allNonFlag.length > matchLen) {
|
|
57
|
+
// Group + extra tokens = unknown subcommand. Return the truncated path so the
|
|
58
|
+
// validator (knownLen < cmdPath.length) reports an error.
|
|
59
|
+
return allNonFlag.slice(0, Math.min(allNonFlag.length, 3));
|
|
106
60
|
}
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
function aliasRename(camel) {
|
|
110
|
-
// argparse used dest="from"; we keep TS field also "from"
|
|
111
|
-
return camel;
|
|
61
|
+
return allNonFlag.slice(0, matchLen);
|
|
112
62
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
flags: [],
|
|
118
|
-
values: ["source_path", "workspace_path", "provider", "model", "concurrency", "batch_mode", "batch_target_lines", "batch_max_chars", "batch_min_lines"],
|
|
119
|
-
arrays: [],
|
|
120
|
-
required: ["source_path"],
|
|
121
|
-
defaults: { workspace_path: "workspace", provider: "anthropic", concurrency: "30", batch_mode: "episode", batch_target_lines: "36", batch_max_chars: "1800", batch_min_lines: "12" },
|
|
122
|
-
acceptsPositionals: false,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
if (k === "direct.validate") {
|
|
126
|
-
return {
|
|
127
|
-
flags: [],
|
|
128
|
-
values: ["workspace_path", "script_path"],
|
|
129
|
-
arrays: [],
|
|
130
|
-
required: [],
|
|
131
|
-
defaults: { workspace_path: "workspace" },
|
|
132
|
-
acceptsPositionals: false,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
if (k === "direct.inspect") {
|
|
136
|
-
return {
|
|
137
|
-
flags: [],
|
|
138
|
-
values: ["workspace_path", "target", "id", "episode"],
|
|
139
|
-
arrays: [],
|
|
140
|
-
required: ["target"],
|
|
141
|
-
defaults: { workspace_path: "workspace" },
|
|
142
|
-
acceptsPositionals: false,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
if (k === "direct.export") {
|
|
146
|
-
return {
|
|
147
|
-
flags: ["force"],
|
|
148
|
-
values: ["workspace_path", "output_path", "project_group_no", "request_id"],
|
|
149
|
-
arrays: [],
|
|
150
|
-
required: [],
|
|
151
|
-
defaults: { workspace_path: "workspace", output_path: "output" },
|
|
152
|
-
acceptsPositionals: false,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
if (k === "script.inspect") {
|
|
156
|
-
return {
|
|
157
|
-
flags: [],
|
|
158
|
-
values: ["script_path", "workspace_path", "project_group_no", "target", "id"],
|
|
159
|
-
arrays: [],
|
|
160
|
-
required: [],
|
|
161
|
-
defaults: { workspace_path: "workspace", target: "summary" },
|
|
162
|
-
acceptsPositionals: false,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
if (k === "script.validate") {
|
|
166
|
-
return {
|
|
167
|
-
flags: [],
|
|
168
|
-
values: ["script_path", "workspace_path", "project_group_no"],
|
|
169
|
-
arrays: [],
|
|
170
|
-
required: [],
|
|
171
|
-
defaults: { workspace_path: "workspace" },
|
|
172
|
-
acceptsPositionals: false,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
if (k === "script.patch") {
|
|
176
|
-
return {
|
|
177
|
-
flags: [],
|
|
178
|
-
values: ["script_path", "workspace_path", "project_group_no", "request_id", "patch"],
|
|
179
|
-
arrays: [],
|
|
180
|
-
required: ["patch"],
|
|
181
|
-
defaults: { workspace_path: "workspace" },
|
|
182
|
-
acceptsPositionals: false,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
if (k.startsWith("script.state.")) {
|
|
186
|
-
const sub = command[2];
|
|
187
|
-
const values = ["script_path", "workspace_path", "project_group_no", "request_id"];
|
|
188
|
-
const required = [];
|
|
189
|
-
const defaults = { workspace_path: "workspace" };
|
|
190
|
-
if (sub === "add" || sub === "rename") {
|
|
191
|
-
values.push("name");
|
|
192
|
-
required.push("name");
|
|
193
|
-
}
|
|
194
|
-
if (sub === "describe") {
|
|
195
|
-
values.push("description");
|
|
196
|
-
required.push("description");
|
|
197
|
-
}
|
|
198
|
-
if (sub === "add") {
|
|
199
|
-
values.push("description", "state_id");
|
|
200
|
-
defaults["description"] = "";
|
|
201
|
-
}
|
|
202
|
-
if (sub === "delete-apply") {
|
|
203
|
-
values.push("strategy", "replacement");
|
|
204
|
-
required.push("strategy");
|
|
205
|
-
}
|
|
206
|
-
return { flags: [], values, arrays: [], required, defaults, acceptsPositionals: true };
|
|
207
|
-
}
|
|
208
|
-
if (k.startsWith("script.context.")) {
|
|
209
|
-
const sub = command[2];
|
|
210
|
-
const values = ["script_path", "workspace_path", "project_group_no", "request_id"];
|
|
211
|
-
const required = [];
|
|
212
|
-
if (sub === "set") {
|
|
213
|
-
values.push("state");
|
|
214
|
-
required.push("state");
|
|
215
|
-
}
|
|
216
|
-
return {
|
|
217
|
-
flags: [],
|
|
218
|
-
values,
|
|
219
|
-
arrays: [],
|
|
220
|
-
required,
|
|
221
|
-
defaults: { workspace_path: "workspace" },
|
|
222
|
-
acceptsPositionals: true,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
if (k.startsWith("script.action.")) {
|
|
226
|
-
const sub = command[2];
|
|
227
|
-
const values = ["script_path", "workspace_path", "project_group_no", "request_id"];
|
|
228
|
-
const required = [];
|
|
229
|
-
const defaults = { workspace_path: "workspace" };
|
|
230
|
-
if (sub === "state-change") {
|
|
231
|
-
values.push("to", "from", "effective");
|
|
232
|
-
required.push("to");
|
|
233
|
-
defaults["effective"] = "after";
|
|
234
|
-
}
|
|
235
|
-
if (sub === "transition-set") {
|
|
236
|
-
values.push("process", "contrast");
|
|
237
|
-
required.push("process", "contrast");
|
|
238
|
-
}
|
|
239
|
-
return { flags: [], values, arrays: [], required, defaults, acceptsPositionals: true };
|
|
240
|
-
}
|
|
241
|
-
if (k === "script.speaker.add") {
|
|
242
|
-
return {
|
|
243
|
-
flags: [],
|
|
244
|
-
values: ["script_path", "workspace_path", "project_group_no", "request_id", "kind", "name", "source_id", "voice_desc", "speaker_id"],
|
|
245
|
-
arrays: [],
|
|
246
|
-
required: ["kind", "name"],
|
|
247
|
-
defaults: { workspace_path: "workspace", voice_desc: "" },
|
|
248
|
-
acceptsPositionals: true,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
if (k === "script.dialogue.speakers") {
|
|
252
|
-
return {
|
|
253
|
-
flags: [],
|
|
254
|
-
values: ["script_path", "workspace_path", "project_group_no", "request_id", "delivery"],
|
|
255
|
-
arrays: ["speaker"],
|
|
256
|
-
required: [],
|
|
257
|
-
defaults: { workspace_path: "workspace", delivery: "simultaneous" },
|
|
258
|
-
acceptsPositionals: true,
|
|
259
|
-
};
|
|
63
|
+
function longestKnownPrefix(path) {
|
|
64
|
+
for (let len = path.length; len > 0; len--) {
|
|
65
|
+
if (KNOWN_COMMANDS.has(path.slice(0, len).join(".")))
|
|
66
|
+
return len;
|
|
260
67
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
function commandPath(cmd) {
|
|
71
|
+
const parts = [];
|
|
72
|
+
let c = cmd;
|
|
73
|
+
while (c) {
|
|
74
|
+
const name = c.name();
|
|
75
|
+
if (!name || name === "scriptctl")
|
|
76
|
+
break;
|
|
77
|
+
parts.unshift(name);
|
|
78
|
+
c = c.parent;
|
|
270
79
|
}
|
|
271
|
-
return
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
80
|
+
return parts;
|
|
81
|
+
}
|
|
82
|
+
function camelToSnake(s) {
|
|
83
|
+
return s.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
84
|
+
}
|
|
85
|
+
function normalizeOpts(opts) {
|
|
86
|
+
const out = {};
|
|
87
|
+
for (const [k, v] of Object.entries(opts))
|
|
88
|
+
out[camelToSnake(k)] = v;
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
function dispatch(handler, state) {
|
|
92
|
+
return async (...args) => {
|
|
93
|
+
const cmd = args[args.length - 1];
|
|
94
|
+
const positionals = args.slice(0, -2).filter((p) => p !== undefined);
|
|
95
|
+
const normalized = normalizeOpts(cmd.opts());
|
|
96
|
+
if (positionals.length > 0)
|
|
97
|
+
normalized._args = positionals;
|
|
98
|
+
const [report, exitCode] = await handler(normalized);
|
|
99
|
+
emit(report, exitCode, state.jsonMode);
|
|
100
|
+
state.exitCode = exitCode;
|
|
278
101
|
};
|
|
279
102
|
}
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (argv[0] !== "direct" && argv[0] !== "script")
|
|
286
|
-
return null;
|
|
287
|
-
const group = argv[0];
|
|
288
|
-
if (argv.length < 2)
|
|
289
|
-
return { command: [group], rest: [] };
|
|
290
|
-
if (argv[1] === "--help" || argv[1] === "-h")
|
|
291
|
-
return { command: [group], rest: [] };
|
|
292
|
-
if (group === "script" && SCRIPT_NESTED_GROUPS.has(argv[1])) {
|
|
293
|
-
if (argv.length < 3 || argv[2] === "--help" || argv[2] === "-h") {
|
|
294
|
-
return { command: ["script", argv[1]], rest: [] };
|
|
295
|
-
}
|
|
296
|
-
return { command: ["script", argv[1], argv[2]], rest: argv.slice(3) };
|
|
297
|
-
}
|
|
298
|
-
return { command: [group, argv[1]], rest: argv.slice(2) };
|
|
103
|
+
function showGroupHelp(commandPathParts, state) {
|
|
104
|
+
return () => {
|
|
105
|
+
process.stdout.write(getHelp(commandPathParts) ?? getHelp([]) ?? "");
|
|
106
|
+
state.exitCode = EXIT_OK;
|
|
107
|
+
};
|
|
299
108
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
109
|
+
const appendStr = (value, prev) => [...prev, value];
|
|
110
|
+
function withScriptOpts(cmd) {
|
|
111
|
+
return cmd
|
|
112
|
+
.option("--script-path <path>")
|
|
113
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
114
|
+
.option("--project-group-no <no>")
|
|
115
|
+
.option("--request-id <id>");
|
|
116
|
+
}
|
|
117
|
+
function buildProgram(state) {
|
|
118
|
+
const program = new Command("scriptctl")
|
|
119
|
+
.exitOverride()
|
|
120
|
+
.allowExcessArguments(false)
|
|
121
|
+
.allowUnknownOption(false)
|
|
122
|
+
.configureOutput({
|
|
123
|
+
writeOut: (str) => process.stdout.write(str),
|
|
124
|
+
writeErr: () => {
|
|
125
|
+
/* swallowed; we emit our own envelope */
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
.configureHelp({
|
|
129
|
+
formatHelp: (cmd) => getHelp(commandPath(cmd)) ?? getHelp([]) ?? "",
|
|
130
|
+
});
|
|
131
|
+
program.command("doctor").action(dispatch((o) => commandDoctor(o), state));
|
|
132
|
+
// ============================== direct ==============================
|
|
133
|
+
const direct = program.command("direct").action(showGroupHelp(["direct"], state));
|
|
134
|
+
direct.command("init")
|
|
135
|
+
.requiredOption("--source-path <path>")
|
|
136
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
137
|
+
.option("--provider <name>", "", "anthropic")
|
|
138
|
+
.option("--model <model>")
|
|
139
|
+
.option("--concurrency <n>", "", "30")
|
|
140
|
+
.option("--batch-mode <mode>", "", "episode")
|
|
141
|
+
.option("--batch-target-lines <n>", "", "36")
|
|
142
|
+
.option("--batch-max-chars <n>", "", "1800")
|
|
143
|
+
.option("--batch-min-lines <n>", "", "12")
|
|
144
|
+
.action(dispatch(commandInit, state));
|
|
145
|
+
direct.command("validate")
|
|
146
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
147
|
+
.option("--script-path <path>")
|
|
148
|
+
.action(dispatch((o) => commandValidate(o), state));
|
|
149
|
+
direct.command("inspect")
|
|
150
|
+
.requiredOption("--target <target>")
|
|
151
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
152
|
+
.option("--id <id>")
|
|
153
|
+
.option("--episode <e>")
|
|
154
|
+
.action(dispatch((o) => commandInspect(o), state));
|
|
155
|
+
direct.command("export")
|
|
156
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
157
|
+
.option("--output-path <path>", "", "output")
|
|
158
|
+
.option("--project-group-no <no>")
|
|
159
|
+
.option("--request-id <id>")
|
|
160
|
+
.option("--force")
|
|
161
|
+
.action(dispatch(commandExport, state));
|
|
162
|
+
// ============================== script ==============================
|
|
163
|
+
const script = program.command("script").action(showGroupHelp(["script"], state));
|
|
164
|
+
withScriptOpts(script.command("inspect"))
|
|
165
|
+
.option("--target <target>", "", "summary")
|
|
166
|
+
.option("--id <id>")
|
|
167
|
+
.option("--min-chars <n>")
|
|
168
|
+
.option("--max-chars <n>")
|
|
169
|
+
.action(dispatch(commandScriptInspect, state));
|
|
170
|
+
withScriptOpts(script.command("validate")).action(dispatch(commandScriptValidate, state));
|
|
171
|
+
withScriptOpts(script.command("patch"))
|
|
172
|
+
.requiredOption("--patch <file>")
|
|
173
|
+
.action(dispatch(commandScriptPatch, state));
|
|
174
|
+
// ------- script state -------
|
|
175
|
+
const scriptState = script.command("state").action(showGroupHelp(["script", "state"], state));
|
|
176
|
+
withScriptOpts(scriptState.command("add <target>"))
|
|
177
|
+
.requiredOption("--name <name>")
|
|
178
|
+
.option("--description <text>", "", "")
|
|
179
|
+
.option("--state-id <id>")
|
|
180
|
+
.action(dispatch((o) => commandScriptState(o, "add"), state));
|
|
181
|
+
withScriptOpts(scriptState.command("rename <target>"))
|
|
182
|
+
.requiredOption("--name <name>")
|
|
183
|
+
.action(dispatch((o) => commandScriptState(o, "rename"), state));
|
|
184
|
+
withScriptOpts(scriptState.command("describe <target>"))
|
|
185
|
+
.requiredOption("--description <text>")
|
|
186
|
+
.action(dispatch((o) => commandScriptState(o, "describe"), state));
|
|
187
|
+
withScriptOpts(scriptState.command("refs <target>"))
|
|
188
|
+
.action(dispatch((o) => commandScriptState(o, "refs"), state));
|
|
189
|
+
withScriptOpts(scriptState.command("delete-plan <target>"))
|
|
190
|
+
.action(dispatch((o) => commandScriptState(o, "delete-plan"), state));
|
|
191
|
+
withScriptOpts(scriptState.command("delete-apply <target>"))
|
|
192
|
+
.requiredOption("--strategy <strategy>")
|
|
193
|
+
.option("--replacement <id>")
|
|
194
|
+
.action(dispatch((o) => commandScriptState(o, "delete-apply"), state));
|
|
195
|
+
// ------- script context -------
|
|
196
|
+
const scriptContext = script.command("context").action(showGroupHelp(["script", "context"], state));
|
|
197
|
+
withScriptOpts(scriptContext.command("set <at> <target>"))
|
|
198
|
+
.requiredOption("--state <id>")
|
|
199
|
+
.action(dispatch((o) => commandScriptContext(o, "set"), state));
|
|
200
|
+
withScriptOpts(scriptContext.command("clear <at> <target>"))
|
|
201
|
+
.action(dispatch((o) => commandScriptContext(o, "clear"), state));
|
|
202
|
+
// ------- script action -------
|
|
203
|
+
const scriptAction = script.command("action").action(showGroupHelp(["script", "action"], state));
|
|
204
|
+
withScriptOpts(scriptAction.command("state-change <at> <target>"))
|
|
205
|
+
.requiredOption("--to <id>")
|
|
206
|
+
.option("--from <id>")
|
|
207
|
+
.option("--effective <when>", "", "after")
|
|
208
|
+
.action(dispatch((o) => commandScriptAction(o, "state-change"), state));
|
|
209
|
+
withScriptOpts(scriptAction.command("state-remove <at> <target>"))
|
|
210
|
+
.action(dispatch((o) => commandScriptAction(o, "state-remove"), state));
|
|
211
|
+
withScriptOpts(scriptAction.command("transition-set <at> <target>"))
|
|
212
|
+
.requiredOption("--process <text>")
|
|
213
|
+
.requiredOption("--contrast <text>")
|
|
214
|
+
.action(dispatch((o) => commandScriptAction(o, "transition-set"), state));
|
|
215
|
+
withScriptOpts(scriptAction.command("transition-clear <at> <target>"))
|
|
216
|
+
.action(dispatch((o) => commandScriptAction(o, "transition-clear"), state));
|
|
217
|
+
// ------- script speaker -------
|
|
218
|
+
const scriptSpeaker = script.command("speaker").action(showGroupHelp(["script", "speaker"], state));
|
|
219
|
+
withScriptOpts(scriptSpeaker.command("add"))
|
|
220
|
+
.requiredOption("--kind <kind>")
|
|
221
|
+
.requiredOption("--name <name>")
|
|
222
|
+
.option("--source-id <id>")
|
|
223
|
+
.option("--voice-desc <text>", "", "")
|
|
224
|
+
.option("--speaker-id <id>")
|
|
225
|
+
.action(dispatch((o) => commandScriptSpeaker(o, "add"), state));
|
|
226
|
+
// ------- script dialogue -------
|
|
227
|
+
const scriptDialogue = script.command("dialogue").action(showGroupHelp(["script", "dialogue"], state));
|
|
228
|
+
withScriptOpts(scriptDialogue.command("speakers <at>"))
|
|
229
|
+
.option("--speaker <id>", "", appendStr, [])
|
|
230
|
+
.option("--delivery <mode>", "", "simultaneous")
|
|
231
|
+
.action(dispatch((o) => commandScriptDialogue(o, "speakers"), state));
|
|
232
|
+
withScriptOpts(scriptDialogue.command("overlap <at>"))
|
|
233
|
+
.option("--line <spec>", "", appendStr, [])
|
|
234
|
+
.action(dispatch((o) => commandScriptDialogue(o, "overlap"), state));
|
|
235
|
+
// ============================== episode ==============================
|
|
236
|
+
const episode = program.command("episode").action(showGroupHelp(["episode"], state));
|
|
237
|
+
episode.command("init")
|
|
238
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
239
|
+
.option("--force")
|
|
240
|
+
.action(dispatch((o) => commandEpisodeInit(o), state));
|
|
241
|
+
episode.command("draft <n>")
|
|
242
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
243
|
+
.option("--prompt-template <path>")
|
|
244
|
+
.option("--outline <path>")
|
|
245
|
+
.option("--ref <spec>", "", appendStr, [])
|
|
246
|
+
.option("--from-md <path>")
|
|
247
|
+
.option("--provider <name>")
|
|
248
|
+
.option("--model <model>")
|
|
249
|
+
.option("--max-tokens <n>")
|
|
250
|
+
.option("--regen")
|
|
251
|
+
.option("--resume")
|
|
252
|
+
.action(dispatch((o) => commandEpisodeDraft(o), state));
|
|
253
|
+
episode.command("batch")
|
|
254
|
+
.requiredOption("--range <a-b>")
|
|
255
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
256
|
+
.option("--prompt-template <path>")
|
|
257
|
+
.option("--outline <path>")
|
|
258
|
+
.option("--ref <spec>", "", appendStr, [])
|
|
259
|
+
.option("--provider <name>")
|
|
260
|
+
.option("--model <model>")
|
|
261
|
+
.option("--max-tokens <n>")
|
|
262
|
+
.action(dispatch((o) => commandEpisodeBatch(o), state));
|
|
263
|
+
episode.command("workspace")
|
|
264
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
265
|
+
.action(dispatch((o) => commandEpisodeWorkspace(o), state));
|
|
266
|
+
episode.command("merge")
|
|
267
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
268
|
+
.option("--meta-path <path>")
|
|
269
|
+
.option("--from <n>")
|
|
270
|
+
.option("--to <n>")
|
|
271
|
+
.option("--force")
|
|
272
|
+
.action(dispatch((o) => commandEpisodeMerge(o), state));
|
|
273
|
+
episode.command("push")
|
|
274
|
+
.option("--workspace-path <path>", "", "workspace")
|
|
275
|
+
.option("--project-group-no <no>")
|
|
276
|
+
.option("--request-id <id>")
|
|
277
|
+
.action(dispatch((o) => commandEpisodePush(o), state));
|
|
278
|
+
episode.command("spec")
|
|
279
|
+
.action(dispatch((o) => commandEpisodeSpec(o), state));
|
|
280
|
+
return program;
|
|
308
281
|
}
|
|
309
|
-
const TOP_LEVEL_GROUPS_WITHOUT_SUB = new Set([
|
|
310
|
-
"direct",
|
|
311
|
-
"script",
|
|
312
|
-
"script.state",
|
|
313
|
-
"script.context",
|
|
314
|
-
"script.action",
|
|
315
|
-
"script.speaker",
|
|
316
|
-
"script.dialogue",
|
|
317
|
-
]);
|
|
318
282
|
export async function main(argv = null) {
|
|
319
283
|
loadLocalEnv();
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (resolved === null)
|
|
329
|
-
return unknownCommandError(args, jsonMode);
|
|
330
|
-
const { command, rest } = resolved;
|
|
331
|
-
const cmdKey = key(command);
|
|
332
|
-
if (TOP_LEVEL_GROUPS_WITHOUT_SUB.has(cmdKey)) {
|
|
333
|
-
if (rest.length === 0 || rest[0] === "--help" || rest[0] === "-h") {
|
|
334
|
-
process.stdout.write(getHelp(command) ?? "");
|
|
284
|
+
let args = argv ?? process.argv.slice(2);
|
|
285
|
+
const state = { jsonMode: args.includes("--json"), exitCode: EXIT_OK };
|
|
286
|
+
args = args.filter((a) => a !== "--json");
|
|
287
|
+
const wantsHelp = args.includes("--help") || args.includes("-h");
|
|
288
|
+
const cmdPath = extractLeadingPath(args);
|
|
289
|
+
if (cmdPath.length === 0) {
|
|
290
|
+
if (args.length === 0 || wantsHelp) {
|
|
291
|
+
process.stdout.write(getHelp([]) ?? "");
|
|
335
292
|
return EXIT_OK;
|
|
336
293
|
}
|
|
294
|
+
return emitError(new CliError("USAGE ERROR: Unknown command", "Unknown command.", {
|
|
295
|
+
exitCode: EXIT_USAGE,
|
|
296
|
+
required: ["valid scriptctl command"],
|
|
297
|
+
received: [args.join(" ") || "<empty>"],
|
|
298
|
+
nextSteps: ["Run: scriptctl --help"],
|
|
299
|
+
}), state.jsonMode);
|
|
337
300
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
return EXIT_OK;
|
|
342
|
-
}
|
|
343
|
-
const [report, exitCode] = commandDoctor({});
|
|
344
|
-
emit(report, exitCode, jsonMode);
|
|
345
|
-
return exitCode;
|
|
346
|
-
}
|
|
347
|
-
// Validate command is recognized
|
|
348
|
-
const helpExists = getHelp(command) !== undefined;
|
|
349
|
-
if (!helpExists && !NESTED_COMMANDS.has(cmdKey)) {
|
|
350
|
-
const required = command[0] === "direct"
|
|
351
|
-
? ["init, validate, inspect, export"]
|
|
352
|
-
: ["inspect, validate, patch, state, context, action, speaker, dialogue"];
|
|
353
|
-
return emitError(new CliError(`USAGE ERROR: Unknown ${command[0]} command`, "Unknown command.", {
|
|
301
|
+
const knownLen = longestKnownPrefix(cmdPath);
|
|
302
|
+
if (knownLen < cmdPath.length) {
|
|
303
|
+
return emitError(new CliError("USAGE ERROR: Unknown command", "Unknown command.", {
|
|
354
304
|
exitCode: EXIT_USAGE,
|
|
355
|
-
required,
|
|
356
|
-
received: [
|
|
357
|
-
nextSteps: [
|
|
358
|
-
}), jsonMode);
|
|
305
|
+
required: ["valid scriptctl command"],
|
|
306
|
+
received: [cmdPath.join(" ")],
|
|
307
|
+
nextSteps: ["Run: scriptctl --help"],
|
|
308
|
+
}), state.jsonMode);
|
|
359
309
|
}
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
if (helpText === undefined && command.length >= 2) {
|
|
363
|
-
helpText = getHelp(command.slice(0, 2));
|
|
364
|
-
}
|
|
365
|
-
if (helpText === undefined)
|
|
366
|
-
helpText = getHelp([]) ?? "";
|
|
367
|
-
process.stdout.write(helpText);
|
|
310
|
+
if (wantsHelp) {
|
|
311
|
+
process.stdout.write(getHelp(cmdPath) ?? getHelp([]) ?? "");
|
|
368
312
|
return EXIT_OK;
|
|
369
313
|
}
|
|
314
|
+
const program = buildProgram(state);
|
|
370
315
|
try {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (
|
|
376
|
-
|
|
316
|
+
await program.parseAsync(args, { from: "user" });
|
|
317
|
+
return state.exitCode;
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
if (err instanceof CommanderError) {
|
|
321
|
+
const code = err.code;
|
|
322
|
+
if (code === "commander.help" ||
|
|
323
|
+
code === "commander.helpDisplayed" ||
|
|
324
|
+
code === "commander.version") {
|
|
325
|
+
return EXIT_OK;
|
|
326
|
+
}
|
|
327
|
+
const message = err.message || "Invalid arguments.";
|
|
328
|
+
return emitError(new CliError("USAGE ERROR: Invalid arguments", "Invalid arguments.", {
|
|
377
329
|
exitCode: EXIT_USAGE,
|
|
378
|
-
required: ["
|
|
379
|
-
received: [
|
|
380
|
-
nextSteps: ["Run scriptctl --help"],
|
|
381
|
-
});
|
|
330
|
+
required: ["Run: scriptctl --help"],
|
|
331
|
+
received: [message],
|
|
332
|
+
nextSteps: ["Run: scriptctl --help"],
|
|
333
|
+
}), state.jsonMode);
|
|
382
334
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return exitCode;
|
|
387
|
-
}
|
|
388
|
-
catch (exc) {
|
|
389
|
-
if (exc instanceof CliError)
|
|
390
|
-
return emitError(exc, jsonMode);
|
|
391
|
-
const err = exc;
|
|
335
|
+
if (err instanceof CliError)
|
|
336
|
+
return emitError(err, state.jsonMode);
|
|
337
|
+
const e = err;
|
|
392
338
|
return emitError(new CliError("RUNTIME FAILED: Unexpected error", "Unexpected error.", {
|
|
393
339
|
exitCode: 70,
|
|
394
340
|
required: ["successful command execution"],
|
|
395
|
-
received: [
|
|
341
|
+
received: [e?.name ?? "Error"],
|
|
396
342
|
nextSteps: ["Rerun with valid inputs or inspect workspace artifacts."],
|
|
397
|
-
}), jsonMode);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
function pickHandler(command) {
|
|
401
|
-
const k = key(command);
|
|
402
|
-
if (k === "direct.init")
|
|
403
|
-
return commandInit;
|
|
404
|
-
if (k === "direct.validate")
|
|
405
|
-
return (opts) => commandValidate(opts);
|
|
406
|
-
if (k === "direct.inspect")
|
|
407
|
-
return (opts) => commandInspect(opts);
|
|
408
|
-
if (k === "direct.export")
|
|
409
|
-
return commandExport;
|
|
410
|
-
if (k === "script.inspect")
|
|
411
|
-
return commandScriptInspect;
|
|
412
|
-
if (k === "script.validate")
|
|
413
|
-
return commandScriptValidate;
|
|
414
|
-
if (k === "script.patch")
|
|
415
|
-
return commandScriptPatch;
|
|
416
|
-
if (command.length === 3 && command[0] === "script" && command[1] === "state") {
|
|
417
|
-
return (opts) => commandScriptState(opts, command[2]);
|
|
418
|
-
}
|
|
419
|
-
if (command.length === 3 && command[0] === "script" && command[1] === "context") {
|
|
420
|
-
return (opts) => commandScriptContext(opts, command[2]);
|
|
421
|
-
}
|
|
422
|
-
if (command.length === 3 && command[0] === "script" && command[1] === "action") {
|
|
423
|
-
return (opts) => commandScriptAction(opts, command[2]);
|
|
424
|
-
}
|
|
425
|
-
if (command.length === 3 && command[0] === "script" && command[1] === "speaker") {
|
|
426
|
-
return (opts) => commandScriptSpeaker(opts, command[2]);
|
|
427
|
-
}
|
|
428
|
-
if (command.length === 3 && command[0] === "script" && command[1] === "dialogue") {
|
|
429
|
-
return (opts) => commandScriptDialogue(opts, command[2]);
|
|
343
|
+
}), state.jsonMode);
|
|
430
344
|
}
|
|
431
|
-
return null;
|
|
432
345
|
}
|
|
433
346
|
//# sourceMappingURL=cli.js.map
|