@react-grab/cli 0.0.68 → 0.0.70

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 (3) hide show
  1. package/dist/cli.cjs +355 -59
  2. package/dist/cli.js +356 -60
  3. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -27,21 +27,25 @@ var detectFramework = (projectRoot) => {
27
27
  if (!fs.existsSync(packageJsonPath)) {
28
28
  return "unknown";
29
29
  }
30
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
31
- const allDependencies = {
32
- ...packageJson.dependencies,
33
- ...packageJson.devDependencies
34
- };
35
- if (allDependencies["next"]) {
36
- return "next";
37
- }
38
- if (allDependencies["vite"]) {
39
- return "vite";
40
- }
41
- if (allDependencies["webpack"]) {
42
- return "webpack";
30
+ try {
31
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
32
+ const allDependencies = {
33
+ ...packageJson.dependencies,
34
+ ...packageJson.devDependencies
35
+ };
36
+ if (allDependencies["next"]) {
37
+ return "next";
38
+ }
39
+ if (allDependencies["vite"]) {
40
+ return "vite";
41
+ }
42
+ if (allDependencies["webpack"]) {
43
+ return "webpack";
44
+ }
45
+ return "unknown";
46
+ } catch {
47
+ return "unknown";
43
48
  }
44
- return "unknown";
45
49
  };
46
50
  var detectNextRouterType = (projectRoot) => {
47
51
  const hasAppDir = fs.existsSync(path.join(projectRoot, "app"));
@@ -65,9 +69,13 @@ var detectMonorepo = (projectRoot) => {
65
69
  }
66
70
  const packageJsonPath = path.join(projectRoot, "package.json");
67
71
  if (fs.existsSync(packageJsonPath)) {
68
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
69
- if (packageJson.workspaces) {
70
- return true;
72
+ try {
73
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
74
+ if (packageJson.workspaces) {
75
+ return true;
76
+ }
77
+ } catch {
78
+ return false;
71
79
  }
72
80
  }
73
81
  return false;
@@ -77,27 +85,63 @@ var detectReactGrab = (projectRoot) => {
77
85
  if (!fs.existsSync(packageJsonPath)) {
78
86
  return false;
79
87
  }
80
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
81
- const allDependencies = {
82
- ...packageJson.dependencies,
83
- ...packageJson.devDependencies
84
- };
85
- return Boolean(allDependencies["react-grab"]);
88
+ try {
89
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
90
+ const allDependencies = {
91
+ ...packageJson.dependencies,
92
+ ...packageJson.devDependencies
93
+ };
94
+ return Boolean(allDependencies["react-grab"]);
95
+ } catch {
96
+ return false;
97
+ }
86
98
  };
87
99
  var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
100
+ var detectUnsupportedFramework = (projectRoot) => {
101
+ const packageJsonPath = path.join(projectRoot, "package.json");
102
+ if (!fs.existsSync(packageJsonPath)) {
103
+ return null;
104
+ }
105
+ try {
106
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
107
+ const allDependencies = {
108
+ ...packageJson.dependencies,
109
+ ...packageJson.devDependencies
110
+ };
111
+ if (allDependencies["@remix-run/react"] || allDependencies["remix"]) {
112
+ return "remix";
113
+ }
114
+ if (allDependencies["astro"]) {
115
+ return "astro";
116
+ }
117
+ if (allDependencies["@sveltejs/kit"]) {
118
+ return "sveltekit";
119
+ }
120
+ if (allDependencies["gatsby"]) {
121
+ return "gatsby";
122
+ }
123
+ return null;
124
+ } catch {
125
+ return null;
126
+ }
127
+ };
88
128
  var detectInstalledAgents = (projectRoot) => {
89
129
  const packageJsonPath = path.join(projectRoot, "package.json");
90
130
  if (!fs.existsSync(packageJsonPath)) {
91
131
  return [];
92
132
  }
93
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
94
- const allDependencies = {
95
- ...packageJson.dependencies,
96
- ...packageJson.devDependencies
97
- };
98
- return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
99
- (agent) => agent.replace("@react-grab/", "")
100
- );
133
+ try {
134
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
135
+ const allDependencies = {
136
+ ...packageJson.dependencies,
137
+ ...packageJson.devDependencies
138
+ };
139
+ return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
140
+ (agent) => agent.replace("@react-grab/", "")
141
+ );
142
+ } catch {
143
+ return [];
144
+ }
101
145
  };
102
146
  var detectProject = async (projectRoot = process.cwd()) => {
103
147
  const framework = detectFramework(projectRoot);
@@ -109,7 +153,8 @@ var detectProject = async (projectRoot = process.cwd()) => {
109
153
  isMonorepo: detectMonorepo(projectRoot),
110
154
  projectRoot,
111
155
  hasReactGrab: detectReactGrab(projectRoot),
112
- installedAgents: detectInstalledAgents(projectRoot)
156
+ installedAgents: detectInstalledAgents(projectRoot),
157
+ unsupportedFramework: detectUnsupportedFramework(projectRoot)
113
158
  };
114
159
  };
115
160
 
@@ -553,7 +598,7 @@ var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured)
553
598
  return {
554
599
  success: false,
555
600
  filePath: "",
556
- message: "Could not find pages/_document.tsx. Please create one or add React Grab manually."
601
+ message: 'Could not find pages/_document.tsx or pages/_document.jsx.\n\nTo set up React Grab with Pages Router, create pages/_document.tsx with:\n\n import { Html, Head, Main, NextScript } from "next/document";\n import Script from "next/script";\n\n export default function Document() {\n return (\n <Html>\n <Head>\n {process.env.NODE_ENV === "development" && (\n <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />\n )}\n </Head>\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }'
557
602
  };
558
603
  }
559
604
  const originalContent = fs.readFileSync(documentPath, "utf-8");
@@ -681,14 +726,134 @@ var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrab
681
726
  };
682
727
  }
683
728
  };
