@react-grab/cli 0.1.0-beta.12 → 0.1.0-beta.13

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 +258 -8
  2. package/dist/cli.js +258 -8
  3. package/package.json +10 -10
package/dist/cli.cjs CHANGED
@@ -37,6 +37,9 @@ var detectFramework = (projectRoot) => {
37
37
  if (allDependencies["next"]) {
38
38
  return "next";
39
39
  }
40
+ if (allDependencies["@tanstack/react-start"]) {
41
+ return "tanstack";
42
+ }
40
43
  if (allDependencies["vite"]) {
41
44
  return "vite";
42
45
  }
@@ -241,7 +244,11 @@ var detectReactGrab = (projectRoot) => {
241
244
  path.join(projectRoot, "src", "index.tsx"),
242
245
  path.join(projectRoot, "src", "index.ts"),
243
246
  path.join(projectRoot, "src", "main.tsx"),
244
- path.join(projectRoot, "src", "main.ts")
247
+ path.join(projectRoot, "src", "main.ts"),
248
+ path.join(projectRoot, "src", "routes", "__root.tsx"),
249
+ path.join(projectRoot, "src", "routes", "__root.jsx"),
250
+ path.join(projectRoot, "app", "routes", "__root.tsx"),
251
+ path.join(projectRoot, "app", "routes", "__root.jsx")
245
252
  ];
246
253
  return filesToCheck.some(hasReactGrabInFile);
247
254
  };
@@ -604,6 +611,20 @@ var WEBPACK_IMPORT_WITH_AGENT = (agent) => {
604
611
  import("@react-grab/${agent}/client");
605
612
  }`;
606
613
  };
614
+ var TANSTACK_EFFECT = `useEffect(() => {
615
+ if (import.meta.env.DEV) {
616
+ void import("react-grab");
617
+ }
618
+ }, []);`;
619
+ var TANSTACK_EFFECT_WITH_AGENT = (agent) => {
620
+ if (agent === "none") return TANSTACK_EFFECT;
621
+ return `useEffect(() => {
622
+ if (import.meta.env.DEV) {
623
+ void import("react-grab");
624
+ void import("@react-grab/${agent}/client");
625
+ }
626
+ }, []);`;
627
+ };
607
628
  var SCRIPT_IMPORT = 'import Script from "next/script";';
608
629
  var hasReactGrabCode = (content) => {
609
630
  const fuzzyPatterns = [
@@ -696,6 +717,20 @@ var findEntryFile = (projectRoot) => {
696
717
  }
697
718
  return null;
698
719
  };
720
+ var findTanStackRootFile = (projectRoot) => {
721
+ const possiblePaths = [
722
+ path.join(projectRoot, "src", "routes", "__root.tsx"),
723
+ path.join(projectRoot, "src", "routes", "__root.jsx"),
724
+ path.join(projectRoot, "app", "routes", "__root.tsx"),
725
+ path.join(projectRoot, "app", "routes", "__root.jsx")
726
+ ];
727
+ for (const filePath of possiblePaths) {
728
+ if (fs.existsSync(filePath)) {
729
+ return filePath;
730
+ }
731
+ }
732
+ return null;
733
+ };
699
734
  var addAgentToExistingNextApp = (originalContent, agent, filePath) => {
700
735
  if (agent === "none") {
701
736
  return {
@@ -848,6 +883,50 @@ var addAgentToExistingWebpack = (originalContent, agent, filePath) => {
848
883
  message: "Could not find React Grab import to add agent after"
849
884
  };
850
885
  };
886
+ var addAgentToExistingTanStack = (originalContent, agent, filePath) => {
887
+ if (agent === "none") {
888
+ return {
889
+ success: true,
890
+ filePath,
891
+ message: "React Grab is already configured",
892
+ noChanges: true
893
+ };
894
+ }
895
+ const agentPackage = `@react-grab/${agent}`;
896
+ if (originalContent.includes(agentPackage)) {
897
+ return {
898
+ success: true,
899
+ filePath,
900
+ message: `Agent ${agent} is already configured`,
901
+ noChanges: true
902
+ };
903
+ }
904
+ const agentImport = `void import("${agentPackage}/client");`;
905
+ const reactGrabImportMatch = originalContent.match(
906
+ /void\s+import\s*\(\s*["']react-grab["']\s*\);?/
907
+ );
908
+ if (reactGrabImportMatch) {
909
+ const matchedText = reactGrabImportMatch[0];
910
+ const hasSemicolon = matchedText.endsWith(";");
911
+ const newContent = originalContent.replace(
912
+ matchedText,
913
+ `${hasSemicolon ? matchedText.slice(0, -1) : matchedText};
914
+ ${agentImport}`
915
+ );
916
+ return {
917
+ success: true,
918
+ filePath,
919
+ message: `Add ${agent} agent`,
920
+ originalContent,
921
+ newContent
922
+ };
923
+ }
924
+ return {
925
+ success: false,
926
+ filePath,
927
+ message: "Could not find React Grab import to add agent after"
928
+ };
929
+ };
851
930
  var transformNextAppRouter = (projectRoot, agent, reactGrabAlreadyConfigured) => {
852
931
  const layoutPath = findLayoutFile(projectRoot);
853
932
  if (!layoutPath) {
@@ -1039,6 +1118,79 @@ ${originalContent}`;
1039
1118
  newContent
