@mcptoolshop/style-dataset-lab 1.2.1 → 2.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 +45 -0
- package/README.md +95 -116
- package/bin/sdlab.js +103 -0
- package/lib/args.js +106 -0
- package/lib/comfyui.js +109 -0
- package/lib/config.js +104 -0
- package/lib/deprecation.js +14 -0
- package/lib/paths.js +38 -0
- package/lib/records.js +69 -0
- package/package.json +13 -4
- package/scripts/bulk-curate-wave2-5.js +1 -1
- package/scripts/bulk-curate-waves11-18.js +1 -1
- package/scripts/canon-bind.js +268 -332
- package/scripts/compare.js +40 -30
- package/scripts/curate-wave25.js +1 -1
- package/scripts/curate.js +34 -28
- package/scripts/doctor.js +321 -0
- package/scripts/generate-controlnet.js +36 -121
- package/scripts/generate-identity.js +38 -106
- package/scripts/generate-ipadapter.js +42 -119
- package/scripts/generate.js +22 -81
- package/scripts/init.js +226 -0
- package/scripts/migrate-records.js +15 -21
- package/scripts/painterly-test.js +20 -47
- package/scripts/painterly.js +27 -106
- package/templates/domains/architecture/constitution.json +97 -0
- package/templates/domains/architecture/lanes.json +41 -0
- package/templates/domains/architecture/project.json +20 -0
- package/templates/domains/architecture/rubric.json +40 -0
- package/templates/domains/architecture/terminology.json +19 -0
- package/templates/domains/character-design/constitution.json +88 -0
- package/templates/domains/character-design/lanes.json +35 -0
- package/templates/domains/character-design/project.json +17 -0
- package/templates/domains/character-design/rubric.json +38 -0
- package/templates/domains/character-design/terminology.json +21 -0
- package/templates/domains/creature-design/constitution.json +87 -0
- package/templates/domains/creature-design/lanes.json +41 -0
- package/templates/domains/creature-design/project.json +18 -0
- package/templates/domains/creature-design/rubric.json +39 -0
- package/templates/domains/creature-design/terminology.json +20 -0
- package/templates/domains/game-art/constitution.json +113 -0
- package/templates/domains/game-art/lanes.json +47 -0
- package/templates/domains/game-art/project.json +17 -0
- package/templates/domains/game-art/rubric.json +36 -0
- package/templates/domains/game-art/terminology.json +30 -0
- package/templates/domains/vehicle-mech/constitution.json +87 -0
- package/templates/domains/vehicle-mech/lanes.json +41 -0
- package/templates/domains/vehicle-mech/project.json +17 -0
- package/templates/domains/vehicle-mech/rubric.json +40 -0
- package/templates/domains/vehicle-mech/terminology.json +21 -0
package/lib/config.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-project config loader.
|
|
3
|
+
*
|
|
4
|
+
* Loads JSON config files from a project root directory.
|
|
5
|
+
* Falls back to sensible defaults when files don't exist,
|
|
6
|
+
* so unmigrated projects (pre-v2.0) still work.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
|
|
12
|
+
function loadJsonFile(filePath) {
|
|
13
|
+
if (!existsSync(filePath)) return null;
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
16
|
+
} catch (err) {
|
|
17
|
+
throw new Error(`Failed to parse ${filePath}: ${err.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load project.json — project identity and generation defaults.
|
|
23
|
+
*/
|
|
24
|
+
export function loadProjectMeta(projectRoot) {
|
|
25
|
+
const data = loadJsonFile(join(projectRoot, 'project.json'));
|
|
26
|
+
if (!data) {
|
|
27
|
+
return {
|
|
28
|
+
name: 'unknown',
|
|
29
|
+
display_name: 'Unknown Project',
|
|
30
|
+
domain: 'generic',
|
|
31
|
+
version: '0.0.0',
|
|
32
|
+
defaults: {},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Load constitution.json — array of rule objects.
|
|
40
|
+
*/
|
|
41
|
+
export function loadConstitution(projectRoot) {
|
|
42
|
+
const data = loadJsonFile(join(projectRoot, 'constitution.json'));
|
|
43
|
+
if (!data) return { rules: [] };
|
|
44
|
+
if (Array.isArray(data)) return { rules: data };
|
|
45
|
+
if (!data.rules) throw new Error(`constitution.json must have a "rules" array`);
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Load lanes.json — lane definitions with detection patterns.
|
|
51
|
+
*/
|
|
52
|
+
export function loadLanes(projectRoot) {
|
|
53
|
+
const data = loadJsonFile(join(projectRoot, 'lanes.json'));
|
|
54
|
+
if (!data) {
|
|
55
|
+
return { default_lane: 'default', lanes: [] };
|
|
56
|
+
}
|
|
57
|
+
if (!data.lanes) throw new Error(`lanes.json must have a "lanes" array`);
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load rubric.json — scoring dimensions, thresholds, failure modes.
|
|
63
|
+
*/
|
|
64
|
+
export function loadRubric(projectRoot) {
|
|
65
|
+
const data = loadJsonFile(join(projectRoot, 'rubric.json'));
|
|
66
|
+
if (!data) {
|
|
67
|
+
return {
|
|
68
|
+
dimensions: [],
|
|
69
|
+
thresholds: { pass: 0.7, partial: 0.5, fail_ceiling: 0.4 },
|
|
70
|
+
failure_to_rules: {},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Load terminology.json — group vocabulary and detection patterns.
|
|
78
|
+
*/
|
|
79
|
+
export function loadTerminology(projectRoot) {
|
|
80
|
+
const data = loadJsonFile(join(projectRoot, 'terminology.json'));
|
|
81
|
+
if (!data) {
|
|
82
|
+
return {
|
|
83
|
+
group_label: 'group',
|
|
84
|
+
groups: {},
|
|
85
|
+
cross_group_patterns: {},
|
|
86
|
+
edge_defaults: {},
|
|
87
|
+
prompt_fallbacks: {},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return data;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Load all project config files at once.
|
|
95
|
+
*/
|
|
96
|
+
export function loadProjectConfig(projectRoot) {
|
|
97
|
+
return {
|
|
98
|
+
meta: loadProjectMeta(projectRoot),
|
|
99
|
+
constitution: loadConstitution(projectRoot),
|
|
100
|
+
lanes: loadLanes(projectRoot),
|
|
101
|
+
rubric: loadRubric(projectRoot),
|
|
102
|
+
terminology: loadTerminology(projectRoot),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecation warning helper — fires once per flag per process.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const warned = new Set();
|
|
6
|
+
|
|
7
|
+
export function warnDeprecated(oldFlag, newFlag) {
|
|
8
|
+
const key = `${oldFlag}→${newFlag}`;
|
|
9
|
+
if (warned.has(key)) return;
|
|
10
|
+
warned.add(key);
|
|
11
|
+
process.stderr.write(
|
|
12
|
+
`\x1b[33m⚠ --${oldFlag} is deprecated and will be removed in v3.0. Use --${newFlag} instead.\x1b[0m\n`
|
|
13
|
+
);
|
|
14
|
+
}
|
package/lib/paths.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path resolution — single source of truth for repo root and project paths.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
|
|
12
|
+
/** Absolute path to the repository root. */
|
|
13
|
+
export const REPO_ROOT = join(__dirname, '..');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the root directory for a named project.
|
|
17
|
+
* @param {string} name — project name (e.g. 'star-freight')
|
|
18
|
+
* @returns {string} absolute path to projects/<name>/
|
|
19
|
+
*/
|
|
20
|
+
export function getProjectRoot(name) {
|
|
21
|
+
const root = join(REPO_ROOT, 'projects', name);
|
|
22
|
+
if (!existsSync(root)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Project "${name}" not found at ${root}.\n` +
|
|
25
|
+
`Run: sdlab init ${name} --domain <domain>`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return root;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Join segments onto a project root.
|
|
33
|
+
* @param {string} name — project name
|
|
34
|
+
* @param {...string} segments — path segments
|
|
35
|
+
*/
|
|
36
|
+
export function resolveProjectPath(name, ...segments) {
|
|
37
|
+
return join(getProjectRoot(name), ...segments);
|
|
38
|
+
}
|
package/lib/records.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record builder and I/O utilities.
|
|
3
|
+
*
|
|
4
|
+
* Standardizes the JSON record format used across all scripts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Build a base provenance record (generated by txt2img scripts).
|
|
12
|
+
*/
|
|
13
|
+
export function buildBaseRecord(id, assetPath, imageData, provenance) {
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
schema_version: '2.0.0',
|
|
17
|
+
created_at: new Date().toISOString(),
|
|
18
|
+
asset_path: assetPath,
|
|
19
|
+
image: {
|
|
20
|
+
format: 'png',
|
|
21
|
+
width: imageData.width || 1024,
|
|
22
|
+
height: imageData.height || 1024,
|
|
23
|
+
bytes: imageData.bytes || 0,
|
|
24
|
+
},
|
|
25
|
+
provenance,
|
|
26
|
+
judgment: null,
|
|
27
|
+
canon: null,
|
|
28
|
+
iteration: null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build an identity record (extends base with identity + lineage blocks).
|
|
34
|
+
*/
|
|
35
|
+
export function buildIdentityRecord(baseRecord, identity, lineage) {
|
|
36
|
+
return {
|
|
37
|
+
...baseRecord,
|
|
38
|
+
identity,
|
|
39
|
+
lineage,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load a record by asset ID from a records directory.
|
|
45
|
+
* @param {string} recordsDir — absolute path to records/
|
|
46
|
+
* @param {string} assetId — record filename (without .json)
|
|
47
|
+
* @returns {Promise<Object|null>}
|
|
48
|
+
*/
|
|
49
|
+
export async function loadRecord(recordsDir, assetId) {
|
|
50
|
+
const path = join(recordsDir, `${assetId}.json`);
|
|
51
|
+
try {
|
|
52
|
+
const raw = await readFile(path, 'utf-8');
|
|
53
|
+
return JSON.parse(raw);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if (err.code === 'ENOENT') return null;
|
|
56
|
+
throw new Error(`Failed to load record ${assetId}: ${err.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save a record to disk.
|
|
62
|
+
* @param {string} recordsDir — absolute path to records/
|
|
63
|
+
* @param {string} assetId — record filename (without .json)
|
|
64
|
+
* @param {Object} record — the record object
|
|
65
|
+
*/
|
|
66
|
+
export async function saveRecord(recordsDir, assetId, record) {
|
|
67
|
+
const path = join(recordsDir, `${assetId}.json`);
|
|
68
|
+
await writeFile(path, JSON.stringify(record, null, 2) + '\n');
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcptoolshop/style-dataset-lab",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Canon-bound visual dataset pipeline — define project canon, generate and curate assets, bind to constitution rules, export trustworthy training data",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sdlab": "./bin/sdlab.js"
|
|
8
|
+
},
|
|
6
9
|
"scripts": {
|
|
7
10
|
"generate": "node scripts/generate.js",
|
|
8
11
|
"curate": "node scripts/curate.js",
|
|
@@ -10,10 +13,12 @@
|
|
|
10
13
|
"canon-bind": "node scripts/canon-bind.js",
|
|
11
14
|
"painterly": "node scripts/painterly.js",
|
|
12
15
|
"generate:identity": "node scripts/generate-identity.js",
|
|
13
|
-
"export": "echo 'Usage: repo-dataset visual generate ./
|
|
14
|
-
"inspect": "echo 'Usage: repo-dataset visual inspect ./
|
|
16
|
+
"export": "echo 'Usage: repo-dataset visual generate ./projects/<project> --format trl'",
|
|
17
|
+
"inspect": "echo 'Usage: repo-dataset visual inspect ./projects/<project>'"
|
|
15
18
|
},
|
|
16
19
|
"files": [
|
|
20
|
+
"bin/",
|
|
21
|
+
"lib/",
|
|
17
22
|
"scripts/",
|
|
18
23
|
"templates/",
|
|
19
24
|
"README.md",
|
|
@@ -25,6 +30,10 @@
|
|
|
25
30
|
"dataset",
|
|
26
31
|
"visual",
|
|
27
32
|
"training-data",
|
|
33
|
+
"concept-art",
|
|
34
|
+
"visual-canon",
|
|
35
|
+
"art-direction",
|
|
36
|
+
"dataset-pipeline",
|
|
28
37
|
"comfyui",
|
|
29
38
|
"vlm",
|
|
30
39
|
"fine-tuning",
|
|
@@ -11,7 +11,7 @@ import { join } from "node:path";
|
|
|
11
11
|
|
|
12
12
|
const REPO_ROOT = new URL("..", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
|
|
13
13
|
const GAME = process.argv.find((a, i) => process.argv[i - 1] === '--game') || 'star-freight';
|
|
14
|
-
const GAME_ROOT = join(REPO_ROOT, '
|
|
14
|
+
const GAME_ROOT = join(REPO_ROOT, 'projects', GAME);
|
|
15
15
|
|
|
16
16
|
const DEFAULT_APPROVED_SCORES = {
|
|
17
17
|
silhouette_clarity: 0.8, palette_adherence: 0.8, material_fidelity: 0.8,
|
|
@@ -17,7 +17,7 @@ import { join, basename } from 'path';
|
|
|
17
17
|
|
|
18
18
|
const REPO_ROOT = new URL("..", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
|
|
19
19
|
const GAME = process.argv.find((a, i) => process.argv[i - 1] === '--game') || 'star-freight';
|
|
20
|
-
const GAME_ROOT = join(REPO_ROOT, '
|
|
20
|
+
const GAME_ROOT = join(REPO_ROOT, 'projects', GAME);
|
|
21
21
|
|
|
22
22
|
const RECORDS_DIR = join(GAME_ROOT, 'records');
|
|
23
23
|
const CANDIDATES_DIR = join(GAME_ROOT, 'outputs', 'candidates');
|