@react-grab/cli 0.0.90 → 0.0.92

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 +1288 -66
  2. package/dist/cli.js +1288 -66
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import pc from 'picocolors';
4
- import prompts2 from 'prompts';
4
+ import prompts3 from 'prompts';
5
5
  import { spawn, execSync } from 'child_process';
6
- import { existsSync, readFileSync, writeFileSync, accessSync, constants, readdirSync } from 'fs';
6
+ import { readFileSync, existsSync, writeFileSync, accessSync, constants, readdirSync } from 'fs';
7
7
  import { join, basename } from 'path';
8
8
  import { detect } from '@antfu/ni';
9
9
  import ora from 'ora';
@@ -459,6 +459,12 @@ var INSTALL_COMMANDS = {
459
459
  pnpm: "pnpm add",
460
460
  bun: "bun add"
461
461
  };
462
+ var UNINSTALL_COMMANDS = {
463
+ npm: "npm uninstall",
464
+ yarn: "yarn remove",
465
+ pnpm: "pnpm remove",
466
+ bun: "bun remove"
467
+ };
462
468
  var installPackages = (packages, packageManager, projectRoot, isDev = true) => {
463
469
  if (packages.length === 0) {
464
470
  return;
@@ -483,6 +489,22 @@ var getPackagesToInstall = (agent, includeReactGrab = true) => {
483
489
  }
484
490
  return packages;
485
491
  };
492
+ var uninstallPackages = (packages, packageManager, projectRoot) => {
493
+ if (packages.length === 0) {
494
+ return;
495
+ }
496
+ const command = UNINSTALL_COMMANDS[packageManager];
497
+ const fullCommand = `${command} ${packages.join(" ")}`;
498
+ console.log(`Running: ${fullCommand}
499
+ `);
500
+ execSync(fullCommand, {
501
+ cwd: projectRoot,
502
+ stdio: "inherit"
503
+ });
504
+ };
505
+ var getPackagesToUninstall = (agent) => {
506
+ return [`@react-grab/${agent}`];
507
+ };
486
508
  var spinner = (text, options) => ora({ text, isSilent: options?.silent, stream: process.stdout });
487
509
 
488
510
  // src/utils/templates.ts
@@ -668,18 +690,20 @@ var addAgentToExistingNextApp = (originalContent, agent, filePath) => {
668
690
  noChanges: true
669
691
  };
670
692
  }
