@mcptoolshop/style-dataset-lab 3.3.0 → 3.4.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 +33 -0
- package/README.es.md +35 -25
- package/README.fr.md +38 -28
- package/README.hi.md +36 -26
- package/README.it.md +37 -27
- package/README.ja.md +40 -30
- package/README.md +15 -5
- package/README.pt-BR.md +35 -25
- package/README.zh.md +33 -23
- package/bin/sdlab.js +51 -4
- package/lib/adapters/ai-toolkit.js +37 -5
- package/lib/adapters/comfyui-runner.js +79 -26
- package/lib/adapters/comfyui-workflows.js +10 -2
- package/lib/adapters/diffusers-lora.js +37 -5
- package/lib/adapters/generic-image-caption.js +43 -5
- package/lib/args.js +16 -1
- package/lib/batch-modes.js +29 -8
- package/lib/batch-runs.js +29 -1
- package/lib/batch-sheet-render.js +13 -3
- package/lib/brief-compiler.js +19 -0
- package/lib/candidate-sheet.js +327 -0
- package/lib/canon-build/build.js +146 -93
- package/lib/captions.js +57 -0
- package/lib/card.js +74 -2
- package/lib/comfyui.js +116 -18
- package/lib/config.js +116 -12
- package/lib/critique-engine.js +36 -2
- package/lib/eligibility.js +121 -9
- package/lib/eval-pack.js +14 -14
- package/lib/eval-runs.js +33 -0
- package/lib/export.js +122 -34
- package/lib/freeze-gate.js +13 -3
- package/lib/freeze-stamp.js +28 -3
- package/lib/generated-provenance.js +16 -1
- package/lib/implementation-packs.js +95 -17
- package/lib/ingest.js +396 -0
- package/lib/judgment-provenance.js +220 -0
- package/lib/measure.js +422 -0
- package/lib/paths.js +196 -7
- package/lib/reingest-selected.js +98 -16
- package/lib/run-manifest.js +102 -16
- package/lib/run-summary.js +12 -6
- package/lib/runtime-runs.js +45 -4
- package/lib/snapshot.js +114 -15
- package/lib/split.js +364 -48
- package/lib/training-manifests.js +114 -21
- package/lib/training-packages.js +147 -15
- package/lib/workflow-profiles.js +24 -3
- package/package.json +3 -2
- package/runtime/comfyui/txt2img-qwen.json +38 -0
- package/runtime/comfyui/txt2img-standard.json +21 -0
- package/scripts/_resume.js +62 -0
- package/scripts/_shared-args.js +44 -0
- package/scripts/brief-compile.js +2 -1
- package/scripts/brief-show.js +2 -1
- package/scripts/canon-bind.js +2 -2
- package/scripts/canon-build.js +27 -20
- package/scripts/canon-drift.js +16 -7
- package/scripts/canon-freeze-status.js +14 -6
- package/scripts/canon-freeze.js +27 -11
- package/scripts/canon-unfreeze.js +17 -9
- package/scripts/card.js +2 -2
- package/scripts/compare.js +2 -2
- package/scripts/critique.js +16 -1
- package/scripts/curate.js +2 -2
- package/scripts/doctor.js +181 -55
- package/scripts/eligibility.js +42 -2
- package/scripts/eval-pack.js +2 -2
- package/scripts/eval-run.js +2 -2
- package/scripts/export.js +2 -2
- package/scripts/generate-controlnet.js +45 -25
- package/scripts/generate-identity.js +53 -23
- package/scripts/generate-ipadapter.js +44 -23
- package/scripts/generate.js +48 -5
- package/scripts/implementation-pack.js +2 -2
- package/scripts/ingest.js +145 -0
- package/scripts/init.js +73 -13
- package/scripts/measure.js +170 -0
- package/scripts/measure_image.py +458 -0
- package/scripts/migrate-records.js +142 -0
- package/scripts/painterly-test.js +4 -3
- package/scripts/painterly.js +7 -3
- package/scripts/qwen_generate.py +138 -20
- package/scripts/reingest.js +2 -2
- package/scripts/sheet.js +142 -0
- package/scripts/snapshot.js +2 -2
- package/scripts/split.js +2 -2
- package/scripts/training-manifest.js +2 -2
- package/scripts/training-package.js +2 -2
- package/scripts/training-profile.js +2 -2
- package/scripts/workflow-list.js +2 -1
- package/scripts/workflow-show.js +2 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _shared-args.js — internal helper module (NOT a CLI command; underscore
|
|
3
|
+
* prefix matches the existing scripts/_batch_cut.py / _seam_cutout.py
|
|
4
|
+
* convention for "helper, not dispatched by bin/sdlab.js").
|
|
5
|
+
*
|
|
6
|
+
* H4 (second half): the five canon-*.js scripts (canon-build.js,
|
|
7
|
+
* canon-drift.js, canon-freeze.js, canon-freeze-status.js,
|
|
8
|
+
* canon-unfreeze.js) all use Node's built-in `util.parseArgs({strict:
|
|
9
|
+
* false})`. strict:false is there deliberately, so a bare positional
|
|
10
|
+
* argument (the entity id) isn't rejected — but it has a side effect: ANY
|
|
11
|
+
* unrecognized `--flag` is silently accepted too. A misspelled OPTIONAL
|
|
12
|
+
* flag (`--staus follow` instead of `--status follow`, `--jsn` instead of
|
|
13
|
+
* `--json`) produces no error and no warning — the option you meant to set
|
|
14
|
+
* just quietly keeps its default, and Node's parser stores the typo under
|
|
15
|
+
* ITS OWN key in `parsed.values`, never touching the one you meant.
|
|
16
|
+
*
|
|
17
|
+
* assertKnownOptions() closes that gap without giving up strict:false's
|
|
18
|
+
* positional leniency: after parsing, walk `parsed.values`'s keys — since
|
|
19
|
+
* every DECLARED option (default or explicit) and every UNDECLARED flag
|
|
20
|
+
* alike lands there — and reject any key outside the known option set.
|
|
21
|
+
* Mirrors lib/args.js's own UNKNOWN_FLAG shape (same code, same
|
|
22
|
+
* "Did you mean" hint via findClosest) so a typo on a canon-*.js flag reads
|
|
23
|
+
* identically to a typo anywhere else in the CLI.
|
|
24
|
+
*/
|
|
25
|
+
import { inputError } from '../lib/errors.js';
|
|
26
|
+
import { findClosest } from '../lib/args.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {Object} parsedValues — the `.values` object returned by
|
|
30
|
+
* node:util's parseArgs({strict:false, ...})
|
|
31
|
+
* @param {Object} optionsSpec — the SAME `options` object passed to parseArgs
|
|
32
|
+
* @param {{ command?: string }} [opts] — command label for the hint text
|
|
33
|
+
*/
|
|
34
|
+
export function assertKnownOptions(parsedValues, optionsSpec, opts = {}) {
|
|
35
|
+
const known = new Set(Object.keys(optionsSpec));
|
|
36
|
+
for (const key of Object.keys(parsedValues || {})) {
|
|
37
|
+
if (known.has(key)) continue;
|
|
38
|
+
const suggestion = findClosest(key, [...known]);
|
|
39
|
+
const hint = suggestion
|
|
40
|
+
? `Did you mean --${suggestion}? (Run "sdlab ${opts.command ? opts.command + ' ' : ''}--help" to see all supported flags.)`
|
|
41
|
+
: `Run "sdlab ${opts.command ? opts.command + ' ' : ''}--help" to see supported flags.`;
|
|
42
|
+
throw inputError('UNKNOWN_FLAG', `Unknown flag: --${key}`, hint);
|
|
43
|
+
}
|
|
44
|
+
}
|
package/scripts/brief-compile.js
CHANGED
|
@@ -14,6 +14,7 @@ import { parseArgs, getProjectName, parseNumberFlag } from '../lib/args.js';
|
|
|
14
14
|
import { getProjectRoot } from '../lib/paths.js';
|
|
15
15
|
import { compileBrief, saveCompiledBrief } from '../lib/brief-compiler.js';
|
|
16
16
|
import { info } from '../lib/log.js';
|
|
17
|
+
import { handleCliError } from '../lib/errors.js';
|
|
17
18
|
|
|
18
19
|
export async function run(argv = process.argv.slice(2)) {
|
|
19
20
|
const { flags } = parseArgs(argv, {
|
|
@@ -130,5 +131,5 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
if (process.argv[1] && (process.argv[1].endsWith('brief-compile.js') || process.argv[1].endsWith('brief-compile'))) {
|
|
133
|
-
run().catch(
|
|
134
|
+
run().catch(handleCliError);
|
|
134
135
|
}
|
package/scripts/brief-show.js
CHANGED
|
@@ -13,6 +13,7 @@ import { parseArgs, getProjectName } from '../lib/args.js';
|
|
|
13
13
|
import { getProjectRoot } from '../lib/paths.js';
|
|
14
14
|
import { loadCompiledBrief } from '../lib/brief-compiler.js';
|
|
15
15
|
import { renderBriefText, renderBriefMarkdown } from '../lib/brief-render.js';
|
|
16
|
+
import { handleCliError } from '../lib/errors.js';
|
|
16
17
|
|
|
17
18
|
export async function run(argv = process.argv.slice(2)) {
|
|
18
19
|
const { flags, positionals } = parseArgs(argv, {
|
|
@@ -49,5 +50,5 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
if (process.argv[1] && (process.argv[1].endsWith('brief-show.js') || process.argv[1].endsWith('brief-show'))) {
|
|
52
|
-
run().catch(
|
|
53
|
+
run().catch(handleCliError);
|
|
53
54
|
}
|
package/scripts/canon-bind.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'fs';
|
|
20
20
|
import { join } from 'path';
|
|
21
21
|
import { getProjectName } from '../lib/args.js';
|
|
22
|
-
import {
|
|
22
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
23
23
|
import { loadConstitution, loadLanes, loadRubric, loadTerminology, detectLane, detectGroup } from '../lib/config.js';
|
|
24
24
|
import { inputError, handleCliError } from '../lib/errors.js';
|
|
25
25
|
|
|
@@ -155,7 +155,7 @@ function generateAssertions(record, constitutionRules, lanesConfig, rubricConfig
|
|
|
155
155
|
|
|
156
156
|
export async function run(argv = process.argv.slice(2)) {
|
|
157
157
|
const projectName = getProjectName(argv);
|
|
158
|
-
const PROJECT_ROOT =
|
|
158
|
+
const PROJECT_ROOT = getProjectRoot(projectName);
|
|
159
159
|
const RECORDS_DIR = join(PROJECT_ROOT, 'records');
|
|
160
160
|
const DRY_RUN = argv.includes('--dry-run');
|
|
161
161
|
const STATS_ONLY = argv.includes('--stats');
|
package/scripts/canon-build.js
CHANGED
|
@@ -7,27 +7,35 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { parseArgs } from 'node:util';
|
|
10
|
-
import {
|
|
11
|
-
import { existsSync } from 'node:fs';
|
|
10
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
12
11
|
import { runBuild } from '../lib/canon-build/build.js';
|
|
13
12
|
import { inputError } from '../lib/errors.js';
|
|
13
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
14
14
|
|
|
15
15
|
export async function run(argv) {
|
|
16
|
+
const options = {
|
|
17
|
+
project: { type: 'string' },
|
|
18
|
+
game: { type: 'string' }, // deprecated alias
|
|
19
|
+
full: { type: 'boolean', default: false },
|
|
20
|
+
'no-cache': { type: 'boolean', default: false },
|
|
21
|
+
'dry-run': { type: 'boolean', default: false },
|
|
22
|
+
only: { type: 'string' },
|
|
23
|
+
json: { type: 'boolean', default: false },
|
|
24
|
+
quiet: { type: 'boolean', default: false },
|
|
25
|
+
};
|
|
16
26
|
const parsed = parseArgs({
|
|
17
27
|
args: argv,
|
|
18
|
-
options
|
|
19
|
-
project: { type: 'string' },
|
|
20
|
-
game: { type: 'string' }, // deprecated alias
|
|
21
|
-
full: { type: 'boolean', default: false },
|
|
22
|
-
'no-cache': { type: 'boolean', default: false },
|
|
23
|
-
'dry-run': { type: 'boolean', default: false },
|
|
24
|
-
only: { type: 'string' },
|
|
25
|
-
json: { type: 'boolean', default: false },
|
|
26
|
-
quiet: { type: 'boolean', default: false },
|
|
27
|
-
},
|
|
28
|
+
options,
|
|
28
29
|
allowPositionals: true,
|
|
29
30
|
strict: false,
|
|
30
31
|
});
|
|
32
|
+
// H4: strict:false is deliberate (it lets the entity-id positional through
|
|
33
|
+
// unrejected) but its side effect is that ANY unrecognized --flag is
|
|
34
|
+
// silently accepted too — a misspelled OPTIONAL flag (--ful instead of
|
|
35
|
+
// --full, --dry-Run) produces no error, no warning, and the option you
|
|
36
|
+
// meant to set just quietly keeps its default. Post-validate instead of
|
|
37
|
+
// trusting strict:false's leniency for everything.
|
|
38
|
+
assertKnownOptions(parsed.values, options, { command: 'canon build' });
|
|
31
39
|
|
|
32
40
|
const projectName = parsed.values.project || parsed.values.game;
|
|
33
41
|
if (!projectName) {
|
|
@@ -38,14 +46,13 @@ export async function run(argv) {
|
|
|
38
46
|
);
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver instead of
|
|
50
|
+
// join(process.cwd(), 'projects', projectName) — for a globally-installed
|
|
51
|
+
// sdlab the user is never inside the package directory, so process.cwd()
|
|
52
|
+
// silently pointed at the wrong place. getProjectRoot() also subsumes the
|
|
53
|
+
// existence check this used to do by hand (same INPUT_UNKNOWN_PROJECT shape
|
|
54
|
+
// as every other command, with an accurate "sdlab init" hint).
|
|
55
|
+
const projectRoot = getProjectRoot(projectName);
|
|
49
56
|
|
|
50
57
|
const only = parsed.values.only
|
|
51
58
|
? parsed.values.only.split(',').map((s) => s.trim()).filter(Boolean)
|
package/scripts/canon-drift.js
CHANGED
|
@@ -19,24 +19,33 @@ import { loadSchema } from '../lib/canon-build/load-schema.js';
|
|
|
19
19
|
import { readFreezeStatus, resolveWatchFields, computeWatchHash } from '../lib/freeze-stamp.js';
|
|
20
20
|
import { readEventsSince } from '../lib/freeze-events.js';
|
|
21
21
|
import { inputError } from '../lib/errors.js';
|
|
22
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
23
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
22
24
|
|
|
23
25
|
export async function run(argv) {
|
|
26
|
+
const options = {
|
|
27
|
+
project: { type: 'string' },
|
|
28
|
+
game: { type: 'string' },
|
|
29
|
+
since: { type: 'string' },
|
|
30
|
+
json: { type: 'boolean', default: false },
|
|
31
|
+
};
|
|
24
32
|
const parsed = parseArgs({
|
|
25
33
|
args: argv,
|
|
26
|
-
options
|
|
27
|
-
project: { type: 'string' },
|
|
28
|
-
game: { type: 'string' },
|
|
29
|
-
since: { type: 'string' },
|
|
30
|
-
json: { type: 'boolean', default: false },
|
|
31
|
-
},
|
|
34
|
+
options,
|
|
32
35
|
allowPositionals: true,
|
|
33
36
|
strict: false,
|
|
34
37
|
});
|
|
38
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
39
|
+
// flags means a typo'd optional flag (e.g. --sinse instead of --since)
|
|
40
|
+
// silently no-ops instead of erroring.
|
|
41
|
+
assertKnownOptions(parsed.values, options, { command: 'canon drift' });
|
|
35
42
|
|
|
36
43
|
const projectName = parsed.values.project || parsed.values.game;
|
|
37
44
|
if (!projectName) throw inputError('INPUT_MISSING_FLAG', '--project required');
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver, not
|
|
47
|
+
// process.cwd() (wrong base for a globally-installed sdlab).
|
|
48
|
+
const projectRoot = getProjectRoot(projectName);
|
|
40
49
|
const config = await loadBuildConfig(join(projectRoot, 'canon-build', 'config.json'), projectRoot);
|
|
41
50
|
|
|
42
51
|
// Load latest build manifest for the stamped watch-hashes
|
|
@@ -10,25 +10,33 @@ import { loadCanonEntriesInDir } from '../lib/canon-build/load-entry.js';
|
|
|
10
10
|
import { readFreezeStatus, resolveWatchFields } from '../lib/freeze-stamp.js';
|
|
11
11
|
import { readEventsFor } from '../lib/freeze-events.js';
|
|
12
12
|
import { inputError } from '../lib/errors.js';
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
13
15
|
|
|
14
16
|
export async function run(argv) {
|
|
17
|
+
const options = {
|
|
18
|
+
project: { type: 'string' },
|
|
19
|
+
game: { type: 'string' },
|
|
20
|
+
json: { type: 'boolean', default: false },
|
|
21
|
+
};
|
|
15
22
|
const parsed = parseArgs({
|
|
16
23
|
args: argv,
|
|
17
|
-
options
|
|
18
|
-
project: { type: 'string' },
|
|
19
|
-
game: { type: 'string' },
|
|
20
|
-
json: { type: 'boolean', default: false },
|
|
21
|
-
},
|
|
24
|
+
options,
|
|
22
25
|
allowPositionals: true,
|
|
23
26
|
strict: false,
|
|
24
27
|
});
|
|
28
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
29
|
+
// flags means a typo'd optional flag silently no-ops instead of erroring.
|
|
30
|
+
assertKnownOptions(parsed.values, options, { command: 'canon freeze-status' });
|
|
25
31
|
|
|
26
32
|
const entityIdArg = (parsed.positionals || [])[0];
|
|
27
33
|
if (!entityIdArg) throw inputError('INPUT_MISSING_ARG', 'sdlab canon freeze-status requires an entity id.');
|
|
28
34
|
const projectName = parsed.values.project || parsed.values.game;
|
|
29
35
|
if (!projectName) throw inputError('INPUT_MISSING_FLAG', '--project required');
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver, not
|
|
38
|
+
// process.cwd() (wrong base for a globally-installed sdlab).
|
|
39
|
+
const projectRoot = getProjectRoot(projectName);
|
|
32
40
|
const config = await loadBuildConfig(join(projectRoot, 'canon-build', 'config.json'), projectRoot);
|
|
33
41
|
|
|
34
42
|
let resolved = null;
|
package/scripts/canon-freeze.js
CHANGED
|
@@ -18,23 +18,33 @@ import { loadCanonEntriesInDir, entryId } from '../lib/canon-build/load-entry.js
|
|
|
18
18
|
import { buildFreezeBlock, FREEZE_STATUSES, resolveWatchFields } from '../lib/freeze-stamp.js';
|
|
19
19
|
import { appendEvent } from '../lib/freeze-events.js';
|
|
20
20
|
import { inputError } from '../lib/errors.js';
|
|
21
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
22
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
21
23
|
|
|
22
24
|
export async function run(argv) {
|
|
25
|
+
const options = {
|
|
26
|
+
project: { type: 'string' },
|
|
27
|
+
game: { type: 'string' },
|
|
28
|
+
reason: { type: 'string' },
|
|
29
|
+
status: { type: 'string', default: 'frozen' },
|
|
30
|
+
watch: { type: 'string' },
|
|
31
|
+
build: { type: 'string' },
|
|
32
|
+
by: { type: 'string', default: 'mike' },
|
|
33
|
+
json: { type: 'boolean', default: false },
|
|
34
|
+
};
|
|
23
35
|
const parsed = parseArgs({
|
|
24
36
|
args: argv,
|
|
25
|
-
options
|
|
26
|
-
project: { type: 'string' },
|
|
27
|
-
game: { type: 'string' },
|
|
28
|
-
reason: { type: 'string' },
|
|
29
|
-
status: { type: 'string', default: 'frozen' },
|
|
30
|
-
watch: { type: 'string' },
|
|
31
|
-
build: { type: 'string' },
|
|
32
|
-
by: { type: 'string', default: 'mike' },
|
|
33
|
-
json: { type: 'boolean', default: false },
|
|
34
|
-
},
|
|
37
|
+
options,
|
|
35
38
|
allowPositionals: true,
|
|
36
39
|
strict: false,
|
|
37
40
|
});
|
|
41
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
42
|
+
// flags means a typo'd optional flag silently no-ops instead of erroring.
|
|
43
|
+
// This one matters more than most: `sdlab canon freeze X --staus
|
|
44
|
+
// soft-advisory` (typo) would silently keep status at its default
|
|
45
|
+
// 'frozen' — a HARD block — instead of the 'soft-advisory' the operator
|
|
46
|
+
// typed, and a typo'd --watch/--build silently drops a real override too.
|
|
47
|
+
assertKnownOptions(parsed.values, options, { command: 'canon freeze' });
|
|
38
48
|
|
|
39
49
|
const positional = parsed.positionals || [];
|
|
40
50
|
const entityIdArg = positional[0];
|
|
@@ -72,7 +82,13 @@ export async function run(argv) {
|
|
|
72
82
|
);
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
|
|
85
|
+
// SDL-H12: was join(process.cwd(), 'projects', projectName) — wrong base for
|
|
86
|
+
// a globally-installed sdlab. getProjectRoot() resolves from the workspace
|
|
87
|
+
// root (not cwd) and throws a clear INPUT_UNKNOWN_PROJECT before ever
|
|
88
|
+
// reaching loadBuildConfig(), instead of a deeper, misleading
|
|
89
|
+
// CANON_BUILD_CONFIG_NOT_FOUND pointed at a config that "already exists" —
|
|
90
|
+
// just under a different (correct) project root.
|
|
91
|
+
const projectRoot = getProjectRoot(projectName);
|
|
76
92
|
const configPath = join(projectRoot, 'canon-build', 'config.json');
|
|
77
93
|
const config = await loadBuildConfig(configPath, projectRoot);
|
|
78
94
|
|
|
@@ -18,21 +18,27 @@ import { loadCanonEntriesInDir } from '../lib/canon-build/load-entry.js';
|
|
|
18
18
|
import { appendOverrideToFreezeBlock, readFreezeStatus } from '../lib/freeze-stamp.js';
|
|
19
19
|
import { appendEvent } from '../lib/freeze-events.js';
|
|
20
20
|
import { inputError } from '../lib/errors.js';
|
|
21
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
22
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
21
23
|
|
|
22
24
|
export async function run(argv) {
|
|
25
|
+
const options = {
|
|
26
|
+
project: { type: 'string' },
|
|
27
|
+
game: { type: 'string' },
|
|
28
|
+
reason: { type: 'string' },
|
|
29
|
+
by: { type: 'string', default: 'mike' },
|
|
30
|
+
build: { type: 'string' },
|
|
31
|
+
json: { type: 'boolean', default: false },
|
|
32
|
+
};
|
|
23
33
|
const parsed = parseArgs({
|
|
24
34
|
args: argv,
|
|
25
|
-
options
|
|
26
|
-
project: { type: 'string' },
|
|
27
|
-
game: { type: 'string' },
|
|
28
|
-
reason: { type: 'string' },
|
|
29
|
-
by: { type: 'string', default: 'mike' },
|
|
30
|
-
build: { type: 'string' },
|
|
31
|
-
json: { type: 'boolean', default: false },
|
|
32
|
-
},
|
|
35
|
+
options,
|
|
33
36
|
allowPositionals: true,
|
|
34
37
|
strict: false,
|
|
35
38
|
});
|
|
39
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
40
|
+
// flags means a typo'd optional flag silently no-ops instead of erroring.
|
|
41
|
+
assertKnownOptions(parsed.values, options, { command: 'canon unfreeze' });
|
|
36
42
|
|
|
37
43
|
const entityIdArg = (parsed.positionals || [])[0];
|
|
38
44
|
if (!entityIdArg) {
|
|
@@ -48,7 +54,9 @@ export async function run(argv) {
|
|
|
48
54
|
);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver, not
|
|
58
|
+
// process.cwd() (wrong base for a globally-installed sdlab).
|
|
59
|
+
const projectRoot = getProjectRoot(projectName);
|
|
52
60
|
const config = await loadBuildConfig(join(projectRoot, 'canon-build', 'config.json'), projectRoot);
|
|
53
61
|
const resolved = await findEntryFile(config, entityIdArg);
|
|
54
62
|
if (!resolved) {
|
package/scripts/card.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { writeFile } from 'node:fs/promises';
|
|
12
12
|
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
13
|
-
import {
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
14
|
import { inputError, handleCliError } from '../lib/errors.js';
|
|
15
15
|
import { listSnapshots } from '../lib/snapshot.js';
|
|
16
16
|
import { listSplits } from '../lib/split.js';
|
|
@@ -27,7 +27,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
const projectName = flags.project || getProjectName(argv);
|
|
30
|
-
const projectRoot =
|
|
30
|
+
const projectRoot = getProjectRoot(projectName);
|
|
31
31
|
const subcommand = positionals[0] || 'generate';
|
|
32
32
|
|
|
33
33
|
if (subcommand === 'generate') {
|
package/scripts/compare.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { writeFile, readdir, mkdir, readFile } from "node:fs/promises";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { parseArgs, getProjectName } from "../lib/args.js";
|
|
14
|
-
import {
|
|
14
|
+
import { getProjectRoot } from "../lib/paths.js";
|
|
15
15
|
import { readJsonFile } from "../lib/config.js";
|
|
16
16
|
import { inputError, handleCliError } from "../lib/errors.js";
|
|
17
17
|
|
|
@@ -25,7 +25,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
const projectName = flags.project || getProjectName(argv);
|
|
28
|
-
const GAME_ROOT =
|
|
28
|
+
const GAME_ROOT = getProjectRoot(projectName);
|
|
29
29
|
|
|
30
30
|
const [assetAId, assetBId, winner, ...reasonParts] = positionals;
|
|
31
31
|
|
package/scripts/critique.js
CHANGED
|
@@ -84,9 +84,24 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
84
84
|
const action = report.recommended_action;
|
|
85
85
|
console.log(`Critique saved to: runs/${flags.run}/critique.json`);
|
|
86
86
|
|
|
87
|
+
// H2: `refine_from_one` fires today purely from every candidate sitting
|
|
88
|
+
// at the unreviewed default overall_fit "usable" (nothing in the
|
|
89
|
+
// codebase currently sets it to anything else) — action.reviewed
|
|
90
|
+
// (lib/critique-engine.js recommendAction) distinguishes that from an
|
|
91
|
+
// actually-assessed run. Printing "Next: ..." with the same confidence
|
|
92
|
+
// either way presented brief-keyword drift tiebreaking as a considered
|
|
93
|
+
// recommendation. Qualify instead of suppressing outright — the pick is
|
|
94
|
+
// still a reasonable place to look first, it's just not a judgment.
|
|
87
95
|
if (action.mode === 'refine_from_one' && action.preferred_candidate) {
|
|
88
96
|
console.log('');
|
|
89
|
-
|
|
97
|
+
if (action.reviewed) {
|
|
98
|
+
info(`Next: sdlab refine --run ${flags.run} --pick ${action.preferred_candidate}`);
|
|
99
|
+
} else {
|
|
100
|
+
info(`No candidate has been reviewed yet — every image is at the rule-based pass's default "usable" ` +
|
|
101
|
+
`until a human or LLM judge assesses fit.`);
|
|
102
|
+
info(`If you want to proceed anyway: sdlab refine --run ${flags.run} --pick ${action.preferred_candidate} ` +
|
|
103
|
+
`(picked by fewest rule-based drift issues, not by looking at the image).`);
|
|
104
|
+
}
|
|
90
105
|
} else if (action.mode === 'accept_one' && action.preferred_candidate) {
|
|
91
106
|
console.log('');
|
|
92
107
|
info(`Preferred candidate: ${action.preferred_candidate}`);
|
package/scripts/curate.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { writeFile, rename, readdir, mkdir, readFile } from "node:fs/promises";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { parseArgs, getProjectName } from "../lib/args.js";
|
|
16
|
-
import {
|
|
16
|
+
import { getProjectRoot } from "../lib/paths.js";
|
|
17
17
|
import { readJsonFile } from "../lib/config.js";
|
|
18
18
|
import { inputError, runtimeError, handleCliError } from "../lib/errors.js";
|
|
19
19
|
import { result } from "../lib/log.js";
|
|
@@ -35,7 +35,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
const projectName = flags.project || getProjectName(argv);
|
|
38
|
-
const GAME_ROOT =
|
|
38
|
+
const GAME_ROOT = getProjectRoot(projectName);
|
|
39
39
|
const dryRun = flags['dry-run'] || flags.dryRun;
|
|
40
40
|
|
|
41
41
|
if (flags.list) {
|