@roster-lock/config-editor-cli 0.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.
- package/.claude-plugin/plugin.json +6 -0
- package/LICENSE.md +11 -0
- package/README.md +40 -0
- package/dist/commands/draft/from-lock.js +31 -0
- package/dist/commands/draft/from-lock.js.map +1 -0
- package/dist/commands/draft/index.js +22 -0
- package/dist/commands/draft/index.js.map +1 -0
- package/dist/commands/draft/init.js +25 -0
- package/dist/commands/draft/init.js.map +1 -0
- package/dist/commands/draft/promote.js +24 -0
- package/dist/commands/draft/promote.js.map +1 -0
- package/dist/commands/draft/publish.js +26 -0
- package/dist/commands/draft/publish.js.map +1 -0
- package/dist/commands/draft/validate.js +30 -0
- package/dist/commands/draft/validate.js.map +1 -0
- package/dist/commands/engine/add-asset.js +44 -0
- package/dist/commands/engine/add-asset.js.map +1 -0
- package/dist/commands/engine/add-piece-type.js +47 -0
- package/dist/commands/engine/add-piece-type.js.map +1 -0
- package/dist/commands/engine/edit-definition.js +49 -0
- package/dist/commands/engine/edit-definition.js.map +1 -0
- package/dist/commands/engine/index.js +24 -0
- package/dist/commands/engine/index.js.map +1 -0
- package/dist/commands/engine/remove-asset.js +27 -0
- package/dist/commands/engine/remove-asset.js.map +1 -0
- package/dist/commands/engine/remove-piece-type.js +28 -0
- package/dist/commands/engine/remove-piece-type.js.map +1 -0
- package/dist/commands/engine/set-info.js +22 -0
- package/dist/commands/engine/set-info.js.map +1 -0
- package/dist/commands/engine/show.js +23 -0
- package/dist/commands/engine/show.js.map +1 -0
- package/dist/commands/piece-meta/index.js +19 -0
- package/dist/commands/piece-meta/index.js.map +1 -0
- package/dist/commands/piece-meta/set.js +27 -0
- package/dist/commands/piece-meta/set.js.map +1 -0
- package/dist/commands/piece-meta/show.js +15 -0
- package/dist/commands/piece-meta/show.js.map +1 -0
- package/dist/commands/roster/add-piece.js +93 -0
- package/dist/commands/roster/add-piece.js.map +1 -0
- package/dist/commands/roster/edit-piece.js +92 -0
- package/dist/commands/roster/edit-piece.js.map +1 -0
- package/dist/commands/roster/import-piece.js +83 -0
- package/dist/commands/roster/import-piece.js.map +1 -0
- package/dist/commands/roster/index.js +24 -0
- package/dist/commands/roster/index.js.map +1 -0
- package/dist/commands/roster/list.js +24 -0
- package/dist/commands/roster/list.js.map +1 -0
- package/dist/commands/roster/remove-piece.js +30 -0
- package/dist/commands/roster/remove-piece.js.map +1 -0
- package/dist/commands/roster/rescan.js +55 -0
- package/dist/commands/roster/rescan.js.map +1 -0
- package/dist/commands/roster/test-download.js +72 -0
- package/dist/commands/roster/test-download.js.map +1 -0
- package/dist/commands/selection/add-script.js +43 -0
- package/dist/commands/selection/add-script.js.map +1 -0
- package/dist/commands/selection/index.js +21 -0
- package/dist/commands/selection/index.js.map +1 -0
- package/dist/commands/selection/run-script.js +27 -0
- package/dist/commands/selection/run-script.js.map +1 -0
- package/dist/commands/selection/set.js +41 -0
- package/dist/commands/selection/set.js.map +1 -0
- package/dist/commands/selection/show.js +15 -0
- package/dist/commands/selection/show.js.map +1 -0
- package/dist/commands/show.js +15 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/validate.js +31 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cli-options.js +7 -0
- package/dist/lib/cli-options.js.map +1 -0
- package/dist/lib/draft-io.js +38 -0
- package/dist/lib/draft-io.js.map +1 -0
- package/dist/lib/errors.js +32 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/fs-walk.js +28 -0
- package/dist/lib/fs-walk.js.map +1 -0
- package/dist/lib/json-input.js +16 -0
- package/dist/lib/json-input.js.map +1 -0
- package/dist/lib/lock-io.js +20 -0
- package/dist/lib/lock-io.js.map +1 -0
- package/dist/lib/parse.js +52 -0
- package/dist/lib/parse.js.map +1 -0
- package/dist/lib/piece-scan.js +12 -0
- package/dist/lib/piece-scan.js.map +1 -0
- package/dist/lib/schema-help.js +38 -0
- package/dist/lib/schema-help.js.map +1 -0
- package/dist/lib/schemas.js +71 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/semver-report.js +13 -0
- package/dist/lib/semver-report.js.map +1 -0
- package/dist/lib/slugify.js +13 -0
- package/dist/lib/slugify.js.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -0
- package/scripts/bundle.js +15 -0
- package/scripts/generate-version.js +14 -0
- package/skills/rosterlock-config-editor/SKILL.md +151 -0
- package/src/commands/draft/from-lock.ts +34 -0
- package/src/commands/draft/index.ts +5 -0
- package/src/commands/draft/init.ts +21 -0
- package/src/commands/draft/promote.ts +24 -0
- package/src/commands/draft/publish.ts +26 -0
- package/src/commands/draft/validate.ts +30 -0
- package/src/commands/engine/add-asset.ts +46 -0
- package/src/commands/engine/add-piece-type.ts +56 -0
- package/src/commands/engine/edit-definition.ts +58 -0
- package/src/commands/engine/index.ts +7 -0
- package/src/commands/engine/remove-asset.ts +25 -0
- package/src/commands/engine/remove-piece-type.ts +27 -0
- package/src/commands/engine/set-info.ts +17 -0
- package/src/commands/engine/show.ts +20 -0
- package/src/commands/piece-meta/index.ts +2 -0
- package/src/commands/piece-meta/set.ts +29 -0
- package/src/commands/piece-meta/show.ts +12 -0
- package/src/commands/roster/add-piece.ts +109 -0
- package/src/commands/roster/edit-piece.ts +99 -0
- package/src/commands/roster/import-piece.ts +112 -0
- package/src/commands/roster/index.ts +7 -0
- package/src/commands/roster/list.ts +23 -0
- package/src/commands/roster/remove-piece.ts +28 -0
- package/src/commands/roster/rescan.ts +63 -0
- package/src/commands/roster/test-download.ts +76 -0
- package/src/commands/selection/add-script.ts +44 -0
- package/src/commands/selection/index.ts +4 -0
- package/src/commands/selection/run-script.ts +30 -0
- package/src/commands/selection/set.ts +54 -0
- package/src/commands/selection/show.ts +12 -0
- package/src/commands/show.ts +12 -0
- package/src/commands/validate.ts +26 -0
- package/src/index.ts +85 -0
- package/src/lib/cli-options.ts +8 -0
- package/src/lib/draft-io.ts +41 -0
- package/src/lib/errors.ts +30 -0
- package/src/lib/fs-walk.ts +31 -0
- package/src/lib/json-input.ts +21 -0
- package/src/lib/lock-io.ts +18 -0
- package/src/lib/parse.ts +42 -0
- package/src/lib/piece-scan.ts +19 -0
- package/src/lib/schema-help.ts +44 -0
- package/src/lib/schemas.ts +100 -0
- package/src/lib/semver-report.ts +11 -0
- package/src/lib/slugify.ts +9 -0
- package/test.rosterlock.draft.json +62 -0
- package/tests/commands/add-piece.spec.ts +92 -0
- package/tests/commands/draft-validate.spec.ts +85 -0
- package/tests/commands/edit-definition.spec.ts +94 -0
- package/tests/commands/edit-piece.spec.ts +153 -0
- package/tests/commands/from-lock.spec.ts +80 -0
- package/tests/commands/help-arguments.spec.ts +31 -0
- package/tests/commands/import-piece.spec.ts +228 -0
- package/tests/commands/piece-meta.spec.ts +123 -0
- package/tests/commands/rescan.spec.ts +137 -0
- package/tests/lib/draft-io.spec.ts +76 -0
- package/tests/lib/errors.spec.ts +48 -0
- package/tests/lib/fs-walk.spec.ts +35 -0
- package/tests/lib/json-input.spec.ts +63 -0
- package/tests/lib/parse.spec.ts +85 -0
- package/tests/lib/schema-help.spec.ts +42 -0
- package/tests/lib/schemas.spec.ts +82 -0
- package/tests/lib/slugify.spec.ts +28 -0
- package/tsconfig.json +19 -0
- package/tsconfig.test.json +10 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.selectionOverridesSchemas = exports.pieceMetaOverridesSchema = exports.pieceOverridesSchema = exports.pieceDefinitionSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const countSchema = zod_1.z.union([
|
|
6
|
+
zod_1.z.number().int(),
|
|
7
|
+
zod_1.z.literal("*"),
|
|
8
|
+
zod_1.z.tuple([zod_1.z.number().int(), zod_1.z.union([zod_1.z.number().int(), zod_1.z.literal("*")])]),
|
|
9
|
+
]);
|
|
10
|
+
const assetSchema = zod_1.z.object({
|
|
11
|
+
name: zod_1.z.string(),
|
|
12
|
+
classification: zod_1.z.enum(["logic", "media", "doc"]),
|
|
13
|
+
count: countSchema,
|
|
14
|
+
glob: zod_1.z.array(zod_1.z.string()),
|
|
15
|
+
}).strict();
|
|
16
|
+
exports.pieceDefinitionSchema = zod_1.z.object({
|
|
17
|
+
selectionStrategy: zod_1.z.enum(["mandatory", "personal", "shared", "on demand"]),
|
|
18
|
+
requires: zod_1.z.array(zod_1.z.string()),
|
|
19
|
+
pathVariables: zod_1.z.array(zod_1.z.string()),
|
|
20
|
+
assets: zod_1.z.array(assetSchema),
|
|
21
|
+
}).strict();
|
|
22
|
+
const pieceHumanInfoSchema = zod_1.z.object({
|
|
23
|
+
name: zod_1.z.string(),
|
|
24
|
+
author: zod_1.z.string(),
|
|
25
|
+
url: zod_1.z.string(),
|
|
26
|
+
image: zod_1.z.string().optional(),
|
|
27
|
+
}).strict();
|
|
28
|
+
exports.pieceOverridesSchema = zod_1.z.object({
|
|
29
|
+
humanInfo: pieceHumanInfoSchema.optional(),
|
|
30
|
+
downloadSources: zod_1.z.array(zod_1.z.string()).optional(),
|
|
31
|
+
pathVariables: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
|
|
32
|
+
}).strict();
|
|
33
|
+
const untrustedScriptRefSchema = zod_1.z.object({
|
|
34
|
+
src: zod_1.z.string(),
|
|
35
|
+
method: zod_1.z.string().optional(),
|
|
36
|
+
}).strict();
|
|
37
|
+
const jsonShallowValueSchema = zod_1.z.union([
|
|
38
|
+
zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean(),
|
|
39
|
+
zod_1.z.array(zod_1.z.string()), zod_1.z.array(zod_1.z.number()), zod_1.z.array(zod_1.z.boolean()),
|
|
40
|
+
]);
|
|
41
|
+
const jsonShallowObjectSchema = zod_1.z.record(zod_1.z.string(), jsonShallowValueSchema);
|
|
42
|
+
exports.pieceMetaOverridesSchema = zod_1.z.object({
|
|
43
|
+
schema: zod_1.z.record(zod_1.z.string(), zod_1.z.enum(["boolean", "number", "string", "boolean[]", "number[]", "string[]"])).optional(),
|
|
44
|
+
defaultMeta: jsonShallowObjectSchema.optional(),
|
|
45
|
+
values: zod_1.z.record(zod_1.z.string(), jsonShallowObjectSchema).optional(),
|
|
46
|
+
}).strict();
|
|
47
|
+
const selectedPieceSchema = zod_1.z.lazy(() => zod_1.z.object({
|
|
48
|
+
id: zod_1.z.string(),
|
|
49
|
+
required: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
|
|
50
|
+
mandatory: zod_1.z.array(selectedPieceSchema),
|
|
51
|
+
selectable: zod_1.z.array(selectedPieceSchema),
|
|
52
|
+
}).strict()),
|
|
53
|
+
}).strict());
|
|
54
|
+
const userSelectionValidationSchema = zod_1.z.object({
|
|
55
|
+
count: countSchema,
|
|
56
|
+
unique: zod_1.z.boolean(),
|
|
57
|
+
banList: zod_1.z.array(zod_1.z.string()),
|
|
58
|
+
customValidation: zod_1.z.array(untrustedScriptRefSchema),
|
|
59
|
+
}).strict();
|
|
60
|
+
exports.selectionOverridesSchemas = {
|
|
61
|
+
normal: zod_1.z.object({
|
|
62
|
+
validation: userSelectionValidationSchema.optional(),
|
|
63
|
+
mergeAlgorithm: untrustedScriptRefSchema.optional(),
|
|
64
|
+
}).strict(),
|
|
65
|
+
preselected: zod_1.z.object({
|
|
66
|
+
pieces: zod_1.z.array(selectedPieceSchema).optional(),
|
|
67
|
+
}).strict(),
|
|
68
|
+
unselectable: zod_1.z.object({}).strict(),
|
|
69
|
+
"game-controlled": zod_1.z.object({}).strict(),
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printSemverReasons = printSemverReasons;
|
|
4
|
+
function printSemverReasons(semver) {
|
|
5
|
+
const reasons = semver.getReasons();
|
|
6
|
+
for (const level of ["major", "minor", "patch"]) {
|
|
7
|
+
for (const { reason, info } of reasons[level]) {
|
|
8
|
+
console.log(`[${level}] ${reason} ${JSON.stringify(info)}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
console.log(`Version: ${semver.toString()}`);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=semver-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semver-report.js","sourceRoot":"","sources":["../../src/lib/semver-report.ts"],"names":[],"mappings":";;;AAEA,4BAAmC,MAAc;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACpC,KAAI,MAAM,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAU,EAAC,CAAC;QACvD,KAAI,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,EAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.slugify = slugify;
|
|
4
|
+
function slugify(text) {
|
|
5
|
+
return text
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/[\s_]+/g, '-')
|
|
9
|
+
.replace(/[^a-z0-9-]+/g, '')
|
|
10
|
+
.replace(/^-+|-+$/g, '')
|
|
11
|
+
.replace(/-+/g, '-');
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=slugify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slugify.js","sourceRoot":"","sources":["../../src/lib/slugify.ts"],"names":[],"mappings":";;;AAAA,iBAAwB,IAAY;IAClC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,IAAI,EAAE;SACN,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACzB,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AACa,QAAA,OAAO,GAAG,OAAO,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@roster-lock/config-editor-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A cli tool to build roster lock configs",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/formula1/roster-lock.git",
|
|
8
|
+
"directory": "core/config-editor-cli"
|
|
9
|
+
},
|
|
10
|
+
"license": "JSON",
|
|
11
|
+
"author": "Sam Tobia",
|
|
12
|
+
"type": "commonjs",
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"bin": {
|
|
15
|
+
"rosterlock-config-editor": "dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"ajv": "^8.17.1",
|
|
22
|
+
"commander": "^14.0.3",
|
|
23
|
+
"zod": "^4.4.3",
|
|
24
|
+
"@roster-lock/plugin-runtime": "0.1.0",
|
|
25
|
+
"@roster-lock/shared": "0.1.0",
|
|
26
|
+
"@roster-lock/types": "1.0.0",
|
|
27
|
+
"@roster-lock/utils": "1.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.14.1",
|
|
31
|
+
"@yao-pkg/pkg": "^6.19.0",
|
|
32
|
+
"esbuild": "^0.28.1",
|
|
33
|
+
"rimraf": "^6.1.3",
|
|
34
|
+
"tsx": "^4.0.0",
|
|
35
|
+
"typescript": "^7.0.2",
|
|
36
|
+
"vitest": "^4.1.5"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"prebuild": "node scripts/generate-version.js",
|
|
40
|
+
"build": "rimraf ./dist && tsc",
|
|
41
|
+
"predev": "node scripts/generate-version.js",
|
|
42
|
+
"dev": "tsx src/index.ts",
|
|
43
|
+
"start": "node dist/index.js",
|
|
44
|
+
"test": "vitest run tests/",
|
|
45
|
+
"prebundle": "pnpm run build",
|
|
46
|
+
"bundle": "node scripts/bundle.js",
|
|
47
|
+
"package": "pnpm run bundle && pkg pkg-build/bundle.cjs -t node24-linux-x64 -o bin/rosterlock-config-editor --no-signature"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Bundles the compiled dist/index.js (and its workspace/node_modules deps) into a
|
|
2
|
+
// single CJS file, so `pkg` can package it into a standalone binary without needing
|
|
3
|
+
// to resolve pnpm's symlinked node_modules layout itself.
|
|
4
|
+
const esbuild = require("esbuild");
|
|
5
|
+
const { join } = require("node:path");
|
|
6
|
+
|
|
7
|
+
esbuild.buildSync({
|
|
8
|
+
entryPoints: [join(__dirname, "..", "dist", "index.js")],
|
|
9
|
+
bundle: true,
|
|
10
|
+
platform: "node",
|
|
11
|
+
format: "cjs",
|
|
12
|
+
outfile: join(__dirname, "..", "pkg-build", "bundle.cjs"),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
console.log("Bundled to pkg-build/bundle.cjs");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Bakes package.json's version into a source file at build time, so the
|
|
2
|
+
// version is embedded directly in the compiled/bundled output instead of
|
|
3
|
+
// being read from package.json at runtime (which wouldn't be available if
|
|
4
|
+
// this package is later packaged into a standalone binary, e.g. via pkg).
|
|
5
|
+
const { writeFileSync, mkdirSync } = require("node:fs");
|
|
6
|
+
const { join } = require("node:path");
|
|
7
|
+
|
|
8
|
+
const pkg = require("../package.json");
|
|
9
|
+
|
|
10
|
+
mkdirSync(join(__dirname, "..", "src"), { recursive: true });
|
|
11
|
+
writeFileSync(
|
|
12
|
+
join(__dirname, "..", "src", "version.ts"),
|
|
13
|
+
`// Generated by scripts/generate-version.js — do not edit by hand.\nexport const VERSION = ${JSON.stringify(pkg.version)};\n`
|
|
14
|
+
);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rosterlock-config-editor
|
|
3
|
+
description: Create and edit roster-lock config drafts (engine piece types, roster pieces, selection config) using the rosterlock CLI, and validate/promote/publish them. Use whenever asked to build, edit, inspect, or validate a *.rosterlock.draft.json or *.rosterlock.json file.
|
|
4
|
+
argument-hint: e.g. "create a draft for a new game called Foo" or "add a character piece type with a sprite asset"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# rosterlock CLI
|
|
8
|
+
|
|
9
|
+
`rosterlock` is the CLI for building and editing roster-lock config files: the
|
|
10
|
+
engine's piece-type definitions, the roster of actual pieces per type, and the
|
|
11
|
+
per-type selection config. This skill ships inside the `@roster-lock/config-editor-cli`
|
|
12
|
+
package itself (this file lives at `skills/rosterlock-config-editor/SKILL.md`
|
|
13
|
+
relative to that package's root, `core/config-editor-cli/` in the roster-lock
|
|
14
|
+
monorepo) so it travels with the CLI - installing or cloning just this package is
|
|
15
|
+
enough to have it.
|
|
16
|
+
|
|
17
|
+
It edits a **draft** file (`*.rosterlock.draft.json`), which wraps a `stagedLock`
|
|
18
|
+
(the config being built), a `previousLock` (the last published baseline, for
|
|
19
|
+
semver diffing), and draft-only bookkeeping (`draft.rosterPieceInfo`,
|
|
20
|
+
`draft.selectionScriptInfo`).
|
|
21
|
+
|
|
22
|
+
**`--help` is the source of truth for exact flags.** This doc gives you the shape
|
|
23
|
+
of the CLI and the order operations need to happen in; run
|
|
24
|
+
`$ROSTERLOCK <command> --help` before using a command you haven't used yet in this
|
|
25
|
+
session, and re-check it if a command errors on unrecognized/missing flags - flags
|
|
26
|
+
do change as the CLI evolves and this doc can drift.
|
|
27
|
+
|
|
28
|
+
## Getting a runnable binary
|
|
29
|
+
|
|
30
|
+
From this package's root, build a standalone binary (bundled via esbuild, packaged
|
|
31
|
+
via `@yao-pkg/pkg` - runs without `pnpm`/`node_modules` on `PATH`):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm run package
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This produces `bin/rosterlock-config-editor` (gitignored, built on demand - targets
|
|
38
|
+
`node24-linux-x64` by default; edit the `package` script in `package.json` to add
|
|
39
|
+
other targets like `node24-macos-arm64`/`node24-win-x64` if needed). Check whether
|
|
40
|
+
it already exists before rebuilding - it only needs to be regenerated after source
|
|
41
|
+
changes:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
test -x bin/rosterlock-config-editor || pnpm run package
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you only need to run something once and don't want to package a binary, and
|
|
48
|
+
you're working inside the roster-lock monorepo (this package's dependencies are
|
|
49
|
+
workspace-linked there), `pnpm dev -- <args>` runs the CLI directly from source
|
|
50
|
+
via `tsx` (no build/package step, slightly slower startup).
|
|
51
|
+
|
|
52
|
+
All examples below assume `ROSTERLOCK=./bin/rosterlock-config-editor` (adjust the
|
|
53
|
+
path if you're invoking it from elsewhere - e.g. `pnpm dev --` if running from
|
|
54
|
+
source).
|
|
55
|
+
|
|
56
|
+
## Command map
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
$ROSTERLOCK draft init [path] --title <t> --author <a>
|
|
60
|
+
$ROSTERLOCK draft from-lock <lockPath> [draftPath] [--clear-rosters]
|
|
61
|
+
$ROSTERLOCK draft validate [-d <draft>] # strict: every issue blocking publish/promote, at once
|
|
62
|
+
$ROSTERLOCK draft promote [-d <draft>] # stagedLock becomes the new previousLock baseline
|
|
63
|
+
$ROSTERLOCK draft publish <outLockPath> [-d <draft>] # writes a standalone versioned lock file
|
|
64
|
+
|
|
65
|
+
$ROSTERLOCK validate [-d <draft>] [--lock <path>] # looser structural check (drafts are allowed to be WIP)
|
|
66
|
+
$ROSTERLOCK show [-d <draft>] # print stagedLock as JSON
|
|
67
|
+
|
|
68
|
+
$ROSTERLOCK engine set-info --name <n> --engine-version <v> [-d <draft>]
|
|
69
|
+
$ROSTERLOCK engine add-piece-type <pieceType> --strategy <s> [--requires <t,..>] [--path-variables <n,..>] [--json <file|->]
|
|
70
|
+
$ROSTERLOCK engine edit-definition <pieceType> [--strategy <s>] [--requires <t,..>] [--path-variables <n,..>] [--json <file|->]
|
|
71
|
+
$ROSTERLOCK engine add-asset <pieceType> <assetName> --classification <c> --count <n> --glob <pattern> [-d <draft>]
|
|
72
|
+
$ROSTERLOCK engine remove-asset <pieceType> <assetName> [-d <draft>]
|
|
73
|
+
$ROSTERLOCK engine remove-piece-type <pieceType> [-d <draft>] # refuses if the roster still has pieces
|
|
74
|
+
$ROSTERLOCK engine show [pieceType] [-d <draft>]
|
|
75
|
+
|
|
76
|
+
$ROSTERLOCK roster add-piece <pieceType> <folder> [--path-variables <k=v,..>] [--download-source <url>] [--json <file|->]
|
|
77
|
+
$ROSTERLOCK roster edit-piece <pieceType> <pieceId> [--name/--author/--url/--image <v>] [--add-download-source <url>] [--remove-download-source <url>] [--path-variables <k=v,..>] [--remove-path-variable <name>] [--json <file|->]
|
|
78
|
+
$ROSTERLOCK roster rescan <pieceType> <pieceId> [folder] [--path-variables <k=v,..>] # recompute hashes in place after asset/file changes; keeps id, humanInfo, downloadSources, requiredPieces
|
|
79
|
+
$ROSTERLOCK roster test-download <pieceType> <pieceId> <sourceUrl> # downloads and verifies a source's hash matches
|
|
80
|
+
$ROSTERLOCK roster remove-piece <pieceType> <pieceId>
|
|
81
|
+
$ROSTERLOCK roster list [pieceType] [-d <draft>]
|
|
82
|
+
|
|
83
|
+
$ROSTERLOCK selection add-script <fileOrFolder> [--key <relPath>]
|
|
84
|
+
$ROSTERLOCK selection set <pieceType> --type <normal|preselected|unselectable|game-controlled> [--json <file|->]
|
|
85
|
+
$ROSTERLOCK selection run-script <scriptRef>
|
|
86
|
+
$ROSTERLOCK selection show [-d <draft>]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The `-d, --draft <path>` option (present on almost every command) defaults to the
|
|
90
|
+
sole `*.rosterlock.draft.json` file in the current directory - only pass it
|
|
91
|
+
explicitly when there's more than one draft in play, or you're not running from
|
|
92
|
+
the draft's directory.
|
|
93
|
+
|
|
94
|
+
## Typical workflow
|
|
95
|
+
|
|
96
|
+
1. **Start a draft.** Either `draft init` (empty) or `draft from-lock <existing.rosterlock.json>`
|
|
97
|
+
(start from a published lock; add `--clear-rosters` to keep the engine config
|
|
98
|
+
but start with empty rosters/selection - useful for "same game, new content
|
|
99
|
+
season").
|
|
100
|
+
2. **Define the engine** (piece types): `engine add-piece-type`, then
|
|
101
|
+
`engine add-asset` for each asset the piece type needs. `engine edit-definition`
|
|
102
|
+
changes strategy/requires/path-variables later without re-adding assets.
|
|
103
|
+
3. **Populate rosters** (actual pieces): `roster add-piece <type> <folder>` scans a
|
|
104
|
+
folder against the piece type's asset definitions and computes content hashes.
|
|
105
|
+
It needs at least one download source, from `--download-source`, a
|
|
106
|
+
`rosterlock.piece-meta.json` in the folder, or `--json`. Use `roster edit-piece`
|
|
107
|
+
to fix up humanInfo/sources/path-variables afterward. Its id is derived from
|
|
108
|
+
`humanInfo` (`@author/name`, or `#logicHash/mediaHash` if unnamed) - there's no
|
|
109
|
+
flag to set it directly. To update an **existing** piece's files (e.g. after
|
|
110
|
+
adding a new asset type or editing media) without changing its id or losing its
|
|
111
|
+
`requiredPieces`, use `roster rescan <type> <pieceId> [folder]` instead of
|
|
112
|
+
removing and re-adding it - `add-piece` always resets `requiredPieces` to empty
|
|
113
|
+
stubs, which `roster edit-piece` cannot restore.
|
|
114
|
+
4. **Configure selection** per piece type: `selection set <type> --type <kind>`.
|
|
115
|
+
`normal` needs `validation` (count/unique/banList/customValidation); `preselected`
|
|
116
|
+
needs `pieces`; `unselectable`/`game-controlled` need neither - just `pieceMeta`
|
|
117
|
+
if you're using per-piece metadata. Pass the type-specific fields via `--json`
|
|
118
|
+
(see shape in `selection set --help`).
|
|
119
|
+
5. **Validate before finishing up**: `draft validate` runs the full (non-draft)
|
|
120
|
+
schema against `stagedLock` and reports *every* issue in one pass (not just the
|
|
121
|
+
first) - use it to iterate through problems instead of running `publish`
|
|
122
|
+
repeatedly. (Plain `validate` without `draft` only checks structure - it
|
|
123
|
+
deliberately allows an in-progress/incomplete draft.)
|
|
124
|
+
6. **Promote and/or publish**: `draft promote` moves `stagedLock` forward as the
|
|
125
|
+
new `previousLock` baseline (for future diffs) without producing an output file.
|
|
126
|
+
`draft publish <path>` writes a standalone, semver-bumped lock file and prints
|
|
127
|
+
why the version changed, without touching the draft.
|
|
128
|
+
|
|
129
|
+
## `--json` inputs
|
|
130
|
+
|
|
131
|
+
Several commands accept `--json <file>` to supply structured input too large/awkward
|
|
132
|
+
for flags (full piece-type definitions, type-specific selection fields, bulk piece
|
|
133
|
+
edits). Pass `-` instead of a file path to pipe JSON via stdin instead of writing a
|
|
134
|
+
temp file - handy when generating the JSON programmatically:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
echo '{"selectionStrategy":"shared","requires":[],"pathVariables":[],"assets":[]}' \
|
|
138
|
+
| $ROSTERLOCK engine edit-definition character --json -
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Every `--json` input is validated with zod before use; the command's `--help`
|
|
142
|
+
prints the exact expected shape (e.g. `Shape: { selectionStrategy: "mandatory" |
|
|
143
|
+
... }`). A shape mismatch fails fast with a per-field error - fix the JSON and
|
|
144
|
+
retry rather than guessing.
|
|
145
|
+
|
|
146
|
+
## Error output
|
|
147
|
+
|
|
148
|
+
Validation errors (from `--json` shape checks, or from `draft validate`/`publish`/
|
|
149
|
+
`promote` against the full lock schema) print one issue per line as
|
|
150
|
+
`<path> <message>`, and the process exits non-zero. Multiple issues are reported
|
|
151
|
+
together, not just the first - read the whole list before making changes.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { EMPTY_ROSTER_DRAFT } from "@roster-lock/shared";
|
|
4
|
+
import { cloneJSON } from "@roster-lock/utils";
|
|
5
|
+
import { readLock } from "../../lib/lock-io";
|
|
6
|
+
import { defaultDraftPath, writeDraft } from "../../lib/draft-io";
|
|
7
|
+
import { withErrorHandling } from "../../lib/errors";
|
|
8
|
+
|
|
9
|
+
export const fromLockCommand = new Command("from-lock")
|
|
10
|
+
.description("Create a new draft from an existing published lock file")
|
|
11
|
+
.argument("<lockPath>", "path to the published lock file")
|
|
12
|
+
.argument("[draftPath]", "where to write the draft (defaults to a name derived from the lock's title)")
|
|
13
|
+
.option(
|
|
14
|
+
"--clear-rosters",
|
|
15
|
+
"keep the lock's engine config, but start with empty rosters and no per-piece-type selection config"
|
|
16
|
+
)
|
|
17
|
+
.action(withErrorHandling(async (lockPath: string, draftPath: string | undefined, opts: { clearRosters?: boolean }) => {
|
|
18
|
+
const lock = readLock(lockPath);
|
|
19
|
+
|
|
20
|
+
const draft = cloneJSON(EMPTY_ROSTER_DRAFT);
|
|
21
|
+
draft.previousLock = cloneJSON(lock);
|
|
22
|
+
draft.stagedLock = cloneJSON(lock);
|
|
23
|
+
|
|
24
|
+
if(opts.clearRosters){
|
|
25
|
+
for(const pieceType of Object.keys(draft.stagedLock.rosters)){
|
|
26
|
+
draft.stagedLock.rosters[pieceType] = [];
|
|
27
|
+
}
|
|
28
|
+
draft.stagedLock.selection.piece = {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const path = draftPath ? resolve(process.cwd(), draftPath) : defaultDraftPath(lock.title);
|
|
32
|
+
writeDraft(path, draft);
|
|
33
|
+
console.log(`Created draft: ${path}${opts.clearRosters ? " (rosters cleared)" : ""}`);
|
|
34
|
+
}));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { EMPTY_ROSTER_DRAFT } from "@roster-lock/shared";
|
|
4
|
+
import { cloneJSON } from "@roster-lock/utils";
|
|
5
|
+
import { defaultDraftPath, writeDraft } from "../../lib/draft-io";
|
|
6
|
+
import { withErrorHandling } from "../../lib/errors";
|
|
7
|
+
|
|
8
|
+
export const initCommand = new Command("init")
|
|
9
|
+
.description("Create a new empty roster-lock draft")
|
|
10
|
+
.argument("[draftPath]", "where to write the draft (defaults to a name derived from --title)")
|
|
11
|
+
.option("--title <title>", "title to seed the staged lock with")
|
|
12
|
+
.option("--author <author>", "author to seed the staged lock with")
|
|
13
|
+
.action(withErrorHandling(async (draftPath: string | undefined, opts: { title?: string, author?: string }) => {
|
|
14
|
+
const draft = cloneJSON(EMPTY_ROSTER_DRAFT);
|
|
15
|
+
if(opts.title) draft.stagedLock.title = opts.title;
|
|
16
|
+
if(opts.author) draft.stagedLock.author = opts.author;
|
|
17
|
+
|
|
18
|
+
const path = draftPath ? resolve(process.cwd(), draftPath) : defaultDraftPath(opts.title);
|
|
19
|
+
writeDraft(path, draft);
|
|
20
|
+
console.log(`Created draft: ${path}`);
|
|
21
|
+
}));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ROSTERLOCK_V1_CASTER_JSONSCHEMA, diffLocks } from "@roster-lock/shared";
|
|
3
|
+
import { cloneJSON } from "@roster-lock/utils";
|
|
4
|
+
import { resolveDraftPath, readDraft, writeDraft } from "../../lib/draft-io";
|
|
5
|
+
import { withDraftOption } from "../../lib/cli-options";
|
|
6
|
+
import { withErrorHandling } from "../../lib/errors";
|
|
7
|
+
import { printSemverReasons } from "../../lib/semver-report";
|
|
8
|
+
|
|
9
|
+
export const promoteCommand = withDraftOption(new Command("promote"))
|
|
10
|
+
.description("Move the staged lock forward as the new baseline (previousLock) for future diffs")
|
|
11
|
+
.action(withErrorHandling(async (opts: { draft?: string }) => {
|
|
12
|
+
const draftPath = resolveDraftPath(opts.draft);
|
|
13
|
+
const draft = readDraft(draftPath);
|
|
14
|
+
|
|
15
|
+
const updatedDraft = cloneJSON(draft);
|
|
16
|
+
ROSTERLOCK_V1_CASTER_JSONSCHEMA.cast(updatedDraft.stagedLock);
|
|
17
|
+
|
|
18
|
+
const semver = diffLocks(updatedDraft.previousLock, updatedDraft.stagedLock);
|
|
19
|
+
updatedDraft.stagedLock.version = semver.toString();
|
|
20
|
+
updatedDraft.previousLock = cloneJSON(updatedDraft.stagedLock);
|
|
21
|
+
|
|
22
|
+
writeDraft(draftPath, updatedDraft);
|
|
23
|
+
printSemverReasons(semver);
|
|
24
|
+
}));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ROSTERLOCK_V1_CASTER_JSONSCHEMA, diffLocks } from "@roster-lock/shared";
|
|
3
|
+
import { cloneJSON } from "@roster-lock/utils";
|
|
4
|
+
import { resolveDraftPath, readDraft } from "../../lib/draft-io";
|
|
5
|
+
import { writeLock } from "../../lib/lock-io";
|
|
6
|
+
import { withDraftOption } from "../../lib/cli-options";
|
|
7
|
+
import { withErrorHandling } from "../../lib/errors";
|
|
8
|
+
import { printSemverReasons } from "../../lib/semver-report";
|
|
9
|
+
|
|
10
|
+
export const publishCommand = withDraftOption(new Command("publish"))
|
|
11
|
+
.description("Publish the staged lock as a standalone, versioned lock file (does not modify the draft)")
|
|
12
|
+
.argument("<outLockPath>", "where to write the published lock file")
|
|
13
|
+
.action(withErrorHandling(async (outLockPath: string, opts: { draft?: string }) => {
|
|
14
|
+
const draftPath = resolveDraftPath(opts.draft);
|
|
15
|
+
const draft = readDraft(draftPath);
|
|
16
|
+
|
|
17
|
+
const stagedLock = cloneJSON(draft.stagedLock);
|
|
18
|
+
ROSTERLOCK_V1_CASTER_JSONSCHEMA.cast(stagedLock);
|
|
19
|
+
|
|
20
|
+
const semver = diffLocks(draft.previousLock, stagedLock);
|
|
21
|
+
stagedLock.version = semver.toString();
|
|
22
|
+
|
|
23
|
+
writeLock(outLockPath, stagedLock);
|
|
24
|
+
printSemverReasons(semver);
|
|
25
|
+
console.log(`Published: ${outLockPath}`);
|
|
26
|
+
}));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ROSTERLOCK_V1_CASTER_JSONSCHEMA } from "@roster-lock/shared";
|
|
3
|
+
import { resolveDraftPath, readDraft } from "../../lib/draft-io";
|
|
4
|
+
import { withDraftOption } from "../../lib/cli-options";
|
|
5
|
+
import { formatError } from "../../lib/errors";
|
|
6
|
+
|
|
7
|
+
export const draftValidateCommand = withDraftOption(new Command("validate"))
|
|
8
|
+
.description(
|
|
9
|
+
"Report every issue blocking \"draft publish\"/\"draft promote\": runs the staged lock through the full " +
|
|
10
|
+
"(non-draft) schema and lists all failures at once, without writing anything"
|
|
11
|
+
)
|
|
12
|
+
.action(async (opts: { draft?: string }) => {
|
|
13
|
+
try {
|
|
14
|
+
const draftPath = resolveDraftPath(opts.draft);
|
|
15
|
+
const draft = readDraft(draftPath);
|
|
16
|
+
|
|
17
|
+
const result = ROSTERLOCK_V1_CASTER_JSONSCHEMA.safeCast(draft.stagedLock);
|
|
18
|
+
if(result.valid){
|
|
19
|
+
console.log("OK: staged lock is valid and ready to publish");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.error(`${result.error.length} issue(s) found in staged lock:`);
|
|
24
|
+
console.error(formatError(result.error));
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
} catch(e){
|
|
27
|
+
console.error(formatError(e));
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ROSTERLOCK_V1_CASTER_JSONSCHEMA, validatePieceDefinition } from "@roster-lock/shared";
|
|
3
|
+
import { EngineAssetDefinition, AssetClassification } from "@roster-lock/types";
|
|
4
|
+
import { resolveDraftPath, readDraft, writeDraft } from "../../lib/draft-io";
|
|
5
|
+
import { withDraftOption } from "../../lib/cli-options";
|
|
6
|
+
import { withErrorHandling } from "../../lib/errors";
|
|
7
|
+
import { parseCount } from "../../lib/parse";
|
|
8
|
+
|
|
9
|
+
function collectGlob(value: string, previous: Array<string>){
|
|
10
|
+
previous.push(value);
|
|
11
|
+
return previous;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const addAssetCommand = withDraftOption(new Command("add-asset"))
|
|
15
|
+
.description("Add an asset definition to an existing piece type")
|
|
16
|
+
.argument("<pieceType>", "the piece type key")
|
|
17
|
+
.argument("<assetName>", "the asset's name")
|
|
18
|
+
.requiredOption("--classification <classification>", "logic|media|doc")
|
|
19
|
+
.requiredOption("--count <count>", "exact number, \"*\", or \"min:max\" (max may be \"*\")")
|
|
20
|
+
.option("--glob <pattern>", "glob pattern (repeatable)", collectGlob, [] as Array<string>)
|
|
21
|
+
.action(withErrorHandling(async (pieceType: string, assetName: string, opts: {
|
|
22
|
+
draft?: string, classification: string, count: string, glob: Array<string>
|
|
23
|
+
}) => {
|
|
24
|
+
const draftPath = resolveDraftPath(opts.draft);
|
|
25
|
+
const draft = readDraft(draftPath);
|
|
26
|
+
|
|
27
|
+
const definition = draft.stagedLock.engine.pieceDefinitions[pieceType];
|
|
28
|
+
if(!definition) throw new Error(`Unknown piece type "${pieceType}"`);
|
|
29
|
+
if(opts.glob.length === 0) throw new Error("At least one --glob is required");
|
|
30
|
+
if(!["logic", "media", "doc"].includes(opts.classification)){
|
|
31
|
+
throw new Error(`Invalid --classification "${opts.classification}" (expected logic|media|doc)`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const asset: EngineAssetDefinition = {
|
|
35
|
+
name: assetName,
|
|
36
|
+
classification: opts.classification as AssetClassification,
|
|
37
|
+
count: parseCount(opts.count),
|
|
38
|
+
glob: opts.glob,
|
|
39
|
+
};
|
|
40
|
+
definition.assets.push(asset);
|
|
41
|
+
validatePieceDefinition(pieceType, definition, draft.stagedLock.engine);
|
|
42
|
+
ROSTERLOCK_V1_CASTER_JSONSCHEMA.cast(draft.stagedLock);
|
|
43
|
+
|
|
44
|
+
writeDraft(draftPath, draft);
|
|
45
|
+
console.log(`Added asset "${assetName}" to piece type "${pieceType}"`);
|
|
46
|
+
}));
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ROSTERLOCK_V1_CASTER_JSONSCHEMA, validatePieceDefinition } from "@roster-lock/shared";
|
|
3
|
+
import { RosterLockV1Config } from "@roster-lock/types";
|
|
4
|
+
import { resolveDraftPath, readDraft, writeDraft } from "../../lib/draft-io";
|
|
5
|
+
import { withDraftOption } from "../../lib/cli-options";
|
|
6
|
+
import { withErrorHandling } from "../../lib/errors";
|
|
7
|
+
import { parseCommaList, parseStrategy } from "../../lib/parse";
|
|
8
|
+
import { readJsonInput } from "../../lib/json-input";
|
|
9
|
+
import { pieceDefinitionSchema } from "../../lib/schemas";
|
|
10
|
+
import { describeSchemaShape } from "../../lib/schema-help";
|
|
11
|
+
|
|
12
|
+
type PieceDefinition = RosterLockV1Config["engine"]["pieceDefinitions"][string];
|
|
13
|
+
|
|
14
|
+
export const addPieceTypeCommand = withDraftOption(new Command("add-piece-type"))
|
|
15
|
+
.description("Define a new piece type in the staged lock's engine")
|
|
16
|
+
.argument("<pieceType>", "the piece type key")
|
|
17
|
+
.option("--strategy <strategy>", "mandatory|personal|shared|on-demand")
|
|
18
|
+
.option("--requires <types>", "comma-separated list of required on-demand piece types")
|
|
19
|
+
.option("--path-variables <names>", "comma-separated list of path variable names")
|
|
20
|
+
.option(
|
|
21
|
+
"--json <file>",
|
|
22
|
+
"read the full piece-type definition (including assets) from a JSON file (or \"-\" for stdin), instead of the flags above. " +
|
|
23
|
+
`Shape: ${describeSchemaShape(pieceDefinitionSchema)}`
|
|
24
|
+
)
|
|
25
|
+
.action(withErrorHandling(async (pieceType: string, opts: {
|
|
26
|
+
draft?: string, strategy?: string, requires?: string, pathVariables?: string, json?: string
|
|
27
|
+
}) => {
|
|
28
|
+
const draftPath = resolveDraftPath(opts.draft);
|
|
29
|
+
const draft = readDraft(draftPath);
|
|
30
|
+
|
|
31
|
+
if(draft.stagedLock.engine.pieceDefinitions[pieceType]){
|
|
32
|
+
throw new Error(`Piece type "${pieceType}" already exists; use "engine remove-piece-type" first`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const definition: PieceDefinition = opts.json
|
|
36
|
+
? await readJsonInput(opts.json, pieceDefinitionSchema)
|
|
37
|
+
: {
|
|
38
|
+
selectionStrategy: parseStrategy(requireOption(opts.strategy, "--strategy")),
|
|
39
|
+
requires: parseCommaList(opts.requires),
|
|
40
|
+
pathVariables: parseCommaList(opts.pathVariables),
|
|
41
|
+
assets: [],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
draft.stagedLock.engine.pieceDefinitions[pieceType] = definition;
|
|
45
|
+
draft.stagedLock.rosters[pieceType] ??= [];
|
|
46
|
+
validatePieceDefinition(pieceType, definition, draft.stagedLock.engine);
|
|
47
|
+
ROSTERLOCK_V1_CASTER_JSONSCHEMA.cast(draft.stagedLock);
|
|
48
|
+
|
|
49
|
+
writeDraft(draftPath, draft);
|
|
50
|
+
console.log(`Added piece type: ${pieceType}`);
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
function requireOption(value: string | undefined, flag: string): string {
|
|
54
|
+
if(value === undefined) throw new Error(`${flag} is required unless --json is given`);
|
|
55
|
+
return value;
|
|
56
|
+
}
|