@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.
Files changed (88) hide show
  1. package/CHANGELOG.md +68 -15
  2. package/CODE_OF_CONDUCT.md +9 -0
  3. package/CONTRIBUTING.md +71 -0
  4. package/README.md +46 -1
  5. package/SECURITY.md +6 -5
  6. package/bin/sdlab.js +281 -62
  7. package/lib/adapters/diffusers-lora.js +7 -2
  8. package/lib/adapters/generic-image-caption.js +6 -1
  9. package/lib/args.js +200 -13
  10. package/lib/batch-compiler.js +7 -3
  11. package/lib/batch-runs.js +100 -31
  12. package/lib/batch-sheet-render.js +5 -2
  13. package/lib/brief-compiler.js +38 -11
  14. package/lib/card.js +11 -4
  15. package/lib/comfyui.js +100 -6
  16. package/lib/config.js +68 -3
  17. package/lib/deprecation.js +1 -1
  18. package/lib/eval-pack.js +94 -4
  19. package/lib/eval-runs.js +42 -3
  20. package/lib/export.js +144 -34
  21. package/lib/implementation-packs.js +15 -1
  22. package/lib/log.js +41 -4
  23. package/lib/paths.js +41 -3
  24. package/lib/refine-briefs.js +16 -9
  25. package/lib/reingest-selected.js +14 -3
  26. package/lib/reingest.js +15 -1
  27. package/lib/run-summary.js +3 -2
  28. package/lib/runtime-runs.js +59 -9
  29. package/lib/selections.js +54 -2
  30. package/lib/snapshot.js +100 -7
  31. package/lib/split.js +175 -17
  32. package/lib/training-manifests.js +17 -3
  33. package/lib/training-packages.js +28 -14
  34. package/package.json +8 -2
  35. package/scripts/batch-generate.js +16 -7
  36. package/scripts/batch-sheet.js +3 -3
  37. package/scripts/batch-show.js +3 -3
  38. package/scripts/brief-compile.js +8 -8
  39. package/scripts/brief-show.js +4 -3
  40. package/scripts/canon-bind.js +7 -9
  41. package/scripts/card.js +22 -25
  42. package/scripts/compare.js +38 -33
  43. package/scripts/critique-show.js +3 -3
  44. package/scripts/critique.js +3 -3
  45. package/scripts/curate.js +91 -46
  46. package/scripts/doctor.js +3 -5
  47. package/scripts/eligibility.js +17 -14
  48. package/scripts/eval-pack.js +13 -11
  49. package/scripts/eval-run.js +30 -29
  50. package/scripts/export.js +27 -31
  51. package/scripts/generate-controlnet.js +31 -17
  52. package/scripts/generate-identity.js +37 -20
  53. package/scripts/generate-ipadapter.js +22 -16
  54. package/scripts/generate.js +68 -13
  55. package/scripts/implementation-pack.js +19 -14
  56. package/scripts/init.js +115 -17
  57. package/scripts/painterly.js +8 -10
  58. package/scripts/refine.js +3 -3
  59. package/scripts/reingest-selected.js +3 -3
  60. package/scripts/reingest.js +33 -20
  61. package/scripts/run-generate.js +4 -4
  62. package/scripts/run-list.js +3 -3
  63. package/scripts/run-show.js +3 -3
  64. package/scripts/select.js +5 -4
  65. package/scripts/selection-show.js +3 -3
  66. package/scripts/snapshot.js +22 -24
  67. package/scripts/split.js +26 -31
  68. package/scripts/training-manifest.js +29 -24
  69. package/scripts/training-package.js +24 -20
  70. package/scripts/training-profile.js +13 -8
  71. package/scripts/workflow-list.js +2 -2
  72. package/scripts/workflow-show.js +4 -3
  73. package/templates/domains/architecture/inputs/prompts/example-wave.json +32 -0
  74. package/templates/domains/architecture/terminology.json +10 -8
  75. package/templates/domains/character-design/inputs/prompts/example-wave.json +32 -0
  76. package/templates/domains/character-design/project.json +4 -2
  77. package/templates/domains/creature-design/inputs/prompts/example-wave.json +32 -0
  78. package/templates/domains/creature-design/project.json +4 -2
  79. package/templates/domains/creature-design/terminology.json +1 -0
  80. package/templates/domains/game-art/inputs/prompts/example-wave.json +37 -0
  81. package/templates/domains/game-art/project.json +4 -2
  82. package/templates/domains/vehicle-mech/inputs/prompts/example-wave.json +37 -0
  83. package/templates/domains/vehicle-mech/project.json +4 -2
  84. package/scripts/bulk-curate-wave2-5.js +0 -138
  85. package/scripts/bulk-curate-waves11-18.js +0 -413
  86. package/scripts/curate-wave25.js +0 -209
  87. package/scripts/migrate-records.js +0 -141
  88. package/templates/inputs/prompts/example-wave.json +0 -27
