@glass-ui-kit/cli 0.2.4 → 1.0.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -44
  3. package/dist/index.js +874 -178
  4. package/package.json +60 -59
package/dist/index.js CHANGED
@@ -1,48 +1,150 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command3 } from "commander";
4
+ import { Command as Command6 } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import chalk from "chalk";
7
+ import chalk2 from "chalk";
8
8
  import { Command } from "commander";
9
+
10
+ // src/utils/init/options.ts
11
+ import path from "path";
12
+ var SUPPORTED_INIT_FRAMEWORKS = ["react", "vite", "next", "remix"];
13
+ function resolveInitOptions(options = {}, currentWorkingDirectory = process.cwd()) {
14
+ validateFrameworkOverride(options.framework);
15
+ return {
16
+ projectRoot: path.resolve(currentWorkingDirectory, options.cwd ?? "."),
17
+ cssOverride: options.css,
18
+ componentsAliasOverride: options.components,
19
+ utilsAliasOverride: options.utils,
20
+ frameworkOverride: options.framework,
21
+ force: options.force === true,
22
+ install: options.install !== false
23
+ };
24
+ }
25
+ function validateFrameworkOverride(framework) {
26
+ if (framework === void 0) {
27
+ return;
28
+ }
29
+ if (SUPPORTED_INIT_FRAMEWORKS.includes(framework)) {
30
+ return;
31
+ }
32
+ throw new Error(
33
+ `Unsupported framework "${framework}". Supported values: ${SUPPORTED_INIT_FRAMEWORKS.join(", ")}.`
34
+ );
35
+ }
36
+
37
+ // src/utils/init/paths.ts
9
38
  import path2 from "path";
