@metacells/mcellui-cli 0.1.5 → 0.2.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 (2) hide show
  1. package/dist/index.js +426 -224
  2. package/package.json +9 -1
package/dist/index.js CHANGED
@@ -2,10 +2,84 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command as Command9 } from "commander";
5
+ import chalk11 from "chalk";
6
+
7
+ // src/utils/errors.ts
8
+ import chalk from "chalk";
9
+ function handleError(error) {
10
+ process.stderr.write("\n");
11
+ process.stderr.write(chalk.red(`Error: ${error.message}
12
+ `));
13
+ if (error.hint) {
14
+ process.stderr.write("\n");
15
+ process.stderr.write(chalk.dim(error.hint + "\n"));
16
+ }
17
+ process.stderr.write("\n");
18
+ process.exit(error.exitCode ?? 1);
19
+ }
20
+ var errors = {
21
+ /**
22
+ * Error when no valid project is found
23
+ */
24
+ noProject() {
25
+ return handleError({
26
+ message: "Could not find a valid Expo/React Native project",
27
+ hint: "Run this command from a project directory containing package.json",
28
+ code: "NO_PROJECT",
29
+ exitCode: 1
30
+ });
31
+ },
32
+ /**
33
+ * Error when project is not initialized with mcellui
34
+ */
35
+ notInitialized() {
36
+ return handleError({
37
+ message: "Project not initialized",
38
+ hint: "Run: npx mcellui init",
39
+ code: "NOT_INITIALIZED",
40
+ exitCode: 1
41
+ });
42
+ },
43
+ /**
44
+ * Error when component registry fetch fails
45
+ */
46
+ registryFetch(cause) {
47
+ return handleError({
48
+ message: "Failed to fetch component registry",
49
+ hint: cause ? `${cause}
50
+
51
+ Check your internet connection and try again` : "Check your internet connection and try again",
52
+ code: "REGISTRY_FETCH_FAILED",
53
+ exitCode: 1
54
+ });
55
+ },
56
+ /**
57
+ * Error when component is not found in registry
58
+ */
59
+ componentNotFound(name) {
60
+ return handleError({
61
+ message: `Component "${name}" not found in registry`,
62
+ hint: "Run: npx mcellui list",
63
+ code: "COMPONENT_NOT_FOUND",
64
+ exitCode: 1
65
+ });
66
+ },
67
+ /**
68
+ * Error when configuration is invalid
69
+ */
70
+ configInvalid(detail) {
71
+ return handleError({
72
+ message: "Invalid configuration",
73
+ hint: detail || "Check mcellui.config.ts",
74
+ code: "CONFIG_INVALID",
75
+ exitCode: 1
76
+ });
77
+ }
78
+ };
5
79
 
6
80
  // src/commands/init.ts
7
81
  import { Command } from "commander";
8
- import chalk2 from "chalk";
82
+ import chalk3 from "chalk";
9
83
  import ora from "ora";
10
84
  import prompts from "prompts";
11
85
  import fs2 from "fs-extra";
@@ -16,7 +90,7 @@ import fs from "fs-extra";
16
90
  import path from "path";
17
91
  import { fileURLToPath } from "url";
18
92
  import createJiti from "jiti";
19
- import chalk from "chalk";
93
+ import chalk2 from "chalk";
20
94
 
21
95
  // src/utils/config-schema.ts
22
96
  import { z } from "zod";
@@ -115,11 +189,11 @@ function validateConfig(config) {
115
189
  if (result.success) {
116
190
  return { success: true, data: result.data };
117
191
  }
118
- const errors = result.error.issues.map((issue) => {
192
+ const errors2 = result.error.issues.map((issue) => {
119
193
  const path12 = issue.path.join(".");
120
194
  return path12 ? `${path12}: ${issue.message}` : issue.message;
121
195
  });
122
- return { success: false, errors };
196
+ return { success: false, errors: errors2 };
123
197
  }
124
198
  function validateConfigOrThrow(config, configPath) {
125
199
  const result = validateConfig(config);
@@ -129,7 +203,7 @@ function validateConfigOrThrow(config, configPath) {
129
203
  `Invalid configuration in ${configPath}:
130
204
  ${errorList}
131
205
 
132
- See https://nativeui.dev/docs/config for valid options.`
206
+ See https://mcellui.dev/docs/config for valid options.`
133
207
  );
134
208
  }
135
209
  return result.data;
@@ -238,7 +312,7 @@ async function getConfig(projectRoot) {
238
312
  return resolveConfig(validatedConfig);
239
313
  } catch (error) {
240
314
  if (error instanceof Error && error.message.includes("Invalid configuration")) {
241
- console.error(chalk.red(error.message));
315
+ console.error(chalk2.red(error.message));
242
316
  throw error;
243
317
  }
244
318
  throw error;
@@ -259,10 +333,10 @@ async function loadTsConfig(configPath) {
259
333
  return resolveConfig(validatedConfig);
260
334
  } catch (error) {
261
335
  if (error instanceof Error && error.message.includes("Invalid configuration")) {
262
- console.error(chalk.red(error.message));
336
+ console.error(chalk2.red(error.message));
263
337
  throw error;
264
338
  }
265
- console.error(chalk.red("Failed to load config:"), error);
339
+ console.error(chalk2.red("Failed to load config:"), error);
266
340
  return getDefaultConfig();
267
341
  }
268
342
  }
@@ -274,10 +348,10 @@ async function loadJsConfig(configPath) {
274
348
  return resolveConfig(validatedConfig);
275
349
  } catch (error) {
276
350
  if (error instanceof Error && error.message.includes("Invalid configuration")) {
277
- console.error(chalk.red(error.message));
351
+ console.error(chalk2.red(error.message));
278
352
  throw error;
279
353
  }
280
- console.error(chalk.red("Failed to load config:"), error);
354
+ console.error(chalk2.red("Failed to load config:"), error);
281
355
  return getDefaultConfig();
282
356
  }
283
357
  }
@@ -311,27 +385,25 @@ var initCommand = new Command().name("init").description("Initialize mcellui in
311
385
  const cwd = path2.resolve(options.cwd);
312
386
  const projectRoot = await getProjectRoot(cwd);
313
387
  if (!projectRoot) {
314
- console.log(chalk2.red("Could not find a valid Expo/React Native project."));
315
- console.log(chalk2.dim("Make sure you run this command in a project with package.json"));
316
- process.exit(1);
388
+ errors.noProject();
317
389
  }
318
390
  console.log();
319
- console.log(chalk2.bold("Welcome to mcellui!"));
320
- console.log(chalk2.dim("The copy-paste component library for Expo/React Native"));
391
+ console.log(chalk3.bold("Welcome to mcellui!"));
392
+ console.log(chalk3.dim("The copy-paste component library for Expo/React Native"));
321
393
  console.log();
322
394
  const projectType = await detectProjectType(projectRoot);
323
- console.log(chalk2.dim(`Detected: ${projectType}`));
395
+ console.log(chalk3.dim(`Detected: ${projectType}`));
324
396
  const configPath = path2.join(projectRoot, "mcellui.config.ts");
325
397
  const legacyConfigPath = path2.join(projectRoot, "nativeui.config.ts");
326
398
  if (await fs2.pathExists(configPath)) {
327
- console.log(chalk2.yellow("Project already initialized."));
328
- console.log(chalk2.dim(`Config found at: ${configPath}`));
399
+ console.log(chalk3.yellow("Project already initialized."));
400
+ console.log(chalk3.dim(`Config found at: ${configPath}`));
329
401
  return;
330
402
  }
331
403
  if (await fs2.pathExists(legacyConfigPath)) {
332
- console.log(chalk2.yellow("Project already initialized with legacy config."));
333
- console.log(chalk2.dim(`Config found at: ${legacyConfigPath}`));
334
- console.log(chalk2.dim("Consider renaming to mcellui.config.ts"));
404
+ console.log(chalk3.yellow("Project already initialized with legacy config."));
405
+ console.log(chalk3.dim(`Config found at: ${legacyConfigPath}`));
406
+ console.log(chalk3.dim("Consider renaming to mcellui.config.ts"));
335
407
  return;
336
408
  }
337
409
  let config = {
@@ -364,6 +436,9 @@ var initCommand = new Command().name("init").description("Initialize mcellui in
364
436
  ]
365
437
  }
366
438
  ]);
439
+ if (response.componentsPath === void 0) {
440
+ process.exit(0);
441
+ }
367
442
  config = { ...config, ...response };
368
443
  }
369
444
  spinner.start("Creating configuration...");