package/scripts/split.js CHANGED
@@ -10,43 +10,45 @@
10
10
  * sdlab split audit <split-id> [--project <name>]
11
11
  */
12
12
 
13
- import { readFile } from 'node:fs/promises';
14
13
  import { join } from 'node:path';
15
- import { getProjectName } from '../lib/args.js';
14
+ import { parseArgs, getProjectName } from '../lib/args.js';
16
15
  import { REPO_ROOT } from '../lib/paths.js';
17
16
  import { loadSplitProfile } from '../lib/config.js';
17
+ import { inputError, handleCliError } from '../lib/errors.js';
18
18
  import { listSnapshots } from '../lib/snapshot.js';
19
19
  import { createSplit, loadSplit, loadSplitAudit, listSplits } from '../lib/split.js';
20
20
 
21
21
  export async function run(argv = process.argv.slice(2)) {
22
- const projectName = getProjectName(argv);
22
+ const { flags, positionals } = parseArgs(argv, {
23
+ flags: {
24
+ project: { type: 'string' },
25
+ snapshot: { type: 'string' },
26
+ profile: { type: 'string' },
27
+ 'dry-run': { type: 'boolean', default: false },
28
+ },
29
+ deprecated: { game: 'project' },
30
+ });
31
+
32
+ const projectName = flags.project || getProjectName(argv);
23
33
  const projectRoot = join(REPO_ROOT, 'projects', projectName);
24
- const subcommand = argv.find(a => !a.startsWith('--')) || 'list';
25
- const args = argv.filter(a => a !== subcommand);
34
+ const subcommand = positionals[0] || 'list';
35
+ const dryRun = flags['dry-run'] || flags.dryRun;
26
36
 
27
37
  if (subcommand === 'build') {
28
- // Find snapshot ID
29
- let snapshotId;
30
- const snapIdx = args.indexOf('--snapshot');
31
- if (snapIdx >= 0) {
32
- snapshotId = args[snapIdx + 1];
33
- } else {
34
- // Use latest snapshot
38
+ let snapshotId = flags.snapshot;
39
+ if (!snapshotId) {
35
40
  const snapshots = await listSnapshots(projectRoot);
36
41
  if (snapshots.length === 0) {
37
- throw new Error('No snapshots found. Run: sdlab snapshot create');
42
+ throw inputError('INPUT_NO_SNAPSHOT', 'No snapshots found. Run: sdlab snapshot create');
38
43
  }
39
44
  snapshotId = snapshots[snapshots.length - 1].id;
40
45
  }
41
46
 
42
- const profileIdx = args.indexOf('--profile');
43
- const profileId = profileIdx >= 0 ? args[profileIdx + 1] : null;
44
- const profile = loadSplitProfile(projectRoot, profileId);
45
- const dryRun = argv.includes('--dry-run');
47
+ const profile = loadSplitProfile(projectRoot, flags.profile);
46
48
 
47
49
  console.log(`\x1b[1msdlab split build\x1b[0m — ${projectName}`);
48
50
  console.log(` Snapshot: ${snapshotId}`);
49
- console.log(` Profile: ${profileId || 'balanced-default (built-in)'}`);
51
+ console.log(` Profile: ${flags.profile || 'balanced-default (built-in)'}`);
50
52
  console.log(` Strategy: ${profile.strategy}`);
51
53
  console.log(` Ratios: ${profile.train_ratio}/${profile.val_ratio}/${profile.test_ratio}`);
52
54
  console.log(` Seed: ${profile.seed}`);
@@ -60,7 +62,6 @@ export async function run(argv = process.argv.slice(2)) {
60
62
  console.log(` Val: ${result.val}`);
61
63
  console.log(` Test: ${result.test}`);
62
64
 
63
- // Show audit summary
64
65
  const audit = await loadSplitAudit(projectRoot, result.splitId);
65
66
  console.log('');
66
67
  if (audit.leakage_check.passed) {
@@ -86,8 +87,8 @@ export async function run(argv = process.argv.slice(2)) {
86
87
  }
87
88
 
88
89
  } else if (subcommand === 'show') {
89
- const splitId = args.find(a => a.startsWith('split-'));
90
- if (!splitId) throw new Error('Usage: sdlab split show <split-id>');
90
+ const splitId = positionals[1] || positionals.find(a => a.startsWith('split-'));
91
+ if (!splitId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab split show <split-id>');
91
92
 
92
93
  const manifest = await loadSplit(projectRoot, splitId);
93
94
  console.log(`\x1b[1mSplit\x1b[0m — ${splitId}\n`);
@@ -112,13 +113,12 @@ export async function run(argv = process.argv.slice(2)) {
112
113
  }
113
114
 
114
115
  } else if (subcommand === 'audit') {
115
- const splitId = args.find(a => a.startsWith('split-'));
116
- if (!splitId) throw new Error('Usage: sdlab split audit <split-id>');
116
+ const splitId = positionals[1] || positionals.find(a => a.startsWith('split-'));
117
+ if (!splitId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab split audit <split-id>');
117
118
 
118
119
  const audit = await loadSplitAudit(projectRoot, splitId);
119
120
  console.log(`\x1b[1mSplit audit\x1b[0m — ${splitId}\n`);
120
121
 
121
- // Leakage check
122
122
  if (audit.leakage_check.passed) {
123
123
  console.log(` \x1b[32m✓\x1b[0m Leakage check: PASSED`);
124
124
  } else {
@@ -128,13 +128,11 @@ export async function run(argv = process.argv.slice(2)) {
128
128
  }
129
129
  }
130
130
 
131
- // Family distribution
132
131
  console.log(`\n Families: ${audit.family_count}`);
133
132
  console.log(` Train: ${audit.families_per_split.train}`);
134
133
  console.log(` Val: ${audit.families_per_split.val}`);
135
134
  console.log(` Test: ${audit.families_per_split.test}`);
136
135
 
137
- // Lane balance
138
136
  console.log('\n Lane balance:');
139
137
  console.log(` ${'Lane'.padEnd(20)} ${'Total'.padStart(6)} ${'Train%'.padStart(8)} ${'Val%'.padStart(8)} ${'Test%'.padStart(8)}`);
140
138
  console.log(` ${'─'.repeat(52)}`);
@@ -143,14 +141,11 @@ export async function run(argv = process.argv.slice(2)) {
143
141
  }
144
142
 
145
143
  } else {
146
- throw new Error(`Unknown subcommand: ${subcommand}. Use: build, list, show, audit`);
144
+ throw inputError('INPUT_BAD_SUBCOMMAND', `Unknown subcommand: ${subcommand}. Use: build, list, show, audit`);
147
145
  }
148
146
  }
149
147
 
150
148
  // Direct execution guard
151
149
  if (process.argv[1] && (process.argv[1].endsWith('split.js') || process.argv[1].endsWith('split'))) {
152
- run().catch((err) => {
153
- console.error(err.message || err);
154
- process.exit(1);
155
- });
150
+ run().catch(handleCliError);
156
151
  }
@@ -11,8 +11,9 @@
11
11
  */
12
12
 
13
13
  import { join } from 'node:path';
14
- import { getProjectName } from '../lib/args.js';
14
+ import { parseArgs, getProjectName } from '../lib/args.js';
15
15
  import { REPO_ROOT } from '../lib/paths.js';
16
+ import { inputError, handleCliError } from '../lib/errors.js';
16
17
  import { listExports } from '../lib/export.js';
17
18
  import {
18
19
  createTrainingManifest,
@@ -22,30 +23,34 @@ import {
22
23
  } from '../lib/training-manifests.js';
23
24
 
24
25
  export async function run(argv = process.argv.slice(2)) {
25
- const projectName = getProjectName(argv);
26
+ const { flags, positionals } = parseArgs(argv, {
27
+ flags: {
28
+ project: { type: 'string' },
29
+ export: { type: 'string' },
30
+ profile: { type: 'string' },
31
+ adapter: { type: 'string' },
32
+ 'base-model': { type: 'string' },
33
+ },
34
+ deprecated: { game: 'project' },
35
+ });
36
+
37
+ const projectName = flags.project || getProjectName(argv);
26
38
  const projectRoot = join(REPO_ROOT, 'projects', projectName);
27
- const subcommand = argv.find(a => !a.startsWith('--') && !a.startsWith('tm-')) || 'list';
28
- const args = argv.filter(a => a !== subcommand);
39
+ const subcommand = positionals.find(a => !a.startsWith('tm-')) || 'list';
29
40
 
30
41
  if (subcommand === 'create') {
31
- // Required: --export and --profile
32
- const exportIdx = args.indexOf('--export');
33
- if (exportIdx < 0) {
34
- // Try latest export
42
+ let exportId = flags.export;
43
+ if (!exportId) {
35
44
  const exports = await listExports(projectRoot);
36
- if (exports.length === 0) throw new Error('No exports found. Run: sdlab export build');
45
+ if (exports.length === 0) throw inputError('INPUT_NO_EXPORT', 'No exports found. Run: sdlab export build');
46
+ exportId = exports[exports.length - 1].id;
37
47
  }
38
- const exportId = exportIdx >= 0 ? args[exportIdx + 1] : (await listExports(projectRoot)).pop().id;
39
48
 
40
- const profileIdx = args.indexOf('--profile');
41
- if (profileIdx < 0) throw new Error('--profile is required. Use: sdlab training-profile list');
42
- const profileId = args[profileIdx + 1];
49
+ if (!flags.profile) throw inputError('INPUT_MISSING_FLAG', '--profile is required. Use: sdlab training-profile list');
50
+ const profileId = flags.profile;
43
51
 
44
- const adapterIdx = args.indexOf('--adapter');
45
- const adapterTarget = adapterIdx >= 0 ? args[adapterIdx + 1] : undefined;
46
-
47
- const baseModelIdx = args.indexOf('--base-model');
48
- const baseModel = baseModelIdx >= 0 ? args[baseModelIdx + 1] : undefined;
52
+ const adapterTarget = flags.adapter;
53
+ const baseModel = flags['base-model'] || flags.baseModel;
49
54
 
50
55
  console.log(`\x1b[1msdlab training-manifest create\x1b[0m — ${projectName}`);
51
56
  console.log(` Export: ${exportId}`);
@@ -62,8 +67,8 @@ export async function run(argv = process.argv.slice(2)) {
62
67
  console.log(` \x1b[32m✓\x1b[0m Manifest: ${result.manifestId}`);
63
68
 
64
69
  } else if (subcommand === 'validate') {
65
- const manifestId = args.find(a => a.startsWith('tm-'));
66
- if (!manifestId) throw new Error('Usage: sdlab training-manifest validate <manifest-id>');
70
+ const manifestId = positionals.find(a => a.startsWith('tm-'));
71
+ if (!manifestId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab training-manifest validate <manifest-id>');
67
72
 
68
73
  console.log(`\x1b[1msdlab training-manifest validate\x1b[0m — ${manifestId}\n`);
69
74
  const { valid, issues } = await validateTrainingManifest(projectRoot, manifestId);
@@ -78,8 +83,8 @@ export async function run(argv = process.argv.slice(2)) {
78
83
  }
79
84
 
80
85
  } else if (subcommand === 'show') {
81
- const manifestId = args.find(a => a.startsWith('tm-'));
82
- if (!manifestId) throw new Error('Usage: sdlab training-manifest show <manifest-id>');
86
+ const manifestId = positionals.find(a => a.startsWith('tm-'));
87
+ if (!manifestId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab training-manifest show <manifest-id>');
83
88
 
84
89
  const manifest = await loadTrainingManifest(projectRoot, manifestId);
85
90
  console.log(`\x1b[1mTraining manifest\x1b[0m — ${manifestId}\n`);
@@ -110,10 +115,10 @@ export async function run(argv = process.argv.slice(2)) {
110
115
  }
111
116
 
112
117
  } else {
113
- throw new Error(`Unknown subcommand: ${subcommand}. Use: create, validate, show, list`);
118
+ throw inputError('INPUT_BAD_SUBCOMMAND', `Unknown subcommand: ${subcommand}. Use: create, validate, show, list`);
114
119
  }
115
120
  }
116
121
 
117
122
  if (process.argv[1] && (process.argv[1].endsWith('training-manifest.js') || process.argv[1].endsWith('training-manifest'))) {
118
- run().catch((err) => { console.error(err.message || err); process.exit(1); });
123
+ run().catch(handleCliError);
119
124
  }
@@ -10,48 +10,52 @@
10
10
  */
11
11
 
12
12
  import { join } from 'node:path';
13
- import { getProjectName } from '../lib/args.js';
13
+ import { parseArgs, getProjectName } from '../lib/args.js';
14
14
  import { REPO_ROOT } from '../lib/paths.js';
15
+ import { inputError, handleCliError } from '../lib/errors.js';
15
16
  import { listTrainingManifests } from '../lib/training-manifests.js';
16
17
  import { buildTrainingPackage, listTrainingPackages, loadTrainingPackage } from '../lib/training-packages.js';
17
18
 
18
19
  export async function run(argv = process.argv.slice(2)) {
19
- const projectName = getProjectName(argv);
20
+ const { flags, positionals } = parseArgs(argv, {
21
+ flags: {
22
+ project: { type: 'string' },
23
+ manifest: { type: 'string' },
24
+ adapter: { type: 'string' },
25
+ copy: { type: 'boolean', default: false },
26
+ },
27
+ deprecated: { game: 'project' },
28
+ });
29
+
30
+ const projectName = flags.project || getProjectName(argv);
20
31
  const projectRoot = join(REPO_ROOT, 'projects', projectName);
21
- const subcommand = argv.find(a => !a.startsWith('--') && !a.startsWith('tp-') && !a.startsWith('tm-')) || 'list';
22
- const args = argv.filter(a => a !== subcommand);
32
+ const subcommand = positionals.find(a => !a.startsWith('tp-') && !a.startsWith('tm-')) || 'list';
23
33
 
24
34
  if (subcommand === 'build') {
25
- // Find manifest
26
- let manifestId;
27
- const manifestIdx = args.indexOf('--manifest');
28
- if (manifestIdx >= 0) {
29
- manifestId = args[manifestIdx + 1];
30
- } else {
35
+ let manifestId = flags.manifest;
36
+ if (!manifestId) {
31
37
  const manifests = await listTrainingManifests(projectRoot);
32
- if (manifests.length === 0) throw new Error('No training manifests found. Run: sdlab training-manifest create');
38
+ if (manifests.length === 0) throw inputError('INPUT_NO_MANIFEST', 'No training manifests found. Run: sdlab training-manifest create');
33
39
  manifestId = manifests[manifests.length - 1].id;
34
40
  }
35
41
 
36
- const adapterIdx = args.indexOf('--adapter');
37
- const adapterOverride = adapterIdx >= 0 ? args[adapterIdx + 1] : undefined;
38
- const copy = args.includes('--copy');
42
+ const adapterOverride = flags.adapter;
39
43
 
40
44
  console.log(`\x1b[1msdlab training-package build\x1b[0m — ${projectName}`);
41
45
  console.log(` Manifest: ${manifestId}`);
42
46
  if (adapterOverride) console.log(` Adapter: ${adapterOverride}`);
43
- console.log(` Images: ${copy ? 'copy' : 'symlink'}`);
47
+ console.log(` Images: ${flags.copy ? 'copy' : 'symlink'}`);
44
48
  console.log('');
45
49
 
46
- const result = await buildTrainingPackage(projectRoot, manifestId, { copy, adapterOverride });
50
+ const result = await buildTrainingPackage(projectRoot, manifestId, { copy: flags.copy, adapterOverride });
47
51
 
48
52
  console.log(` \x1b[32m✓\x1b[0m Package: ${result.packageId}`);
49
53
  console.log(` Records: ${result.records}`);
50
54
  console.log(` Images: ${result.images}`);
51
55
 
52
56
  } else if (subcommand === 'show') {
53
- const packageId = args.find(a => a.startsWith('tp-'));
54
- if (!packageId) throw new Error('Usage: sdlab training-package show <package-id>');
57
+ const packageId = positionals.find(a => a.startsWith('tp-'));
58
+ if (!packageId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab training-package show <package-id>');
55
59
 
56
60
  const pkg = await loadTrainingPackage(projectRoot, packageId);
57
61
  console.log(`\x1b[1mTraining package\x1b[0m — ${packageId}\n`);
@@ -75,10 +79,10 @@ export async function run(argv = process.argv.slice(2)) {
75
79
  }
76
80
 
77
81
  } else {
78
- throw new Error(`Unknown subcommand: ${subcommand}. Use: build, show, list`);
82
+ throw inputError('INPUT_BAD_SUBCOMMAND', `Unknown subcommand: ${subcommand}. Use: build, show, list`);
79
83
  }
80
84
  }
81
85
 
82
86
  if (process.argv[1] && (process.argv[1].endsWith('training-package.js') || process.argv[1].endsWith('training-package'))) {
83
- run().catch((err) => { console.error(err.message || err); process.exit(1); });
87
+ run().catch(handleCliError);
84
88
  }
@@ -9,15 +9,20 @@
9
9
  */
10
10
 
11
11
  import { join } from 'node:path';
12
- import { getProjectName } from '../lib/args.js';
12
+ import { parseArgs, getProjectName } from '../lib/args.js';
13
13
  import { REPO_ROOT } from '../lib/paths.js';
14
+ import { inputError, handleCliError } from '../lib/errors.js';
14
15
  import { listTrainingProfiles, loadTrainingProfile } from '../lib/training-profiles.js';
15
16
 
16
17
  export async function run(argv = process.argv.slice(2)) {
17
- const projectName = getProjectName(argv);
18
+ const { flags, positionals } = parseArgs(argv, {
19
+ flags: { project: { type: 'string' } },
20
+ deprecated: { game: 'project' },
21
+ });
22
+
23
+ const projectName = flags.project || getProjectName(argv);
18
24
  const projectRoot = join(REPO_ROOT, 'projects', projectName);
19
- const subcommand = argv.find(a => !a.startsWith('--')) || 'list';
20
- const args = argv.filter(a => a !== subcommand);
25
+ const subcommand = positionals[0] || 'list';
21
26
 
22
27
  if (subcommand === 'list') {
23
28
  const profiles = await listTrainingProfiles(projectRoot);
@@ -31,8 +36,8 @@ export async function run(argv = process.argv.slice(2)) {
31
36
  }
32
37
 
33
38
  } else if (subcommand === 'show') {
34
- const profileId = args.find(a => !a.startsWith('--'));
35
- if (!profileId) throw new Error('Usage: sdlab training-profile show <profile-id>');
39
+ const profileId = positionals[1];
40
+ if (!profileId) throw inputError('INPUT_MISSING_ARGS', 'Usage: sdlab training-profile show <profile-id>');
36
41
 
37
42
  const profile = await loadTrainingProfile(projectRoot, profileId);
38
43
  console.log(`\x1b[1mTraining profile\x1b[0m — ${profileId}\n`);
@@ -54,10 +59,10 @@ export async function run(argv = process.argv.slice(2)) {
54
59
  }
55
60
 
56
61
  } else {
57
- throw new Error(`Unknown subcommand: ${subcommand}. Use: list, show`);
62
+ throw inputError('INPUT_BAD_SUBCOMMAND', `Unknown subcommand: ${subcommand}. Use: list, show`);
58
63
  }
59
64
  }
60
65
 
61
66
  if (process.argv[1] && (process.argv[1].endsWith('training-profile.js') || process.argv[1].endsWith('training-profile'))) {
62
- run().catch((err) => { console.error(err.message || err); process.exit(1); });
67
+ run().catch(handleCliError);
63
68
  }
@@ -15,13 +15,13 @@ import { listWorkflowProfiles } from '../lib/workflow-profiles.js';
15
15
  export async function run(argv = process.argv.slice(2)) {
16
16
  const { flags } = parseArgs(argv, {
17
17
  flags: {
18
- project: { type: 'string', default: 'star-freight' },
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 profiles = await listWorkflowProfiles(projectRoot);
27
27
 
@@ -8,14 +8,14 @@
8
8
  * sdlab workflow show character-portrait-set --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 { getWorkflowProfile } from '../lib/workflow-profiles.js';
14
14
 
15
15
  export async function run(argv = process.argv.slice(2)) {
16
16
  const { flags, positionals } = parseArgs(argv, {
17
17
  flags: {
18
- project: { type: 'string', default: 'star-freight' },
18
+ project: { type: 'string' },
19
19
  json: { type: 'boolean' },
20
20
  },
21
21
  positionals: ['workflowId'],
@@ -28,7 +28,8 @@ export async function run(argv = process.argv.slice(2)) {
28
28
  return;
29
29
  }
30
30
 
31
- const projectRoot = getProjectRoot(flags.project);
31
+ const projectName = flags.project || getProjectName(argv);
32
+ const projectRoot = getProjectRoot(projectName);
32
33
  const profile = await getWorkflowProfile(projectRoot, workflowId);
33
34
 
34
35
  if (flags.json) {
@@ -0,0 +1,32 @@
1
+ {
2
+ "wave": "wave1",
3
+ "description": "Architecture — first wave. Subject IDs match this domain's lane id_patterns (ext_, int_, street_) so `sdlab generate` will route each candidate into the correct lane instead of default_lane.",
4
+ "defaults": {
5
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
6
+ "lora": { "name": "classipeintxl_v21.safetensors", "weight": 1.0 },
7
+ "width": 1024,
8
+ "height": 1024,
9
+ "steps": 8,
10
+ "cfg": 2.0,
11
+ "sampler": "dpmpp_sde",
12
+ "scheduler": "karras",
13
+ "negative": "photorealistic, photograph, 3d render, smooth CG, sharp photo detail"
14
+ },
15
+ "subjects": [
16
+ {
17
+ "id": "ext_landmark",
18
+ "variations": 2,
19
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR BUILDING — facade, elevation, architectural massing], painterly concept art, canvas grain"
20
+ },
21
+ {
22
+ "id": "int_hall",
23
+ "variations": 2,
24
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR INTERIOR — hall, chamber, spatial atmosphere], painterly concept art, canvas grain"
25
+ },
26
+ {
27
+ "id": "street_district",
28
+ "variations": 2,
29
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR STREETSCAPE — street-level view, urban fabric, district overview], painterly concept art, canvas grain"
30
+ }
31
+ ]
32
+ }
@@ -8,12 +8,14 @@
8
8
  "null_faction_patterns": [],
9
9
  "id_fallbacks": {},
10
10
  "faction_context": {},
11
- "training_profiles": {
12
- "profile_id": "architecture-style",
13
- "intended_use": "Architectural style LoRA for consistent worldbuilding",
14
- "eligible_lanes": ["exterior", "interior", "streetscape"],
15
- "minimum_review_threshold": 0.7,
16
- "excluded_content": ["rejected"],
17
- "export_format": "trl"
18
- }
11
+ "training_profiles": [
12
+ {
13
+ "profile_id": "architecture-style",
14
+ "intended_use": "Architectural style LoRA for consistent worldbuilding",
15
+ "eligible_lanes": ["exterior", "interior", "streetscape"],
16
+ "minimum_review_threshold": 0.7,
17
+ "excluded_content": ["rejected"],
18
+ "export_format": "trl"
19
+ }
20
+ ]
19
21
  }
@@ -0,0 +1,32 @@
1
+ {
2
+ "wave": "wave1",
3
+ "description": "Character design — first wave. Subject IDs match this domain's lane id_patterns (portrait_, full_, expr_) so `sdlab generate` will route each candidate into the correct lane instead of default_lane.",
4
+ "defaults": {
5
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
6
+ "lora": { "name": "classipeintxl_v21.safetensors", "weight": 1.0 },
7
+ "width": 1024,
8
+ "height": 1024,
9
+ "steps": 8,
10
+ "cfg": 2.0,
11
+ "sampler": "dpmpp_sde",
12
+ "scheduler": "karras",
13
+ "negative": "photorealistic, photograph, 3d render, smooth CG, sharp photo detail"
14
+ },
15
+ "subjects": [
16
+ {
17
+ "id": "portrait_hero",
18
+ "variations": 2,
19
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR HERO — head and shoulders, face detail, expression], painterly concept art, canvas grain"
20
+ },
21
+ {
22
+ "id": "full_body_hero",
23
+ "variations": 2,
24
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR HERO — full figure standing pose, costume, silhouette], painterly concept art, canvas grain"
25
+ },
26
+ {
27
+ "id": "expr_hero_sheet",
28
+ "variations": 2,
29
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR HERO — emotion range sheet, consistent face across multiple expressions], painterly concept art, canvas grain"
30
+ }
31
+ ]
32
+ }
@@ -4,8 +4,10 @@
4
4
  "domain": "character-design",
5
5
  "version": "0.1.0",
6
6
  "defaults": {
7
- "checkpoint": "",
8
- "loras": [],
7
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
8
+ "loras": [
9
+ { "name": "classipeintxl_v21.safetensors", "weight": 1.0 }
10
+ ],
9
11
  "width": 1024,
10
12
  "height": 1024,
11
13
  "steps": 8,
@@ -0,0 +1,32 @@
1
+ {
2
+ "wave": "wave1",
3
+ "description": "Creature design — first wave. Subject IDs match this domain's lane id_patterns (concept_, ortho_, action_) so `sdlab generate` will route each candidate into the correct lane instead of default_lane.",
4
+ "defaults": {
5
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
6
+ "lora": { "name": "classipeintxl_v21.safetensors", "weight": 1.0 },
7
+ "width": 1024,
8
+ "height": 1024,
9
+ "steps": 8,
10
+ "cfg": 2.0,
11
+ "sampler": "dpmpp_sde",
12
+ "scheduler": "karras",
13
+ "negative": "photorealistic, photograph, 3d render, smooth CG, sharp photo detail"
14
+ },
15
+ "subjects": [
16
+ {
17
+ "id": "concept_alpha",
18
+ "variations": 2,
19
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR CREATURE — initial concept, ideation exploration], painterly concept art, canvas grain"
20
+ },
21
+ {
22
+ "id": "ortho_alpha",
23
+ "variations": 2,
24
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR CREATURE — turnaround model sheet, orthographic views for modelers], painterly concept art, canvas grain"
25
+ },
26
+ {
27
+ "id": "action_alpha",
28
+ "variations": 2,
29
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR CREATURE — attack stance, locomotion study, movement key], painterly concept art, canvas grain"
30
+ }
31
+ ]
32
+ }
@@ -5,8 +5,10 @@
5
5
  "version": "0.1.0",
6
6
  "description": "Creature concept art — fantasy creatures, alien species, monsters, wildlife, and mythological beings.",
7
7
  "defaults": {
8
- "checkpoint": "",
9
- "loras": [],
8
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
9
+ "loras": [
10
+ { "name": "classipeintxl_v21.safetensors", "weight": 1.0 }
11
+ ],
10
12
  "width": 1024,
11
13
  "height": 1024,
12
14
  "steps": 8,
@@ -7,6 +7,7 @@
7
7
  "edge_defaults": {},
8
8
  "null_faction_patterns": [],
9
9
  "id_fallbacks": {},
10
+ "faction_context": {},
10
11
  "training_profiles": [
11
12
  {
12
13
  "profile_id": "creature-style",
@@ -0,0 +1,37 @@
1
+ {
2
+ "wave": "wave1",
3
+ "description": "Game art — first wave. Subject IDs match this domain's lane id_patterns (char_, env_, prop_, ship_) so `sdlab generate` will route each candidate into the correct lane instead of default_lane.",
4
+ "defaults": {
5
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
6
+ "lora": { "name": "classipeintxl_v21.safetensors", "weight": 1.0 },
7
+ "width": 1024,
8
+ "height": 1024,
9
+ "steps": 8,
10
+ "cfg": 2.0,
11
+ "sampler": "dpmpp_sde",
12
+ "scheduler": "karras",
13
+ "negative": "photorealistic, photograph, 3d render, smooth CG, sharp photo detail"
14
+ },
15
+ "subjects": [
16
+ {
17
+ "id": "char_hero",
18
+ "variations": 2,
19
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR CHARACTER — costume, faction identity, personal gear], painterly concept art, canvas grain"
20
+ },
21
+ {
22
+ "id": "env_town",
23
+ "variations": 2,
24
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR ENVIRONMENT — town, dungeon, overworld location], painterly concept art, canvas grain"
25
+ },
26
+ {
27
+ "id": "prop_key",
28
+ "variations": 2,
29
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR PROP — quest item, treasure, story-carrying object], painterly concept art, canvas grain"
30
+ },
31
+ {
32
+ "id": "ship_flagship",
33
+ "variations": 2,
34
+ "prompt": "oil painting, visible brushstrokes, [DESCRIBE YOUR SHIP OR VEHICLE — exterior view, faction silhouette], painterly concept art, canvas grain"
35
+ }
36
+ ]
37
+ }
@@ -4,8 +4,10 @@
4
4
  "domain": "game-art",
5
5
  "version": "0.1.0",
6
6
  "defaults": {
7
- "checkpoint": "",
8
- "loras": [],
7
+ "checkpoint": "dreamshaperXL_v21TurboDPMSDE.safetensors",
8
+ "loras": [
9
+ { "name": "classipeintxl_v21.safetensors", "weight": 1.0 }
10
+ ],
9
11
  "width": 1024,
10
12
  "height": 1024,
11
13
  "steps": 8,