@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.
Files changed (65) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/README.es.md +76 -144
  3. package/README.fr.md +75 -143
  4. package/README.hi.md +75 -143
  5. package/README.it.md +76 -144
  6. package/README.ja.md +75 -143
  7. package/README.md +36 -8
  8. package/README.pt-BR.md +75 -143
  9. package/README.zh.md +76 -144
  10. package/SECURITY.md +3 -1
  11. package/bin/sdlab.js +146 -17
  12. package/lib/adapters/comfyui-runner.js +170 -0
  13. package/lib/adapters/comfyui-workflows.js +163 -0
  14. package/lib/batch-compiler.js +118 -0
  15. package/lib/batch-modes.js +111 -0
  16. package/lib/batch-runs.js +168 -0
  17. package/lib/batch-sheet-render.js +206 -0
  18. package/lib/brief-compiler.js +598 -0
  19. package/lib/brief-render.js +205 -0
  20. package/lib/critique-engine.js +413 -0
  21. package/lib/critique-render.js +171 -0
  22. package/lib/errors.js +82 -0
  23. package/lib/generated-provenance.js +87 -0
  24. package/lib/log.js +36 -0
  25. package/lib/paths.js +31 -0
  26. package/lib/refine-briefs.js +329 -0
  27. package/lib/reingest-selected.js +173 -0
  28. package/lib/run-summary.js +131 -0
  29. package/lib/runtime-runs.js +168 -0
  30. package/lib/selections.js +323 -0
  31. package/lib/snapshot.js +5 -2
  32. package/lib/split.js +5 -2
  33. package/lib/workflow-profiles.js +161 -0
  34. package/package.json +8 -6
  35. package/scripts/batch-generate.js +167 -0
  36. package/scripts/batch-sheet.js +46 -0
  37. package/scripts/batch-show.js +86 -0
  38. package/scripts/brief-compile.js +134 -0
  39. package/scripts/brief-show.js +52 -0
  40. package/scripts/critique-show.js +50 -0
  41. package/scripts/critique.js +70 -0
  42. package/scripts/doctor.js +169 -1
  43. package/scripts/init.js +22 -0
  44. package/scripts/refine.js +119 -0
  45. package/scripts/reingest-selected.js +78 -0
  46. package/scripts/reingest.js +7 -1
  47. package/scripts/run-generate.js +76 -0
  48. package/scripts/run-list.js +58 -0
  49. package/scripts/run-show.js +63 -0
  50. package/scripts/select.js +118 -0
  51. package/scripts/selection-show.js +88 -0
  52. package/scripts/snapshot.js +27 -23
  53. package/scripts/split.js +3 -1
  54. package/scripts/workflow-list.js +61 -0
  55. package/scripts/workflow-show.js +96 -0
  56. package/templates/domains/architecture/workflows/profiles/environment-moodboard.json +62 -0
  57. package/templates/domains/architecture/workflows/profiles/interior-variant-pack.json +60 -0
  58. package/templates/domains/character-design/workflows/profiles/character-portrait-set.json +61 -0
  59. package/templates/domains/character-design/workflows/profiles/expression-sheet.json +59 -0
  60. package/templates/domains/creature-design/workflows/profiles/creature-portrait-set.json +61 -0
  61. package/templates/domains/creature-design/workflows/profiles/silhouette-exploration.json +58 -0
  62. package/templates/domains/game-art/workflows/profiles/character-portrait-set.json +62 -0
  63. package/templates/domains/game-art/workflows/profiles/environment-moodboard.json +61 -0
  64. package/templates/domains/vehicle-mech/workflows/profiles/variant-pack.json +60 -0
  65. 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": "2.2.1",
4
- "description": "Visual canon and dataset production pipeline — snapshots, splits, versioned exports, eval packs, and canon-bound curation for any image production domain",
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
+ }