1040
1119
  };
1041
1120
  };
1121
+ var transformTanStack = (projectRoot, agent, reactGrabAlreadyConfigured) => {
1122
+ const rootPath = findTanStackRootFile(projectRoot);
1123
+ if (!rootPath) {
1124
+ return {
1125
+ success: false,
1126
+ filePath: "",
1127
+ message: 'Could not find src/routes/__root.tsx or app/routes/__root.tsx.\n\nTo set up React Grab with TanStack Start, add this to your root route component:\n\n import { useEffect } from "react";\n\n useEffect(() => {\n if (import.meta.env.DEV) {\n void import("react-grab");\n }\n }, []);'
1128
+ };
1129
+ }
1130
+ const originalContent = fs.readFileSync(rootPath, "utf-8");
1131
+ let newContent = originalContent;
1132
+ const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
1133
+ if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
1134
+ return addAgentToExistingTanStack(originalContent, agent, rootPath);
1135
+ }
1136
+ if (hasReactGrabInFile2) {
1137
+ return {
1138
+ success: true,
1139
+ filePath: rootPath,
1140
+ message: "React Grab is already installed in this file",
1141
+ noChanges: true
1142
+ };
1143
+ }
1144
+ const hasUseEffectImport = /import\s+\{[^}]*useEffect[^}]*\}\s+from\s+["']react["']/.test(newContent);
1145
+ if (!hasUseEffectImport) {
1146
+ const reactImportMatch = newContent.match(
1147
+ /import\s+\{([^}]*)\}\s+from\s+["']react["'];?/
1148
+ );
1149
+ if (reactImportMatch) {
1150
+ const existingImports = reactImportMatch[1];
1151
+ newContent = newContent.replace(
1152
+ reactImportMatch[0],
1153
+ `import { ${existingImports.trim()}, useEffect } from "react";`
1154
+ );
1155
+ } else {
1156
+ const firstImportMatch = newContent.match(
1157
+ /^import .+ from ['"].+['"];?\s*$/m
1158
+ );
1159
+ if (firstImportMatch) {
1160
+ newContent = newContent.replace(
1161
+ firstImportMatch[0],
1162
+ `import { useEffect } from "react";
1163
+ ${firstImportMatch[0]}`
1164
+ );
1165
+ } else {
1166
+ newContent = `import { useEffect } from "react";
1167
+
1168
+ ${newContent}`;
1169
+ }
1170
+ }
1171
+ }
1172
+ const effectBlock = TANSTACK_EFFECT_WITH_AGENT(agent);
1173
+ const componentMatch = newContent.match(/function\s+(\w+)\s*\([^)]*\)\s*\{/);
1174
+ if (componentMatch) {
1175
+ const insertPosition = componentMatch.index + componentMatch[0].length;
1176
+ newContent = newContent.slice(0, insertPosition) + `
1177
+ ${effectBlock}
1178
+ ` + newContent.slice(insertPosition);
1179
+ } else {
1180
+ return {
1181
+ success: false,
1182
+ filePath: rootPath,
1183
+ message: "Could not find a component function in the root file"
1184
+ };
1185
+ }
1186
+ return {
1187
+ success: true,
1188
+ filePath: rootPath,
1189
+ message: "Add React Grab" + (agent !== "none" ? ` with ${agent} agent` : ""),
1190
+ originalContent,
1191
+ newContent
1192
+ };
1193
+ };
1042
1194
  var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrabAlreadyConfigured = false) => {
1043
1195
  switch (framework) {
1044
1196
  case "next":
@@ -1056,6 +1208,8 @@ var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrab
1056
1208
  );
1057
1209
  case "vite":
1058
1210
  return transformVite(projectRoot, agent, reactGrabAlreadyConfigured);
1211
+ case "tanstack":
1212
+ return transformTanStack(projectRoot, agent, reactGrabAlreadyConfigured);
1059
1213
  case "webpack":
1060
1214
  return transformWebpack(projectRoot, agent, reactGrabAlreadyConfigured);
1061
1215
  default:
@@ -1291,6 +1445,8 @@ var findReactGrabFile = (projectRoot, framework, nextRouterType) => {
1291
1445
  return findDocumentFile(projectRoot);
1292
1446
  case "vite":
1293
1447
  return findIndexHtml(projectRoot);
1448
+ case "tanstack":
1449
+ return findTanStackRootFile(projectRoot);
1294
1450
  case "webpack":
1295
1451
  return findEntryFile(projectRoot);
1296
1452
  default:
@@ -1387,6 +1543,31 @@ var addOptionsToWebpackImport = (originalContent, options, filePath) => {
1387
1543
  newContent
1388
1544
  };
1389
1545
  };
1546
+ var addOptionsToTanStackImport = (originalContent, options, filePath) => {
1547
+ const reactGrabImportMatch = originalContent.match(
1548
+ /void\s+import\s*\(\s*["']react-grab["']\s*\)/
1549
+ );
1550
+ if (!reactGrabImportMatch) {
1551
+ return {
1552
+ success: false,
1553
+ filePath,
1554
+ message: "Could not find React Grab import"
1555
+ };
1556
+ }
1557
+ const optionsJson = formatOptionsAsJson(options);
1558
+ const newImport = `import("react-grab/core").then(({ init }) => init(${optionsJson}))`;
1559
+ const newContent = originalContent.replace(
1560
+ reactGrabImportMatch[0],
1561
+ newImport
1562
+ );
1563
+ return {
1564
+ success: true,
1565
+ filePath,
1566
+ message: "Update React Grab options",
1567
+ originalContent,
1568
+ newContent
1569
+ };
1570
+ };
1390
1571
  var previewOptionsTransform = (projectRoot, framework, nextRouterType, options) => {
1391
1572
  const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
1392
1573
  if (!filePath) {
@@ -1409,6 +1590,8 @@ var previewOptionsTransform = (projectRoot, framework, nextRouterType, options)
1409
1590
  return addOptionsToNextScript(originalContent, options, filePath);
1410
1591
  case "vite":
1411
1592
  return addOptionsToViteScript(originalContent, options, filePath);
1593
+ case "tanstack":
1594
+ return addOptionsToTanStackImport(originalContent, options, filePath);
1412
1595
  case "webpack":
1413
1596
  return addOptionsToWebpackImport(originalContent, options, filePath);
1414
1597
  default:
@@ -1519,6 +1702,36 @@ var removeAgentFromWebpack = (originalContent, agent, filePath) => {
1519
1702
  newContent
1520
1703
  };
1521
1704
  };
1705
+ var removeAgentFromTanStack = (originalContent, agent, filePath) => {
1706
+ const agentPackage = `@react-grab/${agent}`;
1707
+ if (!originalContent.includes(agentPackage)) {
1708
+ return {
1709
+ success: true,
1710
+ filePath,
1711
+ message: `Agent ${agent} is not configured in this file`,
1712
+ noChanges: true
1713
+ };
1714
+ }
1715
+ const agentImportPattern = new RegExp(
1716
+ `\\s*void\\s+import\\s*\\(\\s*["']${agentPackage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/client["']\\s*\\);?`,
1717
+ "g"
1718
+ );
1719
+ const newContent = originalContent.replace(agentImportPattern, "");
1720
+ if (newContent === originalContent) {
1721
+ return {
1722
+ success: false,
1723
+ filePath,
1724
+ message: `Could not find agent ${agent} import to remove`
1725
+ };
1726
+ }
1727
+ return {
1728
+ success: true,
1729
+ filePath,
1730
+ message: `Remove ${agent} agent`,
1731
+ originalContent,
1732
+ newContent
1733
+ };
1734
+ };
1522
1735
  var previewAgentRemoval = (projectRoot, framework, nextRouterType, agent) => {
1523
1736
  const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
1524
1737
  if (!filePath) {
@@ -1535,6 +1748,8 @@ var previewAgentRemoval = (projectRoot, framework, nextRouterType, agent) => {
1535
1748
  return removeAgentFromNextApp(originalContent, agent, filePath);
1536
1749
  case "vite":
1537
1750
  return removeAgentFromVite(originalContent, agent, filePath);
1751
+ case "tanstack":
1752
+ return removeAgentFromTanStack(originalContent, agent, filePath);
1538
1753
  case "webpack":
1539
1754
  return removeAgentFromWebpack(originalContent, agent, filePath);
1540
1755
  default:
@@ -1607,7 +1822,7 @@ var previewPackageJsonAgentRemoval = (projectRoot, agent) => {
1607
1822
  };
1608
1823
 
1609
1824
  // src/commands/add.ts
1610
- var VERSION = "0.1.0-beta.11";
1825
+ var VERSION = "0.1.0-beta.12";
1611
1826
  var formatInstalledAgentNames = (agents) => agents.map((agent) => AGENT_NAMES[agent] || agent).join(", ");
1612
1827
  var add = new commander.Command().name("add").alias("install").description("add an agent integration").argument("[agent]", `agent to add (${AGENTS.join(", ")})`).option("-y, --yes", "skip confirmation prompts", false).option(
1613
1828
  "-c, --cwd <cwd>",
@@ -1943,7 +2158,7 @@ var MAX_KEY_HOLD_DURATION_MS = 2e3;
1943
2158
  var MAX_CONTEXT_LINES = 50;
1944
2159
 
1945
2160
  // src/commands/configure.ts
1946
- var VERSION2 = "0.1.0-beta.11";
2161
+ var VERSION2 = "0.1.0-beta.12";
1947
2162
  var isMac = process.platform === "darwin";
1948
2163
  var META_LABEL = isMac ? "Cmd" : "Win";
1949
2164
  var ALT_LABEL = isMac ? "Option" : "Alt";
@@ -2443,7 +2658,7 @@ var uninstallPackagesWithFeedback = (packages, packageManager, projectRoot) => {
2443
2658
  };
2444
2659
 
2445
2660
  // src/commands/init.ts
2446
- var VERSION3 = "0.1.0-beta.11";
2661
+ var VERSION3 = "0.1.0-beta.12";
2447
2662
  var REPORT_URL = "https://react-grab.com/api/report-cli";
2448
2663
  var DOCS_URL = "https://github.com/aidenybai/react-grab";
2449
2664
  var reportToCli = (type, config, error) => {
@@ -2463,6 +2678,7 @@ var reportToCli = (type, config, error) => {
2463
2678
  var FRAMEWORK_NAMES = {
2464
2679
  next: "Next.js",
2465
2680
  vite: "Vite",
2681
+ tanstack: "TanStack Start",
2466
2682
  webpack: "Webpack",
2467
2683
  unknown: "Unknown"
2468
2684
  };
@@ -2942,7 +3158,7 @@ var init = new commander.Command().name("init").description("initialize React Gr
2942
3158
  frameworkSpinner.fail("Could not detect a supported framework.");
2943
3159
  logger.break();
2944
3160
  logger.log(
2945
- "React Grab supports Next.js, Vite, and Webpack projects."
3161
+ "React Grab supports Next.js, Vite, TanStack Start, and Webpack projects."
2946
3162
  );
2947
3163
  logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
2948
3164
  logger.break();
@@ -2968,8 +3184,42 @@ var init = new commander.Command().name("init").description("initialize React Gr
2968
3184
  const finalFramework = projectInfo.framework;
2969
3185
  const finalPackageManager = projectInfo.packageManager;
2970
3186
  const finalNextRouterType = projectInfo.nextRouterType;
2971
- const agentIntegration = opts.agent || "none";
3187
+ let agentIntegration = opts.agent || "none";
2972
3188
  const agentsToRemove = [];
3189
+ if (!isNonInteractive && !opts.agent) {
3190
+ logger.break();
3191
+ const { wantAddAgent } = await prompts3__default.default({
3192
+ type: "confirm",
3193
+ name: "wantAddAgent",
3194
+ message: `Would you like to add an ${highlighter.info("agent integration")}?`,
3195
+ initial: false
3196
+ });
3197
+ if (wantAddAgent === void 0) {
3198
+ logger.break();
3199
+ process.exit(1);
3200
+ }
3201
+ if (wantAddAgent) {
3202
+ const { agent } = await prompts3__default.default({
3203
+ type: "select",
3204
+ name: "agent",
3205
+ message: `Which ${highlighter.info("agent integration")} would you like to add?`,
3206
+ choices: [
3207
+ ...AGENTS.map((innerAgent) => ({
3208
+ title: getAgentName(innerAgent),
3209
+ value: innerAgent
3210
+ })),
3211
+ { title: "Skip", value: "skip" }
3212
+ ]
3213
+ });
3214
+ if (agent === void 0) {
3215
+ logger.break();
3216
+ process.exit(1);
3217
+ }
3218
+ if (agent !== "skip") {
3219
+ agentIntegration = agent;
3220
+ }
3221
+ }
3222
+ }
2973
3223
  const result = previewTransform(
2974
3224
  projectInfo.projectRoot,
2975
3225
  finalFramework,
@@ -3075,7 +3325,7 @@ var init = new commander.Command().name("init").description("initialize React Gr
3075
3325
  reportToCli("error", void 0, error);
3076
3326
  }
3077
3327
  });
3078
- var VERSION4 = "0.1.0-beta.11";
3328
+ var VERSION4 = "0.1.0-beta.12";
3079
3329
  var remove = new commander.Command().name("remove").description("remove an agent integration").argument(
3080
3330
  "[agent]",
3081
3331
  "agent to remove (claude-code, cursor, opencode, codex, gemini, amp, ami)"
@@ -3254,7 +3504,7 @@ var remove = new commander.Command().name("remove").description("remove an agent
3254
3504
  });
3255
3505
 
3256
3506
  // src/cli.ts
3257
- var VERSION5 = "0.1.0-beta.11";
3507
+ var VERSION5 = "0.1.0-beta.12";
3258
3508
  var VERSION_API_URL = "https://www.react-grab.com/api/version";
3259
3509
  process.on("SIGINT", () => process.exit(0));
3260
3510
  process.on("SIGTERM", () => process.exit(0));
package/dist/cli.js CHANGED
@@ -29,6 +29,9 @@ var detectFramework = (projectRoot) => {
29
29
  if (allDependencies["next"]) {
30
30
  return "next";
31
31
  }
32
+ if (allDependencies["@tanstack/react-start"]) {
33
+ return "tanstack";
34
+ }
32
35
  if (allDependencies["vite"]) {
33
36
  return "vite";
34
37
  }
@@ -233,7 +236,11 @@ var detectReactGrab = (projectRoot) => {
233
236
  join(projectRoot, "src", "index.tsx"),
234
237
  join(projectRoot, "src", "index.ts"),
235
238
  join(projectRoot, "src", "main.tsx"),
236
- join(projectRoot, "src", "main.ts")
239
+ join(projectRoot, "src", "main.ts"),
240
+ join(projectRoot, "src", "routes", "__root.tsx"),
241
+ join(projectRoot, "src", "routes", "__root.jsx"),
242
+ join(projectRoot, "app", "routes", "__root.tsx"),
243
+ join(projectRoot, "app", "routes", "__root.jsx")
237
244
  ];
238
245
  return filesToCheck.some(hasReactGrabInFile);
239
246
  };
@@ -596,6 +603,20 @@ var WEBPACK_IMPORT_WITH_AGENT = (agent) => {
596
603
  import("@react-grab/${agent}/client");
597
604
  }`;
598
605
  };
606
+ var TANSTACK_EFFECT = `useEffect(() => {
607
+ if (import.meta.env.DEV) {
608
+ void import("react-grab");
609
+ }
610
+ }, []);`;
611
+ var TANSTACK_EFFECT_WITH_AGENT = (agent) => {
612
+ if (agent === "none") return TANSTACK_EFFECT;
613
+ return `useEffect(() => {
614
+ if (import.meta.env.DEV) {
615
+ void import("react-grab");
616
+ void import("@react-grab/${agent}/client");
617
+ }
618
+ }, []);`;
619
+ };
599
620
  var SCRIPT_IMPORT = 'import Script from "next/script";';
600
621
  var hasReactGrabCode = (content) => {
601
622
  const fuzzyPatterns = [
@@ -688,6 +709,20 @@ var findEntryFile = (projectRoot) => {
688
709
  }
689
710
  return null;
690
711
  };
712
+ var findTanStackRootFile = (projectRoot) => {
713
+ const possiblePaths = [
714
+ join(projectRoot, "src", "routes", "__root.tsx"),
715
+ join(projectRoot, "src", "routes", "__root.jsx"),
716
+ join(projectRoot, "app", "routes", "__root.tsx"),
717
+ join(projectRoot, "app", "routes", "__root.jsx")
718
+ ];
719
+ for (const filePath of possiblePaths) {
720
+ if (existsSync(filePath)) {
721
+ return filePath;
722
+ }
723
+ }
724
+ return null;
725
+ };
691
726
  var addAgentToExistingNextApp = (originalContent, agent, filePath) => {
692
727
  if (agent === "none") {
693
728
  return {
@@ -840,6 +875,50 @@ var addAgentToExistingWebpack = (originalContent, agent, filePath) => {
840
875
  message: "Could not find React Grab import to add agent after"
841
876
  };
842
877
  };
878
+ var addAgentToExistingTanStack = (originalContent, agent, filePath) => {
879
+ if (agent === "none") {
880
+ return {
881
+ success: true,
882
+ filePath,
883
+ message: "React Grab is already configured",
884
+ noChanges: true
885
+ };
886
+ }
887
+ const agentPackage = `@react-grab/${agent}`;
888
+ if (originalContent.includes(agentPackage)) {
889
+ return {
890
+ success: true,
891
+ filePath,
892
+ message: `Agent ${agent} is already configured`,
893
+ noChanges: true
894
+ };
895
+ }
896
+ const agentImport = `void import("${agentPackage}/client");`;
897
+ const reactGrabImportMatch = originalContent.match(
898
+ /void\s+import\s*\(\s*["']react-grab["']\s*\);?/
899
+ );
900
+ if (reactGrabImportMatch) {
901
+ const matchedText = reactGrabImportMatch[0];
902
+ const hasSemicolon = matchedText.endsWith(";");
903
+ const newContent = originalContent.replace(
904
+ matchedText,
905
+ `${hasSemicolon ? matchedText.slice(0, -1) : matchedText};
906
+ ${agentImport}`
907
+ );
908
+ return {
909
+ success: true,
910
+ filePath,
911
+ message: `Add ${agent} agent`,
912
+ originalContent,
913
+ newContent
914
+ };
915
+ }
916
+ return {
917
+ success: false,
918
+ filePath,
919
+ message: "Could not find React Grab import to add agent after"
920
+ };
921
+ };
843
922
  var transformNextAppRouter = (projectRoot, agent, reactGrabAlreadyConfigured) => {
844
923
  const layoutPath = findLayoutFile(projectRoot);
845
924
  if (!layoutPath) {
@@ -1031,6 +1110,79 @@ ${originalContent}`;
1031
1110
  newContent
1032
1111
  };
1033
1112
  };
1113
+ var transformTanStack = (projectRoot, agent, reactGrabAlreadyConfigured) => {
1114
+ const rootPath = findTanStackRootFile(projectRoot);
1115
+ if (!rootPath) {
1116
+ return {
1117
+ success: false,
1118
+ filePath: "",
1119
+ message: 'Could not find src/routes/__root.tsx or app/routes/__root.tsx.\n\nTo set up React Grab with TanStack Start, add this to your root route component:\n\n import { useEffect } from "react";\n\n useEffect(() => {\n if (import.meta.env.DEV) {\n void import("react-grab");\n }\n }, []);'
1120
+ };
1121
+ }
1122
+ const originalContent = readFileSync(rootPath, "utf-8");
1123
+ let newContent = originalContent;
1124
+ const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
1125
+ if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
1126
+ return addAgentToExistingTanStack(originalContent, agent, rootPath);
1127
+ }
1128
+ if (hasReactGrabInFile2) {
1129
+ return {
1130
+ success: true,
1131
+ filePath: rootPath,
1132
+ message: "React Grab is already installed in this file",
1133
+ noChanges: true
1134
+ };
1135
+ }
1136
+ const hasUseEffectImport = /import\s+\{[^}]*useEffect[^}]*\}\s+from\s+["']react["']/.test(newContent);
1137
+ if (!hasUseEffectImport) {
1138
+ const reactImportMatch = newContent.match(
1139
+ /import\s+\{([^}]*)\}\s+from\s+["']react["'];?/
1140
+ );
1141
+ if (reactImportMatch) {
1142
+ const existingImports = reactImportMatch[1];
1143
+ newContent = newContent.replace(
1144
+ reactImportMatch[0],
1145
+ `import { ${existingImports.trim()}, useEffect } from "react";`
1146
+ );
1147
+ } else {
1148
+ const firstImportMatch = newContent.match(
1149
+ /^import .+ from ['"].+['"];?\s*$/m
1150
+ );
1151
+ if (firstImportMatch) {
1152
+ newContent = newContent.replace(
1153
+ firstImportMatch[0],
1154
+ `import { useEffect } from "react";
1155
+ ${firstImportMatch[0]}`
1156
+ );
1157
+ } else {
1158
+ newContent = `import { useEffect } from "react";
1159
+
1160
+ ${newContent}`;
1161
+ }
1162
+ }
1163
+ }
1164
+ const effectBlock = TANSTACK_EFFECT_WITH_AGENT(agent);
1165
+ const componentMatch = newContent.match(/function\s+(\w+)\s*\([^)]*\)\s*\{/);
1166
+ if (componentMatch) {
1167
+ const insertPosition = componentMatch.index + componentMatch[0].length;
1168
+ newContent = newContent.slice(0, insertPosition) + `
1169
+ ${effectBlock}
1170
+ ` + newContent.slice(insertPosition);
1171
+ } else {
1172
+ return {
1173
+ success: false,
1174
+ filePath: rootPath,
1175
+ message: "Could not find a component function in the root file"
1176
+ };
1177
+ }
1178
+ return {
1179
+ success: true,
1180
+ filePath: rootPath,
1181
+ message: "Add React Grab" + (agent !== "none" ? ` with ${agent} agent` : ""),
1182
+ originalContent,
1183
+ newContent
1184
+ };
1185
+ };
1034
1186
  var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrabAlreadyConfigured = false) => {
1035
1187
  switch (framework) {
1036
1188
  case "next":
@@ -1048,6 +1200,8 @@ var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrab
1048
1200
  );
1049
1201
  case "vite":
1050
1202
  return transformVite(projectRoot, agent, reactGrabAlreadyConfigured);
1203
+ case "tanstack":
1204
+ return transformTanStack(projectRoot, agent, reactGrabAlreadyConfigured);
1051
1205
  case "webpack":
1052
1206
  return transformWebpack(projectRoot, agent, reactGrabAlreadyConfigured);
1053
1207
  default:
@@ -1283,6 +1437,8 @@ var findReactGrabFile = (projectRoot, framework, nextRouterType) => {
1283
1437
  return findDocumentFile(projectRoot);
1284
1438
  case "vite":
1285
1439
  return findIndexHtml(projectRoot);
1440
+ case "tanstack":
1441
+ return findTanStackRootFile(projectRoot);
1286
1442
  case "webpack":
1287
1443
  return findEntryFile(projectRoot);
1288
1444
  default:
@@ -1379,6 +1535,31 @@ var addOptionsToWebpackImport = (originalContent, options, filePath) => {
1379
1535
  newContent
1380
1536
  };
1381
1537
  };
1538
+ var addOptionsToTanStackImport = (originalContent, options, filePath) => {
1539
+ const reactGrabImportMatch = originalContent.match(
1540
+ /void\s+import\s*\(\s*["']react-grab["']\s*\)/
1541
+ );
1542
+ if (!reactGrabImportMatch) {
1543
+ return {
1544
+ success: false,
1545
+ filePath,
1546
+ message: "Could not find React Grab import"
1547
+ };
1548
+ }
1549
+ const optionsJson = formatOptionsAsJson(options);
1550
+ const newImport = `import("react-grab/core").then(({ init }) => init(${optionsJson}))`;
1551
+ const newContent = originalContent.replace(
1552
+ reactGrabImportMatch[0],
1553
+ newImport
1554
+ );
1555
+ return {
1556
+ success: true,
1557
+ filePath,
1558
+ message: "Update React Grab options",
1559
+ originalContent,
1560
+ newContent
1561
+ };
1562
+ };
1382
1563
  var previewOptionsTransform = (projectRoot, framework, nextRouterType, options) => {
1383
1564
  const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
1384
1565
  if (!filePath) {
@@ -1401,6 +1582,8 @@ var previewOptionsTransform = (projectRoot, framework, nextRouterType, options)
1401
1582
  return addOptionsToNextScript(originalContent, options, filePath);
1402
1583
  case "vite":
1403
1584
  return addOptionsToViteScript(originalContent, options, filePath);
1585
+ case "tanstack":
1586
+ return addOptionsToTanStackImport(originalContent, options, filePath);
1404
1587
  case "webpack":
1405
1588
  return addOptionsToWebpackImport(originalContent, options, filePath);
1406
1589
  default:
@@ -1511,6 +1694,36 @@ var removeAgentFromWebpack = (originalContent, agent, filePath) => {
1511
1694
  newContent
1512
1695
  };
1513
1696
  };
1697
+ var removeAgentFromTanStack = (originalContent, agent, filePath) => {
1698
+ const agentPackage = `@react-grab/${agent}`;
1699
+ if (!originalContent.includes(agentPackage)) {
1700
+ return {
1701
+ success: true,
1702
+ filePath,
1703
+ message: `Agent ${agent} is not configured in this file`,
1704
+ noChanges: true
1705
+ };
1706
+ }
1707
+ const agentImportPattern = new RegExp(
1708
+ `\\s*void\\s+import\\s*\\(\\s*["']${agentPackage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/client["']\\s*\\);?`,
1709
+ "g"
1710
+ );
1711
+ const newContent = originalContent.replace(agentImportPattern, "");
1712
+ if (newContent === originalContent) {
1713
+ return {
1714
+ success: false,
1715
+ filePath,
1716
+ message: `Could not find agent ${agent} import to remove`
1717
+ };
1718
+ }
1719
+ return {
1720
+ success: true,
1721
+ filePath,
1722
+ message: `Remove ${agent} agent`,
1723
+ originalContent,
1724
+ newContent
1725
+ };
1726
+ };
1514
1727
  var previewAgentRemoval = (projectRoot, framework, nextRouterType, agent) => {
1515
1728
  const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
1516
1729
  if (!filePath) {
@@ -1527,6 +1740,8 @@ var previewAgentRemoval = (projectRoot, framework, nextRouterType, agent) => {
1527
1740
  return removeAgentFromNextApp(originalContent, agent, filePath);
1528
1741
  case "vite":
1529
1742
  return removeAgentFromVite(originalContent, agent, filePath);
1743
+ case "tanstack":
1744
+ return removeAgentFromTanStack(originalContent, agent, filePath);
1530
1745
  case "webpack":
1531
1746
  return removeAgentFromWebpack(originalContent, agent, filePath);
1532
1747
  default:
@@ -1599,7 +1814,7 @@ var previewPackageJsonAgentRemoval = (projectRoot, agent) => {
1599
1814
  };
1600
1815
 
1601
1816
  // src/commands/add.ts
1602
- var VERSION = "0.1.0-beta.11";
1817
+ var VERSION = "0.1.0-beta.12";
1603
1818
  var formatInstalledAgentNames = (agents) => agents.map((agent) => AGENT_NAMES[agent] || agent).join(", ");
1604
1819
  var add = new Command().name("add").alias("install").description("add an agent integration").argument("[agent]", `agent to add (${AGENTS.join(", ")})`).option("-y, --yes", "skip confirmation prompts", false).option(
1605
1820
  "-c, --cwd <cwd>",
@@ -1935,7 +2150,7 @@ var MAX_KEY_HOLD_DURATION_MS = 2e3;
1935
2150
  var MAX_CONTEXT_LINES = 50;
1936
2151
 
1937
2152
  // src/commands/configure.ts
1938
- var VERSION2 = "0.1.0-beta.11";
2153
+ var VERSION2 = "0.1.0-beta.12";
1939
2154
  var isMac = process.platform === "darwin";
1940
2155
  var META_LABEL = isMac ? "Cmd" : "Win";
1941
2156
  var ALT_LABEL = isMac ? "Option" : "Alt";
@@ -2435,7 +2650,7 @@ var uninstallPackagesWithFeedback = (packages, packageManager, projectRoot) => {
2435
2650
  };
2436
2651
 
2437
2652
  // src/commands/init.ts
2438
- var VERSION3 = "0.1.0-beta.11";
2653
+ var VERSION3 = "0.1.0-beta.12";
2439
2654
  var REPORT_URL = "https://react-grab.com/api/report-cli";
2440
2655
  var DOCS_URL = "https://github.com/aidenybai/react-grab";
2441
2656
  var reportToCli = (type, config, error) => {
@@ -2455,6 +2670,7 @@ var reportToCli = (type, config, error) => {
2455
2670
  var FRAMEWORK_NAMES = {
2456
2671
  next: "Next.js",
2457
2672
  vite: "Vite",
2673
+ tanstack: "TanStack Start",
2458
2674
  webpack: "Webpack",
2459
2675
  unknown: "Unknown"
2460
2676
  };
@@ -2934,7 +3150,7 @@ var init = new Command().name("init").description("initialize React Grab in your
2934
3150
  frameworkSpinner.fail("Could not detect a supported framework.");
2935
3151
  logger.break();
2936
3152
  logger.log(
2937
- "React Grab supports Next.js, Vite, and Webpack projects."
3153
+ "React Grab supports Next.js, Vite, TanStack Start, and Webpack projects."
2938
3154
  );
2939
3155
  logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
2940
3156
  logger.break();
@@ -2960,8 +3176,42 @@ var init = new Command().name("init").description("initialize React Grab in your
2960
3176
  const finalFramework = projectInfo.framework;
2961
3177
  const finalPackageManager = projectInfo.packageManager;
2962
3178
  const finalNextRouterType = projectInfo.nextRouterType;
2963
- const agentIntegration = opts.agent || "none";
3179
+ let agentIntegration = opts.agent || "none";
2964
3180
  const agentsToRemove = [];
3181
+ if (!isNonInteractive && !opts.agent) {
3182
+ logger.break();
3183
+ const { wantAddAgent } = await prompts3({
3184
+ type: "confirm",
3185
+ name: "wantAddAgent",
3186
+ message: `Would you like to add an ${highlighter.info("agent integration")}?`,
3187
+ initial: false
3188
+ });
3189
+ if (wantAddAgent === void 0) {
3190
+ logger.break();
3191
+ process.exit(1);
3192
+ }
3193
+ if (wantAddAgent) {
3194
+ const { agent } = await prompts3({
3195
+ type: "select",
3196
+ name: "agent",
3197
+ message: `Which ${highlighter.info("agent integration")} would you like to add?`,
3198
+ choices: [
3199
+ ...AGENTS.map((innerAgent) => ({
3200
+ title: getAgentName(innerAgent),
3201
+ value: innerAgent
3202
+ })),
3203
+ { title: "Skip", value: "skip" }
3204
+ ]
3205
+ });
3206
+ if (agent === void 0) {
3207
+ logger.break();
3208
+ process.exit(1);
3209
+ }
3210
+ if (agent !== "skip") {
3211
+ agentIntegration = agent;
3212
+ }
3213
+ }
3214
+ }
2965
3215
  const result = previewTransform(
2966
3216
  projectInfo.projectRoot,
2967
3217
  finalFramework,
@@ -3067,7 +3317,7 @@ var init = new Command().name("init").description("initialize React Grab in your
3067
3317
  reportToCli("error", void 0, error);
3068
3318
  }
3069
3319
  });
3070
- var VERSION4 = "0.1.0-beta.11";
3320
+ var VERSION4 = "0.1.0-beta.12";
3071
3321
  var remove = new Command().name("remove").description("remove an agent integration").argument(
3072
3322
  "[agent]",
3073
3323
  "agent to remove (claude-code, cursor, opencode, codex, gemini, amp, ami)"
@@ -3246,7 +3496,7 @@ var remove = new Command().name("remove").description("remove an agent integrati
3246
3496
  });
3247
3497
 
3248
3498
  // src/cli.ts
3249
- var VERSION5 = "0.1.0-beta.11";
3499
+ var VERSION5 = "0.1.0-beta.12";
3250
3500
  var VERSION_API_URL = "https://www.react-grab.com/api/version";
3251
3501
  process.on("SIGINT", () => process.exit(0));
3252
3502
  process.on("SIGTERM", () => process.exit(0));
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@react-grab/cli",
3
- "version": "0.1.0-beta.12",
4
- "type": "module",
3
+ "version": "0.1.0-beta.13",
5
4
  "bin": {
6
5
  "react-grab": "./dist/cli.js"
7
6
  },
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "type": "module",
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/cli.d.ts",
@@ -12,14 +15,6 @@
12
15
  "require": "./dist/cli.cjs"
13
16
  }
14
17
  },
15
- "files": [
16
- "dist"
17
- ],
18
- "devDependencies": {
19
- "@types/prompts": "^2.4.9",
20
- "tsup": "^8.4.0",
21
- "vitest": "^3.2.4"
22
- },
23
18
  "dependencies": {
24
19
  "@antfu/ni": "^0.23.0",
25
20
  "commander": "^14.0.0",
@@ -27,6 +22,11 @@
27
22
  "picocolors": "^1.1.1",
28
23
  "prompts": "^2.4.2"
29
24
  },
25
+ "devDependencies": {
26
+ "@types/prompts": "^2.4.9",
27
+ "tsup": "^8.4.0",
28
+ "vitest": "^3.2.4"
29
+ },
30
30
  "scripts": {
31
31
  "dev": "tsup --watch",
32
32
  "build": "rm -rf dist && NODE_ENV=production tsup",