@react-grab/cli 0.1.38 → 0.1.39

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.
package/dist/cli.cjs CHANGED
@@ -393,6 +393,45 @@ const handleError = (error) => {
393
393
  process.exit(1);
394
394
  };
395
395
  //#endregion
396
+ //#region src/utils/detect-agents.ts
397
+ const PATH_BINARIES = {
398
+ "claude-code": ["claude"],
399
+ codex: ["codex"],
400
+ cursor: ["cursor", "cursor-agent"],
401
+ droid: ["droid"],
402
+ "gemini-cli": ["gemini"],
403
+ "github-copilot": ["copilot"],
404
+ opencode: ["opencode"],
405
+ pi: ["pi", "omegon"]
406
+ };
407
+ const isCommandAvailable = (command) => {
408
+ const pathDirectories = (process.env.PATH ?? "").split(node_path.delimiter).filter(Boolean);
409
+ for (const directory of pathDirectories) {
410
+ const binaryPath = (0, node_path.join)(directory, command);
411
+ try {
412
+ if ((0, node_fs.statSync)(binaryPath).isFile()) {
413
+ (0, node_fs.accessSync)(binaryPath, node_fs.constants.X_OK);
414
+ return true;
415
+ }
416
+ } catch {}
417
+ }
418
+ return false;
419
+ };
420
+ const detectAvailableAgents = async () => {
421
+ const installedAgents = new Set(await (0, agent_install_skill.detectInstalledSkillAgents)());
422
+ return (0, agent_install_skill.getSkillAgentTypes)().filter((agent) => {
423
+ if (agent === "universal") return false;
424
+ if (installedAgents.has(agent)) return true;
425
+ return PATH_BINARIES[agent]?.some(isCommandAvailable) ?? false;
426
+ });
427
+ };
428
+ //#endregion
429
+ //#region src/utils/unref-stdin.ts
430
+ const unrefStdin = () => {
431
+ if (process.stdin.isTTY) return;
432
+ process.stdin.unref?.();
433
+ };
434
+ //#endregion
396
435
  //#region src/utils/prompts.ts
