@react-grab/cli 0.0.70 → 0.0.71

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 +453 -76
  2. package/dist/cli.js +455 -78
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3,8 +3,8 @@ 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 { existsSync, readFileSync, writeFileSync, accessSync, constants } from 'fs';
7
- import { join } from 'path';
6
+ import { readFileSync, existsSync, writeFileSync, readdirSync, accessSync, constants } from 'fs';
7
+ import { join, basename } from 'path';
8
8
  import { detect } from '@antfu/ni';
9
9
  import { execSync } from 'child_process';
10
10
 
@@ -73,22 +73,165 @@ var detectMonorepo = (projectRoot) => {
73
73
  }
74
74
  return false;
75
75
  };
76
- var detectReactGrab = (projectRoot) => {
76
+ var getWorkspacePatterns = (projectRoot) => {
77
+ const patterns = [];
78
+ const pnpmWorkspacePath = join(projectRoot, "pnpm-workspace.yaml");
79
+ if (existsSync(pnpmWorkspacePath)) {
80
+ const content = readFileSync(pnpmWorkspacePath, "utf-8");
81
+ const lines = content.split("\n");
82
+ let inPackages = false;
83
+ for (const line of lines) {
84
+ if (line.match(/^packages:\s*$/)) {
85
+ inPackages = true;
86
+ continue;
87
+ }
88
+ if (inPackages) {
89
+ if (line.match(/^[a-zA-Z]/) || line.trim() === "") {
90
+ if (line.match(/^[a-zA-Z]/)) inPackages = false;
91
+ continue;
92
+ }
93
+ const match = line.match(/^\s*-\s*['"]?([^'"#\n]+?)['"]?\s*$/);
94
+ if (match) {
95
+ patterns.push(match[1].trim());
96
+ }
97
+ }
98
+ }
99
+ }
100
+ const lernaJsonPath = join(projectRoot, "lerna.json");
101
+ if (existsSync(lernaJsonPath)) {
102
+ try {
103
+ const lernaJson = JSON.parse(readFileSync(lernaJsonPath, "utf-8"));
104
+ if (Array.isArray(lernaJson.packages)) {
105
+ patterns.push(...lernaJson.packages);
106
+ }
107
+ } catch {
108
+ }
109
+ }
77
110
  const packageJsonPath = join(projectRoot, "package.json");
78
- if (!existsSync(packageJsonPath)) {
79
- return false;
111
+ if (existsSync(packageJsonPath)) {
112
+ try {
113
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
114
+ if (Array.isArray(packageJson.workspaces)) {
115
+ patterns.push(...packageJson.workspaces);
116
+ } else if (packageJson.workspaces?.packages) {
117
+ patterns.push(...packageJson.workspaces.packages);
118
+ }
119
+ } catch {
120
+ }
80
121
  }
122
+ return [...new Set(patterns)];
123
+ };
124
+ var expandWorkspacePattern = (projectRoot, pattern) => {
125
+ const results = [];
126
+ const cleanPattern = pattern.replace(/\/\*$/, "");
127
+ const basePath = join(projectRoot, cleanPattern);
128
+ if (!existsSync(basePath)) return results;
129
+ try {
130
+ const entries = readdirSync(basePath, { withFileTypes: true });
131
+ for (const entry of entries) {
132
+ if (entry.isDirectory()) {
133
+ const packageJsonPath = join(basePath, entry.name, "package.json");
134
+ if (existsSync(packageJsonPath)) {
135
+ results.push(join(basePath, entry.name));
136
+ }
137
+ }
138
+ }
139
+ } catch {
140
+ return results;
141
+ }
142
+ return results;
143
+ };
144
+ var hasReactDependency = (projectPath) => {
145
+ const packageJsonPath = join(projectPath, "package.json");
146
+ if (!existsSync(packageJsonPath)) return false;
81
147
  try {
82
148
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
83
- const allDependencies = {
84
- ...packageJson.dependencies,
85
- ...packageJson.devDependencies
86
- };
87
- return Boolean(allDependencies["react-grab"]);
149
+ const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
150
+ return Boolean(allDeps["react"] || allDeps["react-dom"]);
88
151
  } catch {
89
152
  return false;
90
153
  }
91
154
  };
155
+ var findWorkspaceProjects = (projectRoot) => {
156
+ const patterns = getWorkspacePatterns(projectRoot);
157
+ const projects = [];
158
+ for (const pattern of patterns) {
159
+ const projectPaths = expandWorkspacePattern(projectRoot, pattern);
160
+ for (const projectPath of projectPaths) {
161
+ const framework = detectFramework(projectPath);
162
+ const hasReact = hasReactDependency(projectPath);
163
+ if (hasReact || framework !== "unknown") {
164
+ const packageJsonPath = join(projectPath, "package.json");
165
+ let name = basename(projectPath);
166
+ try {
167
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
168
+ name = packageJson.name || name;
169
+ } catch {
170
+ }
171
+ projects.push({
172
+ name,
173
+ path: projectPath,
174
+ framework,
175
+ hasReact
176
+ });
177
+ }
178
+ }
179
+ }
180
+ return projects;
181
+ };
182
+ var hasReactGrabInFile = (filePath) => {
183
+ if (!existsSync(filePath)) return false;
184
+ try {
185
+ const content = readFileSync(filePath, "utf-8");
186
+ const fuzzyPatterns = [
187
+ /["'`][^"'`]*react-grab/,
188
+ /react-grab[^"'`]*["'`]/,
189
+ /<[^>]*react-grab/i,
190
+ /import[^;]*react-grab/i,
191
+ /require[^)]*react-grab/i,
192
+ /from\s+[^;]*react-grab/i,
193
+ /src[^>]*react-grab/i
194
+ ];
195
+ return fuzzyPatterns.some((pattern) => pattern.test(content));
196
+ } catch {
197
+ return false;
198
+ }
199
+ };
200
+ var detectReactGrab = (projectRoot) => {
201
+ const packageJsonPath = join(projectRoot, "package.json");
202
+ if (existsSync(packageJsonPath)) {
203
+ try {
204
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
205
+ const allDependencies = {
206
+ ...packageJson.dependencies,
207
+ ...packageJson.devDependencies
208
+ };
209
+ if (allDependencies["react-grab"]) {
210
+ return true;
211
+ }
212
+ } catch {
213
+ }
214
+ }
215
+ const filesToCheck = [
216
+ join(projectRoot, "app", "layout.tsx"),
217
+ join(projectRoot, "app", "layout.jsx"),
218
+ join(projectRoot, "src", "app", "layout.tsx"),
219
+ join(projectRoot, "src", "app", "layout.jsx"),
220
+ join(projectRoot, "pages", "_document.tsx"),
221
+ join(projectRoot, "pages", "_document.jsx"),
222
+ join(projectRoot, "instrumentation-client.ts"),
223
+ join(projectRoot, "instrumentation-client.js"),
224
+ join(projectRoot, "src", "instrumentation-client.ts"),
225
+ join(projectRoot, "src", "instrumentation-client.js"),
226
+ join(projectRoot, "index.html"),
227
+ join(projectRoot, "public", "index.html"),
228
+ join(projectRoot, "src", "index.tsx"),
229
+ join(projectRoot, "src", "index.ts"),
230
+ join(projectRoot, "src", "main.tsx"),
231
+ join(projectRoot, "src", "main.ts")
232
+ ];
233
+ return filesToCheck.some(hasReactGrabInFile);
234
+ };
92
235
  var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
93
236
  var detectUnsupportedFramework = (projectRoot) => {
94
237
  const packageJsonPath = join(projectRoot, "package.json");
@@ -343,6 +486,19 @@ var WEBPACK_IMPORT_WITH_AGENT = (agent) => {
343
486
  var SCRIPT_IMPORT = 'import Script from "next/script";';
344
487
 
345
488
  // src/transform.ts
489
+ var hasReactGrabCode = (content) => {
490
+ const fuzzyPatterns = [
491
+ /["'`][^"'`]*react-grab/,
492
+ /react-grab[^"'`]*["'`]/,
493
+ /<[^>]*react-grab/i,
494
+ /import[^;]*react-grab/i,
495
+ /require[^)]*react-grab/i,
496
+ /from\s+[^;]*react-grab/i,
497
+ /src[^>]*react-grab/i,
498
+ /href[^>]*react-grab/i
499
+ ];
500
+ return fuzzyPatterns.some((pattern) => pattern.test(content));
501
+ };
346
502
  var findLayoutFile = (projectRoot) => {
347
503
  const possiblePaths = [
348
504
  join(projectRoot, "app", "layout.tsx"),
@@ -357,6 +513,26 @@ var findLayoutFile = (projectRoot) => {
357
513
  }
358
514
  return null;
359
515
  };
516
+ var findInstrumentationFile = (projectRoot) => {
517
+ const possiblePaths = [
518
+ join(projectRoot, "instrumentation-client.ts"),
519
+ join(projectRoot, "instrumentation-client.js"),
520
+ join(projectRoot, "src", "instrumentation-client.ts"),
521
+ join(projectRoot, "src", "instrumentation-client.js")
522
+ ];
523
+ for (const filePath of possiblePaths) {
524
+ if (existsSync(filePath)) {
525
+ return filePath;
526
+ }
527
+ }
528
+ return null;
529
+ };
530
+ var hasReactGrabInInstrumentation = (projectRoot) => {
531
+ const instrumentationPath = findInstrumentationFile(projectRoot);
532
+ if (!instrumentationPath) return false;
533
+ const content = readFileSync(instrumentationPath, "utf-8");
534
+ return hasReactGrabCode(content);
535
+ };
360
536
  var findDocumentFile = (projectRoot) => {
361
537
  const possiblePaths = [
362
538
  join(projectRoot, "pages", "_document.tsx"),
@@ -424,7 +600,7 @@ var addAgentToExistingNextApp = (originalContent, agent, filePath) => {
424
600
  strategy="lazyOnload"
425
601
  />`;
426
602
  const reactGrabScriptMatch = originalContent.match(
427
- /<Script[^>]*src="[^"]*react-grab[^"]*"[^>]*\/?>/
603
+ /<(?:Script|script|NextScript)[^>]*react-grab[^>]*\/?>/is
428
604
  );
429
605
  if (reactGrabScriptMatch) {
430
606
  const newContent = originalContent.replace(
@@ -537,15 +713,16 @@ var transformNextAppRouter = (projectRoot, agent, reactGrabAlreadyConfigured) =>
537
713
  }
538
714
  const originalContent = readFileSync(layoutPath, "utf-8");
539
715
  let newContent = originalContent;
540
- const hasReactGrabInFile = originalContent.includes("react-grab");
541
- if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
716
+ const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
717
+ const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
718
+ if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
542
719
  return addAgentToExistingNextApp(originalContent, agent, layoutPath);
543
720
  }
544
- if (hasReactGrabInFile) {
721
+ if (hasReactGrabInFile2 || hasReactGrabInInstrumentationFile) {
545
722
  return {
546
723
  success: true,
547
724
  filePath: layoutPath,
548
- message: "React Grab is already installed in this file",
725
+ message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
549
726
  noChanges: true
550
727
  };
551
728
  }
@@ -596,15 +773,16 @@ var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured)
596
773
  }
597
774
  const originalContent = readFileSync(documentPath, "utf-8");
598
775
  let newContent = originalContent;
599
- const hasReactGrabInFile = originalContent.includes("react-grab");
600
- if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
776
+ const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
777
+ const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
778
+ if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
601
779
  return addAgentToExistingNextApp(originalContent, agent, documentPath);
602
780
  }
603
- if (hasReactGrabInFile) {
781
+ if (hasReactGrabInFile2 || hasReactGrabInInstrumentationFile) {
604
782
  return {
605
783
  success: true,
606
784
  filePath: documentPath,
607
- message: "React Grab is already installed in this file",
785
+ message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
608
786
  noChanges: true
609
787
  };
610
788
  }
@@ -640,11 +818,11 @@ var transformVite = (projectRoot, agent, reactGrabAlreadyConfigured) => {
640
818
  }
641
819
  const originalContent = readFileSync(indexPath, "utf-8");
642
820
  let newContent = originalContent;
643
- const hasReactGrabInFile = originalContent.includes("react-grab");
644
- if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
821
+ const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
822
+ if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
645
823
  return addAgentToExistingVite(originalContent, agent, indexPath);
646
824
  }
647
- if (hasReactGrabInFile) {
825
+ if (hasReactGrabInFile2) {
648
826
  return {
649
827
  success: true,
650
828
  filePath: indexPath,
@@ -676,11 +854,11 @@ var transformWebpack = (projectRoot, agent, reactGrabAlreadyConfigured) => {
676
854
  };
677
855
  }
678
856
  const originalContent = readFileSync(entryPath, "utf-8");
679
- const hasReactGrabInFile = originalContent.includes("react-grab");
680
- if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
857
+ const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
858
+ if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
681
859
  return addAgentToExistingWebpack(originalContent, agent, entryPath);
682
860
  }
683
- if (hasReactGrabInFile) {
861
+ if (hasReactGrabInFile2) {
684
862
  return {
685
863
  success: true,
686
864
  filePath: entryPath,
@@ -846,7 +1024,25 @@ var applyPackageJsonTransform = (result) => {
846
1024
  };
847
1025
 
848
1026
  // src/cli.ts
849
- var VERSION = "0.0.68";
1027
+ var VERSION = "0.0.71";
1028
+ var REPORT_URL = "https://reactgrab.com/api/report-cli";
1029
+ var reportToCli = async (type, config, error) => {
1030
+ try {
1031
+ await fetch(REPORT_URL, {
1032
+ method: "POST",
1033
+ headers: { "Content-Type": "application/json" },
1034
+ body: JSON.stringify({
1035
+ type,
1036
+ version: VERSION,
1037
+ config,
1038
+ error: error ? { message: error.message, stack: error.stack } : void 0,
1039
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1040
+ })
1041
+ }).catch(() => {
1042
+ });
1043
+ } catch {
1044
+ }
1045
+ };
850
1046
  var FRAMEWORK_NAMES = {
851
1047
  next: "Next.js",
852
1048
  vite: "Vite",
@@ -877,49 +1073,122 @@ var showDocsLink = () => {
877
1073
  `);
878
1074
  };
879
1075
  var showManualInstructions = (framework, nextRouterType) => {
880
- console.log(`
1076
+ console.log(
1077
+ `
881
1078
  ${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Manual Setup Instructions:")}
882
- `);
1079
+ `
1080
+ );
883
1081
  if (framework === "next" && nextRouterType === "app") {
884
1082
  console.log(`${pc.bold("Next.js App Router:")}`);
885
1083
  console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
886
- console.log(` 2. Add to ${pc.cyan("app/layout.tsx")}:`);
1084
+ console.log(` 2. Add to ${pc.cyan("app/layout.tsx")} inside <head>:`);
887
1085
  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" />')}`);
1086
+ console.log(
1087
+ ` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
1088
+ );
1089
+ console.log(` ${pc.gray(" <Script")}`);
1090
+ console.log(
1091
+ ` ${pc.gray(' src="//unpkg.com/react-grab/dist/index.global.js"')}`
1092
+ );
1093
+ console.log(` ${pc.gray(' crossOrigin="anonymous"')}`);
1094
+ console.log(` ${pc.gray(' strategy="beforeInteractive"')}`);
1095
+ console.log(` ${pc.gray(" />")}`);
1096
+ console.log(` ${pc.gray(")}")}`);
890
1097
  } else if (framework === "next" && nextRouterType === "pages") {
891
1098
  console.log(`${pc.bold("Next.js Pages Router:")}`);
892
1099
  console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
893
- console.log(` 2. Create or edit ${pc.cyan("pages/_document.tsx")}:`);
1100
+ console.log(` 2. Add to ${pc.cyan("pages/_document.tsx")} inside <Head>:`);
894
1101
  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" />')}`);
1102
+ console.log(
1103
+ ` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
1104
+ );
1105
+ console.log(` ${pc.gray(" <Script")}`);
1106
+ console.log(
1107
+ ` ${pc.gray(' src="//unpkg.com/react-grab/dist/index.global.js"')}`
1108
+ );
1109
+ console.log(` ${pc.gray(' crossOrigin="anonymous"')}`);
1110
+ console.log(` ${pc.gray(' strategy="beforeInteractive"')}`);
1111
+ console.log(` ${pc.gray(" />")}`);
1112
+ console.log(` ${pc.gray(")}")}`);
897
1113
  } else if (framework === "vite") {
898
1114
  console.log(`${pc.bold("Vite:")}`);
899
1115
  console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
900
1116
  console.log(` 2. Add to ${pc.cyan("index.html")} inside <head>:`);
901
1117
  console.log(` ${pc.gray('<script type="module">')}`);
902
- console.log(` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`);
1118
+ console.log(
1119
+ ` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`
1120
+ );
903
1121
  console.log(` ${pc.gray("</script>")}`);
904
1122
  } else if (framework === "webpack") {
905
1123
  console.log(`${pc.bold("Webpack:")}`);
906
1124
  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") {')}`);
1125
+ console.log(
1126
+ ` 2. Add to your entry file (e.g., ${pc.cyan("src/index.tsx")}):`
1127
+ );
1128
+ console.log(
1129
+ ` ${pc.gray('if (process.env.NODE_ENV === "development") {')}`
1130
+ );
909
1131
  console.log(` ${pc.gray(' import("react-grab");')}`);
910
1132
  console.log(` ${pc.gray("}")}`);
911
1133
  } 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.`);
1134
+ console.log(
1135
+ `${pc.bold("Next.js App Router:")} Add to ${pc.cyan("app/layout.tsx")} inside <head>:`
1136
+ );
1137
+ console.log(` ${pc.gray('import Script from "next/script";')}`);
1138
+ console.log(
1139
+ ` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
1140
+ );
1141
+ console.log(
1142
+ ` ${pc.gray(' <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`
1143
+ );
1144
+ console.log(` ${pc.gray(")}")}`);
1145
+ console.log("");
1146
+ console.log(
1147
+ `${pc.bold("Next.js Pages Router:")} Add to ${pc.cyan("pages/_document.tsx")} inside <Head>:`
1148
+ );
1149
+ console.log(` ${pc.gray('import Script from "next/script";')}`);
1150
+ console.log(
1151
+ ` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
1152
+ );
1153
+ console.log(
1154
+ ` ${pc.gray(' <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`
1155
+ );
1156
+ console.log(` ${pc.gray(")}")}`);
1157
+ console.log("");
1158
+ console.log(
1159
+ `${pc.bold("Vite:")} Add to ${pc.cyan("index.html")} inside <head>:`
1160
+ );
1161
+ console.log(` ${pc.gray('<script type="module">')}`);
1162
+ console.log(
1163
+ ` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`
1164
+ );
1165
+ console.log(` ${pc.gray("</script>")}`);
1166
+ console.log("");
1167
+ console.log(
1168
+ `${pc.bold("Webpack:")} Add to entry file (e.g., ${pc.cyan("src/index.tsx")}):`
1169
+ );
1170
+ console.log(
1171
+ ` ${pc.gray('if (process.env.NODE_ENV === "development") {')}`
1172
+ );
1173
+ console.log(` ${pc.gray(' import("react-grab");')}`);
1174
+ console.log(` ${pc.gray("}")}`);
1175
+ console.log("");
1176
+ console.log(`For full instructions, visit:`);
1177
+ console.log(
1178
+ ` ${pc.cyan("https://github.com/aidenybai/react-grab#readme")}`
1179
+ );
1180
+ return;
916
1181
  }
917
1182
  showDocsLink();
918
1183
  };
919
1184
  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.")}`);
1185
+ console.log(
1186
+ `
1187
+ ${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Auto-detection may not be 100% accurate.")}`
1188
+ );
1189
+ console.log(
1190
+ `${pc.yellow(" Please verify the changes in your file before committing.")}`
1191
+ );
923
1192
  };
924
1193
  var parseArgs = async () => {
925
1194
  const argv = await yargs(hideBin(process.argv)).scriptName("react-grab").usage(
@@ -961,7 +1230,13 @@ router type) and installs React Grab with optional agent integrations.`
961
1230
  type: "boolean",
962
1231
  default: false,
963
1232
  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(
1233
+ }).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(
1234
+ "$0 -a cursor -y",
1235
+ "Add Cursor agent integration non-interactively"
1236
+ ).example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent").example(
1237
+ "$0 --skip-install",
1238
+ "Only modify files, install packages manually"
1239
+ ).epilog(
965
1240
  `${pc.bold("Agent Integrations:")}
966
1241
  ${pc.cyan("claude-code")} Connect React Grab to Claude Code
967
1242
  ${pc.cyan("cursor")} Connect React Grab to Cursor IDE
@@ -986,33 +1261,104 @@ ${pc.bold("Documentation:")} ${pc.underline(DOCS_URL)}`
986
1261
  var main = async () => {
987
1262
  const args = await parseArgs();
988
1263
  const isNonInteractive = args.yes;
989
- console.log(`
990
- ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
1264
+ console.log(
1265
+ `
1266
+ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`
1267
+ );
991
1268
  const projectInfo = await detectProject(process.cwd());
992
- console.log(`- Framework: ${pc.cyan(FRAMEWORK_NAMES[projectInfo.framework])}`);
993
- console.log(`- Package Manager: ${pc.cyan(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}`);
1269
+ console.log(
1270
+ `- Framework: ${pc.cyan(FRAMEWORK_NAMES[projectInfo.framework])}`
1271
+ );
1272
+ console.log(
1273
+ `- Package Manager: ${pc.cyan(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}`
1274
+ );
994
1275
  if (projectInfo.framework === "next") {
995
- console.log(`- Router Type: ${pc.cyan(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`);
1276
+ console.log(
1277
+ `- Router Type: ${pc.cyan(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`
1278
+ );
996
1279
  }
997
- console.log(`- Monorepo: ${pc.cyan(projectInfo.isMonorepo ? "Yes" : "No")}`);
998
- console.log(`- React Grab: ${projectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`);
1280
+ console.log(
1281
+ `- Monorepo: ${pc.cyan(projectInfo.isMonorepo ? "Yes" : "No")}`
1282
+ );
1283
+ console.log(
1284
+ `- React Grab: ${projectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`
1285
+ );
999
1286
  if (projectInfo.installedAgents.length > 0) {
1000
- console.log(`- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`);
1287
+ console.log(
1288
+ `- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`
1289
+ );
1001
1290
  }
1002
1291
  console.log("");
1003
1292
  if (projectInfo.unsupportedFramework) {
1004
1293
  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
- `);
1294
+ console.log(
1295
+ `${pc.yellow("\u26A0\uFE0F")} ${pc.yellow(`Detected ${frameworkName} - this framework requires manual setup.`)}`
1296
+ );
1297
+ console.log(
1298
+ `${pc.yellow(" React Grab may not work correctly with auto-configuration.")}
1299
+ `
1300
+ );
1008
1301
  showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
1009
1302
  process.exit(0);
1010
1303
  }
1011
1304
  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);
1305
+ if (projectInfo.isMonorepo && !isNonInteractive) {
1306
+ const workspaceProjects = findWorkspaceProjects(projectInfo.projectRoot);
1307
+ const reactProjects = workspaceProjects.filter(
1308
+ (p) => p.hasReact || p.framework !== "unknown"
1309
+ );
1310
+ if (reactProjects.length > 0) {
1311
+ console.log(
1312
+ `${pc.cyan("\u2139")} ${pc.cyan("Found React projects in this monorepo:")}
1313
+ `
1314
+ );
1315
+ const selectedProject = await select({
1316
+ message: "Select a project to install React Grab:",
1317
+ choices: reactProjects.map((project) => ({
1318
+ name: `${project.name} ${pc.gray(`(${FRAMEWORK_NAMES[project.framework] || "React"})`)}`,
1319
+ value: project.path
1320
+ }))
1321
+ });
1322
+ console.log(
1323
+ `
1324
+ ${pc.magenta("\u269B")} Switching to ${pc.cyan(selectedProject)}...
1325
+ `
1326
+ );
1327
+ process.chdir(selectedProject);
1328
+ const newProjectInfo = await detectProject(selectedProject);
1329
+ projectInfo.framework = newProjectInfo.framework;
1330
+ projectInfo.nextRouterType = newProjectInfo.nextRouterType;
1331
+ projectInfo.hasReactGrab = newProjectInfo.hasReactGrab;
1332
+ projectInfo.installedAgents = newProjectInfo.installedAgents;
1333
+ projectInfo.projectRoot = newProjectInfo.projectRoot;
1334
+ console.log(
1335
+ `- Framework: ${pc.cyan(FRAMEWORK_NAMES[newProjectInfo.framework])}`
1336
+ );
1337
+ if (newProjectInfo.framework === "next") {
1338
+ console.log(
1339
+ `- Router Type: ${pc.cyan(newProjectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`
1340
+ );
1341
+ }
1342
+ console.log(
1343
+ `- React Grab: ${newProjectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`
1344
+ );
1345
+ console.log("");
1346
+ } else {
1347
+ console.log(
1348
+ `${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
1349
+ `
1350
+ );
1351
+ showManualInstructions("unknown");
1352
+ process.exit(0);
1353
+ }
1354
+ } else {
1355
+ console.log(
1356
+ `${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
1357
+ `
1358
+ );
1359
+ showManualInstructions("unknown");
1360
+ process.exit(0);
1361
+ }
1016
1362
  }
1017
1363
  let action = "install-all";
1018
1364
  if (projectInfo.hasReactGrab && !isNonInteractive) {
@@ -1027,9 +1373,13 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
1027
1373
  } else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
1028
1374
  action = "add-agent";
1029
1375
  } 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
- `);
1376
+ console.log(
1377
+ `${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("React Grab is already installed.")}`
1378
+ );
1379
+ console.log(
1380
+ `${pc.yellow(" Use --agent to add an agent, or run without -y for interactive mode.")}
1381
+ `
1382
+ );
1033
1383
  action = "reconfigure";
1034
1384
  }
1035
1385
  let finalFramework = args.framework || projectInfo.framework;
@@ -1081,9 +1431,11 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
1081
1431
  { name: "Opencode", value: "opencode" }
1082
1432
  ].filter((agent) => !projectInfo.installedAgents.includes(agent.value));
1083
1433
  if (availableAgents.length === 0) {
1084
- console.log(`
1434
+ console.log(
1435
+ `
1085
1436
  ${pc.green("All agent integrations are already installed.")}
1086
- `);
1437
+ `
1438
+ );
1087
1439
  } else if (action === "add-agent") {
1088
1440
  agentIntegration = await select({
1089
1441
  message: "Select an agent integration to add:",
@@ -1139,7 +1491,11 @@ ${pc.magenta("\u269B")} Previewing changes...
1139
1491
  if (hasLayoutChanges) {
1140
1492
  console.log("");
1141
1493
  }
1142
- printDiff(packageJsonResult.filePath, packageJsonResult.originalContent, packageJsonResult.newContent);
1494
+ printDiff(
1495
+ packageJsonResult.filePath,
1496
+ packageJsonResult.originalContent,
1497
+ packageJsonResult.newContent
1498
+ );
1143
1499
  }
1144
1500
  if (hasLayoutChanges || hasPackageJsonChanges) {
1145
1501
  showAccuracyWarning();
@@ -1156,16 +1512,25 @@ ${pc.yellow("Changes cancelled.")}
1156
1512
  }
1157
1513
  }
1158
1514
  }
1159
- const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
1515
+ const shouldInstallReactGrab = (action === "install-all" || action === "add-agent") && !projectInfo.hasReactGrab;
1160
1516
  const shouldInstallAgent = agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration);
1161
1517
  if (!args.skipInstall && (shouldInstallReactGrab || shouldInstallAgent)) {
1162
- const packages = getPackagesToInstall(agentIntegration, shouldInstallReactGrab);
1518
+ const packages = getPackagesToInstall(
1519
+ agentIntegration,
1520
+ shouldInstallReactGrab
1521
+ );
1163
1522
  if (packages.length > 0) {
1164
- console.log(`
1523
+ console.log(
1524
+ `
1165
1525
  ${pc.magenta("\u269B")} Installing: ${pc.cyan(packages.join(", "))}
1166
- `);
1526
+ `
1527
+ );
1167
1528
  try {
1168
- installPackages(packages, finalPackageManager, projectInfo.projectRoot);
1529
+ installPackages(
1530
+ packages,
1531
+ finalPackageManager,
1532
+ projectInfo.projectRoot
1533
+ );
1169
1534
  console.log(`
1170
1535
  ${pc.green("Packages installed successfully!")}
1171
1536
  `);
@@ -1191,8 +1556,10 @@ ${pc.green("Applied:")} ${result.filePath}`);
1191
1556
  if (hasPackageJsonChanges) {
1192
1557
  const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
1193
1558
  if (!packageJsonWriteResult.success) {
1194
- console.error(`
1195
- ${pc.red("Error:")} ${packageJsonWriteResult.error}`);
1559
+ console.error(
1560
+ `
1561
+ ${pc.red("Error:")} ${packageJsonWriteResult.error}`
1562
+ );
1196
1563
  showDocsLink();
1197
1564
  process.exit(1);
1198
1565
  }
@@ -1204,20 +1571,30 @@ ${pc.red("Error:")} ${packageJsonWriteResult.error}`);
1204
1571
  ${pc.green("Done!")}`);
1205
1572
  console.log(`
1206
1573
  Next steps:`);
1207
- console.log(` - Start your development server`);
1208
- console.log(` - Select an element to copy its context`);
1209
- console.log(` - Learn more at ${pc.cyan(DOCS_URL)}
1574
+ console.log(`- Start your development server`);
1575
+ console.log(`- Select an element to copy its context`);
1576
+ console.log(`- Learn more at ${pc.cyan(DOCS_URL)}
1210
1577
  `);
1211
1578
  if (agentIntegration !== "none") {
1212
- console.log(`${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`);
1579
+ console.log(
1580
+ `${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`
1581
+ );
1213
1582
  console.log(` Make sure to start the agent server before using it.
1214
1583
  `);
1215
1584
  }
1585
+ await reportToCli("completed", {
1586
+ framework: finalFramework,
1587
+ packageManager: finalPackageManager,
1588
+ router: finalNextRouterType,
1589
+ agent: agentIntegration !== "none" ? agentIntegration : void 0,
1590
+ isMonorepo: projectInfo.isMonorepo
1591
+ });
1216
1592
  };
1217
- main().catch((error) => {
1593
+ main().catch(async (error) => {
1218
1594
  console.error(`${pc.red("Error:")}`, error);
1219
1595
  console.log("\nFor manual installation instructions, visit:");
1220
1596
  console.log(` ${DOCS_URL}
1221
1597
  `);
1598
+ await reportToCli("error", void 0, error);
1222
1599
  process.exit(1);
1223
1600
  });