@mcptoolshop/style-dataset-lab 2.1.0 → 2.2.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.
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * eval-run.js — Create, score, and inspect eval runs.
5
+ *
6
+ * Usage:
7
+ * sdlab eval-run create --manifest <id> --eval-pack <id> [--project <name>]
8
+ * sdlab eval-run score <eval-run-id> --outputs <path> [--project <name>]
9
+ * sdlab eval-run show <eval-run-id> [--project <name>]
10
+ * sdlab eval-run list [--project <name>]
11
+ */
12
+
13
+ import { readFile } from 'node:fs/promises';
14
+ import { join } from 'node:path';
15
+ import { getProjectName } from '../lib/args.js';
16
+ import { REPO_ROOT } from '../lib/paths.js';
17
+ import { listTrainingManifests } from '../lib/training-manifests.js';
18
+ import { listEvalPacks } from '../lib/eval-pack.js';
19
+ import { createEvalRun, scoreEvalRun, loadEvalRun, listEvalRuns } from '../lib/eval-runs.js';
20
+
21
+ export async function run(argv = process.argv.slice(2)) {
22
+ const projectName = getProjectName(argv);
23
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
24
+ const subcommand = argv.find(a => !a.startsWith('--') && !a.startsWith('er-')) || 'list';
25
+ const args = argv.filter(a => a !== subcommand);
26
+
27
+ if (subcommand === 'create') {
28
+ // Find manifest
29
+ let manifestId;
30
+ const manifestIdx = args.indexOf('--manifest');
31
+ if (manifestIdx >= 0) {
32
+ manifestId = args[manifestIdx + 1];
33
+ } else {
34
+ const manifests = await listTrainingManifests(projectRoot);
35
+ if (manifests.length === 0) throw new Error('No training manifests. Run: sdlab training-manifest create');
36
+ manifestId = manifests[manifests.length - 1].id;
37
+ }
38
+
39
+ // Find eval pack
40
+ let evalPackId;
41
+ const evalIdx = args.indexOf('--eval-pack');
42
+ if (evalIdx >= 0) {
43
+ evalPackId = args[evalIdx + 1];
44
+ } else {
45
+ const packs = await listEvalPacks(projectRoot);
46
+ if (packs.length === 0) throw new Error('No eval packs. Run: sdlab eval-pack build');
47
+ evalPackId = packs[packs.length - 1].id;
48
+ }
49
+
50
+ console.log(`\x1b[1msdlab eval-run create\x1b[0m — ${projectName}`);
51
+ console.log(` Manifest: ${manifestId}`);
52
+ console.log(` Eval pack: ${evalPackId}`);
53
+ console.log('');
54
+
55
+ const result = await createEvalRun(projectRoot, manifestId, evalPackId);
56
+ console.log(` \x1b[32m✓\x1b[0m Eval run: ${result.evalRunId}`);
57
+ console.log(` Status: created (use "sdlab eval-run score" to score outputs)`);
58
+
59
+ } else if (subcommand === 'score') {
60
+ const evalRunId = args.find(a => a.startsWith('er-'));
61
+ if (!evalRunId) throw new Error('Usage: sdlab eval-run score <eval-run-id> --outputs <path>');
62
+
63
+ const outputsIdx = args.indexOf('--outputs');
64
+ if (outputsIdx < 0) throw new Error('--outputs <path> is required (JSONL with record_id, accept, notes)');
65
+ const outputsPath = args[outputsIdx + 1];
66
+
67
+ console.log(`\x1b[1msdlab eval-run score\x1b[0m — ${evalRunId}\n`);
68
+
69
+ const { scorecard } = await scoreEvalRun(projectRoot, evalRunId, outputsPath);
70
+
71
+ const verdict = scorecard.overall_verdict === 'pass'
72
+ ? `\x1b[32mPASS\x1b[0m` : `\x1b[31mFAIL\x1b[0m`;
73
+ console.log(` Verdict: ${verdict} (${(scorecard.overall_score * 100).toFixed(1)}%)`);
74
+ console.log(` Outputs: ${scorecard.output_count} (${scorecard.accepted_count} accepted, ${scorecard.rejected_count} rejected)`);
75
+ console.log('');
76
+ console.log(' Tasks:');
77
+ for (const [name, task] of Object.entries(scorecard.tasks)) {
78
+ const status = task.passed ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
79
+ console.log(` ${status} ${name.padEnd(24)} ${(task.score * 100).toFixed(1)}%`);
80
+ }
81
+
82
+ } else if (subcommand === 'show') {
83
+ const evalRunId = args.find(a => a.startsWith('er-'));
84
+ if (!evalRunId) throw new Error('Usage: sdlab eval-run show <eval-run-id>');
85
+
86
+ const run = await loadEvalRun(projectRoot, evalRunId);
87
+ console.log(`\x1b[1mEval run\x1b[0m — ${evalRunId}\n`);
88
+ console.log(` Created: ${run.created_at}`);
89
+ console.log(` Status: ${run.status}`);
90
+ console.log(` Manifest: ${run.training_manifest_id}`);
91
+ console.log(` Eval pack: ${run.eval_pack_id}`);
92
+ console.log(` Profile: ${run.training_profile_id}`);
93
+ console.log(` Adapter: ${run.adapter_target}`);
94
+
95
+ if (run.scorecard) {
96
+ console.log('');
97
+ console.log(` Verdict: ${run.scorecard.overall_verdict.toUpperCase()}`);
98
+ console.log(` Score: ${(run.scorecard.overall_score * 100).toFixed(1)}%`);
99
+ console.log(` Outputs: ${run.scorecard.output_count} (${run.scorecard.accepted_count} accepted)`);
100
+ }
101
+
102
+ } else if (subcommand === 'list') {
103
+ const runs = await listEvalRuns(projectRoot);
104
+ if (runs.length === 0) {
105
+ console.log('No eval runs found. Run: sdlab eval-run create');
106
+ return;
107
+ }
108
+ console.log(`\x1b[1mEval runs\x1b[0m — ${projectName}\n`);
109
+ for (const r of runs) {
110
+ const verdict = r.verdict ? ` verdict=${r.verdict}` : '';
111
+ console.log(` ${r.id} manifest=${r.manifest} status=${r.status}${verdict} ${r.created_at}`);
112
+ }
113
+
114
+ } else {
115
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: create, score, show, list`);
116
+ }
117
+ }
118
+
119
+ if (process.argv[1] && (process.argv[1].endsWith('eval-run.js') || process.argv[1].endsWith('eval-run'))) {
120
+ run().catch((err) => { console.error(err.message || err); process.exit(1); });
121
+ }
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * implementation-pack.js — Build and inspect implementation example packs.
5
+ *
6
+ * Usage:
7
+ * sdlab implementation-pack build --manifest <id> [--project <name>]
8
+ * sdlab implementation-pack show <impl-id> [--project <name>]
9
+ * sdlab implementation-pack list [--project <name>]
10
+ */
11
+
12
+ import { join } from 'node:path';
13
+ import { getProjectName } from '../lib/args.js';
14
+ import { REPO_ROOT } from '../lib/paths.js';
15
+ import { listTrainingManifests } from '../lib/training-manifests.js';
16
+ import { buildImplementationPack, listImplementationPacks, loadImplementationPack } from '../lib/implementation-packs.js';
17
+
18
+ export async function run(argv = process.argv.slice(2)) {
19
+ const projectName = getProjectName(argv);
20
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
21
+ const subcommand = argv.find(a => !a.startsWith('--') && !a.startsWith('impl-') && !a.startsWith('tm-')) || 'list';
22
+ const args = argv.filter(a => a !== subcommand);
23
+
24
+ if (subcommand === 'build') {
25
+ let manifestId;
26
+ const manifestIdx = args.indexOf('--manifest');
27
+ if (manifestIdx >= 0) {
28
+ manifestId = args[manifestIdx + 1];
29
+ } else {
30
+ const manifests = await listTrainingManifests(projectRoot);
31
+ if (manifests.length === 0) throw new Error('No training manifests. Run: sdlab training-manifest create');
32
+ manifestId = manifests[manifests.length - 1].id;
33
+ }
34
+
35
+ console.log(`\x1b[1msdlab implementation-pack build\x1b[0m — ${projectName}`);
36
+ console.log(` Manifest: ${manifestId}`);
37
+ console.log('');
38
+
39
+ const result = await buildImplementationPack(projectRoot, manifestId);
40
+
41
+ console.log(` \x1b[32m✓\x1b[0m Implementation pack: ${result.implId}`);
42
+ console.log(` Prompt examples: ${result.prompts}`);
43
+ console.log(` Known failures: ${result.failures}`);
44
+ console.log(` Subject continuity: ${result.subjects}`);
45
+
46
+ } else if (subcommand === 'show') {
47
+ const implId = args.find(a => a.startsWith('impl-'));
48
+ if (!implId) throw new Error('Usage: sdlab implementation-pack show <impl-id>');
49
+
50
+ const pack = await loadImplementationPack(projectRoot, implId);
51
+ console.log(`\x1b[1mImplementation pack\x1b[0m — ${implId}\n`);
52
+ console.log(` Created: ${pack.created_at}`);
53
+ console.log(` Manifest: ${pack.training_manifest_id}`);
54
+ console.log(` Profile: ${pack.training_profile_id}`);
55
+ console.log(` Prompts: ${pack.counts.prompt_examples}`);
56
+ console.log(` Failures: ${pack.counts.known_failures}`);
57
+ console.log(` Subjects: ${pack.counts.subject_examples}`);
58
+ console.log(` Lanes: ${pack.counts.lanes_covered}`);
59
+
60
+ } else if (subcommand === 'list') {
61
+ const packs = await listImplementationPacks(projectRoot);
62
+ if (packs.length === 0) {
63
+ console.log('No implementation packs. Run: sdlab implementation-pack build');
64
+ return;
65
+ }
66
+ console.log(`\x1b[1mImplementation packs\x1b[0m — ${projectName}\n`);
67
+ for (const p of packs) {
68
+ console.log(` ${p.id} manifest=${p.manifest} prompts=${p.prompts} failures=${p.failures} ${p.created_at}`);
69
+ }
70
+
71
+ } else {
72
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: build, show, list`);
73
+ }
74
+ }
75
+
76
+ if (process.argv[1] && (process.argv[1].endsWith('implementation-pack.js') || process.argv[1].endsWith('implementation-pack'))) {
77
+ run().catch((err) => { console.error(err.message || err); process.exit(1); });
78
+ }
package/scripts/init.js CHANGED
@@ -35,6 +35,11 @@ const PROJECT_DIRS = [
35
35
  'snapshots',
36
36
  'splits',
37
37
  'eval-packs',
38
+ 'training/profiles',
39
+ 'training/manifests',
40
+ 'training/packages',
41
+ 'training/eval-runs',
42
+ 'training/implementations',
38
43
  ];
39
44
 
40
45
  function isValidName(name) {
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * reingest.js — Re-ingest generated outputs as new project records.
5
+ *
6
+ * Usage:
7
+ * sdlab reingest generated --source <dir> --manifest <id> [--dry-run] [--project <name>]
8
+ * sdlab reingest audit [--project <name>]
9
+ */
10
+
11
+ import { join } from 'node:path';
12
+ import { getProjectName } from '../lib/args.js';
13
+ import { REPO_ROOT } from '../lib/paths.js';
14
+ import { reingestGenerated, auditReingest } from '../lib/reingest.js';
15
+
16
+ export async function run(argv = process.argv.slice(2)) {
17
+ const projectName = getProjectName(argv);
18
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
19
+ const subcommand = argv.find(a => !a.startsWith('--')) || 'audit';
20
+ const args = argv.filter(a => a !== subcommand);
21
+
22
+ if (subcommand === 'generated') {
23
+ const sourceIdx = args.indexOf('--source');
24
+ if (sourceIdx < 0) throw new Error('--source <dir> is required (directory of generated images)');
25
+ const sourcePath = args[sourceIdx + 1];
26
+
27
+ const manifestIdx = args.indexOf('--manifest');
28
+ if (manifestIdx < 0) throw new Error('--manifest <id> is required (training manifest that produced the outputs)');
29
+ const manifestId = args[manifestIdx + 1];
30
+
31
+ const dryRun = args.includes('--dry-run');
32
+
33
+ console.log(`\x1b[1msdlab reingest generated\x1b[0m — ${projectName}`);
34
+ console.log(` Source: ${sourcePath}`);
35
+ console.log(` Manifest: ${manifestId}`);
36
+ if (dryRun) console.log(` Mode: DRY RUN (no records written)`);
37
+ console.log('');
38
+
39
+ const result = await reingestGenerated(projectRoot, sourcePath, manifestId, { dryRun });
40
+
41
+ console.log(` \x1b[32m✓\x1b[0m Created: ${result.created} records`);
42
+ if (result.skipped > 0) {
43
+ console.log(` Skipped: ${result.skipped} (already exist)`);
44
+ }
45
+ if (result.created > 0) {
46
+ console.log('');
47
+ console.log(' Next steps:');
48
+ console.log(' 1. Review: sdlab curate <id> approved/rejected "explanation"');
49
+ console.log(' 2. Bind: sdlab bind --project ' + projectName);
50
+ console.log(' 3. Then eligible for future snapshots');
51
+ }
52
+
53
+ } else if (subcommand === 'audit') {
54
+ console.log(`\x1b[1msdlab reingest audit\x1b[0m — ${projectName}\n`);
55
+
56
+ const result = await auditReingest(projectRoot);
57
+
58
+ if (result.total === 0) {
59
+ console.log(' No re-ingested records found (gen_* prefix).');
60
+ return;
61
+ }
62
+
63
+ console.log(` Re-ingested records: ${result.total}`);
64
+ console.log(` Reviewed: ${result.reviewed}`);
65
+ console.log(` Unreviewed: ${result.unreviewed}`);
66
+ console.log(` Canon-bound: ${result.bound}`);
67
+ console.log(` Unbound: ${result.unbound}`);
68
+
69
+ if (result.unreviewed > 0) {
70
+ console.log(`\n \x1b[33m⚠\x1b[0m ${result.unreviewed} records awaiting review`);
71
+ }
72
+ if (result.unbound > 0) {
73
+ console.log(` \x1b[33m⚠\x1b[0m ${result.unbound} reviewed records not yet canon-bound`);
74
+ }
75
+ if (result.unreviewed === 0 && result.unbound === 0) {
76
+ console.log(`\n \x1b[32m✓\x1b[0m All re-ingested records are reviewed and canon-bound`);
77
+ }
78
+
79
+ } else {
80
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: generated, audit`);
81
+ }
82
+ }
83
+
84
+ if (process.argv[1] && (process.argv[1].endsWith('reingest.js') || process.argv[1].endsWith('reingest'))) {
85
+ run().catch((err) => { console.error(err.message || err); process.exit(1); });
86
+ }
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * training-manifest.js — Create, validate, list, and show training manifests.
5
+ *
6
+ * Usage:
7
+ * sdlab training-manifest create --export <id> --profile <id> [--adapter <target>] [--base-model <name>] [--project <name>]
8
+ * sdlab training-manifest validate <manifest-id> [--project <name>]
9
+ * sdlab training-manifest show <manifest-id> [--project <name>]
10
+ * sdlab training-manifest list [--project <name>]
11
+ */
12
+
13
+ import { join } from 'node:path';
14
+ import { getProjectName } from '../lib/args.js';
15
+ import { REPO_ROOT } from '../lib/paths.js';
16
+ import { listExports } from '../lib/export.js';
17
+ import {
18
+ createTrainingManifest,
19
+ validateTrainingManifest,
20
+ loadTrainingManifest,
21
+ listTrainingManifests,
22
+ } from '../lib/training-manifests.js';
23
+
24
+ export async function run(argv = process.argv.slice(2)) {
25
+ const projectName = getProjectName(argv);
26
+ 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);
29
+
30
+ if (subcommand === 'create') {
31
+ // Required: --export and --profile
32
+ const exportIdx = args.indexOf('--export');
33
+ if (exportIdx < 0) {
34
+ // Try latest export
35
+ const exports = await listExports(projectRoot);
36
+ if (exports.length === 0) throw new Error('No exports found. Run: sdlab export build');
37
+ }
38
+ const exportId = exportIdx >= 0 ? args[exportIdx + 1] : (await listExports(projectRoot)).pop().id;
39
+
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];
43
+
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;
49
+
50
+ console.log(`\x1b[1msdlab training-manifest create\x1b[0m — ${projectName}`);
51
+ console.log(` Export: ${exportId}`);
52
+ console.log(` Profile: ${profileId}`);
53
+ if (adapterTarget) console.log(` Adapter: ${adapterTarget}`);
54
+ if (baseModel) console.log(` Base: ${baseModel}`);
55
+ console.log('');
56
+
57
+ const result = await createTrainingManifest(projectRoot, exportId, profileId, {
58
+ adapterTarget,
59
+ baseModel,
60
+ });
61
+
62
+ console.log(` \x1b[32m✓\x1b[0m Manifest: ${result.manifestId}`);
63
+
64
+ } 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>');
67
+
68
+ console.log(`\x1b[1msdlab training-manifest validate\x1b[0m — ${manifestId}\n`);
69
+ const { valid, issues } = await validateTrainingManifest(projectRoot, manifestId);
70
+
71
+ if (valid) {
72
+ console.log(` \x1b[32m✓\x1b[0m Manifest is valid — all references intact, fingerprints match`);
73
+ } else {
74
+ console.log(` \x1b[31m✗\x1b[0m Manifest has issues:`);
75
+ for (const issue of issues) {
76
+ console.log(` - ${issue}`);
77
+ }
78
+ }
79
+
80
+ } 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>');
83
+
84
+ const manifest = await loadTrainingManifest(projectRoot, manifestId);
85
+ console.log(`\x1b[1mTraining manifest\x1b[0m — ${manifestId}\n`);
86
+ console.log(` Created: ${manifest.created_at}`);
87
+ console.log(` Profile: ${manifest.training_profile_id}`);
88
+ console.log(` Export: ${manifest.source_export_id}`);
89
+ console.log(` Snapshot: ${manifest.source_snapshot_id}`);
90
+ console.log(` Split: ${manifest.source_split_id}`);
91
+ console.log(` Adapter: ${manifest.adapter_target}`);
92
+ console.log(` Base model: ${manifest.base_model || '(not specified)'}`);
93
+ console.log(` Caption: ${manifest.caption_mode}`);
94
+ console.log(` Records: ${manifest.dataset_counts?.records || 0}`);
95
+ console.log(` Fingerprint: ${manifest.config_fingerprint.slice(0, 16)}...`);
96
+ console.log(` Export hash: ${manifest.export_manifest_hash.slice(0, 16)}...`);
97
+ if (manifest.linked_eval_pack_ids?.length) {
98
+ console.log(` Eval packs: ${manifest.linked_eval_pack_ids.length}`);
99
+ }
100
+
101
+ } else if (subcommand === 'list') {
102
+ const manifests = await listTrainingManifests(projectRoot);
103
+ if (manifests.length === 0) {
104
+ console.log('No training manifests found. Run: sdlab training-manifest create');
105
+ return;
106
+ }
107
+ console.log(`\x1b[1mTraining manifests\x1b[0m — ${projectName}\n`);
108
+ for (const m of manifests) {
109
+ console.log(` ${m.id} profile=${m.profile} export=${m.export_id} adapter=${m.adapter} records=${m.records} ${m.created_at}`);
110
+ }
111
+
112
+ } else {
113
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: create, validate, show, list`);
114
+ }
115
+ }
116
+
117
+ 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); });
119
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * training-package.js — Build and inspect training packages.
5
+ *
6
+ * Usage:
7
+ * sdlab training-package build --manifest <id> [--adapter <target>] [--copy] [--project <name>]
8
+ * sdlab training-package show <package-id> [--project <name>]
9
+ * sdlab training-package list [--project <name>]
10
+ */
11
+
12
+ import { join } from 'node:path';
13
+ import { getProjectName } from '../lib/args.js';
14
+ import { REPO_ROOT } from '../lib/paths.js';
15
+ import { listTrainingManifests } from '../lib/training-manifests.js';
16
+ import { buildTrainingPackage, listTrainingPackages, loadTrainingPackage } from '../lib/training-packages.js';
17
+
18
+ export async function run(argv = process.argv.slice(2)) {
19
+ const projectName = getProjectName(argv);
20
+ 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);
23
+
24
+ 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 {
31
+ const manifests = await listTrainingManifests(projectRoot);
32
+ if (manifests.length === 0) throw new Error('No training manifests found. Run: sdlab training-manifest create');
33
+ manifestId = manifests[manifests.length - 1].id;
34
+ }
35
+
36
+ const adapterIdx = args.indexOf('--adapter');
37
+ const adapterOverride = adapterIdx >= 0 ? args[adapterIdx + 1] : undefined;
38
+ const copy = args.includes('--copy');
39
+
40
+ console.log(`\x1b[1msdlab training-package build\x1b[0m — ${projectName}`);
41
+ console.log(` Manifest: ${manifestId}`);
42
+ if (adapterOverride) console.log(` Adapter: ${adapterOverride}`);
43
+ console.log(` Images: ${copy ? 'copy' : 'symlink'}`);
44
+ console.log('');
45
+
46
+ const result = await buildTrainingPackage(projectRoot, manifestId, { copy, adapterOverride });
47
+
48
+ console.log(` \x1b[32m✓\x1b[0m Package: ${result.packageId}`);
49
+ console.log(` Records: ${result.records}`);
50
+ console.log(` Images: ${result.images}`);
51
+
52
+ } 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>');
55
+
56
+ const pkg = await loadTrainingPackage(projectRoot, packageId);
57
+ console.log(`\x1b[1mTraining package\x1b[0m — ${packageId}\n`);
58
+ console.log(` Created: ${pkg.created_at}`);
59
+ console.log(` Profile: ${pkg.training_profile_id}`);
60
+ console.log(` Adapter: ${pkg.adapter_target}`);
61
+ console.log(` Manifest: ${pkg.training_manifest_id}`);
62
+ console.log(` Export: ${pkg.source_export_id}`);
63
+ console.log(` Records: ${pkg.counts.total_records} (train=${pkg.counts.train} val=${pkg.counts.val} test=${pkg.counts.test})`);
64
+ console.log(` Images: ${pkg.counts.images}`);
65
+
66
+ } else if (subcommand === 'list') {
67
+ const packages = await listTrainingPackages(projectRoot);
68
+ if (packages.length === 0) {
69
+ console.log('No training packages found. Run: sdlab training-package build');
70
+ return;
71
+ }
72
+ console.log(`\x1b[1mTraining packages\x1b[0m — ${projectName}\n`);
73
+ for (const p of packages) {
74
+ console.log(` ${p.id} profile=${p.profile} adapter=${p.adapter} records=${p.records} images=${p.images} ${p.created_at}`);
75
+ }
76
+
77
+ } else {
78
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: build, show, list`);
79
+ }
80
+ }
81
+
82
+ 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); });
84
+ }
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * training-profile.js — List and show training profiles.
5
+ *
6
+ * Usage:
7
+ * sdlab training-profile list [--project <name>]
8
+ * sdlab training-profile show <profile-id> [--project <name>]
9
+ */
10
+
11
+ import { join } from 'node:path';
12
+ import { getProjectName } from '../lib/args.js';
13
+ import { REPO_ROOT } from '../lib/paths.js';
14
+ import { listTrainingProfiles, loadTrainingProfile } from '../lib/training-profiles.js';
15
+
16
+ export async function run(argv = process.argv.slice(2)) {
17
+ const projectName = getProjectName(argv);
18
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
19
+ const subcommand = argv.find(a => !a.startsWith('--')) || 'list';
20
+ const args = argv.filter(a => a !== subcommand);
21
+
22
+ if (subcommand === 'list') {
23
+ const profiles = await listTrainingProfiles(projectRoot);
24
+ if (profiles.length === 0) {
25
+ console.log('No training profiles found. Add JSON files to training/profiles/');
26
+ return;
27
+ }
28
+ console.log(`\x1b[1mTraining profiles\x1b[0m — ${projectName}\n`);
29
+ for (const p of profiles) {
30
+ console.log(` ${p.profile_id.padEnd(30)} ${p.target_family.padEnd(15)} ${p.asset_type.padEnd(10)} lanes=${p.lanes} adapters=${p.adapters.join(',')}`);
31
+ }
32
+
33
+ } 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>');
36
+
37
+ const profile = await loadTrainingProfile(projectRoot, profileId);
38
+ console.log(`\x1b[1mTraining profile\x1b[0m — ${profileId}\n`);
39
+ console.log(` Label: ${profile.label}`);
40
+ console.log(` Target family: ${profile.target_family}`);
41
+ console.log(` Asset type: ${profile.asset_type}`);
42
+ console.log(` Intended use: ${profile.intended_use || '(not specified)'}`);
43
+ console.log(` Eligible lanes: ${profile.eligible_lanes.join(', ')}`);
44
+ console.log(` Adapter targets: ${profile.adapter_targets.join(', ')}`);
45
+ if (profile.caption_strategy) console.log(` Caption: ${profile.caption_strategy}`);
46
+ if (profile.base_model_recommendations?.length) {
47
+ console.log(` Base models: ${profile.base_model_recommendations.join(', ')}`);
48
+ }
49
+ if (profile.excluded_tags?.length) {
50
+ console.log(` Excluded tags: ${profile.excluded_tags.join(', ')}`);
51
+ }
52
+ if (profile.required_eval_pack_profiles?.length) {
53
+ console.log(` Required evals: ${profile.required_eval_pack_profiles.join(', ')}`);
54
+ }
55
+
56
+ } else {
57
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: list, show`);
58
+ }
59
+ }
60
+
61
+ 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); });
63
+ }