39
+ function resolveInitPaths({
40
+ projectRoot,
41
+ hasSrc,
42
+ framework,
43
+ detectedCssPath,
44
+ cssOverride,
45
+ componentsAliasOverride,
46
+ utilsAliasOverride
47
+ }) {
48
+ let baseDir = hasSrc ? "src" : "";
49
+ if (framework === "remix") {
50
+ baseDir = "app";
51
+ }
52
+ let cssPath = cssOverride ?? detectedCssPath;
53
+ if (!cssPath) {
54
+ if (framework === "next") {
55
+ cssPath = hasSrc ? "src/app/globals.css" : "app/globals.css";
56
+ } else if (framework === "remix") {
57
+ cssPath = "app/app.css";
58
+ } else {
59
+ cssPath = hasSrc ? "src/index.css" : "index.css";
60
+ }
61
+ }
62
+ const defaultComponentsAlias = `${framework === "remix" ? "~" : "@"}/components/ui`;
63
+ const defaultUtilsAlias = `${framework === "remix" ? "~" : "@"}/lib/utils`;
64
+ const componentsAlias = componentsAliasOverride ?? defaultComponentsAlias;
65
+ const utilsAlias = utilsAliasOverride ?? defaultUtilsAlias;
66
+ const utilsRelativePath = resolveUtilsRelativePath(utilsAlias, baseDir);
67
+ const utilsPath = path2.join(projectRoot, utilsRelativePath);
68
+ const aliasPrefix = framework === "remix" ? "~" : "@";
69
+ return {
70
+ framework,
71
+ cssPath,
72
+ utilsRelativePath,
73
+ utilsPath,
74
+ aliasPrefix,
75
+ config: {
76
+ framework,
77
+ style: "default",
78
+ css: cssPath,
79
+ aliases: {
80
+ components: componentsAlias,
81
+ utils: utilsAlias
82
+ }
83
+ }
84
+ };
85
+ }
86
+ function resolveUtilsRelativePath(utilsAlias, baseDir) {
87
+ const aliasBody = stripAliasPrefix(utilsAlias);
88
+ const relativePath = baseDir && !aliasBody.startsWith(`${baseDir}/`) ? path2.posix.join(baseDir, aliasBody) : aliasBody;
89
+ return path2.posix.extname(relativePath) ? relativePath : `${relativePath}.ts`;
90
+ }
91
+ function stripAliasPrefix(alias) {
92
+ return alias.replace(/^[@~]\//, "");
93
+ }
94
+
95
+ // src/utils/init/run-init.ts
96
+ import chalk from "chalk";
10
97
 
11
98
  // src/utils/filesystem.ts
12
99
  import fs from "fs";
13
- import path from "path";
14
- async function writeFile(filePath, content) {
15
- const absolutePath = path.resolve(process.cwd(), filePath);
16
- const dir = path.dirname(absolutePath);
100
+ import path3 from "path";
101
+ function resolveFromBaseDir(filePath, baseDir = process.cwd()) {
102
+ return path3.resolve(baseDir, filePath);
103
+ }
104
+ async function writeFile(filePath, content, baseDir = process.cwd()) {
105
+ const absolutePath = resolveFromBaseDir(filePath, baseDir);
106
+ const dir = path3.dirname(absolutePath);
17
107
  if (!fs.existsSync(dir)) {
18
108
  await fs.promises.mkdir(dir, { recursive: true });
19
109
  }
20
110
  await fs.promises.writeFile(absolutePath, content, "utf-8");
21
111
  return absolutePath;
22
112
  }
23
- async function readFile(filePath) {
24
- const absolutePath = path.resolve(process.cwd(), filePath);
113
+ async function readFile(filePath, baseDir = process.cwd()) {
114
+ const absolutePath = resolveFromBaseDir(filePath, baseDir);
25
115
  if (!fs.existsSync(absolutePath)) {
26
116
  throw new Error(`File not found: ${filePath}`);
27
117
  }
28
118
  return await fs.promises.readFile(absolutePath, "utf-8");
29
119
  }
30
- function exists(filePath) {
31
- return fs.existsSync(path.resolve(process.cwd(), filePath));
120
+ function exists(filePath, baseDir = process.cwd()) {
121
+ return fs.existsSync(resolveFromBaseDir(filePath, baseDir));
32
122
  }
33
123
 
34
124
  // src/utils/get-project-info.ts
125
+ import path4 from "path";
35
126
  import { spawn } from "child_process";
36
- async function getPackageManager() {
37
- if (exists("bun.lockb")) return "bun";
38
- if (exists("pnpm-lock.yaml")) return "pnpm";
39
- if (exists("yarn.lock")) return "yarn";
127
+ function resolveProjectRoot(cwdOption, cwd = process.cwd()) {
128
+ return cwdOption ? path4.resolve(cwd, cwdOption) : cwd;
129
+ }
130
+ function getLockfilePath(projectRoot = process.cwd(), existsAtPath = exists) {
131
+ if (existsAtPath("bun.lock", projectRoot)) return "bun.lock";
132
+ if (existsAtPath("bun.lockb", projectRoot)) return "bun.lockb";
133
+ if (existsAtPath("pnpm-lock.yaml", projectRoot)) return "pnpm-lock.yaml";
134
+ if (existsAtPath("yarn.lock", projectRoot)) return "yarn.lock";
135
+ return null;
136
+ }
137
+ async function getPackageManager(projectRoot = process.cwd()) {
138
+ if (exists("bun.lock", projectRoot)) return "bun";
139
+ if (exists("bun.lockb", projectRoot)) return "bun";
140
+ if (exists("pnpm-lock.yaml", projectRoot)) return "pnpm";
141
+ if (exists("yarn.lock", projectRoot)) return "yarn";
40
142
  return "npm";
41
143
  }
42
- async function getFramework() {
43
- if (!exists("package.json")) return "unknown";
144
+ async function getFramework(projectRoot = process.cwd()) {
145
+ if (!exists("package.json", projectRoot)) return "unknown";
44
146
  try {
45
- const content = await readFile("package.json");
147
+ const content = await readFile("package.json", projectRoot);
46
148
  const pkg = JSON.parse(content);
47
149
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
48
150
  if (deps["@remix-run/react"] || deps["@remix-run/dev"] || deps["@react-router/dev"] || deps["@react-router/node"]) {
@@ -56,29 +158,26 @@ async function getFramework() {
56
158
  }
57
159
  return "unknown";
58
160
  }
59
- function getCssPath(framework) {
161
+ function getCssPath(framework, projectRoot = process.cwd()) {
60
162
  const paths = {
163
+ react: ["src/index.css", "src/main.css", "src/style.css", "index.css"],
61
164
  next: ["app/globals.css", "src/app/globals.css", "styles/globals.css"],
62
165
  vite: ["src/index.css", "src/main.css", "src/style.css"],
63
166
  astro: ["src/styles/global.css", "src/global.css"],
64
- remix: [
65
- "app/app.css",
66
- "app/tailwind.css",
67
- "app/globals.css",
68
- "app/styles/tailwind.css"
69
- ],
167
+ remix: ["app/app.css", "app/tailwind.css", "app/globals.css", "app/styles/tailwind.css"],
70
168
  unknown: ["src/index.css", "styles.css"]
71
169
  };
72
170
  for (const p of paths[framework] || []) {
73
- if (exists(p)) return p;
171
+ if (exists(p, projectRoot)) return p;
74
172
  }
75
173
  return paths[framework]?.[0] || null;
76
174
  }
77
- async function installDependencies(deps, pm) {
175
+ async function installDependencies(deps, pm, projectRoot = process.cwd()) {
78
176
  const installCmd = pm === "npm" ? "install" : "add";
79
177
  console.log(`Running ${pm} ${installCmd}...`);
80
178
  return new Promise((resolve, reject) => {
81
179
  const child = spawn(pm, [installCmd, ...deps], {
180
+ cwd: projectRoot,
82
181
  stdio: "inherit",
83
182
  shell: true
84
183
  });
@@ -287,113 +386,152 @@ export function cn(...inputs: ClassValue[]) {
287
386
  }
288
387
  `;
289
388
 
290
- // src/commands/init.ts
291
- var init = new Command().name("init").description("Initialize configuration and dependencies").option("-y, --yes", "Skip confirmation prompt", false).action(async (opts) => {
292
- try {
293
- console.log(chalk.bold("\nInitializing Glass UI..."));
294
- const framework = await getFramework();
295
- const pm = await getPackageManager();
296
- const cwd = process.cwd();
297
- const configPath = "glass.config.json";
298
- const hasSrc = exists("src");
299
- let baseDir = hasSrc ? "src" : "";
300
- if (framework === "remix") {
301
- baseDir = "app";
302
- }
303
- let cssPath = getCssPath(framework);
304
- if (!cssPath || framework === "next") {
305
- if (framework === "next") {
306
- cssPath = hasSrc ? "src/app/globals.css" : "app/globals.css";
307
- } else if (framework === "remix") {
308
- cssPath = "app/app.css";
309
- } else {
310
- cssPath = hasSrc ? "src/index.css" : "index.css";
311
- }
312
- }
313
- const utilsRelativePath = baseDir ? `${baseDir}/lib/utils.ts` : "lib/utils.ts";
314
- const utilsPath = path2.join(cwd, utilsRelativePath);
315
- const aliasPrefix = framework === "remix" ? "~" : "@";
316
- if (!exists(configPath)) {
317
- await writeFile(
318
- configPath,
319
- JSON.stringify(
320
- {
321
- framework,
322
- style: "default",
323
- css: cssPath,
324
- aliases: {
325
- components: `${aliasPrefix}/components/ui`,
326
- utils: `${aliasPrefix}/lib/utils`
327
- }
328
- },
329
- null,
330
- 2
331
- )
332
- );
333
- console.log(chalk.green(" Created glass.config.json"));
334
- } else {
335
- console.log(chalk.gray(" glass.config.json already exists."));
389
+ // src/utils/init/run-init.ts
390
+ var defaultRuntime = {
391
+ cwd: () => process.cwd(),
392
+ exists,
393
+ readFile,
394
+ writeFile,
395
+ getFramework,
396
+ getPackageManager,
397
+ getCssPath,
398
+ installDependencies,
399
+ log: console.log
400
+ };
401
+ async function runInitCommand(options = {}, runtime = defaultRuntime) {
402
+ const resolvedOptions = resolveInitOptions(options, runtime.cwd());
403
+ runtime.log(chalk.bold("\nInitializing Glass UI..."));
404
+ const framework = resolvedOptions.frameworkOverride ?? await runtime.getFramework(resolvedOptions.projectRoot);
405
+ const pm = await runtime.getPackageManager(resolvedOptions.projectRoot);
406
+ const configPath = "glass.config.json";
407
+ const hasSrc = runtime.exists("src", resolvedOptions.projectRoot);
408
+ const plan = resolveInitPaths({
409
+ projectRoot: resolvedOptions.projectRoot,
410
+ hasSrc,
411
+ framework,
412
+ detectedCssPath: runtime.getCssPath(framework, resolvedOptions.projectRoot),
413
+ cssOverride: resolvedOptions.cssOverride,
414
+ componentsAliasOverride: resolvedOptions.componentsAliasOverride,
415
+ utilsAliasOverride: resolvedOptions.utilsAliasOverride
416
+ });
417
+ const writes = [
418
+ {
419
+ filePath: configPath,
420
+ content: JSON.stringify(plan.config, null, 2),
421
+ label: "glass.config.json"
422
+ },
423
+ {
424
+ filePath: plan.utilsRelativePath,
425
+ content: UTILS_CN,
426
+ label: plan.utilsRelativePath
427
+ },
428
+ {
429
+ filePath: plan.cssPath,
430
+ content: `@import "tailwindcss";
431
+
432
+ ${GLASS_BASE_STYLES}`,
433
+ label: plan.cssPath
336
434
  }
337
- if (!exists(utilsPath)) {
338
- await writeFile(utilsPath, UTILS_CN);
339
- console.log(chalk.green(` Created ${utilsRelativePath}`));
340
- } else {
341
- console.log(chalk.gray(` ${utilsRelativePath} already exists.`));
435
+ ];
436
+ for (const write of writes.slice(0, 2)) {
437
+ const alreadyExists = runtime.exists(write.filePath, resolvedOptions.projectRoot);
438
+ if (alreadyExists && !resolvedOptions.force) {
439
+ runtime.log(chalk.yellow(` Skipped ${write.label} (already exists)`));
440
+ continue;
342
441
  }
343
- let cssContent = "";
344
- try {
345
- if (exists(cssPath)) {
346
- cssContent = await readFile(cssPath);
347
- } else {
348
- console.log(chalk.yellow(` Creating new CSS file at ${cssPath}`));
349
- }
350
- } catch (e) {
351
- }
352
- if (!cssContent.includes("--glass-bg")) {
353
- const newCssContent = `@import "tailwindcss";
354
-
355
- ${GLASS_BASE_STYLES}`;
356
- await writeFile(cssPath, newCssContent);
357
- console.log(
358
- chalk.green(
359
- ` Overwrote ${cssPath} with Tailwind import and glass tokens`
360
- )
361
- );
442
+ await runtime.writeFile(write.filePath, write.content, resolvedOptions.projectRoot);
443
+ runtime.log(
444
+ alreadyExists ? chalk.green(` Replaced ${write.label}`) : chalk.green(` Created ${write.label}`)
445
+ );
446
+ }
447
+ const cssWrite = writes[2];
448
+ const cssAlreadyExists = runtime.exists(cssWrite.filePath, resolvedOptions.projectRoot);
449
+ if (!cssAlreadyExists) {
450
+ await runtime.writeFile(cssWrite.filePath, cssWrite.content, resolvedOptions.projectRoot);
451
+ runtime.log(chalk.green(` Created ${cssWrite.label}`));
452
+ } else if (resolvedOptions.force) {
453
+ await runtime.writeFile(cssWrite.filePath, cssWrite.content, resolvedOptions.projectRoot);
454
+ runtime.log(chalk.green(` Replaced ${cssWrite.label}`));
455
+ } else {
456
+ const existingCss = await runtime.readFile(cssWrite.filePath, resolvedOptions.projectRoot) ?? "";
457
+ if (existingCss.includes("--glass-bg")) {
458
+ runtime.log(chalk.yellow(` Skipped ${cssWrite.label} (already exists)`));
362
459
  } else {
363
- console.log(chalk.gray(` Tokens already present in ${cssPath}`));
460
+ const updatedCss = `${existingCss.trimEnd()}
461
+
462
+ ${GLASS_BASE_STYLES}
463
+ `;
464
+ await runtime.writeFile(cssWrite.filePath, updatedCss, resolvedOptions.projectRoot);
465
+ runtime.log(chalk.green(` Updated ${cssWrite.label} with Glass tokens`));
364
466
  }
365
- console.log(
366
- chalk.cyan(
367
- ` Installing dependencies (clsx, tailwind-merge, lucide-react)...`
368
- )
369
- );
370
- await installDependencies(["clsx", "tailwind-merge", "lucide-react"], pm);
371
- const runCommand = pm === "bun" ? "bunx" : pm === "pnpm" ? "pnpm dlx" : "npx";
372
- console.log(chalk.bold.green("\nSetup complete."));
373
- console.log(`Try adding a component:
374
- `);
375
- console.log(
376
- chalk.cyan(` ${runCommand} @glass-ui-kit/cli@latest add card`)
467
+ }
468
+ if (resolvedOptions.install) {
469
+ runtime.log(chalk.cyan(` Installing dependencies (clsx, tailwind-merge, lucide-react)...`));
470
+ await runtime.installDependencies(
471
+ ["clsx", "tailwind-merge", "lucide-react"],
472
+ pm,
473
+ resolvedOptions.projectRoot
377
474
  );
378
- console.log("");
475
+ } else {
476
+ runtime.log(chalk.gray(" Skipped dependency installation (--no-install)."));
477
+ }
478
+ const runCommand = pm === "bun" ? "bunx" : pm === "pnpm" ? "pnpm dlx" : "npx";
479
+ runtime.log(chalk.bold.green("\nSetup complete."));
480
+ runtime.log(chalk.gray(` Project root: ${resolvedOptions.projectRoot}`));
481
+ runtime.log(chalk.gray(` Framework: ${plan.framework}`));
482
+ runtime.log("Try adding a component:\n");
483
+ runtime.log(chalk.cyan(` ${runCommand} @glass-ui-kit/cli@latest add card`));
484
+ runtime.log("");
485
+ }
486
+
487
+ // src/commands/init.ts
488
+ var InitCommandError = class extends Error {
489
+ };
490
+ async function runInitCommand2(options = {}, runtime = defaultRuntime) {
491
+ try {
492
+ await runInitCommand(options, runtime);
379
493
  } catch (error) {
380
- console.error(chalk.red("\nInitialization failed:"));
381
494
  if (error instanceof Error) {
382
- console.error(chalk.gray(error.message));
383
- } else {
384
- console.error(chalk.gray(String(error)));
495
+ throw new InitCommandError(error.message);
385
496
  }
386
- process.exit(1);
497
+ throw new InitCommandError(String(error));
387
498
  }
388
- });
499
+ }
500
+ function createInitAction(deps = {
501
+ runInitCommand: runInitCommand2,
502
+ error: console.error,
503
+ exit: process.exit
504
+ }) {
505
+ return async (options = {}) => {
506
+ try {
507
+ await deps.runInitCommand({
508
+ cwd: options.cwd,
509
+ css: options.css,
510
+ components: options.components,
511
+ utils: options.utils,
512
+ framework: options.framework,
513
+ force: options.force === true,
514
+ install: options.install
515
+ });
516
+ } catch (error) {
517
+ deps.error(chalk2.red("\nInitialization failed:"));
518
+ if (error instanceof Error) {
519
+ deps.error(chalk2.gray(error.message));
520
+ } else {
521
+ deps.error(chalk2.gray(String(error)));
522
+ }
523
+ deps.exit(1);
524
+ }
525
+ };
526
+ }
527
+ var init = new Command().name("init").description("Initialize configuration and dependencies").option("-y, --yes", "Skip confirmation prompt", false).option("--cwd <path>", "Run the command against another project directory").option("--css <path>", "Use a custom CSS file instead of autodetecting one").option("--components <alias>", "Set the components alias saved in glass.config.json").option("--utils <alias>", "Set the utils alias saved in glass.config.json").option("--framework <name>", "Force the framework instead of autodetecting it").option("--force", "Overwrite generated files if they already exist").option("--no-install", "Skip dependency installation").action(createInitAction());
389
528
 
390
529
  // src/commands/add.ts
391
- import chalk2 from "chalk";
530
+ import chalk4 from "chalk";
392
531
  import { Command as Command2 } from "commander";
393
- import path4 from "path";
394
532
 
395
533
  // src/utils/registry.ts
396
- import path3 from "path";
534
+ import path5 from "path";
397
535
  import os from "os";
398
536
  import fs2 from "fs/promises";
399
537
 
@@ -427,8 +565,8 @@ var registryIndexSchema = z.array(registryItemSchema);
427
565
 
428
566
  // src/utils/registry.ts
429
567
  var DEFAULT_REGISTRY_URL = "https://ui-glass.vercel.app/registry.json";
430
- var CACHE_DIR = path3.join(os.homedir(), ".glass-ui");
431
- var CACHE_FILE = path3.join(CACHE_DIR, "registry.json");
568
+ var CACHE_DIR = path5.join(os.homedir(), ".glass-ui");
569
+ var CACHE_FILE = path5.join(CACHE_DIR, "registry.json");
432
570
  var CACHE_TTL = 1e3 * 60 * 60 * 24;
433
571
  function getRegistryUrl() {
434
572
  return process.env.GLASS_UI_REGISTRY_URL || DEFAULT_REGISTRY_URL;
@@ -459,6 +597,12 @@ async function writeCache(data) {
459
597
  } catch {
460
598
  }
461
599
  }
600
+ function getErrorMessage(error, fallback) {
601
+ if (error instanceof Error && error.message.length > 0) {
602
+ return error.message;
603
+ }
604
+ return fallback;
605
+ }
462
606
  async function fetchRegistry() {
463
607
  const cachedRegistry = await readCache();
464
608
  if (cachedRegistry) {
@@ -469,27 +613,22 @@ async function fetchRegistry() {
469
613
  try {
470
614
  response = await fetch(url);
471
615
  } catch (error) {
472
- throw new Error(
473
- "Network error: Unable to connect to registry. Check your internet connection."
474
- );
616
+ throw new Error(getErrorMessage(error, "Network error: Unable to connect to registry."));
475
617
  }
476
618
  if (!response.ok) {
477
- throw new Error(
478
- `Registry unavailable. URL: ${url} (Status: ${response.status})`
479
- );
619
+ throw new Error(`Registry unavailable. URL: ${url} (Status: ${response.status})`);
480
620
  }
481
621
  let data;
482
622
  try {
483
623
  data = await response.json();
484
- } catch {
485
- throw new Error("Invalid response: Registry returned non-JSON data.");
624
+ } catch (error) {
625
+ throw new Error(getErrorMessage(error, "Invalid response: Registry returned non-JSON data."));
486
626
  }
487
627
  const parsed = registryIndexSchema.safeParse(data);
488
628
  if (!parsed.success) {
489
- console.error("Debug: Schema validation errors:", parsed.error.flatten());
490
- throw new Error(
491
- "Incompatible registry version. Your CLI might be outdated. Please try updating @glass-ui-kit/cli."
492
- );
629
+ const issue = parsed.error.issues[0];
630
+ const location = issue?.path.length ? issue.path.join(".") : "registry";
631
+ throw new Error(`${issue?.message ?? "Schema validation failed"}: ${location}`);
493
632
  }
494
633
  await writeCache(parsed.data);
495
634
  return parsed.data;
@@ -497,6 +636,85 @@ async function fetchRegistry() {
497
636
  function getItem(registry, name) {
498
637
  return registry.find((item) => item.name === name);
499
638
  }
639
+ function getItems(registry, names) {
640
+ const items = [];
641
+ const missing = [];
642
+ for (const name of names) {
643
+ const item = getItem(registry, name);
644
+ if (item) {
645
+ items.push(item);
646
+ continue;
647
+ }
648
+ missing.push(name);
649
+ }
650
+ return { items, missing };
651
+ }
652
+
653
+ // src/utils/add/selection.ts
654
+ function dedupeNames(names) {
655
+ const uniqueNames = [];
656
+ const seen = /* @__PURE__ */ new Set();
657
+ for (const name of names) {
658
+ if (seen.has(name)) {
659
+ continue;
660
+ }
661
+ seen.add(name);
662
+ uniqueNames.push(name);
663
+ }
664
+ return uniqueNames;
665
+ }
666
+ function resolveAddSelection(registry, requestedNames, selectAll) {
667
+ if (selectAll && requestedNames.length > 0) {
668
+ return { ok: false, reason: "invalid-combination" };
669
+ }
670
+ if (selectAll) {
671
+ return { ok: true, items: registry };
672
+ }
673
+ const uniqueRequestedNames = dedupeNames(requestedNames);
674
+ if (uniqueRequestedNames.length === 0) {
675
+ return { ok: false, reason: "empty-selection" };
676
+ }
677
+ const { items, missing } = getItems(registry, uniqueRequestedNames);
678
+ if (missing.length > 0) {
679
+ return { ok: false, reason: "missing-components", names: missing };
680
+ }
681
+ return { ok: true, items };
682
+ }
683
+
684
+ // src/utils/add/dependencies.ts
685
+ function collectDependencies(items) {
686
+ const dependencies = [];
687
+ const seen = /* @__PURE__ */ new Set();
688
+ for (const item of items) {
689
+ for (const dependency of item.dependencies || []) {
690
+ if (seen.has(dependency)) {
691
+ continue;
692
+ }
693
+ seen.add(dependency);
694
+ dependencies.push(dependency);
695
+ }
696
+ }
697
+ return dependencies;
698
+ }
699
+
700
+ // src/utils/add/paths.ts
701
+ function resolveTargetDir(config, hasSrc, explicitPath) {
702
+ if (explicitPath) {
703
+ return explicitPath;
704
+ }
705
+ const targetDirAlias = config.aliases.components || "@/components/ui";
706
+ const relativeAliasPath = targetDirAlias.replace(/^[@~]\//, "");
707
+ if (config.framework === "remix") {
708
+ return `./app/${relativeAliasPath}`;
709
+ }
710
+ if (hasSrc) {
711
+ return `./src/${relativeAliasPath}`;
712
+ }
713
+ return `./${relativeAliasPath}`;
714
+ }
715
+
716
+ // src/utils/add/planner.ts
717
+ import path6 from "path";
500
718
 
501
719
  // src/utils/transformers.ts
502
720
  function transformImports(content, config) {
@@ -519,63 +737,541 @@ function transformImports(content, config) {
519
737
  return transformed;
520
738
  }
521
739
 
522
- // src/commands/add.ts
523
- var add = new Command2().name("add").description("Add a component to your project").argument("<component>", "The component to add").action(async (componentName) => {
524
- try {
525
- if (!exists("glass.config.json")) {
526
- console.error(chalk2.red("Configuration file not found."));
527
- console.log(chalk2.gray("Please run the init command first:"));
528
- console.log(chalk2.cyan(" npx @glass-ui-kit/cli@latest init"));
529
- process.exit(1);
530
- }
531
- const config = JSON.parse(await readFile("glass.config.json"));
532
- const pm = await getPackageManager();
533
- console.log(chalk2.bold(`Fetching component: ${componentName}...`));
534
- const registry = await fetchRegistry();
535
- const item = getItem(registry, componentName);
536
- if (!item) {
537
- console.error(chalk2.red(`Component '${componentName}' not found.`));
538
- process.exit(1);
740
+ // src/utils/add/planner.ts
741
+ function buildWritePlan(items, config, hasSrc, options) {
742
+ const targetDir = resolveTargetDir(config, hasSrc, options.path);
743
+ return items.flatMap(
744
+ (item) => item.files.filter((file) => Boolean(file.content)).map((file) => {
745
+ const fileName = path6.basename(file.path);
746
+ const filePath = path6.join(targetDir, fileName);
747
+ return {
748
+ filePath,
749
+ content: transformImports(file.content || "", config),
750
+ action: !options.overwrite && options.exists(filePath) ? "skip-existing" : "write"
751
+ };
752
+ })
753
+ );
754
+ }
755
+
756
+ // src/utils/add/run-add.ts
757
+ import path7 from "path";
758
+ import chalk3 from "chalk";
759
+ var defaultRuntime2 = {
760
+ cwd: () => process.cwd(),
761
+ exists,
762
+ readFile,
763
+ writeFile,
764
+ fetchRegistry,
765
+ getPackageManager,
766
+ installDependencies,
767
+ log: console.log
768
+ };
769
+ function buildMissingComponentsMessage(names) {
770
+ return `Components not found: ${names.join(", ")}.`;
771
+ }
772
+ function resolveProjectRoot2(cwdOption, cwd) {
773
+ return cwdOption ? path7.resolve(cwd, cwdOption) : cwd;
774
+ }
775
+ async function runAddCommand(componentNames = [], options = {}, runtime = defaultRuntime2) {
776
+ const projectRoot = resolveProjectRoot2(options.cwd, runtime.cwd?.() ?? process.cwd());
777
+ if (options.depsOnly && options.install === false) {
778
+ throw new Error("invalid-install-combination");
779
+ }
780
+ if (!runtime.exists("glass.config.json", projectRoot)) {
781
+ throw new Error("config-not-found");
782
+ }
783
+ const config = JSON.parse(await runtime.readFile("glass.config.json", projectRoot));
784
+ runtime.log(chalk3.bold("Fetching components..."));
785
+ const registry = await runtime.fetchRegistry();
786
+ const selection = resolveAddSelection(registry, componentNames, Boolean(options.all));
787
+ if (!selection.ok) {
788
+ if (selection.reason === "invalid-combination") {
789
+ throw new Error("invalid-combination");
539
790
  }
540
- const hasSrc = exists("src");
541
- const targetDirAlias = config.aliases.components || "@/components/ui";
542
- const relativeAliasPath = targetDirAlias.replace(/^[@~]\//, "");
543
- let targetDir = "";
544
- if (config.framework === "remix") {
545
- targetDir = `./app/${relativeAliasPath}`;
546
- } else if (hasSrc) {
547
- targetDir = `./src/${relativeAliasPath}`;
548
- } else {
549
- targetDir = `./${relativeAliasPath}`;
791
+ if (selection.reason === "missing-components") {
792
+ throw new Error(buildMissingComponentsMessage(selection.names || []));
550
793
  }
551
- for (const file of item.files) {
552
- const fileName = path4.basename(file.path);
553
- const filePath = path4.join(targetDir, fileName);
554
- if (!file.content) {
794
+ throw new Error("empty-selection");
795
+ }
796
+ const shouldWrite = options.depsOnly !== true;
797
+ const shouldInstall = options.install !== false;
798
+ if (shouldWrite) {
799
+ const plannedWrites = buildWritePlan(
800
+ selection.items,
801
+ config,
802
+ runtime.exists("src", projectRoot),
803
+ {
804
+ exists: (filePath) => runtime.exists(filePath, projectRoot),
805
+ overwrite: options.overwrite,
806
+ path: options.path
807
+ }
808
+ );
809
+ for (const file of plannedWrites) {
810
+ if (file.action === "skip-existing") {
811
+ runtime.log(chalk3.yellow(` Skipped ${file.filePath} (already exists)`));
555
812
  continue;
556
813
  }
557
- const transformedContent = transformImports(file.content, config);
558
- await writeFile(filePath, transformedContent);
559
- console.log(chalk2.green(` Created ${filePath}`));
814
+ await runtime.writeFile(file.filePath, file.content, projectRoot);
815
+ runtime.log(chalk3.green(` Created ${file.filePath}`));
560
816
  }
561
- if (item.dependencies?.length) {
562
- console.log(chalk2.cyan(` Installing dependencies...`));
563
- await installDependencies(item.dependencies, pm);
564
- }
565
- console.log(chalk2.bold.green(`
566
- Done.`));
817
+ }
818
+ const dependencies = collectDependencies(selection.items);
819
+ if (shouldInstall && dependencies.length > 0) {
820
+ const pm = await runtime.getPackageManager(projectRoot);
821
+ runtime.log(chalk3.cyan(" Installing dependencies..."));
822
+ await runtime.installDependencies(dependencies, pm, projectRoot);
823
+ }
824
+ runtime.log(chalk3.bold.green("\nDone."));
825
+ }
826
+
827
+ // src/commands/add.ts
828
+ var AddCommandError = class extends Error {
829
+ constructor(code, message) {
830
+ super(message);
831
+ this.code = code;
832
+ }
833
+ code;
834
+ };
835
+ async function runAddCommand2(componentNames = [], options = {}, runtime = defaultRuntime2) {
836
+ try {
837
+ await runAddCommand(componentNames, options, runtime);
567
838
  } catch (error) {
568
- console.error(chalk2.red("\nOperation failed:"));
569
839
  if (error instanceof Error) {
570
- console.error(chalk2.gray(` ${error.message}`));
840
+ if (error.message === "config-not-found") {
841
+ throw new AddCommandError("config-not-found", "Configuration file not found.");
842
+ }
843
+ if (error.message === "invalid-combination") {
844
+ throw new AddCommandError(
845
+ "invalid-combination",
846
+ "Cannot combine --all with specific component names."
847
+ );
848
+ }
849
+ if (error.message === "invalid-install-combination") {
850
+ throw new AddCommandError(
851
+ "invalid-combination",
852
+ "Cannot combine --deps-only with --no-install."
853
+ );
854
+ }
855
+ if (error.message === "empty-selection") {
856
+ throw new AddCommandError(
857
+ "empty-selection",
858
+ "Please specify at least one component or use --all."
859
+ );
860
+ }
861
+ if (error.message.startsWith("Components not found:")) {
862
+ throw new AddCommandError("missing-components", error.message);
863
+ }
571
864
  }
572
- process.exit(1);
865
+ throw error;
573
866
  }
867
+ }
868
+ function createAddAction(deps = {
869
+ runAddCommand: runAddCommand2,
870
+ error: console.error,
871
+ log: console.log,
872
+ exit: process.exit
873
+ }) {
874
+ return async (componentNames = [], options = {}) => {
875
+ try {
876
+ await deps.runAddCommand(componentNames, options);
877
+ } catch (error) {
878
+ if (error instanceof AddCommandError) {
879
+ if (error.code === "config-not-found") {
880
+ deps.error(chalk4.red(error.message));
881
+ deps.log(chalk4.gray("Please run the init command first:"));
882
+ deps.log(chalk4.cyan(" npx @glass-ui-kit/cli@latest init"));
883
+ deps.exit(1);
884
+ }
885
+ deps.error(chalk4.red(error.message));
886
+ deps.exit(1);
887
+ }
888
+ deps.error(chalk4.red("\nOperation failed:"));
889
+ if (error instanceof Error) {
890
+ deps.error(chalk4.gray(` ${error.message}`));
891
+ }
892
+ deps.exit(1);
893
+ }
894
+ };
895
+ }
896
+ var add = new Command2().name("add").description("Add one or more components to your project").argument("[components...]", "The components to add").option("--all", "Add all available components").option("--overwrite", "Replace existing component files").option("--path <dir>", "Use a custom output directory").option("--no-install", "Skip dependency installation").option("--deps-only", "Install dependencies without writing component files").option("--cwd <path>", "Run the command against another project directory").action(createAddAction());
897
+
898
+ // src/commands/list.ts
899
+ import chalk5 from "chalk";
900
+ import { Command as Command3 } from "commander";
901
+
902
+ // src/utils/list/run-list.ts
903
+ var defaultRuntime3 = {
904
+ fetchRegistry
905
+ };
906
+ async function runListCommand(options = {}, runtime = defaultRuntime3) {
907
+ const registry = await runtime.fetchRegistry();
908
+ if (options.json) {
909
+ return JSON.stringify(registry, null, 2);
910
+ }
911
+ return registry.map((item) => item.name).join("\n");
912
+ }
913
+
914
+ // src/commands/list.ts
915
+ async function runListCommand2(options = {}, runtime = defaultRuntime3) {
916
+ return runListCommand(options, runtime);
917
+ }
918
+ function createListAction(deps = {
919
+ runListCommand: runListCommand2,
920
+ error: console.error,
921
+ log: console.log,
922
+ exit: process.exit
923
+ }) {
924
+ return async (options = {}) => {
925
+ try {
926
+ const output = await deps.runListCommand({ json: options.json === true });
927
+ if (output.length > 0) {
928
+ deps.log(output);
929
+ }
930
+ } catch (error) {
931
+ deps.error(chalk5.red("\nList failed:"));
932
+ if (error instanceof Error) {
933
+ deps.error(chalk5.gray(error.message));
934
+ } else {
935
+ deps.error(chalk5.gray(String(error)));
936
+ }
937
+ deps.exit(1);
938
+ }
939
+ };
940
+ }
941
+ var list = new Command3().name("list").description("List available components from the registry").option("--json", "Output the validated registry payload as JSON", false).action(createListAction());
942
+
943
+ // src/commands/info.ts
944
+ import chalk6 from "chalk";
945
+ import { Command as Command4 } from "commander";
946
+
947
+ // src/utils/info/format-info.ts
948
+ function formatDependencySection(label, values) {
949
+ return `${label}: ${values && values.length > 0 ? values.join(", ") : "None"}`;
950
+ }
951
+ function formatInfo(item) {
952
+ const lines = [
953
+ `Name: ${item.name}`,
954
+ `Type: ${item.type}`,
955
+ "Files:",
956
+ ...item.files.map((file) => `- ${file.path} (${file.type})`),
957
+ formatDependencySection("Dependencies", item.dependencies),
958
+ formatDependencySection("Dev Dependencies", item.devDependencies),
959
+ formatDependencySection("Registry Dependencies", item.registryDependencies)
960
+ ];
961
+ if (item.meta?.requiresBlur !== void 0) {
962
+ lines.push(`Requires Blur: ${item.meta.requiresBlur}`);
963
+ }
964
+ return lines.join("\n");
965
+ }
966
+
967
+ // src/utils/info/run-info.ts
968
+ var defaultRuntime4 = {
969
+ fetchRegistry,
970
+ getItem
971
+ };
972
+ async function runInfoCommand(componentName, options = {}, runtime = defaultRuntime4) {
973
+ const registry = await runtime.fetchRegistry();
974
+ const item = runtime.getItem(registry, componentName);
975
+ if (!item) {
976
+ throw new Error(`Component not found: "${componentName}"`);
977
+ }
978
+ if (options.json) {
979
+ return JSON.stringify(item, null, 2);
980
+ }
981
+ return formatInfo(item);
982
+ }
983
+
984
+ // src/commands/info.ts
985
+ async function runInfoCommand2(componentName, options = {}, runtime = defaultRuntime4) {
986
+ return runInfoCommand(componentName, options, runtime);
987
+ }
988
+ function createInfoAction(deps = {
989
+ runInfoCommand: runInfoCommand2,
990
+ error: console.error,
991
+ log: console.log,
992
+ exit: process.exit
993
+ }) {
994
+ return async (componentName, options = {}) => {
995
+ try {
996
+ const output = await deps.runInfoCommand(componentName, { json: options.json === true });
997
+ if (output.length > 0) {
998
+ deps.log(output);
999
+ }
1000
+ } catch (error) {
1001
+ deps.error(chalk6.red("\nInfo failed:"));
1002
+ if (error instanceof Error) {
1003
+ deps.error(chalk6.gray(error.message));
1004
+ } else {
1005
+ deps.error(chalk6.gray(String(error)));
1006
+ }
1007
+ deps.exit(1);
1008
+ }
1009
+ };
1010
+ }
1011
+ var info = new Command4().name("info").description("Show details for a single registry component").argument("<component>", "The exact component name to inspect").option("--json", "Output the validated registry item as JSON", false).action(createInfoAction());
1012
+
1013
+ // src/commands/doctor.ts
1014
+ import chalk7 from "chalk";
1015
+ import { Command as Command5 } from "commander";
1016
+
1017
+ // src/utils/doctor/format-doctor.ts
1018
+ function formatCheck(check) {
1019
+ const icon = check.status === "ok" ? "\u2713" : check.status === "warn" ? "!" : "\u2716";
1020
+ const lines = [`${icon} ${check.id} \u2014 ${check.summary}`];
1021
+ if (check.detail) {
1022
+ lines.push(` Next step: ${check.detail}`);
1023
+ }
1024
+ return lines.join("\n");
1025
+ }
1026
+ function formatDoctorReport(report) {
1027
+ const aliasLine = report.config.aliases ? `- Aliases: components=${report.config.aliases.components}, utils=${report.config.aliases.utils}` : "- Aliases: missing";
1028
+ return [
1029
+ "Glass UI Doctor",
1030
+ `Project root: ${report.projectRoot}`,
1031
+ `Init readiness: ${report.readiness.init ? "ready" : "not ready"}`,
1032
+ `Add readiness: ${report.readiness.add ? "ready" : "not ready"}`,
1033
+ "",
1034
+ "Detected state:",
1035
+ `- Framework: ${report.framework.value} (${report.framework.source})`,
1036
+ `- Package manager: ${report.packageManager.value} (${report.packageManager.source})`,
1037
+ `- Config: ${report.config.path} (${report.config.source})`,
1038
+ `- CSS: ${report.css.path ?? "unresolved"} (${report.css.source}, ${report.css.exists ? "exists" : "missing"})`,
1039
+ aliasLine,
1040
+ "",
1041
+ "Checks:",
1042
+ ...report.checks.map(formatCheck)
1043
+ ].join("\n");
1044
+ }
1045
+
1046
+ // src/utils/doctor/inspect-project.ts
1047
+ import { z as z2 } from "zod";
1048
+ var configSchema = z2.object({
1049
+ framework: z2.enum(["react", "next", "vite", "astro", "remix", "unknown"]),
1050
+ style: z2.string(),
1051
+ css: z2.string().min(1),
1052
+ aliases: z2.object({
1053
+ components: z2.string().min(1),
1054
+ utils: z2.string().min(1)
1055
+ })
574
1056
  });
1057
+ function buildChecks(report) {
1058
+ const checks = [];
1059
+ if (report.config.source === "detected") {
1060
+ checks.push({ id: "config", status: "ok", summary: "Found valid glass.config.json." });
1061
+ } else if (report.config.source === "missing") {
1062
+ checks.push({
1063
+ id: "config",
1064
+ status: "error",
1065
+ summary: "glass.config.json was not found.",
1066
+ detail: "Run `glass-ui init` in this project before running `glass-ui add`."
1067
+ });
1068
+ } else {
1069
+ checks.push({
1070
+ id: "config",
1071
+ status: "error",
1072
+ summary: "glass.config.json is invalid.",
1073
+ detail: report.config.error ?? "Fix the config JSON and try doctor again."
1074
+ });
1075
+ }
1076
+ if (report.framework.source === "detected") {
1077
+ checks.push({
1078
+ id: "framework",
1079
+ status: "ok",
1080
+ summary: `Detected framework: ${report.framework.value}.`
1081
+ });
1082
+ } else {
1083
+ checks.push({
1084
+ id: "framework",
1085
+ status: "warn",
1086
+ summary: `Could not confidently detect the framework; using ${report.framework.value}.`,
1087
+ detail: "Check package.json dependencies or pass an explicit framework during `glass-ui init`."
1088
+ });
1089
+ }
1090
+ if (report.packageManager.source === "detected") {
1091
+ checks.push({
1092
+ id: "package-manager",
1093
+ status: "ok",
1094
+ summary: `Detected package manager: ${report.packageManager.value}.`
1095
+ });
1096
+ } else {
1097
+ checks.push({
1098
+ id: "package-manager",
1099
+ status: "warn",
1100
+ summary: `No lockfile found; defaulting to ${report.packageManager.value}.`,
1101
+ detail: "Create or commit the lockfile used by this project to confirm the package manager."
1102
+ });
1103
+ }
1104
+ if (report.css.path && report.css.exists && report.css.source === "detected") {
1105
+ checks.push({
1106
+ id: "css",
1107
+ status: "ok",
1108
+ summary: `CSS file is available at ${report.css.path}.`
1109
+ });
1110
+ } else if (report.css.path && report.css.exists) {
1111
+ checks.push({
1112
+ id: "css",
1113
+ status: "warn",
1114
+ summary: `CSS path was inferred as ${report.css.path}.`,
1115
+ detail: "Save the CSS path in glass.config.json to confirm it explicitly."
1116
+ });
1117
+ } else if (report.css.path) {
1118
+ checks.push({
1119
+ id: "css",
1120
+ status: "warn",
1121
+ summary: `CSS file is missing at ${report.css.path}.`,
1122
+ detail: "Create the CSS file or update glass.config.json to point at the correct path."
1123
+ });
1124
+ } else {
1125
+ checks.push({
1126
+ id: "css",
1127
+ status: "warn",
1128
+ summary: "Could not resolve a CSS file path.",
1129
+ detail: "Set the css field in glass.config.json or rerun `glass-ui init`."
1130
+ });
1131
+ }
1132
+ if (report.config.aliases) {
1133
+ checks.push({
1134
+ id: "aliases",
1135
+ status: "ok",
1136
+ summary: "Detected components and utils aliases."
1137
+ });
1138
+ } else {
1139
+ checks.push({
1140
+ id: "aliases",
1141
+ status: "error",
1142
+ summary: "Components and utils aliases are missing.",
1143
+ detail: "Save both aliases in glass.config.json so generated imports resolve correctly."
1144
+ });
1145
+ }
1146
+ if (report.readiness.add) {
1147
+ checks.push({ id: "add-readiness", status: "ok", summary: "Project is ready for add." });
1148
+ } else {
1149
+ checks.push({
1150
+ id: "add-readiness",
1151
+ status: "error",
1152
+ summary: "Project is not ready for add.",
1153
+ detail: "Create a valid glass.config.json with aliases before adding components."
1154
+ });
1155
+ }
1156
+ return checks;
1157
+ }
1158
+ async function inspectProject(options = {}, runtime) {
1159
+ const projectRoot = resolveProjectRoot(options.cwd, runtime.cwd());
1160
+ if (!runtime.exists(".", projectRoot)) {
1161
+ throw new Error(
1162
+ `Could not inspect directory "${projectRoot}": path does not exist or is not accessible.`
1163
+ );
1164
+ }
1165
+ const hasPackageJson = runtime.exists("package.json", projectRoot);
1166
+ const frameworkValue = await runtime.getFramework(projectRoot);
1167
+ const frameworkSource = hasPackageJson && frameworkValue !== "unknown" ? "detected" : "inferred";
1168
+ const packageManagerValue = await runtime.getPackageManager(projectRoot);
1169
+ const packageManagerSource = getLockfilePath(projectRoot, runtime.exists) ? "detected" : "inferred";
1170
+ let config = {
1171
+ path: "glass.config.json",
1172
+ source: "missing"
1173
+ };
1174
+ if (runtime.exists("glass.config.json", projectRoot)) {
1175
+ try {
1176
+ const parsed = configSchema.parse(
1177
+ JSON.parse(await runtime.readFile("glass.config.json", projectRoot))
1178
+ );
1179
+ config = {
1180
+ path: "glass.config.json",
1181
+ source: "detected",
1182
+ css: parsed.css,
1183
+ aliases: parsed.aliases
1184
+ };
1185
+ } catch (error) {
1186
+ config = {
1187
+ path: "glass.config.json",
1188
+ source: "invalid",
1189
+ error: error instanceof Error ? `Invalid config: ${error.message}` : "Invalid config."
1190
+ };
1191
+ }
1192
+ }
1193
+ const cssPath = config.css ?? runtime.getCssPath(frameworkValue, projectRoot);
1194
+ const cssSource = config.css ? "detected" : cssPath ? "inferred" : "missing";
1195
+ const cssExists = cssPath ? runtime.exists(cssPath, projectRoot) : false;
1196
+ const readiness = {
1197
+ init: true,
1198
+ add: config.source === "detected" && Boolean(config.aliases?.components) && Boolean(config.aliases?.utils)
1199
+ };
1200
+ const reportWithoutChecks = {
1201
+ projectRoot,
1202
+ framework: { value: frameworkValue, source: frameworkSource },
1203
+ packageManager: { value: packageManagerValue, source: packageManagerSource },
1204
+ config,
1205
+ css: {
1206
+ path: cssPath,
1207
+ exists: cssExists,
1208
+ source: cssSource
1209
+ },
1210
+ readiness
1211
+ };
1212
+ return {
1213
+ ...reportWithoutChecks,
1214
+ checks: buildChecks(reportWithoutChecks)
1215
+ };
1216
+ }
1217
+
1218
+ // src/utils/doctor/run-doctor.ts
1219
+ var defaultRuntime5 = {
1220
+ cwd: () => process.cwd(),
1221
+ exists,
1222
+ readFile,
1223
+ writeFile,
1224
+ getFramework,
1225
+ getPackageManager,
1226
+ getCssPath,
1227
+ installDependencies,
1228
+ inspectProject,
1229
+ formatDoctorReport
1230
+ };
1231
+ async function runDoctorCommand(options = {}, runtime = defaultRuntime5) {
1232
+ const resolvedRuntime = { ...defaultRuntime5, ...runtime };
1233
+ const report = await resolvedRuntime.inspectProject(options, resolvedRuntime);
1234
+ if (options.json) {
1235
+ return JSON.stringify(report, null, 2);
1236
+ }
1237
+ return resolvedRuntime.formatDoctorReport(report);
1238
+ }
1239
+
1240
+ // src/commands/doctor.ts
1241
+ async function runDoctorCommand2(options = {}, runtime = defaultRuntime5) {
1242
+ return runDoctorCommand(options, runtime);
1243
+ }
1244
+ function createDoctorAction(deps = {
1245
+ runDoctorCommand: runDoctorCommand2,
1246
+ error: console.error,
1247
+ log: console.log,
1248
+ exit: process.exit
1249
+ }) {
1250
+ return async (options = {}) => {
1251
+ try {
1252
+ const output = await deps.runDoctorCommand({ cwd: options.cwd, json: options.json === true });
1253
+ if (output.length > 0) {
1254
+ deps.log(output);
1255
+ }
1256
+ } catch (error) {
1257
+ deps.error(chalk7.red("\nDoctor failed:"));
1258
+ if (error instanceof Error) {
1259
+ deps.error(chalk7.gray(error.message));
1260
+ } else {
1261
+ deps.error(chalk7.gray(String(error)));
1262
+ }
1263
+ deps.exit(1);
1264
+ }
1265
+ };
1266
+ }
1267
+ var doctor = new Command5().name("doctor").description("Diagnose whether a project is ready to use the CLI").option("--cwd <path>", "Run the command against another project directory").option("--json", "Output the diagnostic report as JSON", false).action(createDoctorAction());
575
1268
 
576
1269
  // src/index.ts
577
- var program = new Command3();
578
- program.name("glass-ui").description("The Glass UI CLI - Add glassmorphism components to your app").version("0.0.1");
1270
+ var program = new Command6();
1271
+ program.name("glass-ui").description("The Glass UI CLI - Add glassmorphism components to your app").version("0.2.5");
579
1272
  program.addCommand(init);
580
1273
  program.addCommand(add);
1274
+ program.addCommand(list);
1275
+ program.addCommand(info);
1276
+ program.addCommand(doctor);
581
1277
  program.parse(process.argv);