@@ -402,7 +477,7 @@ export default defineConfig({
402
477
  // },
403
478
 
404
479
  // ============================================
405
- // CLI Configuration (used by npx nativeui add)
480
+ // CLI Configuration (used by npx mcellui add)
406
481
  // ============================================
407
482
 
408
483
  // Path where components will be installed
@@ -472,25 +547,27 @@ export function cn(...inputs: StyleInput[]): Style {
472
547
  );
473
548
  spinner.succeed("Utilities installed");
474
549
  console.log();
475
- console.log(chalk2.green("Success!") + " mcellui initialized.");
550
+ console.log(chalk3.green("Success!") + " mcellui initialized.");
476
551
  console.log();
477
552
  console.log("Next steps:");
478
- console.log(chalk2.dim(" 1."), "Add your first component:");
479
- console.log(chalk2.cyan(" npx mcellui add button"));
553
+ console.log(chalk3.dim(" 1."), "Add your first component:");
554
+ console.log(chalk3.cyan(" npx mcellui add button"));
480
555
  console.log();
481
- console.log(chalk2.dim(" 2."), "Browse available components:");
482
- console.log(chalk2.cyan(" npx mcellui list"));
556
+ console.log(chalk3.dim(" 2."), "Browse available components:");
557
+ console.log(chalk3.cyan(" npx mcellui list"));
483
558
  console.log();
484
559
  } catch (error) {
485
560
  spinner.fail("Failed to initialize");
486
- console.error(error);
487
- process.exit(1);
561
+ handleError({
562
+ message: "Initialization failed",
563
+ hint: error instanceof Error ? error.message : "Check file permissions and try again"
564
+ });
488
565
  }
489
566
  });
490
567
 
491
568
  // src/commands/add.ts
492
569
  import { Command as Command2 } from "commander";
493
- import chalk3 from "chalk";
570
+ import chalk4 from "chalk";
494
571
  import ora2 from "ora";
495
572
  import prompts2 from "prompts";
496
573
  import fs5 from "fs-extra";
@@ -508,6 +585,54 @@ var REGISTRY_URL = hasEnvOverride ? process.env.MCELLUI_REGISTRY_URL || process.
508
585
  function getLocalRegistryPath() {
509
586
  return path3.resolve(__dirname2, "..", "..", "registry");
510
587
  }
588
+ function sleep(ms) {
589
+ return new Promise((resolve) => setTimeout(resolve, ms));
590
+ }
591
+ function isNetworkError(error) {
592
+ if (!error) return false;
593
+ if (error instanceof DOMException && error.name === "AbortError") {
594
+ return true;
595
+ }
596
+ const err = error;
597
+ const errorCodes = ["ECONNRESET", "ENOTFOUND", "ETIMEDOUT", "ECONNREFUSED", "ECONNABORTED"];
598
+ if (err.code && errorCodes.includes(err.code)) {
599
+ return true;
600
+ }
601
+ if (err.message && typeof err.message === "string") {
602
+ const message = err.message.toLowerCase();
603
+ if (errorCodes.some((code) => message.includes(code.toLowerCase()))) {
604
+ return true;
605
+ }
606
+ }
607
+ if (err.cause) {
608
+ return isNetworkError(err.cause);
609
+ }
610
+ return false;
611
+ }
612
+ async function fetchWithRetry(url, maxRetries = 3) {
613
+ let lastError;
614
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
615
+ try {
616
+ const controller = new AbortController();
617
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
618
+ const response = await fetch(url, {
619
+ signal: controller.signal
620
+ });
621
+ clearTimeout(timeoutId);
622
+ return response;
623
+ } catch (error) {
624
+ lastError = error;
625
+ if (!isNetworkError(error) || attempt === maxRetries) {
626
+ throw error;
627
+ }
628
+ const baseDelay = Math.min(1e3 * Math.pow(2, attempt - 1), 5e3);
629
+ const jitter = Math.random() * 200;
630
+ const delay = baseDelay + jitter;
631
+ await sleep(delay);
632
+ }
633
+ }
634
+ throw lastError;
635
+ }
511
636
  function isLocalMode() {
512
637
  if (REGISTRY_URL) return false;
513
638
  const localPath = getLocalRegistryPath();
@@ -525,18 +650,23 @@ async function getLocalRegistry() {
525
650
  const registry = await fs3.readJson(registryPath);
526
651
  return registry.components;
527
652
  } catch (error) {
528
- console.error("Failed to load local registry:", error);
529
- return [];
653
+ throw new Error(
654
+ "Failed to load local registry: " + (error instanceof Error ? error.message : String(error))
655
+ );
530
656
  }
531
657
  }
532
658
  async function getRemoteRegistry() {
533
659
  try {
534
- const response = await fetch(`${REGISTRY_URL}/registry.json`);
660
+ const response = await fetchWithRetry(`${REGISTRY_URL}/registry.json`);
661
+ if (!response.ok) {
662
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
663
+ }
535
664
  const registry = await response.json();
536
665
  return registry.components;
537
666
  } catch (error) {
538
- console.error("Failed to fetch remote registry:", error);
539
- return [];
667
+ throw new Error(
668
+ "Failed to fetch registry: " + (error instanceof Error ? error.message : String(error))
669
+ );
540
670
  }
541
671
  }