671
- const agentScript = `<Script
672
- src="//unpkg.com/${agentPackage}/dist/client.global.js"
673
- strategy="lazyOnload"
674
- />`;
675
- const reactGrabScriptMatch = originalContent.match(
676
- /<(?:Script|script|NextScript)[^>]*react-grab[^>]*\/?>/is
693
+ const agentScript = `{process.env.NODE_ENV === "development" && (
694
+ <Script
695
+ src="//unpkg.com/${agentPackage}/dist/client.global.js"
696
+ strategy="lazyOnload"
697
+ />
698
+ )}`;
699
+ const reactGrabBlockMatch = originalContent.match(
700
+ /\{process\.env\.NODE_ENV\s*===\s*["']development["']\s*&&\s*\(\s*<Script[^>]*react-grab[^>]*\/>\s*\)\}/is
677
701
  );
678
- if (reactGrabScriptMatch) {
702
+ if (reactGrabBlockMatch) {
679
703
  const newContent = originalContent.replace(
680
- reactGrabScriptMatch[0],
681
- `${reactGrabScriptMatch[0]}
682
- ${agentScript}`
704
+ reactGrabBlockMatch[0],
705
+ `${reactGrabBlockMatch[0]}
706
+ ${agentScript}`
683
707
  );
684
708
  return {
685
709
  success: true,
@@ -1029,20 +1053,49 @@ var applyTransform = (result) => {
1029
1053
  }
1030
1054
  return { success: true };
1031
1055
  };
1032
- var AGENT_PREFIXES = {
1033
- "claude-code": "npx @react-grab/claude-code@latest &&",
1034
- cursor: "npx @react-grab/cursor@latest &&",
1035
- opencode: "npx @react-grab/opencode@latest &&",
1036
- codex: "npx @react-grab/codex@latest &&",
1037
- gemini: "npx @react-grab/gemini@latest &&",
1038
- amp: "npx @react-grab/amp@latest &&"
1056
+ var getPackageExecutor = (packageManager) => {
1057
+ switch (packageManager) {
1058
+ case "bun":
1059
+ return "bunx";
1060
+ case "pnpm":
1061
+ return "pnpm dlx";
1062
+ case "yarn":
1063
+ return "npx";
1064
+ case "npm":
1065
+ default:
1066
+ return "npx";
1067
+ }
1068
+ };
1069
+ var AGENT_PACKAGES2 = {
1070
+ "claude-code": "@react-grab/claude-code@latest",
1071
+ cursor: "@react-grab/cursor@latest",
1072
+ opencode: "@react-grab/opencode@latest",
1073
+ codex: "@react-grab/codex@latest",
1074
+ gemini: "@react-grab/gemini@latest",
1075
+ amp: "@react-grab/amp@latest"
1076
+ };
1077
+ var getAgentPrefix = (agent, packageManager) => {
1078
+ const agentPackage = AGENT_PACKAGES2[agent];
1079
+ if (!agentPackage) return null;
1080
+ const executor = getPackageExecutor(packageManager);
1081
+ return `${executor} ${agentPackage} &&`;
1082
+ };
1083
+ var getAllAgentPrefixVariants = (agent) => {
1084
+ const agentPackage = AGENT_PACKAGES2[agent];
1085
+ if (!agentPackage) return [];
1086
+ return [
1087
+ `npx ${agentPackage} &&`,
1088
+ `bunx ${agentPackage} &&`,
1089
+ `pnpm dlx ${agentPackage} &&`,
1090
+ `yarn dlx ${agentPackage} &&`
1091
+ ];
1039
1092
  };
1040
- var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
1041
- if (agent === "none" || agent === "ami" || agent === "visual-edit") {
1093
+ var previewPackageJsonTransform = (projectRoot, agent, installedAgents, packageManager = "npm") => {
1094
+ if (agent === "none" || agent === "visual-edit") {
1042
1095
  return {
1043
1096
  success: true,
1044
1097
  filePath: "",
1045
- message: agent === "ami" || agent === "visual-edit" ? `${agent === "ami" ? "Ami" : "Visual Edit"} does not require package.json modification` : "No agent selected, skipping package.json modification",
1098
+ message: agent === "visual-edit" ? "Visual Edit does not require package.json modification" : "No agent selected, skipping package.json modification",
1046
1099
  noChanges: true
1047
1100
  };
1048
1101
  }
@@ -1055,7 +1108,7 @@ var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
1055
1108
  };
1056
1109
  }
1057
1110
  const originalContent = readFileSync(packageJsonPath, "utf-8");
1058
- const agentPrefix = AGENT_PREFIXES[agent];
1111
+ const agentPrefix = getAgentPrefix(agent, packageManager);
1059
1112
  if (!agentPrefix) {
1060
1113
  return {
1061
1114
  success: false,
@@ -1063,7 +1116,11 @@ var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
1063
1116
  message: `Unknown agent: ${agent}`
1064
1117
  };
1065
1118
  }
1066
- if (originalContent.includes(agentPrefix)) {
1119
+ const allPrefixVariants = getAllAgentPrefixVariants(agent);
1120
+ const hasExistingPrefix = allPrefixVariants.some(
1121
+ (prefix) => originalContent.includes(prefix)
1122
+ );
1123
+ if (hasExistingPrefix) {
1067
1124
  return {
1068
1125
  success: true,
1069
1126
  filePath: packageJsonPath,
@@ -1086,14 +1143,19 @@ var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
1086
1143
  filePath: packageJsonPath,
1087
1144
  message: "No dev script found in package.json",
1088
1145
  noChanges: true,
1089
- warning: `No dev script found. Run: ${agentPrefix} <your dev command>`
1146
+ warning: `Could not inject agent into package.json (no dev script found).
1147
+ Run this command manually before starting your dev server:
1148
+ ${agentPrefix} <your dev command>`
1090
1149
  };
1091
1150
  }
1092
1151
  }
1093
1152
  const currentDevScript = packageJson.scripts[targetScriptKey];
1094
1153
  for (const installedAgent of installedAgents) {
1095
- const existingPrefix = AGENT_PREFIXES[installedAgent];
1096
- if (existingPrefix && currentDevScript.includes(existingPrefix)) {
1154
+ const installedPrefixVariants = getAllAgentPrefixVariants(installedAgent);
1155
+ const hasInstalledAgentPrefix = installedPrefixVariants.some(
1156
+ (prefix) => currentDevScript.includes(prefix)
1157
+ );
1158
+ if (hasInstalledAgentPrefix) {
1097
1159
  return {
1098
1160
  success: true,
1099
1161
  filePath: packageJsonPath,
@@ -1337,9 +1399,192 @@ var previewOptionsTransform = (projectRoot, framework, nextRouterType, options)
1337
1399
  var applyOptionsTransform = (result) => {
1338
1400
  return applyTransform(result);
1339
1401
  };
1402
+ var removeAgentFromNextApp = (originalContent, agent, filePath) => {
1403
+ const agentPackage = `@react-grab/${agent}`;
1404
+ if (!originalContent.includes(agentPackage)) {
1405
+ return {
1406
+ success: true,
1407
+ filePath,
1408
+ message: `Agent ${agent} is not configured in this file`,
1409
+ noChanges: true
1410
+ };
1411
+ }
1412
+ const agentScriptPattern = new RegExp(
1413
+ `\\s*\\{process\\.env\\.NODE_ENV === "development" && \\(\\s*<Script[^>]*${agentPackage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^>]*\\/>\\s*\\)\\}`,
1414
+ "gs"
1415
+ );
1416
+ const simpleScriptPattern = new RegExp(
1417
+ `\\s*<Script[^>]*${agentPackage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^>]*\\/>`,
1418
+ "gi"
1419
+ );
1420
+ let newContent = originalContent.replace(agentScriptPattern, "");
1421
+ if (newContent === originalContent) {
1422
+ newContent = originalContent.replace(simpleScriptPattern, "");
1423
+ }
1424
+ if (newContent === originalContent) {
1425
+ return {
1426
+ success: false,
1427
+ filePath,
1428
+ message: `Could not find agent ${agent} script to remove`
1429
+ };
1430
+ }
1431
+ return {
1432
+ success: true,
1433
+ filePath,
1434
+ message: `Remove ${agent} agent`,
1435
+ originalContent,
1436
+ newContent
1437
+ };
1438
+ };
1439
+ var removeAgentFromVite = (originalContent, agent, filePath) => {
1440
+ const agentPackage = `@react-grab/${agent}`;
1441
+ if (!originalContent.includes(agentPackage)) {
1442
+ return {
1443
+ success: true,
1444
+ filePath,
1445
+ message: `Agent ${agent} is not configured in this file`,
1446
+ noChanges: true
1447
+ };
1448
+ }
1449
+ const agentImportPattern = new RegExp(
1450
+ `\\s*import\\s*\\(\\s*["']${agentPackage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/client["']\\s*\\);?`,
1451
+ "g"
1452
+ );
1453
+ const newContent = originalContent.replace(agentImportPattern, "");
1454
+ if (newContent === originalContent) {
1455
+ return {
1456
+ success: false,
1457
+ filePath,
1458
+ message: `Could not find agent ${agent} import to remove`
1459
+ };
1460
+ }
1461
+ return {
1462
+ success: true,
1463
+ filePath,
1464
+ message: `Remove ${agent} agent`,
1465
+ originalContent,
1466
+ newContent
1467
+ };
1468
+ };
1469
+ var removeAgentFromWebpack = (originalContent, agent, filePath) => {
1470
+ const agentPackage = `@react-grab/${agent}`;
1471
+ if (!originalContent.includes(agentPackage)) {
1472
+ return {
1473
+ success: true,
1474
+ filePath,
1475
+ message: `Agent ${agent} is not configured in this file`,
1476
+ noChanges: true
1477
+ };
1478
+ }
1479
+ const agentImportPattern = new RegExp(
1480
+ `\\s*import\\s*\\(\\s*["']${agentPackage.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/client["']\\s*\\);?`,
1481
+ "g"
1482
+ );
1483
+ const newContent = originalContent.replace(agentImportPattern, "");
1484
+ if (newContent === originalContent) {
1485
+ return {
1486
+ success: false,
1487
+ filePath,
1488
+ message: `Could not find agent ${agent} import to remove`
1489
+ };
1490
+ }
1491
+ return {
1492
+ success: true,
1493
+ filePath,
1494
+ message: `Remove ${agent} agent`,
1495
+ originalContent,
1496
+ newContent
1497
+ };
1498
+ };
1499
+ var previewAgentRemoval = (projectRoot, framework, nextRouterType, agent) => {
1500
+ const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
1501
+ if (!filePath) {
1502
+ return {
1503
+ success: true,
1504
+ filePath: "",
1505
+ message: "Could not find file containing React Grab configuration",
1506
+ noChanges: true
1507
+ };
1508
+ }
1509
+ const originalContent = readFileSync(filePath, "utf-8");
1510
+ switch (framework) {
1511
+ case "next":
1512
+ return removeAgentFromNextApp(originalContent, agent, filePath);
1513
+ case "vite":
1514
+ return removeAgentFromVite(originalContent, agent, filePath);
1515
+ case "webpack":
1516
+ return removeAgentFromWebpack(originalContent, agent, filePath);
1517
+ default:
1518
+ return {
1519
+ success: false,
1520
+ filePath,
1521
+ message: `Unknown framework: ${framework}`
1522
+ };
1523
+ }
1524
+ };
1525
+ var previewPackageJsonAgentRemoval = (projectRoot, agent) => {
1526
+ const packageJsonPath = join(projectRoot, "package.json");
1527
+ if (!existsSync(packageJsonPath)) {
1528
+ return {
1529
+ success: true,
1530
+ filePath: "",
1531
+ message: "Could not find package.json",
1532
+ noChanges: true
1533
+ };
1534
+ }
1535
+ const originalContent = readFileSync(packageJsonPath, "utf-8");
1536
+ const allPrefixVariants = getAllAgentPrefixVariants(agent);
1537
+ if (allPrefixVariants.length === 0) {
1538
+ return {
1539
+ success: true,
1540
+ filePath: packageJsonPath,
1541
+ message: `Unknown agent: ${agent}`,
1542
+ noChanges: true
1543
+ };
1544
+ }
1545
+ const hasAnyPrefix = allPrefixVariants.some(
1546
+ (prefix) => originalContent.includes(prefix)
1547
+ );
1548
+ if (!hasAnyPrefix) {
1549
+ return {
1550
+ success: true,
1551
+ filePath: packageJsonPath,
1552
+ message: `Agent ${agent} dev script is not configured`,
1553
+ noChanges: true
1554
+ };
1555
+ }
1556
+ try {
1557
+ const packageJson = JSON.parse(originalContent);
1558
+ for (const scriptKey of Object.keys(packageJson.scripts || {})) {
1559
+ let scriptValue = packageJson.scripts[scriptKey];
1560
+ if (typeof scriptValue === "string") {
1561
+ for (const prefix of allPrefixVariants) {
1562
+ if (scriptValue.includes(prefix)) {
1563
+ scriptValue = scriptValue.replace(prefix + " ", "").replace(prefix, "");
1564
+ }
1565
+ }
1566
+ packageJson.scripts[scriptKey] = scriptValue;
1567
+ }
1568
+ }
1569
+ const newContent = JSON.stringify(packageJson, null, 2) + "\n";
1570
+ return {
1571
+ success: true,
1572
+ filePath: packageJsonPath,
1573
+ message: `Remove ${agent} server from dev script`,
1574
+ originalContent,
1575
+ newContent
1576
+ };
1577
+ } catch {
1578
+ return {
1579
+ success: false,
1580
+ filePath: packageJsonPath,
1581
+ message: "Failed to parse package.json"
1582
+ };
1583
+ }
1584
+ };
1340
1585
 
1341
1586
  // src/commands/add.ts
1342
- var VERSION = "0.0.90";
1587
+ var VERSION = "0.0.92";
1343
1588
  var AGENT_NAMES = {
1344
1589
  "claude-code": "Claude Code",
1345
1590
  cursor: "Cursor",
@@ -1376,7 +1621,7 @@ var add = new Command().name("add").description("add an agent integration").argu
1376
1621
  process.exit(1);
1377
1622
  }
1378
1623
  preflightSpinner.succeed();
1379
- const availableAgents = [
1624
+ const allAgents = [
1380
1625
  "claude-code",
1381
1626
  "cursor",
1382
1627
  "opencode",
@@ -1384,7 +1629,10 @@ var add = new Command().name("add").description("add an agent integration").argu
1384
1629
  "gemini",
1385
1630
  "amp",
1386
1631
  "visual-edit"
1387
- ].filter((agent) => !projectInfo.installedAgents.includes(agent));
1632
+ ];
1633
+ const availableAgents = allAgents.filter(
1634
+ (agent) => !projectInfo.installedAgents.includes(agent)
1635
+ );
1388
1636
  if (availableAgents.length === 0) {
1389
1637
  logger.break();
1390
1638
  logger.success("All agent integrations are already installed.");
@@ -1392,16 +1640,9 @@ var add = new Command().name("add").description("add an agent integration").argu
1392
1640
  process.exit(0);
1393
1641
  }
1394
1642
  let agentIntegration;
1643
+ let agentsToRemove = [];
1395
1644
  if (agentArg) {
1396
- if (![
1397
- "claude-code",
1398
- "cursor",
1399
- "opencode",
1400
- "codex",
1401
- "gemini",
1402
- "amp",
1403
- "visual-edit"
1404
- ].includes(agentArg)) {
1645
+ if (!allAgents.includes(agentArg)) {
1405
1646
  logger.break();
1406
1647
  logger.error(`Invalid agent: ${agentArg}`);
1407
1648
  logger.error(
@@ -1417,9 +1658,44 @@ var add = new Command().name("add").description("add an agent integration").argu
1417
1658
  process.exit(0);
1418
1659
  }
1419
1660
  agentIntegration = agentArg;
1661
+ if (projectInfo.installedAgents.length > 0 && !isNonInteractive) {
1662
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES[innerAgent] || innerAgent).join(", ");
1663
+ logger.break();
1664
+ logger.warn(`${installedNames} is already installed.`);
1665
+ const { action } = await prompts3({
1666
+ type: "select",
1667
+ name: "action",
1668
+ message: "How would you like to proceed?",
1669
+ choices: [
1670
+ {
1671
+ title: `Replace with ${AGENT_NAMES[agentIntegration]}`,
1672
+ value: "replace"
1673
+ },
1674
+ {
1675
+ title: `Add ${AGENT_NAMES[agentIntegration]} alongside existing`,
1676
+ value: "add"
1677
+ },
1678
+ { title: "Cancel", value: "cancel" }
1679
+ ]
1680
+ });
1681
+ if (!action || action === "cancel") {
1682
+ logger.break();
1683
+ logger.log("Changes cancelled.");
1684
+ logger.break();
1685
+ process.exit(0);
1686
+ }
1687
+ if (action === "replace") {
1688
+ agentsToRemove = [...projectInfo.installedAgents];
1689
+ }
1690
+ }
1420
1691
  } else if (!isNonInteractive) {
1421
1692
  logger.break();
1422
- const { agent } = await prompts2({
1693
+ if (projectInfo.installedAgents.length > 0) {
1694
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES[innerAgent] || innerAgent).join(", ");
1695
+ logger.warn(`Currently installed: ${installedNames}`);
1696
+ logger.break();
1697
+ }
1698
+ const { agent } = await prompts3({
1423
1699
  type: "select",
1424
1700
  name: "agent",
1425
1701
  message: `Which ${highlighter.info("agent integration")} would you like to add?`,
@@ -1433,6 +1709,34 @@ var add = new Command().name("add").description("add an agent integration").argu
1433
1709
  process.exit(1);
1434
1710
  }
1435
1711
  agentIntegration = agent;
1712
+ if (projectInfo.installedAgents.length > 0) {
1713
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES[innerAgent] || innerAgent).join(", ");
1714
+ const { action } = await prompts3({
1715
+ type: "select",
1716
+ name: "action",
1717
+ message: "How would you like to proceed?",
1718
+ choices: [
1719
+ {
1720
+ title: `Replace ${installedNames} with ${AGENT_NAMES[agentIntegration]}`,
1721
+ value: "replace"
1722
+ },
1723
+ {
1724
+ title: `Add ${AGENT_NAMES[agentIntegration]} alongside existing`,
1725
+ value: "add"
1726
+ },
1727
+ { title: "Cancel", value: "cancel" }
1728
+ ]
1729
+ });
1730
+ if (!action || action === "cancel") {
1731
+ logger.break();
1732
+ logger.log("Changes cancelled.");
1733
+ logger.break();
1734
+ process.exit(0);
1735
+ }
1736
+ if (action === "replace") {
1737
+ agentsToRemove = [...projectInfo.installedAgents];
1738
+ }
1739
+ }
1436
1740
  } else {
1437
1741
  logger.break();
1438
1742
  logger.error("Please specify an agent to add.");
@@ -1440,6 +1744,72 @@ var add = new Command().name("add").description("add an agent integration").argu
1440
1744
  logger.break();
1441
1745
  process.exit(1);
1442
1746
  }
1747
+ if (agentsToRemove.length > 0) {
1748
+ for (const agentToRemove of agentsToRemove) {
1749
+ const removalResult = previewAgentRemoval(
1750
+ projectInfo.projectRoot,
1751
+ projectInfo.framework,
1752
+ projectInfo.nextRouterType,
1753
+ agentToRemove
1754
+ );
1755
+ const removalPackageJsonResult = previewPackageJsonAgentRemoval(
1756
+ projectInfo.projectRoot,
1757
+ agentToRemove
1758
+ );
1759
+ const packagesToRemove = getPackagesToUninstall(agentToRemove);
1760
+ if (packagesToRemove.length > 0) {
1761
+ const uninstallSpinner = spinner(
1762
+ `Removing ${packagesToRemove.join(", ")}.`
1763
+ ).start();
1764
+ try {
1765
+ uninstallPackages(
1766
+ packagesToRemove,
1767
+ projectInfo.packageManager,
1768
+ projectInfo.projectRoot
1769
+ );
1770
+ uninstallSpinner.succeed();
1771
+ } catch (error) {
1772
+ uninstallSpinner.fail();
1773
+ handleError(error);
1774
+ }
1775
+ }
1776
+ if (removalResult.success && !removalResult.noChanges && removalResult.newContent) {
1777
+ const removeWriteSpinner = spinner(
1778
+ `Removing ${AGENT_NAMES[agentToRemove] || agentToRemove} from ${removalResult.filePath}.`
1779
+ ).start();
1780
+ const writeResult = applyTransform(removalResult);
1781
+ if (!writeResult.success) {
1782
+ removeWriteSpinner.fail();
1783
+ logger.break();
1784
+ logger.error(writeResult.error || "Failed to write file.");
1785
+ logger.break();
1786
+ process.exit(1);
1787
+ }
1788
+ removeWriteSpinner.succeed();
1789
+ }
1790
+ if (removalPackageJsonResult.success && !removalPackageJsonResult.noChanges && removalPackageJsonResult.newContent) {
1791
+ const removePackageJsonSpinner = spinner(
1792
+ `Removing ${AGENT_NAMES[agentToRemove] || agentToRemove} from ${removalPackageJsonResult.filePath}.`
1793
+ ).start();
1794
+ const packageJsonWriteResult = applyPackageJsonTransform(
1795
+ removalPackageJsonResult
1796
+ );
1797
+ if (!packageJsonWriteResult.success) {
1798
+ removePackageJsonSpinner.fail();
1799
+ logger.break();
1800
+ logger.error(
1801
+ packageJsonWriteResult.error || "Failed to write file."
1802
+ );
1803
+ logger.break();
1804
+ process.exit(1);
1805
+ }
1806
+ removePackageJsonSpinner.succeed();
1807
+ }
1808
+ }
1809
+ projectInfo.installedAgents = projectInfo.installedAgents.filter(
1810
+ (innerAgent) => !agentsToRemove.includes(innerAgent)
1811
+ );
1812
+ }
1443
1813
  const addingSpinner = spinner(
1444
1814
  `Adding ${AGENT_NAMES[agentIntegration]}.`
1445
1815
  ).start();
@@ -1454,7 +1824,8 @@ var add = new Command().name("add").description("add an agent integration").argu
1454
1824
  const packageJsonResult = previewPackageJsonTransform(
1455
1825
  projectInfo.projectRoot,
1456
1826
  agentIntegration,
1457
- projectInfo.installedAgents
1827
+ projectInfo.installedAgents,
1828
+ projectInfo.packageManager
1458
1829
  );
1459
1830
  if (!result.success) {
1460
1831
  logger.break();
@@ -1483,9 +1854,9 @@ var add = new Command().name("add").description("add an agent integration").argu
1483
1854
  packageJsonResult.newContent
1484
1855
  );
1485
1856
  }
1486
- if (!isNonInteractive) {
1857
+ if (!isNonInteractive && agentsToRemove.length === 0) {
1487
1858
  logger.break();
1488
- const { proceed } = await prompts2({
1859
+ const { proceed } = await prompts3({
1489
1860
  type: "confirm",
1490
1861
  name: "proceed",
1491
1862
  message: "Apply these changes?",
@@ -1558,7 +1929,7 @@ var add = new Command().name("add").description("add an agent integration").argu
1558
1929
  handleError(error);
1559
1930
  }
1560
1931
  });
1561
- var VERSION2 = "0.0.90";
1932
+ var VERSION2 = "0.0.92";
1562
1933
  var MODIFIER_KEY_NAMES = {
1563
1934
  metaKey: process.platform === "darwin" ? "\u2318 Command" : "\u229E Windows",
1564
1935
  ctrlKey: "Ctrl",
@@ -1612,7 +1983,7 @@ var configure = new Command().name("configure").alias("config").description("con
1612
1983
  logger.log(`Configure ${highlighter.info("React Grab")} options:`);
1613
1984
  logger.break();
1614
1985
  const collectedOptions = {};
1615
- const { wantActivationKey } = await prompts2({
1986
+ const { wantActivationKey } = await prompts3({
1616
1987
  type: "confirm",
1617
1988
  name: "wantActivationKey",
1618
1989
  message: `Configure ${highlighter.info("activation key")}?`,
@@ -1623,7 +1994,7 @@ var configure = new Command().name("configure").alias("config").description("con
1623
1994
  process.exit(1);
1624
1995
  }
1625
1996
  if (wantActivationKey) {
1626
- const { key } = await prompts2({
1997
+ const { key } = await prompts3({
1627
1998
  type: "text",
1628
1999
  name: "key",
1629
2000
  message: "Enter the activation key (e.g., g, k, space):",
@@ -1633,7 +2004,7 @@ var configure = new Command().name("configure").alias("config").description("con
1633
2004
  logger.break();
1634
2005
  process.exit(1);
1635
2006
  }
1636
- const { modifiers } = await prompts2({
2007
+ const { modifiers } = await prompts3({
1637
2008
  type: "multiselect",
1638
2009
  name: "modifiers",
1639
2010
  message: "Select modifier keys (space to select, enter to confirm):",
@@ -1664,7 +2035,7 @@ var configure = new Command().name("configure").alias("config").description("con
1664
2035
  ` Activation key: ${highlighter.info(formatActivationKey(collectedOptions.activationKey))}`
1665
2036
  );
1666
2037
  }
1667
- const { activationMode } = await prompts2({
2038
+ const { activationMode } = await prompts3({
1668
2039
  type: "select",
1669
2040
  name: "activationMode",
1670
2041
  message: `Select ${highlighter.info("activation mode")}:`,
@@ -1680,7 +2051,7 @@ var configure = new Command().name("configure").alias("config").description("con
1680
2051
  }
1681
2052
  collectedOptions.activationMode = activationMode;
1682
2053
  if (activationMode === "hold") {
1683
- const { keyHoldDuration } = await prompts2({
2054
+ const { keyHoldDuration } = await prompts3({
1684
2055
  type: "number",
1685
2056
  name: "keyHoldDuration",
1686
2057
  message: `Enter ${highlighter.info("key hold duration")} in milliseconds:`,
@@ -1694,7 +2065,7 @@ var configure = new Command().name("configure").alias("config").description("con
1694
2065
  }
1695
2066
  collectedOptions.keyHoldDuration = keyHoldDuration;
1696
2067
  }
1697
- const { allowActivationInsideInput } = await prompts2({
2068
+ const { allowActivationInsideInput } = await prompts3({
1698
2069
  type: "confirm",
1699
2070
  name: "allowActivationInsideInput",
1700
2071
  message: `Allow activation ${highlighter.info("inside input fields")}?`,
@@ -1705,7 +2076,7 @@ var configure = new Command().name("configure").alias("config").description("con
1705
2076
  process.exit(1);
1706
2077
  }
1707
2078
  collectedOptions.allowActivationInsideInput = allowActivationInsideInput;
1708
- const { maxContextLines } = await prompts2({
2079
+ const { maxContextLines } = await prompts3({
1709
2080
  type: "number",
1710
2081
  name: "maxContextLines",
1711
2082
  message: `Enter ${highlighter.info("max context lines")} to include:`,
@@ -1735,7 +2106,7 @@ var configure = new Command().name("configure").alias("config").description("con
1735
2106
  logger.break();
1736
2107
  printDiff(result.filePath, result.originalContent, result.newContent);
1737
2108
  logger.break();
1738
- const { proceed } = await prompts2({
2109
+ const { proceed } = await prompts3({
1739
2110
  type: "confirm",
1740
2111
  name: "proceed",
1741
2112
  message: "Apply these changes?",
@@ -1772,7 +2143,7 @@ var configure = new Command().name("configure").alias("config").description("con
1772
2143
  handleError(error);
1773
2144
  }
1774
2145
  });
1775
- var VERSION3 = "0.0.90";
2146
+ var VERSION3 = "0.0.92";
1776
2147
  var REPORT_URL = "https://react-grab.com/api/report-cli";
1777
2148
  var DOCS_URL = "https://github.com/aidenybai/react-grab";
1778
2149
  var reportToCli = async (type, config, error) => {
@@ -1810,6 +2181,34 @@ var UNSUPPORTED_FRAMEWORK_NAMES = {
1810
2181
  sveltekit: "SvelteKit",
1811
2182
  gatsby: "Gatsby"
1812
2183
  };
2184
+ var AGENT_NAMES2 = {
2185
+ "claude-code": "Claude Code",
2186
+ cursor: "Cursor",
2187
+ opencode: "OpenCode",
2188
+ codex: "Codex",
2189
+ gemini: "Gemini",
2190
+ amp: "Amp",
2191
+ ami: "Ami",
2192
+ "visual-edit": "Visual Edit"
2193
+ };
2194
+ var MODIFIER_KEY_NAMES2 = {
2195
+ metaKey: process.platform === "darwin" ? "\u2318 Command" : "\u229E Windows",
2196
+ ctrlKey: "Ctrl",
2197
+ shiftKey: "Shift",
2198
+ altKey: process.platform === "darwin" ? "\u2325 Option" : "Alt"
2199
+ };
2200
+ var formatActivationKey2 = (activationKey) => {
2201
+ if (!activationKey) return "Default (Option/Alt)";
2202
+ const parts = [];
2203
+ if (activationKey.metaKey)
2204
+ parts.push(process.platform === "darwin" ? "\u2318" : "Win");
2205
+ if (activationKey.ctrlKey) parts.push("Ctrl");
2206
+ if (activationKey.shiftKey) parts.push("Shift");
2207
+ if (activationKey.altKey)
2208
+ parts.push(process.platform === "darwin" ? "\u2325" : "Alt");
2209
+ if (activationKey.key) parts.push(activationKey.key.toUpperCase());
2210
+ return parts.length > 0 ? parts.join(" + ") : "Default (Option/Alt)";
2211
+ };
1813
2212
  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(
1814
2213
  "-a, --agent <agent>",
1815
2214
  "agent integration (claude-code, cursor, opencode, codex, gemini, amp, visual-edit)"
@@ -1829,11 +2228,514 @@ var init = new Command().name("init").description("initialize React Grab in your
1829
2228
  const projectInfo = await detectProject(cwd);
1830
2229
  if (projectInfo.hasReactGrab && !opts.force) {
1831
2230
  preflightSpinner.succeed();
2231
+ if (isNonInteractive) {
2232
+ logger.break();
2233
+ logger.warn("React Grab is already installed.");
2234
+ logger.log(
2235
+ `Use ${highlighter.info("--force")} to reconfigure, or remove ${highlighter.info("--yes")} for interactive mode.`
2236
+ );
2237
+ logger.break();
2238
+ process.exit(0);
2239
+ }
1832
2240
  logger.break();
1833
- logger.warn("React Grab is already installed.");
1834
- logger.log(
1835
- `Use ${highlighter.info("--force")} to reconfigure, or ${highlighter.info("npx grab@latest add")} to add an agent.`
2241
+ logger.success("React Grab is already installed.");
2242
+ logger.break();
2243
+ const allAgents = [
2244
+ "claude-code",
2245
+ "cursor",
2246
+ "opencode",
2247
+ "codex",
2248
+ "gemini",
2249
+ "amp",
2250
+ "visual-edit"
2251
+ ];
2252
+ const availableAgents = allAgents.filter(
2253
+ (agent) => !projectInfo.installedAgents.includes(agent)
1836
2254
  );
2255
+ if (projectInfo.installedAgents.length > 0) {
2256
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES2[innerAgent] || innerAgent).join(", ");
2257
+ logger.log(
2258
+ `Currently installed agents: ${highlighter.info(installedNames)}`
2259
+ );
2260
+ logger.break();
2261
+ }
2262
+ let didAddAgent = false;
2263
+ if (availableAgents.length > 0) {
2264
+ const { wantAddAgent } = await prompts3({
2265
+ type: "confirm",
2266
+ name: "wantAddAgent",
2267
+ message: `Would you like to add an ${highlighter.info("agent integration")}?`,
2268
+ initial: true
2269
+ });
2270
+ if (wantAddAgent === void 0) {
2271
+ logger.break();
2272
+ process.exit(1);
2273
+ }
2274
+ if (wantAddAgent) {
2275
+ const { agent } = await prompts3({
2276
+ type: "select",
2277
+ name: "agent",
2278
+ message: `Which ${highlighter.info("agent integration")} would you like to add?`,
2279
+ choices: availableAgents.map((innerAgent) => ({
2280
+ title: AGENT_NAMES2[innerAgent],
2281
+ value: innerAgent
2282
+ }))
2283
+ });
2284
+ if (agent === void 0) {
2285
+ logger.break();
2286
+ process.exit(1);
2287
+ }
2288
+ const agentIntegration2 = agent;
2289
+ let agentsToRemove2 = [];
2290
+ if (projectInfo.installedAgents.length > 0) {
2291
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES2[innerAgent] || innerAgent).join(", ");
2292
+ const { action } = await prompts3({
2293
+ type: "select",
2294
+ name: "action",
2295
+ message: "How would you like to proceed?",
2296
+ choices: [
2297
+ {
2298
+ title: `Replace ${installedNames} with ${AGENT_NAMES2[agentIntegration2]}`,
2299
+ value: "replace"
2300
+ },
2301
+ {
2302
+ title: `Add ${AGENT_NAMES2[agentIntegration2]} alongside existing`,
2303
+ value: "add"
2304
+ },
2305
+ { title: "Cancel", value: "cancel" }
2306
+ ]
2307
+ });
2308
+ if (!action || action === "cancel") {
2309
+ logger.break();
2310
+ logger.log("Agent addition cancelled.");
2311
+ } else {
2312
+ if (action === "replace") {
2313
+ agentsToRemove2 = [...projectInfo.installedAgents];
2314
+ }
2315
+ if (agentsToRemove2.length > 0) {
2316
+ for (const agentToRemove of agentsToRemove2) {
2317
+ const removalResult = previewAgentRemoval(
2318
+ projectInfo.projectRoot,
2319
+ projectInfo.framework,
2320
+ projectInfo.nextRouterType,
2321
+ agentToRemove
2322
+ );
2323
+ const removalPackageJsonResult = previewPackageJsonAgentRemoval(
2324
+ projectInfo.projectRoot,
2325
+ agentToRemove
2326
+ );
2327
+ const packagesToRemove = getPackagesToUninstall(agentToRemove);
2328
+ if (packagesToRemove.length > 0) {
2329
+ const uninstallSpinner = spinner(
2330
+ `Removing ${packagesToRemove.join(", ")}.`
2331
+ ).start();
2332
+ try {
2333
+ uninstallPackages(
2334
+ packagesToRemove,
2335
+ projectInfo.packageManager,
2336
+ projectInfo.projectRoot
2337
+ );
2338
+ uninstallSpinner.succeed();
2339
+ } catch (error) {
2340
+ uninstallSpinner.fail();
2341
+ handleError(error);
2342
+ }
2343
+ }
2344
+ if (removalResult.success && !removalResult.noChanges && removalResult.newContent) {
2345
+ const removeWriteSpinner = spinner(
2346
+ `Removing ${AGENT_NAMES2[agentToRemove] || agentToRemove} from ${removalResult.filePath}.`
2347
+ ).start();
2348
+ const writeResult = applyTransform(removalResult);
2349
+ if (!writeResult.success) {
2350
+ removeWriteSpinner.fail();
2351
+ logger.break();
2352
+ logger.error(
2353
+ writeResult.error || "Failed to write file."
2354
+ );
2355
+ logger.break();
2356
+ process.exit(1);
2357
+ }
2358
+ removeWriteSpinner.succeed();
2359
+ }
2360
+ if (removalPackageJsonResult.success && !removalPackageJsonResult.noChanges && removalPackageJsonResult.newContent) {
2361
+ const removePackageJsonSpinner = spinner(
2362
+ `Removing ${AGENT_NAMES2[agentToRemove] || agentToRemove} from ${removalPackageJsonResult.filePath}.`
2363
+ ).start();
2364
+ const packageJsonWriteResult = applyPackageJsonTransform(
2365
+ removalPackageJsonResult
2366
+ );
2367
+ if (!packageJsonWriteResult.success) {
2368
+ removePackageJsonSpinner.fail();
2369
+ logger.break();
2370
+ logger.error(
2371
+ packageJsonWriteResult.error || "Failed to write file."
2372
+ );
2373
+ logger.break();
2374
+ process.exit(1);
2375
+ }
2376
+ removePackageJsonSpinner.succeed();
2377
+ }
2378
+ }
2379
+ projectInfo.installedAgents = projectInfo.installedAgents.filter(
2380
+ (innerAgent) => !agentsToRemove2.includes(innerAgent)
2381
+ );
2382
+ }
2383
+ const result2 = previewTransform(
2384
+ projectInfo.projectRoot,
2385
+ projectInfo.framework,
2386
+ projectInfo.nextRouterType,
2387
+ agentIntegration2,
2388
+ true
2389
+ );
2390
+ const packageJsonResult2 = previewPackageJsonTransform(
2391
+ projectInfo.projectRoot,
2392
+ agentIntegration2,
2393
+ projectInfo.installedAgents,
2394
+ projectInfo.packageManager
2395
+ );
2396
+ if (!result2.success) {
2397
+ logger.break();
2398
+ logger.error(result2.message);
2399
+ logger.break();
2400
+ process.exit(1);
2401
+ }
2402
+ const hasLayoutChanges2 = !result2.noChanges && result2.originalContent && result2.newContent;
2403
+ const hasPackageJsonChanges2 = packageJsonResult2.success && !packageJsonResult2.noChanges && packageJsonResult2.originalContent && packageJsonResult2.newContent;
2404
+ if (hasLayoutChanges2 || hasPackageJsonChanges2) {
2405
+ logger.break();
2406
+ if (hasLayoutChanges2) {
2407
+ printDiff(
2408
+ result2.filePath,
2409
+ result2.originalContent,
2410
+ result2.newContent
2411
+ );
2412
+ }
2413
+ if (hasPackageJsonChanges2) {
2414
+ if (hasLayoutChanges2) {
2415
+ logger.break();
2416
+ }
2417
+ printDiff(
2418
+ packageJsonResult2.filePath,
2419
+ packageJsonResult2.originalContent,
2420
+ packageJsonResult2.newContent
2421
+ );
2422
+ }
2423
+ if (agentsToRemove2.length === 0) {
2424
+ logger.break();
2425
+ const { proceed } = await prompts3({
2426
+ type: "confirm",
2427
+ name: "proceed",
2428
+ message: "Apply these changes?",
2429
+ initial: true
2430
+ });
2431
+ if (!proceed) {
2432
+ logger.break();
2433
+ logger.log("Agent addition cancelled.");
2434
+ } else {
2435
+ const packages = getPackagesToInstall(
2436
+ agentIntegration2,
2437
+ false
2438
+ );
2439
+ if (packages.length > 0) {
2440
+ const installSpinner = spinner(
2441
+ `Installing ${packages.join(", ")}.`
2442
+ ).start();
2443
+ try {
2444
+ installPackages(
2445
+ packages,
2446
+ projectInfo.packageManager,
2447
+ projectInfo.projectRoot
2448
+ );
2449
+ installSpinner.succeed();
2450
+ } catch (error) {
2451
+ installSpinner.fail();
2452
+ handleError(error);
2453
+ }
2454
+ }
2455
+ if (hasLayoutChanges2) {
2456
+ const writeSpinner = spinner(
2457
+ `Applying changes to ${result2.filePath}.`
2458
+ ).start();
2459
+ const writeResult = applyTransform(result2);
2460
+ if (!writeResult.success) {
2461
+ writeSpinner.fail();
2462
+ logger.break();
2463
+ logger.error(
2464
+ writeResult.error || "Failed to write file."
2465
+ );
2466
+ logger.break();
2467
+ process.exit(1);
2468
+ }
2469
+ writeSpinner.succeed();
2470
+ }
2471
+ if (hasPackageJsonChanges2) {
2472
+ const packageJsonSpinner = spinner(
2473
+ `Applying changes to ${packageJsonResult2.filePath}.`
2474
+ ).start();
2475
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult2);
2476
+ if (!packageJsonWriteResult.success) {
2477
+ packageJsonSpinner.fail();
2478
+ logger.break();
2479
+ logger.error(
2480
+ packageJsonWriteResult.error || "Failed to write file."
2481
+ );
2482
+ logger.break();
2483
+ process.exit(1);
2484
+ }
2485
+ packageJsonSpinner.succeed();
2486
+ }
2487
+ didAddAgent = true;
2488
+ logger.break();
2489
+ logger.success(
2490
+ `${AGENT_NAMES2[agentIntegration2]} has been added.`
2491
+ );
2492
+ }
2493
+ } else {
2494
+ const packages = getPackagesToInstall(
2495
+ agentIntegration2,
2496
+ false
2497
+ );
2498
+ if (packages.length > 0) {
2499
+ const installSpinner = spinner(
2500
+ `Installing ${packages.join(", ")}.`
2501
+ ).start();
2502
+ try {
2503
+ installPackages(
2504
+ packages,
2505
+ projectInfo.packageManager,
2506
+ projectInfo.projectRoot
2507
+ );
2508
+ installSpinner.succeed();
2509
+ } catch (error) {
2510
+ installSpinner.fail();
2511
+ handleError(error);
2512
+ }
2513
+ }
2514
+ if (hasLayoutChanges2) {
2515
+ const writeSpinner = spinner(
2516
+ `Applying changes to ${result2.filePath}.`
2517
+ ).start();
2518
+ const writeResult = applyTransform(result2);
2519
+ if (!writeResult.success) {
2520
+ writeSpinner.fail();
2521
+ logger.break();
2522
+ logger.error(
2523
+ writeResult.error || "Failed to write file."
2524
+ );
2525
+ logger.break();
2526
+ process.exit(1);
2527
+ }
2528
+ writeSpinner.succeed();
2529
+ }
2530
+ if (hasPackageJsonChanges2) {
2531
+ const packageJsonSpinner = spinner(
2532
+ `Applying changes to ${packageJsonResult2.filePath}.`
2533
+ ).start();
2534
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult2);
2535
+ if (!packageJsonWriteResult.success) {
2536
+ packageJsonSpinner.fail();
2537
+ logger.break();
2538
+ logger.error(
2539
+ packageJsonWriteResult.error || "Failed to write file."
2540
+ );
2541
+ logger.break();
2542
+ process.exit(1);
2543
+ }
2544
+ packageJsonSpinner.succeed();
2545
+ }
2546
+ didAddAgent = true;
2547
+ logger.break();
2548
+ logger.success(
2549
+ `${AGENT_NAMES2[agentIntegration2]} has been added.`
2550
+ );
2551
+ }
2552
+ }
2553
+ }
2554
+ }
2555
+ }
2556
+ } else {
2557
+ logger.log("All agent integrations are already installed.");
2558
+ }
2559
+ logger.break();
2560
+ const { wantCustomizeOptions } = await prompts3({
2561
+ type: "confirm",
2562
+ name: "wantCustomizeOptions",
2563
+ message: `Would you like to customize ${highlighter.info("options")}?`,
2564
+ initial: false
2565
+ });
2566
+ if (wantCustomizeOptions === void 0) {
2567
+ logger.break();
2568
+ process.exit(1);
2569
+ }
2570
+ if (wantCustomizeOptions) {
2571
+ logger.break();
2572
+ logger.log(`Configure ${highlighter.info("React Grab")} options:`);
2573
+ logger.break();
2574
+ const collectedOptions = {};
2575
+ const { wantActivationKey } = await prompts3({
2576
+ type: "confirm",
2577
+ name: "wantActivationKey",
2578
+ message: `Configure ${highlighter.info("activation key")}?`,
2579
+ initial: false
2580
+ });
2581
+ if (wantActivationKey === void 0) {
2582
+ logger.break();
2583
+ process.exit(1);
2584
+ }
2585
+ if (wantActivationKey) {
2586
+ const { key } = await prompts3({
2587
+ type: "text",
2588
+ name: "key",
2589
+ message: "Enter the activation key (e.g., g, k, space):",
2590
+ initial: ""
2591
+ });
2592
+ if (key === void 0) {
2593
+ logger.break();
2594
+ process.exit(1);
2595
+ }
2596
+ const { modifiers } = await prompts3({
2597
+ type: "multiselect",
2598
+ name: "modifiers",
2599
+ message: "Select modifier keys (space to select, enter to confirm):",
2600
+ choices: [
2601
+ { title: MODIFIER_KEY_NAMES2.metaKey, value: "metaKey" },
2602
+ { title: MODIFIER_KEY_NAMES2.ctrlKey, value: "ctrlKey" },
2603
+ { title: MODIFIER_KEY_NAMES2.shiftKey, value: "shiftKey" },
2604
+ {
2605
+ title: MODIFIER_KEY_NAMES2.altKey,
2606
+ value: "altKey",
2607
+ selected: true
2608
+ }
2609
+ ],
2610
+ hint: "- Space to select, Enter to confirm"
2611
+ });
2612
+ if (modifiers === void 0) {
2613
+ logger.break();
2614
+ process.exit(1);
2615
+ }
2616
+ collectedOptions.activationKey = {
2617
+ ...key && { key: key.toLowerCase() },
2618
+ ...modifiers.includes("metaKey") && { metaKey: true },
2619
+ ...modifiers.includes("ctrlKey") && { ctrlKey: true },
2620
+ ...modifiers.includes("shiftKey") && { shiftKey: true },
2621
+ ...modifiers.includes("altKey") && { altKey: true }
2622
+ };
2623
+ logger.log(
2624
+ ` Activation key: ${highlighter.info(formatActivationKey2(collectedOptions.activationKey))}`
2625
+ );
2626
+ }
2627
+ const { activationMode } = await prompts3({
2628
+ type: "select",
2629
+ name: "activationMode",
2630
+ message: `Select ${highlighter.info("activation mode")}:`,
2631
+ choices: [
2632
+ {
2633
+ title: "Toggle (press to activate/deactivate)",
2634
+ value: "toggle"
2635
+ },
2636
+ { title: "Hold (hold key to keep active)", value: "hold" }
2637
+ ],
2638
+ initial: 0
2639
+ });
2640
+ if (activationMode === void 0) {
2641
+ logger.break();
2642
+ process.exit(1);
2643
+ }
2644
+ collectedOptions.activationMode = activationMode;
2645
+ if (activationMode === "hold") {
2646
+ const { keyHoldDuration } = await prompts3({
2647
+ type: "number",
2648
+ name: "keyHoldDuration",
2649
+ message: `Enter ${highlighter.info("key hold duration")} in milliseconds:`,
2650
+ initial: 150,
2651
+ min: 0,
2652
+ max: 2e3
2653
+ });
2654
+ if (keyHoldDuration === void 0) {
2655
+ logger.break();
2656
+ process.exit(1);
2657
+ }
2658
+ collectedOptions.keyHoldDuration = keyHoldDuration;
2659
+ }
2660
+ const { allowActivationInsideInput } = await prompts3({
2661
+ type: "confirm",
2662
+ name: "allowActivationInsideInput",
2663
+ message: `Allow activation ${highlighter.info("inside input fields")}?`,
2664
+ initial: true
2665
+ });
2666
+ if (allowActivationInsideInput === void 0) {
2667
+ logger.break();
2668
+ process.exit(1);
2669
+ }
2670
+ collectedOptions.allowActivationInsideInput = allowActivationInsideInput;
2671
+ const { maxContextLines } = await prompts3({
2672
+ type: "number",
2673
+ name: "maxContextLines",
2674
+ message: `Enter ${highlighter.info("max context lines")} to include:`,
2675
+ initial: 3,
2676
+ min: 0,
2677
+ max: 50
2678
+ });
2679
+ if (maxContextLines === void 0) {
2680
+ logger.break();
2681
+ process.exit(1);
2682
+ }
2683
+ collectedOptions.maxContextLines = maxContextLines;
2684
+ const optionsResult = previewOptionsTransform(
2685
+ projectInfo.projectRoot,
2686
+ projectInfo.framework,
2687
+ projectInfo.nextRouterType,
2688
+ collectedOptions
2689
+ );
2690
+ if (!optionsResult.success) {
2691
+ logger.break();
2692
+ logger.error(optionsResult.message);
2693
+ logger.break();
2694
+ process.exit(1);
2695
+ }
2696
+ const hasOptionsChanges = !optionsResult.noChanges && optionsResult.originalContent && optionsResult.newContent;
2697
+ if (hasOptionsChanges) {
2698
+ logger.break();
2699
+ printDiff(
2700
+ optionsResult.filePath,
2701
+ optionsResult.originalContent,
2702
+ optionsResult.newContent
2703
+ );
2704
+ logger.break();
2705
+ const { proceed } = await prompts3({
2706
+ type: "confirm",
2707
+ name: "proceed",
2708
+ message: "Apply these changes?",
2709
+ initial: true
2710
+ });
2711
+ if (!proceed) {
2712
+ logger.break();
2713
+ logger.log("Options configuration cancelled.");
2714
+ } else {
2715
+ const writeSpinner = spinner(
2716
+ `Applying changes to ${optionsResult.filePath}.`
2717
+ ).start();
2718
+ const writeResult = applyOptionsTransform(optionsResult);
2719
+ if (!writeResult.success) {
2720
+ writeSpinner.fail();
2721
+ logger.break();
2722
+ logger.error(writeResult.error || "Failed to write file.");
2723
+ logger.break();
2724
+ process.exit(1);
2725
+ }
2726
+ writeSpinner.succeed();
2727
+ logger.break();
2728
+ logger.success("React Grab options have been configured.");
2729
+ }
2730
+ } else {
2731
+ logger.break();
2732
+ logger.log("No option changes needed.");
2733
+ }
2734
+ }
2735
+ if (!didAddAgent && !wantCustomizeOptions) {
2736
+ logger.break();
2737
+ logger.log("No changes made.");
2738
+ }
1837
2739
  logger.break();
1838
2740
  process.exit(0);
1839
2741
  }
@@ -1868,7 +2770,7 @@ var init = new Command().name("init").description("initialize React Grab in your
1868
2770
  return 0;
1869
2771
  }
1870
2772
  );
1871
- const { selectedProject } = await prompts2({
2773
+ const { selectedProject } = await prompts3({
1872
2774
  type: "select",
1873
2775
  name: "selectedProject",
1874
2776
  message: "Select a project to install React Grab:",
@@ -1929,9 +2831,15 @@ var init = new Command().name("init").description("initialize React Grab in your
1929
2831
  let finalPackageManager = projectInfo.packageManager;
1930
2832
  let finalNextRouterType = projectInfo.nextRouterType;
1931
2833
  let agentIntegration = opts.agent || "none";
2834
+ let agentsToRemove = [];
1932
2835
  if (!isNonInteractive && !opts.agent) {
1933
2836
  logger.break();
1934
- const { agent } = await prompts2({
2837
+ if (opts.force && projectInfo.installedAgents.length > 0) {
2838
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES2[innerAgent] || innerAgent).join(", ");
2839
+ logger.warn(`Currently installed: ${installedNames}`);
2840
+ logger.break();
2841
+ }
2842
+ const { agent } = await prompts3({
1935
2843
  type: "select",
1936
2844
  name: "agent",
1937
2845
  message: `Would you like to add an ${highlighter.info("agent integration")}?`,
@@ -1951,6 +2859,63 @@ var init = new Command().name("init").description("initialize React Grab in your
1951
2859
  process.exit(1);
1952
2860
  }
1953
2861
  agentIntegration = agent;
2862
+ if (opts.force && projectInfo.installedAgents.length > 0 && agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration)) {
2863
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES2[innerAgent] || innerAgent).join(", ");
2864
+ const { action } = await prompts3({
2865
+ type: "select",
2866
+ name: "action",
2867
+ message: "How would you like to proceed?",
2868
+ choices: [
2869
+ {
2870
+ title: `Replace ${installedNames} with ${AGENT_NAMES2[agentIntegration]}`,
2871
+ value: "replace"
2872
+ },
2873
+ {
2874
+ title: `Add ${AGENT_NAMES2[agentIntegration]} alongside existing`,
2875
+ value: "add"
2876
+ },
2877
+ { title: "Cancel", value: "cancel" }
2878
+ ]
2879
+ });
2880
+ if (!action || action === "cancel") {
2881
+ logger.break();
2882
+ logger.log("Changes cancelled.");
2883
+ logger.break();
2884
+ process.exit(0);
2885
+ }
2886
+ if (action === "replace") {
2887
+ agentsToRemove = [...projectInfo.installedAgents];
2888
+ }
2889
+ }
2890
+ } else if (opts.agent && opts.force && projectInfo.installedAgents.length > 0 && !projectInfo.installedAgents.includes(opts.agent) && !isNonInteractive) {
2891
+ const installedNames = projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES2[innerAgent] || innerAgent).join(", ");
2892
+ logger.break();
2893
+ logger.warn(`Currently installed: ${installedNames}`);
2894
+ const { action } = await prompts3({
2895
+ type: "select",
2896
+ name: "action",
2897
+ message: "How would you like to proceed?",
2898
+ choices: [
2899
+ {
2900
+ title: `Replace ${installedNames} with ${AGENT_NAMES2[agentIntegration]}`,
2901
+ value: "replace"
2902
+ },
2903
+ {
2904
+ title: `Add ${AGENT_NAMES2[agentIntegration]} alongside existing`,
2905
+ value: "add"
2906
+ },
2907
+ { title: "Cancel", value: "cancel" }
2908
+ ]
2909
+ });
2910
+ if (!action || action === "cancel") {
2911
+ logger.break();
2912
+ logger.log("Changes cancelled.");
2913
+ logger.break();
2914
+ process.exit(0);
2915
+ }
2916
+ if (action === "replace") {
2917
+ agentsToRemove = [...projectInfo.installedAgents];
2918
+ }
1954
2919
  }
1955
2920
  const result = previewTransform(
1956
2921
  projectInfo.projectRoot,
@@ -1962,7 +2927,8 @@ var init = new Command().name("init").description("initialize React Grab in your
1962
2927
  const packageJsonResult = previewPackageJsonTransform(
1963
2928
  projectInfo.projectRoot,
1964
2929
  agentIntegration,
1965
- projectInfo.installedAgents
2930
+ projectInfo.installedAgents,
2931
+ finalPackageManager
1966
2932
  );
1967
2933
  if (!result.success) {
1968
2934
  logger.break();
@@ -1997,7 +2963,7 @@ var init = new Command().name("init").description("initialize React Grab in your
1997
2963
  logger.warn("Please verify the changes before committing.");
1998
2964
  if (!isNonInteractive) {
1999
2965
  logger.break();
2000
- const { proceed } = await prompts2({
2966
+ const { proceed } = await prompts3({
2001
2967
  type: "confirm",
2002
2968
  name: "proceed",
2003
2969
  message: "Apply these changes?",
@@ -2011,6 +2977,72 @@ var init = new Command().name("init").description("initialize React Grab in your
2011
2977
  }
2012
2978
  }
2013
2979
  }
2980
+ if (agentsToRemove.length > 0) {
2981
+ for (const agentToRemove of agentsToRemove) {
2982
+ const removalResult = previewAgentRemoval(
2983
+ projectInfo.projectRoot,
2984
+ projectInfo.framework,
2985
+ projectInfo.nextRouterType,
2986
+ agentToRemove
2987
+ );
2988
+ const removalPackageJsonResult = previewPackageJsonAgentRemoval(
2989
+ projectInfo.projectRoot,
2990
+ agentToRemove
2991
+ );
2992
+ const packagesToRemove = getPackagesToUninstall(agentToRemove);
2993
+ if (packagesToRemove.length > 0 && !opts.skipInstall) {
2994
+ const uninstallSpinner = spinner(
2995
+ `Removing ${packagesToRemove.join(", ")}.`
2996
+ ).start();
2997
+ try {
2998
+ uninstallPackages(
2999
+ packagesToRemove,
3000
+ finalPackageManager,
3001
+ projectInfo.projectRoot
3002
+ );
3003
+ uninstallSpinner.succeed();
3004
+ } catch (error) {
3005
+ uninstallSpinner.fail();
3006
+ handleError(error);
3007
+ }
3008
+ }
3009
+ if (removalResult.success && !removalResult.noChanges && removalResult.newContent) {
3010
+ const removeWriteSpinner = spinner(
3011
+ `Removing ${AGENT_NAMES2[agentToRemove] || agentToRemove} from ${removalResult.filePath}.`
3012
+ ).start();
3013
+ const writeResult = applyTransform(removalResult);
3014
+ if (!writeResult.success) {
3015
+ removeWriteSpinner.fail();
3016
+ logger.break();
3017
+ logger.error(writeResult.error || "Failed to write file.");
3018
+ logger.break();
3019
+ process.exit(1);
3020
+ }
3021
+ removeWriteSpinner.succeed();
3022
+ }
3023
+ if (removalPackageJsonResult.success && !removalPackageJsonResult.noChanges && removalPackageJsonResult.newContent) {
3024
+ const removePackageJsonSpinner = spinner(
3025
+ `Removing ${AGENT_NAMES2[agentToRemove] || agentToRemove} from ${removalPackageJsonResult.filePath}.`
3026
+ ).start();
3027
+ const packageJsonWriteResult = applyPackageJsonTransform(
3028
+ removalPackageJsonResult
3029
+ );
3030
+ if (!packageJsonWriteResult.success) {
3031
+ removePackageJsonSpinner.fail();
3032
+ logger.break();
3033
+ logger.error(
3034
+ packageJsonWriteResult.error || "Failed to write file."
3035
+ );
3036
+ logger.break();
3037
+ process.exit(1);
3038
+ }
3039
+ removePackageJsonSpinner.succeed();
3040
+ }
3041
+ }
3042
+ projectInfo.installedAgents = projectInfo.installedAgents.filter(
3043
+ (innerAgent) => !agentsToRemove.includes(innerAgent)
3044
+ );
3045
+ }
2014
3046
  const shouldInstallReactGrab = !projectInfo.hasReactGrab;
2015
3047
  const shouldInstallAgent = agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration);
2016
3048
  if (!opts.skipInstall && (shouldInstallReactGrab || shouldInstallAgent)) {
@@ -2068,7 +3100,9 @@ var init = new Command().name("init").description("initialize React Grab in your
2068
3100
  `${highlighter.success("Success!")} React Grab has been installed.`
2069
3101
  );
2070
3102
  if (packageJsonResult.warning) {
3103
+ logger.break();
2071
3104
  logger.warn(packageJsonResult.warning);
3105
+ logger.break();
2072
3106
  } else {
2073
3107
  logger.log("You may now start your development server.");
2074
3108
  }
@@ -2085,7 +3119,194 @@ var init = new Command().name("init").description("initialize React Grab in your
2085
3119
  await reportToCli("error", void 0, error);
2086
3120
  }
2087
3121
  });
2088
- var VERSION4 = "0.0.90";
3122
+ var VERSION4 = "0.0.92";
3123
+ var AGENT_NAMES3 = {
3124
+ "claude-code": "Claude Code",
3125
+ cursor: "Cursor",
3126
+ opencode: "OpenCode",
3127
+ codex: "Codex",
3128
+ gemini: "Gemini",
3129
+ amp: "Amp",
3130
+ ami: "Ami",
3131
+ "visual-edit": "Visual Edit"
3132
+ };
3133
+ var remove = new Command().name("remove").description("remove an agent integration").argument(
3134
+ "[agent]",
3135
+ "agent to remove (claude-code, cursor, opencode, codex, gemini, amp, ami, visual-edit)"
3136
+ ).option("-y, --yes", "skip confirmation prompts", false).option(
3137
+ "-c, --cwd <cwd>",
3138
+ "working directory (defaults to current directory)",
3139
+ process.cwd()
3140
+ ).action(async (agentArg, opts) => {
3141
+ console.log(
3142
+ `${pc.magenta("\u273F")} ${pc.bold("React Grab")} ${pc.gray(VERSION4)}`
3143
+ );
3144
+ console.log();
3145
+ try {
3146
+ const cwd = opts.cwd;
3147
+ const isNonInteractive = opts.yes;
3148
+ const preflightSpinner = spinner("Preflight checks.").start();
3149
+ const projectInfo = await detectProject(cwd);
3150
+ if (!projectInfo.hasReactGrab) {
3151
+ preflightSpinner.fail("React Grab is not installed.");
3152
+ logger.break();
3153
+ logger.error(
3154
+ `Run ${highlighter.info("react-grab init")} first to install React Grab.`
3155
+ );
3156
+ logger.break();
3157
+ process.exit(1);
3158
+ }
3159
+ if (projectInfo.installedAgents.length === 0) {
3160
+ preflightSpinner.succeed();
3161
+ logger.break();
3162
+ logger.warn("No agent integrations are installed.");
3163
+ logger.break();
3164
+ process.exit(0);
3165
+ }
3166
+ preflightSpinner.succeed();
3167
+ let agentToRemove;
3168
+ if (agentArg) {
3169
+ if (!projectInfo.installedAgents.includes(agentArg)) {
3170
+ logger.break();
3171
+ logger.error(`Agent ${highlighter.info(agentArg)} is not installed.`);
3172
+ logger.log(
3173
+ `Installed agents: ${projectInfo.installedAgents.map((innerAgent) => AGENT_NAMES3[innerAgent] || innerAgent).join(", ")}`
3174
+ );
3175
+ logger.break();
3176
+ process.exit(1);
3177
+ }
3178
+ agentToRemove = agentArg;
3179
+ } else if (!isNonInteractive) {
3180
+ logger.break();
3181
+ const { agent } = await prompts3({
3182
+ type: "select",
3183
+ name: "agent",
3184
+ message: `Which ${highlighter.info("agent integration")} would you like to remove?`,
3185
+ choices: projectInfo.installedAgents.map((innerAgent) => ({
3186
+ title: AGENT_NAMES3[innerAgent] || innerAgent,
3187
+ value: innerAgent
3188
+ }))
3189
+ });
3190
+ if (!agent) {
3191
+ logger.break();
3192
+ process.exit(1);
3193
+ }
3194
+ agentToRemove = agent;
3195
+ } else {
3196
+ logger.break();
3197
+ logger.error("Please specify an agent to remove.");
3198
+ logger.error(
3199
+ "Installed agents: " + projectInfo.installedAgents.join(", ")
3200
+ );
3201
+ logger.break();
3202
+ process.exit(1);
3203
+ }
3204
+ const removingSpinner = spinner(
3205
+ `Preparing to remove ${AGENT_NAMES3[agentToRemove] || agentToRemove}.`
3206
+ ).start();
3207
+ removingSpinner.succeed();
3208
+ const result = previewAgentRemoval(
3209
+ projectInfo.projectRoot,
3210
+ projectInfo.framework,
3211
+ projectInfo.nextRouterType,
3212
+ agentToRemove
3213
+ );
3214
+ const packageJsonResult = previewPackageJsonAgentRemoval(
3215
+ projectInfo.projectRoot,
3216
+ agentToRemove
3217
+ );
3218
+ const hasLayoutChanges = result.success && !result.noChanges && result.originalContent && result.newContent;
3219
+ const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
3220
+ if (hasLayoutChanges || hasPackageJsonChanges) {
3221
+ logger.break();
3222
+ if (hasLayoutChanges) {
3223
+ printDiff(
3224
+ result.filePath,
3225
+ result.originalContent,
3226
+ result.newContent
3227
+ );
3228
+ }
3229
+ if (hasPackageJsonChanges) {
3230
+ if (hasLayoutChanges) {
3231
+ logger.break();
3232
+ }
3233
+ printDiff(
3234
+ packageJsonResult.filePath,
3235
+ packageJsonResult.originalContent,
3236
+ packageJsonResult.newContent
3237
+ );
3238
+ }
3239
+ if (!isNonInteractive) {
3240
+ logger.break();
3241
+ const { proceed } = await prompts3({
3242
+ type: "confirm",
3243
+ name: "proceed",
3244
+ message: "Apply these changes?",
3245
+ initial: true
3246
+ });
3247
+ if (!proceed) {
3248
+ logger.break();
3249
+ logger.log("Changes cancelled.");
3250
+ logger.break();
3251
+ process.exit(0);
3252
+ }
3253
+ }
3254
+ }
3255
+ const packages = getPackagesToUninstall(agentToRemove);
3256
+ if (packages.length > 0) {
3257
+ const uninstallSpinner = spinner(
3258
+ `Removing ${packages.join(", ")}.`
3259
+ ).start();
3260
+ try {
3261
+ uninstallPackages(
3262
+ packages,
3263
+ projectInfo.packageManager,
3264
+ projectInfo.projectRoot
3265
+ );
3266
+ uninstallSpinner.succeed();
3267
+ } catch (error) {
3268
+ uninstallSpinner.fail();
3269
+ handleError(error);
3270
+ }
3271
+ }
3272
+ if (hasLayoutChanges) {
3273
+ const writeSpinner = spinner(
3274
+ `Applying changes to ${result.filePath}.`
3275
+ ).start();
3276
+ const writeResult = applyTransform(result);
3277
+ if (!writeResult.success) {
3278
+ writeSpinner.fail();
3279
+ logger.break();
3280
+ logger.error(writeResult.error || "Failed to write file.");
3281
+ logger.break();
3282
+ process.exit(1);
3283
+ }
3284
+ writeSpinner.succeed();
3285
+ }
3286
+ if (hasPackageJsonChanges) {
3287
+ const packageJsonSpinner = spinner(
3288
+ `Applying changes to ${packageJsonResult.filePath}.`
3289
+ ).start();
3290
+ const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
3291
+ if (!packageJsonWriteResult.success) {
3292
+ packageJsonSpinner.fail();
3293
+ logger.break();
3294
+ logger.error(packageJsonWriteResult.error || "Failed to write file.");
3295
+ logger.break();
3296
+ process.exit(1);
3297
+ }
3298
+ packageJsonSpinner.succeed();
3299
+ }
3300
+ logger.break();
3301
+ logger.log(
3302
+ `${highlighter.success("Success!")} ${AGENT_NAMES3[agentToRemove] || agentToRemove} has been removed.`
3303
+ );
3304
+ logger.break();
3305
+ } catch (error) {
3306
+ handleError(error);
3307
+ }
3308
+ });
3309
+ var VERSION5 = "0.0.92";
2089
3310
  var DEFAULT_PROXY_PORT = 2e3;
2090
3311
  var REACT_GRAB_SCRIPT = '<script src="https://unpkg.com/react-grab/dist/index.global.js"></script>';
2091
3312
  var buildProviderScript = (provider) => `<script src="https://unpkg.com/${provider}/dist/client.global.js"></script>`;
@@ -2130,13 +3351,13 @@ var start = new Command().name("start").alias("proxy").description("start a prox
2130
3351
  "provider package to run via npx (e.g., @react-grab/cursor)"
2131
3352
  ).action(async (urlArg, opts) => {
2132
3353
  console.log(
2133
- `${pc.magenta("\u273F")} ${pc.bold("React Grab")} ${pc.gray(VERSION4)}`
3354
+ `${pc.magenta("\u273F")} ${pc.bold("React Grab")} ${pc.gray(VERSION5)}`
2134
3355
  );
2135
3356
  console.log();
2136
3357
  let url = urlArg;
2137
3358
  let provider = opts.provider;
2138
3359
  if (!url) {
2139
- const { targetUrl: promptedUrl } = await prompts2({
3360
+ const { targetUrl: promptedUrl } = await prompts3({
2140
3361
  type: "text",
2141
3362
  name: "targetUrl",
2142
3363
  message: "Enter the target URL to proxy:",
@@ -2148,7 +3369,7 @@ var start = new Command().name("start").alias("proxy").description("start a prox
2148
3369
  }
2149
3370
  url = promptedUrl;
2150
3371
  if (!provider) {
2151
- const { selectedProvider } = await prompts2({
3372
+ const { selectedProvider } = await prompts3({
2152
3373
  type: "select",
2153
3374
  name: "selectedProvider",
2154
3375
  message: `Select a ${highlighter.info("provider")} to use:`,
@@ -2315,7 +3536,7 @@ var start = new Command().name("start").alias("proxy").description("start a prox
2315
3536
 
2316
3537
  // src/cli.ts
2317
3538
  process.noDeprecation = true;
2318
- var VERSION5 = "0.0.90";
3539
+ var VERSION6 = "0.0.92";
2319
3540
  var VERSION_API_URL = "https://www.react-grab.com/api/version";
2320
3541
  process.on("SIGINT", () => process.exit(0));
2321
3542
  process.on("SIGTERM", () => process.exit(0));
@@ -2324,9 +3545,10 @@ try {
2324
3545
  });
2325
3546
  } catch {
2326
3547
  }
2327
- var program = new Command().name("react-grab").description("add React Grab to your project").version(VERSION5, "-v, --version", "display the version number");
3548
+ var program = new Command().name("react-grab").description("add React Grab to your project").version(VERSION6, "-v, --version", "display the version number");
2328
3549
  program.addCommand(init);
2329
3550
  program.addCommand(add);
3551
+ program.addCommand(remove);
2330
3552
  program.addCommand(configure);
2331
3553
  program.addCommand(start);
2332
3554
  program.parse();