729
+ var canWriteToFile = (filePath) => {
730
+ try {
731
+ fs.accessSync(filePath, fs.constants.W_OK);
732
+ return true;
733
+ } catch {
734
+ return false;
735
+ }
736
+ };
684
737
  var applyTransform = (result) => {
685
738
  if (result.success && result.newContent && result.filePath) {
686
- fs.writeFileSync(result.filePath, result.newContent);
739
+ if (!canWriteToFile(result.filePath)) {
740
+ return {
741
+ success: false,
742
+ error: `Cannot write to ${result.filePath}. Check file permissions.`
743
+ };
744
+ }
745
+ try {
746
+ fs.writeFileSync(result.filePath, result.newContent);
747
+ return { success: true };
748
+ } catch (error) {
749
+ return {
750
+ success: false,
751
+ error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
752
+ };
753
+ }
687
754
  }
755
+ return { success: true };
756
+ };
757
+ var AGENT_PREFIXES = {
758
+ "claude-code": "npx @react-grab/claude-code@latest &&",
759
+ cursor: "npx @react-grab/cursor@latest &&",
760
+ opencode: "npx @react-grab/opencode@latest &&"
761
+ };
762
+ var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
763
+ if (agent === "none") {
764
+ return {
765
+ success: true,
766
+ filePath: "",
767
+ message: "No agent selected, skipping package.json modification",
768
+ noChanges: true
769
+ };
770
+ }
771
+ const packageJsonPath = path.join(projectRoot, "package.json");
772
+ if (!fs.existsSync(packageJsonPath)) {
773
+ return {
774
+ success: false,
775
+ filePath: "",
776
+ message: "Could not find package.json"
777
+ };
778
+ }
779
+ const originalContent = fs.readFileSync(packageJsonPath, "utf-8");
780
+ const agentPrefix = AGENT_PREFIXES[agent];
781
+ if (!agentPrefix) {
782
+ return {
783
+ success: false,
784
+ filePath: packageJsonPath,
785
+ message: `Unknown agent: ${agent}`
786
+ };
787
+ }
788
+ if (originalContent.includes(agentPrefix)) {
789
+ return {
790
+ success: true,
791
+ filePath: packageJsonPath,
792
+ message: `Agent ${agent} dev script is already configured`,
793
+ noChanges: true
794
+ };
795
+ }
796
+ try {
797
+ const packageJson = JSON.parse(originalContent);
798
+ if (!packageJson.scripts?.dev) {
799
+ return {
800
+ success: false,
801
+ filePath: packageJsonPath,
802
+ message: 'No "dev" script found in package.json'
803
+ };
804
+ }
805
+ const currentDevScript = packageJson.scripts.dev;
806
+ for (const installedAgent of installedAgents) {
807
+ const existingPrefix = AGENT_PREFIXES[installedAgent];
808
+ if (existingPrefix && currentDevScript.includes(existingPrefix)) {
809
+ return {
810
+ success: true,
811
+ filePath: packageJsonPath,
812
+ message: `Agent ${installedAgent} is already in dev script`,
813
+ noChanges: true
814
+ };
815
+ }
816
+ }
817
+ packageJson.scripts.dev = `${agentPrefix} ${currentDevScript}`;
818
+ const newContent = JSON.stringify(packageJson, null, 2) + "\n";
819
+ return {
820
+ success: true,
821
+ filePath: packageJsonPath,
822
+ message: `Add ${agent} server to dev script`,
823
+ originalContent,
824
+ newContent
825
+ };
826
+ } catch {
827
+ return {
828
+ success: false,
829
+ filePath: packageJsonPath,
830
+ message: "Failed to parse package.json"
831
+ };
832
+ }
833
+ };
834
+ var applyPackageJsonTransform = (result) => {
835
+ if (result.success && result.newContent && result.filePath) {
836
+ if (!canWriteToFile(result.filePath)) {
837
+ return {
838
+ success: false,
839
+ error: `Cannot write to ${result.filePath}. Check file permissions.`
840
+ };
841
+ }
842
+ try {
843
+ fs.writeFileSync(result.filePath, result.newContent);
844
+ return { success: true };
845
+ } catch (error) {
846
+ return {
847
+ success: false,
848
+ error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
849
+ };
850
+ }
851
+ }
852
+ return { success: true };
688
853
  };
689
854
 
690
855
  // src/cli.ts