542
672
  async function fetchComponent(name) {
@@ -571,7 +701,12 @@ async function fetchLocalComponent(item) {
571
701
  async function fetchRemoteComponent(item) {
572
702
  const files = await Promise.all(
573
703
  item.files.map(async (filePath) => {
574
- const response = await fetch(`${REGISTRY_URL}/${filePath}`);
704
+ const response = await fetchWithRetry(`${REGISTRY_URL}/${filePath}`);
705
+ if (!response.ok) {
706
+ throw new Error(
707
+ `Failed to fetch ${filePath}: HTTP ${response.status} ${response.statusText}`
708
+ );
709
+ }
575
710
  const content = await response.text();
576
711
  const name = path3.basename(filePath);
577
712
  return { name, content };
@@ -754,35 +889,36 @@ var addCommand = new Command2().name("add").description("Add a component to your
754
889
  const cwd = path5.resolve(options.cwd);
755
890
  const projectRoot = await getProjectRoot(cwd);
756
891
  if (!projectRoot) {
757
- console.log(chalk3.red("Could not find a valid project."));
758
- console.log(chalk3.dim("Run `npx nativeui init` first."));
759
- process.exit(1);
892
+ errors.noProject();
760
893
  }
761
894
  const config = await getConfig(projectRoot);
762
895
  if (!config) {
763
- console.log(chalk3.red("Project not initialized."));
764
- console.log(chalk3.dim("Run `npx nativeui init` first."));
765
- process.exit(1);
896
+ errors.notInitialized();
766
897
  }
767
898
  if (!components.length) {
768
899
  const registry2 = await getRegistry();
769
900
  if (!registry2.length) {
770
- console.log(chalk3.red("No components found in registry."));
771
- process.exit(1);
901
+ handleError({
902
+ message: "No components found in registry",
903
+ hint: "Check your internet connection or try again later"
904
+ });
772
905
  }
773
906
  const { selected } = await prompts2({
774
907
  type: "multiselect",
775
908
  name: "selected",
776
909
  message: "Which components would you like to add?",
777
910
  choices: registry2.map((item) => ({
778
- title: `${item.name} ${chalk3.dim(`(${item.status})`)}`,
911
+ title: `${item.name} ${chalk4.dim(`(${item.status})`)}`,
779
912
  value: item.name,
780
913
  description: item.description
781
914
  })),
782
915
  hint: "- Space to select, Enter to confirm"
783
916
  });
917
+ if (selected === void 0) {
918
+ process.exit(0);
919
+ }
784
920
  if (!selected?.length) {
785
- console.log(chalk3.dim("No components selected."));
921
+ console.log(chalk4.dim("No components selected."));
786
922
  return;
787
923
  }
788
924
  components = selected;
@@ -792,8 +928,11 @@ var addCommand = new Command2().name("add").description("Add a component to your
792
928
  const { resolved, circular } = resolveDependencies(components, registry);
793
929
  if (circular) {
794
930
  spinner.fail("Circular dependency detected");
795
- console.log(chalk3.red(` ${formatCircularError(circular)}`));
796
- process.exit(1);
931
+ handleError({
932
+ message: `Circular dependency: ${formatCircularError(circular)}`,
933
+ hint: "Check component dependencies for cycles",
934
+ code: "CIRCULAR_DEPENDENCY"
935
+ });
797
936
  }
798
937
  spinner.stop();
799
938
  const componentsDir = path5.join(projectRoot, config.componentsPath);
@@ -801,19 +940,19 @@ var addCommand = new Command2().name("add").description("Add a component to your
801
940
  const installedNames = new Set(getInstalledNames(installedFiles));
802
941
  const toInstall = options.overwrite ? resolved : resolved.filter((name) => !installedNames.has(name));
803
942
  if (toInstall.length === 0) {
804
- console.log(chalk3.yellow("All components are already installed."));
805
- console.log(chalk3.dim("Use --overwrite to reinstall."));
943
+ console.log(chalk4.yellow("All components are already installed."));
944
+ console.log(chalk4.dim("Use --overwrite to reinstall."));
806
945
  return;
807
946
  }
808
947
  const requested = new Set(components);
809
948
  const dependencies = toInstall.filter((name) => !requested.has(name));
810
949
  console.log();
811
- console.log(chalk3.bold("Adding components:"));
950
+ console.log(chalk4.bold("Adding components:"));
812
951
  for (const name of toInstall) {
813
952
  if (requested.has(name)) {
814
- console.log(chalk3.dim(` - ${name}`));
953
+ console.log(chalk4.dim(` - ${name}`));
815
954
  } else {
816
- console.log(chalk3.dim(` - ${name} ${chalk3.cyan("(dependency)")}`));
955
+ console.log(chalk4.dim(` - ${name} ${chalk4.cyan("(dependency)")}`));
817
956
  }
818
957
  }
819
958
  console.log();
@@ -824,19 +963,24 @@ var addCommand = new Command2().name("add").description("Add a component to your
824
963
  message: `Add ${dependencies.length} additional dependencies?`,
825
964
  initial: true
826
965
  });
966
+ if (confirm === void 0) {
967
+ process.exit(0);
968
+ }
827
969
  if (!confirm) {
828
- console.log(chalk3.dim("Cancelled."));
970
+ console.log(chalk4.dim("Cancelled."));
829
971
  return;
830
972
  }
831
973
  }
832
974
  const allDependencies = [];
833
975
  const allDevDependencies = [];
976
+ let failCount = 0;
834
977
  for (const componentName of toInstall) {
835
978
  spinner.start(`Fetching ${componentName}...`);
836
979
  try {
837
980
  const component = await fetchComponent(componentName);
838
981
  if (!component) {
839
982
  spinner.fail(`Component "${componentName}" not found`);
983
+ failCount++;
840
984
  continue;
841
985
  }
842
986
  const targetDir = path5.join(projectRoot, config.componentsPath);
@@ -859,36 +1003,42 @@ var addCommand = new Command2().name("add").description("Add a component to your
859
1003
  }
860
1004
  } catch (error) {
861
1005
  spinner.fail(`Failed to add ${componentName}`);
862
- console.error(chalk3.dim(String(error)));
1006
+ console.error(chalk4.dim(String(error)));
1007
+ failCount++;
863
1008
  }
864
1009
  }
1010
+ if (failCount > 0) {
1011
+ process.exit(1);
1012
+ }
865
1013
  const uniqueDeps = [...new Set(allDependencies)];
866
1014
  const uniqueDevDeps = [...new Set(allDevDependencies)];
867
1015
  if (uniqueDeps.length || uniqueDevDeps.length) {
868
1016
  console.log();
869
- console.log(chalk3.bold("Install dependencies:"));
1017
+ console.log(chalk4.bold("Install dependencies:"));
870
1018
  if (uniqueDeps.length) {
871
- console.log(chalk3.cyan(` npx expo install ${uniqueDeps.join(" ")}`));
1019
+ console.log(chalk4.cyan(` npx expo install ${uniqueDeps.join(" ")}`));
872
1020
  }
873
1021
  if (uniqueDevDeps.length) {
874
- console.log(chalk3.cyan(` npm install -D ${uniqueDevDeps.join(" ")}`));
1022
+ console.log(chalk4.cyan(` npm install -D ${uniqueDevDeps.join(" ")}`));
875
1023
  }
876
1024
  }
877
1025
  console.log();
878
- console.log(chalk3.green("Done!"));
1026
+ console.log(chalk4.green("Done!"));
879
1027
  } catch (error) {
880
1028
  spinner.fail("Failed");
881
- console.error(error);
882
- process.exit(1);
1029
+ handleError({
1030
+ message: "Failed to add components",
1031
+ hint: error instanceof Error ? error.message : "Check your network connection and try again"
1032
+ });
883
1033
  }
884
1034
  });
885
1035
 
886
1036
  // src/commands/list.ts
887
1037
  import { Command as Command3 } from "commander";
888
- import chalk4 from "chalk";
1038
+ import chalk5 from "chalk";
889
1039
  import ora3 from "ora";
890
1040
  import path6 from "path";
891
- var listCommand = new Command3().name("list").description("List available or installed components").option("-c, --category <category>", "Filter by category").option("-i, --installed", "Show installed components and their sync status").option("--cwd <path>", "Working directory", process.cwd()).action(async (options) => {
1041
+ var listCommand = new Command3().name("list").description("List available or installed components").option("-c, --category <category>", "Filter by category").option("-i, --installed", "Show installed components and their sync status").option("--json", "Output as JSON").option("--cwd <path>", "Working directory", process.cwd()).action(async (options) => {
892
1042
  if (options.installed) {
893
1043
  await listInstalledComponents(options);
894
1044
  } else {
@@ -900,8 +1050,13 @@ async function listAvailableComponents(options) {
900
1050
  try {
901
1051
  const registry = await getRegistry();
902
1052
  spinner.stop();
1053
+ if (options.json) {
1054
+ const items = options.category ? registry.filter((item) => (item.category || "Other").toLowerCase() === options.category.toLowerCase()) : registry;
1055
+ console.log(JSON.stringify(items, null, 2));
1056
+ return;
1057
+ }
903
1058
  console.log();
904
- console.log(chalk4.bold("Available Components"));
1059
+ console.log(chalk5.bold("Available Components"));
905
1060
  console.log();
906
1061
  const categories = /* @__PURE__ */ new Map();
907
1062
  for (const item of registry) {
@@ -915,20 +1070,22 @@ async function listAvailableComponents(options) {
915
1070
  categories.get(category).push(item);
916
1071
  }
917
1072
  for (const [category, items] of categories) {
918
- console.log(chalk4.cyan.bold(`${category}`));
1073
+ console.log(chalk5.cyan.bold(`${category}`));
919
1074
  for (const item of items) {
920
- const status = item.status === "stable" ? "" : chalk4.yellow(` [${item.status}]`);
921
- console.log(` ${chalk4.white(item.name)}${status}`);
922
- console.log(chalk4.dim(` ${item.description}`));
1075
+ const status = item.status === "stable" ? "" : chalk5.yellow(` [${item.status}]`);
1076
+ console.log(` ${chalk5.white(item.name)}${status}`);
1077
+ console.log(chalk5.dim(` ${item.description}`));
923
1078
  }
924
1079
  console.log();
925
1080
  }
926
- console.log(chalk4.dim("Add a component: npx mcellui add <component>"));
1081
+ console.log(chalk5.dim("Add a component: npx mcellui add <component>"));
927
1082
  console.log();
928
1083
  } catch (error) {
929
1084
  spinner.fail("Failed to fetch components");
930
- console.error(error);
931
- process.exit(1);
1085
+ handleError({
1086
+ message: "Failed to fetch components",
1087
+ hint: error instanceof Error ? error.message : "Check your network connection and try again"
1088
+ });
932
1089
  }
933
1090
  }
934
1091
  async function listInstalledComponents(options) {
@@ -937,22 +1094,23 @@ async function listInstalledComponents(options) {
937
1094
  const cwd = path6.resolve(options.cwd || process.cwd());
938
1095
  const projectRoot = await getProjectRoot(cwd);
939
1096
  if (!projectRoot) {
940
- console.log(chalk4.red("Could not find a valid project."));
941
- console.log(chalk4.dim("Run `npx mcellui init` first."));
942
- process.exit(1);
1097
+ errors.noProject();
943
1098
  }
944
1099
  const config = await getConfig(projectRoot);
945
1100
  if (!config) {
946
- console.log(chalk4.red("Project not initialized."));
947
- console.log(chalk4.dim("Run `npx mcellui init` first."));
948
- process.exit(1);
1101
+ errors.notInitialized();
949
1102
  }
950
1103
  spinner.start("Scanning installed components...");
951
1104
  const componentsDir = path6.join(projectRoot, config.componentsPath);
952
1105
  const installedFiles = await getInstalledFiles(componentsDir);
953
1106
  if (installedFiles.length === 0) {
1107
+ spinner.stop();
1108
+ if (options.json) {
1109
+ console.log(JSON.stringify([], null, 2));
1110
+ return;
1111
+ }
954
1112
  spinner.info("No components installed yet.");
955
- console.log(chalk4.dim("\nAdd components with: npx mcellui add <component>"));
1113
+ console.log(chalk5.dim("\nAdd components with: npx mcellui add <component>"));
956
1114
  return;
957
1115
  }
958
1116
  spinner.text = "Fetching registry...";
@@ -960,13 +1118,17 @@ async function listInstalledComponents(options) {
960
1118
  spinner.text = "Comparing components...";
961
1119
  const installed = await getInstallStatus(installedFiles, registry, config);
962
1120
  spinner.stop();
1121
+ if (options.json) {
1122
+ console.log(JSON.stringify(installed, null, 2));
1123
+ return;
1124
+ }
963
1125
  const identical = installed.filter((c) => c.status === "identical");
964
1126
  const modified = installed.filter((c) => c.status === "modified");
965
1127
  const localOnly = installed.filter((c) => c.status === "local-only");
966
1128
  const installedNames = new Set(installed.map((c) => c.name));
967
1129
  const notInstalled = registry.filter((item) => !installedNames.has(item.name));
968
1130
  console.log();
969
- console.log(chalk4.bold(`Installed Components (${installed.length})`));
1131
+ console.log(chalk5.bold(`Installed Components (${installed.length})`));
970
1132
  console.log();
971
1133
  const categories = /* @__PURE__ */ new Map();
972
1134
  for (const comp of installed) {
@@ -978,56 +1140,58 @@ async function listInstalledComponents(options) {
978
1140
  categories.get(category).push(comp);
979
1141
  }
980
1142
  for (const [category, components] of categories) {
981
- console.log(chalk4.cyan.bold(category));
1143
+ console.log(chalk5.cyan.bold(category));
982
1144
  for (const comp of components) {
983
1145
  let statusIcon;
984
1146
  let statusText;
985
1147
  switch (comp.status) {
986
1148
  case "identical":
987
- statusIcon = chalk4.green("\u2713");
988
- statusText = chalk4.dim("(up to date)");
1149
+ statusIcon = chalk5.green("\u2713");
1150
+ statusText = chalk5.dim("(up to date)");
989
1151
  break;
990
1152
  case "modified":
991
- statusIcon = chalk4.yellow("\u26A0");
992
- statusText = chalk4.yellow("(modified locally)");
1153
+ statusIcon = chalk5.yellow("\u26A0");
1154
+ statusText = chalk5.yellow("(modified locally)");
993
1155
  break;
994
1156
  case "local-only":
995
- statusIcon = chalk4.blue("?");
996
- statusText = chalk4.dim("(custom component)");
1157
+ statusIcon = chalk5.blue("?");
1158
+ statusText = chalk5.dim("(custom component)");
997
1159
  break;
998
1160
  }
999
- console.log(` ${statusIcon} ${chalk4.white(comp.name)} ${statusText}`);
1161
+ console.log(` ${statusIcon} ${chalk5.white(comp.name)} ${statusText}`);
1000
1162
  }
1001
1163
  console.log();
1002
1164
  }
1003
1165
  if (notInstalled.length > 0) {
1004
- console.log(chalk4.dim("Not Installed"));
1166
+ console.log(chalk5.dim("Not Installed"));
1005
1167
  const notInstalledNames = notInstalled.map((c) => c.name).slice(0, 10);
1006
1168
  const remaining = notInstalled.length - 10;
1007
- console.log(chalk4.dim(` ${notInstalledNames.join(", ")}${remaining > 0 ? `, ... +${remaining} more` : ""}`));
1169
+ console.log(chalk5.dim(` ${notInstalledNames.join(", ")}${remaining > 0 ? `, ... +${remaining} more` : ""}`));
1008
1170
  console.log();
1009
1171
  }
1010
- console.log(chalk4.dim("\u2500".repeat(50)));
1172
+ console.log(chalk5.dim("\u2500".repeat(50)));
1011
1173
  const parts = [];
1012
- if (identical.length > 0) parts.push(chalk4.green(`${identical.length} up to date`));
1013
- if (modified.length > 0) parts.push(chalk4.yellow(`${modified.length} modified`));
1014
- if (localOnly.length > 0) parts.push(chalk4.blue(`${localOnly.length} custom`));
1174
+ if (identical.length > 0) parts.push(chalk5.green(`${identical.length} up to date`));
1175
+ if (modified.length > 0) parts.push(chalk5.yellow(`${modified.length} modified`));
1176
+ if (localOnly.length > 0) parts.push(chalk5.blue(`${localOnly.length} custom`));
1015
1177
  console.log(parts.join(" \u2022 "));
1016
1178
  if (modified.length > 0) {
1017
1179
  console.log();
1018
- console.log(chalk4.dim("Sync modified components:"));
1019
- console.log(chalk4.cyan(` npx mcellui diff`));
1180
+ console.log(chalk5.dim("Sync modified components:"));
1181
+ console.log(chalk5.cyan(` npx mcellui diff`));
1020
1182
  }
1021
1183
  } catch (error) {
1022
1184
  spinner.fail("Failed to list installed components");
1023
- console.error(error);
1024
- process.exit(1);
1185
+ handleError({
1186
+ message: "Failed to list installed components",
1187
+ hint: error instanceof Error ? error.message : "Check your network connection and try again"
1188
+ });
1025
1189
  }
1026
1190
  }
1027
1191
 
1028
1192
  // src/commands/doctor.ts
1029
1193
  import { Command as Command4 } from "commander";
1030
- import chalk5 from "chalk";
1194
+ import chalk6 from "chalk";
1031
1195
  import fs6 from "fs-extra";
1032
1196
  import path7 from "path";
1033
1197
  var REQUIRED_PEER_DEPS = [
@@ -1408,43 +1572,43 @@ async function checkExpoGo(projectRoot) {
1408
1572
  }
1409
1573
  function printReport(report) {
1410
1574
  console.log();
1411
- console.log(chalk5.bold("mcellui Doctor"));
1412
- console.log(chalk5.dim("Checking your project setup..."));
1575
+ console.log(chalk6.bold("mcellui Doctor"));
1576
+ console.log(chalk6.dim("Checking your project setup..."));
1413
1577
  console.log();
1414
- console.log(chalk5.dim("Project:"), report.projectRoot);
1415
- console.log(chalk5.dim("Type:"), report.projectType);
1416
- console.log(chalk5.dim("Package Manager:"), report.packageManager);
1578
+ console.log(chalk6.dim("Project:"), report.projectRoot);
1579
+ console.log(chalk6.dim("Type:"), report.projectType);
1580
+ console.log(chalk6.dim("Package Manager:"), report.packageManager);
1417
1581
  console.log();
1418
- console.log(chalk5.bold("Checks"));
1582
+ console.log(chalk6.bold("Checks"));
1419
1583
  console.log();
1420
1584
  for (const check of report.checks) {
1421
- const icon = check.status === "pass" ? chalk5.green("\u2713") : check.status === "warn" ? chalk5.yellow("!") : chalk5.red("\u2717");
1422
- const statusColor = check.status === "pass" ? chalk5.green : check.status === "warn" ? chalk5.yellow : chalk5.red;
1423
- console.log(` ${icon} ${chalk5.white(check.name)}`);
1585
+ const icon = check.status === "pass" ? chalk6.green("\u2713") : check.status === "warn" ? chalk6.yellow("!") : chalk6.red("\u2717");
1586
+ const statusColor = check.status === "pass" ? chalk6.green : check.status === "warn" ? chalk6.yellow : chalk6.red;
1587
+ console.log(` ${icon} ${chalk6.white(check.name)}`);
1424
1588
  console.log(` ${statusColor(check.message)}`);
1425
1589
  if (check.fix && check.status !== "pass") {
1426
- console.log(chalk5.dim(` Fix: ${check.fix}`));
1590
+ console.log(chalk6.dim(` Fix: ${check.fix}`));
1427
1591
  }
1428
1592
  }
1429
1593
  console.log();
1430
1594
  const summaryParts = [];
1431
1595
  if (report.passed > 0) {
1432
- summaryParts.push(chalk5.green(`${report.passed} passed`));
1596
+ summaryParts.push(chalk6.green(`${report.passed} passed`));
1433
1597
  }
1434
1598
  if (report.warnings > 0) {
1435
- summaryParts.push(chalk5.yellow(`${report.warnings} warnings`));
1599
+ summaryParts.push(chalk6.yellow(`${report.warnings} warnings`));
1436
1600
  }
1437
1601
  if (report.failed > 0) {
1438
- summaryParts.push(chalk5.red(`${report.failed} failed`));
1602
+ summaryParts.push(chalk6.red(`${report.failed} failed`));
1439
1603
  }
1440
- console.log(chalk5.bold("Summary:"), summaryParts.join(", "));
1604
+ console.log(chalk6.bold("Summary:"), summaryParts.join(", "));
1441
1605
  console.log();
1442
1606
  if (report.failed > 0) {
1443
- console.log(chalk5.red("Some checks failed. Please fix the issues above."));
1607
+ console.log(chalk6.red("Some checks failed. Please fix the issues above."));
1444
1608
  } else if (report.warnings > 0) {
1445
- console.log(chalk5.yellow("Your project has some warnings but should work."));
1609
+ console.log(chalk6.yellow("Your project has some warnings but should work."));
1446
1610
  } else {
1447
- console.log(chalk5.green("Your project is properly configured!"));
1611
+ console.log(chalk6.green("Your project is properly configured!"));
1448
1612
  }
1449
1613
  console.log();
1450
1614
  }
@@ -1453,9 +1617,7 @@ var doctorCommand = new Command4().name("doctor").description("Check project set
1453
1617
  const cwd = path7.resolve(options.cwd);
1454
1618
  const projectRoot = await getProjectRoot(cwd);
1455
1619
  if (!projectRoot) {
1456
- console.log(chalk5.red("Could not find a valid project."));
1457
- console.log(chalk5.dim("Make sure you run this command in a project directory."));
1458
- process.exit(1);
1620
+ errors.noProject();
1459
1621
  }
1460
1622
  const projectType = await detectProjectType(projectRoot);
1461
1623
  const packageManager = await detectPackageManager(projectRoot);
@@ -1493,14 +1655,16 @@ var doctorCommand = new Command4().name("doctor").description("Check project set
1493
1655
  process.exit(1);
1494
1656
  }
1495
1657
  } catch (error) {
1496
- console.error(chalk5.red("Doctor check failed:"), error);
1497
- process.exit(1);
1658
+ handleError({
1659
+ message: "Doctor check failed",
1660
+ hint: error instanceof Error ? error.message : "Run again with --json for details"
1661
+ });
1498
1662
  }
1499
1663
  });
1500
1664
 
1501
1665
  // src/commands/diff.ts
1502
1666
  import { Command as Command5 } from "commander";
1503
- import chalk6 from "chalk";
1667
+ import chalk7 from "chalk";
1504
1668
  import ora4 from "ora";
1505
1669
  import fs7 from "fs-extra";
1506
1670
  import path8 from "path";
@@ -1511,22 +1675,18 @@ var diffCommand = new Command5().name("diff").description("Compare locally insta
1511
1675
  const cwd = path8.resolve(options.cwd);
1512
1676
  const projectRoot = await getProjectRoot(cwd);
1513
1677
  if (!projectRoot) {
1514
- console.log(chalk6.red("Could not find a valid project."));
1515
- console.log(chalk6.dim("Run `npx mcellui init` first."));
1516
- process.exit(1);
1678
+ errors.noProject();
1517
1679
  }
1518
1680
  const config = await getConfig(projectRoot);
1519
1681
  if (!config) {
1520
- console.log(chalk6.red("Project not initialized."));
1521
- console.log(chalk6.dim("Run `npx mcellui init` first."));
1522
- process.exit(1);
1682
+ errors.notInitialized();
1523
1683
  }
1524
1684
  spinner.start("Scanning installed components...");
1525
1685
  const componentsDir = path8.join(projectRoot, config.componentsPath);
1526
1686
  const installedFiles = await getInstalledFiles(componentsDir);
1527
1687
  if (installedFiles.length === 0) {
1528
1688
  spinner.info("No components installed yet.");
1529
- console.log(chalk6.dim("\nAdd components with: npx mcellui add <component>"));
1689
+ console.log(chalk7.dim("\nAdd components with: npx mcellui add <component>"));
1530
1690
  return;
1531
1691
  }
1532
1692
  spinner.text = "Fetching registry...";
@@ -1548,8 +1708,11 @@ var diffCommand = new Command5().name("diff").description("Compare locally insta
1548
1708
  return componentFileNames.includes(fileName);
1549
1709
  });
1550
1710
  if (filesToCompare.length === 0) {
1551
- spinner.fail(`None of the specified components found: ${components.join(", ")}`);
1552
- process.exit(1);
1711
+ spinner.stop();
1712
+ handleError({
1713
+ message: "None of the specified components are installed",
1714
+ hint: "Check component names: npx mcellui list --installed"
1715
+ });
1553
1716
  }
1554
1717
  }
1555
1718
  spinner.text = "Comparing components...";
@@ -1633,35 +1796,37 @@ var diffCommand = new Command5().name("diff").description("Compare locally insta
1633
1796
  const hasChanges = results.some((r) => r.status === "modified");
1634
1797
  process.exit(hasChanges ? 1 : 0);
1635
1798
  } catch (error) {
1636
- spinner.fail("Failed to diff components");
1637
- console.error(error);
1638
- process.exit(1);
1799
+ spinner.stop();
1800
+ handleError({
1801
+ message: "Failed to diff components",
1802
+ hint: error instanceof Error ? error.message : "Check your network connection"
1803
+ });
1639
1804
  }
1640
1805
  });
1641
1806
  function printResults(results, listOnly) {
1642
1807
  const identical = results.filter((r) => r.status === "identical");
1643
1808
  const modified = results.filter((r) => r.status === "modified");
1644
1809
  const localOnly = results.filter((r) => r.status === "local-only");
1645
- const errors = results.filter((r) => r.status === "error");
1810
+ const errors2 = results.filter((r) => r.status === "error");
1646
1811
  console.log();
1647
- console.log(chalk6.bold("Comparing components..."));
1812
+ console.log(chalk7.bold("Comparing components..."));
1648
1813
  console.log();
1649
1814
  for (const result of results) {
1650
1815
  switch (result.status) {
1651
1816
  case "identical":
1652
- console.log(`${chalk6.green("\u2713")} ${result.fileName} ${chalk6.dim("(identical)")}`);
1817
+ console.log(`${chalk7.green("\u2713")} ${result.fileName} ${chalk7.dim("(identical)")}`);
1653
1818
  break;
1654
1819
  case "modified":
1655
- console.log(`${chalk6.red("\u2717")} ${result.fileName} ${chalk6.yellow("(modified)")}`);
1820
+ console.log(`${chalk7.red("\u2717")} ${result.fileName} ${chalk7.yellow("(modified)")}`);
1656
1821
  if (!listOnly && result.diff) {
1657
1822
  printColoredDiff(result.diff);
1658
1823
  }
1659
1824
  break;
1660
1825
  case "local-only":
1661
- console.log(`${chalk6.yellow("\u26A0")} ${result.fileName} ${chalk6.dim("(not in registry)")}`);
1826
+ console.log(`${chalk7.yellow("\u26A0")} ${result.fileName} ${chalk7.dim("(not in registry)")}`);
1662
1827
  break;
1663
1828
  case "error":
1664
- console.log(`${chalk6.red("!")} ${result.fileName} ${chalk6.red(`(error: ${result.error})`)}`);
1829
+ console.log(`${chalk7.red("!")} ${result.fileName} ${chalk7.red(`(error: ${result.error})`)}`);
1665
1830
  break;
1666
1831
  }
1667
1832
  }
@@ -1670,12 +1835,12 @@ function printResults(results, listOnly) {
1670
1835
  if (identical.length > 0) parts.push(`${identical.length} identical`);
1671
1836
  if (modified.length > 0) parts.push(`${modified.length} modified`);
1672
1837
  if (localOnly.length > 0) parts.push(`${localOnly.length} custom`);
1673
- if (errors.length > 0) parts.push(`${errors.length} errors`);
1674
- console.log(chalk6.dim(`Summary: ${parts.join(", ")}`));
1838
+ if (errors2.length > 0) parts.push(`${errors2.length} errors`);
1839
+ console.log(chalk7.dim(`Summary: ${parts.join(", ")}`));
1675
1840
  if (modified.length > 0) {
1676
1841
  console.log();
1677
- console.log(chalk6.dim("Update modified components with:"));
1678
- console.log(chalk6.cyan(` npx mcellui add ${modified.map((m) => m.name).join(" ")} --overwrite`));
1842
+ console.log(chalk7.dim("Update modified components with:"));
1843
+ console.log(chalk7.cyan(` npx mcellui add ${modified.map((m) => m.name).join(" ")} --overwrite`));
1679
1844
  }
1680
1845
  }
1681
1846
  function printColoredDiff(diffOutput) {
@@ -1683,20 +1848,20 @@ function printColoredDiff(diffOutput) {
1683
1848
  const contentLines = lines.slice(4);
1684
1849
  for (const line of contentLines) {
1685
1850
  if (line.startsWith("+") && !line.startsWith("+++")) {
1686
- console.log(chalk6.green(` ${line}`));
1851
+ console.log(chalk7.green(` ${line}`));
1687
1852
  } else if (line.startsWith("-") && !line.startsWith("---")) {
1688
- console.log(chalk6.red(` ${line}`));
1853
+ console.log(chalk7.red(` ${line}`));
1689
1854
  } else if (line.startsWith("@@")) {
1690
- console.log(chalk6.cyan(` ${line}`));
1855
+ console.log(chalk7.cyan(` ${line}`));
1691
1856
  } else if (line.trim()) {
1692
- console.log(chalk6.dim(` ${line}`));
1857
+ console.log(chalk7.dim(` ${line}`));
1693
1858
  }
1694
1859
  }
1695
1860
  }
1696
1861
 
1697
1862
  // src/commands/update.ts
1698
1863
  import { Command as Command6 } from "commander";
1699
- import chalk7 from "chalk";
1864
+ import chalk8 from "chalk";
1700
1865
  import ora5 from "ora";
1701
1866
  import prompts3 from "prompts";
1702
1867
  import fs8 from "fs-extra";
@@ -1707,22 +1872,18 @@ var updateCommand = new Command6().name("update").description("Update installed
1707
1872
  const cwd = path9.resolve(options.cwd);
1708
1873
  const projectRoot = await getProjectRoot(cwd);
1709
1874
  if (!projectRoot) {
1710
- console.log(chalk7.red("Could not find a valid project."));
1711
- console.log(chalk7.dim("Run `npx nativeui init` first."));
1712
- process.exit(1);
1875
+ errors.noProject();
1713
1876
  }
1714
1877
  const config = await getConfig(projectRoot);
1715
1878
  if (!config) {
1716
- console.log(chalk7.red("Project not initialized."));
1717
- console.log(chalk7.dim("Run `npx nativeui init` first."));
1718
- process.exit(1);
1879
+ errors.notInitialized();
1719
1880
  }
1720
1881
  spinner.start("Checking for updates...");
1721
1882
  const componentsDir = path9.join(projectRoot, config.componentsPath);
1722
1883
  const diffs = await getComponentDiffs(componentsDir, config);
1723
1884
  if (diffs.length === 0) {
1724
1885
  spinner.info("No components installed yet.");
1725
- console.log(chalk7.dim("\nAdd components with: npx nativeui add <component>"));
1886
+ console.log(chalk8.dim("\nAdd components with: npx mcellui add <component>"));
1726
1887
  return;
1727
1888
  }
1728
1889
  spinner.stop();
@@ -1731,7 +1892,7 @@ var updateCommand = new Command6().name("update").description("Update installed
1731
1892
  toUpdate = diffs.filter((d) => components.includes(d.name));
1732
1893
  const notFound = components.filter((c) => !diffs.some((d) => d.name === c));
1733
1894
  if (notFound.length > 0) {
1734
- console.log(chalk7.yellow(`Components not found: ${notFound.join(", ")}`));
1895
+ console.log(chalk8.yellow(`Components not found: ${notFound.join(", ")}`));
1735
1896
  }
1736
1897
  } else if (options.all) {
1737
1898
  toUpdate = diffs;
@@ -1739,19 +1900,19 @@ var updateCommand = new Command6().name("update").description("Update installed
1739
1900
  toUpdate = diffs.filter((d) => d.hasUpdate);
1740
1901
  }
1741
1902
  if (toUpdate.length === 0) {
1742
- console.log(chalk7.green("\n\u2713 All components are up to date!"));
1903
+ console.log(chalk8.green("\n\u2713 All components are up to date!"));
1743
1904
  return;
1744
1905
  }
1745
1906
  console.log();
1746
- console.log(chalk7.bold(`Components to update (${toUpdate.length}):`));
1907
+ console.log(chalk8.bold(`Components to update (${toUpdate.length}):`));
1747
1908
  for (const comp of toUpdate) {
1748
- const status = comp.hasUpdate ? chalk7.yellow("\u25CF") : chalk7.green("\u25CF");
1749
- const label = comp.hasUpdate ? chalk7.dim("(update available)") : chalk7.dim("(re-sync)");
1909
+ const status = comp.hasUpdate ? chalk8.yellow("\u25CF") : chalk8.green("\u25CF");
1910
+ const label = comp.hasUpdate ? chalk8.dim("(update available)") : chalk8.dim("(re-sync)");
1750
1911
  console.log(` ${status} ${comp.name} ${label}`);
1751
1912
  }
1752
1913
  console.log();
1753
1914
  if (options.dryRun) {
1754
- console.log(chalk7.dim("Dry run mode - no changes made."));
1915
+ console.log(chalk8.dim("Dry run mode - no changes made."));
1755
1916
  return;
1756
1917
  }
1757
1918
  if (!options.yes) {
@@ -1761,8 +1922,11 @@ var updateCommand = new Command6().name("update").description("Update installed
1761
1922
  message: `Update ${toUpdate.length} component${toUpdate.length !== 1 ? "s" : ""}?`,
1762
1923
  initial: true
1763
1924
  });
1925
+ if (confirm === void 0) {
1926
+ process.exit(0);
1927
+ }
1764
1928
  if (!confirm) {
1765
- console.log(chalk7.dim("Cancelled."));
1929
+ console.log(chalk8.dim("Cancelled."));
1766
1930
  return;
1767
1931
  }
1768
1932
  }
@@ -1802,27 +1966,32 @@ var updateCommand = new Command6().name("update").description("Update installed
1802
1966
  }
1803
1967
  console.log();
1804
1968
  if (successCount > 0) {
1805
- console.log(chalk7.green(`\u2713 Updated ${successCount} component${successCount !== 1 ? "s" : ""}`));
1969
+ console.log(chalk8.green(`\u2713 Updated ${successCount} component${successCount !== 1 ? "s" : ""}`));
1806
1970
  }
1807
1971
  if (failCount > 0) {
1808
- console.log(chalk7.red(`\u2717 Failed to update ${failCount} component${failCount !== 1 ? "s" : ""}`));
1972
+ console.log(chalk8.red(`\u2717 Failed to update ${failCount} component${failCount !== 1 ? "s" : ""}`));
1809
1973
  }
1810
1974
  const uniqueDeps = [...new Set(allDependencies)];
1811
1975
  const uniqueDevDeps = [...new Set(allDevDependencies)];
1812
1976
  if (uniqueDeps.length || uniqueDevDeps.length) {
1813
1977
  console.log();
1814
- console.log(chalk7.bold("Install/update dependencies:"));
1978
+ console.log(chalk8.bold("Install/update dependencies:"));
1815
1979
  if (uniqueDeps.length) {
1816
- console.log(chalk7.cyan(` npx expo install ${uniqueDeps.join(" ")}`));
1980
+ console.log(chalk8.cyan(` npx expo install ${uniqueDeps.join(" ")}`));
1817
1981
  }
1818
1982
  if (uniqueDevDeps.length) {
1819
- console.log(chalk7.cyan(` npm install -D ${uniqueDevDeps.join(" ")}`));
1983
+ console.log(chalk8.cyan(` npm install -D ${uniqueDevDeps.join(" ")}`));
1820
1984
  }
1821
1985
  }
1986
+ if (failCount > 0) {
1987
+ process.exit(1);
1988
+ }
1822
1989
  } catch (error) {
1823
1990
  spinner.fail("Failed to update");
1824
- console.error(error);
1825
- process.exit(1);
1991
+ handleError({
1992
+ message: "Failed to update components",
1993
+ hint: error instanceof Error ? error.message : "Check your network connection and try again"
1994
+ });
1826
1995
  }
1827
1996
  });
1828
1997
  async function getComponentDiffs(componentsDir, config) {
@@ -1878,7 +2047,7 @@ async function checkForUpdate(localFile, registryItem, config) {
1878
2047
 
1879
2048
  // src/commands/create.ts
1880
2049
  import { Command as Command7 } from "commander";
1881
- import chalk8 from "chalk";
2050
+ import chalk9 from "chalk";
1882
2051
  import ora6 from "ora";
1883
2052
  import prompts4 from "prompts";
1884
2053
  import fs9 from "fs-extra";
@@ -1889,33 +2058,31 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
1889
2058
  const cwd = path10.resolve(options.cwd);
1890
2059
  const projectRoot = await getProjectRoot(cwd);
1891
2060
  if (!projectRoot) {
1892
- console.log(chalk8.red("Could not find a valid project."));
1893
- console.log(chalk8.dim("Run `npx nativeui init` first."));
1894
- process.exit(1);
2061
+ errors.noProject();
1895
2062
  }
1896
2063
  const config = await getConfig(projectRoot);
1897
2064
  if (!config) {
1898
- console.log(chalk8.red("Project not initialized."));
1899
- console.log(chalk8.dim("Run `npx nativeui init` first."));
1900
- process.exit(1);
2065
+ errors.notInitialized();
1901
2066
  }
1902
2067
  const componentName = toPascalCase(name);
1903
2068
  const fileName = toKebabCase(name) + ".tsx";
1904
2069
  const targetDir = path10.join(projectRoot, config.componentsPath);
1905
2070
  const targetPath = path10.join(targetDir, fileName);
1906
2071
  if (await fs9.pathExists(targetPath)) {
1907
- console.log(chalk8.red(`Component already exists: ${fileName}`));
1908
- console.log(chalk8.dim(`Path: ${targetPath}`));
1909
- process.exit(1);
2072
+ handleError({
2073
+ message: `Component already exists: ${fileName}`,
2074
+ hint: "Choose a different name or delete the existing file",
2075
+ code: "COMPONENT_EXISTS"
2076
+ });
1910
2077
  }
1911
2078
  if (!options.yes) {
1912
2079
  console.log();
1913
- console.log(chalk8.bold("Create new component:"));
1914
- console.log(` Name: ${chalk8.cyan(componentName)}`);
1915
- console.log(` File: ${chalk8.dim(fileName)}`);
1916
- console.log(` Path: ${chalk8.dim(targetPath)}`);
1917
- console.log(` Template: ${chalk8.dim(options.template)}`);
1918
- console.log(` ForwardRef: ${chalk8.dim(options.forwardRef ? "Yes" : "No")}`);
2080
+ console.log(chalk9.bold("Create new component:"));
2081
+ console.log(` Name: ${chalk9.cyan(componentName)}`);
2082
+ console.log(` File: ${chalk9.dim(fileName)}`);
2083
+ console.log(` Path: ${chalk9.dim(targetPath)}`);
2084
+ console.log(` Template: ${chalk9.dim(options.template)}`);
2085
+ console.log(` ForwardRef: ${chalk9.dim(options.forwardRef ? "Yes" : "No")}`);
1919
2086
  console.log();
1920
2087
  const { confirm } = await prompts4({
1921
2088
  type: "confirm",
@@ -1923,8 +2090,11 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
1923
2090
  message: "Create this component?",
1924
2091
  initial: true
1925
2092
  });
2093
+ if (confirm === void 0) {
2094
+ process.exit(0);
2095
+ }
1926
2096
  if (!confirm) {
1927
- console.log(chalk8.dim("Cancelled."));
2097
+ console.log(chalk9.dim("Cancelled."));
1928
2098
  return;
1929
2099
  }
1930
2100
  }
@@ -1934,15 +2104,17 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
1934
2104
  await fs9.writeFile(targetPath, code);
1935
2105
  spinner.succeed(`Created ${fileName}`);
1936
2106
  console.log();
1937
- console.log(chalk8.bold("Next steps:"));
1938
- console.log(` 1. Edit your component: ${chalk8.cyan(targetPath)}`);
2107
+ console.log(chalk9.bold("Next steps:"));
2108
+ console.log(` 1. Edit your component: ${chalk9.cyan(targetPath)}`);
1939
2109
  console.log(` 2. Import it in your app:`);
1940
- console.log(chalk8.dim(` import { ${componentName} } from '${config.aliases?.components || "@/components"}/ui/${toKebabCase(name)}';`));
2110
+ console.log(chalk9.dim(` import { ${componentName} } from '${config.aliases?.components || "@/components"}/ui/${toKebabCase(name)}';`));
1941
2111
  console.log();
1942
2112
  } catch (error) {
1943
2113
  spinner.fail("Failed to create component");
1944
- console.error(error);
1945
- process.exit(1);
2114
+ handleError({
2115
+ message: "Failed to create component",
2116
+ hint: error instanceof Error ? error.message : "Check file permissions and try again"
2117
+ });
1946
2118
  }
1947
2119
  });
1948
2120
  function toPascalCase(str) {
@@ -1976,7 +2148,7 @@ export interface ${name}Props extends ViewProps {
1976
2148
  /**
1977
2149
  * ${name}
1978
2150
  *
1979
- * A custom component created with nativeui create.
2151
+ * A custom component created with mcellui create.
1980
2152
  *
1981
2153
  * @example
1982
2154
  * \`\`\`tsx
@@ -2036,7 +2208,7 @@ export interface ${name}Props extends ViewProps {
2036
2208
  /**
2037
2209
  * ${name}
2038
2210
  *
2039
- * A custom component created with nativeui create.
2211
+ * A custom component created with mcellui create.
2040
2212
  *
2041
2213
  * @example
2042
2214
  * \`\`\`tsx
@@ -2103,7 +2275,7 @@ export interface ${name}Props extends ViewProps {
2103
2275
  /**
2104
2276
  * ${name}
2105
2277
  *
2106
- * An animated component created with nativeui create.
2278
+ * An animated component created with mcellui create.
2107
2279
  *
2108
2280
  * @example
2109
2281
  * \`\`\`tsx
@@ -2178,7 +2350,7 @@ export interface ${name}Props extends Omit<PressableProps, 'style'> {
2178
2350
  /**
2179
2351
  * ${name}
2180
2352
  *
2181
- * A pressable component created with nativeui create.
2353
+ * A pressable component created with mcellui create.
2182
2354
  *
2183
2355
  * @example
2184
2356
  * \`\`\`tsx
@@ -2271,7 +2443,7 @@ export interface ${name}Props extends Omit<PressableProps, 'style'> {
2271
2443
  /**
2272
2444
  * ${name}
2273
2445
  *
2274
- * A pressable component created with nativeui create.
2446
+ * A pressable component created with mcellui create.
2275
2447
  *
2276
2448
  * @example
2277
2449
  * \`\`\`tsx
@@ -2359,7 +2531,7 @@ export interface ${name}Props extends TextInputProps {
2359
2531
  /**
2360
2532
  * ${name}
2361
2533
  *
2362
- * A custom input component created with nativeui create.
2534
+ * A custom input component created with mcellui create.
2363
2535
  *
2364
2536
  * @example
2365
2537
  * \`\`\`tsx
@@ -2484,7 +2656,7 @@ export interface ${name}Props extends TextInputProps {
2484
2656
  /**
2485
2657
  * ${name}
2486
2658
  *
2487
- * A custom input component created with nativeui create.
2659
+ * A custom input component created with mcellui create.
2488
2660
  *
2489
2661
  * @example
2490
2662
  * \`\`\`tsx
@@ -2584,7 +2756,7 @@ const styles = StyleSheet.create({
2584
2756
 
2585
2757
  // src/commands/pick.ts
2586
2758
  import { Command as Command8 } from "commander";
2587
- import chalk9 from "chalk";
2759
+ import chalk10 from "chalk";
2588
2760
  import prompts5 from "prompts";
2589
2761
  import ora7 from "ora";
2590
2762
  import fs10 from "fs-extra";
@@ -2636,7 +2808,7 @@ function formatCategoryName(category) {
2636
2808
  }
2637
2809
  function formatComponentChoice(item, installed) {
2638
2810
  const isInstalled = installed.has(item.name);
2639
- const status = isInstalled ? chalk9.green(" \u2713") : "";
2811
+ const status = isInstalled ? chalk10.green(" \u2713") : "";
2640
2812
  const description = item.description || "";
2641
2813
  return {
2642
2814
  title: `${item.name}${status}`,
@@ -2646,24 +2818,24 @@ function formatComponentChoice(item, installed) {
2646
2818
  };
2647
2819
  }
2648
2820
  var pickCommand = new Command8().name("pick").description("Interactively browse and select components to add").option("--all", "Show all components without category selection").option("-o, --overwrite", "Overwrite existing files").option("--cwd <path>", "Working directory", process.cwd()).action(async (options) => {
2649
- console.log(chalk9.bold("\n\u{1F3A8} nativeui Component Picker\n"));
2821
+ console.log(chalk10.bold("\n\u{1F3A8} mcellui Component Picker\n"));
2650
2822
  const cwd = path11.resolve(options.cwd);
2651
2823
  const projectRoot = await getProjectRoot(cwd);
2652
2824
  if (!projectRoot) {
2653
- console.log(chalk9.red("Could not find a valid project."));
2654
- console.log(chalk9.dim("Run `npx nativeui init` first.\n"));
2655
- return;
2825
+ errors.noProject();
2656
2826
  }
2657
2827
  const config = await getConfig(projectRoot);
2658
2828
  if (!config) {
2659
- console.log(chalk9.yellow("No nativeui.config.ts found. Run `npx nativeui init` first.\n"));
2660
- return;
2829
+ errors.notInitialized();
2661
2830
  }
2662
2831
  const spinner = ora7("Loading component registry...").start();
2663
2832
  const registry = await getRegistry();
2664
2833
  if (!registry.length) {
2665
- spinner.fail("Could not load registry");
2666
- return;
2834
+ spinner.stop();
2835
+ handleError({
2836
+ message: "Could not load component registry",
2837
+ hint: "Check your internet connection and try again"
2838
+ });
2667
2839
  }
2668
2840
  spinner.succeed(`Loaded ${registry.length} components`);
2669
2841
  const componentsDir = path11.join(projectRoot, config.componentsPath);
@@ -2693,8 +2865,11 @@ var pickCommand = new Command8().name("pick").description("Interactively browse
2693
2865
  message: "Select a category",
2694
2866
  choices: categoryChoices
2695
2867
  });
2868
+ if (categoryResponse.category === void 0) {
2869
+ process.exit(0);
2870
+ }
2696
2871
  if (!categoryResponse.category) {
2697
- console.log(chalk9.yellow("\nCancelled.\n"));
2872
+ console.log(chalk10.yellow("\nCancelled.\n"));
2698
2873
  return;
2699
2874
  }
2700
2875
  selectedCategory = categoryResponse.category;
@@ -2707,10 +2882,10 @@ var pickCommand = new Command8().name("pick").description("Interactively browse
2707
2882
  );
2708
2883
  const availableCount = componentChoices.filter((c) => !c.disabled).length;
2709
2884
  if (availableCount === 0) {
2710
- console.log(chalk9.green("\n\u2713 All components in this category are already installed!\n"));
2885
+ console.log(chalk10.green("\n\u2713 All components in this category are already installed!\n"));
2711
2886
  return;
2712
2887
  }
2713
- console.log(chalk9.dim(`
2888
+ console.log(chalk10.dim(`
2714
2889
  ${installed.size} already installed, ${availableCount} available
2715
2890
  `));
2716
2891
  const componentResponse = await prompts5({
@@ -2721,14 +2896,17 @@ ${installed.size} already installed, ${availableCount} available
2721
2896
  hint: "- Space to select. Return to submit",
2722
2897
  instructions: false
2723
2898
  });
2899
+ if (componentResponse.components === void 0) {
2900
+ process.exit(0);
2901
+ }
2724
2902
  if (!componentResponse.components || componentResponse.components.length === 0) {
2725
- console.log(chalk9.yellow("\nNo components selected.\n"));
2903
+ console.log(chalk10.yellow("\nNo components selected.\n"));
2726
2904
  return;
2727
2905
  }
2728
2906
  const selectedComponents = componentResponse.components;
2729
- console.log(chalk9.bold("\nComponents to install:"));
2907
+ console.log(chalk10.bold("\nComponents to install:"));
2730
2908
  for (const name of selectedComponents) {
2731
- console.log(chalk9.cyan(` \u2022 ${name}`));
2909
+ console.log(chalk10.cyan(` \u2022 ${name}`));
2732
2910
  }
2733
2911
  const confirmResponse = await prompts5({
2734
2912
  type: "confirm",
@@ -2736,12 +2914,16 @@ ${installed.size} already installed, ${availableCount} available
2736
2914
  message: `Install ${selectedComponents.length} component(s)?`,
2737
2915
  initial: true
2738
2916
  });
2917
+ if (confirmResponse.proceed === void 0) {
2918
+ process.exit(0);
2919
+ }
2739
2920
  if (!confirmResponse.proceed) {
2740
- console.log(chalk9.yellow("\nCancelled.\n"));
2921
+ console.log(chalk10.yellow("\nCancelled.\n"));
2741
2922
  return;
2742
2923
  }
2743
2924
  console.log("");
2744
2925
  let successCount = 0;
2926
+ let failCount = 0;
2745
2927
  const allDependencies = [];
2746
2928
  const allDevDependencies = [];
2747
2929
  for (const name of selectedComponents) {
@@ -2750,6 +2932,7 @@ ${installed.size} already installed, ${availableCount} available
2750
2932
  const component = await fetchComponent(name);
2751
2933
  if (!component) {
2752
2934
  installSpinner.fail(`Component "${name}" not found`);
2935
+ failCount++;
2753
2936
  continue;
2754
2937
  }
2755
2938
  const targetDir = path11.join(projectRoot, config.componentsPath);
@@ -2763,7 +2946,7 @@ ${installed.size} already installed, ${availableCount} available
2763
2946
  const transformedContent = transformToInstalled(file.content, config);
2764
2947
  await fs10.writeFile(targetPath, transformedContent);
2765
2948
  }
2766
- installSpinner.succeed(`Installed ${chalk9.green(name)}`);
2949
+ installSpinner.succeed(`Installed ${chalk10.green(name)}`);
2767
2950
  successCount++;
2768
2951
  if (component.dependencies?.length) {
2769
2952
  allDependencies.push(...component.dependencies);
@@ -2772,35 +2955,39 @@ ${installed.size} already installed, ${availableCount} available
2772
2955
  allDevDependencies.push(...component.devDependencies);
2773
2956
  }
2774
2957
  if (component.registryDependencies?.length) {
2775
- console.log(chalk9.dim(` Requires: ${component.registryDependencies.join(", ")}`));
2958
+ console.log(chalk10.dim(` Requires: ${component.registryDependencies.join(", ")}`));
2776
2959
  }
2777
2960
  } catch (error) {
2778
2961
  installSpinner.fail(`Failed to install ${name}: ${error}`);
2962
+ failCount++;
2779
2963
  }
2780
2964
  }
2965
+ if (failCount > 0) {
2966
+ process.exit(1);
2967
+ }
2781
2968
  console.log(
2782
- chalk9.bold.green(`
2969
+ chalk10.bold.green(`
2783
2970
  \u2713 Successfully installed ${successCount}/${selectedComponents.length} components
2784
2971
  `)
2785
2972
  );
2786
2973
  const uniqueDeps = [...new Set(allDependencies)];
2787
2974
  const uniqueDevDeps = [...new Set(allDevDependencies)];
2788
2975
  if (uniqueDeps.length || uniqueDevDeps.length) {
2789
- console.log(chalk9.bold("Install dependencies:"));
2976
+ console.log(chalk10.bold("Install dependencies:"));
2790
2977
  if (uniqueDeps.length) {
2791
- console.log(chalk9.cyan(` npx expo install ${uniqueDeps.join(" ")}`));
2978
+ console.log(chalk10.cyan(` npx expo install ${uniqueDeps.join(" ")}`));
2792
2979
  }
2793
2980
  if (uniqueDevDeps.length) {
2794
- console.log(chalk9.cyan(` npm install -D ${uniqueDevDeps.join(" ")}`));
2981
+ console.log(chalk10.cyan(` npm install -D ${uniqueDevDeps.join(" ")}`));
2795
2982
  }
2796
2983
  console.log("");
2797
2984
  }
2798
2985
  if (successCount > 0) {
2799
- console.log(chalk9.dim("Import example:"));
2986
+ console.log(chalk10.dim("Import example:"));
2800
2987
  const firstComponent = selectedComponents[0];
2801
2988
  const pascalName = firstComponent.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
2802
2989
  const alias = config.aliases?.components || "@/components/ui";
2803
- console.log(chalk9.dim(` import { ${pascalName} } from '${alias}/${firstComponent}';
2990
+ console.log(chalk10.dim(` import { ${pascalName} } from '${alias}/${firstComponent}';
2804
2991
  `));
2805
2992
  }
2806
2993
  });
@@ -2808,6 +2995,11 @@ ${installed.size} already installed, ${availableCount} available
2808
2995
  // src/index.ts
2809
2996
  var program = new Command9();
2810
2997
  program.name("mcellui").description("Add beautiful UI components to your Expo/React Native project").version("0.1.4");
2998
+ program.configureOutput({
2999
+ writeOut: (str) => process.stdout.write(str),
3000
+ writeErr: (str) => process.stderr.write(str),
3001
+ outputError: (str, write) => write(chalk11.red(str))
3002
+ });
2811
3003
  program.addCommand(initCommand);
2812
3004
  program.addCommand(addCommand);
2813
3005
  program.addCommand(listCommand);
@@ -2816,4 +3008,14 @@ program.addCommand(diffCommand);
2816
3008
  program.addCommand(updateCommand);
2817
3009
  program.addCommand(createCommand);
2818
3010
  program.addCommand(pickCommand);
2819
- program.parse();
3011
+ async function main() {
3012
+ try {
3013
+ await program.parseAsync(process.argv);
3014
+ } catch (error) {
3015
+ handleError({
3016
+ message: error instanceof Error ? error.message : "An unexpected error occurred",
3017
+ hint: "If this persists, run: npx mcellui doctor"
3018
+ });
3019
+ }
3020
+ }
3021
+ main();