create-agentic-monorepo 0.1.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.
@@ -0,0 +1,769 @@
1
+ import { parseArgs } from "node:util";
2
+ //#region ../../../libs/domains/scaffolding/dist/index.mjs
3
+ const catalog = [
4
+ {
5
+ id: "base",
6
+ label: "Workspace",
7
+ hint: "Workspace tooling and architecture without sample projects.",
8
+ status: "available",
9
+ sourcePaths: [
10
+ "package.json",
11
+ "pnpm-lock.yaml",
12
+ "pnpm-workspace.yaml",
13
+ "nx.json",
14
+ "tsconfig.base.json",
15
+ ".npmrc",
16
+ ".gitignore",
17
+ ".editorconfig",
18
+ ".oxlintrc.json",
19
+ ".oxfmtrc.json",
20
+ "LICENSE",
21
+ "docs/architecture.md",
22
+ "tools/check-boundaries.mjs",
23
+ ".github/workflows/ci.yml",
24
+ ".github/dependabot.yml"
25
+ ],
26
+ requires: [],
27
+ projectNames: ["workspace"],
28
+ nextCommands: [{
29
+ executable: "pnpm",
30
+ args: [
31
+ "nx",
32
+ "run-many",
33
+ "-t",
34
+ "typecheck",
35
+ "build",
36
+ "test",
37
+ "lint"
38
+ ]
39
+ }],
40
+ requirements: []
41
+ },
42
+ {
43
+ id: "web",
44
+ label: "Web · TypeScript + Vite",
45
+ hint: "A vanilla TypeScript + Vite browser starter with a shared search domain.",
46
+ status: "available",
47
+ sourcePaths: ["apps/web/example-app"],
48
+ requires: ["search-domain"],
49
+ projectNames: ["web-example-app"],
50
+ nextCommands: [{
51
+ executable: "pnpm",
52
+ args: [
53
+ "nx",
54
+ "run",
55
+ "web-example-app:build"
56
+ ]
57
+ }, {
58
+ executable: "pnpm",
59
+ args: [
60
+ "nx",
61
+ "run",
62
+ "web-example-app:dev"
63
+ ]
64
+ }],
65
+ requirements: []
66
+ },
67
+ {
68
+ id: "cli",
69
+ label: "CLI · Node + TypeScript",
70
+ hint: "A Node and TypeScript command-line starter with a shared search domain.",
71
+ status: "available",
72
+ sourcePaths: ["apps/cli/example-app"],
73
+ requires: ["search-domain"],
74
+ projectNames: ["cli-example-app"],
75
+ nextCommands: [{
76
+ executable: "pnpm",
77
+ args: [
78
+ "nx",
79
+ "run",
80
+ "cli-example-app:build"
81
+ ]
82
+ }, {
83
+ executable: "pnpm",
84
+ args: [
85
+ "nx",
86
+ "run",
87
+ "cli-example-app:start",
88
+ "--",
89
+ "demo"
90
+ ]
91
+ }],
92
+ requirements: []
93
+ },
94
+ {
95
+ id: "mac",
96
+ label: "Mac · SwiftUI",
97
+ hint: "A native SwiftUI and SwiftPM starter for macOS utilities and products.",
98
+ status: "available",
99
+ sourcePaths: [
100
+ "apps/mac/example-app",
101
+ ".swift-format",
102
+ ".swiftlint.yml",
103
+ "tools/swiftlint.sh"
104
+ ],
105
+ requires: ["mac-filesystem"],
106
+ projectNames: ["mac-example-app"],
107
+ nextCommands: [{
108
+ executable: "pnpm",
109
+ args: [
110
+ "nx",
111
+ "run",
112
+ "mac-example-app:build"
113
+ ]
114
+ }, {
115
+ executable: "pnpm",
116
+ args: [
117
+ "nx",
118
+ "run",
119
+ "mac-example-app:run"
120
+ ]
121
+ }],
122
+ requirements: ["macOS 14+ and Swift 6", "SwiftLint for lint checks"]
123
+ },
124
+ {
125
+ id: "search-domain",
126
+ label: "Search domain",
127
+ hint: "A pure TypeScript search query and result-ranking domain library.",
128
+ status: "available",
129
+ sourcePaths: ["libs/domains/search"],
130
+ requires: [],
131
+ projectNames: ["@domains/search"],
132
+ nextCommands: [{
133
+ executable: "pnpm",
134
+ args: [
135
+ "nx",
136
+ "run",
137
+ "@domains/search:build"
138
+ ]
139
+ }, {
140
+ executable: "pnpm",
141
+ args: [
142
+ "nx",
143
+ "run",
144
+ "@domains/search:test"
145
+ ]
146
+ }],
147
+ requirements: []
148
+ },
149
+ {
150
+ id: "mac-filesystem",
151
+ label: "Mac filesystem",
152
+ hint: "A SwiftPM filesystem locations library for macOS applications.",
153
+ status: "available",
154
+ sourcePaths: ["libs/platform/mac/filesystem"],
155
+ requires: [],
156
+ projectNames: ["mac-filesystem"],
157
+ nextCommands: [{
158
+ executable: "pnpm",
159
+ args: [
160
+ "nx",
161
+ "run",
162
+ "mac-filesystem:build"
163
+ ]
164
+ }, {
165
+ executable: "pnpm",
166
+ args: [
167
+ "nx",
168
+ "run",
169
+ "mac-filesystem:test"
170
+ ]
171
+ }],
172
+ requirements: ["macOS 14+ and Swift 6"]
173
+ },
174
+ {
175
+ id: "rust",
176
+ label: "Rust · independent search index",
177
+ hint: "An optional independent Rust library; no application bindings are included.",
178
+ status: "available",
179
+ sourcePaths: ["libs/rust/search-index", "rust-toolchain.toml"],
180
+ requires: [],
181
+ projectNames: ["search-index"],
182
+ nextCommands: [{
183
+ executable: "pnpm",
184
+ args: [
185
+ "nx",
186
+ "run",
187
+ "search-index:build"
188
+ ]
189
+ }, {
190
+ executable: "pnpm",
191
+ args: [
192
+ "nx",
193
+ "run",
194
+ "search-index:test"
195
+ ]
196
+ }],
197
+ requirements: ["Rust 1.97.1 with clippy and rustfmt"]
198
+ },
199
+ {
200
+ id: "react",
201
+ label: "React",
202
+ hint: "Coming soon: this template has no React starter yet.",
203
+ status: "coming-soon",
204
+ sourcePaths: [],
205
+ requires: [],
206
+ projectNames: [],
207
+ nextCommands: [],
208
+ requirements: []
209
+ },
210
+ {
211
+ id: "nextjs",
212
+ label: "Next.js",
213
+ hint: "Coming soon: this template has no Next.js starter or integration yet.",
214
+ status: "coming-soon",
215
+ sourcePaths: [],
216
+ requires: [],
217
+ projectNames: [],
218
+ nextCommands: [],
219
+ requirements: []
220
+ },
221
+ {
222
+ id: "vue",
223
+ label: "Vue",
224
+ hint: "Coming soon: this template has no Vue starter yet.",
225
+ status: "coming-soon",
226
+ sourcePaths: [],
227
+ requires: [],
228
+ projectNames: [],
229
+ nextCommands: [],
230
+ requirements: []
231
+ },
232
+ {
233
+ id: "sveltekit",
234
+ label: "SvelteKit",
235
+ hint: "Coming soon: this template has no SvelteKit starter or integration yet.",
236
+ status: "coming-soon",
237
+ sourcePaths: [],
238
+ requires: [],
239
+ projectNames: [],
240
+ nextCommands: [],
241
+ requirements: []
242
+ },
243
+ {
244
+ id: "astro",
245
+ label: "Astro",
246
+ hint: "Coming soon: this template has no Astro starter or integration yet.",
247
+ status: "coming-soon",
248
+ sourcePaths: [],
249
+ requires: [],
250
+ projectNames: [],
251
+ nextCommands: [],
252
+ requirements: []
253
+ },
254
+ {
255
+ id: "node-api",
256
+ label: "Node API",
257
+ hint: "Coming soon: this template has no Node API starter or integration yet.",
258
+ status: "coming-soon",
259
+ sourcePaths: [],
260
+ requires: [],
261
+ projectNames: [],
262
+ nextCommands: [],
263
+ requirements: []
264
+ },
265
+ {
266
+ id: "ios",
267
+ label: "iOS",
268
+ hint: "Coming soon: this template has no iOS starter or integration yet.",
269
+ status: "coming-soon",
270
+ sourcePaths: [],
271
+ requires: [],
272
+ projectNames: [],
273
+ nextCommands: [],
274
+ requirements: []
275
+ },
276
+ {
277
+ id: "android",
278
+ label: "Android",
279
+ hint: "Coming soon: this template has no Android starter or integration yet.",
280
+ status: "coming-soon",
281
+ sourcePaths: [],
282
+ requires: [],
283
+ projectNames: [],
284
+ nextCommands: [],
285
+ requirements: []
286
+ },
287
+ {
288
+ id: "expo",
289
+ label: "Expo",
290
+ hint: "Coming soon: this template has no Expo starter or integration yet.",
291
+ status: "coming-soon",
292
+ sourcePaths: [],
293
+ requires: [],
294
+ projectNames: [],
295
+ nextCommands: [],
296
+ requirements: []
297
+ },
298
+ {
299
+ id: "electron",
300
+ label: "Electron",
301
+ hint: "Coming soon: this template has no Electron starter or integration yet.",
302
+ status: "coming-soon",
303
+ sourcePaths: [],
304
+ requires: [],
305
+ projectNames: [],
306
+ nextCommands: [],
307
+ requirements: []
308
+ },
309
+ {
310
+ id: "tauri",
311
+ label: "Tauri",
312
+ hint: "Coming soon: this template has no Tauri starter or integration yet.",
313
+ status: "coming-soon",
314
+ sourcePaths: [],
315
+ requires: [],
316
+ projectNames: [],
317
+ nextCommands: [],
318
+ requirements: []
319
+ },
320
+ {
321
+ id: "rust-bindings",
322
+ label: "Rust bindings",
323
+ hint: "Coming soon: this template has no Rust application bindings yet.",
324
+ status: "coming-soon",
325
+ sourcePaths: [],
326
+ requires: [],
327
+ projectNames: [],
328
+ nextCommands: [],
329
+ requirements: []
330
+ }
331
+ ];
332
+ const presets = [
333
+ {
334
+ id: "web",
335
+ label: "Web · TypeScript + Vite",
336
+ hint: "Browser tools, dashboards, and web product frontends.",
337
+ apps: ["web"],
338
+ rust: false
339
+ },
340
+ {
341
+ id: "cli",
342
+ label: "CLI · Node + TypeScript",
343
+ hint: "Developer tools, automation, and internal command-line utilities.",
344
+ apps: ["cli"],
345
+ rust: false
346
+ },
347
+ {
348
+ id: "mac",
349
+ label: "Mac · SwiftUI",
350
+ hint: "Native Mac utilities and products using SwiftUI and SwiftPM.",
351
+ apps: ["mac"],
352
+ rust: false
353
+ },
354
+ {
355
+ id: "web-cli",
356
+ label: "Web + CLI",
357
+ hint: "A browser app and command-line app sharing one search domain.",
358
+ apps: ["web", "cli"],
359
+ rust: false
360
+ },
361
+ {
362
+ id: "full",
363
+ label: "Full stack",
364
+ hint: "Web, CLI, and Mac examples plus an independent Rust library.",
365
+ apps: [
366
+ "web",
367
+ "cli",
368
+ "mac"
369
+ ],
370
+ rust: true
371
+ },
372
+ {
373
+ id: "workspace",
374
+ label: "Workspace only",
375
+ hint: "Architecture-first tooling with no application yet.",
376
+ apps: [],
377
+ rust: false
378
+ }
379
+ ];
380
+ function isAppId(value) {
381
+ return value === "web" || value === "cli" || value === "mac";
382
+ }
383
+ function isPresetId(value) {
384
+ return value === "web" || value === "cli" || value === "mac" || value === "web-cli" || value === "full" || value === "workspace";
385
+ }
386
+ function isComingSoonId(value) {
387
+ return value === "react" || value === "nextjs" || value === "vue" || value === "sveltekit" || value === "astro" || value === "node-api" || value === "ios" || value === "android" || value === "expo" || value === "electron" || value === "tauri" || value === "rust-bindings";
388
+ }
389
+ var CreatorError = class extends Error {
390
+ code;
391
+ constructor(code, message, options) {
392
+ super(message, options);
393
+ this.name = "CreatorError";
394
+ this.code = code;
395
+ }
396
+ };
397
+ const NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
398
+ const CONFIG_KEYS = {
399
+ schemaVersion: true,
400
+ name: true,
401
+ preset: true,
402
+ apps: true,
403
+ rust: true,
404
+ install: true,
405
+ git: true,
406
+ templateVersion: true,
407
+ templateDigest: true
408
+ };
409
+ const OVERRIDE_KEYS = {
410
+ name: true,
411
+ preset: true,
412
+ apps: true,
413
+ rust: true,
414
+ install: true,
415
+ git: true
416
+ };
417
+ const APP_ORDER = [
418
+ "web",
419
+ "cli",
420
+ "mac"
421
+ ];
422
+ function isRecord(value) {
423
+ return typeof value === "object" && value !== null && !Array.isArray(value);
424
+ }
425
+ function hasOwn(value, key) {
426
+ return Object.prototype.hasOwnProperty.call(value, key);
427
+ }
428
+ function invalidConfig(message, cause) {
429
+ return new CreatorError("INVALID_CONFIG", message, cause === void 0 ? void 0 : { cause });
430
+ }
431
+ function invalidArgument(message, cause) {
432
+ return new CreatorError("INVALID_ARGUMENT", message, cause === void 0 ? void 0 : { cause });
433
+ }
434
+ function assertName(value, code) {
435
+ if (typeof value !== "string" || value.length === 0 || value.length > 100 || !NAME_PATTERN.test(value)) throw new CreatorError(code, "Name must be 1–100 characters of lower-kebab-case (a-z, 0-9, and single hyphens).");
436
+ return value;
437
+ }
438
+ /** Return whether a value is a valid lower-kebab-case repository name. */
439
+ function isValidName(value) {
440
+ return typeof value === "string" && value.length > 0 && value.length <= 100 && NAME_PATTERN.test(value);
441
+ }
442
+ function validatePinnedString(value, field) {
443
+ if (typeof value !== "string" || value.length === 0) throw invalidConfig(`${field} must be a non-empty string when provided.`);
444
+ return value;
445
+ }
446
+ function normalizeConfigApps(value) {
447
+ if (!Array.isArray(value)) throw invalidConfig("apps must be an array containing web, cli, and/or mac.");
448
+ const seen = /* @__PURE__ */ new Set();
449
+ const apps = [];
450
+ for (const item of value) {
451
+ if (!isAppId(item)) {
452
+ if (isComingSoonId(item)) throw new CreatorError("UNSUPPORTED_OPTION", `The ${item} capability is coming soon and cannot be selected.`);
453
+ throw invalidConfig(`Unknown app capability: ${String(item)}.`);
454
+ }
455
+ if (seen.has(item)) throw invalidConfig(`apps contains duplicate entry: ${item}.`);
456
+ seen.add(item);
457
+ apps.push(item);
458
+ }
459
+ return sortApps(apps);
460
+ }
461
+ function normalizeOverrideApps(value) {
462
+ try {
463
+ return normalizeConfigApps(value);
464
+ } catch (error) {
465
+ if (error instanceof CreatorError && error.code === "INVALID_CONFIG") throw invalidArgument(error.message, error);
466
+ throw error;
467
+ }
468
+ }
469
+ function sortApps(apps) {
470
+ return APP_ORDER.filter((app) => apps.includes(app));
471
+ }
472
+ /**
473
+ * Validate the complete JSON configuration shape. Unknown fields and malformed
474
+ * values are rejected rather than being ignored or merged into defaults.
475
+ */
476
+ function validateConfig(value) {
477
+ if (!isRecord(value)) throw invalidConfig("Configuration must be a JSON object.");
478
+ for (const key of Object.keys(value)) if (CONFIG_KEYS[key] !== true) throw invalidConfig(`Unknown configuration field: ${key}.`);
479
+ if (value["schemaVersion"] !== 1) throw invalidConfig("schemaVersion must be exactly 1.");
480
+ const result = { schemaVersion: 1 };
481
+ if (hasOwn(value, "name")) result.name = assertName(value["name"], "INVALID_CONFIG");
482
+ if (hasOwn(value, "preset")) {
483
+ const preset = value["preset"];
484
+ if (!isPresetId(preset)) {
485
+ if (isComingSoonId(preset)) throw new CreatorError("UNSUPPORTED_OPTION", `The ${String(preset)} capability is coming soon and cannot be selected.`);
486
+ throw invalidConfig(`Unknown preset: ${String(preset)}.`);
487
+ }
488
+ result.preset = preset;
489
+ }
490
+ if (hasOwn(value, "apps")) result.apps = normalizeConfigApps(value["apps"]);
491
+ if (hasOwn(value, "rust")) {
492
+ const rust = value["rust"];
493
+ if (typeof rust !== "boolean") throw invalidConfig("rust must be a boolean when provided.");
494
+ result.rust = rust;
495
+ }
496
+ if (hasOwn(value, "install")) {
497
+ const install = value["install"];
498
+ if (typeof install !== "boolean") throw invalidConfig("install must be a boolean when provided.");
499
+ result.install = install;
500
+ }
501
+ if (hasOwn(value, "git")) {
502
+ const git = value["git"];
503
+ if (typeof git !== "boolean") throw invalidConfig("git must be a boolean when provided.");
504
+ result.git = git;
505
+ }
506
+ if (hasOwn(value, "templateVersion")) result.templateVersion = validatePinnedString(value["templateVersion"], "templateVersion");
507
+ if (hasOwn(value, "templateDigest")) result.templateDigest = validatePinnedString(value["templateDigest"], "templateDigest");
508
+ if (hasOwn(value, "preset") && hasOwn(value, "apps")) throw invalidConfig("preset and apps cannot both be specified in the same configuration.");
509
+ return result;
510
+ }
511
+ function validateOverrides(value) {
512
+ if (!isRecord(value)) throw invalidArgument("Overrides must be an object.");
513
+ for (const key of Object.keys(value)) if (OVERRIDE_KEYS[key] !== true) throw invalidArgument(`Unknown override field: ${key}.`);
514
+ if (hasOwn(value, "name")) assertName(value["name"], "INVALID_ARGUMENT");
515
+ if (hasOwn(value, "preset")) {
516
+ const preset = value["preset"];
517
+ if (!isPresetId(preset)) {
518
+ if (isComingSoonId(preset)) throw new CreatorError("UNSUPPORTED_OPTION", `The ${String(preset)} capability is coming soon and cannot be selected.`);
519
+ throw invalidArgument(`Unknown preset: ${String(preset)}.`);
520
+ }
521
+ }
522
+ if (hasOwn(value, "apps")) normalizeOverrideApps(value["apps"]);
523
+ if (hasOwn(value, "rust") && typeof value["rust"] !== "boolean") throw invalidArgument("rust must be a boolean when provided.");
524
+ if (hasOwn(value, "install") && typeof value["install"] !== "boolean") throw invalidArgument("install must be a boolean when provided.");
525
+ if (hasOwn(value, "git") && typeof value["git"] !== "boolean") throw invalidArgument("git must be a boolean when provided.");
526
+ if (hasOwn(value, "preset") && hasOwn(value, "apps")) throw invalidArgument("preset and apps cannot both be specified as explicit overrides.");
527
+ return value;
528
+ }
529
+ function descriptorFor(id) {
530
+ const descriptor = catalog.find((entry) => entry.id === id);
531
+ if (!descriptor || descriptor.status !== "available") throw new CreatorError("INVALID_TEMPLATE", `Catalog is missing available component ${id}.`);
532
+ return descriptor;
533
+ }
534
+ function resolveClosure(apps, rust) {
535
+ const components = [];
536
+ const visited = /* @__PURE__ */ new Set();
537
+ const visiting = /* @__PURE__ */ new Set();
538
+ const visit = (id) => {
539
+ if (visited.has(id)) return;
540
+ if (visiting.has(id)) throw new CreatorError("INVALID_TEMPLATE", `Catalog dependency cycle includes ${id}.`);
541
+ visiting.add(id);
542
+ const descriptor = descriptorFor(id);
543
+ for (const required of descriptor.requires) {
544
+ if (!isComponentId(required)) throw new CreatorError("INVALID_TEMPLATE", `Catalog component ${id} requires unknown component ${String(required)}.`);
545
+ visit(required);
546
+ }
547
+ visiting.delete(id);
548
+ visited.add(id);
549
+ components.push(id);
550
+ };
551
+ visit("base");
552
+ for (const app of apps) visit(app);
553
+ if (rust) visit("rust");
554
+ return components;
555
+ }
556
+ function isComponentId(value) {
557
+ if (value === "base" || value === "search-domain" || value === "mac-filesystem" || value === "rust") return true;
558
+ return isAppId(value);
559
+ }
560
+ function presetFor(id) {
561
+ const descriptor = presets.find((entry) => entry.id === id);
562
+ if (!descriptor) throw new CreatorError("INVALID_TEMPLATE", `Catalog is missing preset ${id}.`);
563
+ return descriptor;
564
+ }
565
+ /**
566
+ * Resolve config and explicit flag overrides into one deterministic generation
567
+ * plan. Selection flags replace, rather than merge with, config selection.
568
+ */
569
+ function resolveOptions(config, overrides, fallbackName) {
570
+ const validatedConfig = validateConfig(config);
571
+ const validatedOverrides = validateOverrides(overrides);
572
+ const name = hasOwn(validatedOverrides, "name") ? assertName(validatedOverrides.name, "INVALID_ARGUMENT") : validatedConfig.name ?? assertName(fallbackName, "INVALID_ARGUMENT");
573
+ const overrideHasPreset = hasOwn(validatedOverrides, "preset");
574
+ const overrideHasApps = hasOwn(validatedOverrides, "apps");
575
+ const configHasPreset = hasOwn(validatedConfig, "preset");
576
+ const configHasApps = hasOwn(validatedConfig, "apps");
577
+ let apps;
578
+ let rustDefault;
579
+ if (overrideHasPreset) {
580
+ const preset = presetFor(validatedOverrides.preset);
581
+ apps = sortApps(preset.apps);
582
+ rustDefault = preset.rust;
583
+ } else if (overrideHasApps) {
584
+ apps = normalizeOverrideApps(validatedOverrides.apps);
585
+ rustDefault = false;
586
+ } else if (configHasPreset) {
587
+ const preset = presetFor(validatedConfig.preset);
588
+ apps = sortApps(preset.apps);
589
+ rustDefault = preset.rust;
590
+ } else if (configHasApps) {
591
+ apps = sortApps(validatedConfig.apps ?? []);
592
+ rustDefault = false;
593
+ } else {
594
+ const preset = presetFor("web");
595
+ apps = sortApps(preset.apps);
596
+ rustDefault = preset.rust;
597
+ }
598
+ const rust = hasOwn(validatedOverrides, "rust") ? validatedOverrides.rust : hasOwn(validatedConfig, "rust") ? validatedConfig.rust : rustDefault;
599
+ const install = hasOwn(validatedOverrides, "install") ? validatedOverrides.install : validatedConfig.install ?? false;
600
+ const git = hasOwn(validatedOverrides, "git") ? validatedOverrides.git : validatedConfig.git ?? false;
601
+ return {
602
+ name,
603
+ apps,
604
+ rust,
605
+ install,
606
+ git,
607
+ components: resolveClosure(apps, rust)
608
+ };
609
+ }
610
+ //#endregion
611
+ //#region src/args.ts
612
+ const comingSoon = /* @__PURE__ */ new Set([
613
+ "react",
614
+ "nextjs",
615
+ "vue",
616
+ "sveltekit",
617
+ "astro",
618
+ "node-api",
619
+ "ios",
620
+ "android",
621
+ "expo",
622
+ "electron",
623
+ "tauri",
624
+ "rust-bindings"
625
+ ]);
626
+ const appIds = /* @__PURE__ */ new Set([
627
+ "web",
628
+ "cli",
629
+ "mac"
630
+ ]);
631
+ const presetIds = /* @__PURE__ */ new Set([
632
+ "web",
633
+ "cli",
634
+ "mac",
635
+ "web-cli",
636
+ "full",
637
+ "workspace"
638
+ ]);
639
+ function usageError(message, cause) {
640
+ return new CreatorError("INVALID_ARGUMENT", message, cause === void 0 ? void 0 : { cause });
641
+ }
642
+ /** Parse the public command line without accepting unknown or repeated options. */
643
+ function parseCliArgs(argv) {
644
+ const config = {
645
+ args: [...argv],
646
+ options: {
647
+ name: { type: "string" },
648
+ preset: { type: "string" },
649
+ apps: { type: "string" },
650
+ config: { type: "string" },
651
+ rust: { type: "boolean" },
652
+ "no-rust": { type: "boolean" },
653
+ install: { type: "boolean" },
654
+ "no-install": { type: "boolean" },
655
+ git: { type: "boolean" },
656
+ "no-git": { type: "boolean" },
657
+ yes: {
658
+ type: "boolean",
659
+ short: "y"
660
+ },
661
+ "dry-run": { type: "boolean" },
662
+ json: { type: "boolean" },
663
+ plain: { type: "boolean" },
664
+ list: { type: "boolean" },
665
+ help: {
666
+ type: "boolean",
667
+ short: "h"
668
+ },
669
+ version: {
670
+ type: "boolean",
671
+ short: "v"
672
+ }
673
+ },
674
+ allowPositionals: true,
675
+ strict: true,
676
+ tokens: true
677
+ };
678
+ let parsed;
679
+ try {
680
+ parsed = parseArgs(config);
681
+ } catch (error) {
682
+ throw usageError(error instanceof Error ? error.message : "Invalid command-line arguments", error);
683
+ }
684
+ const seen = /* @__PURE__ */ new Set();
685
+ const values = /* @__PURE__ */ new Map();
686
+ for (const token of parsed.tokens) {
687
+ if (token.kind !== "option") continue;
688
+ const option = token;
689
+ const normalized = option.name === "h" ? "help" : option.name === "v" ? "version" : option.name === "y" ? "yes" : option.name;
690
+ if (normalized === "no-rust" || normalized === "no-install" || normalized === "no-git") {
691
+ const positive = normalized.slice(3);
692
+ if (seen.has(positive)) throw usageError(`Option --${positive} was specified more than once`);
693
+ seen.add(positive);
694
+ values.set(positive, false);
695
+ continue;
696
+ }
697
+ if (seen.has(normalized)) throw usageError(`Option --${normalized} was specified more than once`);
698
+ seen.add(normalized);
699
+ values.set(normalized, option.value ?? true);
700
+ }
701
+ if (parsed.positionals.length > 1) throw usageError("Expected at most one destination positional argument");
702
+ const getString = (name) => {
703
+ const value = values.get(name);
704
+ return typeof value === "string" ? value : void 0;
705
+ };
706
+ const getBoolean = (name) => {
707
+ const value = values.get(name);
708
+ return typeof value === "boolean" ? value : void 0;
709
+ };
710
+ const result = {
711
+ ...parsed.positionals[0] === void 0 ? {} : { destination: parsed.positionals[0] },
712
+ ...getString("name") === void 0 ? {} : { name: getString("name") },
713
+ ...getString("preset") === void 0 ? {} : { preset: getString("preset") },
714
+ ...getString("apps") === void 0 ? {} : { apps: getString("apps") },
715
+ ...getBoolean("rust") === void 0 ? {} : { rust: getBoolean("rust") },
716
+ ...getBoolean("install") === void 0 ? {} : { install: getBoolean("install") },
717
+ ...getBoolean("git") === void 0 ? {} : { git: getBoolean("git") },
718
+ ...getString("config") === void 0 ? {} : { config: getString("config") },
719
+ yes: getBoolean("yes") ?? false,
720
+ dryRun: getBoolean("dry-run") ?? false,
721
+ json: getBoolean("json") ?? false,
722
+ plain: getBoolean("plain") ?? false,
723
+ list: getBoolean("list") ?? false,
724
+ help: getBoolean("help") ?? false,
725
+ version: getBoolean("version") ?? false
726
+ };
727
+ if (result.help && (result.version || result.list || result.destination !== void 0)) throw usageError("--help cannot be combined with another command");
728
+ if (result.version && (result.list || result.destination !== void 0)) throw usageError("--version cannot be combined with another command");
729
+ if (result.list && result.destination !== void 0) throw usageError("--list does not accept a destination");
730
+ if (result.name !== void 0 && result.name.length === 0) throw usageError("--name cannot be empty");
731
+ if (result.preset !== void 0) parsePreset(result.preset);
732
+ if (result.apps !== void 0) parseApps(result.apps);
733
+ return result;
734
+ }
735
+ function parsePreset(value) {
736
+ if (!presetIds.has(value)) throw usageError(`Unknown preset ${JSON.stringify(value)}`);
737
+ return value;
738
+ }
739
+ function parseApps(value) {
740
+ if (value === "none") return [];
741
+ const parts = value.split(",");
742
+ if (parts.some((part) => part.length === 0)) throw usageError("--apps cannot contain empty values");
743
+ const selected = [];
744
+ const seen = /* @__PURE__ */ new Set();
745
+ for (const part of parts) {
746
+ if (seen.has(part)) throw usageError(`Duplicate app ${JSON.stringify(part)}`);
747
+ seen.add(part);
748
+ if (comingSoon.has(part)) throw new CreatorError("UNSUPPORTED_OPTION", `${part} is Coming soon and has no starter yet`);
749
+ if (!appIds.has(part)) throw usageError(`Unknown app ${JSON.stringify(part)}`);
750
+ selected.push(part);
751
+ }
752
+ return selected;
753
+ }
754
+ /** Turn command-line values into only the overrides explicitly supplied by a user. */
755
+ function toOverrides(args) {
756
+ const overrides = {};
757
+ if (args.name !== void 0) overrides.name = args.name;
758
+ if (args.preset !== void 0) overrides.preset = parsePreset(args.preset);
759
+ if (args.apps !== void 0) overrides.apps = parseApps(args.apps);
760
+ if (args.rust !== void 0) overrides.rust = args.rust;
761
+ if (args.install !== void 0) overrides.install = args.install;
762
+ if (args.git !== void 0) overrides.git = args.git;
763
+ return overrides;
764
+ }
765
+ function hasSelection(args) {
766
+ return args.preset !== void 0 || args.apps !== void 0;
767
+ }
768
+ //#endregion
769
+ export { toOverrides as a, isValidName as c, validateConfig as d, parsePreset as i, presets as l, parseApps as n, CreatorError as o, parseCliArgs as r, catalog as s, hasSelection as t, resolveOptions as u };