@react-grab/cli 0.0.71 → 0.0.73

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 +445 -504
  2. package/dist/cli.js +444 -503
  3. package/package.json +6 -5
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { select, confirm } from '@inquirer/prompts';
3
- import pc from 'picocolors';
4
- import yargs from 'yargs';
5
- import { hideBin } from 'yargs/helpers';
6
- import { readFileSync, existsSync, writeFileSync, readdirSync, accessSync, constants } from 'fs';
2
+ import { Command } from 'commander';
3
+ import prompts2 from 'prompts';
4
+ import { execSync } from 'child_process';
5
+ import { existsSync, readFileSync, writeFileSync, accessSync, constants, readdirSync } from 'fs';
7
6
  import { join, basename } from 'path';
8
7
  import { detect } from '@antfu/ni';
9
- import { execSync } from 'child_process';
8
+ import { cyan, dim, green, yellow, red } from 'kleur/colors';
9
+ import ora from 'ora';
10
10
 
11
11
  var detectPackageManager = async (projectRoot) => {
12
12
  const detected = await detect({ cwd: projectRoot });
@@ -294,7 +294,7 @@ var detectProject = async (projectRoot = process.cwd()) => {
294
294
  };
295
295
  };
296
296
 
297
- // src/diff.ts
297
+ // src/utils/diff.ts
298
298
  var RED = "\x1B[31m";
299
299
  var GREEN = "\x1B[32m";
300
300
  var GRAY = "\x1B[90m";
@@ -381,6 +381,50 @@ ${BOLD}File: ${filePath}${RESET}`);
381
381
  console.log(formatDiff(diff));
382
382
  console.log("\u2500".repeat(60));
383
383
  };
384
+ var highlighter = {
385
+ error: red,
386
+ warn: yellow,
387
+ info: cyan,
388
+ success: green,
389
+ dim
390
+ };
391
+
392
+ // src/utils/logger.ts
393
+ var logger = {
394
+ error(...args) {
395
+ console.log(highlighter.error(args.join(" ")));
396
+ },
397
+ warn(...args) {
398
+ console.log(highlighter.warn(args.join(" ")));
399
+ },
400
+ info(...args) {
401
+ console.log(highlighter.info(args.join(" ")));
402
+ },
403
+ success(...args) {
404
+ console.log(highlighter.success(args.join(" ")));
405
+ },
406
+ log(...args) {
407
+ console.log(args.join(" "));
408
+ },
409
+ break() {
410
+ console.log("");
411
+ }
412
+ };
413
+
414
+ // src/utils/handle-error.ts
415
+ var handleError = (error) => {
416
+ logger.break();
417
+ logger.error(
418
+ "Something went wrong. Please check the error below for more details."
419
+ );
420
+ logger.error("If the problem persists, please open an issue on GitHub.");
421
+ logger.error("");
422
+ if (error instanceof Error) {
423
+ logger.error(error.message);
424
+ }
425
+ logger.break();
426
+ process.exit(1);
427
+ };
384
428
  var INSTALL_COMMANDS = {
385
429
  npm: "npm install",
386
430
  yarn: "yarn add",
@@ -411,8 +455,9 @@ var getPackagesToInstall = (agent, includeReactGrab = true) => {
411
455
  }
412
456
  return packages;
413
457
  };
458
+ var spinner = (text, options) => ora({ text, isSilent: options?.silent, stream: process.stdout });
414
459
 
415
- // src/templates.ts
460
+ // src/utils/templates.ts
416
461
  var NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
417
462
  <Script
418
463
  src="//unpkg.com/react-grab/dist/index.global.js"
@@ -485,7 +530,7 @@ var WEBPACK_IMPORT_WITH_AGENT = (agent) => {
485
530
  };
486
531
  var SCRIPT_IMPORT = 'import Script from "next/script";';
487
532
 
488
- // src/transform.ts
533
+ // src/utils/transform.ts
489
534
  var hasReactGrabCode = (content) => {
490
535
  const fuzzyPatterns = [
491
536
  /["'`][^"'`]*react-grab/,
@@ -641,11 +686,13 @@ var addAgentToExistingVite = (originalContent, agent, filePath) => {
641
686
  };
642
687
  }
643
688
  const agentImport = `import("${agentPackage}/client");`;
