@mcptoolshop/style-dataset-lab 2.2.1 → 3.0.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/CHANGELOG.md +125 -0
- package/README.es.md +76 -144
- package/README.fr.md +75 -143
- package/README.hi.md +75 -143
- package/README.it.md +76 -144
- package/README.ja.md +75 -143
- package/README.md +36 -8
- package/README.pt-BR.md +75 -143
- package/README.zh.md +76 -144
- package/SECURITY.md +3 -1
- package/bin/sdlab.js +146 -17
- package/lib/adapters/comfyui-runner.js +170 -0
- package/lib/adapters/comfyui-workflows.js +163 -0
- package/lib/batch-compiler.js +118 -0
- package/lib/batch-modes.js +111 -0
- package/lib/batch-runs.js +168 -0
- package/lib/batch-sheet-render.js +206 -0
- package/lib/brief-compiler.js +598 -0
- package/lib/brief-render.js +205 -0
- package/lib/critique-engine.js +413 -0
- package/lib/critique-render.js +171 -0
- package/lib/errors.js +82 -0
- package/lib/generated-provenance.js +87 -0
- package/lib/log.js +36 -0
- package/lib/paths.js +31 -0
- package/lib/refine-briefs.js +329 -0
- package/lib/reingest-selected.js +173 -0
- package/lib/run-summary.js +131 -0
- package/lib/runtime-runs.js +168 -0
- package/lib/selections.js +323 -0
- package/lib/snapshot.js +5 -2
- package/lib/split.js +5 -2
- package/lib/workflow-profiles.js +161 -0
- package/package.json +8 -6
- package/scripts/batch-generate.js +167 -0
- package/scripts/batch-sheet.js +46 -0
- package/scripts/batch-show.js +86 -0
- package/scripts/brief-compile.js +134 -0
- package/scripts/brief-show.js +52 -0
- package/scripts/critique-show.js +50 -0
- package/scripts/critique.js +70 -0
- package/scripts/doctor.js +169 -1
- package/scripts/init.js +22 -0
- package/scripts/refine.js +119 -0
- package/scripts/reingest-selected.js +78 -0
- package/scripts/reingest.js +7 -1
- package/scripts/run-generate.js +76 -0
- package/scripts/run-list.js +58 -0
- package/scripts/run-show.js +63 -0
- package/scripts/select.js +118 -0
- package/scripts/selection-show.js +88 -0
- package/scripts/snapshot.js +27 -23
- package/scripts/split.js +3 -1
- package/scripts/workflow-list.js +61 -0
- package/scripts/workflow-show.js +96 -0
- package/templates/domains/architecture/workflows/profiles/environment-moodboard.json +62 -0
- package/templates/domains/architecture/workflows/profiles/interior-variant-pack.json +60 -0
- package/templates/domains/character-design/workflows/profiles/character-portrait-set.json +61 -0
- package/templates/domains/character-design/workflows/profiles/expression-sheet.json +59 -0
- package/templates/domains/creature-design/workflows/profiles/creature-portrait-set.json +61 -0
- package/templates/domains/creature-design/workflows/profiles/silhouette-exploration.json +58 -0
- package/templates/domains/game-art/workflows/profiles/character-portrait-set.json +62 -0
- package/templates/domains/game-art/workflows/profiles/environment-moodboard.json +61 -0
- package/templates/domains/vehicle-mech/workflows/profiles/variant-pack.json +60 -0
- package/templates/domains/vehicle-mech/workflows/profiles/vehicle-silhouette-pack.json +59 -0
package/package.json
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcptoolshop/style-dataset-lab",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Canon-aligned dataset production and generation workbench — define visual rules, build versioned training data, compile production briefs, run local workflows, batch-produce, select winners, and re-ingest into your corpus",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sdlab": "./bin/sdlab.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
+
"verify": "node bin/sdlab.js --help && node bin/sdlab.js project doctor --project star-freight",
|
|
10
11
|
"generate": "node scripts/generate.js",
|
|
11
12
|
"curate": "node scripts/curate.js",
|
|
12
13
|
"compare": "node scripts/compare.js",
|
|
13
14
|
"canon-bind": "node scripts/canon-bind.js",
|
|
14
15
|
"painterly": "node scripts/painterly.js",
|
|
15
|
-
"generate:identity": "node scripts/generate-identity.js"
|
|
16
|
-
"export": "echo 'Usage: repo-dataset visual generate ./projects/<project> --format trl'",
|
|
17
|
-
"inspect": "echo 'Usage: repo-dataset visual inspect ./projects/<project>'"
|
|
16
|
+
"generate:identity": "node scripts/generate-identity.js"
|
|
18
17
|
},
|
|
19
18
|
"files": [
|
|
20
19
|
"bin/",
|
|
@@ -39,7 +38,10 @@
|
|
|
39
38
|
"style-guide",
|
|
40
39
|
"lora",
|
|
41
40
|
"multimodal",
|
|
42
|
-
"comfyui"
|
|
41
|
+
"comfyui",
|
|
42
|
+
"generation-workflow",
|
|
43
|
+
"batch-production",
|
|
44
|
+
"reintegration"
|
|
43
45
|
],
|
|
44
46
|
"homepage": "https://mcp-tool-shop-org.github.io/style-dataset-lab/",
|
|
45
47
|
"repository": {
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* batch-generate.js — Execute a batch production mode.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab batch generate --mode expression-sheet --subject captain-orryn --project star-freight
|
|
8
|
+
* sdlab batch generate --mode environment-board --theme freeport-night --project star-freight
|
|
9
|
+
* sdlab batch generate --mode silhouette-pack --project star-freight --dry-run
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from '../lib/args.js';
|
|
13
|
+
import { getProjectRoot, getRunsDir } from '../lib/paths.js';
|
|
14
|
+
import { compileBatch } from '../lib/batch-compiler.js';
|
|
15
|
+
import { getBatchMode } from '../lib/batch-modes.js';
|
|
16
|
+
import {
|
|
17
|
+
createBatchDir,
|
|
18
|
+
executeBatchRuns,
|
|
19
|
+
saveBatchManifest,
|
|
20
|
+
} from '../lib/batch-runs.js';
|
|
21
|
+
import { renderSheetHTML, saveSheet } from '../lib/batch-sheet-render.js';
|
|
22
|
+
import { info } from '../lib/log.js';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { writeFile } from 'node:fs/promises';
|
|
25
|
+
|
|
26
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
27
|
+
const { flags } = parseArgs(argv, {
|
|
28
|
+
flags: {
|
|
29
|
+
project: { type: 'string', default: 'star-freight' },
|
|
30
|
+
mode: { type: 'string' },
|
|
31
|
+
subject: { type: 'string' },
|
|
32
|
+
theme: { type: 'string' },
|
|
33
|
+
asset: { type: 'string' },
|
|
34
|
+
'dry-run': { type: 'boolean' },
|
|
35
|
+
json: { type: 'boolean' },
|
|
36
|
+
},
|
|
37
|
+
deprecated: { game: 'project' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!flags.mode) {
|
|
41
|
+
console.log('Usage: sdlab batch generate --mode <id> [--subject <id>] [--theme <label>] [--project <name>]');
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log('Options:');
|
|
44
|
+
console.log(' --mode <id> Batch mode (required)');
|
|
45
|
+
console.log(' --subject <id> Subject for subject-driven modes');
|
|
46
|
+
console.log(' --theme <label> Theme label for environment modes');
|
|
47
|
+
console.log(' --asset <id> Training asset reference');
|
|
48
|
+
console.log(' --dry-run Prepare batch without submitting to ComfyUI');
|
|
49
|
+
console.log(' --json Output manifest as JSON');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const projectName = flags.project;
|
|
54
|
+
const projectRoot = getProjectRoot(projectName);
|
|
55
|
+
const dryRun = flags['dry-run'] || false;
|
|
56
|
+
|
|
57
|
+
// Load mode for display
|
|
58
|
+
const mode = getBatchMode(projectRoot, flags.mode);
|
|
59
|
+
|
|
60
|
+
console.log(`\x1b[1mstyle-dataset-lab\x1b[0m batch generate`);
|
|
61
|
+
console.log(` Project: ${projectName}`);
|
|
62
|
+
console.log(` Mode: ${mode.label} (${mode.mode_id})`);
|
|
63
|
+
console.log(` Type: ${mode.batch_type}`);
|
|
64
|
+
console.log(` Slots: ${mode.variant_plan.length}`);
|
|
65
|
+
if (flags.subject) console.log(` Subject: ${flags.subject}`);
|
|
66
|
+
if (flags.theme) console.log(` Theme: ${flags.theme}`);
|
|
67
|
+
if (dryRun) console.log(' ⚠ DRY RUN');
|
|
68
|
+
console.log('');
|
|
69
|
+
|
|
70
|
+
// 1. Compile batch briefs
|
|
71
|
+
const { baseBrief, slotBriefs } = await compileBatch({
|
|
72
|
+
projectRoot,
|
|
73
|
+
projectId: projectName,
|
|
74
|
+
modeId: flags.mode,
|
|
75
|
+
subjectId: flags.subject,
|
|
76
|
+
theme: flags.theme,
|
|
77
|
+
assetRef: flags.asset,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 2. Create batch directory
|
|
81
|
+
const { batchId, batchDir } = await createBatchDir(projectRoot);
|
|
82
|
+
info(`Batch: ${batchId}`);
|
|
83
|
+
|
|
84
|
+
// 3. Execute runs
|
|
85
|
+
const results = await executeBatchRuns({
|
|
86
|
+
projectRoot,
|
|
87
|
+
projectName,
|
|
88
|
+
batchDir,
|
|
89
|
+
mode,
|
|
90
|
+
slotBriefs,
|
|
91
|
+
dryRun,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// 4. Build manifest
|
|
95
|
+
const manifest = {
|
|
96
|
+
batch_id: batchId,
|
|
97
|
+
project_id: projectName,
|
|
98
|
+
mode_id: mode.mode_id,
|
|
99
|
+
created_at: new Date().toISOString(),
|
|
100
|
+
brief_ids: results.map(r => r.brief_id),
|
|
101
|
+
run_ids: results.map(r => r.run_id),
|
|
102
|
+
slots: results.map(r => ({
|
|
103
|
+
slot_id: r.slot_id,
|
|
104
|
+
label: r.label,
|
|
105
|
+
brief_id: r.brief_id,
|
|
106
|
+
run_id: r.run_id,
|
|
107
|
+
selected_output: r.selected_output,
|
|
108
|
+
})),
|
|
109
|
+
sheet_paths: [],
|
|
110
|
+
summary: {
|
|
111
|
+
total_slots: results.length,
|
|
112
|
+
successful_slots: results.filter(r => r.selected_output).length,
|
|
113
|
+
dry_run: dryRun,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (flags.subject) manifest.subject_id = flags.subject;
|
|
118
|
+
if (flags.theme) manifest.theme = flags.theme;
|
|
119
|
+
|
|
120
|
+
// 5. Render sheet
|
|
121
|
+
const sheetHtml = renderSheetHTML({ manifest, mode, projectRoot, batchDir });
|
|
122
|
+
const sheetPath = await saveSheet(batchDir, 'primary-sheet.html', sheetHtml);
|
|
123
|
+
manifest.sheet_paths.push('sheets/primary-sheet.html');
|
|
124
|
+
|
|
125
|
+
// 6. Save manifest + summary
|
|
126
|
+
await saveBatchManifest(batchDir, manifest);
|
|
127
|
+
|
|
128
|
+
// Write summary.md
|
|
129
|
+
const summaryLines = [
|
|
130
|
+
`# Batch: ${batchId}`,
|
|
131
|
+
'',
|
|
132
|
+
`**Mode:** ${mode.label} (${mode.mode_id})`,
|
|
133
|
+
`**Type:** ${mode.batch_type}`,
|
|
134
|
+
`**Project:** ${projectName}`,
|
|
135
|
+
];
|
|
136
|
+
if (manifest.subject_id) summaryLines.push(`**Subject:** ${manifest.subject_id}`);
|
|
137
|
+
if (manifest.theme) summaryLines.push(`**Theme:** ${manifest.theme}`);
|
|
138
|
+
summaryLines.push(`**Created:** ${manifest.created_at}`);
|
|
139
|
+
if (dryRun) summaryLines.push('**⚠ DRY RUN**');
|
|
140
|
+
summaryLines.push('');
|
|
141
|
+
summaryLines.push('## Slots');
|
|
142
|
+
summaryLines.push('');
|
|
143
|
+
summaryLines.push('| Slot | Run | Output |');
|
|
144
|
+
summaryLines.push('|------|-----|--------|');
|
|
145
|
+
for (const r of results) {
|
|
146
|
+
summaryLines.push(`| ${r.label} | ${r.run_id} | ${r.selected_output || '—'} |`);
|
|
147
|
+
}
|
|
148
|
+
summaryLines.push('');
|
|
149
|
+
await writeFile(join(batchDir, 'summary.md'), summaryLines.join('\n'));
|
|
150
|
+
|
|
151
|
+
// 7. Output
|
|
152
|
+
console.log('');
|
|
153
|
+
if (flags.json) {
|
|
154
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
155
|
+
} else {
|
|
156
|
+
console.log(`Batch: ${batchId}`);
|
|
157
|
+
console.log(`Slots: ${results.length}`);
|
|
158
|
+
console.log('');
|
|
159
|
+
for (const r of results) {
|
|
160
|
+
const status = r.selected_output ? '✓' : '○';
|
|
161
|
+
console.log(` ${status} ${r.label.padEnd(24)} → ${r.run_id} ${r.selected_output || '(dry run)'}`);
|
|
162
|
+
}
|
|
163
|
+
console.log('');
|
|
164
|
+
info(`Sheet: batches/${batchId}/sheets/primary-sheet.html`);
|
|
165
|
+
info(`Manifest: batches/${batchId}/manifest.json`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* batch-sheet.js — Re-render a batch sheet from saved manifest.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab batch sheet batch_2025-06-30_001 --project star-freight
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parseArgs } from '../lib/args.js';
|
|
11
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
12
|
+
import { loadBatchManifest } from '../lib/batch-runs.js';
|
|
13
|
+
import { getBatchMode } from '../lib/batch-modes.js';
|
|
14
|
+
import { renderSheetHTML, saveSheet } from '../lib/batch-sheet-render.js';
|
|
15
|
+
import { getBatchesDir } from '../lib/batch-runs.js';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { inputError } from '../lib/errors.js';
|
|
18
|
+
import { info } from '../lib/log.js';
|
|
19
|
+
|
|
20
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
21
|
+
const { flags, positionals } = parseArgs(argv, {
|
|
22
|
+
flags: {
|
|
23
|
+
project: { type: 'string', default: 'star-freight' },
|
|
24
|
+
batch: { type: 'string' },
|
|
25
|
+
},
|
|
26
|
+
deprecated: { game: 'project' },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const projectName = flags.project;
|
|
30
|
+
const projectRoot = getProjectRoot(projectName);
|
|
31
|
+
const batchId = positionals[0] || flags.batch;
|
|
32
|
+
|
|
33
|
+
if (!batchId) {
|
|
34
|
+
console.log('Usage: sdlab batch sheet <batch-id> [--project <name>]');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const manifest = loadBatchManifest(projectRoot, batchId);
|
|
39
|
+
const mode = getBatchMode(projectRoot, manifest.mode_id);
|
|
40
|
+
const batchDir = join(getBatchesDir(projectRoot), batchId);
|
|
41
|
+
|
|
42
|
+
const html = renderSheetHTML({ manifest, mode, projectRoot, batchDir });
|
|
43
|
+
const sheetPath = await saveSheet(batchDir, 'primary-sheet.html', html);
|
|
44
|
+
|
|
45
|
+
info(`Sheet written: ${sheetPath}`);
|
|
46
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* batch-show.js — List batches or show batch details.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab batch show --project star-freight # list all batches
|
|
8
|
+
* sdlab batch show batch_2025-06-30_001 # show specific batch
|
|
9
|
+
* sdlab batch show batch_2025-06-30_001 --json # as JSON
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from '../lib/args.js';
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
|
+
import { listBatches, loadBatchManifest } from '../lib/batch-runs.js';
|
|
15
|
+
|
|
16
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
17
|
+
const { flags, positionals } = parseArgs(argv, {
|
|
18
|
+
flags: {
|
|
19
|
+
project: { type: 'string', default: 'star-freight' },
|
|
20
|
+
batch: { type: 'string' },
|
|
21
|
+
json: { type: 'boolean' },
|
|
22
|
+
},
|
|
23
|
+
deprecated: { game: 'project' },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const projectName = flags.project;
|
|
27
|
+
const projectRoot = getProjectRoot(projectName);
|
|
28
|
+
const batchId = positionals[0] || flags.batch;
|
|
29
|
+
|
|
30
|
+
// List mode
|
|
31
|
+
if (!batchId) {
|
|
32
|
+
const batches = listBatches(projectRoot);
|
|
33
|
+
if (batches.length === 0) {
|
|
34
|
+
console.log('No batches found.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log(`\x1b[1mstyle-dataset-lab\x1b[0m batches (${projectName})`);
|
|
38
|
+
console.log('');
|
|
39
|
+
for (const id of batches) {
|
|
40
|
+
try {
|
|
41
|
+
const manifest = loadBatchManifest(projectRoot, id);
|
|
42
|
+
const mode = manifest.mode_id || '?';
|
|
43
|
+
const slots = manifest.summary?.total_slots || manifest.slots?.length || 0;
|
|
44
|
+
const ok = manifest.summary?.successful_slots ?? '?';
|
|
45
|
+
const dry = manifest.summary?.dry_run ? ' [dry]' : '';
|
|
46
|
+
const subject = manifest.subject_id ? ` (${manifest.subject_id})` : '';
|
|
47
|
+
console.log(` ${id} ${mode.padEnd(24)} ${ok}/${slots} slots${dry}${subject}`);
|
|
48
|
+
} catch {
|
|
49
|
+
console.log(` ${id} (manifest unreadable)`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
console.log('');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Detail mode
|
|
57
|
+
const manifest = loadBatchManifest(projectRoot, batchId);
|
|
58
|
+
|
|
59
|
+
if (flags.json) {
|
|
60
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(`\x1b[1mstyle-dataset-lab\x1b[0m batch ${batchId}`);
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(` Mode: ${manifest.mode_id}`);
|
|
67
|
+
console.log(` Project: ${manifest.project_id}`);
|
|
68
|
+
if (manifest.subject_id) console.log(` Subject: ${manifest.subject_id}`);
|
|
69
|
+
if (manifest.theme) console.log(` Theme: ${manifest.theme}`);
|
|
70
|
+
console.log(` Created: ${manifest.created_at}`);
|
|
71
|
+
if (manifest.summary?.dry_run) console.log(' ⚠ DRY RUN');
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(' Slots:');
|
|
74
|
+
for (const slot of manifest.slots || []) {
|
|
75
|
+
const status = slot.selected_output ? '✓' : '○';
|
|
76
|
+
console.log(` ${status} ${(slot.label || slot.slot_id).padEnd(24)} run=${slot.run_id} ${slot.selected_output || ''}`);
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
if (manifest.sheet_paths?.length) {
|
|
80
|
+
console.log(' Sheets:');
|
|
81
|
+
for (const p of manifest.sheet_paths) {
|
|
82
|
+
console.log(` ${p}`);
|
|
83
|
+
}
|
|
84
|
+
console.log('');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* brief-compile.js — Compile a generation brief from project truth.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab brief compile --project star-freight --workflow character-portrait-set --subject kael_maren
|
|
8
|
+
* sdlab brief compile --project star-freight --workflow environment-moodboard
|
|
9
|
+
* sdlab brief compile --project star-freight --workflow character-portrait-set --subject kael_maren --reference sf_001,sf_018
|
|
10
|
+
* sdlab brief compile --project star-freight --workflow character-portrait-set --subject kael_maren --output-count 8 --json
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { parseArgs } from '../lib/args.js';
|
|
14
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
15
|
+
import { compileBrief, saveCompiledBrief } from '../lib/brief-compiler.js';
|
|
16
|
+
import { info } from '../lib/log.js';
|
|
17
|
+
|
|
18
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
19
|
+
const { flags } = parseArgs(argv, {
|
|
20
|
+
flags: {
|
|
21
|
+
project: { type: 'string', default: 'star-freight' },
|
|
22
|
+
workflow: { type: 'string' },
|
|
23
|
+
subject: { type: 'string' },
|
|
24
|
+
asset: { type: 'string' },
|
|
25
|
+
'implementation-pack': { type: 'string' },
|
|
26
|
+
reference: { type: 'string' },
|
|
27
|
+
'output-count': { type: 'string' },
|
|
28
|
+
width: { type: 'string' },
|
|
29
|
+
height: { type: 'string' },
|
|
30
|
+
steps: { type: 'string' },
|
|
31
|
+
cfg: { type: 'string' },
|
|
32
|
+
sampler: { type: 'string' },
|
|
33
|
+
'seed-mode': { type: 'string' },
|
|
34
|
+
json: { type: 'boolean' },
|
|
35
|
+
'dry-run': { type: 'boolean' },
|
|
36
|
+
},
|
|
37
|
+
deprecated: { game: 'project' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!flags.workflow) {
|
|
41
|
+
console.log('Usage: sdlab brief compile --workflow <id> [--subject <id>] [--project <name>]');
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log('Options:');
|
|
44
|
+
console.log(' --workflow <id> Workflow profile to use (required)');
|
|
45
|
+
console.log(' --subject <id> Subject ID for subject-driven workflows');
|
|
46
|
+
console.log(' --asset <id> Training asset reference');
|
|
47
|
+
console.log(' --implementation-pack <id> Implementation pack reference');
|
|
48
|
+
console.log(' --reference <ids> Comma-separated reference record IDs');
|
|
49
|
+
console.log(' --output-count <n> Override output count');
|
|
50
|
+
console.log(' --width <px> Override width');
|
|
51
|
+
console.log(' --height <px> Override height');
|
|
52
|
+
console.log(' --steps <n> Override steps');
|
|
53
|
+
console.log(' --cfg <n> Override CFG scale');
|
|
54
|
+
console.log(' --sampler <name> Override sampler');
|
|
55
|
+
console.log(' --seed-mode <mode> Override seed mode (fixed|increment|random)');
|
|
56
|
+
console.log(' --json Output raw JSON');
|
|
57
|
+
console.log(' --dry-run Preview without saving');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const projectName = flags.project;
|
|
62
|
+
const projectRoot = getProjectRoot(projectName);
|
|
63
|
+
|
|
64
|
+
// Parse reference IDs
|
|
65
|
+
const referenceIds = flags.reference
|
|
66
|
+
? flags.reference.split(',').map(s => s.trim()).filter(Boolean)
|
|
67
|
+
: undefined;
|
|
68
|
+
|
|
69
|
+
// Parse overrides
|
|
70
|
+
const overrides = {};
|
|
71
|
+
if (flags['output-count']) overrides.output_count = parseInt(flags['output-count'], 10);
|
|
72
|
+
if (flags.width) overrides.width = parseInt(flags.width, 10);
|
|
73
|
+
if (flags.height) overrides.height = parseInt(flags.height, 10);
|
|
74
|
+
if (flags.steps) overrides.steps = parseInt(flags.steps, 10);
|
|
75
|
+
if (flags.cfg) overrides.cfg = parseFloat(flags.cfg);
|
|
76
|
+
if (flags.sampler) overrides.sampler = flags.sampler;
|
|
77
|
+
if (flags['seed-mode']) overrides.seed_mode = flags['seed-mode'];
|
|
78
|
+
|
|
79
|
+
const brief = await compileBrief({
|
|
80
|
+
projectRoot,
|
|
81
|
+
projectId: projectName,
|
|
82
|
+
workflowId: flags.workflow,
|
|
83
|
+
subjectId: flags.subject,
|
|
84
|
+
assetRef: flags.asset,
|
|
85
|
+
implementationPackRef: flags['implementation-pack'],
|
|
86
|
+
referenceIds,
|
|
87
|
+
overrides,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (flags.json) {
|
|
91
|
+
console.log(JSON.stringify(brief, null, 2));
|
|
92
|
+
if (!flags['dry-run']) {
|
|
93
|
+
await saveCompiledBrief(projectRoot, brief);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!flags['dry-run']) {
|
|
99
|
+
const { jsonPath, mdPath } = await saveCompiledBrief(projectRoot, brief);
|
|
100
|
+
info(`\x1b[1msdlab brief compile\x1b[0m — ${projectName}\n`);
|
|
101
|
+
info(` Brief ID: ${brief.brief_id}`);
|
|
102
|
+
info(` Workflow: ${brief.workflow_id}`);
|
|
103
|
+
info(` Lane: ${brief.lane_id}`);
|
|
104
|
+
if (brief.subject_id) info(` Subject: ${brief.subject_id}`);
|
|
105
|
+
info(` Outputs: ${brief.expected_outputs.output_count}× ${brief.expected_outputs.output_mode}`);
|
|
106
|
+
info('');
|
|
107
|
+
info(` Saved: ${jsonPath}`);
|
|
108
|
+
info(` ${mdPath}`);
|
|
109
|
+
} else {
|
|
110
|
+
info(`\x1b[1msdlab brief compile\x1b[0m — dry run\n`);
|
|
111
|
+
info(` Brief ID: ${brief.brief_id}`);
|
|
112
|
+
info(` Workflow: ${brief.workflow_id}`);
|
|
113
|
+
info(` Lane: ${brief.lane_id}`);
|
|
114
|
+
if (brief.subject_id) info(` Subject: ${brief.subject_id}`);
|
|
115
|
+
info(` Outputs: ${brief.expected_outputs.output_count}× ${brief.expected_outputs.output_mode}`);
|
|
116
|
+
info('');
|
|
117
|
+
info(' Prompt:');
|
|
118
|
+
info(` ${brief.prompt}`);
|
|
119
|
+
info('');
|
|
120
|
+
info(' Negative:');
|
|
121
|
+
info(` ${brief.negative_prompt}`);
|
|
122
|
+
info('');
|
|
123
|
+
if (brief.drift_warnings?.length > 0) {
|
|
124
|
+
info(' Drift warnings:');
|
|
125
|
+
for (const w of brief.drift_warnings) {
|
|
126
|
+
info(` \x1b[33m⚠\x1b[0m ${w}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (process.argv[1] && (process.argv[1].endsWith('brief-compile.js') || process.argv[1].endsWith('brief-compile'))) {
|
|
133
|
+
run().catch((err) => { console.error(err.message || err); process.exit(1); });
|
|
134
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* brief-show.js — Show a compiled brief.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab brief show brief_2026-04-15_001 --project star-freight
|
|
8
|
+
* sdlab brief show brief_2026-04-15_001 --json
|
|
9
|
+
* sdlab brief show brief_2026-04-15_001 --md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from '../lib/args.js';
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
|
+
import { loadCompiledBrief } from '../lib/brief-compiler.js';
|
|
15
|
+
import { renderBriefText, renderBriefMarkdown } from '../lib/brief-render.js';
|
|
16
|
+
|
|
17
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
18
|
+
const { flags, positionals } = parseArgs(argv, {
|
|
19
|
+
flags: {
|
|
20
|
+
project: { type: 'string', default: 'star-freight' },
|
|
21
|
+
json: { type: 'boolean' },
|
|
22
|
+
md: { type: 'boolean' },
|
|
23
|
+
},
|
|
24
|
+
positionals: ['briefId'],
|
|
25
|
+
deprecated: { game: 'project' },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const briefId = positionals[0];
|
|
29
|
+
if (!briefId) {
|
|
30
|
+
console.log('Usage: sdlab brief show <brief-id> [--project <name>] [--json] [--md]');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const projectRoot = getProjectRoot(flags.project);
|
|
35
|
+
const brief = await loadCompiledBrief(projectRoot, briefId);
|
|
36
|
+
|
|
37
|
+
if (flags.json) {
|
|
38
|
+
console.log(JSON.stringify(brief, null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (flags.md) {
|
|
43
|
+
console.log(renderBriefMarkdown(brief));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(renderBriefText(brief));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (process.argv[1] && (process.argv[1].endsWith('brief-show.js') || process.argv[1].endsWith('brief-show'))) {
|
|
51
|
+
run().catch((err) => { console.error(err.message || err); process.exit(1); });
|
|
52
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* critique-show.js — Display a saved critique report.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab critique show --project star-freight --run run_2026-04-15_001
|
|
8
|
+
* sdlab critique show --project star-freight --run run_2026-04-15_001 --json
|
|
9
|
+
* sdlab critique show --project star-freight --run run_2026-04-15_001 --md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from '../lib/args.js';
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
|
+
import { loadCritique } from '../lib/critique-engine.js';
|
|
15
|
+
import { renderCritiqueMarkdown, renderCritiqueText } from '../lib/critique-render.js';
|
|
16
|
+
|
|
17
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
18
|
+
const { flags } = parseArgs(argv, {
|
|
19
|
+
flags: {
|
|
20
|
+
project: { type: 'string', default: 'star-freight' },
|
|
21
|
+
run: { type: 'string' },
|
|
22
|
+
json: { type: 'boolean' },
|
|
23
|
+
md: { type: 'boolean' },
|
|
24
|
+
},
|
|
25
|
+
deprecated: { game: 'project' },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!flags.run) {
|
|
29
|
+
console.log('Usage: sdlab critique show --run <id> [--project <name>]');
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log('Options:');
|
|
32
|
+
console.log(' --run <id> Run ID (required)');
|
|
33
|
+
console.log(' --json Output raw JSON');
|
|
34
|
+
console.log(' --md Output full Markdown');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const projectName = flags.project;
|
|
39
|
+
const projectRoot = getProjectRoot(projectName);
|
|
40
|
+
|
|
41
|
+
const report = loadCritique(projectRoot, flags.run);
|
|
42
|
+
|
|
43
|
+
if (flags.json) {
|
|
44
|
+
console.log(JSON.stringify(report, null, 2));
|
|
45
|
+
} else if (flags.md) {
|
|
46
|
+
console.log(renderCritiqueMarkdown(report));
|
|
47
|
+
} else {
|
|
48
|
+
console.log(renderCritiqueText(report));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* critique.js — Generate a critique report for a completed run.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* sdlab critique --project star-freight --run run_2026-04-15_001
|
|
8
|
+
* sdlab critique --project star-freight --run run_2026-04-15_001 --json
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { parseArgs } from '../lib/args.js';
|
|
12
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
13
|
+
import { critiqueRun, saveCritique } from '../lib/critique-engine.js';
|
|
14
|
+
import { renderCritiqueMarkdown, renderCritiqueText } from '../lib/critique-render.js';
|
|
15
|
+
import { info } from '../lib/log.js';
|
|
16
|
+
|
|
17
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
18
|
+
const { flags } = parseArgs(argv, {
|
|
19
|
+
flags: {
|
|
20
|
+
project: { type: 'string', default: 'star-freight' },
|
|
21
|
+
run: { type: 'string' },
|
|
22
|
+
json: { type: 'boolean' },
|
|
23
|
+
},
|
|
24
|
+
deprecated: { game: 'project' },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!flags.run) {
|
|
28
|
+
console.log('Usage: sdlab critique --run <id> [--project <name>]');
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('Options:');
|
|
31
|
+
console.log(' --run <id> Run to critique (required)');
|
|
32
|
+
console.log(' --json Output raw JSON');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const projectName = flags.project;
|
|
37
|
+
const projectRoot = getProjectRoot(projectName);
|
|
38
|
+
|
|
39
|
+
console.log(`\x1b[1mstyle-dataset-lab\x1b[0m critique`);
|
|
40
|
+
console.log(` Project: ${projectName}`);
|
|
41
|
+
console.log(` Run: ${flags.run}`);
|
|
42
|
+
console.log('');
|
|
43
|
+
|
|
44
|
+
// Generate critique
|
|
45
|
+
const report = await critiqueRun({
|
|
46
|
+
projectRoot,
|
|
47
|
+
projectId: projectName,
|
|
48
|
+
runId: flags.run,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Save
|
|
52
|
+
const md = renderCritiqueMarkdown(report);
|
|
53
|
+
await saveCritique(projectRoot, flags.run, report, md);
|
|
54
|
+
|
|
55
|
+
if (flags.json) {
|
|
56
|
+
console.log(JSON.stringify(report, null, 2));
|
|
57
|
+
} else {
|
|
58
|
+
console.log(renderCritiqueText(report));
|
|
59
|
+
const action = report.recommended_action;
|
|
60
|
+
console.log(`Critique saved to: runs/${flags.run}/critique.json`);
|
|
61
|
+
|
|
62
|
+
if (action.mode === 'refine_from_one' && action.preferred_candidate) {
|
|
63
|
+
console.log('');
|
|
64
|
+
info(`Next: sdlab refine --run ${flags.run} --pick ${action.preferred_candidate}`);
|
|
65
|
+
} else if (action.mode === 'accept_one' && action.preferred_candidate) {
|
|
66
|
+
console.log('');
|
|
67
|
+
info(`Preferred candidate: ${action.preferred_candidate}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|