691
- var VERSION = "0.0.1";
856
+ var VERSION = "0.0.68";
692
857
  var FRAMEWORK_NAMES = {
693
858
  next: "Next.js",
694
859
  vite: "Vite",
@@ -706,43 +871,116 @@ var AGENT_NAMES = {
706
871
  cursor: "Cursor",
707
872
  opencode: "Opencode"
708
873
  };
709
- var DOCS_URL = "https://react-grab.com/docs";
874
+ var UNSUPPORTED_FRAMEWORK_NAMES = {
875
+ remix: "Remix",
876
+ astro: "Astro",
877
+ sveltekit: "SvelteKit",
878
+ gatsby: "Gatsby"
879
+ };
880
+ var DOCS_URL = "https://github.com/aidenybai/react-grab";
710
881
  var showDocsLink = () => {
711
882
  console.log("\nFor manual installation instructions, visit:");
712
- console.log(` ${DOCS_URL}
883
+ console.log(` ${pc__default.default.cyan(DOCS_URL)}
713
884
  `);
714
885
  };
886
+ var showManualInstructions = (framework, nextRouterType) => {
887
+ console.log(`
888
+ ${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("Manual Setup Instructions:")}
889
+ `);
890
+ if (framework === "next" && nextRouterType === "app") {
891
+ console.log(`${pc__default.default.bold("Next.js App Router:")}`);
892
+ console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
893
+ console.log(` 2. Add to ${pc__default.default.cyan("app/layout.tsx")}:`);
894
+ console.log(` ${pc__default.default.gray('import Script from "next/script";')}`);
895
+ console.log(` ${pc__default.default.gray("// Inside <head> or after <html>:")}`);
896
+ console.log(` ${pc__default.default.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
897
+ } else if (framework === "next" && nextRouterType === "pages") {
898
+ console.log(`${pc__default.default.bold("Next.js Pages Router:")}`);
899
+ console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
900
+ console.log(` 2. Create or edit ${pc__default.default.cyan("pages/_document.tsx")}:`);
901
+ console.log(` ${pc__default.default.gray('import Script from "next/script";')}`);
902
+ console.log(` ${pc__default.default.gray("// Inside <Head>:")}`);
903
+ console.log(` ${pc__default.default.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
904
+ } else if (framework === "vite") {
905
+ console.log(`${pc__default.default.bold("Vite:")}`);
906
+ console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
907
+ console.log(` 2. Add to ${pc__default.default.cyan("index.html")} inside <head>:`);
908
+ console.log(` ${pc__default.default.gray('<script type="module">')}`);
909
+ console.log(` ${pc__default.default.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`);
910
+ console.log(` ${pc__default.default.gray("</script>")}`);
911
+ } else if (framework === "webpack") {
912
+ console.log(`${pc__default.default.bold("Webpack:")}`);
913
+ console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
914
+ console.log(` 2. Add to your entry file (e.g., ${pc__default.default.cyan("src/index.tsx")}):`);
915
+ console.log(` ${pc__default.default.gray('if (process.env.NODE_ENV === "development") {')}`);
916
+ console.log(` ${pc__default.default.gray(' import("react-grab");')}`);
917
+ console.log(` ${pc__default.default.gray("}")}`);
918
+ } else {
919
+ console.log(`${pc__default.default.bold("Generic Setup:")}`);
920
+ console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
921
+ console.log(` 2. Add the script to your HTML or entry file.`);
922
+ console.log(` 3. See docs for framework-specific instructions.`);
923
+ }
924
+ showDocsLink();
925
+ };
926
+ var showAccuracyWarning = () => {
927
+ console.log(`
928
+ ${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("Auto-detection may not be 100% accurate.")}`);
929
+ console.log(`${pc__default.default.yellow(" Please verify the changes in your file before committing.")}`);
930
+ };
715
931
  var parseArgs = async () => {
716
- const argv = await yargs__default.default(helpers.hideBin(process.argv)).usage("Usage: $0 [options]").option("framework", {
932
+ const argv = await yargs__default.default(helpers.hideBin(process.argv)).scriptName("react-grab").usage(
933
+ `${pc__default.default.magenta("\u269B")} ${pc__default.default.bold("React Grab CLI")} ${pc__default.default.gray(`v${VERSION}`)}
934
+
935
+ ${pc__default.default.cyan("Usage:")} $0 [options]
936
+
937
+ React Grab adds AI-powered context selection to your React application,
938
+ allowing you to select components and copy their context for AI assistants.
939
+
940
+ The CLI auto-detects your project configuration (framework, package manager,
941
+ router type) and installs React Grab with optional agent integrations.`
942
+ ).option("framework", {
717
943
  alias: "f",
718
944
  type: "string",
719
945
  choices: ["next", "vite", "webpack"],
720
- description: "Framework to configure (next, vite, webpack)"
946
+ description: "Override detected framework"
721
947
  }).option("package-manager", {
722
948
  alias: "p",
723
949
  type: "string",
724
950
  choices: ["npm", "yarn", "pnpm", "bun"],
725
- description: "Package manager to use"
951
+ description: "Override detected package manager"
726
952
  }).option("router", {
727
953
  alias: "r",
728
954
  type: "string",
729
955
  choices: ["app", "pages"],
730
- description: "Next.js router type (app or pages)"
956
+ description: "Next.js router type (only for Next.js projects)"
731
957
  }).option("agent", {
732
958
  alias: "a",
733
959
  type: "string",
734
960
  choices: ["claude-code", "cursor", "opencode", "none"],
735
- description: "Agent integration to add"
961
+ description: "Agent integration to automatically forward selected elements to agent instead of copying to clipboard"
736
962
  }).option("yes", {
737
963
  alias: "y",
738
964
  type: "boolean",
739
965
  default: false,
740
- description: "Skip all confirmation prompts"
966
+ description: "Skip all prompts and use auto-detected/default values"
741
967
  }).option("skip-install", {
742
968
  type: "boolean",
743
969
  default: false,
744
- description: "Skip package installation (only modify files)"
745
- }).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup").example("$0 -y", "Auto-detect and install without prompts").example("$0 -f next -r app -a cursor", "Install for Next.js App Router with Cursor agent").example("$0 -p pnpm -a claude-code -y", "Use pnpm and add Claude Code agent").parse();
970
+ description: "Only modify config files, skip npm/yarn/pnpm install"
971
+ }).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup with prompts").example("$0 -y", "Auto-detect everything and install without prompts").example("$0 -f next -r app", "Configure for Next.js App Router").example("$0 -a cursor -y", "Add Cursor agent integration non-interactively").example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent").example("$0 --skip-install", "Only modify files, install packages manually").epilog(
972
+ `${pc__default.default.bold("Agent Integrations:")}
973
+ ${pc__default.default.cyan("claude-code")} Connect React Grab to Claude Code
974
+ ${pc__default.default.cyan("cursor")} Connect React Grab to Cursor IDE
975
+ ${pc__default.default.cyan("opencode")} Connect React Grab to Opencode
976
+
977
+ ${pc__default.default.bold("Supported Frameworks:")}
978
+ ${pc__default.default.cyan("next")} Next.js (App Router & Pages Router)
979
+ ${pc__default.default.cyan("vite")} Vite-based React projects
980
+ ${pc__default.default.cyan("webpack")} Webpack-based React projects
981
+
982
+ ${pc__default.default.bold("Documentation:")} ${pc__default.default.underline(DOCS_URL)}`
983
+ ).wrap(Math.min(100, process.stdout.columns || 80)).parse();
746
984
  return {
747
985
  framework: argv.framework,
748
986
  packageManager: argv["package-manager"],
@@ -769,6 +1007,20 @@ ${pc__default.default.magenta("\u269B")} ${pc__default.default.bold("React Grab"
769
1007
  console.log(`- Agents: ${pc__default.default.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`);
770
1008
  }
771
1009
  console.log("");
1010
+ if (projectInfo.unsupportedFramework) {
1011
+ const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
1012
+ console.log(`${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow(`Detected ${frameworkName} - this framework requires manual setup.`)}`);
1013
+ console.log(`${pc__default.default.yellow(" React Grab may not work correctly with auto-configuration.")}
1014
+ `);
1015
+ showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
1016
+ process.exit(0);
1017
+ }
1018
+ if (projectInfo.framework === "unknown") {
1019
+ console.log(`${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("Could not detect framework automatically.")}
1020
+ `);
1021
+ showManualInstructions("unknown");
1022
+ process.exit(0);
1023
+ }
772
1024
  let action = "install-all";
773
1025
  if (projectInfo.hasReactGrab && !isNonInteractive) {
774
1026
  action = await prompts.select({
@@ -781,6 +1033,11 @@ ${pc__default.default.magenta("\u269B")} ${pc__default.default.bold("React Grab"
781
1033
  });
782
1034
  } else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
783
1035
  action = "add-agent";
1036
+ } else if (projectInfo.hasReactGrab && isNonInteractive && !args.agent) {
1037
+ console.log(`${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("React Grab is already installed.")}`);
1038
+ console.log(`${pc__default.default.yellow(" Use --agent to add an agent, or run without -y for interactive mode.")}
1039
+ `);
1040
+ action = "reconfigure";
784
1041
  }
785
1042
  let finalFramework = args.framework || projectInfo.framework;
786
1043
  let finalPackageManager = args.packageManager || projectInfo.packageManager;
@@ -864,25 +1121,46 @@ ${pc__default.default.magenta("\u269B")} Previewing changes...
864
1121
  agentIntegration,
865
1122
  projectInfo.hasReactGrab || action === "add-agent"
866
1123
  );
1124
+ const packageJsonResult = previewPackageJsonTransform(
1125
+ projectInfo.projectRoot,
1126
+ agentIntegration,
1127
+ projectInfo.installedAgents
1128
+ );
867
1129
  if (!result.success) {
868
1130
  console.error(`${pc__default.default.red("Error:")} ${result.message}`);
869
1131
  showDocsLink();
870
1132
  process.exit(1);
871
1133
  }
872
- if (result.noChanges) {
1134
+ const hasLayoutChanges = !result.noChanges && result.originalContent && result.newContent;
1135
+ const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
1136
+ if (result.noChanges && packageJsonResult.noChanges) {
873
1137
  console.log(`${pc__default.default.cyan("Info:")} ${result.message}`);
874
- } else if (result.originalContent && result.newContent) {
875
- printDiff(result.filePath, result.originalContent, result.newContent);
876
- if (!isNonInteractive) {
877
- const confirmChanges = await prompts.confirm({
878
- message: "Apply these changes?",
879
- default: true
880
- });
881
- if (!confirmChanges) {
882
- console.log(`
1138
+ if (packageJsonResult.message) {
1139
+ console.log(`${pc__default.default.cyan("Info:")} ${packageJsonResult.message}`);
1140
+ }
1141
+ } else {
1142
+ if (hasLayoutChanges) {
1143
+ printDiff(result.filePath, result.originalContent, result.newContent);
1144
+ }
1145
+ if (hasPackageJsonChanges) {
1146
+ if (hasLayoutChanges) {
1147
+ console.log("");
1148
+ }
1149
+ printDiff(packageJsonResult.filePath, packageJsonResult.originalContent, packageJsonResult.newContent);
1150
+ }
1151
+ if (hasLayoutChanges || hasPackageJsonChanges) {
1152
+ showAccuracyWarning();
1153
+ if (!isNonInteractive) {
1154
+ const confirmChanges = await prompts.confirm({
1155
+ message: "Apply these changes?",
1156
+ default: true
1157
+ });
1158
+ if (!confirmChanges) {
1159
+ console.log(`
883
1160
  ${pc__default.default.yellow("Changes cancelled.")}
884
1161
  `);
885
- process.exit(0);
1162
+ process.exit(0);
1163
+ }
886
1164
  }
887
1165
  }
888
1166
  const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
@@ -906,9 +1184,27 @@ ${pc__default.default.red("Failed to install packages:")}`, error);
906
1184
  }
907
1185
  }
908
1186
  }
909
- applyTransform(result);
910
- console.log(`
1187
+ if (hasLayoutChanges) {
1188
+ const writeResult = applyTransform(result);
1189
+ if (!writeResult.success) {
1190
+ console.error(`
1191
+ ${pc__default.default.red("Error:")} ${writeResult.error}`);
1192
+ showDocsLink();
1193
+ process.exit(1);
1194
+ }
1195
+ console.log(`
911
1196
  ${pc__default.default.green("Applied:")} ${result.filePath}`);
1197
+ }
1198
+ if (hasPackageJsonChanges) {
1199
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
1200
+ if (!packageJsonWriteResult.success) {
1201
+ console.error(`
1202
+ ${pc__default.default.red("Error:")} ${packageJsonWriteResult.error}`);
1203
+ showDocsLink();
1204
+ process.exit(1);
1205
+ }
1206
+ console.log(`${pc__default.default.green("Applied:")} ${packageJsonResult.filePath}`);
1207
+ }
912
1208
  }
913
1209
  }
914
1210
  console.log(`
@@ -917,7 +1213,7 @@ ${pc__default.default.green("Done!")}`);
917
1213
  Next steps:`);
918
1214
  console.log(` - Start your development server`);
919
1215
  console.log(` - Select an element to copy its context`);
920
- console.log(` - Learn more at ${pc__default.default.cyan("https://react-grab.com")}
1216
+ console.log(` - Learn more at ${pc__default.default.cyan(DOCS_URL)}
921
1217
  `);
922
1218
  if (agentIntegration !== "none") {
923
1219
  console.log(`${pc__default.default.magenta("\u269B")} Agent: ${pc__default.default.cyan(AGENT_NAMES[agentIntegration])}`);
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { select, confirm } from '@inquirer/prompts';
3
3
  import pc from 'picocolors';
4
4
  import yargs from 'yargs';
5
5
  import { hideBin } from 'yargs/helpers';
6
- import { writeFileSync, existsSync, readFileSync } from 'fs';
6
+ import { existsSync, readFileSync, writeFileSync, accessSync, constants } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { detect } from '@antfu/ni';
9
9
  import { execSync } from 'child_process';
@@ -20,21 +20,25 @@ var detectFramework = (projectRoot) => {
20
20
  if (!existsSync(packageJsonPath)) {
21
21
  return "unknown";
22
22
  }
23
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
24
- const allDependencies = {
25
- ...packageJson.dependencies,
26
- ...packageJson.devDependencies
27
- };
28
- if (allDependencies["next"]) {
29
- return "next";
30
- }
31
- if (allDependencies["vite"]) {
32
- return "vite";
33
- }
34
- if (allDependencies["webpack"]) {
35
- return "webpack";
23
+ try {
24
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
25
+ const allDependencies = {
26
+ ...packageJson.dependencies,
27
+ ...packageJson.devDependencies
28
+ };
29
+ if (allDependencies["next"]) {
30
+ return "next";
31
+ }
32
+ if (allDependencies["vite"]) {
33
+ return "vite";
34
+ }
35
+ if (allDependencies["webpack"]) {
36
+ return "webpack";
37
+ }
38
+ return "unknown";
39
+ } catch {
40
+ return "unknown";
36
41
  }
37
- return "unknown";
38
42
  };
39
43
  var detectNextRouterType = (projectRoot) => {
40
44
  const hasAppDir = existsSync(join(projectRoot, "app"));
@@ -58,9 +62,13 @@ var detectMonorepo = (projectRoot) => {
58
62
  }
59
63
  const packageJsonPath = join(projectRoot, "package.json");
60
64
  if (existsSync(packageJsonPath)) {
61
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
62
- if (packageJson.workspaces) {
63
- return true;
65
+ try {
66
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
67
+ if (packageJson.workspaces) {
68
+ return true;
69
+ }
70
+ } catch {
71
+ return false;
64
72
  }
65
73
  }
66
74
  return false;
@@ -70,27 +78,63 @@ var detectReactGrab = (projectRoot) => {
70
78
  if (!existsSync(packageJsonPath)) {
71
79
  return false;
72
80
  }
73
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
74
- const allDependencies = {
75
- ...packageJson.dependencies,
76
- ...packageJson.devDependencies
77
- };
78
- return Boolean(allDependencies["react-grab"]);
81
+ try {
82
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
83
+ const allDependencies = {
84
+ ...packageJson.dependencies,
85
+ ...packageJson.devDependencies
86
+ };
87
+ return Boolean(allDependencies["react-grab"]);
88
+ } catch {
89
+ return false;
90
+ }
79
91
  };
80
92
  var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
93
+ var detectUnsupportedFramework = (projectRoot) => {
94
+ const packageJsonPath = join(projectRoot, "package.json");
95
+ if (!existsSync(packageJsonPath)) {
96
+ return null;
97
+ }
98
+ try {
99
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
100
+ const allDependencies = {
101
+ ...packageJson.dependencies,
102
+ ...packageJson.devDependencies
103
+ };
104
+ if (allDependencies["@remix-run/react"] || allDependencies["remix"]) {
105
+ return "remix";
106
+ }
107
+ if (allDependencies["astro"]) {
108
+ return "astro";
109
+ }
110
+ if (allDependencies["@sveltejs/kit"]) {
111
+ return "sveltekit";
112
+ }
113
+ if (allDependencies["gatsby"]) {
114
+ return "gatsby";
115
+ }
116
+ return null;
117
+ } catch {
118
+ return null;
119
+ }
120
+ };
81
121
  var detectInstalledAgents = (projectRoot) => {
82
122
  const packageJsonPath = join(projectRoot, "package.json");
83
123
  if (!existsSync(packageJsonPath)) {
84
124
  return [];
85
125
  }
86
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
87
- const allDependencies = {
88
- ...packageJson.dependencies,
89
- ...packageJson.devDependencies
90
- };
91
- return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
92
- (agent) => agent.replace("@react-grab/", "")
93
- );
126
+ try {
127
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
128
+ const allDependencies = {
129
+ ...packageJson.dependencies,
130
+ ...packageJson.devDependencies
131
+ };
132
+ return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
133
+ (agent) => agent.replace("@react-grab/", "")
134
+ );
135
+ } catch {
136
+ return [];
137
+ }
94
138
  };
95
139
  var detectProject = async (projectRoot = process.cwd()) => {
96
140
  const framework = detectFramework(projectRoot);
@@ -102,7 +146,8 @@ var detectProject = async (projectRoot = process.cwd()) => {
102
146
  isMonorepo: detectMonorepo(projectRoot),
103
147
  projectRoot,
104
148
  hasReactGrab: detectReactGrab(projectRoot),
105
- installedAgents: detectInstalledAgents(projectRoot)
149
+ installedAgents: detectInstalledAgents(projectRoot),
150
+ unsupportedFramework: detectUnsupportedFramework(projectRoot)
106
151
  };
107
152
  };
108
153
 
@@ -546,7 +591,7 @@ var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured)
546
591
  return {
547
592
  success: false,
548
593
  filePath: "",
549
- message: "Could not find pages/_document.tsx. Please create one or add React Grab manually."
594
+ message: 'Could not find pages/_document.tsx or pages/_document.jsx.\n\nTo set up React Grab with Pages Router, create pages/_document.tsx with:\n\n import { Html, Head, Main, NextScript } from "next/document";\n import Script from "next/script";\n\n export default function Document() {\n return (\n <Html>\n <Head>\n {process.env.NODE_ENV === "development" && (\n <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />\n )}\n </Head>\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }'
550
595
  };
551
596
  }
552
597
  const originalContent = readFileSync(documentPath, "utf-8");
@@ -674,14 +719,134 @@ var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrab
674
719
  };
675
720
  }
676
721
  };
722
+ var canWriteToFile = (filePath) => {
723
+ try {
724
+ accessSync(filePath, constants.W_OK);
725
+ return true;
726
+ } catch {
727
+ return false;
728
+ }
729
+ };
677
730
  var applyTransform = (result) => {
678
731
  if (result.success && result.newContent && result.filePath) {
679
- writeFileSync(result.filePath, result.newContent);
732
+ if (!canWriteToFile(result.filePath)) {
733
+ return {
734
+ success: false,
735
+ error: `Cannot write to ${result.filePath}. Check file permissions.`
736
+ };
737
+ }
738
+ try {
739
+ writeFileSync(result.filePath, result.newContent);
740
+ return { success: true };
741
+ } catch (error) {
742
+ return {
743
+ success: false,
744
+ error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
745
+ };
746
+ }
680
747
  }
748
+ return { success: true };
749
+ };
750
+ var AGENT_PREFIXES = {
751
+ "claude-code": "npx @react-grab/claude-code@latest &&",
752
+ cursor: "npx @react-grab/cursor@latest &&",
753
+ opencode: "npx @react-grab/opencode@latest &&"
754
+ };
755
+ var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
756
+ if (agent === "none") {
757
+ return {
758
+ success: true,
759
+ filePath: "",
760
+ message: "No agent selected, skipping package.json modification",
761
+ noChanges: true
762
+ };
763
+ }
764
+ const packageJsonPath = join(projectRoot, "package.json");
765
+ if (!existsSync(packageJsonPath)) {
766
+ return {
767
+ success: false,
768
+ filePath: "",
769
+ message: "Could not find package.json"
770
+ };
771
+ }
772
+ const originalContent = readFileSync(packageJsonPath, "utf-8");
773
+ const agentPrefix = AGENT_PREFIXES[agent];
774
+ if (!agentPrefix) {
775
+ return {
776
+ success: false,
777
+ filePath: packageJsonPath,
778
+ message: `Unknown agent: ${agent}`
779
+ };
780
+ }
781
+ if (originalContent.includes(agentPrefix)) {
782
+ return {
783
+ success: true,
784
+ filePath: packageJsonPath,
785
+ message: `Agent ${agent} dev script is already configured`,
786
+ noChanges: true
787
+ };
788
+ }
789
+ try {
790
+ const packageJson = JSON.parse(originalContent);
791
+ if (!packageJson.scripts?.dev) {
792
+ return {
793
+ success: false,
794
+ filePath: packageJsonPath,
795
+ message: 'No "dev" script found in package.json'
796
+ };
797
+ }
798
+ const currentDevScript = packageJson.scripts.dev;
799
+ for (const installedAgent of installedAgents) {
800
+ const existingPrefix = AGENT_PREFIXES[installedAgent];
801
+ if (existingPrefix && currentDevScript.includes(existingPrefix)) {
802
+ return {
803
+ success: true,
804
+ filePath: packageJsonPath,
805
+ message: `Agent ${installedAgent} is already in dev script`,
806
+ noChanges: true
807
+ };
808
+ }
809
+ }
810
+ packageJson.scripts.dev = `${agentPrefix} ${currentDevScript}`;
811
+ const newContent = JSON.stringify(packageJson, null, 2) + "\n";
812
+ return {
813
+ success: true,
814
+ filePath: packageJsonPath,
815
+ message: `Add ${agent} server to dev script`,
816
+ originalContent,
817
+ newContent
818
+ };
819
+ } catch {
820
+ return {
821
+ success: false,
822
+ filePath: packageJsonPath,
823
+ message: "Failed to parse package.json"
824
+ };
825
+ }
826
+ };
827
+ var applyPackageJsonTransform = (result) => {
828
+ if (result.success && result.newContent && result.filePath) {
829
+ if (!canWriteToFile(result.filePath)) {
830
+ return {
831
+ success: false,
832
+ error: `Cannot write to ${result.filePath}. Check file permissions.`
833
+ };
834
+ }
835
+ try {
836
+ writeFileSync(result.filePath, result.newContent);
837
+ return { success: true };
838
+ } catch (error) {
839
+ return {
840
+ success: false,
841
+ error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
842
+ };
843
+ }
844
+ }
845
+ return { success: true };
681
846
  };
682
847
 
683
848
  // src/cli.ts
684
- var VERSION = "0.0.1";
849
+ var VERSION = "0.0.68";
685
850
  var FRAMEWORK_NAMES = {
686
851
  next: "Next.js",
687
852
  vite: "Vite",
@@ -699,43 +864,116 @@ var AGENT_NAMES = {
699
864
  cursor: "Cursor",
700
865
  opencode: "Opencode"
701
866
  };
702
- var DOCS_URL = "https://react-grab.com/docs";
867
+ var UNSUPPORTED_FRAMEWORK_NAMES = {
868
+ remix: "Remix",
869
+ astro: "Astro",
870
+ sveltekit: "SvelteKit",
871
+ gatsby: "Gatsby"
872
+ };
873
+ var DOCS_URL = "https://github.com/aidenybai/react-grab";
703
874
  var showDocsLink = () => {
704
875
  console.log("\nFor manual installation instructions, visit:");
705
- console.log(` ${DOCS_URL}
876
+ console.log(` ${pc.cyan(DOCS_URL)}
706
877
  `);
707
878
  };
879
+ var showManualInstructions = (framework, nextRouterType) => {
880
+ console.log(`
881
+ ${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Manual Setup Instructions:")}
882
+ `);
883
+ if (framework === "next" && nextRouterType === "app") {
884
+ console.log(`${pc.bold("Next.js App Router:")}`);
885
+ console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
886
+ console.log(` 2. Add to ${pc.cyan("app/layout.tsx")}:`);
887
+ console.log(` ${pc.gray('import Script from "next/script";')}`);
888
+ console.log(` ${pc.gray("// Inside <head> or after <html>:")}`);
889
+ console.log(` ${pc.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
890
+ } else if (framework === "next" && nextRouterType === "pages") {
891
+ console.log(`${pc.bold("Next.js Pages Router:")}`);
892
+ console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
893
+ console.log(` 2. Create or edit ${pc.cyan("pages/_document.tsx")}:`);
894
+ console.log(` ${pc.gray('import Script from "next/script";')}`);
895
+ console.log(` ${pc.gray("// Inside <Head>:")}`);
896
+ console.log(` ${pc.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
897
+ } else if (framework === "vite") {
898
+ console.log(`${pc.bold("Vite:")}`);
899
+ console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
900
+ console.log(` 2. Add to ${pc.cyan("index.html")} inside <head>:`);
901
+ console.log(` ${pc.gray('<script type="module">')}`);
902
+ console.log(` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`);
903
+ console.log(` ${pc.gray("</script>")}`);
904
+ } else if (framework === "webpack") {
905
+ console.log(`${pc.bold("Webpack:")}`);
906
+ console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
907
+ console.log(` 2. Add to your entry file (e.g., ${pc.cyan("src/index.tsx")}):`);
908
+ console.log(` ${pc.gray('if (process.env.NODE_ENV === "development") {')}`);
909
+ console.log(` ${pc.gray(' import("react-grab");')}`);
910
+ console.log(` ${pc.gray("}")}`);
911
+ } else {
912
+ console.log(`${pc.bold("Generic Setup:")}`);
913
+ console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
914
+ console.log(` 2. Add the script to your HTML or entry file.`);
915
+ console.log(` 3. See docs for framework-specific instructions.`);
916
+ }
917
+ showDocsLink();
918
+ };
919
+ var showAccuracyWarning = () => {
920
+ console.log(`
921
+ ${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Auto-detection may not be 100% accurate.")}`);
922
+ console.log(`${pc.yellow(" Please verify the changes in your file before committing.")}`);
923
+ };
708
924
  var parseArgs = async () => {
709
- const argv = await yargs(hideBin(process.argv)).usage("Usage: $0 [options]").option("framework", {
925
+ const argv = await yargs(hideBin(process.argv)).scriptName("react-grab").usage(
926
+ `${pc.magenta("\u269B")} ${pc.bold("React Grab CLI")} ${pc.gray(`v${VERSION}`)}
927
+
928
+ ${pc.cyan("Usage:")} $0 [options]
929
+
930
+ React Grab adds AI-powered context selection to your React application,
931
+ allowing you to select components and copy their context for AI assistants.
932
+
933
+ The CLI auto-detects your project configuration (framework, package manager,
934
+ router type) and installs React Grab with optional agent integrations.`
935
+ ).option("framework", {
710
936
  alias: "f",
711
937
  type: "string",
712
938
  choices: ["next", "vite", "webpack"],
713
- description: "Framework to configure (next, vite, webpack)"
939
+ description: "Override detected framework"
714
940
  }).option("package-manager", {
715
941
  alias: "p",
716
942
  type: "string",
717
943
  choices: ["npm", "yarn", "pnpm", "bun"],
718
- description: "Package manager to use"
944
+ description: "Override detected package manager"
719
945
  }).option("router", {
720
946
  alias: "r",
721
947
  type: "string",
722
948
  choices: ["app", "pages"],
723
- description: "Next.js router type (app or pages)"
949
+ description: "Next.js router type (only for Next.js projects)"
724
950
  }).option("agent", {
725
951
  alias: "a",
726
952
  type: "string",
727
953
  choices: ["claude-code", "cursor", "opencode", "none"],
728
- description: "Agent integration to add"
954
+ description: "Agent integration to automatically forward selected elements to agent instead of copying to clipboard"
729
955
  }).option("yes", {
730
956
  alias: "y",
731
957
  type: "boolean",
732
958
  default: false,
733
- description: "Skip all confirmation prompts"
959
+ description: "Skip all prompts and use auto-detected/default values"
734
960
  }).option("skip-install", {
735
961
  type: "boolean",
736
962
  default: false,
737
- description: "Skip package installation (only modify files)"
738
- }).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup").example("$0 -y", "Auto-detect and install without prompts").example("$0 -f next -r app -a cursor", "Install for Next.js App Router with Cursor agent").example("$0 -p pnpm -a claude-code -y", "Use pnpm and add Claude Code agent").parse();
963
+ description: "Only modify config files, skip npm/yarn/pnpm install"
964
+ }).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup with prompts").example("$0 -y", "Auto-detect everything and install without prompts").example("$0 -f next -r app", "Configure for Next.js App Router").example("$0 -a cursor -y", "Add Cursor agent integration non-interactively").example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent").example("$0 --skip-install", "Only modify files, install packages manually").epilog(
965
+ `${pc.bold("Agent Integrations:")}
966
+ ${pc.cyan("claude-code")} Connect React Grab to Claude Code
967
+ ${pc.cyan("cursor")} Connect React Grab to Cursor IDE
968
+ ${pc.cyan("opencode")} Connect React Grab to Opencode
969
+
970
+ ${pc.bold("Supported Frameworks:")}
971
+ ${pc.cyan("next")} Next.js (App Router & Pages Router)
972
+ ${pc.cyan("vite")} Vite-based React projects
973
+ ${pc.cyan("webpack")} Webpack-based React projects
974
+
975
+ ${pc.bold("Documentation:")} ${pc.underline(DOCS_URL)}`
976
+ ).wrap(Math.min(100, process.stdout.columns || 80)).parse();
739
977
  return {
740
978
  framework: argv.framework,
741
979
  packageManager: argv["package-manager"],
@@ -762,6 +1000,20 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
762
1000
  console.log(`- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`);
763
1001
  }
764
1002
  console.log("");
1003
+ if (projectInfo.unsupportedFramework) {
1004
+ const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
1005
+ console.log(`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow(`Detected ${frameworkName} - this framework requires manual setup.`)}`);
1006
+ console.log(`${pc.yellow(" React Grab may not work correctly with auto-configuration.")}
1007
+ `);
1008
+ showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
1009
+ process.exit(0);
1010
+ }
1011
+ if (projectInfo.framework === "unknown") {
1012
+ console.log(`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
1013
+ `);
1014
+ showManualInstructions("unknown");
1015
+ process.exit(0);
1016
+ }
765
1017
  let action = "install-all";
766
1018
  if (projectInfo.hasReactGrab && !isNonInteractive) {
767
1019
  action = await select({
@@ -774,6 +1026,11 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
774
1026
  });
775
1027
  } else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
776
1028
  action = "add-agent";
1029
+ } else if (projectInfo.hasReactGrab && isNonInteractive && !args.agent) {
1030
+ console.log(`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("React Grab is already installed.")}`);
1031
+ console.log(`${pc.yellow(" Use --agent to add an agent, or run without -y for interactive mode.")}
1032
+ `);
1033
+ action = "reconfigure";
777
1034
  }
778
1035
  let finalFramework = args.framework || projectInfo.framework;
779
1036
  let finalPackageManager = args.packageManager || projectInfo.packageManager;
@@ -857,25 +1114,46 @@ ${pc.magenta("\u269B")} Previewing changes...
857
1114
  agentIntegration,
858
1115
  projectInfo.hasReactGrab || action === "add-agent"
859
1116
  );
1117
+ const packageJsonResult = previewPackageJsonTransform(
1118
+ projectInfo.projectRoot,
1119
+ agentIntegration,
1120
+ projectInfo.installedAgents
1121
+ );
860
1122
  if (!result.success) {
861
1123
  console.error(`${pc.red("Error:")} ${result.message}`);
862
1124
  showDocsLink();
863
1125
  process.exit(1);
864
1126
  }
865
- if (result.noChanges) {
1127
+ const hasLayoutChanges = !result.noChanges && result.originalContent && result.newContent;
1128
+ const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
1129
+ if (result.noChanges && packageJsonResult.noChanges) {
866
1130
  console.log(`${pc.cyan("Info:")} ${result.message}`);
867
- } else if (result.originalContent && result.newContent) {
868
- printDiff(result.filePath, result.originalContent, result.newContent);
869
- if (!isNonInteractive) {
870
- const confirmChanges = await confirm({
871
- message: "Apply these changes?",
872
- default: true
873
- });
874
- if (!confirmChanges) {
875
- console.log(`
1131
+ if (packageJsonResult.message) {
1132
+ console.log(`${pc.cyan("Info:")} ${packageJsonResult.message}`);
1133
+ }
1134
+ } else {
1135
+ if (hasLayoutChanges) {
1136
+ printDiff(result.filePath, result.originalContent, result.newContent);
1137
+ }
1138
+ if (hasPackageJsonChanges) {
1139
+ if (hasLayoutChanges) {
1140
+ console.log("");
1141
+ }
1142
+ printDiff(packageJsonResult.filePath, packageJsonResult.originalContent, packageJsonResult.newContent);
1143
+ }
1144
+ if (hasLayoutChanges || hasPackageJsonChanges) {
1145
+ showAccuracyWarning();
1146
+ if (!isNonInteractive) {
1147
+ const confirmChanges = await confirm({
1148
+ message: "Apply these changes?",
1149
+ default: true
1150
+ });
1151
+ if (!confirmChanges) {
1152
+ console.log(`
876
1153
  ${pc.yellow("Changes cancelled.")}
877
1154
  `);
878
- process.exit(0);
1155
+ process.exit(0);
1156
+ }
879
1157
  }
880
1158
  }
881
1159
  const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
@@ -899,9 +1177,27 @@ ${pc.red("Failed to install packages:")}`, error);
899
1177
  }
900
1178
  }
901
1179
  }
902
- applyTransform(result);
903
- console.log(`
1180
+ if (hasLayoutChanges) {
1181
+ const writeResult = applyTransform(result);
1182
+ if (!writeResult.success) {
1183
+ console.error(`
1184
+ ${pc.red("Error:")} ${writeResult.error}`);
1185
+ showDocsLink();
1186
+ process.exit(1);
1187
+ }
1188
+ console.log(`
904
1189
  ${pc.green("Applied:")} ${result.filePath}`);
1190
+ }
1191
+ if (hasPackageJsonChanges) {
1192
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
1193
+ if (!packageJsonWriteResult.success) {
1194
+ console.error(`
1195
+ ${pc.red("Error:")} ${packageJsonWriteResult.error}`);
1196
+ showDocsLink();
1197
+ process.exit(1);
1198
+ }
1199
+ console.log(`${pc.green("Applied:")} ${packageJsonResult.filePath}`);
1200
+ }
905
1201
  }
906
1202
  }
907
1203
  console.log(`
@@ -910,7 +1206,7 @@ ${pc.green("Done!")}`);
910
1206
  Next steps:`);
911
1207
  console.log(` - Start your development server`);
912
1208
  console.log(` - Select an element to copy its context`);
913
- console.log(` - Learn more at ${pc.cyan("https://react-grab.com")}
1209
+ console.log(` - Learn more at ${pc.cyan(DOCS_URL)}
914
1210
  `);
915
1211
  if (agentIntegration !== "none") {
916
1212
  console.log(`${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-grab/cli",
3
- "version": "0.0.68",
3
+ "version": "0.0.70",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "react-grab": "./dist/cli.js"