@mcptoolshop/style-dataset-lab 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -15
- package/CODE_OF_CONDUCT.md +9 -0
- package/CONTRIBUTING.md +71 -0
- package/README.md +46 -1
- package/SECURITY.md +6 -5
- package/bin/sdlab.js +281 -62
- package/lib/adapters/diffusers-lora.js +7 -2
- package/lib/adapters/generic-image-caption.js +6 -1
- package/lib/args.js +200 -13
- package/lib/batch-compiler.js +7 -3
- package/lib/batch-runs.js +100 -31
- package/lib/batch-sheet-render.js +5 -2
- package/lib/brief-compiler.js +38 -11
- package/lib/card.js +11 -4
- package/lib/comfyui.js +100 -6
- package/lib/config.js +68 -3
- package/lib/deprecation.js +1 -1
- package/lib/eval-pack.js +94 -4
- package/lib/eval-runs.js +42 -3
- package/lib/export.js +144 -34
- package/lib/implementation-packs.js +15 -1
- package/lib/log.js +41 -4
- package/lib/paths.js +41 -3
- package/lib/refine-briefs.js +16 -9
- package/lib/reingest-selected.js +14 -3
- package/lib/reingest.js +15 -1
- package/lib/run-summary.js +3 -2
- package/lib/runtime-runs.js +59 -9
- package/lib/selections.js +54 -2
- package/lib/snapshot.js +100 -7
- package/lib/split.js +175 -17
- package/lib/training-manifests.js +17 -3
- package/lib/training-packages.js +28 -14
- package/package.json +8 -2
- package/scripts/batch-generate.js +16 -7
- package/scripts/batch-sheet.js +3 -3
- package/scripts/batch-show.js +3 -3
- package/scripts/brief-compile.js +8 -8
- package/scripts/brief-show.js +4 -3
- package/scripts/canon-bind.js +7 -9
- package/scripts/card.js +22 -25
- package/scripts/compare.js +38 -33
- package/scripts/critique-show.js +3 -3
- package/scripts/critique.js +3 -3
- package/scripts/curate.js +91 -46
- package/scripts/doctor.js +3 -5
- package/scripts/eligibility.js +17 -14
- package/scripts/eval-pack.js +13 -11
- package/scripts/eval-run.js +30 -29
- package/scripts/export.js +27 -31
- package/scripts/generate-controlnet.js +31 -17
- package/scripts/generate-identity.js +37 -20
- package/scripts/generate-ipadapter.js +22 -16
- package/scripts/generate.js +68 -13
- package/scripts/implementation-pack.js +19 -14
- package/scripts/init.js +115 -17
- package/scripts/painterly.js +8 -10
- package/scripts/refine.js +3 -3
- package/scripts/reingest-selected.js +3 -3
- package/scripts/reingest.js +33 -20
- package/scripts/run-generate.js +4 -4
- package/scripts/run-list.js +3 -3
- package/scripts/run-show.js +3 -3
- package/scripts/select.js +5 -4
- package/scripts/selection-show.js +3 -3
- package/scripts/snapshot.js +22 -24
- package/scripts/split.js +26 -31
- package/scripts/training-manifest.js +29 -24
- package/scripts/training-package.js +24 -20
- package/scripts/training-profile.js +13 -8
- package/scripts/workflow-list.js +2 -2
- package/scripts/workflow-show.js +4 -3
- package/templates/domains/architecture/inputs/prompts/example-wave.json +32 -0
- package/templates/domains/architecture/terminology.json +10 -8
- package/templates/domains/character-design/inputs/prompts/example-wave.json +32 -0
- package/templates/domains/character-design/project.json +4 -2
- package/templates/domains/creature-design/inputs/prompts/example-wave.json +32 -0
- package/templates/domains/creature-design/project.json +4 -2
- package/templates/domains/creature-design/terminology.json +1 -0
- package/templates/domains/game-art/inputs/prompts/example-wave.json +37 -0
- package/templates/domains/game-art/project.json +4 -2
- package/templates/domains/vehicle-mech/inputs/prompts/example-wave.json +37 -0
- package/templates/domains/vehicle-mech/project.json +4 -2
- package/scripts/bulk-curate-wave2-5.js +0 -138
- package/scripts/bulk-curate-waves11-18.js +0 -413
- package/scripts/curate-wave25.js +0 -209
- package/scripts/migrate-records.js +0 -141
- package/templates/inputs/prompts/example-wave.json +0 -27
package/scripts/init.js
CHANGED
|
@@ -13,6 +13,7 @@ import { mkdir, readdir, readFile, writeFile, copyFile } from 'node:fs/promises'
|
|
|
13
13
|
import { join, basename } from 'node:path';
|
|
14
14
|
import { existsSync } from 'node:fs';
|
|
15
15
|
import { REPO_ROOT } from '../lib/paths.js';
|
|
16
|
+
import { inputError, handleCliError } from '../lib/errors.js';
|
|
16
17
|
|
|
17
18
|
const TEMPLATES_DIR = join(REPO_ROOT, 'templates');
|
|
18
19
|
const PROJECTS_DIR = join(REPO_ROOT, 'projects');
|
|
@@ -82,15 +83,28 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
if (!isValidName(projectName)) {
|
|
85
|
-
throw
|
|
86
|
-
|
|
86
|
+
throw inputError(
|
|
87
|
+
'INPUT_BAD_NAME',
|
|
88
|
+
`Invalid project name "${projectName}".`,
|
|
87
89
|
`Use lowercase letters, numbers, and hyphens (e.g. "my-project").`
|
|
88
90
|
);
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
// Validate --domain to prevent path traversal via template lookup.
|
|
94
|
+
if (!isValidName(domain)) {
|
|
95
|
+
throw inputError(
|
|
96
|
+
'INPUT_BAD_DOMAIN',
|
|
97
|
+
`Invalid --domain "${domain}".`,
|
|
98
|
+
`Use lowercase letters, numbers, and hyphens (e.g. "character-design").`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
const projectDir = join(PROJECTS_DIR, projectName);
|
|
92
103
|
if (existsSync(projectDir)) {
|
|
93
|
-
throw
|
|
104
|
+
throw inputError(
|
|
105
|
+
'INPUT_PROJECT_EXISTS',
|
|
106
|
+
`Project "${projectName}" already exists at ${projectDir}`
|
|
107
|
+
);
|
|
94
108
|
}
|
|
95
109
|
|
|
96
110
|
// Resolve domain template source
|
|
@@ -99,8 +113,9 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
99
113
|
domainDir = join(TEMPLATES_DIR, 'domains', domain);
|
|
100
114
|
if (!existsSync(domainDir)) {
|
|
101
115
|
const domains = await listDomains();
|
|
102
|
-
throw
|
|
103
|
-
|
|
116
|
+
throw inputError(
|
|
117
|
+
'INPUT_UNKNOWN_DOMAIN',
|
|
118
|
+
`Domain "${domain}" not found.`,
|
|
104
119
|
`Available: generic, ${domains.join(', ')}`
|
|
105
120
|
);
|
|
106
121
|
}
|
|
@@ -206,18 +221,30 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
206
221
|
|
|
207
222
|
// Copy canon markdown templates
|
|
208
223
|
const canonTemplates = ['constitution.md', 'review-rubric.md'];
|
|
224
|
+
const wroteCanon = {};
|
|
209
225
|
for (const file of canonTemplates) {
|
|
210
226
|
const src = join(TEMPLATES_DIR, 'canon', file);
|
|
211
227
|
if (existsSync(src)) {
|
|
212
228
|
await copyFile(src, join(projectDir, 'canon', file));
|
|
229
|
+
wroteCanon[file] = true;
|
|
213
230
|
console.log(` \x1b[32m✓\x1b[0m canon/${file}`);
|
|
214
231
|
}
|
|
215
232
|
}
|
|
216
233
|
|
|
217
|
-
// Copy example prompt pack
|
|
218
|
-
|
|
219
|
-
|
|
234
|
+
// Copy example prompt pack — prefer per-domain example (subjects match domain lane id_patterns),
|
|
235
|
+
// fall back to legacy global template if it exists (pre-v3.0.1 installs may still ship it).
|
|
236
|
+
let wroteExamplePack = false;
|
|
237
|
+
const domainExamplePack = domainDir
|
|
238
|
+
? join(domainDir, 'inputs', 'prompts', 'example-wave.json')
|
|
239
|
+
: null;
|
|
240
|
+
const globalExamplePack = join(TEMPLATES_DIR, 'inputs', 'prompts', 'example-wave.json');
|
|
241
|
+
const examplePack =
|
|
242
|
+
domainExamplePack && existsSync(domainExamplePack) ? domainExamplePack
|
|
243
|
+
: existsSync(globalExamplePack) ? globalExamplePack
|
|
244
|
+
: null;
|
|
245
|
+
if (examplePack) {
|
|
220
246
|
await copyFile(examplePack, join(projectDir, 'inputs', 'prompts', 'example-wave.json'));
|
|
247
|
+
wroteExamplePack = true;
|
|
221
248
|
console.log(` \x1b[32m✓\x1b[0m inputs/prompts/example-wave.json`);
|
|
222
249
|
}
|
|
223
250
|
|
|
@@ -236,21 +263,92 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
236
263
|
}
|
|
237
264
|
}
|
|
238
265
|
|
|
266
|
+
// Write a starter README.md inside the project so users have a home base
|
|
267
|
+
// with copy-pasteable next commands and a place for their notes.
|
|
268
|
+
const readmeLines = [
|
|
269
|
+
`# ${projectName}`,
|
|
270
|
+
'',
|
|
271
|
+
`A \`style-dataset-lab\` project — domain: **${domain}**.`,
|
|
272
|
+
'',
|
|
273
|
+
'## Quick commands',
|
|
274
|
+
'',
|
|
275
|
+
'```bash',
|
|
276
|
+
`# Validate that the scaffold is healthy`,
|
|
277
|
+
`sdlab project doctor --project ${projectName}`,
|
|
278
|
+
'',
|
|
279
|
+
];
|
|
280
|
+
if (wroteExamplePack) {
|
|
281
|
+
readmeLines.push(
|
|
282
|
+
`# Generate candidates from the example prompt pack`,
|
|
283
|
+
`sdlab generate inputs/prompts/example-wave.json --project ${projectName}`,
|
|
284
|
+
''
|
|
285
|
+
);
|
|
286
|
+
} else {
|
|
287
|
+
readmeLines.push(
|
|
288
|
+
`# Generate candidates (add a prompt pack under inputs/prompts/ first)`,
|
|
289
|
+
`sdlab generate <pack-path> --project ${projectName}`,
|
|
290
|
+
''
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
readmeLines.push(
|
|
294
|
+
`# Curate a candidate (approved | rejected | borderline)`,
|
|
295
|
+
`sdlab curate <asset_id> approved "reads clean" --project ${projectName}`,
|
|
296
|
+
'',
|
|
297
|
+
`# Bind approved records to constitution rules`,
|
|
298
|
+
`sdlab canon-bind --project ${projectName}`,
|
|
299
|
+
'',
|
|
300
|
+
`# Create a frozen dataset snapshot`,
|
|
301
|
+
`sdlab snapshot create --project ${projectName}`,
|
|
302
|
+
'```',
|
|
303
|
+
'',
|
|
304
|
+
'## Project layout',
|
|
305
|
+
'',
|
|
306
|
+
'- `project.json` — defaults (checkpoint, width, height, sampler)',
|
|
307
|
+
'- `constitution.json` — machine-readable style rules',
|
|
308
|
+
'- `canon/` — human-authored style reference (constitution.md, review-rubric.md)',
|
|
309
|
+
'- `inputs/prompts/` — generation prompt packs',
|
|
310
|
+
'- `outputs/candidates/` — raw generated PNGs',
|
|
311
|
+
'- `outputs/approved/` · `outputs/rejected/` · `outputs/borderline/` — curated outputs',
|
|
312
|
+
'- `records/` — provenance + judgment records (one JSON per candidate)',
|
|
313
|
+
'- `snapshots/` · `splits/` · `exports/` — frozen dataset artifacts',
|
|
314
|
+
'',
|
|
315
|
+
'## Docs',
|
|
316
|
+
'',
|
|
317
|
+
'- Handbook: https://mcp-tool-shop-org.github.io/style-dataset-lab/',
|
|
318
|
+
'- CLI reference: `sdlab --help` (or `sdlab <cmd> --help` for any command)',
|
|
319
|
+
'',
|
|
320
|
+
'## TODO',
|
|
321
|
+
'',
|
|
322
|
+
'- [ ] Fill in `canon/constitution.md` with your style rules',
|
|
323
|
+
'- [ ] Encode those rules in `constitution.json` for canon-bind',
|
|
324
|
+
'- [ ] Add at least one prompt pack under `inputs/prompts/`',
|
|
325
|
+
'- [ ] Run `sdlab project doctor` and fix any warnings',
|
|
326
|
+
''
|
|
327
|
+
);
|
|
328
|
+
await writeFile(join(projectDir, 'README.md'), readmeLines.join('\n'));
|
|
329
|
+
console.log(` \x1b[32m✓\x1b[0m README.md`);
|
|
330
|
+
|
|
239
331
|
console.log('');
|
|
240
332
|
console.log(`\x1b[32m✓\x1b[0m Project "${projectName}" initialized`);
|
|
241
333
|
console.log('');
|
|
242
334
|
console.log('Next steps:');
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
console.log(`
|
|
335
|
+
let step = 1;
|
|
336
|
+
if (wroteCanon['constitution.md']) {
|
|
337
|
+
console.log(` ${step++}. Edit ${projectName}/canon/constitution.md — define your visual rules`);
|
|
338
|
+
}
|
|
339
|
+
console.log(` ${step++}. Edit ${projectName}/constitution.json — encode rules for canon-bind`);
|
|
340
|
+
if (wroteExamplePack) {
|
|
341
|
+
console.log(` ${step++}. Run: sdlab generate inputs/prompts/example-wave.json --project ${projectName}`);
|
|
342
|
+
} else {
|
|
343
|
+
console.log(` ${step++}. Create prompt packs under ${projectName}/inputs/prompts/`);
|
|
344
|
+
console.log(` ${step++}. Run: sdlab generate <pack> --project ${projectName}`);
|
|
345
|
+
}
|
|
346
|
+
console.log(` ${step++}. Run: sdlab project doctor --project ${projectName}`);
|
|
347
|
+
console.log('');
|
|
348
|
+
console.log(`See ${projectName}/README.md for the full quick-start.`);
|
|
248
349
|
}
|
|
249
350
|
|
|
250
351
|
// Direct execution guard
|
|
251
352
|
if (process.argv[1] && (process.argv[1].endsWith('init.js') || process.argv[1].endsWith('init'))) {
|
|
252
|
-
run().catch(
|
|
253
|
-
console.error(err.message || err);
|
|
254
|
-
process.exit(1);
|
|
255
|
-
});
|
|
353
|
+
run().catch(handleCliError);
|
|
256
354
|
}
|
package/scripts/painterly.js
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
import { writeFile, mkdir, readdir } from "node:fs/promises";
|
|
19
19
|
import { join } from "node:path";
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
|
-
import { getProjectName } from "../lib/args.js";
|
|
22
|
-
import { REPO_ROOT } from "../lib/paths.js";
|
|
21
|
+
import { getProjectName, parseNumberFlag } from "../lib/args.js";
|
|
22
|
+
import { REPO_ROOT, resolveSafeProjectPath } from "../lib/paths.js";
|
|
23
|
+
import { runtimeError, handleCliError } from "../lib/errors.js";
|
|
23
24
|
import { comfyHealth, submitAndWait, downloadImage, uploadImage } from "../lib/comfyui.js";
|
|
24
25
|
|
|
25
26
|
// ── Config ──
|
|
@@ -156,11 +157,11 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
156
157
|
|
|
157
158
|
for (let i = 0; i < argv.length; i++) {
|
|
158
159
|
if (argv[i] === "--source" && argv[i + 1]) sourceDir = argv[++i];
|
|
159
|
-
if (argv[i] === "--limit" && argv[i + 1]) limit =
|
|
160
|
-
if (argv[i] === "--offset" && argv[i + 1]) offset =
|
|
160
|
+
else if (argv[i] === "--limit" && argv[i + 1]) limit = parseNumberFlag('limit', argv[++i], { int: true, min: 1 });
|
|
161
|
+
else if (argv[i] === "--offset" && argv[i + 1]) offset = parseNumberFlag('offset', argv[++i], { int: true, min: 0 });
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
const fullSourceDir =
|
|
164
|
+
const fullSourceDir = resolveSafeProjectPath(GAME_ROOT, sourceDir, { flagName: 'source' });
|
|
164
165
|
const outDir = join(GAME_ROOT, "outputs/painterly");
|
|
165
166
|
await mkdir(outDir, { recursive: true });
|
|
166
167
|
|
|
@@ -195,7 +196,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
195
196
|
if (!dryRun) {
|
|
196
197
|
const online = await comfyHealth(COMFY_URL);
|
|
197
198
|
if (!online) {
|
|
198
|
-
throw
|
|
199
|
+
throw runtimeError('RUNTIME_COMFY_UNREACHABLE', "ComfyUI not reachable at " + COMFY_URL);
|
|
199
200
|
}
|
|
200
201
|
console.log("\x1b[32m✓\x1b[0m ComfyUI online");
|
|
201
202
|
}
|
|
@@ -263,8 +264,5 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
263
264
|
|
|
264
265
|
// Direct execution guard
|
|
265
266
|
if (process.argv[1] && (process.argv[1].endsWith('painterly.js') || process.argv[1].endsWith('painterly'))) {
|
|
266
|
-
run().catch(
|
|
267
|
-
console.error(err.message || err);
|
|
268
|
-
process.exit(1);
|
|
269
|
-
});
|
|
267
|
+
run().catch(handleCliError);
|
|
270
268
|
}
|
package/scripts/refine.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { readFileSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import { parseArgs } from '../lib/args.js';
|
|
14
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
15
15
|
import { getProjectRoot, getRunsDir } from '../lib/paths.js';
|
|
16
16
|
import { loadCritique } from '../lib/critique-engine.js';
|
|
17
17
|
import {
|
|
@@ -25,7 +25,7 @@ import { info } from '../lib/log.js';
|
|
|
25
25
|
export async function run(argv = process.argv.slice(2)) {
|
|
26
26
|
const { flags } = parseArgs(argv, {
|
|
27
27
|
flags: {
|
|
28
|
-
project: { type: 'string'
|
|
28
|
+
project: { type: 'string' },
|
|
29
29
|
run: { type: 'string' },
|
|
30
30
|
pick: { type: 'string' },
|
|
31
31
|
preserve: { type: 'string' },
|
|
@@ -49,7 +49,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const projectName = flags.project;
|
|
52
|
+
const projectName = flags.project || getProjectName(argv);
|
|
53
53
|
const projectRoot = getProjectRoot(projectName);
|
|
54
54
|
const runId = flags.run;
|
|
55
55
|
const pickedFilename = flags.pick;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* sdlab reingest selected --selection selection_2026-04-16_001 --dry-run
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { parseArgs } from '../lib/args.js';
|
|
11
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
12
12
|
import { getProjectRoot } from '../lib/paths.js';
|
|
13
13
|
import { reingestSelection } from '../lib/reingest-selected.js';
|
|
14
14
|
import { info } from '../lib/log.js';
|
|
@@ -16,7 +16,7 @@ import { info } from '../lib/log.js';
|
|
|
16
16
|
export async function run(argv = process.argv.slice(2)) {
|
|
17
17
|
const { flags } = parseArgs(argv, {
|
|
18
18
|
flags: {
|
|
19
|
-
project: { type: 'string'
|
|
19
|
+
project: { type: 'string' },
|
|
20
20
|
selection: { type: 'string' },
|
|
21
21
|
'dry-run': { type: 'boolean' },
|
|
22
22
|
json: { type: 'boolean' },
|
|
@@ -34,7 +34,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const projectName = flags.project;
|
|
37
|
+
const projectName = flags.project || getProjectName(argv);
|
|
38
38
|
const projectRoot = getProjectRoot(projectName);
|
|
39
39
|
const dryRun = flags['dry-run'] || false;
|
|
40
40
|
|
package/scripts/reingest.js
CHANGED
|
@@ -5,38 +5,51 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* sdlab reingest generated --source <dir> --manifest <id> [--dry-run] [--project <name>]
|
|
8
|
+
* sdlab reingest selected --selection <id> [--project <name>]
|
|
8
9
|
* sdlab reingest audit [--project <name>]
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import { join } from 'node:path';
|
|
12
|
-
import { getProjectName } from '../lib/args.js';
|
|
13
|
-
import { REPO_ROOT } from '../lib/paths.js';
|
|
13
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
14
|
+
import { REPO_ROOT, resolveSafeProjectPath } from '../lib/paths.js';
|
|
15
|
+
import { inputError, handleCliError } from '../lib/errors.js';
|
|
14
16
|
import { reingestGenerated, auditReingest } from '../lib/reingest.js';
|
|
15
17
|
|
|
16
18
|
export async function run(argv = process.argv.slice(2)) {
|
|
17
|
-
const
|
|
19
|
+
const { flags, positionals } = parseArgs(argv, {
|
|
20
|
+
flags: {
|
|
21
|
+
project: { type: 'string' },
|
|
22
|
+
source: { type: 'string' },
|
|
23
|
+
manifest: { type: 'string' },
|
|
24
|
+
selection: { type: 'string' },
|
|
25
|
+
'dry-run': { type: 'boolean', default: false },
|
|
26
|
+
},
|
|
27
|
+
deprecated: { game: 'project' },
|
|
28
|
+
allowUnknown: true, // forward unknowns to delegated 'selected' path
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const projectName = flags.project || getProjectName(argv);
|
|
18
32
|
const projectRoot = join(REPO_ROOT, 'projects', projectName);
|
|
19
|
-
const subcommand =
|
|
20
|
-
const args = argv.filter(a => a !== subcommand);
|
|
33
|
+
const subcommand = positionals[0] || 'audit';
|
|
21
34
|
|
|
22
35
|
if (subcommand === 'generated') {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const manifestId = args[manifestIdx + 1];
|
|
36
|
+
if (!flags.source) {
|
|
37
|
+
throw inputError('INPUT_MISSING_FLAG', '--source <dir> is required (directory of generated images)');
|
|
38
|
+
}
|
|
39
|
+
if (!flags.manifest) {
|
|
40
|
+
throw inputError('INPUT_MISSING_FLAG', '--manifest <id> is required (training manifest that produced the outputs)');
|
|
41
|
+
}
|
|
30
42
|
|
|
31
|
-
const
|
|
43
|
+
const safeSource = resolveSafeProjectPath(projectRoot, flags.source, { baseRoot: REPO_ROOT, flagName: 'source' });
|
|
44
|
+
const dryRun = flags['dry-run'] || flags.dryRun;
|
|
32
45
|
|
|
33
46
|
console.log(`\x1b[1msdlab reingest generated\x1b[0m — ${projectName}`);
|
|
34
|
-
console.log(` Source: ${
|
|
35
|
-
console.log(` Manifest: ${
|
|
47
|
+
console.log(` Source: ${flags.source}`);
|
|
48
|
+
console.log(` Manifest: ${flags.manifest}`);
|
|
36
49
|
if (dryRun) console.log(` Mode: DRY RUN (no records written)`);
|
|
37
50
|
console.log('');
|
|
38
51
|
|
|
39
|
-
const result = await reingestGenerated(projectRoot,
|
|
52
|
+
const result = await reingestGenerated(projectRoot, safeSource, flags.manifest, { dryRun });
|
|
40
53
|
|
|
41
54
|
console.log(` \x1b[32m✓\x1b[0m Created: ${result.created} records`);
|
|
42
55
|
if (result.skipped > 0) {
|
|
@@ -51,9 +64,9 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
} else if (subcommand === 'selected') {
|
|
54
|
-
// Delegate to reingest-selected.js
|
|
67
|
+
// Delegate to reingest-selected.js — pass the full argv so it can re-parse
|
|
55
68
|
const { run: runSelected } = await import('./reingest-selected.js');
|
|
56
|
-
await runSelected(
|
|
69
|
+
await runSelected(argv.slice(argv.indexOf(subcommand) + 1));
|
|
57
70
|
return;
|
|
58
71
|
|
|
59
72
|
} else if (subcommand === 'audit') {
|
|
@@ -83,10 +96,10 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
} else {
|
|
86
|
-
throw
|
|
99
|
+
throw inputError('INPUT_BAD_SUBCOMMAND', `Unknown subcommand: ${subcommand}. Use: generated, selected, audit`);
|
|
87
100
|
}
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
if (process.argv[1] && (process.argv[1].endsWith('reingest.js') || process.argv[1].endsWith('reingest'))) {
|
|
91
|
-
run().catch(
|
|
104
|
+
run().catch(handleCliError);
|
|
92
105
|
}
|
package/scripts/run-generate.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* sdlab run generate --brief brief_2026-04-15_001 --project star-freight --seed 42
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { parseArgs } from '../lib/args.js';
|
|
12
|
+
import { parseArgs, getProjectName, parseNumberFlag } from '../lib/args.js';
|
|
13
13
|
import { getProjectRoot, getRunsDir } from '../lib/paths.js';
|
|
14
14
|
import { loadCompiledBrief } from '../lib/brief-compiler.js';
|
|
15
15
|
import { executeRun } from '../lib/adapters/comfyui-runner.js';
|
|
@@ -20,7 +20,7 @@ import { join } from 'node:path';
|
|
|
20
20
|
export async function run(argv = process.argv.slice(2)) {
|
|
21
21
|
const { flags } = parseArgs(argv, {
|
|
22
22
|
flags: {
|
|
23
|
-
project: { type: 'string'
|
|
23
|
+
project: { type: 'string' },
|
|
24
24
|
brief: { type: 'string' },
|
|
25
25
|
seed: { type: 'string' },
|
|
26
26
|
'dry-run': { type: 'boolean' },
|
|
@@ -40,7 +40,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const projectName = flags.project;
|
|
43
|
+
const projectName = flags.project || getProjectName(argv);
|
|
44
44
|
const projectRoot = getProjectRoot(projectName);
|
|
45
45
|
|
|
46
46
|
console.log(`\x1b[1mstyle-dataset-lab\x1b[0m run generate`);
|
|
@@ -57,7 +57,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
57
57
|
projectRoot,
|
|
58
58
|
projectName,
|
|
59
59
|
brief,
|
|
60
|
-
baseSeed: flags.seed ?
|
|
60
|
+
baseSeed: flags.seed ? parseNumberFlag('seed', flags.seed, { int: true, min: 0 }) : undefined,
|
|
61
61
|
dryRun: flags['dry-run'] || false,
|
|
62
62
|
});
|
|
63
63
|
|
package/scripts/run-list.js
CHANGED
|
@@ -8,20 +8,20 @@
|
|
|
8
8
|
* sdlab run list --project star-freight --json
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { parseArgs } from '../lib/args.js';
|
|
11
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
12
12
|
import { getProjectRoot } from '../lib/paths.js';
|
|
13
13
|
import { listRuns } from '../lib/runtime-runs.js';
|
|
14
14
|
|
|
15
15
|
export async function run(argv = process.argv.slice(2)) {
|
|
16
16
|
const { flags } = parseArgs(argv, {
|
|
17
17
|
flags: {
|
|
18
|
-
project: { type: 'string'
|
|
18
|
+
project: { type: 'string' },
|
|
19
19
|
json: { type: 'boolean' },
|
|
20
20
|
},
|
|
21
21
|
deprecated: { game: 'project' },
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const projectName = flags.project;
|
|
24
|
+
const projectName = flags.project || getProjectName(argv);
|
|
25
25
|
const projectRoot = getProjectRoot(projectName);
|
|
26
26
|
const runs = listRuns(projectRoot);
|
|
27
27
|
|
package/scripts/run-show.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* sdlab run show run_2026-04-16_001 --project star-freight --md
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { parseArgs } from '../lib/args.js';
|
|
12
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
13
13
|
import { getProjectRoot } from '../lib/paths.js';
|
|
14
14
|
import { loadRun, listRuns } from '../lib/runtime-runs.js';
|
|
15
15
|
import { renderRunText, renderRunMarkdown } from '../lib/run-summary.js';
|
|
@@ -17,7 +17,7 @@ import { renderRunText, renderRunMarkdown } from '../lib/run-summary.js';
|
|
|
17
17
|
export async function run(argv = process.argv.slice(2)) {
|
|
18
18
|
const { positionals, flags } = parseArgs(argv, {
|
|
19
19
|
flags: {
|
|
20
|
-
project: { type: 'string'
|
|
20
|
+
project: { type: 'string' },
|
|
21
21
|
json: { type: 'boolean' },
|
|
22
22
|
md: { type: 'boolean' },
|
|
23
23
|
},
|
|
@@ -25,7 +25,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
const runId = positionals[0];
|
|
28
|
-
const projectName = flags.project;
|
|
28
|
+
const projectName = flags.project || getProjectName(argv);
|
|
29
29
|
const projectRoot = getProjectRoot(projectName);
|
|
30
30
|
|
|
31
31
|
// No run ID → list runs
|
package/scripts/select.js
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* sdlab select --run run_2026-04-15_001 --approve 001.png --reason "best continuity" --tags portrait,keeper
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { parseArgs } from '../lib/args.js';
|
|
12
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
13
13
|
import { getProjectRoot } from '../lib/paths.js';
|
|
14
|
+
import { inputError, handleCliError } from '../lib/errors.js';
|
|
14
15
|
import {
|
|
15
16
|
createSelectionFromRun,
|
|
16
17
|
createSelectionFromBatch,
|
|
@@ -21,7 +22,7 @@ import { info } from '../lib/log.js';
|
|
|
21
22
|
export async function run(argv = process.argv.slice(2)) {
|
|
22
23
|
const { flags } = parseArgs(argv, {
|
|
23
24
|
flags: {
|
|
24
|
-
project: { type: 'string'
|
|
25
|
+
project: { type: 'string' },
|
|
25
26
|
run: { type: 'string' },
|
|
26
27
|
batch: { type: 'string' },
|
|
27
28
|
approve: { type: 'string' },
|
|
@@ -46,7 +47,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
const projectName = flags.project;
|
|
50
|
+
const projectName = flags.project || getProjectName(argv);
|
|
50
51
|
const projectRoot = getProjectRoot(projectName);
|
|
51
52
|
const reason = flags.reason || undefined;
|
|
52
53
|
const tags = flags.tags ? flags.tags.split(',').map(t => t.trim()) : undefined;
|
|
@@ -71,7 +72,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
71
72
|
const approvedSlots = pairs.map(pair => {
|
|
72
73
|
const colonIdx = pair.indexOf(':');
|
|
73
74
|
if (colonIdx < 0) {
|
|
74
|
-
throw
|
|
75
|
+
throw inputError('INPUT_BAD_FORMAT', `Invalid batch approve format: "${pair}". Use slot_id:filename`);
|
|
75
76
|
}
|
|
76
77
|
return {
|
|
77
78
|
slotId: pair.slice(0, colonIdx),
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* sdlab selection show selection_2026-04-16_001 --json
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { parseArgs } from '../lib/args.js';
|
|
12
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
13
13
|
import { getProjectRoot } from '../lib/paths.js';
|
|
14
14
|
import { loadSelection, listSelections, getSelectionsDir } from '../lib/selections.js';
|
|
15
15
|
import { join } from 'node:path';
|
|
@@ -18,14 +18,14 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
18
18
|
export async function run(argv = process.argv.slice(2)) {
|
|
19
19
|
const { flags, positionals } = parseArgs(argv, {
|
|
20
20
|
flags: {
|
|
21
|
-
project: { type: 'string'
|
|
21
|
+
project: { type: 'string' },
|
|
22
22
|
selection: { type: 'string' },
|
|
23
23
|
json: { type: 'boolean' },
|
|
24
24
|
},
|
|
25
25
|
deprecated: { game: 'project' },
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
const projectName = flags.project;
|
|
28
|
+
const projectName = flags.project || getProjectName(argv);
|
|
29
29
|
const projectRoot = getProjectRoot(projectName);
|
|
30
30
|
const selectionId = positionals[0] || flags.selection;
|
|
31
31
|
|
package/scripts/snapshot.js
CHANGED
|
@@ -12,30 +12,33 @@
|
|
|
12
12
|
|
|
13
13
|
import { readFile } from 'node:fs/promises';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
|
-
import {
|
|
16
|
-
import { getProjectName } from '../lib/args.js';
|
|
15
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
17
16
|
import { REPO_ROOT } from '../lib/paths.js';
|
|
18
17
|
import { loadSelectionProfile } from '../lib/config.js';
|
|
18
|
+
import { inputError, handleCliError } from '../lib/errors.js';
|
|
19
19
|
import { createSnapshot, loadSnapshot, listSnapshots, diffSnapshots } from '../lib/snapshot.js';
|
|
20
|
-
import { evaluateEligibility } from '../lib/eligibility.js';
|
|
21
20
|
import { categorizeExclusions } from '../lib/eligibility.js';
|
|
22
21
|
|
|
23
22
|
export async function run(argv = process.argv.slice(2)) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const { flags, positionals } = parseArgs(argv, {
|
|
24
|
+
flags: {
|
|
25
|
+
project: { type: 'string' },
|
|
26
|
+
profile: { type: 'string' },
|
|
27
|
+
'dry-run': { type: 'boolean', default: false },
|
|
28
|
+
},
|
|
29
|
+
deprecated: { game: 'project' },
|
|
30
|
+
});
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
const
|
|
32
|
+
const projectName = flags.project || getProjectName(argv);
|
|
33
|
+
const projectRoot = join(REPO_ROOT, 'projects', projectName);
|
|
34
|
+
const subcommand = positionals[0] || 'list';
|
|
35
|
+
const dryRun = flags['dry-run'] || flags.dryRun;
|
|
30
36
|
|
|
31
37
|
if (subcommand === 'create') {
|
|
32
|
-
const
|
|
33
|
-
const profileId = profileIdx >= 0 ? args[profileIdx + 1] : null;
|
|
34
|
-
const profile = loadSelectionProfile(projectRoot, profileId);
|
|
35
|
-
const dryRun = argv.includes('--dry-run');
|
|
38
|
+
const profile = loadSelectionProfile(projectRoot, flags.profile);
|
|
36
39
|
|
|
37
40
|
console.log(`\x1b[1msdlab snapshot create\x1b[0m — ${projectName}`);
|
|
38
|
-
console.log(` Profile: ${
|
|
41
|
+
console.log(` Profile: ${flags.profile || 'training-default (built-in)'}`);
|
|
39
42
|
if (dryRun) console.log(' Mode: DRY RUN (no files written)');
|
|
40
43
|
console.log('');
|
|
41
44
|
|
|
@@ -46,7 +49,6 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
46
49
|
console.log(` Excluded: ${result.excluded}`);
|
|
47
50
|
|
|
48
51
|
if (!dryRun) {
|
|
49
|
-
// Read and display summary
|
|
50
52
|
const summary = JSON.parse(
|
|
51
53
|
await readFile(join(projectRoot, 'snapshots', result.snapshotId, 'summary.json'), 'utf-8')
|
|
52
54
|
);
|
|
@@ -59,7 +61,6 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
59
61
|
console.log(` ${f}: ${c}`);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
// Read and categorize exclusions
|
|
63
64
|
const excludedRaw = await readFile(
|
|
64
65
|
join(projectRoot, 'snapshots', result.snapshotId, 'excluded.jsonl'), 'utf-8'
|
|
65
66
|
);
|
|
@@ -83,8 +84,8 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
} else if (subcommand === 'show') {
|
|
86
|
-
const snapshotId =
|
|
87
|
-
if (!snapshotId) throw
|
|
87
|
+
const snapshotId = positionals[1] || positionals.find(a => a.startsWith('snap-'));
|
|
88
|
+
if (!snapshotId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab snapshot show <snapshot-id>');
|
|
88
89
|
|
|
89
90
|
const manifest = await loadSnapshot(projectRoot, snapshotId);
|
|
90
91
|
console.log(`\x1b[1mSnapshot\x1b[0m — ${snapshotId}\n`);
|
|
@@ -102,8 +103,8 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
} else if (subcommand === 'diff') {
|
|
105
|
-
const snapIds =
|
|
106
|
-
if (snapIds.length < 2) throw
|
|
106
|
+
const snapIds = positionals.slice(1).filter(a => a.startsWith('snap-'));
|
|
107
|
+
if (snapIds.length < 2) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab snapshot diff <id-a> <id-b>');
|
|
107
108
|
|
|
108
109
|
const diff = await diffSnapshots(projectRoot, snapIds[0], snapIds[1]);
|
|
109
110
|
console.log(`\x1b[1mSnapshot diff\x1b[0m — ${snapIds[0]} vs ${snapIds[1]}\n`);
|
|
@@ -123,14 +124,11 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
} else {
|
|
126
|
-
throw
|
|
127
|
+
throw inputError('INPUT_BAD_SUBCOMMAND', `Unknown subcommand: ${subcommand}. Use: create, list, show, diff`);
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
// Direct execution guard
|
|
131
132
|
if (process.argv[1] && (process.argv[1].endsWith('snapshot.js') || process.argv[1].endsWith('snapshot'))) {
|
|
132
|
-
run().catch(
|
|
133
|
-
console.error(err.message || err);
|
|
134
|
-
process.exit(1);
|
|
135
|
-
});
|
|
133
|
+
run().catch(handleCliError);
|
|
136
134
|
}
|