rig-parts 1.65.0 → 1.67.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/README.md +6 -5
- package/cli.ts +43 -16
- package/docs/AUTHORING.md +125 -37
- package/docs/MIGRATION.md +30 -1
- package/docs/STABILITY.md +50 -1
- package/package.json +1 -1
- package/skills/rig-parts/SKILL.md +2 -2
- package/src/assemble.ts +17 -7
- package/src/check.ts +124 -3
- package/src/clothconfig.ts +3 -2
- package/src/config.ts +14 -4
- package/src/layers.ts +62 -0
- package/src/motionseampose.ts +117 -0
- package/src/motionseams.ts +91 -0
- package/src/parts.ts +11 -3
- package/src/propose.ts +43 -3
- package/src/rig.ts +20 -3
- package/tools/configwrite.ts +3 -3
package/README.md
CHANGED
|
@@ -272,7 +272,7 @@ Use the graph's node classes and the host's `/object_info` when checking the ada
|
|
|
272
272
|
```sh
|
|
273
273
|
rig-parts inputs --source painting.png --config config.json --out inputs # st_input_full.png; the config needs assemble.rig_scale
|
|
274
274
|
# See-through on st_input_full.png (external, or `rig-parts comfy seethrough`) -> layers/full
|
|
275
|
-
rig-parts layers layers/full #
|
|
275
|
+
rig-parts layers layers/full # drawn(a>8)/opaque(a>=250) boxes, CORE body-group height in input-canvas px, WARNs
|
|
276
276
|
rig-parts propose --head-box --full layers/full --canvas 1664x2432
|
|
277
277
|
# -> seethrough.head_box into config.json
|
|
278
278
|
rig-parts inputs --source painting.png --config config.json --out inputs # now st_input_head.png too
|
|
@@ -287,11 +287,12 @@ rig-parts assemble --source painting.png --full layers/full --head layers/head -
|
|
|
287
287
|
# -> a large red hole neither run holds? add an assemble.patches entry (cut from the painting) and assemble again
|
|
288
288
|
rig-parts propose --parts work/rig --source painting.png --out work
|
|
289
289
|
# -> proposal.json, basis.json (what each bone rests on) and render/landmarks.png; per-bone coverage lines;
|
|
290
|
-
# correct it, copy bones/meshes/regions/limbs/motion/constraints/turn/proposed_rig_scale into config.json, each it wrote
|
|
290
|
+
# correct it, copy bones/meshes/regions/limbs/motion/constraints/turn/motion_seams/proposed_rig_scale into config.json, each it wrote
|
|
291
291
|
# (limbs only with --keypoints, constraints only with --cloth, turn only with --full/--head;
|
|
292
292
|
# a limbs block left out is refused next)
|
|
293
293
|
rig-parts propose --parts work/rig --source painting.png --out work --from-config config.json
|
|
294
|
-
# -> LINT lines; exit 1 while any remain
|
|
294
|
+
# -> LINT lines; exit 1 while any remain (takes --parts, --source, --out, --from-config and optional --keypoints [--person] only;
|
|
295
|
+
# proposes nothing, so --full, --head, --cloth, --write-config, --wrist-snap, --grid-rule, --only-missing and --compare are refused)
|
|
295
296
|
rig-parts build --config config.json --source painting.png --full layers/full --head layers/head --out out --loop
|
|
296
297
|
# -> read out/check/check.json; every FAIL line names what has to change
|
|
297
298
|
```
|
|
@@ -333,7 +334,7 @@ agent skill.
|
|
|
333
334
|
| `inputs --source <png> --config <json> --out <dir>` | the two images See-through is fed: the painting on a white square, and the head box's crop once the config has one |
|
|
334
335
|
| `comfy paint --config --out [--host]` | optional: generate the painting on a ComfyUI box from the config's `generation` block |
|
|
335
336
|
| `comfy seethrough --image --out [--host]` | optional: run the ComfyUI See-through wrapper on one image and write the form `layers` reads |
|
|
336
|
-
| `layers <dir \| layers.json \| file.psd>` |
|
|
337
|
+
| `layers <dir \| layers.json \| file.psd>` | keep the existing columns (`opaque_px` still means alpha >8) and append source-alpha boxes `drawn(a>8)` / `opaque(a>=250)` with exclusive right/bottom; name absent boxes, report the existing body-group union height at **CORE_ALPHA (>=250)** in **input-canvas px**, and show its >8 union separately; no anatomical height, ownership/erosion or painting registration is inferred ([units and rig-scale guidance](docs/AUTHORING.md#4-the-command-order)) |
|
|
337
338
|
| `sheet --source <png> --layers <path>… --out <png>` | a labelled contact sheet of the painting and every layer or part |
|
|
338
339
|
| `assemble --propose-plan … [--write-config <config>]` | propose `assemble.plan` and `extend_below_crop` from the two runs, leaving out an implausible layer with a note naming the rule, naming in a note any full-run body no planned part covers, and naming every layer no row takes with why (empty, taken from the other run only, a plausibility rule; `nose`, never planned, with the row that lets `propose` read the head's pitch) |
|
|
339
340
|
| `assemble --source --full --head --config --out [--seam] [--project]` | merge the two runs into rig-space parts, `parts.json`, the recomposite and its error map, and list the uncovered holes |
|
|
@@ -341,7 +342,7 @@ agent skill.
|
|
|
341
342
|
| `propose --parts --source --out [--compare <config>] [--write-config <config> [--only-missing]]` | propose bones, meshes, regions and an idle; draw the overlay; print, per bone, which check read it or that none did and why (its role, its kind of segment), and write `basis.json` beside the proposal — what each bone rests on (a joint, a part's mask, a ratio, other bones) and, with `--compare`, whether the config's bone differs |
|
|
342
343
|
| `propose … --keypoints <file> [--person <id>]` | read a posed figure's joints from one explicit file (space, image size, people, each joint observed, occluded or missing, its source): a joint with a position places the neck, hip, chest or a sleeve chain as given, a note says how every joint was used or which rule stood in, and LINT reads the torso and sleeves along the joints instead of by screen y ([AUTHORING §3](docs/AUTHORING.md)) |
|
|
343
344
|
| `propose … --grid-rule figure\|part` | how each drafted lattice's spacing is chosen: `figure` (the default) one spacing for the figure, round(H / 37) rig px, a part already coarser keeping its own, with a note on its calibration and cost; `part` the reference's rule, a tenth of the part's short side (the oracle examples are proposed under it) |
|
|
344
|
-
| `propose
|
|
345
|
+
| `propose --parts --source --out --from-config <config> [--keypoints <file> [--person <id>]]` | draw and LINT the config's current bones (by the joints too, given `--keypoints`); proposes nothing, so takes only these flags (not `--full`, `--head`, `--cloth`, `--write-config`, `--wrist-snap`, `--grid-rule`, `--only-missing` or `--compare`) |
|
|
345
346
|
| `compare --left <file> --right <file> [--map <bonemap.json>]` | compare two skeletons — a config, a proposal or a `rig.json` — bone by bone: origin, parent, tip, length and direction per pair, the bones neither side pairs, the required ones missing, and each side's roles read off the spec; no frame is guessed, and nothing pairs by resemblance (`propose --compare` reads origins only) |
|
|
346
347
|
| `rig --config --parts --out [--idle-keys ctl\|direct]` | author `rig.json` + `motion.json`, written only after rig-c's gate is green; each mesh is in the mode its config entry names — `grid` (the lattice, what `propose` writes), `contour` (the traced outline with declared interior points) or `auto` (the contour mesh at alpha 1 and above, reduced and locally refined by rig-c's `reduceMesh` under the author's declared bounds, written only when its motion against the unreduced source on the idle passes the author's `motion` bounds through rig-c's `compareMeshesInMotion`; [AUTHORING §3](docs/AUTHORING.md)); every chain link is turned along its chain and carries its `length`, so a physics constraint added later has a lever, with every offset under it in the turned frame so nothing moves; `--idle-keys` says whether the idle's keys on mesh-driving bones go through `<bone>_ctl` parents (default) or stay on the bones with `invariants.idleDrivesMeshes` declared; the config's `constraints` go to rigc as written, and a two-bone ik over keyed links needs `--idle-keys direct` (on `rig` or `build`) |
|
|
347
348
|
| `check --rig --out [--parts] [--source] [--requirements]` | build packed, gated under `rig-play`, render the idle, measure seam, loop and the six judgement lines (mesh texture stretch among them, from the idle's `render --geometry`), and report the recomposite's holes from `parts.json`; on a rig spec with no `parts.json` or no `idle` (a merged rig, issue #77) the gate still runs and every line that needs the missing input says SKIP by name; `--source <painting.png>` adds `SETUP_POSE_VS_SOURCE`, the setup pose against the painting by assemble's recomposite figures; `--requirements <scene.json>` measures what a scene declares of the motion — a contact, a follow fraction, an aim, a joint range, a mesh's stretch, the seam between two parts over the motion — each against the author's own bar, from rig-c's posed frames, one line each, PASS, FAIL or NOT MEASURABLE (AUTHORING §7, *Declared requirements*) |
|
package/cli.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { GUIDE_DOC, type GuideAnswer, guideRule, guideSection } from './src/guid
|
|
|
28
28
|
import { proposeHeadBox } from './src/headbox.ts';
|
|
29
29
|
import { makeInputs } from './src/inputs.ts';
|
|
30
30
|
import { idleKeysOf, isIdleKeys } from './src/rig.ts';
|
|
31
|
-
import { depthSkipLine, exportLayers, figuresPhrase, hiddenLayerLine, implausibleRules, layerFigures, type LayerSet, layerSetDiff, pct, readLayers, readWrapperLayers, ruleSummary, templateLayerNames, templatePsdBytes, times } from './src/layers.ts';
|
|
31
|
+
import { bodyCoreBox, bodyOpaqueBox, CORE_ALPHA, depthSkipLine, exportLayers, figuresPhrase, hiddenLayerLine, implausibleRules, layerCoreBox, layerFigures, layerOpaqueBox, type LayerSet, layerSetDiff, type OpaqueBox, OPAQUE_ALPHA_ABOVE, pct, readLayers, readWrapperLayers, ruleSummary, templateLayerNames, templatePsdBytes, times } from './src/layers.ts';
|
|
32
32
|
import { checkImageSize, choosePerson, KEYPOINTS_SPEC, loadKeypoints, type RigJoints, toRigJoints } from './src/keypoints.ts';
|
|
33
33
|
import { basisLines, branchSqueezeLine, checkProposal, compare, compareLines, DEFAULT_GRID_RULE, draftTurn, drawLandmarks, FIGURE_GRID_DIVISOR, FRINGE_STEP_PX, GRID_RULES, type GridRule, HIP_MIN_FRACTION, type DraftLintFinding, lint, lintLine, type PartSet, PROPOSAL_CONFIG_SECTIONS, proposalAsConfig, proposeWithBasis, readPartSet, serializeProposal, withTurn, WRIST_ON_PART_PX } from './src/propose.ts';
|
|
34
34
|
import { BASIS_FILE, BASIS_SPEC, basisFile, basisLine, coverageLines, legChainChoice, serializeBasis } from './src/diagnostics.ts';
|
|
@@ -79,6 +79,11 @@ usage:
|
|
|
79
79
|
where something covers it: listed apart, never drawn, never a part. An
|
|
80
80
|
input with no depth (a PSD, or depth_median null on every layer, whose
|
|
81
81
|
manifest order is then the draw order) prints one SKIP depth line.
|
|
82
|
+
Trailing boxes name both source-alpha cuts: drawn(a>8), and opaque
|
|
83
|
+
(a>=250, CORE_ALPHA). The legacy opaque_px still counts alpha above 8.
|
|
84
|
+
Right/bottom are exclusive, in INPUT-CANVAS px; absent boxes are named.
|
|
85
|
+
The body-group union height uses alpha >=250, with the >8 union shown
|
|
86
|
+
separately: no cleanup, ownership, anatomical height or painting map.
|
|
82
87
|
Refuses, by name, a missing file, an unknown tag, a PNG whose
|
|
83
88
|
size is not its box, and anything else outside the input contract —
|
|
84
89
|
never a WARN.
|
|
@@ -268,9 +273,10 @@ usage:
|
|
|
268
273
|
|
|
269
274
|
rig-parts propose --parts <dir> --source <painting.png> --out <dir> --from-config <config.json>
|
|
270
275
|
Draw the config's CURRENT bones instead (<out>/render/landmarks_config.png
|
|
271
|
-
and _head) and LINT them (by the joints too, given --keypoints), with the
|
|
272
|
-
coverage lines after them; no ${BASIS_FILE}, as nothing is proposed
|
|
273
|
-
|
|
276
|
+
and _head) and LINT them (by the joints too, given --keypoints [--person]), with the
|
|
277
|
+
coverage lines after them; no ${BASIS_FILE}, as nothing is proposed (takes
|
|
278
|
+
--parts, --source, --out, --from-config and optional --keypoints [--person]
|
|
279
|
+
only). Exits 1 when any LINT line is printed.
|
|
274
280
|
|
|
275
281
|
rig-parts propose … --write-config <config.json>
|
|
276
282
|
Without --cloth, update an existing config with the whole proposal's
|
|
@@ -416,7 +422,8 @@ usage:
|
|
|
416
422
|
rig-parts mesh-stats --config <config.json> --parts <dir>
|
|
417
423
|
Print the vertices and triangles of every mesh the config states, as the
|
|
418
424
|
rig stage would make them from the parts in --parts (the directory
|
|
419
|
-
holding parts.json and parts
|
|
425
|
+
holding parts.json and parts/ — work/rig from assemble --out work, or
|
|
426
|
+
out from build --out out), and their total, in seconds:
|
|
420
427
|
the rig stage's own mesh construction, without rigc, without assemble
|
|
421
428
|
and without check (issue #566). One line per mesh in parts.json's order,
|
|
422
429
|
"mesh-stats: <part> — <mode> <V> vertices, <T> triangles" (mode lattice,
|
|
@@ -993,10 +1000,29 @@ function printLayerTable(set: LayerSet): void {
|
|
|
993
1000
|
const head = ['order', 'name', 'group', 'left,top', 'right,bottom', 'size', 'opaque_px', 'depth', 'translucent', 'background', 'area'];
|
|
994
1001
|
const widths = head.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
995
1002
|
const line = (cells: string[]): string => ` ${cells.map((c, i) => c.padEnd(widths[i])).join(' ')}`.trimEnd();
|
|
996
|
-
|
|
997
|
-
|
|
1003
|
+
// Keep every legacy line byte-for-byte as a prefix; size the trailing fields separately.
|
|
1004
|
+
const legacy = [line(head), ...rows.map(line)];
|
|
1005
|
+
const legacyWidth = Math.max(...legacy.map(l => l.length));
|
|
1006
|
+
const drawn = `drawn(a>${OPAQUE_ALPHA_ABOVE})`;
|
|
1007
|
+
const opaque = `opaque(a>=${CORE_ALPHA})`;
|
|
1008
|
+
const boxes = set.layers.map(l => [layerOpaqueBox(l), layerCoreBox(l)] as const);
|
|
1009
|
+
const opaqueHead = [drawn, opaque].flatMap(label => [`${label}_left,top`, `${label}_right,bottom`, `${label}_size`]);
|
|
1010
|
+
const opaqueRows = boxes.map(pair => pair.flatMap(b => b === null ? ['none', 'none', 'none'] : [`${b.left},${b.top}`, `${b.right},${b.bottom}`, `${b.right - b.left}x${b.bottom - b.top}`]));
|
|
1011
|
+
const opaqueWidths = opaqueHead.map((h, i) => Math.max(h.length, ...opaqueRows.map(r => r[i].length)));
|
|
1012
|
+
for (const [i, cells] of [opaqueHead, ...opaqueRows].entries()) {
|
|
1013
|
+
console.log(`${legacy[i].padEnd(legacyWidth)} ${cells.map((c, j) => c.padEnd(opaqueWidths[j])).join(' ')}`.trimEnd());
|
|
1014
|
+
}
|
|
998
1015
|
const painted = set.layers.filter((l) => l.opaquePx > 0).length;
|
|
999
1016
|
console.log(` ${set.layers.length} layer(s): ${painted} with opaque pixels, ${set.layers.length - painted} with none`);
|
|
1017
|
+
console.log(` source-alpha boxes: ${drawn}, ${opaque}; legacy opaque_px remains alpha > ${OPAQUE_ALPHA_ABOVE}; INPUT-CANVAS px; right/bottom exclusive`);
|
|
1018
|
+
for (const [i, [drawnBox, coreBox]] of boxes.entries()) {
|
|
1019
|
+
if (drawnBox === null) console.log(` layer "${set.layers[i].name}": no ${drawn} box (no pixels with alpha > ${OPAQUE_ALPHA_ABOVE})`);
|
|
1020
|
+
if (coreBox === null) console.log(` layer "${set.layers[i].name}": no ${opaque} box (no pixels with alpha >= ${CORE_ALPHA})${drawnBox === null ? '' : `; ${drawn} box is present`}`);
|
|
1021
|
+
}
|
|
1022
|
+
const bodyBounds = (body: OpaqueBox | null): string => body === null ? 'none; height unavailable INPUT-CANVAS px' : `${body.left},${body.top} to ${body.right},${body.bottom}; size ${body.right - body.left}x${body.bottom - body.top}; height ${body.bottom - body.top} INPUT-CANVAS px`;
|
|
1023
|
+
console.log(` body-group union [${opaque}]: ${bodyBounds(bodyCoreBox(set))}; source alpha >= ${CORE_ALPHA} only, not an owned/eroded core or anatomical height`);
|
|
1024
|
+
console.log(` body-group union [${drawn}]: ${bodyBounds(bodyOpaqueBox(set))}; source alpha > ${OPAQUE_ALPHA_ABOVE} only; not an anatomical height`);
|
|
1025
|
+
console.log(' body-group population: drawn group=body (includes hair/head/wings/objects; excludes face/headwear, author and hidden art); before cleanup, resampling or painting registration');
|
|
1000
1026
|
// Issue #256: a layer the author made is said to be one, so it never reads as See-through's. None, no line.
|
|
1001
1027
|
for (const l of set.layers) if (l.tag.group === 'author') console.log(` ${authorLayerLine(l.name)}`);
|
|
1002
1028
|
// Issue #359: a hidden-art layer is listed apart, never in the drawn table. None, no line.
|
|
@@ -1351,11 +1377,12 @@ function cmdPropose(args: string[]): number {
|
|
|
1351
1377
|
}
|
|
1352
1378
|
// Issue #671: --only-missing adds entries to an existing config for the parts it attaches by neither section; it
|
|
1353
1379
|
// belongs to the ordinary proposal's --write-config alone.
|
|
1380
|
+
const fromConfigTakes = '(takes --parts, --source, --out, --from-config and optional --keypoints [--person] only)';
|
|
1354
1381
|
if (onlyMissing) {
|
|
1355
1382
|
const mode = 'propose --parts … --write-config <config.json> --only-missing';
|
|
1356
1383
|
if (headBox) return usage(`--only-missing belongs to ${mode}; --head-box writes seethrough.head_box only`);
|
|
1357
1384
|
if (!flags.has('--parts')) return usage(`--only-missing belongs to ${mode}; give --parts <dir>, the parts the entries are drafted from`);
|
|
1358
|
-
if (flags.has('--from-config')) return usage(`--only-missing belongs to ${mode}; --from-config proposes nothing`);
|
|
1385
|
+
if (flags.has('--from-config')) return usage(`--only-missing belongs to ${mode}; --from-config proposes nothing ${fromConfigTakes}`);
|
|
1359
1386
|
if (flags.has('--cloth')) return usage(`--only-missing belongs to ${mode}; --cloth writes a new config with the recipe's entries spliced in`);
|
|
1360
1387
|
if (!flags.has('--write-config')) return usage(`--only-missing belongs to ${mode}; give --write-config <config.json>, the existing config the entries are added to`);
|
|
1361
1388
|
}
|
|
@@ -1400,17 +1427,17 @@ function cmdPropose(args: string[]): number {
|
|
|
1400
1427
|
if (partsArg === undefined) return usage('propose needs --parts <dir>, the directory holding parts.json and parts/');
|
|
1401
1428
|
if (source === undefined) return usage('propose needs --source <painting.png>, drawn under the overlay');
|
|
1402
1429
|
if (out === undefined) return usage('propose needs --out <dir>');
|
|
1403
|
-
if (flags.has('--from-config') && flags.has('--compare')) return usage(
|
|
1430
|
+
if (flags.has('--from-config') && flags.has('--compare')) return usage(`--from-config and --compare are two modes; give one (--from-config ${fromConfigTakes.slice(1, -1)})`);
|
|
1404
1431
|
if (flags.has('--person') && !flags.has('--keypoints')) return usage('--person names a person in the --keypoints file; give --keypoints <file>');
|
|
1405
1432
|
// Issue #251: the See-through runs the turn control's depths are read from; without them nothing is drafted.
|
|
1406
|
-
if (flags.has('--from-config') && (flags.has('--full') || flags.has('--head'))) return usage(
|
|
1433
|
+
if (flags.has('--from-config') && (flags.has('--full') || flags.has('--head'))) return usage(`--full and --head draft the turn control while proposing; --from-config proposes nothing ${fromConfigTakes}`);
|
|
1407
1434
|
// Issue #199: a declared tolerance for a wrist on no handwear part. Whole px above the built-in one: the distance is
|
|
1408
1435
|
// counted in whole px, so a fraction would bind what its floor binds, and a value at or under the built-in binds nothing.
|
|
1409
1436
|
const snapArg = flags.get('--wrist-snap');
|
|
1410
1437
|
let wristSnap: number | null = null;
|
|
1411
1438
|
if (snapArg !== undefined) {
|
|
1412
1439
|
if (!flags.has('--keypoints')) return usage('--wrist-snap binds a wrist joint to a handwear part; give --keypoints <file>');
|
|
1413
|
-
if (flags.has('--from-config')) return usage(
|
|
1440
|
+
if (flags.has('--from-config')) return usage(`--wrist-snap binds a wrist while proposing; --from-config proposes nothing ${fromConfigTakes}`);
|
|
1414
1441
|
if (!/^[0-9]+$/.test(snapArg) || Number(snapArg) <= WRIST_ON_PART_PX) {
|
|
1415
1442
|
return usage(`--wrist-snap ${snapArg} is not an integer above ${WRIST_ON_PART_PX}, the px within which a wrist already holds a part`);
|
|
1416
1443
|
}
|
|
@@ -1418,15 +1445,15 @@ function cmdPropose(args: string[]): number {
|
|
|
1418
1445
|
}
|
|
1419
1446
|
// Issue #265: the drafted lattice's spacing; the figure's unless the reference's per-part rule is asked for.
|
|
1420
1447
|
const ruleArg = flags.get('--grid-rule');
|
|
1421
|
-
if (ruleArg !== undefined && flags.has('--from-config')) return usage(
|
|
1448
|
+
if (ruleArg !== undefined && flags.has('--from-config')) return usage(`--grid-rule chooses the drafted spacing; --from-config proposes nothing ${fromConfigTakes}`);
|
|
1422
1449
|
if (ruleArg !== undefined && !(GRID_RULES as readonly string[]).includes(ruleArg)) return usage(`--grid-rule ${ruleArg}; one of ${GRID_RULES.join(', ')} is required`);
|
|
1423
1450
|
const gridRule = (ruleArg ?? DEFAULT_GRID_RULE) as GridRule;
|
|
1424
1451
|
// Issue #364: the cloth and hair recipe, read from a config's cloth block; without it nothing changes.
|
|
1425
1452
|
const clothArg = flags.get('--cloth');
|
|
1426
|
-
if (clothArg !== undefined && flags.has('--from-config')) return usage(
|
|
1453
|
+
if (clothArg !== undefined && flags.has('--from-config')) return usage(`--cloth drafts trees while proposing; --from-config proposes nothing ${fromConfigTakes}`);
|
|
1427
1454
|
// Issue #537: the --cloth config's own text with what its recipe drafts spliced in, beside proposal.json.
|
|
1428
1455
|
const writeArg = flags.get('--write-config');
|
|
1429
|
-
if (writeArg !== undefined && flags.has('--from-config')) return usage(
|
|
1456
|
+
if (writeArg !== undefined && flags.has('--from-config')) return usage(`--write-config writes what the recipe drafts into its config; --from-config proposes nothing ${fromConfigTakes}`);
|
|
1430
1457
|
// Issue #571: the config the recipe is changed from; an entry equal to its recipe's draft is the recipe's, not a hand edit.
|
|
1431
1458
|
const fromArg = flags.get('--from');
|
|
1432
1459
|
if (fromArg !== undefined && writeArg === undefined) return usage('--from names the config the recipe is changed from, for --write-config; give --write-config <out.json>');
|
|
@@ -1434,7 +1461,7 @@ function cmdPropose(args: string[]): number {
|
|
|
1434
1461
|
if (writeArg !== undefined && clothArg !== undefined) {
|
|
1435
1462
|
const ours = ['proposal.json', BASIS_FILE, 'render/landmarks.png', 'render/landmarks_head.png'].map((f) => resolve(out, f));
|
|
1436
1463
|
if (existsSync(writeArg) || ours.includes(resolve(writeArg))) {
|
|
1437
|
-
throw new PartsError([{ code: 'PROPOSE_WRITE_CONFIG_FRESH', object: writeArg, detail: existsSync(writeArg) ? 'exists; the config is written to a new file, never over the --cloth config or an earlier one' : `is a file this run writes under ${out}; name another file` }]);
|
|
1464
|
+
throw new PartsError([{ code: 'PROPOSE_WRITE_CONFIG_FRESH', object: writeArg, detail: existsSync(writeArg) ? 'exists; the config is written to a new file, never over the --cloth config or an earlier one (unlike assemble --propose-plan and propose without --cloth, which update an existing --config in place)' : `is a file this run writes under ${out}; name another file` }]);
|
|
1438
1465
|
}
|
|
1439
1466
|
separateConfigWriteTarget({ path: resolve(writeArg) }, ours, 'PROPOSE_WRITE_CONFIG_FRESH');
|
|
1440
1467
|
}
|
|
@@ -1642,7 +1669,7 @@ function cmdAssemble(args: string[]): number {
|
|
|
1642
1669
|
const [source, full, config] = ['--source', '--full', '--config'].map((f) => flags.get(f) as string);
|
|
1643
1670
|
const head = flags.get('--head') ?? null;
|
|
1644
1671
|
const writePath = flags.get('--write-config');
|
|
1645
|
-
if (writePath !== undefined && resolve(writePath) !== resolve(config)) return usage('--write-config must name the same file as --config for assemble --propose-plan');
|
|
1672
|
+
if (writePath !== undefined && resolve(writePath) !== resolve(config)) return usage('--write-config must name the same file as --config for assemble --propose-plan (assemble --propose-plan updates --config in place; only propose --cloth --write-config writes to a new file)');
|
|
1646
1673
|
let staged: StagedConfigWrite | null = null;
|
|
1647
1674
|
try {
|
|
1648
1675
|
if (propose) {
|