644
- const reactGrabImportMatch = originalContent.match(/import\s*\(\s*["']react-grab["']\s*\)/);
689
+ const reactGrabImportMatch = originalContent.match(/import\s*\(\s*["']react-grab["']\s*\);?/);
645
690
  if (reactGrabImportMatch) {
691
+ const matchedText = reactGrabImportMatch[0];
692
+ const hasSemicolon = matchedText.endsWith(";");
646
693
  const newContent = originalContent.replace(
647
- reactGrabImportMatch[0],
648
- `${reactGrabImportMatch[0]};
694
+ matchedText,
695
+ `${hasSemicolon ? matchedText.slice(0, -1) : matchedText};
649
696
  ${agentImport}`
650
697
  );
651
698
  return {
@@ -681,11 +728,13 @@ var addAgentToExistingWebpack = (originalContent, agent, filePath) => {
681
728
  };
682
729
  }
683
730
  const agentImport = `import("${agentPackage}/client");`;
684
- const reactGrabImportMatch = originalContent.match(/import\s*\(\s*["']react-grab["']\s*\)/);
731
+ const reactGrabImportMatch = originalContent.match(/import\s*\(\s*["']react-grab["']\s*\);?/);
685
732
  if (reactGrabImportMatch) {
733
+ const matchedText = reactGrabImportMatch[0];
734
+ const hasSemicolon = matchedText.endsWith(";");
686
735
  const newContent = originalContent.replace(
687
- reactGrabImportMatch[0],
688
- `${reactGrabImportMatch[0]};
736
+ matchedText,
737
+ `${hasSemicolon ? matchedText.slice(0, -1) : matchedText};
689
738
  ${agentImport}`
690
739
  );
691
740
  return {
@@ -1023,9 +1072,187 @@ var applyPackageJsonTransform = (result) => {
1023
1072
  return { success: true };
1024
1073
  };
1025
1074
 
1026
- // src/cli.ts
1027
- var VERSION = "0.0.71";
1028
- var REPORT_URL = "https://reactgrab.com/api/report-cli";
1075
+ // src/commands/add.ts
1076
+ var AGENT_NAMES = {
1077
+ "claude-code": "Claude Code",
1078
+ cursor: "Cursor",
1079
+ opencode: "Opencode"
1080
+ };
1081
+ var add = new Command().name("add").description("add an agent integration").argument("[agent]", "agent to add (claude-code, cursor, opencode)").option("-y, --yes", "skip confirmation prompts", false).option(
1082
+ "-c, --cwd <cwd>",
1083
+ "working directory (defaults to current directory)",
1084
+ process.cwd()
1085
+ ).action(async (agentArg, opts) => {
1086
+ try {
1087
+ const cwd = opts.cwd;
1088
+ const isNonInteractive = opts.yes;
1089
+ logger.log(`\u269B ${highlighter.info("React Grab")}`);
1090
+ logger.break();
1091
+ const preflightSpinner = spinner("Preflight checks.").start();
1092
+ const projectInfo = await detectProject(cwd);
1093
+ if (!projectInfo.hasReactGrab) {
1094
+ preflightSpinner.fail("React Grab is not installed.");
1095
+ logger.break();
1096
+ logger.error(
1097
+ `Run ${highlighter.info("react-grab init")} first to install React Grab.`
1098
+ );
1099
+ logger.break();
1100
+ process.exit(1);
1101
+ }
1102
+ preflightSpinner.succeed();
1103
+ const availableAgents = ["claude-code", "cursor", "opencode"].filter((agent) => !projectInfo.installedAgents.includes(agent));
1104
+ if (availableAgents.length === 0) {
1105
+ logger.break();
1106
+ logger.success("All agent integrations are already installed.");
1107
+ logger.break();
1108
+ process.exit(0);
1109
+ }
1110
+ let agentIntegration;
1111
+ if (agentArg) {
1112
+ if (!["claude-code", "cursor", "opencode"].includes(agentArg)) {
1113
+ logger.break();
1114
+ logger.error(`Invalid agent: ${agentArg}`);
1115
+ logger.error("Available agents: claude-code, cursor, opencode");
1116
+ logger.break();
1117
+ process.exit(1);
1118
+ }
1119
+ if (projectInfo.installedAgents.includes(agentArg)) {
1120
+ logger.break();
1121
+ logger.warn(`${AGENT_NAMES[agentArg]} is already installed.`);
1122
+ logger.break();
1123
+ process.exit(0);
1124
+ }
1125
+ agentIntegration = agentArg;
1126
+ } else if (!isNonInteractive) {
1127
+ logger.break();
1128
+ const { agent } = await prompts2({
1129
+ type: "select",
1130
+ name: "agent",
1131
+ message: `Which ${highlighter.info("agent integration")} would you like to add?`,
1132
+ choices: availableAgents.map((innerAgent) => ({
1133
+ title: AGENT_NAMES[innerAgent],
1134
+ value: innerAgent
1135
+ }))
1136
+ });
1137
+ if (!agent) {
1138
+ logger.break();
1139
+ process.exit(1);
1140
+ }
1141
+ agentIntegration = agent;
1142
+ } else {
1143
+ logger.break();
1144
+ logger.error("Please specify an agent to add.");
1145
+ logger.error("Available agents: " + availableAgents.join(", "));
1146
+ logger.break();
1147
+ process.exit(1);
1148
+ }
1149
+ const addingSpinner = spinner(
1150
+ `Adding ${AGENT_NAMES[agentIntegration]}.`
1151
+ ).start();
1152
+ addingSpinner.succeed();
1153
+ const result = previewTransform(
1154
+ projectInfo.projectRoot,
1155
+ projectInfo.framework,
1156
+ projectInfo.nextRouterType,
1157
+ agentIntegration,
1158
+ true
1159
+ );
1160
+ const packageJsonResult = previewPackageJsonTransform(
1161
+ projectInfo.projectRoot,
1162
+ agentIntegration,
1163
+ projectInfo.installedAgents
1164
+ );
1165
+ if (!result.success) {
1166
+ logger.break();
1167
+ logger.error(result.message);
1168
+ logger.break();
1169
+ process.exit(1);
1170
+ }
1171
+ const hasLayoutChanges = !result.noChanges && result.originalContent && result.newContent;
1172
+ const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
1173
+ if (hasLayoutChanges || hasPackageJsonChanges) {
1174
+ logger.break();
1175
+ if (hasLayoutChanges) {
1176
+ printDiff(result.filePath, result.originalContent, result.newContent);
1177
+ }
1178
+ if (hasPackageJsonChanges) {
1179
+ if (hasLayoutChanges) {
1180
+ logger.break();
1181
+ }
1182
+ printDiff(
1183
+ packageJsonResult.filePath,
1184
+ packageJsonResult.originalContent,
1185
+ packageJsonResult.newContent
1186
+ );
1187
+ }
1188
+ if (!isNonInteractive) {
1189
+ logger.break();
1190
+ const { proceed } = await prompts2({
1191
+ type: "confirm",
1192
+ name: "proceed",
1193
+ message: "Apply these changes?",
1194
+ initial: true
1195
+ });
1196
+ if (!proceed) {
1197
+ logger.break();
1198
+ logger.log("Changes cancelled.");
1199
+ logger.break();
1200
+ process.exit(0);
1201
+ }
1202
+ }
1203
+ }
1204
+ const packages = getPackagesToInstall(agentIntegration, false);
1205
+ if (packages.length > 0) {
1206
+ const installSpinner = spinner(
1207
+ `Installing ${packages.join(", ")}.`
1208
+ ).start();
1209
+ try {
1210
+ installPackages(packages, projectInfo.packageManager, projectInfo.projectRoot);
1211
+ installSpinner.succeed();
1212
+ } catch (error) {
1213
+ installSpinner.fail();
1214
+ handleError(error);
1215
+ }
1216
+ }
1217
+ if (hasLayoutChanges) {
1218
+ const writeSpinner = spinner(`Applying changes to ${result.filePath}.`).start();
1219
+ const writeResult = applyTransform(result);
1220
+ if (!writeResult.success) {
1221
+ writeSpinner.fail();
1222
+ logger.break();
1223
+ logger.error(writeResult.error || "Failed to write file.");
1224
+ logger.break();
1225
+ process.exit(1);
1226
+ }
1227
+ writeSpinner.succeed();
1228
+ }
1229
+ if (hasPackageJsonChanges) {
1230
+ const packageJsonSpinner = spinner(
1231
+ `Applying changes to ${packageJsonResult.filePath}.`
1232
+ ).start();
1233
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
1234
+ if (!packageJsonWriteResult.success) {
1235
+ packageJsonSpinner.fail();
1236
+ logger.break();
1237
+ logger.error(packageJsonWriteResult.error || "Failed to write file.");
1238
+ logger.break();
1239
+ process.exit(1);
1240
+ }
1241
+ packageJsonSpinner.succeed();
1242
+ }
1243
+ logger.break();
1244
+ logger.log(
1245
+ `${highlighter.success("Success!")} ${AGENT_NAMES[agentIntegration]} has been added.`
1246
+ );
1247
+ logger.log("Make sure to start the agent server before using it.");
1248
+ logger.break();
1249
+ } catch (error) {
1250
+ handleError(error);
1251
+ }
1252
+ });
1253
+ var VERSION = "0.0.73";
1254
+ var REPORT_URL = "https://react-grab.com/api/report-cli";
1255
+ var DOCS_URL = "https://github.com/aidenybai/react-grab";
1029
1256
  var reportToCli = async (type, config, error) => {
1030
1257
  try {
1031
1258
  await fetch(REPORT_URL, {
@@ -1055,416 +1282,144 @@ var PACKAGE_MANAGER_NAMES = {
1055
1282
  pnpm: "pnpm",
1056
1283
  bun: "Bun"
1057
1284
  };
1058
- var AGENT_NAMES = {
1059
- "claude-code": "Claude Code",
1060
- cursor: "Cursor",
1061
- opencode: "Opencode"
1062
- };
1063
1285
  var UNSUPPORTED_FRAMEWORK_NAMES = {
1064
1286
  remix: "Remix",
1065
1287
  astro: "Astro",
1066
1288
  sveltekit: "SvelteKit",
1067
1289
  gatsby: "Gatsby"
1068
1290
  };
1069
- var DOCS_URL = "https://github.com/aidenybai/react-grab";
1070
- var showDocsLink = () => {
1071
- console.log("\nFor manual installation instructions, visit:");
1072
- console.log(` ${pc.cyan(DOCS_URL)}
1073
- `);
1074
- };
1075
- var showManualInstructions = (framework, nextRouterType) => {
1076
- console.log(
1077
- `
1078
- ${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Manual Setup Instructions:")}
1079
- `
1080
- );
1081
- if (framework === "next" && nextRouterType === "app") {
1082
- console.log(`${pc.bold("Next.js App Router:")}`);
1083
- console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
1084
- console.log(` 2. Add to ${pc.cyan("app/layout.tsx")} inside <head>:`);
1085
- console.log(` ${pc.gray('import Script from "next/script";')}`);
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(")}")}`);
1097
- } else if (framework === "next" && nextRouterType === "pages") {
1098
- console.log(`${pc.bold("Next.js Pages Router:")}`);
1099
- console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
1100
- console.log(` 2. Add to ${pc.cyan("pages/_document.tsx")} inside <Head>:`);
1101
- console.log(` ${pc.gray('import Script from "next/script";')}`);
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(")}")}`);
1113
- } else if (framework === "vite") {
1114
- console.log(`${pc.bold("Vite:")}`);
1115
- console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
1116
- console.log(` 2. Add to ${pc.cyan("index.html")} inside <head>:`);
1117
- console.log(` ${pc.gray('<script type="module">')}`);
1118
- console.log(
1119
- ` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`
1120
- );
1121
- console.log(` ${pc.gray("</script>")}`);
1122
- } else if (framework === "webpack") {
1123
- console.log(`${pc.bold("Webpack:")}`);
1124
- console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
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
- );
1131
- console.log(` ${pc.gray(' import("react-grab");')}`);
1132
- console.log(` ${pc.gray("}")}`);
1133
- } else {
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;
1181
- }
1182
- showDocsLink();
1183
- };
1184
- var showAccuracyWarning = () => {
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
- );
1192
- };
1193
- var parseArgs = async () => {
1194
- const argv = await yargs(hideBin(process.argv)).scriptName("react-grab").usage(
1195
- `${pc.magenta("\u269B")} ${pc.bold("React Grab CLI")} ${pc.gray(`v${VERSION}`)}
1196
-
1197
- ${pc.cyan("Usage:")} $0 [options]
1198
-
1199
- React Grab adds AI-powered context selection to your React application,
1200
- allowing you to select components and copy their context for AI assistants.
1201
-
1202
- The CLI auto-detects your project configuration (framework, package manager,
1203
- router type) and installs React Grab with optional agent integrations.`
1204
- ).option("framework", {
1205
- alias: "f",
1206
- type: "string",
1207
- choices: ["next", "vite", "webpack"],
1208
- description: "Override detected framework"
1209
- }).option("package-manager", {
1210
- alias: "p",
1211
- type: "string",
1212
- choices: ["npm", "yarn", "pnpm", "bun"],
1213
- description: "Override detected package manager"
1214
- }).option("router", {
1215
- alias: "r",
1216
- type: "string",
1217
- choices: ["app", "pages"],
1218
- description: "Next.js router type (only for Next.js projects)"
1219
- }).option("agent", {
1220
- alias: "a",
1221
- type: "string",
1222
- choices: ["claude-code", "cursor", "opencode", "none"],
1223
- description: "Agent integration to automatically forward selected elements to agent instead of copying to clipboard"
1224
- }).option("yes", {
1225
- alias: "y",
1226
- type: "boolean",
1227
- default: false,
1228
- description: "Skip all prompts and use auto-detected/default values"
1229
- }).option("skip-install", {
1230
- type: "boolean",
1231
- default: false,
1232
- description: "Only modify config files, skip npm/yarn/pnpm install"
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(
1240
- `${pc.bold("Agent Integrations:")}
1241
- ${pc.cyan("claude-code")} Connect React Grab to Claude Code
1242
- ${pc.cyan("cursor")} Connect React Grab to Cursor IDE
1243
- ${pc.cyan("opencode")} Connect React Grab to Opencode
1244
-
1245
- ${pc.bold("Supported Frameworks:")}
1246
- ${pc.cyan("next")} Next.js (App Router & Pages Router)
1247
- ${pc.cyan("vite")} Vite-based React projects
1248
- ${pc.cyan("webpack")} Webpack-based React projects
1249
-
1250
- ${pc.bold("Documentation:")} ${pc.underline(DOCS_URL)}`
1251
- ).wrap(Math.min(100, process.stdout.columns || 80)).parse();
1252
- return {
1253
- framework: argv.framework,
1254
- packageManager: argv["package-manager"],
1255
- router: argv.router,
1256
- agent: argv.agent,
1257
- yes: argv.yes,
1258
- skipInstall: argv["skip-install"]
1259
- };
1260
- };
1261
- var main = async () => {
1262
- const args = await parseArgs();
1263
- const isNonInteractive = args.yes;
1264
- console.log(
1265
- `
1266
- ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`
1267
- );
1268
- const projectInfo = await detectProject(process.cwd());
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
- );
1275
- if (projectInfo.framework === "next") {
1276
- console.log(
1277
- `- Router Type: ${pc.cyan(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`
1278
- );
1279
- }
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
- );
1286
- if (projectInfo.installedAgents.length > 0) {
1287
- console.log(
1288
- `- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`
1289
- );
1290
- }
1291
- console.log("");
1292
- if (projectInfo.unsupportedFramework) {
1293
- const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
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
- );
1301
- showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
1302
- process.exit(0);
1303
- }
1304
- if (projectInfo.framework === "unknown") {
1305
- if (projectInfo.isMonorepo && !isNonInteractive) {
1306
- const workspaceProjects = findWorkspaceProjects(projectInfo.projectRoot);
1307
- const reactProjects = workspaceProjects.filter(
1308
- (p) => p.hasReact || p.framework !== "unknown"
1291
+ var init = new Command().name("init").description("initialize React Grab in your project").option("-y, --yes", "skip confirmation prompts", false).option("-f, --force", "force overwrite existing config", false).option(
1292
+ "-a, --agent <agent>",
1293
+ "agent integration (claude-code, cursor, opencode)"
1294
+ ).option("--skip-install", "skip package installation", false).option(
1295
+ "-c, --cwd <cwd>",
1296
+ "working directory (defaults to current directory)",
1297
+ process.cwd()
1298
+ ).action(async (opts) => {
1299
+ try {
1300
+ const cwd = opts.cwd;
1301
+ const isNonInteractive = opts.yes;
1302
+ logger.log(`\u269B ${highlighter.info("React Grab")}`);
1303
+ logger.break();
1304
+ const preflightSpinner = spinner("Preflight checks.").start();
1305
+ const projectInfo = await detectProject(cwd);
1306
+ if (projectInfo.hasReactGrab && !opts.force) {
1307
+ preflightSpinner.succeed();
1308
+ logger.break();
1309
+ logger.warn("React Grab is already installed.");
1310
+ logger.log(
1311
+ `Use ${highlighter.info("--force")} to reconfigure, or ${highlighter.info("react-grab add")} to add an agent.`
1309
1312
  );
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])}`
1313
+ logger.break();
1314
+ process.exit(0);
1315
+ }
1316
+ preflightSpinner.succeed();
1317
+ const frameworkSpinner = spinner("Verifying framework.").start();
1318
+ if (projectInfo.unsupportedFramework) {
1319
+ const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
1320
+ frameworkSpinner.fail(`Found ${highlighter.info(frameworkName)}.`);
1321
+ logger.break();
1322
+ logger.log(`${frameworkName} is not yet supported by automatic setup.`);
1323
+ logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1324
+ logger.break();
1325
+ process.exit(1);
1326
+ }
1327
+ if (projectInfo.framework === "unknown") {
1328
+ if (projectInfo.isMonorepo && !isNonInteractive) {
1329
+ frameworkSpinner.info("Verifying framework. Found monorepo.");
1330
+ const workspaceProjects = findWorkspaceProjects(projectInfo.projectRoot);
1331
+ const reactProjects = workspaceProjects.filter(
1332
+ (project) => project.hasReact || project.framework !== "unknown"
1336
1333
  );
1337
- if (newProjectInfo.framework === "next") {
1338
- console.log(
1339
- `- Router Type: ${pc.cyan(newProjectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`
1334
+ if (reactProjects.length > 0) {
1335
+ logger.break();
1336
+ const sortedProjects = [...reactProjects].sort((projectA, projectB) => {
1337
+ if (projectA.framework === "unknown" && projectB.framework !== "unknown") return 1;
1338
+ if (projectA.framework !== "unknown" && projectB.framework === "unknown") return -1;
1339
+ return 0;
1340
+ });
1341
+ const { selectedProject } = await prompts2({
1342
+ type: "select",
1343
+ name: "selectedProject",
1344
+ message: "Select a project to install React Grab:",
1345
+ choices: sortedProjects.map((project) => {
1346
+ const frameworkLabel = project.framework !== "unknown" ? ` ${highlighter.dim(`(${FRAMEWORK_NAMES[project.framework]})`)}` : "";
1347
+ return {
1348
+ title: `${project.name}${frameworkLabel}`,
1349
+ value: project.path
1350
+ };
1351
+ })
1352
+ });
1353
+ if (!selectedProject) {
1354
+ logger.break();
1355
+ process.exit(1);
1356
+ }
1357
+ process.chdir(selectedProject);
1358
+ const newProjectInfo = await detectProject(selectedProject);
1359
+ Object.assign(projectInfo, newProjectInfo);
1360
+ const newFrameworkSpinner = spinner("Verifying framework.").start();
1361
+ newFrameworkSpinner.succeed(
1362
+ `Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[newProjectInfo.framework])}.`
1340
1363
  );
1364
+ } else {
1365
+ frameworkSpinner.fail("Could not detect a supported framework.");
1366
+ logger.break();
1367
+ logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1368
+ logger.break();
1369
+ process.exit(1);
1341
1370
  }
1342
- console.log(
1343
- `- React Grab: ${newProjectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`
1344
- );
1345
- console.log("");
1346
1371
  } 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);
1372
+ frameworkSpinner.fail("Could not detect a supported framework.");
1373
+ logger.break();
1374
+ logger.log("React Grab supports Next.js, Vite, and Webpack projects.");
1375
+ logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1376
+ logger.break();
1377
+ process.exit(1);
1353
1378
  }
1354
1379
  } else {
1355
- console.log(
1356
- `${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
1357
- `
1380
+ frameworkSpinner.succeed(
1381
+ `Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[projectInfo.framework])}.`
1358
1382
  );
1359
- showManualInstructions("unknown");
1360
- process.exit(0);
1361
1383
  }
1362
- }
1363
- let action = "install-all";
1364
- if (projectInfo.hasReactGrab && !isNonInteractive) {
1365
- action = await select({
1366
- message: "React Grab is already installed. What would you like to do?",
1367
- choices: [
1368
- { name: "Add an agent integration", value: "add-agent" },
1369
- { name: "Reconfigure project files", value: "reconfigure" },
1370
- { name: "Reinstall everything", value: "install-all" }
1371
- ]
1372
- });
1373
- } else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
1374
- action = "add-agent";
1375
- } else if (projectInfo.hasReactGrab && isNonInteractive && !args.agent) {
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
- `
1384
+ if (projectInfo.framework === "next") {
1385
+ const routerSpinner = spinner("Detecting router type.").start();
1386
+ routerSpinner.succeed(
1387
+ `Detecting router type. Found ${highlighter.info(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}.`
1388
+ );
1389
+ }
1390
+ const packageManagerSpinner = spinner("Detecting package manager.").start();
1391
+ packageManagerSpinner.succeed(
1392
+ `Detecting package manager. Found ${highlighter.info(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}.`
1382
1393
  );
1383
- action = "reconfigure";
1384
- }
1385
- let finalFramework = args.framework || projectInfo.framework;
1386
- let finalPackageManager = args.packageManager || projectInfo.packageManager;
1387
- let finalNextRouterType = args.router || projectInfo.nextRouterType;
1388
- if (!isNonInteractive && !args.framework) {
1389
- const confirmSettings = await confirm({
1390
- message: "Are these settings correct?",
1391
- default: true
1392
- });
1393
- if (!confirmSettings) {
1394
- finalFramework = await select({
1395
- message: "Select your framework:",
1396
- choices: [
1397
- { name: "Next.js", value: "next" },
1398
- { name: "Vite", value: "vite" },
1399
- { name: "Webpack", value: "webpack" }
1400
- ],
1401
- default: projectInfo.framework
1402
- });
1403
- finalPackageManager = await select({
1404
- message: "Select your package manager:",
1394
+ let finalFramework = projectInfo.framework;
1395
+ let finalPackageManager = projectInfo.packageManager;
1396
+ let finalNextRouterType = projectInfo.nextRouterType;
1397
+ let agentIntegration = opts.agent || "none";
1398
+ if (!isNonInteractive && !opts.agent) {
1399
+ logger.break();
1400
+ const { agent } = await prompts2({
1401
+ type: "select",
1402
+ name: "agent",
1403
+ message: `Would you like to add an ${highlighter.info("agent integration")}?`,
1405
1404
  choices: [
1406
- { name: "npm", value: "npm" },
1407
- { name: "Yarn", value: "yarn" },
1408
- { name: "pnpm", value: "pnpm" },
1409
- { name: "Bun", value: "bun" }
1410
- ],
1411
- default: projectInfo.packageManager
1405
+ { title: "None", value: "none" },
1406
+ { title: "Claude Code", value: "claude-code" },
1407
+ { title: "Cursor", value: "cursor" },
1408
+ { title: "Opencode", value: "opencode" }
1409
+ ]
1412
1410
  });
1413
- if (finalFramework === "next") {
1414
- finalNextRouterType = await select({
1415
- message: "Select your Next.js router type:",
1416
- choices: [
1417
- { name: "App Router", value: "app" },
1418
- { name: "Pages Router", value: "pages" }
1419
- ],
1420
- default: projectInfo.nextRouterType === "app" ? "app" : "pages"
1421
- });
1411
+ if (agent === void 0) {
1412
+ logger.break();
1413
+ process.exit(1);
1422
1414
  }
1415
+ agentIntegration = agent;
1423
1416
  }
1424
- }
1425
- let agentIntegration = args.agent || "none";
1426
- const shouldAskForAgent = (action === "install-all" || action === "add-agent") && !args.agent;
1427
- if (shouldAskForAgent && !isNonInteractive) {
1428
- const availableAgents = [
1429
- { name: "Claude Code", value: "claude-code" },
1430
- { name: "Cursor", value: "cursor" },
1431
- { name: "Opencode", value: "opencode" }
1432
- ].filter((agent) => !projectInfo.installedAgents.includes(agent.value));
1433
- if (availableAgents.length === 0) {
1434
- console.log(
1435
- `
1436
- ${pc.green("All agent integrations are already installed.")}
1437
- `
1438
- );
1439
- } else if (action === "add-agent") {
1440
- agentIntegration = await select({
1441
- message: "Select an agent integration to add:",
1442
- choices: availableAgents
1443
- });
1444
- } else {
1445
- const wantAgentIntegration = await confirm({
1446
- message: "Do you want to add an agent integration (Claude Code, Cursor, or Opencode)?",
1447
- default: false
1448
- });
1449
- if (wantAgentIntegration) {
1450
- agentIntegration = await select({
1451
- message: "Select an agent integration:",
1452
- choices: availableAgents
1453
- });
1454
- }
1455
- }
1456
- }
1457
- const shouldTransform = action === "reconfigure" || action === "install-all" || action === "add-agent" && agentIntegration !== "none";
1458
- if (shouldTransform) {
1459
- console.log(`
1460
- ${pc.magenta("\u269B")} Previewing changes...
1461
- `);
1462
1417
  const result = previewTransform(
1463
1418
  projectInfo.projectRoot,
1464
1419
  finalFramework,
1465
1420
  finalNextRouterType,
1466
1421
  agentIntegration,
1467
- projectInfo.hasReactGrab || action === "add-agent"
1422
+ false
1468
1423
  );
1469
1424
  const packageJsonResult = previewPackageJsonTransform(
1470
1425
  projectInfo.projectRoot,
@@ -1472,24 +1427,22 @@ ${pc.magenta("\u269B")} Previewing changes...
1472
1427
  projectInfo.installedAgents
1473
1428
  );
1474
1429
  if (!result.success) {
1475
- console.error(`${pc.red("Error:")} ${result.message}`);
1476
- showDocsLink();
1430
+ logger.break();
1431
+ logger.error(result.message);
1432
+ logger.error(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1433
+ logger.break();
1477
1434
  process.exit(1);
1478
1435
  }
1479
1436
  const hasLayoutChanges = !result.noChanges && result.originalContent && result.newContent;
1480
1437
  const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
1481
- if (result.noChanges && packageJsonResult.noChanges) {
1482
- console.log(`${pc.cyan("Info:")} ${result.message}`);
1483
- if (packageJsonResult.message) {
1484
- console.log(`${pc.cyan("Info:")} ${packageJsonResult.message}`);
1485
- }
1486
- } else {
1438
+ if (hasLayoutChanges || hasPackageJsonChanges) {
1439
+ logger.break();
1487
1440
  if (hasLayoutChanges) {
1488
1441
  printDiff(result.filePath, result.originalContent, result.newContent);
1489
1442
  }
1490
1443
  if (hasPackageJsonChanges) {
1491
1444
  if (hasLayoutChanges) {
1492
- console.log("");
1445
+ logger.break();
1493
1446
  }
1494
1447
  printDiff(
1495
1448
  packageJsonResult.filePath,
@@ -1497,104 +1450,92 @@ ${pc.magenta("\u269B")} Previewing changes...
1497
1450
  packageJsonResult.newContent
1498
1451
  );
1499
1452
  }
1500
- if (hasLayoutChanges || hasPackageJsonChanges) {
1501
- showAccuracyWarning();
1502
- if (!isNonInteractive) {
1503
- const confirmChanges = await confirm({
1504
- message: "Apply these changes?",
1505
- default: true
1506
- });
1507
- if (!confirmChanges) {
1508
- console.log(`
1509
- ${pc.yellow("Changes cancelled.")}
1510
- `);
1511
- process.exit(0);
1512
- }
1453
+ logger.break();
1454
+ logger.warn("Auto-detection may not be 100% accurate.");
1455
+ logger.warn("Please verify the changes before committing.");
1456
+ if (!isNonInteractive) {
1457
+ logger.break();
1458
+ const { proceed } = await prompts2({
1459
+ type: "confirm",
1460
+ name: "proceed",
1461
+ message: "Apply these changes?",
1462
+ initial: true
1463
+ });
1464
+ if (!proceed) {
1465
+ logger.break();
1466
+ logger.log("Changes cancelled.");
1467
+ logger.break();
1468
+ process.exit(0);
1513
1469
  }
1514
1470
  }
1515
- const shouldInstallReactGrab = (action === "install-all" || action === "add-agent") && !projectInfo.hasReactGrab;
1516
- const shouldInstallAgent = agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration);
1517
- if (!args.skipInstall && (shouldInstallReactGrab || shouldInstallAgent)) {
1518
- const packages = getPackagesToInstall(
1519
- agentIntegration,
1520
- shouldInstallReactGrab
1521
- );
1522
- if (packages.length > 0) {
1523
- console.log(
1524
- `
1525
- ${pc.magenta("\u269B")} Installing: ${pc.cyan(packages.join(", "))}
1526
- `
1527
- );
1528
- try {
1529
- installPackages(
1530
- packages,
1531
- finalPackageManager,
1532
- projectInfo.projectRoot
1533
- );
1534
- console.log(`
1535
- ${pc.green("Packages installed successfully!")}
1536
- `);
1537
- } catch (error) {
1538
- console.error(`
1539
- ${pc.red("Failed to install packages:")}`, error);
1540
- showDocsLink();
1541
- process.exit(1);
1542
- }
1471
+ }
1472
+ const shouldInstallReactGrab = !projectInfo.hasReactGrab;
1473
+ const shouldInstallAgent = agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration);
1474
+ if (!opts.skipInstall && (shouldInstallReactGrab || shouldInstallAgent)) {
1475
+ const packages = getPackagesToInstall(agentIntegration, shouldInstallReactGrab);
1476
+ if (packages.length > 0) {
1477
+ const installSpinner = spinner(
1478
+ `Installing ${packages.join(", ")}.`
1479
+ ).start();
1480
+ try {
1481
+ installPackages(packages, finalPackageManager, projectInfo.projectRoot);
1482
+ installSpinner.succeed();
1483
+ } catch (error) {
1484
+ installSpinner.fail();
1485
+ handleError(error);
1543
1486
  }
1544
1487
  }
1545
- if (hasLayoutChanges) {
1546
- const writeResult = applyTransform(result);
1547
- if (!writeResult.success) {
1548
- console.error(`
1549
- ${pc.red("Error:")} ${writeResult.error}`);
1550
- showDocsLink();
1551
- process.exit(1);
1552
- }
1553
- console.log(`
1554
- ${pc.green("Applied:")} ${result.filePath}`);
1488
+ }
1489
+ if (hasLayoutChanges) {
1490
+ const writeSpinner = spinner(`Applying changes to ${result.filePath}.`).start();
1491
+ const writeResult = applyTransform(result);
1492
+ if (!writeResult.success) {
1493
+ writeSpinner.fail();
1494
+ logger.break();
1495
+ logger.error(writeResult.error || "Failed to write file.");
1496
+ logger.break();
1497
+ process.exit(1);
1555
1498
  }
1556
- if (hasPackageJsonChanges) {
1557
- const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
1558
- if (!packageJsonWriteResult.success) {
1559
- console.error(
1560
- `
1561
- ${pc.red("Error:")} ${packageJsonWriteResult.error}`
1562
- );
1563
- showDocsLink();
1564
- process.exit(1);
1565
- }
1566
- console.log(`${pc.green("Applied:")} ${packageJsonResult.filePath}`);
1499
+ writeSpinner.succeed();
1500
+ }
1501
+ if (hasPackageJsonChanges) {
1502
+ const packageJsonSpinner = spinner(
1503
+ `Applying changes to ${packageJsonResult.filePath}.`
1504
+ ).start();
1505
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
1506
+ if (!packageJsonWriteResult.success) {
1507
+ packageJsonSpinner.fail();
1508
+ logger.break();
1509
+ logger.error(packageJsonWriteResult.error || "Failed to write file.");
1510
+ logger.break();
1511
+ process.exit(1);
1567
1512
  }
1513
+ packageJsonSpinner.succeed();
1568
1514
  }
1569
- }
1570
- console.log(`
1571
- ${pc.green("Done!")}`);
1572
- console.log(`
1573
- Next steps:`);
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)}
1577
- `);
1578
- if (agentIntegration !== "none") {
1579
- console.log(
1580
- `${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`
1515
+ logger.break();
1516
+ logger.log(
1517
+ `${highlighter.success("Success!")} React Grab has been installed.`
1581
1518
  );
1582
- console.log(` Make sure to start the agent server before using it.
1583
- `);
1519
+ logger.log("You may now start your development server.");
1520
+ logger.break();
1521
+ await reportToCli("completed", {
1522
+ framework: finalFramework,
1523
+ packageManager: finalPackageManager,
1524
+ router: finalNextRouterType,
1525
+ agent: agentIntegration !== "none" ? agentIntegration : void 0,
1526
+ isMonorepo: projectInfo.isMonorepo
1527
+ });
1528
+ } catch (error) {
1529
+ handleError(error);
1530
+ await reportToCli("error", void 0, error);
1584
1531
  }
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
- });
1592
- };
1593
- main().catch(async (error) => {
1594
- console.error(`${pc.red("Error:")}`, error);
1595
- console.log("\nFor manual installation instructions, visit:");
1596
- console.log(` ${DOCS_URL}
1597
- `);
1598
- await reportToCli("error", void 0, error);
1599
- process.exit(1);
1600
1532
  });
1533
+
1534
+ // src/cli.ts
1535
+ var VERSION2 = "0.0.73";
1536
+ process.on("SIGINT", () => process.exit(0));
1537
+ process.on("SIGTERM", () => process.exit(0));
1538
+ var program = new Command().name("react-grab").description("add React Grab to your project").version(VERSION2, "-v, --version", "display the version number");
1539
+ program.addCommand(init);
1540
+ program.addCommand(add);
1541
+ program.parse();