@metacells/mcellui-cli 0.1.2 → 0.1.4

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 +481 -184
  2. package/package.json +5 -3
package/dist/index.js CHANGED
@@ -116,8 +116,8 @@ function validateConfig(config) {
116
116
  return { success: true, data: result.data };
117
117
  }
118
118
  const errors = result.error.issues.map((issue) => {
119
- const path10 = issue.path.join(".");
120
- return path10 ? `${path10}: ${issue.message}` : issue.message;
119
+ const path12 = issue.path.join(".");
120
+ return path12 ? `${path12}: ${issue.message}` : issue.message;
121
121
  });
122
122
  return { success: false, errors };
123
123
  }
@@ -475,8 +475,8 @@ import { Command as Command2 } from "commander";
475
475
  import chalk3 from "chalk";
476
476
  import ora2 from "ora";
477
477
  import prompts2 from "prompts";
478
- import fs4 from "fs-extra";
479
- import path4 from "path";
478
+ import fs5 from "fs-extra";
479
+ import path5 from "path";
480
480
 
481
481
  // src/utils/registry.ts
482
482
  import fs3 from "fs-extra";
@@ -484,7 +484,7 @@ import path3 from "path";
484
484
  import { fileURLToPath as fileURLToPath2 } from "url";
485
485
  var __filename3 = fileURLToPath2(import.meta.url);
486
486
  var __dirname2 = path3.dirname(__filename3);
487
- var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/metacells/mcellui/main/packages/registry";
487
+ var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/metacells-development/mcellui/main/packages/registry";
488
488
  var REGISTRY_URL = process.env.MCELLUI_REGISTRY_URL || process.env.NATIVEUI_REGISTRY_URL || DEFAULT_REGISTRY_URL;
489
489
  function getLocalRegistryPath() {
490
490
  return path3.resolve(__dirname2, "..", "..", "registry");
@@ -567,11 +567,173 @@ async function fetchRemoteComponent(item) {
567
567
  };
568
568
  }
569
569
 
