ply-ui-cli 1.1.118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,378 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/commands/update.ts
31
+ var update_exports = {};
32
+ __export(update_exports, {
33
+ update: () => update
34
+ });
35
+ module.exports = __toCommonJS(update_exports);
36
+ var import_fs5 = __toESM(require("fs"));
37
+ var import_path6 = __toESM(require("path"));
38
+ var import_vendor = require("../vendor");
39
+
40
+ // src/config.ts
41
+ var import_fs = __toESM(require("fs"));
42
+ var import_path = __toESM(require("path"));
43
+
44
+ // src/brand.ts
45
+ var CLI_BIN = "ply-ui-cli";
46
+ var CLI_NPX = `npx ${CLI_BIN}`;
47
+ var MCP_PACKAGE = "ply-ui-mcp";
48
+ var MCP_NPX = `npx -y ${MCP_PACKAGE}`;
49
+
50
+ // src/config.ts
51
+ function readConfig() {
52
+ const configPath = import_path.default.resolve(process.cwd(), "base-ui.json");
53
+ if (!import_fs.default.existsSync(configPath)) {
54
+ console.error(`base-ui.json not found. Please run "${CLI_NPX} init" first.`);
55
+ return null;
56
+ }
57
+ try {
58
+ const config = JSON.parse(import_fs.default.readFileSync(configPath, "utf8"));
59
+ if (!config || !config.aliases || !config.aliases.components) {
60
+ throw new Error("Missing aliases.components in base-ui.json");
61
+ }
62
+ return config;
63
+ } catch (err) {
64
+ console.error(`Error reading base-ui.json: ${err.message}`);
65
+ return null;
66
+ }
67
+ }
68
+
69
+ // src/lockfile.ts
70
+ var import_fs2 = __toESM(require("fs"));
71
+ var import_path2 = __toESM(require("path"));
72
+ var import_crypto = __toESM(require("crypto"));
73
+ var LOCKFILE_NAME = "base-ui-lock.json";
74
+ var LOCKFILE_VERSION = 1;
75
+ function hashContent(content) {
76
+ return import_crypto.default.createHash("sha256").update(content, "utf8").digest("hex");
77
+ }
78
+ function lockfilePath() {
79
+ return import_path2.default.resolve(process.cwd(), LOCKFILE_NAME);
80
+ }
81
+ function readLockFile() {
82
+ const p = lockfilePath();
83
+ if (!import_fs2.default.existsSync(p)) {
84
+ return { version: LOCKFILE_VERSION, components: {} };
85
+ }
86
+ try {
87
+ const parsed = JSON.parse(import_fs2.default.readFileSync(p, "utf8"));
88
+ if (parsed && typeof parsed === "object" && parsed.components) {
89
+ return { version: parsed.version ?? LOCKFILE_VERSION, components: parsed.components };
90
+ }
91
+ } catch {
92
+ }
93
+ return { version: LOCKFILE_VERSION, components: {} };
94
+ }
95
+ function writeLockFile(lock) {
96
+ import_fs2.default.writeFileSync(lockfilePath(), JSON.stringify(lock, null, 2) + "\n", "utf8");
97
+ }
98
+
99
+ // src/path-safety.ts
100
+ var import_path3 = __toESM(require("path"));
101
+ function isSafeFileName(name) {
102
+ if (import_path3.default.isAbsolute(name) || /^[a-zA-Z]:[\\/]/.test(name)) return false;
103
+ const segments = name.split(/[\\/]/);
104
+ return segments.every((s) => s !== "" && s !== "..");
105
+ }
106
+
107
+ // src/commands/update.ts
108
+ var import_registry = require("../registry");
109
+ var import_sync = require("../sync");
110
+
111
+ // src/snapshot.ts
112
+ var import_fs3 = __toESM(require("fs"));
113
+ var import_path4 = __toESM(require("path"));
114
+ var SNAPSHOT_ROOT = ".base-ui/snapshots";
115
+ function snapshotPath(component, file) {
116
+ return import_path4.default.resolve(process.cwd(), SNAPSHOT_ROOT, component, file);
117
+ }
118
+ function writeSnapshot(component, file, content) {
119
+ if (!isSafeFileName(file) || !isSafeFileName(component)) return;
120
+ const dest = snapshotPath(component, file);
121
+ import_fs3.default.mkdirSync(import_path4.default.dirname(dest), { recursive: true });
122
+ import_fs3.default.writeFileSync(dest, content, "utf8");
123
+ }
124
+
125
+ // src/merge.ts
126
+ var import_fs4 = __toESM(require("fs"));
127
+ var import_os = __toESM(require("os"));
128
+ var import_path5 = __toESM(require("path"));
129
+ var import_child_process = require("child_process");
130
+ function mergeThreeWay(args) {
131
+ const dir = import_fs4.default.mkdtempSync(import_path5.default.join(import_os.default.tmpdir(), "base-ui-merge-"));
132
+ const ancestorPath = import_path5.default.join(dir, "ancestor");
133
+ const localPath = import_path5.default.join(dir, "local");
134
+ const upstreamPath = import_path5.default.join(dir, "upstream");
135
+ try {
136
+ import_fs4.default.writeFileSync(ancestorPath, args.ancestor, "utf8");
137
+ import_fs4.default.writeFileSync(localPath, args.local, "utf8");
138
+ import_fs4.default.writeFileSync(upstreamPath, args.upstream, "utf8");
139
+ const result = (0, import_child_process.spawnSync)(
140
+ "git",
141
+ ["merge-file", "-p", "--diff3", localPath, ancestorPath, upstreamPath],
142
+ { encoding: "utf8" }
143
+ );
144
+ if (result.error) {
145
+ return {
146
+ clean: false,
147
+ text: conflictMarkers(args),
148
+ error: result.error.message
149
+ };
150
+ }
151
+ const text = result.stdout ?? "";
152
+ return { clean: result.status === 0, text };
153
+ } finally {
154
+ import_fs4.default.rmSync(dir, { recursive: true, force: true });
155
+ }
156
+ }
157
+ function conflictMarkers(args) {
158
+ return `<<<<<<< local
159
+ ${args.local}` + (args.local.endsWith("\n") ? "" : "\n") + `||||||| ancestor
160
+ ${args.ancestor}` + (args.ancestor.endsWith("\n") ? "" : "\n") + `=======
161
+ ${args.upstream}` + (args.upstream.endsWith("\n") ? "" : "\n") + `>>>>>>> upstream
162
+ `;
163
+ }
164
+
165
+ // src/commands/update.ts
166
+ async function resolveConflict(name, file) {
167
+ while (true) {
168
+ const res = await (0, import_vendor.prompts)({
169
+ type: "select",
170
+ name: "choice",
171
+ message: `${name}/${file.path} was edited both locally and upstream. What do you want to do?`,
172
+ choices: [
173
+ { title: "Merge (3-way, installed snapshot as ancestor)", value: "merge" },
174
+ { title: "Keep my version (skip)", value: "keep" },
175
+ { title: "Take the upstream version (overwrite my edits)", value: "upstream" },
176
+ { title: `Save upstream as ${file.path}.upstream (merge by hand, keep my file as-is)`, value: "save-aside" },
177
+ { title: "View diff", value: "view" }
178
+ ]
179
+ });
180
+ if (!res.choice || res.choice === "keep") return "keep";
181
+ if (res.choice === "view") {
182
+ console.log((0, import_sync.renderDiff)(file.localContent, file.upstreamContent));
183
+ continue;
184
+ }
185
+ return res.choice;
186
+ }
187
+ }
188
+ async function update(components, options = {}) {
189
+ const config = readConfig();
190
+ if (!config) {
191
+ process.exitCode = 1;
192
+ return;
193
+ }
194
+ const componentsRoot = import_path6.default.resolve(process.cwd(), config.aliases.components);
195
+ const lock = readLockFile();
196
+ const tracked = Object.keys(lock.components).sort();
197
+ if (!tracked.length) {
198
+ console.log('No components are tracked yet. Components installed with "add" (after this version of the CLI) are tracked automatically.');
199
+ return;
200
+ }
201
+ const targets = components.length ? components : tracked;
202
+ const unknown = targets.filter((n) => !tracked.includes(n));
203
+ if (unknown.length) {
204
+ console.error(`\u2716 Not tracked: ${unknown.join(", ")}`);
205
+ console.error(` Tracked components: ${tracked.join(", ")}`);
206
+ process.exitCode = 1;
207
+ return;
208
+ }
209
+ const spinner = (0, import_vendor.ora)(`Checking ${targets.length} component${targets.length === 1 ? "" : "s"} against upstream...`).start();
210
+ let index;
211
+ try {
212
+ index = await (0, import_registry.fetchIndex)();
213
+ } catch (err) {
214
+ spinner.fail(err.message);
215
+ process.exitCode = 1;
216
+ return;
217
+ }
218
+ const diffs = [];
219
+ for (const name of targets) {
220
+ diffs.push(await (0, import_sync.computeComponentDiff)(name, componentsRoot, lock, index));
221
+ }
222
+ spinner.stop();
223
+ let updated = 0;
224
+ let added = 0;
225
+ let keptLocal = 0;
226
+ let overwrittenLocal = 0;
227
+ let conflictsToUpstream = 0;
228
+ let conflictsSavedAside = 0;
229
+ let conflictsSkipped = 0;
230
+ let conflictsMerged = 0;
231
+ let conflictsMergeDirty = 0;
232
+ const errored = [];
233
+ let lockChanged = false;
234
+ for (const result of diffs) {
235
+ if (result.error) {
236
+ errored.push(result.name);
237
+ console.log(`\u2716 ${result.name}: ${result.error}`);
238
+ continue;
239
+ }
240
+ if (!(0, import_sync.hasChanges)(result)) continue;
241
+ for (const file of result.files) {
242
+ const localPath = import_path6.default.join(componentsRoot, result.name, file.path);
243
+ if (file.status === "unchanged") {
244
+ if (file.localContent !== void 0 && !(file.path in lock.components[result.name].files)) {
245
+ lock.components[result.name].files[file.path] = hashContent(file.localContent);
246
+ lockChanged = true;
247
+ }
248
+ continue;
249
+ }
250
+ if (file.status === "locally-modified") {
251
+ if (options.overwriteLocal) {
252
+ if (!isSafeFileName(file.path)) {
253
+ console.warn(`\u26A0 Skipping unsafe file path from registry: ${result.name}/${file.path}`);
254
+ continue;
255
+ }
256
+ import_fs5.default.mkdirSync(import_path6.default.dirname(localPath), { recursive: true });
257
+ import_fs5.default.writeFileSync(localPath, file.upstreamContent ?? "", "utf8");
258
+ lock.components[result.name].files[file.path] = hashContent(file.upstreamContent ?? "");
259
+ writeSnapshot(result.name, file.path, file.upstreamContent ?? "");
260
+ lockChanged = true;
261
+ overwrittenLocal++;
262
+ console.log(` \u2193 ${result.name}/${file.path} (reverted local edits; upstream unchanged)`);
263
+ } else {
264
+ keptLocal++;
265
+ }
266
+ continue;
267
+ }
268
+ if (file.status === "removed-upstream" || file.status === "missing-locally") {
269
+ continue;
270
+ }
271
+ if (!isSafeFileName(file.path)) {
272
+ console.warn(`\u26A0 Skipping unsafe file path from registry: ${result.name}/${file.path}`);
273
+ continue;
274
+ }
275
+ if (file.status === "upstream-changed" || file.status === "new-upstream") {
276
+ import_fs5.default.mkdirSync(import_path6.default.dirname(localPath), { recursive: true });
277
+ import_fs5.default.writeFileSync(localPath, file.upstreamContent ?? "", "utf8");
278
+ lock.components[result.name].files[file.path] = hashContent(file.upstreamContent ?? "");
279
+ writeSnapshot(result.name, file.path, file.upstreamContent ?? "");
280
+ lockChanged = true;
281
+ if (file.status === "new-upstream") added++;
282
+ else updated++;
283
+ console.log(` ${file.status === "new-upstream" ? "+" : "\u2191"} ${result.name}/${file.path}`);
284
+ continue;
285
+ }
286
+ if (file.status === "conflict") {
287
+ let choice;
288
+ if (options.force) {
289
+ choice = "upstream";
290
+ } else if (options.merge) {
291
+ choice = "merge";
292
+ } else if (options.yes) {
293
+ choice = "keep";
294
+ } else {
295
+ choice = await resolveConflict(result.name, file);
296
+ }
297
+ if (choice === "merge") {
298
+ const ancestor = file.ancestorContent;
299
+ if (ancestor === void 0) {
300
+ conflictsSkipped++;
301
+ console.log(
302
+ ` = ${result.name}/${file.path} (no install snapshot \u2014 cannot 3-way merge; re-add the component to start tracking ancestors)`
303
+ );
304
+ } else {
305
+ const merged = mergeThreeWay({
306
+ ancestor,
307
+ local: file.localContent ?? "",
308
+ upstream: file.upstreamContent ?? ""
309
+ });
310
+ import_fs5.default.writeFileSync(localPath, merged.text, "utf8");
311
+ lock.components[result.name].files[file.path] = hashContent(merged.text);
312
+ writeSnapshot(result.name, file.path, merged.text);
313
+ lockChanged = true;
314
+ if (merged.clean) {
315
+ conflictsMerged++;
316
+ console.log(` \u22C8 ${result.name}/${file.path} (3-way merge, clean)`);
317
+ } else {
318
+ conflictsMergeDirty++;
319
+ console.log(
320
+ ` \u26A0 ${result.name}/${file.path} (3-way merge left conflict markers \u2014 edit the file)`
321
+ );
322
+ }
323
+ }
324
+ } else if (choice === "upstream") {
325
+ import_fs5.default.writeFileSync(localPath, file.upstreamContent ?? "", "utf8");
326
+ lock.components[result.name].files[file.path] = hashContent(file.upstreamContent ?? "");
327
+ writeSnapshot(result.name, file.path, file.upstreamContent ?? "");
328
+ lockChanged = true;
329
+ conflictsToUpstream++;
330
+ console.log(` \u2191 ${result.name}/${file.path} (took upstream, your edits were overwritten)`);
331
+ } else if (choice === "save-aside") {
332
+ import_fs5.default.writeFileSync(`${localPath}.upstream`, file.upstreamContent ?? "", "utf8");
333
+ conflictsSavedAside++;
334
+ console.log(` \u21C4 ${result.name}/${file.path}.upstream (merge by hand \u2014 your file was left untouched)`);
335
+ } else {
336
+ conflictsSkipped++;
337
+ console.log(` = ${result.name}/${file.path} (kept your version)`);
338
+ }
339
+ }
340
+ }
341
+ }
342
+ if (lockChanged) {
343
+ writeLockFile(lock);
344
+ }
345
+ console.log("");
346
+ if (updated) console.log(`\u2714 ${updated} file${updated === 1 ? "" : "s"} updated from upstream.`);
347
+ if (added) console.log(`\u2714 ${added} new file${added === 1 ? "" : "s"} added.`);
348
+ if (overwrittenLocal) {
349
+ console.log(
350
+ `\u2714 ${overwrittenLocal} local edit${overwrittenLocal === 1 ? "" : "s"} overwritten (--overwrite-local).`
351
+ );
352
+ }
353
+ if (keptLocal) {
354
+ console.log(
355
+ `\u2014 ${keptLocal} local edit${keptLocal === 1 ? "" : "s"} kept (upstream unchanged). Re-run with --overwrite-local to discard them.`
356
+ );
357
+ }
358
+ if (conflictsMerged) console.log(`\u22C8 ${conflictsMerged} conflict${conflictsMerged === 1 ? "" : "s"} resolved with a 3-way merge.`);
359
+ if (conflictsMergeDirty) {
360
+ console.log(
361
+ `\u26A0 ${conflictsMergeDirty} conflict${conflictsMergeDirty === 1 ? "" : "s"} merged with leftover markers \u2014 edit those files.`
362
+ );
363
+ process.exitCode = 1;
364
+ }
365
+ if (conflictsToUpstream) console.log(`\u2191 ${conflictsToUpstream} conflict${conflictsToUpstream === 1 ? "" : "s"} resolved by taking upstream.`);
366
+ if (conflictsSavedAside) console.log(`\u21C4 ${conflictsSavedAside} conflict${conflictsSavedAside === 1 ? "" : "s"} saved as .upstream for manual merge.`);
367
+ if (conflictsSkipped) console.log(`= ${conflictsSkipped} conflict${conflictsSkipped === 1 ? "" : "s"} skipped (kept local). Re-run without --yes, or with --force, to resolve.`);
368
+ if (!updated && !added && !overwrittenLocal && !conflictsToUpstream && !conflictsSavedAside && !conflictsSkipped && !conflictsMerged && !conflictsMergeDirty) {
369
+ console.log("Everything is up to date.");
370
+ }
371
+ if (errored.length) {
372
+ process.exitCode = 1;
373
+ }
374
+ }
375
+ // Annotate the CommonJS export names for ESM import in node:
376
+ 0 && (module.exports = {
377
+ update
378
+ });
package/dist/index.js ADDED
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/brand.ts
27
+ var CLI_BIN = "ply-ui-cli";
28
+ var CLI_NPX = `npx ${CLI_BIN}`;
29
+ var MCP_PACKAGE = "ply-ui-mcp";
30
+ var MCP_NPX = `npx -y ${MCP_PACKAGE}`;
31
+
32
+ // src/index.ts
33
+ function printHelp() {
34
+ console.log(`Usage: ${CLI_BIN} <command> [options]
35
+
36
+ Commands:
37
+ init Initialize your project (base-ui.json, styles, icons, Cursor rules, snippets)
38
+ add [components...] Add components; omit names to pick from the catalog
39
+ list List all available components (free and pro)
40
+ diff [components...] Show upstream changes for installed components since you added them
41
+ update [components...] Pull in upstream changes, preserving local edits
42
+ changelog <name> Print product changelog entries for one component (or cli / mcp)
43
+ doctor Check that your project is set up correctly for Ply
44
+
45
+ Options:
46
+ -y, --yes Skip prompts
47
+ -o, --overwrite Overwrite components that already exist (add)
48
+ -f, --force On conflict, take the upstream version (update)
49
+ --overwrite-local On update, also revert files you edited that upstream did not change
50
+ --merge On conflict, 3-way merge using the installed snapshot as ancestor
51
+ -V, --version Print version
52
+ -h, --help Show help
53
+
54
+ https://ply-ui.com
55
+ Issues: https://github.com/ply-ui-ng/ply/issues
56
+ `);
57
+ }
58
+ function parseArgs(argv) {
59
+ const flags = {
60
+ yes: false,
61
+ overwrite: false,
62
+ force: false,
63
+ overwriteLocal: false,
64
+ merge: false,
65
+ help: false,
66
+ version: false
67
+ };
68
+ const positionals = [];
69
+ for (const arg of argv) {
70
+ switch (arg) {
71
+ case "-y":
72
+ case "--yes":
73
+ flags.yes = true;
74
+ break;
75
+ case "-o":
76
+ case "--overwrite":
77
+ flags.overwrite = true;
78
+ break;
79
+ case "-f":
80
+ case "--force":
81
+ flags.force = true;
82
+ break;
83
+ case "--overwrite-local":
84
+ flags.overwriteLocal = true;
85
+ break;
86
+ case "--merge":
87
+ flags.merge = true;
88
+ break;
89
+ case "-h":
90
+ case "--help":
91
+ flags.help = true;
92
+ break;
93
+ case "-V":
94
+ case "--version":
95
+ flags.version = true;
96
+ break;
97
+ default:
98
+ if (arg.startsWith("-")) {
99
+ console.error(`Unknown option: ${arg}`);
100
+ printHelp();
101
+ process.exit(1);
102
+ }
103
+ positionals.push(arg);
104
+ }
105
+ }
106
+ return { command: positionals[0], positionals: positionals.slice(1), flags };
107
+ }
108
+ async function main() {
109
+ const { command, positionals, flags } = parseArgs(process.argv.slice(2));
110
+ if (flags.version && !command) {
111
+ console.log("1.1.118");
112
+ return;
113
+ }
114
+ if (flags.help || !command) {
115
+ printHelp();
116
+ return;
117
+ }
118
+ switch (command) {
119
+ case "init": {
120
+ const { init } = await import("./commands/init.js");
121
+ await init({ yes: flags.yes });
122
+ return;
123
+ }
124
+ case "add": {
125
+ const { add } = await import("./commands/add.js");
126
+ await add(positionals, { yes: flags.yes, overwrite: flags.overwrite });
127
+ return;
128
+ }
129
+ case "list": {
130
+ const { list } = await import("./commands/list.js");
131
+ await list();
132
+ return;
133
+ }
134
+ case "diff": {
135
+ const { diff } = await import("./commands/diff.js");
136
+ await diff(positionals);
137
+ return;
138
+ }
139
+ case "update": {
140
+ const { update } = await import("./commands/update.js");
141
+ await update(positionals, {
142
+ yes: flags.yes,
143
+ force: flags.force,
144
+ overwriteLocal: flags.overwriteLocal,
145
+ merge: flags.merge
146
+ });
147
+ return;
148
+ }
149
+ case "changelog": {
150
+ const { changelog } = await import("./commands/changelog.js");
151
+ await changelog(positionals);
152
+ return;
153
+ }
154
+ case "doctor": {
155
+ const { doctor } = await import("./commands/doctor.js");
156
+ await doctor();
157
+ return;
158
+ }
159
+ case "--version":
160
+ case "-V":
161
+ console.log("1.1.118");
162
+ return;
163
+ default:
164
+ console.error(`Unknown command: ${command}`);
165
+ printHelp();
166
+ process.exitCode = 1;
167
+ }
168
+ }
169
+ main().catch((err) => {
170
+ console.error(err instanceof Error ? err.message : err);
171
+ process.exitCode = 1;
172
+ });