397
436
  const onCancel = () => {
398
437
  logger.break();
@@ -401,7 +440,7 @@ const onCancel = () => {
401
440
  process.exit(0);
402
441
  };
403
442
  const prompts$1 = (questions) => {
404
- return (0, prompts.default)(questions, { onCancel });
443
+ return (0, prompts.default)(questions, { onCancel }).finally(unrefStdin);
405
444
  };
406
445
  //#endregion
407
446
  //#region src/utils/spinner.ts
@@ -413,24 +452,32 @@ const SKILL_SOURCE = (0, node_url.fileURLToPath)(new URL("../skills/react-grab",
413
452
  const agentLabel = (agent) => (0, agent_install_skill.getSkillAgentConfig)(agent).displayName;
414
453
  const installedSkillDir = (agent) => (0, node_path.join)((0, agent_install_skill.isUniversalSkillAgent)(agent) ? (0, agent_install_skill.getCanonicalSkillsDir)(true) : (0, agent_install_skill.getSkillAgentDir)(agent, { global: true }), SKILL_NAME);
415
454
  const promptSkillInstall = async ({ yes = false } = {}) => {
416
- const agents = await (0, agent_install_skill.detectInstalledSkillAgents)();
417
- if (agents.length === 0) {
455
+ const detectedAgents = await detectAvailableAgents();
456
+ if (detectedAgents.length === 0) {
418
457
  logger.warn("No supported agents detected.");
419
458
  return false;
420
459
  }
460
+ let selectedAgents = detectedAgents;
421
461
  if (!yes) {
422
- const { confirmed } = await prompts$1({
423
- type: "confirm",
424
- name: "confirmed",
425
- message: `Install the React Grab skill for ${highlighter.info(agents.map(agentLabel).join(", "))}?`,
426
- initial: true
462
+ const { agents } = await prompts$1({
463
+ type: "multiselect",
464
+ name: "agents",
465
+ message: "Install the React Grab skill for:",
466
+ choices: detectedAgents.map((agent) => ({
467
+ title: agentLabel(agent),
468
+ value: agent,
469
+ selected: true
470
+ })),
471
+ instructions: false,
472
+ min: 1
427
473
  });
428
- if (!confirmed) return false;
474
+ selectedAgents = agents ?? [];
475
+ if (selectedAgents.length === 0) return false;
429
476
  }
430
477
  const installSpinner = spinner("Installing React Grab skill.").start();
431
478
  const { installed, failed } = await (0, agent_install_skill.add)({
432
479
  source: SKILL_SOURCE,
433
- agents,
480
+ agents: selectedAgents,
434
481
  global: true,
435
482
  mode: "copy"
436
483
  });
@@ -443,7 +490,7 @@ const promptSkillInstall = async ({ yes = false } = {}) => {
443
490
  return true;
444
491
  };
445
492
  const removeSkill = async () => {
446
- const agentsWithSkill = (await (0, agent_install_skill.detectInstalledSkillAgents)()).filter((agent) => (0, node_fs.existsSync)(installedSkillDir(agent)));
493
+ const agentsWithSkill = (await detectAvailableAgents()).filter((agent) => (0, node_fs.existsSync)(installedSkillDir(agent)));
447
494
  for (const skillDir of new Set(agentsWithSkill.map(installedSkillDir))) (0, node_fs.rmSync)(skillDir, {
448
495
  recursive: true,
449
496
  force: true
@@ -453,7 +500,7 @@ const removeSkill = async () => {
453
500
  };
454
501
  //#endregion
455
502
  //#region src/commands/add.ts
456
- const VERSION$5 = "0.1.38";
503
+ const VERSION$5 = "0.1.39";
457
504
  const add = new commander.Command().name("add").alias("install").description("install the React Grab skill for your agent").option("-y, --yes", "skip confirmation prompts", false).option("-c, --cwd <cwd>", "working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
458
505
  console.log(`${picocolors.default.magenta("✿")} ${picocolors.default.bold("React Grab")} ${picocolors.default.gray(VERSION$5)}`);
459
506
  console.log();
@@ -856,6 +903,15 @@ const transformTanStack = (projectRoot, reactGrabAlreadyConfigured, force = fals
856
903
  newContent
857
904
  };
858
905
  };
906
+ const hasFrameworkEntryPoint = (projectRoot, framework, nextRouterType) => {
907
+ switch (framework) {
908
+ case "next": return nextRouterType === "app" ? findLayoutFile(projectRoot) !== null : findDocumentFile(projectRoot) !== null;
909
+ case "vite":
910
+ case "webpack": return findEntryFile(projectRoot) !== null;
911
+ case "tanstack": return findTanStackRootFile(projectRoot) !== null;
912
+ default: return false;
913
+ }
914
+ };
859
915
  const previewTransform = (projectRoot, framework, nextRouterType, reactGrabAlreadyConfigured = false, force = false) => {
860
916
  switch (framework) {
861
917
  case "next":
@@ -1055,7 +1111,7 @@ const formatActivationKeyDisplay = (activationKey) => {
1055
1111
  };
1056
1112
  //#endregion
1057
1113
  //#region src/commands/configure.ts
1058
- const VERSION$4 = "0.1.38";
1114
+ const VERSION$4 = "0.1.39";
1059
1115
  const isMac = process.platform === "darwin";
1060
1116
  const META_LABEL = isMac ? "Cmd" : "Win";
1061
1117
  const ALT_LABEL = isMac ? "Option" : "Alt";
@@ -1674,7 +1730,7 @@ const installPackagesWithFeedback = async (packages, packageManager, projectRoot
1674
1730
  };
1675
1731
  //#endregion
1676
1732
  //#region src/commands/init.ts
1677
- const VERSION$3 = "0.1.38";
1733
+ const VERSION$3 = "0.1.39";
1678
1734
  const REPORT_URL = "https://react-grab.com/api/report-cli";
1679
1735
  const DOCS_URL = "https://github.com/aidenybai/react-grab";
1680
1736
  const reportToCli = (type, config, error) => {
@@ -1732,6 +1788,15 @@ const printSubprojects = (searchRoot, sortedProjects) => {
1732
1788
  logger.log(` ${highlighter.dim("$")} npx grab@latest init -c ${(0, node_path.relative)(searchRoot, sortedProjects[0].path)}`);
1733
1789
  logger.break();
1734
1790
  };
1791
+ const SUPPORTED_FRAMEWORKS_LINE = "React Grab supports Next.js, Vite, TanStack Start, and Webpack projects.";
1792
+ const failWithManualSetup = (failingSpinner, message, { listSupportedFrameworks = false } = {}) => {
1793
+ failingSpinner.fail(message);
1794
+ logger.break();
1795
+ if (listSupportedFrameworks) logger.log(SUPPORTED_FRAMEWORKS_LINE);
1796
+ logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1797
+ logger.break();
1798
+ process.exit(1);
1799
+ };
1735
1800
  const init = new commander.Command().name("init").alias("setup").description("initialize React Grab in your project").option("-y, --yes", "skip confirmation prompts", false).option("-f, --force", "force overwrite existing config", false).option("-k, --key <key>", "activation key (e.g., Meta+K, Ctrl+Shift+G, Space)").option("--skip-install", "skip package installation", false).option("--pkg <pkg>", "custom package URL for CLI (e.g., grab)").option("-c, --cwd <cwd>", "working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
1736
1801
  console.log(`${picocolors.default.magenta("✿")} ${picocolors.default.bold("React Grab")} ${picocolors.default.gray(VERSION$3)}`);
1737
1802
  console.log();
@@ -1905,7 +1970,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
1905
1970
  logger.break();
1906
1971
  process.exit(1);
1907
1972
  }
1908
- if (projectInfo.framework === "unknown") {
1973
+ if (projectInfo.framework === "unknown" || projectInfo.isMonorepo && !hasFrameworkEntryPoint(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType)) {
1909
1974
  let searchRoot = cwd;
1910
1975
  let reactProjects = findReactProjects(searchRoot);
1911
1976
  if (reactProjects.length === 0 && cwd !== process.cwd()) {
@@ -1943,23 +2008,10 @@ const init = new commander.Command().name("init").alias("setup").description("in
1943
2008
  const newProjectInfo = await detectProject(selectedProject);
1944
2009
  Object.assign(projectInfo, newProjectInfo);
1945
2010
  const newFrameworkSpinner = spinner("Verifying framework.").start();
1946
- if (newProjectInfo.framework === "unknown") {
1947
- newFrameworkSpinner.fail("Could not detect a supported framework in this project.");
1948
- logger.break();
1949
- logger.log("React Grab supports Next.js, Vite, TanStack Start, and Webpack projects.");
1950
- logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1951
- logger.break();
1952
- process.exit(1);
1953
- }
2011
+ if (newProjectInfo.framework === "unknown") failWithManualSetup(newFrameworkSpinner, "Could not detect a supported framework in this project.", { listSupportedFrameworks: true });
1954
2012
  newFrameworkSpinner.succeed(`Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[newProjectInfo.framework])}.`);
1955
- } else {
1956
- frameworkSpinner.fail("Could not detect a supported framework.");
1957
- logger.break();
1958
- logger.log("React Grab supports Next.js, Vite, TanStack Start, and Webpack projects.");
1959
- logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1960
- logger.break();
1961
- process.exit(1);
1962
- }
2013
+ } else if (projectInfo.framework !== "unknown") failWithManualSetup(frameworkSpinner, `Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[projectInfo.framework])}, but could not find an entry file.`);
2014
+ else failWithManualSetup(frameworkSpinner, "Could not detect a supported framework.", { listSupportedFrameworks: true });
1963
2015
  } else frameworkSpinner.succeed(`Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[projectInfo.framework])}.`);
1964
2016
  if (projectInfo.framework === "next") spinner("Detecting router type.").start().succeed(`Detecting router type. Found ${highlighter.info(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}.`);
1965
2017
  spinner("Detecting package manager.").start().succeed(`Detecting package manager. Found ${highlighter.info(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}.`);
@@ -2023,7 +2075,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
2023
2075
  });
2024
2076
  //#endregion
2025
2077
  //#region src/commands/remove.ts
2026
- const VERSION$2 = "0.1.38";
2078
+ const VERSION$2 = "0.1.39";
2027
2079
  const remove = new commander.Command().name("remove").description("uninstall the React Grab skill from your agent").action(async () => {
2028
2080
  console.log(`${picocolors.default.magenta("✿")} ${picocolors.default.bold("React Grab")} ${picocolors.default.gray(VERSION$2)}`);
2029
2081
  console.log();
@@ -2040,7 +2092,7 @@ const remove = new commander.Command().name("remove").description("uninstall the
2040
2092
  });
2041
2093
  //#endregion
2042
2094
  //#region src/commands/upgrade.ts
2043
- const VERSION$1 = "0.1.38";
2095
+ const VERSION$1 = "0.1.39";
2044
2096
  const NPM_REGISTRY_URL = "https://registry.npmjs.org/react-grab/latest";
2045
2097
  const fetchLatestVersion = async () => {
2046
2098
  try {
@@ -2475,7 +2527,7 @@ const watch = new commander.Command().name("watch").description("watch the clipb
2475
2527
  });
2476
2528
  //#endregion
2477
2529
  //#region src/cli.ts
2478
- const VERSION = "0.1.38";
2530
+ const VERSION = "0.1.39";
2479
2531
  const VERSION_API_URL = "https://www.react-grab.com/api/version";
2480
2532
  process.on("SIGINT", () => process.exit(0));
2481
2533
  process.on("SIGTERM", () => process.exit(0));
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
3
  import pc from "picocolors";
4
- import fs, { accessSync, constants, existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
5
- import path, { basename, dirname, join, relative, resolve } from "node:path";
4
+ import fs, { accessSync, constants, existsSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
5
+ import path, { basename, delimiter, dirname, join, relative, resolve } from "node:path";
6
6
  import { detect } from "package-manager-detector/detect";
7
7
  import ignore from "ignore";
8
8
  import { fileURLToPath } from "node:url";
9
- import { add, detectInstalledSkillAgents, getCanonicalSkillsDir, getSkillAgentConfig, getSkillAgentDir, isUniversalSkillAgent } from "agent-install/skill";
9
+ import { add, detectInstalledSkillAgents, getCanonicalSkillsDir, getSkillAgentConfig, getSkillAgentDir, getSkillAgentTypes, isUniversalSkillAgent } from "agent-install/skill";
10
10
  import basePrompts from "prompts";
11
11
  import ora from "ora";
12
12
  import { x } from "tinyexec";
@@ -365,6 +365,45 @@ const handleError = (error) => {
365
365
  process.exit(1);
366
366
  };
367
367
  //#endregion
368
+ //#region src/utils/detect-agents.ts
369
+ const PATH_BINARIES = {
370
+ "claude-code": ["claude"],
371
+ codex: ["codex"],
372
+ cursor: ["cursor", "cursor-agent"],
373
+ droid: ["droid"],
374
+ "gemini-cli": ["gemini"],
375
+ "github-copilot": ["copilot"],
376
+ opencode: ["opencode"],
377
+ pi: ["pi", "omegon"]
378
+ };
379
+ const isCommandAvailable = (command) => {
380
+ const pathDirectories = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
381
+ for (const directory of pathDirectories) {
382
+ const binaryPath = join(directory, command);
383
+ try {
384
+ if (statSync(binaryPath).isFile()) {
385
+ accessSync(binaryPath, constants.X_OK);
386
+ return true;
387
+ }
388
+ } catch {}
389
+ }
390
+ return false;
391
+ };
392
+ const detectAvailableAgents = async () => {
393
+ const installedAgents = new Set(await detectInstalledSkillAgents());
394
+ return getSkillAgentTypes().filter((agent) => {
395
+ if (agent === "universal") return false;
396
+ if (installedAgents.has(agent)) return true;
397
+ return PATH_BINARIES[agent]?.some(isCommandAvailable) ?? false;
398
+ });
399
+ };
400
+ //#endregion
401
+ //#region src/utils/unref-stdin.ts
402
+ const unrefStdin = () => {
403
+ if (process.stdin.isTTY) return;
404
+ process.stdin.unref?.();
405
+ };
406
+ //#endregion
368
407
  //#region src/utils/prompts.ts
369
408
  const onCancel = () => {
370
409
  logger.break();
@@ -373,7 +412,7 @@ const onCancel = () => {
373
412
  process.exit(0);
374
413
  };
375
414
  const prompts = (questions) => {
376
- return basePrompts(questions, { onCancel });
415
+ return basePrompts(questions, { onCancel }).finally(unrefStdin);
377
416
  };
378
417
  //#endregion
379
418
  //#region src/utils/spinner.ts
@@ -385,24 +424,32 @@ const SKILL_SOURCE = fileURLToPath(new URL("../skills/react-grab", import.meta.u
385
424
  const agentLabel = (agent) => getSkillAgentConfig(agent).displayName;
386
425
  const installedSkillDir = (agent) => join(isUniversalSkillAgent(agent) ? getCanonicalSkillsDir(true) : getSkillAgentDir(agent, { global: true }), SKILL_NAME);
387
426
  const promptSkillInstall = async ({ yes = false } = {}) => {
388
- const agents = await detectInstalledSkillAgents();
389
- if (agents.length === 0) {
427
+ const detectedAgents = await detectAvailableAgents();
428
+ if (detectedAgents.length === 0) {
390
429
  logger.warn("No supported agents detected.");
391
430
  return false;
392
431
  }
432
+ let selectedAgents = detectedAgents;
393
433
  if (!yes) {
394
- const { confirmed } = await prompts({
395
- type: "confirm",
396
- name: "confirmed",
397
- message: `Install the React Grab skill for ${highlighter.info(agents.map(agentLabel).join(", "))}?`,
398
- initial: true
434
+ const { agents } = await prompts({
435
+ type: "multiselect",
436
+ name: "agents",
437
+ message: "Install the React Grab skill for:",
438
+ choices: detectedAgents.map((agent) => ({
439
+ title: agentLabel(agent),
440
+ value: agent,
441
+ selected: true
442
+ })),
443
+ instructions: false,
444
+ min: 1
399
445
  });
400
- if (!confirmed) return false;
446
+ selectedAgents = agents ?? [];
447
+ if (selectedAgents.length === 0) return false;
401
448
  }
402
449
  const installSpinner = spinner("Installing React Grab skill.").start();
403
450
  const { installed, failed } = await add({
404
451
  source: SKILL_SOURCE,
405
- agents,
452
+ agents: selectedAgents,
406
453
  global: true,
407
454
  mode: "copy"
408
455
  });
@@ -415,7 +462,7 @@ const promptSkillInstall = async ({ yes = false } = {}) => {
415
462
  return true;
416
463
  };
417
464
  const removeSkill = async () => {
418
- const agentsWithSkill = (await detectInstalledSkillAgents()).filter((agent) => existsSync(installedSkillDir(agent)));
465
+ const agentsWithSkill = (await detectAvailableAgents()).filter((agent) => existsSync(installedSkillDir(agent)));
419
466
  for (const skillDir of new Set(agentsWithSkill.map(installedSkillDir))) rmSync(skillDir, {
420
467
  recursive: true,
421
468
  force: true
@@ -425,7 +472,7 @@ const removeSkill = async () => {
425
472
  };
426
473
  //#endregion
427
474
  //#region src/commands/add.ts
428
- const VERSION$5 = "0.1.38";
475
+ const VERSION$5 = "0.1.39";
429
476
  const add$1 = new Command().name("add").alias("install").description("install the React Grab skill for your agent").option("-y, --yes", "skip confirmation prompts", false).option("-c, --cwd <cwd>", "working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
430
477
  console.log(`${pc.magenta("✿")} ${pc.bold("React Grab")} ${pc.gray(VERSION$5)}`);
431
478
  console.log();
@@ -828,6 +875,15 @@ const transformTanStack = (projectRoot, reactGrabAlreadyConfigured, force = fals
828
875
  newContent
829
876
  };
830
877
  };
878
+ const hasFrameworkEntryPoint = (projectRoot, framework, nextRouterType) => {
879
+ switch (framework) {
880
+ case "next": return nextRouterType === "app" ? findLayoutFile(projectRoot) !== null : findDocumentFile(projectRoot) !== null;
881
+ case "vite":
882
+ case "webpack": return findEntryFile(projectRoot) !== null;
883
+ case "tanstack": return findTanStackRootFile(projectRoot) !== null;
884
+ default: return false;
885
+ }
886
+ };
831
887
  const previewTransform = (projectRoot, framework, nextRouterType, reactGrabAlreadyConfigured = false, force = false) => {
832
888
  switch (framework) {
833
889
  case "next":
@@ -1027,7 +1083,7 @@ const formatActivationKeyDisplay = (activationKey) => {
1027
1083
  };
1028
1084
  //#endregion
1029
1085
  //#region src/commands/configure.ts
1030
- const VERSION$4 = "0.1.38";
1086
+ const VERSION$4 = "0.1.39";
1031
1087
  const isMac = process.platform === "darwin";
1032
1088
  const META_LABEL = isMac ? "Cmd" : "Win";
1033
1089
  const ALT_LABEL = isMac ? "Option" : "Alt";
@@ -1646,7 +1702,7 @@ const installPackagesWithFeedback = async (packages, packageManager, projectRoot
1646
1702
  };
1647
1703
  //#endregion
1648
1704
  //#region src/commands/init.ts
1649
- const VERSION$3 = "0.1.38";
1705
+ const VERSION$3 = "0.1.39";
1650
1706
  const REPORT_URL = "https://react-grab.com/api/report-cli";
1651
1707
  const DOCS_URL = "https://github.com/aidenybai/react-grab";
1652
1708
  const reportToCli = (type, config, error) => {
@@ -1704,6 +1760,15 @@ const printSubprojects = (searchRoot, sortedProjects) => {
1704
1760
  logger.log(` ${highlighter.dim("$")} npx grab@latest init -c ${relative(searchRoot, sortedProjects[0].path)}`);
1705
1761
  logger.break();
1706
1762
  };
1763
+ const SUPPORTED_FRAMEWORKS_LINE = "React Grab supports Next.js, Vite, TanStack Start, and Webpack projects.";
1764
+ const failWithManualSetup = (failingSpinner, message, { listSupportedFrameworks = false } = {}) => {
1765
+ failingSpinner.fail(message);
1766
+ logger.break();
1767
+ if (listSupportedFrameworks) logger.log(SUPPORTED_FRAMEWORKS_LINE);
1768
+ logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1769
+ logger.break();
1770
+ process.exit(1);
1771
+ };
1707
1772
  const init = new Command().name("init").alias("setup").description("initialize React Grab in your project").option("-y, --yes", "skip confirmation prompts", false).option("-f, --force", "force overwrite existing config", false).option("-k, --key <key>", "activation key (e.g., Meta+K, Ctrl+Shift+G, Space)").option("--skip-install", "skip package installation", false).option("--pkg <pkg>", "custom package URL for CLI (e.g., grab)").option("-c, --cwd <cwd>", "working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
1708
1773
  console.log(`${pc.magenta("✿")} ${pc.bold("React Grab")} ${pc.gray(VERSION$3)}`);
1709
1774
  console.log();
@@ -1877,7 +1942,7 @@ const init = new Command().name("init").alias("setup").description("initialize R
1877
1942
  logger.break();
1878
1943
  process.exit(1);
1879
1944
  }
1880
- if (projectInfo.framework === "unknown") {
1945
+ if (projectInfo.framework === "unknown" || projectInfo.isMonorepo && !hasFrameworkEntryPoint(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType)) {
1881
1946
  let searchRoot = cwd;
1882
1947
  let reactProjects = findReactProjects(searchRoot);
1883
1948
  if (reactProjects.length === 0 && cwd !== process.cwd()) {
@@ -1915,23 +1980,10 @@ const init = new Command().name("init").alias("setup").description("initialize R
1915
1980
  const newProjectInfo = await detectProject(selectedProject);
1916
1981
  Object.assign(projectInfo, newProjectInfo);
1917
1982
  const newFrameworkSpinner = spinner("Verifying framework.").start();
1918
- if (newProjectInfo.framework === "unknown") {
1919
- newFrameworkSpinner.fail("Could not detect a supported framework in this project.");
1920
- logger.break();
1921
- logger.log("React Grab supports Next.js, Vite, TanStack Start, and Webpack projects.");
1922
- logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1923
- logger.break();
1924
- process.exit(1);
1925
- }
1983
+ if (newProjectInfo.framework === "unknown") failWithManualSetup(newFrameworkSpinner, "Could not detect a supported framework in this project.", { listSupportedFrameworks: true });
1926
1984
  newFrameworkSpinner.succeed(`Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[newProjectInfo.framework])}.`);
1927
- } else {
1928
- frameworkSpinner.fail("Could not detect a supported framework.");
1929
- logger.break();
1930
- logger.log("React Grab supports Next.js, Vite, TanStack Start, and Webpack projects.");
1931
- logger.log(`Visit ${highlighter.info(DOCS_URL)} for manual setup.`);
1932
- logger.break();
1933
- process.exit(1);
1934
- }
1985
+ } else if (projectInfo.framework !== "unknown") failWithManualSetup(frameworkSpinner, `Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[projectInfo.framework])}, but could not find an entry file.`);
1986
+ else failWithManualSetup(frameworkSpinner, "Could not detect a supported framework.", { listSupportedFrameworks: true });
1935
1987
  } else frameworkSpinner.succeed(`Verifying framework. Found ${highlighter.info(FRAMEWORK_NAMES[projectInfo.framework])}.`);
1936
1988
  if (projectInfo.framework === "next") spinner("Detecting router type.").start().succeed(`Detecting router type. Found ${highlighter.info(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}.`);
1937
1989
  spinner("Detecting package manager.").start().succeed(`Detecting package manager. Found ${highlighter.info(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}.`);
@@ -1995,7 +2047,7 @@ const init = new Command().name("init").alias("setup").description("initialize R
1995
2047
  });
1996
2048
  //#endregion
1997
2049
  //#region src/commands/remove.ts
1998
- const VERSION$2 = "0.1.38";
2050
+ const VERSION$2 = "0.1.39";
1999
2051
  const remove = new Command().name("remove").description("uninstall the React Grab skill from your agent").action(async () => {
2000
2052
  console.log(`${pc.magenta("✿")} ${pc.bold("React Grab")} ${pc.gray(VERSION$2)}`);
2001
2053
  console.log();
@@ -2012,7 +2064,7 @@ const remove = new Command().name("remove").description("uninstall the React Gra
2012
2064
  });
2013
2065
  //#endregion
2014
2066
  //#region src/commands/upgrade.ts
2015
- const VERSION$1 = "0.1.38";
2067
+ const VERSION$1 = "0.1.39";
2016
2068
  const NPM_REGISTRY_URL = "https://registry.npmjs.org/react-grab/latest";
2017
2069
  const fetchLatestVersion = async () => {
2018
2070
  try {
@@ -2447,7 +2499,7 @@ const watch = new Command().name("watch").description("watch the clipboard for t
2447
2499
  });
2448
2500
  //#endregion
2449
2501
  //#region src/cli.ts
2450
- const VERSION = "0.1.38";
2502
+ const VERSION = "0.1.39";
2451
2503
  const VERSION_API_URL = "https://www.react-grab.com/api/version";
2452
2504
  process.on("SIGINT", () => process.exit(0));
2453
2505
  process.on("SIGTERM", () => process.exit(0));