570
+ // src/utils/dependencies.ts
571
+ function resolveDependencies(components, registry) {
572
+ const registryMap = /* @__PURE__ */ new Map();
573
+ for (const item of registry) {
574
+ registryMap.set(item.name, item);
575
+ }
576
+ const resolved = /* @__PURE__ */ new Set();
577
+ const resolving = /* @__PURE__ */ new Set();
578
+ const resolvedOrder = [];
579
+ function resolve(name, path12) {
580
+ if (resolved.has(name)) {
581
+ return null;
582
+ }
583
+ if (resolving.has(name)) {
584
+ return [...path12, name];
585
+ }
586
+ const item = registryMap.get(name);
587
+ if (!item) {
588
+ return null;
589
+ }
590
+ resolving.add(name);
591
+ const deps = item.registryDependencies || [];
592
+ for (const dep of deps) {
593
+ const circular = resolve(dep, [...path12, name]);
594
+ if (circular) {
595
+ return circular;
596
+ }
597
+ }
598
+ resolving.delete(name);
599
+ resolved.add(name);
600
+ resolvedOrder.push(name);
601
+ return null;
602
+ }
603
+ for (const name of components) {
604
+ const circular = resolve(name, []);
605
+ if (circular) {
606
+ return {
607
+ resolved: [],
608
+ circular
609
+ };
610
+ }
611
+ }
612
+ return {
613
+ resolved: resolvedOrder,
614
+ circular: null
615
+ };
616
+ }
617
+ function formatCircularError(chain) {
618
+ return chain.join(" \u2192 ");
619
+ }
620
+
621
+ // src/utils/installed.ts
622
+ import fs4 from "fs-extra";
623
+ import path4 from "path";
624
+ async function getInstalledFiles(componentsDir) {
625
+ if (!await fs4.pathExists(componentsDir)) {
626
+ return [];
627
+ }
628
+ const files = await fs4.readdir(componentsDir);
629
+ const componentFiles = [];
630
+ for (const file of files) {
631
+ if (file.endsWith(".tsx") || file.endsWith(".ts")) {
632
+ if (file === "index.ts" || file === "index.tsx") {
633
+ continue;
634
+ }
635
+ componentFiles.push(path4.join(componentsDir, file));
636
+ }
637
+ }
638
+ return componentFiles.sort();
639
+ }
640
+ function normalizeContent(content) {
641
+ return content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
642
+ }
643
+ function transformImports(code, config) {
644
+ let transformed = code;
645
+ transformed = transformed.replace(
646
+ /from ['"]@nativeui\/core['"]/g,
647
+ `from '@metacells/mcellui-core'`
648
+ );
649
+ const utilsAlias = config.aliases?.utils || "@/lib/utils";
650
+ if (utilsAlias !== "@/lib/utils") {
651
+ transformed = transformed.replace(
652
+ /from ['"]@\/lib\/utils['"]/g,
653
+ `from '${utilsAlias}'`
654
+ );
655
+ }
656
+ return transformed;
657
+ }
658
+ async function getInstallStatus(installedFiles, registry, config) {
659
+ const results = [];
660
+ const registryMap = /* @__PURE__ */ new Map();
661
+ for (const item of registry) {
662
+ for (const file of item.files) {
663
+ const fileName = path4.basename(file);
664
+ registryMap.set(fileName, item);
665
+ }
666
+ }
667
+ for (const localFile of installedFiles) {
668
+ const fileName = path4.basename(localFile);
669
+ const componentName = fileName.replace(/\.tsx?$/, "");
670
+ const registryItem = registryMap.get(fileName);
671
+ if (!registryItem) {
672
+ results.push({
673
+ name: componentName,
674
+ fileName,
675
+ status: "local-only",
676
+ localPath: localFile
677
+ });
678
+ continue;
679
+ }
680
+ try {
681
+ const component = await fetchComponent(registryItem.name);
682
+ if (!component) {
683
+ results.push({
684
+ name: registryItem.name,
685
+ fileName,
686
+ status: "modified",
687
+ // Assume modified if we can't verify
688
+ localPath: localFile
689
+ });
690
+ continue;
691
+ }
692
+ const registryFile = component.files.find((f) => f.name === fileName);
693
+ if (!registryFile) {
694
+ results.push({
695
+ name: registryItem.name,
696
+ fileName,
697
+ status: "modified",
698
+ localPath: localFile
699
+ });
700
+ continue;
701
+ }
702
+ const localContent = await fs4.readFile(localFile, "utf-8");
703
+ const normalizedLocal = normalizeContent(localContent);
704
+ const normalizedRegistry = normalizeContent(
705
+ transformImports(registryFile.content, config)
706
+ );
707
+ results.push({
708
+ name: registryItem.name,
709
+ fileName,
710
+ status: normalizedLocal === normalizedRegistry ? "identical" : "modified",
711
+ localPath: localFile
712
+ });
713
+ } catch {
714
+ results.push({
715
+ name: registryItem.name,
716
+ fileName,
717
+ status: "modified",
718
+ // Assume modified on error
719
+ localPath: localFile
720
+ });
721
+ }
722
+ }
723
+ return results;
724
+ }
725
+ function getInstalledNames(installedFiles) {
726
+ return installedFiles.map((file) => {
727
+ const fileName = path4.basename(file);
728
+ return fileName.replace(/\.tsx?$/, "");
729
+ });
730
+ }
731
+
570
732
  // src/commands/add.ts
571
733
  var addCommand = new Command2().name("add").description("Add a component to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation").option("-o, --overwrite", "Overwrite existing files").option("--cwd <path>", "Working directory", process.cwd()).action(async (components, options) => {
572
734
  const spinner = ora2();
573
735
  try {
574
- const cwd = path4.resolve(options.cwd);
736
+ const cwd = path5.resolve(options.cwd);
575
737
  const projectRoot = await getProjectRoot(cwd);
576
738
  if (!projectRoot) {
577
739
  console.log(chalk3.red("Could not find a valid project."));
@@ -585,8 +747,8 @@ var addCommand = new Command2().name("add").description("Add a component to your
585
747
  process.exit(1);
586
748
  }
587
749
  if (!components.length) {
588
- const registry = await getRegistry();
589
- if (!registry.length) {
750
+ const registry2 = await getRegistry();
751
+ if (!registry2.length) {
590
752
  console.log(chalk3.red("No components found in registry."));
591
753
  process.exit(1);
592
754
  }
@@ -594,7 +756,7 @@ var addCommand = new Command2().name("add").description("Add a component to your
594
756
  type: "multiselect",
595
757
  name: "selected",
596
758
  message: "Which components would you like to add?",
597
- choices: registry.map((item) => ({
759
+ choices: registry2.map((item) => ({
598
760
  title: `${item.name} ${chalk3.dim(`(${item.status})`)}`,
599
761
  value: item.name,
600
762
  description: item.description
@@ -607,13 +769,51 @@ var addCommand = new Command2().name("add").description("Add a component to your
607
769
  }
608
770
  components = selected;
609
771
  }
772
+ spinner.start("Resolving dependencies...");
773
+ const registry = await getRegistry();
774
+ const { resolved, circular } = resolveDependencies(components, registry);
775
+ if (circular) {
776
+ spinner.fail("Circular dependency detected");
777
+ console.log(chalk3.red(` ${formatCircularError(circular)}`));
778
+ process.exit(1);
779
+ }
780
+ spinner.stop();
781
+ const componentsDir = path5.join(projectRoot, config.componentsPath);
782
+ const installedFiles = await getInstalledFiles(componentsDir);
783
+ const installedNames = new Set(getInstalledNames(installedFiles));
784
+ const toInstall = options.overwrite ? resolved : resolved.filter((name) => !installedNames.has(name));
785
+ if (toInstall.length === 0) {
786
+ console.log(chalk3.yellow("All components are already installed."));
787
+ console.log(chalk3.dim("Use --overwrite to reinstall."));
788
+ return;
789
+ }
790
+ const requested = new Set(components);
791
+ const dependencies = toInstall.filter((name) => !requested.has(name));
610
792
  console.log();
611
793
  console.log(chalk3.bold("Adding components:"));
612
- components.forEach((c) => console.log(chalk3.dim(` - ${c}`)));
794
+ for (const name of toInstall) {
795
+ if (requested.has(name)) {
796
+ console.log(chalk3.dim(` - ${name}`));
797
+ } else {
798
+ console.log(chalk3.dim(` - ${name} ${chalk3.cyan("(dependency)")}`));
799
+ }
800
+ }
613
801
  console.log();
802
+ if (dependencies.length > 0 && !options.yes) {
803
+ const { confirm } = await prompts2({
804
+ type: "confirm",
805
+ name: "confirm",
806
+ message: `Add ${dependencies.length} additional dependencies?`,
807
+ initial: true
808
+ });
809
+ if (!confirm) {
810
+ console.log(chalk3.dim("Cancelled."));
811
+ return;
812
+ }
813
+ }
614
814
  const allDependencies = [];
615
815
  const allDevDependencies = [];
616
- for (const componentName of components) {
816
+ for (const componentName of toInstall) {
617
817
  spinner.start(`Fetching ${componentName}...`);
618
818
  try {
619
819
  const component = await fetchComponent(componentName);
@@ -621,16 +821,16 @@ var addCommand = new Command2().name("add").description("Add a component to your
621
821
  spinner.fail(`Component "${componentName}" not found`);
622
822
  continue;
623
823
  }
624
- const targetDir = path4.join(projectRoot, config.componentsPath);
824
+ const targetDir = path5.join(projectRoot, config.componentsPath);
625
825
  for (const file of component.files) {
626
- const targetPath = path4.join(targetDir, file.name);
627
- if (await fs4.pathExists(targetPath) && !options.overwrite) {
826
+ const targetPath = path5.join(targetDir, file.name);
827
+ if (await fs5.pathExists(targetPath) && !options.overwrite) {
628
828
  spinner.warn(`${file.name} already exists (use --overwrite)`);
629
829
  continue;
630
830
  }
631
- await fs4.ensureDir(targetDir);
632
- const transformedContent = transformImports(file.content, config);
633
- await fs4.writeFile(targetPath, transformedContent);
831
+ await fs5.ensureDir(targetDir);
832
+ const transformedContent = transformImports2(file.content, config);
833
+ await fs5.writeFile(targetPath, transformedContent);
634
834
  }
635
835
  spinner.succeed(`Added ${componentName}`);
636
836
  if (component.dependencies?.length) {
@@ -639,11 +839,6 @@ var addCommand = new Command2().name("add").description("Add a component to your
639
839
  if (component.devDependencies?.length) {
640
840
  allDevDependencies.push(...component.devDependencies);
641
841
  }
642
- if (component.registryDependencies?.length) {
643
- console.log(
644
- chalk3.dim(` Requires: ${component.registryDependencies.join(", ")}`)
645
- );
646
- }
647
842
  } catch (error) {
648
843
  spinner.fail(`Failed to add ${componentName}`);
649
844
  console.error(chalk3.dim(String(error)));
@@ -669,7 +864,7 @@ var addCommand = new Command2().name("add").description("Add a component to your
669
864
  process.exit(1);
670
865
  }
671
866
  });
672
- function transformImports(code, config) {
867
+ function transformImports2(code, config) {
673
868
  let transformed = code;
674
869
  const utilsAlias = config.aliases?.utils || "@/lib/utils";
675
870
  if (utilsAlias === "@/lib/utils") {
@@ -686,7 +881,15 @@ function transformImports(code, config) {
686
881
  import { Command as Command3 } from "commander";
687
882
  import chalk4 from "chalk";
688
883
  import ora3 from "ora";
689
- var listCommand = new Command3().name("list").description("List available components").option("-c, --category <category>", "Filter by category").action(async (options) => {
884
+ import path6 from "path";
885
+ 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) => {
886
+ if (options.installed) {
887
+ await listInstalledComponents(options);
888
+ } else {
889
+ await listAvailableComponents(options);
890
+ }
891
+ });
892
+ async function listAvailableComponents(options) {
690
893
  const spinner = ora3("Fetching components...").start();
691
894
  try {
692
895
  const registry = await getRegistry();
@@ -714,20 +917,113 @@ var listCommand = new Command3().name("list").description("List available compon
714
917
  }
715
918
  console.log();
716
919
  }
717
- console.log(chalk4.dim("Add a component: npx nativeui add <component>"));
920
+ console.log(chalk4.dim("Add a component: npx mcellui add <component>"));
718
921
  console.log();
719
922
  } catch (error) {
720
923
  spinner.fail("Failed to fetch components");
721
924
  console.error(error);
722
925
  process.exit(1);
723
926
  }
724
- });
927
+ }
928
+ async function listInstalledComponents(options) {
929
+ const spinner = ora3();
930
+ try {
931
+ const cwd = path6.resolve(options.cwd || process.cwd());
932
+ const projectRoot = await getProjectRoot(cwd);
933
+ if (!projectRoot) {
934
+ console.log(chalk4.red("Could not find a valid project."));
935
+ console.log(chalk4.dim("Run `npx mcellui init` first."));
936
+ process.exit(1);
937
+ }
938
+ const config = await getConfig(projectRoot);
939
+ if (!config) {
940
+ console.log(chalk4.red("Project not initialized."));
941
+ console.log(chalk4.dim("Run `npx mcellui init` first."));
942
+ process.exit(1);
943
+ }
944
+ spinner.start("Scanning installed components...");
945
+ const componentsDir = path6.join(projectRoot, config.componentsPath);
946
+ const installedFiles = await getInstalledFiles(componentsDir);
947
+ if (installedFiles.length === 0) {
948
+ spinner.info("No components installed yet.");
949
+ console.log(chalk4.dim("\nAdd components with: npx mcellui add <component>"));
950
+ return;
951
+ }
952
+ spinner.text = "Fetching registry...";
953
+ const registry = await getRegistry();
954
+ spinner.text = "Comparing components...";
955
+ const installed = await getInstallStatus(installedFiles, registry, config);
956
+ spinner.stop();
957
+ const identical = installed.filter((c) => c.status === "identical");
958
+ const modified = installed.filter((c) => c.status === "modified");
959
+ const localOnly = installed.filter((c) => c.status === "local-only");
960
+ const installedNames = new Set(installed.map((c) => c.name));
961
+ const notInstalled = registry.filter((item) => !installedNames.has(item.name));
962
+ console.log();
963
+ console.log(chalk4.bold(`Installed Components (${installed.length})`));
964
+ console.log();
965
+ const categories = /* @__PURE__ */ new Map();
966
+ for (const comp of installed) {
967
+ const registryItem = registry.find((r) => r.name === comp.name);
968
+ const category = registryItem?.category || "Custom";
969
+ if (!categories.has(category)) {
970
+ categories.set(category, []);
971
+ }
972
+ categories.get(category).push(comp);
973
+ }
974
+ for (const [category, components] of categories) {
975
+ console.log(chalk4.cyan.bold(category));
976
+ for (const comp of components) {
977
+ let statusIcon;
978
+ let statusText;
979
+ switch (comp.status) {
980
+ case "identical":
981
+ statusIcon = chalk4.green("\u2713");
982
+ statusText = chalk4.dim("(up to date)");
983
+ break;
984
+ case "modified":
985
+ statusIcon = chalk4.yellow("\u26A0");
986
+ statusText = chalk4.yellow("(modified locally)");
987
+ break;
988
+ case "local-only":
989
+ statusIcon = chalk4.blue("?");
990
+ statusText = chalk4.dim("(custom component)");
991
+ break;
992
+ }
993
+ console.log(` ${statusIcon} ${chalk4.white(comp.name)} ${statusText}`);
994
+ }
995
+ console.log();
996
+ }
997
+ if (notInstalled.length > 0) {
998
+ console.log(chalk4.dim("Not Installed"));
999
+ const notInstalledNames = notInstalled.map((c) => c.name).slice(0, 10);
1000
+ const remaining = notInstalled.length - 10;
1001
+ console.log(chalk4.dim(` ${notInstalledNames.join(", ")}${remaining > 0 ? `, ... +${remaining} more` : ""}`));
1002
+ console.log();
1003
+ }
1004
+ console.log(chalk4.dim("\u2500".repeat(50)));
1005
+ const parts = [];
1006
+ if (identical.length > 0) parts.push(chalk4.green(`${identical.length} up to date`));
1007
+ if (modified.length > 0) parts.push(chalk4.yellow(`${modified.length} modified`));
1008
+ if (localOnly.length > 0) parts.push(chalk4.blue(`${localOnly.length} custom`));
1009
+ console.log(parts.join(" \u2022 "));
1010
+ if (modified.length > 0) {
1011
+ console.log();
1012
+ console.log(chalk4.dim("Sync modified components:"));
1013
+ console.log(chalk4.cyan(` npx mcellui diff`));
1014
+ }
1015
+ } catch (error) {
1016
+ spinner.fail("Failed to list installed components");
1017
+ console.error(error);
1018
+ process.exit(1);
1019
+ }
1020
+ }
725
1021
 
726
1022
  // src/commands/doctor.ts
727
1023
  import { Command as Command4 } from "commander";
728
1024
  import chalk5 from "chalk";
729
- import fs5 from "fs-extra";
730
- import path5 from "path";
1025
+ import fs6 from "fs-extra";
1026
+ import path7 from "path";
731
1027
  var REQUIRED_PEER_DEPS = [
732
1028
  { name: "react-native-reanimated", minVersion: "3.0.0" },
733
1029
  { name: "react-native-gesture-handler", minVersion: "2.0.0" },
@@ -796,7 +1092,7 @@ async function checkInitialized(projectRoot) {
796
1092
  ];
797
1093
  let foundConfig = null;
798
1094
  for (const file of configFiles) {
799
- if (await fs5.pathExists(path5.join(projectRoot, file))) {
1095
+ if (await fs6.pathExists(path7.join(projectRoot, file))) {
800
1096
  foundConfig = file;
801
1097
  break;
802
1098
  }
@@ -810,8 +1106,8 @@ async function checkInitialized(projectRoot) {
810
1106
  };
811
1107
  }
812
1108
  try {
813
- const configPath = path5.join(projectRoot, foundConfig);
814
- const content = await fs5.readFile(configPath, "utf-8");
1109
+ const configPath = path7.join(projectRoot, foundConfig);
1110
+ const content = await fs6.readFile(configPath, "utf-8");
815
1111
  if (foundConfig.endsWith(".ts") || foundConfig.endsWith(".js")) {
816
1112
  const hasDefineConfig = content.includes("defineConfig");
817
1113
  const hasExport = content.includes("export default");
@@ -865,10 +1161,10 @@ async function checkPaths(projectRoot) {
865
1161
  let utilsPathValue = defaultUtilsPath;
866
1162
  const configFiles = ["nativeui.config.ts", "nativeui.config.js", "nativeui.config.json"];
867
1163
  for (const file of configFiles) {
868
- const configPath = path5.join(projectRoot, file);
869
- if (await fs5.pathExists(configPath)) {
1164
+ const configPath = path7.join(projectRoot, file);
1165
+ if (await fs6.pathExists(configPath)) {
870
1166
  try {
871
- const content = await fs5.readFile(configPath, "utf-8");
1167
+ const content = await fs6.readFile(configPath, "utf-8");
872
1168
  const componentsMatch = content.match(/componentsPath:\s*['"]([^'"]+)['"]/);
873
1169
  const utilsMatch = content.match(/utilsPath:\s*['"]([^'"]+)['"]/);
874
1170
  if (componentsMatch) componentsPathValue = componentsMatch[1];
@@ -878,10 +1174,10 @@ async function checkPaths(projectRoot) {
878
1174
  break;
879
1175
  }
880
1176
  }
881
- const componentsPath = path5.join(projectRoot, componentsPathValue);
882
- const utilsPath = path5.join(projectRoot, utilsPathValue);
883
- const componentsExist = await fs5.pathExists(componentsPath);
884
- const utilsExist = await fs5.pathExists(utilsPath);
1177
+ const componentsPath = path7.join(projectRoot, componentsPathValue);
1178
+ const utilsPath = path7.join(projectRoot, utilsPathValue);
1179
+ const componentsExist = await fs6.pathExists(componentsPath);
1180
+ const utilsExist = await fs6.pathExists(utilsPath);
885
1181
  if (!componentsExist && !utilsExist) {
886
1182
  return {
887
1183
  name: "Component Paths",
@@ -978,10 +1274,10 @@ async function checkBabelConfig(projectRoot) {
978
1274
  let babelConfigPath = null;
979
1275
  let babelContent = null;
980
1276
  for (const configFile of babelConfigPaths) {
981
- const fullPath = path5.join(projectRoot, configFile);
982
- if (await fs5.pathExists(fullPath)) {
1277
+ const fullPath = path7.join(projectRoot, configFile);
1278
+ if (await fs6.pathExists(fullPath)) {
983
1279
  babelConfigPath = configFile;
984
- babelContent = await fs5.readFile(fullPath, "utf-8");
1280
+ babelContent = await fs6.readFile(fullPath, "utf-8");
985
1281
  break;
986
1282
  }
987
1283
  }
@@ -1009,8 +1305,8 @@ async function checkBabelConfig(projectRoot) {
1009
1305
  };
1010
1306
  }
1011
1307
  async function checkTypeScript(projectRoot) {
1012
- const tsconfigPath = path5.join(projectRoot, "tsconfig.json");
1013
- if (!await fs5.pathExists(tsconfigPath)) {
1308
+ const tsconfigPath = path7.join(projectRoot, "tsconfig.json");
1309
+ if (!await fs6.pathExists(tsconfigPath)) {
1014
1310
  return {
1015
1311
  name: "TypeScript",
1016
1312
  status: "warn",
@@ -1019,7 +1315,7 @@ async function checkTypeScript(projectRoot) {
1019
1315
  };
1020
1316
  }
1021
1317
  try {
1022
- const tsconfig = await fs5.readJson(tsconfigPath);
1318
+ const tsconfig = await fs6.readJson(tsconfigPath);
1023
1319
  const hasPathAliases = tsconfig.compilerOptions?.paths;
1024
1320
  if (!hasPathAliases) {
1025
1321
  return {
@@ -1125,7 +1421,7 @@ function printReport(report) {
1125
1421
  }
1126
1422
  var doctorCommand = new Command4().name("doctor").description("Check project setup and diagnose common issues").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output results as JSON").action(async (options) => {
1127
1423
  try {
1128
- const cwd = path5.resolve(options.cwd);
1424
+ const cwd = path7.resolve(options.cwd);
1129
1425
  const projectRoot = await getProjectRoot(cwd);
1130
1426
  if (!projectRoot) {
1131
1427
  console.log(chalk5.red("Could not find a valid project."));
@@ -1177,31 +1473,31 @@ var doctorCommand = new Command4().name("doctor").description("Check project set
1177
1473
  import { Command as Command5 } from "commander";
1178
1474
  import chalk6 from "chalk";
1179
1475
  import ora4 from "ora";
1180
- import fs6 from "fs-extra";
1181
- import path6 from "path";
1182
- import crypto from "crypto";
1183
- var diffCommand = new Command5().name("diff").description("Check for component updates against the registry").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON").option("-v, --verbose", "Show detailed diff information").action(async (options) => {
1476
+ import fs7 from "fs-extra";
1477
+ import path8 from "path";
1478
+ import * as Diff from "diff";
1479
+ var diffCommand = new Command5().name("diff").description("Compare locally installed components against the registry source").argument("[components...]", "Component names to diff (optional, diffs all if omitted)").option("--cwd <path>", "Working directory", process.cwd()).option("--list", "Only list components with differences").option("--json", "Output as JSON").action(async (components, options) => {
1184
1480
  const spinner = ora4();
1185
1481
  try {
1186
- const cwd = path6.resolve(options.cwd);
1482
+ const cwd = path8.resolve(options.cwd);
1187
1483
  const projectRoot = await getProjectRoot(cwd);
1188
1484
  if (!projectRoot) {
1189
1485
  console.log(chalk6.red("Could not find a valid project."));
1190
- console.log(chalk6.dim("Run `npx nativeui init` first."));
1486
+ console.log(chalk6.dim("Run `npx mcellui init` first."));
1191
1487
  process.exit(1);
1192
1488
  }
1193
1489
  const config = await getConfig(projectRoot);
1194
1490
  if (!config) {
1195
1491
  console.log(chalk6.red("Project not initialized."));
1196
- console.log(chalk6.dim("Run `npx nativeui init` first."));
1492
+ console.log(chalk6.dim("Run `npx mcellui init` first."));
1197
1493
  process.exit(1);
1198
1494
  }
1199
1495
  spinner.start("Scanning installed components...");
1200
- const componentsDir = path6.join(projectRoot, config.componentsPath);
1201
- const installedFiles = await getInstalledComponents(componentsDir);
1496
+ const componentsDir = path8.join(projectRoot, config.componentsPath);
1497
+ const installedFiles = await getInstalledFiles(componentsDir);
1202
1498
  if (installedFiles.length === 0) {
1203
1499
  spinner.info("No components installed yet.");
1204
- console.log(chalk6.dim("\nAdd components with: npx nativeui add <component>"));
1500
+ console.log(chalk6.dim("\nAdd components with: npx mcellui add <component>"));
1205
1501
  return;
1206
1502
  }
1207
1503
  spinner.text = "Fetching registry...";
@@ -1209,18 +1505,34 @@ var diffCommand = new Command5().name("diff").description("Check for component u
1209
1505
  const registryMap = /* @__PURE__ */ new Map();
1210
1506
  for (const item of registry) {
1211
1507
  for (const file of item.files) {
1212
- const fileName = path6.basename(file);
1508
+ const fileName = path8.basename(file);
1213
1509
  registryMap.set(fileName, item);
1214
1510
  }
1215
1511
  }
1512
+ let filesToCompare = installedFiles;
1513
+ if (components.length > 0) {
1514
+ const componentFileNames = components.map((c) => {
1515
+ return c.endsWith(".tsx") ? c : `${c}.tsx`;
1516
+ });
1517
+ filesToCompare = installedFiles.filter((file) => {
1518
+ const fileName = path8.basename(file);
1519
+ return componentFileNames.includes(fileName);
1520
+ });
1521
+ if (filesToCompare.length === 0) {
1522
+ spinner.fail(`None of the specified components found: ${components.join(", ")}`);
1523
+ process.exit(1);
1524
+ }
1525
+ }
1216
1526
  spinner.text = "Comparing components...";
1217
1527
  const results = [];
1218
- for (const localFile of installedFiles) {
1219
- const fileName = path6.basename(localFile);
1528
+ for (const localFile of filesToCompare) {
1529
+ const fileName = path8.basename(localFile);
1530
+ const componentName = fileName.replace(/\.tsx?$/, "");
1220
1531
  const registryItem = registryMap.get(fileName);
1221
1532
  if (!registryItem) {
1222
1533
  results.push({
1223
- name: fileName.replace(/\.tsx?$/, ""),
1534
+ name: componentName,
1535
+ fileName,
1224
1536
  status: "local-only",
1225
1537
  localFile
1226
1538
  });
@@ -1231,6 +1543,7 @@ var diffCommand = new Command5().name("diff").description("Check for component u
1231
1543
  if (!component) {
1232
1544
  results.push({
1233
1545
  name: registryItem.name,
1546
+ fileName,
1234
1547
  status: "error",
1235
1548
  error: "Failed to fetch from registry"
1236
1549
  });
@@ -1240,31 +1553,44 @@ var diffCommand = new Command5().name("diff").description("Check for component u
1240
1553
  if (!registryFile) {
1241
1554
  results.push({
1242
1555
  name: registryItem.name,
1556
+ fileName,
1243
1557
  status: "error",
1244
1558
  error: "File not found in registry"
1245
1559
  });
1246
1560
  continue;
1247
1561
  }
1248
- const localContent = await fs6.readFile(localFile, "utf-8");
1249
- const transformedRegistryContent = transformImports2(registryFile.content, config);
1250
- const localHash = hashContent(localContent);
1251
- const registryHash = hashContent(transformedRegistryContent);
1252
- if (localHash === registryHash) {
1562
+ const localContent = await fs7.readFile(localFile, "utf-8");
1563
+ const normalizedLocal = normalizeContent(localContent);
1564
+ const normalizedRegistry = normalizeContent(
1565
+ transformImports(registryFile.content, config)
1566
+ );
1567
+ if (normalizedLocal === normalizedRegistry) {
1253
1568
  results.push({
1254
1569
  name: registryItem.name,
1255
- status: "up-to-date",
1570
+ fileName,
1571
+ status: "identical",
1256
1572
  localFile
1257
1573
  });
1258
1574
  } else {
1575
+ const diffOutput = Diff.createPatch(
1576
+ fileName,
1577
+ normalizedRegistry,
1578
+ normalizedLocal,
1579
+ "registry",
1580
+ "local"
1581
+ );
1259
1582
  results.push({
1260
1583
  name: registryItem.name,
1261
- status: "changed",
1262
- localFile
1584
+ fileName,
1585
+ status: "modified",
1586
+ localFile,
1587
+ diff: diffOutput
1263
1588
  });
1264
1589
  }
1265
1590
  } catch (error) {
1266
1591
  results.push({
1267
1592
  name: registryItem.name,
1593
+ fileName,
1268
1594
  status: "error",
1269
1595
  error: String(error)
1270
1596
  });
@@ -1273,100 +1599,71 @@ var diffCommand = new Command5().name("diff").description("Check for component u
1273
1599
  spinner.stop();
1274
1600
  if (options.json) {
1275
1601
  console.log(JSON.stringify(results, null, 2));
1276
- return;
1602
+ const hasChanges2 = results.some((r) => r.status === "modified");
1603
+ process.exit(hasChanges2 ? 1 : 0);
1277
1604
  }
1278
- printResults(results, options.verbose);
1605
+ printResults(results, options.list);
1606
+ const hasChanges = results.some((r) => r.status === "modified");
1607
+ process.exit(hasChanges ? 1 : 0);
1279
1608
  } catch (error) {
1280
- spinner.fail("Failed to check for updates");
1609
+ spinner.fail("Failed to diff components");
1281
1610
  console.error(error);
1282
1611
  process.exit(1);
1283
1612
  }
1284
1613
  });
1285
- async function getInstalledComponents(componentsDir) {
1286
- if (!await fs6.pathExists(componentsDir)) {
1287
- return [];
1288
- }
1289
- const files = await fs6.readdir(componentsDir);
1290
- const componentFiles = [];
1291
- for (const file of files) {
1292
- if (file.endsWith(".tsx") || file.endsWith(".ts")) {
1293
- if (file === "index.ts" || file === "index.tsx") {
1294
- continue;
1295
- }
1296
- componentFiles.push(path6.join(componentsDir, file));
1297
- }
1298
- }
1299
- return componentFiles;
1300
- }
1301
- function hashContent(content) {
1302
- const normalized = content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
1303
- return crypto.createHash("md5").update(normalized).digest("hex");
1304
- }
1305
- function transformImports2(code, config) {
1306
- let transformed = code;
1307
- const utilsAlias = config.aliases?.utils || "@/lib/utils";
1308
- if (utilsAlias === "@/lib/utils") {
1309
- return transformed;
1310
- }
1311
- transformed = transformed.replace(
1312
- /from ['"]@\/lib\/utils['"]/g,
1313
- `from '${utilsAlias}'`
1314
- );
1315
- return transformed;
1316
- }
1317
- function printResults(results, verbose) {
1318
- const upToDate = results.filter((r) => r.status === "up-to-date");
1319
- const changed = results.filter((r) => r.status === "changed");
1614
+ function printResults(results, listOnly) {
1615
+ const identical = results.filter((r) => r.status === "identical");
1616
+ const modified = results.filter((r) => r.status === "modified");
1320
1617
  const localOnly = results.filter((r) => r.status === "local-only");
1321
1618
  const errors = results.filter((r) => r.status === "error");
1322
1619
  console.log();
1323
- console.log(chalk6.bold("Component Diff Report"));
1324
- console.log(chalk6.dim("\u2500".repeat(40)));
1325
- console.log();
1326
- const total = results.length;
1327
- console.log(`Found ${chalk6.bold(total)} installed component${total !== 1 ? "s" : ""}`);
1620
+ console.log(chalk6.bold("Comparing components..."));
1328
1621
  console.log();
1329
- if (changed.length > 0) {
1330
- console.log(chalk6.yellow(`\u26A1 ${changed.length} update${changed.length !== 1 ? "s" : ""} available:`));
1331
- for (const item of changed) {
1332
- console.log(` ${chalk6.yellow("\u25CF")} ${item.name}`);
1333
- }
1334
- console.log();
1335
- console.log(chalk6.dim(" Update with: npx nativeui add <component> --overwrite"));
1336
- console.log();
1337
- }
1338
- if (upToDate.length > 0) {
1339
- console.log(chalk6.green(`\u2713 ${upToDate.length} up to date:`));
1340
- if (verbose) {
1341
- for (const item of upToDate) {
1342
- console.log(` ${chalk6.green("\u25CF")} ${item.name}`);
1343
- }
1344
- } else {
1345
- const names = upToDate.map((r) => r.name).join(", ");
1346
- console.log(chalk6.dim(` ${names}`));
1622
+ for (const result of results) {
1623
+ switch (result.status) {
1624
+ case "identical":
1625
+ console.log(`${chalk6.green("\u2713")} ${result.fileName} ${chalk6.dim("(identical)")}`);
1626
+ break;
1627
+ case "modified":
1628
+ console.log(`${chalk6.red("\u2717")} ${result.fileName} ${chalk6.yellow("(modified)")}`);
1629
+ if (!listOnly && result.diff) {
1630
+ printColoredDiff(result.diff);
1631
+ }
1632
+ break;
1633
+ case "local-only":
1634
+ console.log(`${chalk6.yellow("\u26A0")} ${result.fileName} ${chalk6.dim("(not in registry)")}`);
1635
+ break;
1636
+ case "error":
1637
+ console.log(`${chalk6.red("!")} ${result.fileName} ${chalk6.red(`(error: ${result.error})`)}`);
1638
+ break;
1347
1639
  }
1348
- console.log();
1349
1640
  }
1350
- if (localOnly.length > 0) {
1351
- console.log(chalk6.blue(`\u25D0 ${localOnly.length} local-only (not in registry):`));
1352
- for (const item of localOnly) {
1353
- console.log(` ${chalk6.blue("\u25CF")} ${item.name}`);
1354
- }
1641
+ console.log();
1642
+ const parts = [];
1643
+ if (identical.length > 0) parts.push(`${identical.length} identical`);
1644
+ if (modified.length > 0) parts.push(`${modified.length} modified`);
1645
+ if (localOnly.length > 0) parts.push(`${localOnly.length} custom`);
1646
+ if (errors.length > 0) parts.push(`${errors.length} errors`);
1647
+ console.log(chalk6.dim(`Summary: ${parts.join(", ")}`));
1648
+ if (modified.length > 0) {
1355
1649
  console.log();
1650
+ console.log(chalk6.dim("Update modified components with:"));
1651
+ console.log(chalk6.cyan(` npx mcellui add ${modified.map((m) => m.name).join(" ")} --overwrite`));
1356
1652
  }
1357
- if (errors.length > 0) {
1358
- console.log(chalk6.red(`\u2717 ${errors.length} error${errors.length !== 1 ? "s" : ""}:`));
1359
- for (const item of errors) {
1360
- console.log(` ${chalk6.red("\u25CF")} ${item.name}: ${chalk6.dim(item.error)}`);
1653
+ }
1654
+ function printColoredDiff(diffOutput) {
1655
+ const lines = diffOutput.split("\n");
1656
+ const contentLines = lines.slice(4);
1657
+ for (const line of contentLines) {
1658
+ if (line.startsWith("+") && !line.startsWith("+++")) {
1659
+ console.log(chalk6.green(` ${line}`));
1660
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
1661
+ console.log(chalk6.red(` ${line}`));
1662
+ } else if (line.startsWith("@@")) {
1663
+ console.log(chalk6.cyan(` ${line}`));
1664
+ } else if (line.trim()) {
1665
+ console.log(chalk6.dim(` ${line}`));
1361
1666
  }
1362
- console.log();
1363
- }
1364
- if (changed.length === 0 && errors.length === 0) {
1365
- console.log(chalk6.green("All registry components are up to date!"));
1366
- } else if (changed.length > 0) {
1367
- console.log(
1368
- chalk6.yellow(`Run ${chalk6.cyan("npx nativeui add " + changed.map((c) => c.name).join(" ") + " --overwrite")} to update`)
1369
- );
1370
1667
  }
1371
1668
  }
1372
1669
 
@@ -1375,13 +1672,13 @@ import { Command as Command6 } from "commander";
1375
1672
  import chalk7 from "chalk";
1376
1673
  import ora5 from "ora";
1377
1674
  import prompts3 from "prompts";
1378
- import fs7 from "fs-extra";
1379
- import path7 from "path";
1380
- import crypto2 from "crypto";
1675
+ import fs8 from "fs-extra";
1676
+ import path9 from "path";
1677
+ import crypto from "crypto";
1381
1678
  var updateCommand = new Command6().name("update").description("Update installed components to latest registry versions").argument("[components...]", "Specific components to update (default: all outdated)").option("-y, --yes", "Skip confirmation prompt").option("--all", "Update all components (including up-to-date)").option("--dry-run", "Show what would be updated without making changes").option("--cwd <path>", "Working directory", process.cwd()).action(async (components, options) => {
1382
1679
  const spinner = ora5();
1383
1680
  try {
1384
- const cwd = path7.resolve(options.cwd);
1681
+ const cwd = path9.resolve(options.cwd);
1385
1682
  const projectRoot = await getProjectRoot(cwd);
1386
1683
  if (!projectRoot) {
1387
1684
  console.log(chalk7.red("Could not find a valid project."));
@@ -1395,7 +1692,7 @@ var updateCommand = new Command6().name("update").description("Update installed
1395
1692
  process.exit(1);
1396
1693
  }
1397
1694
  spinner.start("Checking for updates...");
1398
- const componentsDir = path7.join(projectRoot, config.componentsPath);
1695
+ const componentsDir = path9.join(projectRoot, config.componentsPath);
1399
1696
  const diffs = await getComponentDiffs(componentsDir, config);
1400
1697
  if (diffs.length === 0) {
1401
1698
  spinner.info("No components installed yet.");
@@ -1457,12 +1754,12 @@ var updateCommand = new Command6().name("update").description("Update installed
1457
1754
  failCount++;
1458
1755
  continue;
1459
1756
  }
1460
- const targetDir = path7.join(projectRoot, config.componentsPath);
1757
+ const targetDir = path9.join(projectRoot, config.componentsPath);
1461
1758
  for (const file of component.files) {
1462
- const targetPath = path7.join(targetDir, file.name);
1463
- await fs7.ensureDir(targetDir);
1759
+ const targetPath = path9.join(targetDir, file.name);
1760
+ await fs8.ensureDir(targetDir);
1464
1761
  const transformedContent = transformImports3(file.content, config);
1465
- await fs7.writeFile(targetPath, transformedContent);
1762
+ await fs8.writeFile(targetPath, transformedContent);
1466
1763
  }
1467
1764
  spinner.succeed(`Updated ${comp.name}`);
1468
1765
  successCount++;
@@ -1503,18 +1800,18 @@ var updateCommand = new Command6().name("update").description("Update installed
1503
1800
  }
1504
1801
  });
1505
1802
  async function getComponentDiffs(componentsDir, config) {
1506
- if (!await fs7.pathExists(componentsDir)) {
1803
+ if (!await fs8.pathExists(componentsDir)) {
1507
1804
  return [];
1508
1805
  }
1509
1806
  const registry = await getRegistry();
1510
1807
  const registryMap = /* @__PURE__ */ new Map();
1511
1808
  for (const item of registry) {
1512
1809
  for (const file of item.files) {
1513
- const fileName = path7.basename(file);
1810
+ const fileName = path9.basename(file);
1514
1811
  registryMap.set(fileName, item);
1515
1812
  }
1516
1813
  }
1517
- const files = await fs7.readdir(componentsDir);
1814
+ const files = await fs8.readdir(componentsDir);
1518
1815
  const results = [];
1519
1816
  for (const file of files) {
1520
1817
  if (!file.endsWith(".tsx") && !file.endsWith(".ts")) {
@@ -1527,7 +1824,7 @@ async function getComponentDiffs(componentsDir, config) {
1527
1824
  if (!registryItem) {
1528
1825
  continue;
1529
1826
  }
1530
- const localFile = path7.join(componentsDir, file);
1827
+ const localFile = path9.join(componentsDir, file);
1531
1828
  const hasUpdate = await checkForUpdate(localFile, registryItem, config);
1532
1829
  results.push({
1533
1830
  name: registryItem.name,
@@ -1541,21 +1838,21 @@ async function checkForUpdate(localFile, registryItem, config) {
1541
1838
  try {
1542
1839
  const component = await fetchComponent(registryItem.name);
1543
1840
  if (!component) return false;
1544
- const fileName = path7.basename(localFile);
1841
+ const fileName = path9.basename(localFile);
1545
1842
  const registryFile = component.files.find((f) => f.name === fileName);
1546
1843
  if (!registryFile) return false;
1547
- const localContent = await fs7.readFile(localFile, "utf-8");
1844
+ const localContent = await fs8.readFile(localFile, "utf-8");
1548
1845
  const transformedRegistryContent = transformImports3(registryFile.content, config);
1549
- const localHash = hashContent2(localContent);
1550
- const registryHash = hashContent2(transformedRegistryContent);
1846
+ const localHash = hashContent(localContent);
1847
+ const registryHash = hashContent(transformedRegistryContent);
1551
1848
  return localHash !== registryHash;
1552
1849
  } catch {
1553
1850
  return false;
1554
1851
  }
1555
1852
  }
1556
- function hashContent2(content) {
1853
+ function hashContent(content) {
1557
1854
  const normalized = content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
1558
- return crypto2.createHash("md5").update(normalized).digest("hex");
1855
+ return crypto.createHash("md5").update(normalized).digest("hex");
1559
1856
  }
1560
1857
  function transformImports3(code, config) {
1561
1858
  let transformed = code;
@@ -1575,12 +1872,12 @@ import { Command as Command7 } from "commander";
1575
1872
  import chalk8 from "chalk";
1576
1873
  import ora6 from "ora";
1577
1874
  import prompts4 from "prompts";
1578
- import fs8 from "fs-extra";
1579
- import path8 from "path";
1875
+ import fs9 from "fs-extra";
1876
+ import path10 from "path";
1580
1877
  var createCommand = new Command7().name("create").description("Scaffold a new custom component").argument("<name>", "Component name (e.g., my-button, ProfileCard)").option("-t, --template <type>", "Component template: basic, animated, pressable, input", "basic").option("--forward-ref", "Include forwardRef pattern").option("-y, --yes", "Skip confirmation").option("--cwd <path>", "Working directory", process.cwd()).action(async (name, options) => {
1581
1878
  const spinner = ora6();
1582
1879
  try {
1583
- const cwd = path8.resolve(options.cwd);
1880
+ const cwd = path10.resolve(options.cwd);
1584
1881
  const projectRoot = await getProjectRoot(cwd);
1585
1882
  if (!projectRoot) {
1586
1883
  console.log(chalk8.red("Could not find a valid project."));
@@ -1595,9 +1892,9 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
1595
1892
  }
1596
1893
  const componentName = toPascalCase(name);
1597
1894
  const fileName = toKebabCase(name) + ".tsx";
1598
- const targetDir = path8.join(projectRoot, config.componentsPath);
1599
- const targetPath = path8.join(targetDir, fileName);
1600
- if (await fs8.pathExists(targetPath)) {
1895
+ const targetDir = path10.join(projectRoot, config.componentsPath);
1896
+ const targetPath = path10.join(targetDir, fileName);
1897
+ if (await fs9.pathExists(targetPath)) {
1601
1898
  console.log(chalk8.red(`Component already exists: ${fileName}`));
1602
1899
  console.log(chalk8.dim(`Path: ${targetPath}`));
1603
1900
  process.exit(1);
@@ -1624,8 +1921,8 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
1624
1921
  }
1625
1922
  spinner.start("Creating component...");
1626
1923
  const code = generateComponent(componentName, options.template, options.forwardRef);
1627
- await fs8.ensureDir(targetDir);
1628
- await fs8.writeFile(targetPath, code);
1924
+ await fs9.ensureDir(targetDir);
1925
+ await fs9.writeFile(targetPath, code);
1629
1926
  spinner.succeed(`Created ${fileName}`);
1630
1927
  console.log();
1631
1928
  console.log(chalk8.bold("Next steps:"));
@@ -2281,8 +2578,8 @@ import { Command as Command8 } from "commander";
2281
2578
  import chalk9 from "chalk";
2282
2579
  import prompts5 from "prompts";
2283
2580
  import ora7 from "ora";
2284
- import fs9 from "fs-extra";
2285
- import path9 from "path";
2581
+ import fs10 from "fs-extra";
2582
+ import path11 from "path";
2286
2583
  function groupByCategory(items) {
2287
2584
  const groups = {};
2288
2585
  for (const item of items) {
@@ -2353,7 +2650,7 @@ function transformImports4(code, config) {
2353
2650
  }
2354
2651
  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) => {
2355
2652
  console.log(chalk9.bold("\n\u{1F3A8} nativeui Component Picker\n"));
2356
- const cwd = path9.resolve(options.cwd);
2653
+ const cwd = path11.resolve(options.cwd);
2357
2654
  const projectRoot = await getProjectRoot(cwd);
2358
2655
  if (!projectRoot) {
2359
2656
  console.log(chalk9.red("Could not find a valid project."));
@@ -2372,10 +2669,10 @@ var pickCommand = new Command8().name("pick").description("Interactively browse
2372
2669
  return;
2373
2670
  }
2374
2671
  spinner.succeed(`Loaded ${registry.length} components`);
2375
- const componentsDir = path9.join(projectRoot, config.componentsPath);
2672
+ const componentsDir = path11.join(projectRoot, config.componentsPath);
2376
2673
  const installed = /* @__PURE__ */ new Set();
2377
- if (fs9.existsSync(componentsDir)) {
2378
- const files = fs9.readdirSync(componentsDir);
2674
+ if (fs10.existsSync(componentsDir)) {
2675
+ const files = fs10.readdirSync(componentsDir);
2379
2676
  for (const file of files) {
2380
2677
  if (file.endsWith(".tsx")) {
2381
2678
  installed.add(file.replace(".tsx", ""));
@@ -2458,16 +2755,16 @@ ${installed.size} already installed, ${availableCount} available
2458
2755
  installSpinner.fail(`Component "${name}" not found`);
2459
2756
  continue;
2460
2757
  }
2461
- const targetDir = path9.join(projectRoot, config.componentsPath);
2758
+ const targetDir = path11.join(projectRoot, config.componentsPath);
2462
2759
  for (const file of component.files) {
2463
- const targetPath = path9.join(targetDir, file.name);
2464
- if (await fs9.pathExists(targetPath) && !options.overwrite) {
2760
+ const targetPath = path11.join(targetDir, file.name);
2761
+ if (await fs10.pathExists(targetPath) && !options.overwrite) {
2465
2762
  installSpinner.warn(`${name}: ${file.name} already exists (use --overwrite)`);
2466
2763
  continue;
2467
2764
  }
2468
- await fs9.ensureDir(targetDir);
2765
+ await fs10.ensureDir(targetDir);
2469
2766
  const transformedContent = transformImports4(file.content, config);
2470
- await fs9.writeFile(targetPath, transformedContent);
2767
+ await fs10.writeFile(targetPath, transformedContent);
2471
2768
  }
2472
2769
  installSpinner.succeed(`Installed ${chalk9.green(name)}`);
2473
2770
  successCount++;
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@metacells/mcellui-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI for mcellui - add beautiful, accessible UI components to your Expo/React Native project",
5
5
  "author": "metacells",
6
6
  "license": "MIT",
7
7
  "homepage": "https://mcellui.dev",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/metacells/mcellui.git",
10
+ "url": "git+https://github.com/metacells-development/mcellui.git",
11
11
  "directory": "packages/cli"
12
12
  },
13
13
  "bugs": {
14
- "url": "https://github.com/metacells/mcellui/issues"
14
+ "url": "https://github.com/metacells-development/mcellui/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "react-native",
@@ -43,6 +43,7 @@
43
43
  "dependencies": {
44
44
  "commander": "^12.0.0",
45
45
  "chalk": "^5.3.0",
46
+ "diff": "^7.0.0",
46
47
  "ora": "^8.0.0",
47
48
  "prompts": "^2.4.2",
48
49
  "fs-extra": "^11.2.0",
@@ -52,6 +53,7 @@
52
53
  "zod": "^3.23.0"
53
54
  },
54
55
  "devDependencies": {
56
+ "@types/diff": "^6.0.0",
55
57
  "@types/fs-extra": "^11.0.0",
56
58
  "@types/node": "^20.0.0",
57
59
  "@types/prompts": "^2.4.0",