@react-grab/cli 0.0.68 → 0.0.70
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 +355 -59
- package/dist/cli.js +356 -60
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -27,21 +27,25 @@ var detectFramework = (projectRoot) => {
|
|
|
27
27
|
if (!fs.existsSync(packageJsonPath)) {
|
|
28
28
|
return "unknown";
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
try {
|
|
31
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
32
|
+
const allDependencies = {
|
|
33
|
+
...packageJson.dependencies,
|
|
34
|
+
...packageJson.devDependencies
|
|
35
|
+
};
|
|
36
|
+
if (allDependencies["next"]) {
|
|
37
|
+
return "next";
|
|
38
|
+
}
|
|
39
|
+
if (allDependencies["vite"]) {
|
|
40
|
+
return "vite";
|
|
41
|
+
}
|
|
42
|
+
if (allDependencies["webpack"]) {
|
|
43
|
+
return "webpack";
|
|
44
|
+
}
|
|
45
|
+
return "unknown";
|
|
46
|
+
} catch {
|
|
47
|
+
return "unknown";
|
|
43
48
|
}
|
|
44
|
-
return "unknown";
|
|
45
49
|
};
|
|
46
50
|
var detectNextRouterType = (projectRoot) => {
|
|
47
51
|
const hasAppDir = fs.existsSync(path.join(projectRoot, "app"));
|
|
@@ -65,9 +69,13 @@ var detectMonorepo = (projectRoot) => {
|
|
|
65
69
|
}
|
|
66
70
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
67
71
|
if (fs.existsSync(packageJsonPath)) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
try {
|
|
73
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
74
|
+
if (packageJson.workspaces) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
return false;
|
|
@@ -77,27 +85,63 @@ var detectReactGrab = (projectRoot) => {
|
|
|
77
85
|
if (!fs.existsSync(packageJsonPath)) {
|
|
78
86
|
return false;
|
|
79
87
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
try {
|
|
89
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
90
|
+
const allDependencies = {
|
|
91
|
+
...packageJson.dependencies,
|
|
92
|
+
...packageJson.devDependencies
|
|
93
|
+
};
|
|
94
|
+
return Boolean(allDependencies["react-grab"]);
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
86
98
|
};
|
|
87
99
|
var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
|
|
100
|
+
var detectUnsupportedFramework = (projectRoot) => {
|
|
101
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
102
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
107
|
+
const allDependencies = {
|
|
108
|
+
...packageJson.dependencies,
|
|
109
|
+
...packageJson.devDependencies
|
|
110
|
+
};
|
|
111
|
+
if (allDependencies["@remix-run/react"] || allDependencies["remix"]) {
|
|
112
|
+
return "remix";
|
|
113
|
+
}
|
|
114
|
+
if (allDependencies["astro"]) {
|
|
115
|
+
return "astro";
|
|
116
|
+
}
|
|
117
|
+
if (allDependencies["@sveltejs/kit"]) {
|
|
118
|
+
return "sveltekit";
|
|
119
|
+
}
|
|
120
|
+
if (allDependencies["gatsby"]) {
|
|
121
|
+
return "gatsby";
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
88
128
|
var detectInstalledAgents = (projectRoot) => {
|
|
89
129
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
90
130
|
if (!fs.existsSync(packageJsonPath)) {
|
|
91
131
|
return [];
|
|
92
132
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(agent) => agent.
|
|
100
|
-
|
|
133
|
+
try {
|
|
134
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
135
|
+
const allDependencies = {
|
|
136
|
+
...packageJson.dependencies,
|
|
137
|
+
...packageJson.devDependencies
|
|
138
|
+
};
|
|
139
|
+
return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
|
|
140
|
+
(agent) => agent.replace("@react-grab/", "")
|
|
141
|
+
);
|
|
142
|
+
} catch {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
101
145
|
};
|
|
102
146
|
var detectProject = async (projectRoot = process.cwd()) => {
|
|
103
147
|
const framework = detectFramework(projectRoot);
|
|
@@ -109,7 +153,8 @@ var detectProject = async (projectRoot = process.cwd()) => {
|
|
|
109
153
|
isMonorepo: detectMonorepo(projectRoot),
|
|
110
154
|
projectRoot,
|
|
111
155
|
hasReactGrab: detectReactGrab(projectRoot),
|
|
112
|
-
installedAgents: detectInstalledAgents(projectRoot)
|
|
156
|
+
installedAgents: detectInstalledAgents(projectRoot),
|
|
157
|
+
unsupportedFramework: detectUnsupportedFramework(projectRoot)
|
|
113
158
|
};
|
|
114
159
|
};
|
|
115
160
|
|
|
@@ -553,7 +598,7 @@ var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured)
|
|
|
553
598
|
return {
|
|
554
599
|
success: false,
|
|
555
600
|
filePath: "",
|
|
556
|
-
message:
|
|
601
|
+
message: 'Could not find pages/_document.tsx or pages/_document.jsx.\n\nTo set up React Grab with Pages Router, create pages/_document.tsx with:\n\n import { Html, Head, Main, NextScript } from "next/document";\n import Script from "next/script";\n\n export default function Document() {\n return (\n <Html>\n <Head>\n {process.env.NODE_ENV === "development" && (\n <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />\n )}\n </Head>\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }'
|
|
557
602
|
};
|
|
558
603
|
}
|
|
559
604
|
const originalContent = fs.readFileSync(documentPath, "utf-8");
|
|
@@ -681,14 +726,134 @@ var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrab
|
|
|
681
726
|
};
|
|
682
727
|
}
|
|
683
728
|
};
|
|
729
|
+
var canWriteToFile = (filePath) => {
|
|
730
|
+
try {
|
|
731
|
+
fs.accessSync(filePath, fs.constants.W_OK);
|
|
732
|
+
return true;
|
|
733
|
+
} catch {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
684
737
|
var applyTransform = (result) => {
|
|
685
738
|
if (result.success && result.newContent && result.filePath) {
|
|
686
|
-
|
|
739
|
+
if (!canWriteToFile(result.filePath)) {
|
|
740
|
+
return {
|
|
741
|
+
success: false,
|
|
742
|
+
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
try {
|
|
746
|
+
fs.writeFileSync(result.filePath, result.newContent);
|
|
747
|
+
return { success: true };
|
|
748
|
+
} catch (error) {
|
|
749
|
+
return {
|
|
750
|
+
success: false,
|
|
751
|
+
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
752
|
+
};
|
|
753
|
+
}
|
|
687
754
|
}
|
|
755
|
+
return { success: true };
|
|
756
|
+
};
|
|
757
|
+
var AGENT_PREFIXES = {
|
|
758
|
+
"claude-code": "npx @react-grab/claude-code@latest &&",
|
|
759
|
+
cursor: "npx @react-grab/cursor@latest &&",
|
|
760
|
+
opencode: "npx @react-grab/opencode@latest &&"
|
|
761
|
+
};
|
|
762
|
+
var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
|
|
763
|
+
if (agent === "none") {
|
|
764
|
+
return {
|
|
765
|
+
success: true,
|
|
766
|
+
filePath: "",
|
|
767
|
+
message: "No agent selected, skipping package.json modification",
|
|
768
|
+
noChanges: true
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
772
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
773
|
+
return {
|
|
774
|
+
success: false,
|
|
775
|
+
filePath: "",
|
|
776
|
+
message: "Could not find package.json"
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const originalContent = fs.readFileSync(packageJsonPath, "utf-8");
|
|
780
|
+
const agentPrefix = AGENT_PREFIXES[agent];
|
|
781
|
+
if (!agentPrefix) {
|
|
782
|
+
return {
|
|
783
|
+
success: false,
|
|
784
|
+
filePath: packageJsonPath,
|
|
785
|
+
message: `Unknown agent: ${agent}`
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
if (originalContent.includes(agentPrefix)) {
|
|
789
|
+
return {
|
|
790
|
+
success: true,
|
|
791
|
+
filePath: packageJsonPath,
|
|
792
|
+
message: `Agent ${agent} dev script is already configured`,
|
|
793
|
+
noChanges: true
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
const packageJson = JSON.parse(originalContent);
|
|
798
|
+
if (!packageJson.scripts?.dev) {
|
|
799
|
+
return {
|
|
800
|
+
success: false,
|
|
801
|
+
filePath: packageJsonPath,
|
|
802
|
+
message: 'No "dev" script found in package.json'
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const currentDevScript = packageJson.scripts.dev;
|
|
806
|
+
for (const installedAgent of installedAgents) {
|
|
807
|
+
const existingPrefix = AGENT_PREFIXES[installedAgent];
|
|
808
|
+
if (existingPrefix && currentDevScript.includes(existingPrefix)) {
|
|
809
|
+
return {
|
|
810
|
+
success: true,
|
|
811
|
+
filePath: packageJsonPath,
|
|
812
|
+
message: `Agent ${installedAgent} is already in dev script`,
|
|
813
|
+
noChanges: true
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
packageJson.scripts.dev = `${agentPrefix} ${currentDevScript}`;
|
|
818
|
+
const newContent = JSON.stringify(packageJson, null, 2) + "\n";
|
|
819
|
+
return {
|
|
820
|
+
success: true,
|
|
821
|
+
filePath: packageJsonPath,
|
|
822
|
+
message: `Add ${agent} server to dev script`,
|
|
823
|
+
originalContent,
|
|
824
|
+
newContent
|
|
825
|
+
};
|
|
826
|
+
} catch {
|
|
827
|
+
return {
|
|
828
|
+
success: false,
|
|
829
|
+
filePath: packageJsonPath,
|
|
830
|
+
message: "Failed to parse package.json"
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
var applyPackageJsonTransform = (result) => {
|
|
835
|
+
if (result.success && result.newContent && result.filePath) {
|
|
836
|
+
if (!canWriteToFile(result.filePath)) {
|
|
837
|
+
return {
|
|
838
|
+
success: false,
|
|
839
|
+
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
fs.writeFileSync(result.filePath, result.newContent);
|
|
844
|
+
return { success: true };
|
|
845
|
+
} catch (error) {
|
|
846
|
+
return {
|
|
847
|
+
success: false,
|
|
848
|
+
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return { success: true };
|
|
688
853
|
};
|
|
689
854
|
|
|
690
855
|
// src/cli.ts
|
|
691
|
-
var VERSION = "0.0.
|
|
856
|
+
var VERSION = "0.0.68";
|
|
692
857
|
var FRAMEWORK_NAMES = {
|
|
693
858
|
next: "Next.js",
|
|
694
859
|
vite: "Vite",
|
|
@@ -706,43 +871,116 @@ var AGENT_NAMES = {
|
|
|
706
871
|
cursor: "Cursor",
|
|
707
872
|
opencode: "Opencode"
|
|
708
873
|
};
|
|
709
|
-
var
|
|
874
|
+
var UNSUPPORTED_FRAMEWORK_NAMES = {
|
|
875
|
+
remix: "Remix",
|
|
876
|
+
astro: "Astro",
|
|
877
|
+
sveltekit: "SvelteKit",
|
|
878
|
+
gatsby: "Gatsby"
|
|
879
|
+
};
|
|
880
|
+
var DOCS_URL = "https://github.com/aidenybai/react-grab";
|
|
710
881
|
var showDocsLink = () => {
|
|
711
882
|
console.log("\nFor manual installation instructions, visit:");
|
|
712
|
-
console.log(` ${DOCS_URL}
|
|
883
|
+
console.log(` ${pc__default.default.cyan(DOCS_URL)}
|
|
713
884
|
`);
|
|
714
885
|
};
|
|
886
|
+
var showManualInstructions = (framework, nextRouterType) => {
|
|
887
|
+
console.log(`
|
|
888
|
+
${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("Manual Setup Instructions:")}
|
|
889
|
+
`);
|
|
890
|
+
if (framework === "next" && nextRouterType === "app") {
|
|
891
|
+
console.log(`${pc__default.default.bold("Next.js App Router:")}`);
|
|
892
|
+
console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
|
|
893
|
+
console.log(` 2. Add to ${pc__default.default.cyan("app/layout.tsx")}:`);
|
|
894
|
+
console.log(` ${pc__default.default.gray('import Script from "next/script";')}`);
|
|
895
|
+
console.log(` ${pc__default.default.gray("// Inside <head> or after <html>:")}`);
|
|
896
|
+
console.log(` ${pc__default.default.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
|
|
897
|
+
} else if (framework === "next" && nextRouterType === "pages") {
|
|
898
|
+
console.log(`${pc__default.default.bold("Next.js Pages Router:")}`);
|
|
899
|
+
console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
|
|
900
|
+
console.log(` 2. Create or edit ${pc__default.default.cyan("pages/_document.tsx")}:`);
|
|
901
|
+
console.log(` ${pc__default.default.gray('import Script from "next/script";')}`);
|
|
902
|
+
console.log(` ${pc__default.default.gray("// Inside <Head>:")}`);
|
|
903
|
+
console.log(` ${pc__default.default.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
|
|
904
|
+
} else if (framework === "vite") {
|
|
905
|
+
console.log(`${pc__default.default.bold("Vite:")}`);
|
|
906
|
+
console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
|
|
907
|
+
console.log(` 2. Add to ${pc__default.default.cyan("index.html")} inside <head>:`);
|
|
908
|
+
console.log(` ${pc__default.default.gray('<script type="module">')}`);
|
|
909
|
+
console.log(` ${pc__default.default.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`);
|
|
910
|
+
console.log(` ${pc__default.default.gray("</script>")}`);
|
|
911
|
+
} else if (framework === "webpack") {
|
|
912
|
+
console.log(`${pc__default.default.bold("Webpack:")}`);
|
|
913
|
+
console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
|
|
914
|
+
console.log(` 2. Add to your entry file (e.g., ${pc__default.default.cyan("src/index.tsx")}):`);
|
|
915
|
+
console.log(` ${pc__default.default.gray('if (process.env.NODE_ENV === "development") {')}`);
|
|
916
|
+
console.log(` ${pc__default.default.gray(' import("react-grab");')}`);
|
|
917
|
+
console.log(` ${pc__default.default.gray("}")}`);
|
|
918
|
+
} else {
|
|
919
|
+
console.log(`${pc__default.default.bold("Generic Setup:")}`);
|
|
920
|
+
console.log(` 1. Install: ${pc__default.default.cyan("npm install -D react-grab")}`);
|
|
921
|
+
console.log(` 2. Add the script to your HTML or entry file.`);
|
|
922
|
+
console.log(` 3. See docs for framework-specific instructions.`);
|
|
923
|
+
}
|
|
924
|
+
showDocsLink();
|
|
925
|
+
};
|
|
926
|
+
var showAccuracyWarning = () => {
|
|
927
|
+
console.log(`
|
|
928
|
+
${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("Auto-detection may not be 100% accurate.")}`);
|
|
929
|
+
console.log(`${pc__default.default.yellow(" Please verify the changes in your file before committing.")}`);
|
|
930
|
+
};
|
|
715
931
|
var parseArgs = async () => {
|
|
716
|
-
const argv = await yargs__default.default(helpers.hideBin(process.argv)).
|
|
932
|
+
const argv = await yargs__default.default(helpers.hideBin(process.argv)).scriptName("react-grab").usage(
|
|
933
|
+
`${pc__default.default.magenta("\u269B")} ${pc__default.default.bold("React Grab CLI")} ${pc__default.default.gray(`v${VERSION}`)}
|
|
934
|
+
|
|
935
|
+
${pc__default.default.cyan("Usage:")} $0 [options]
|
|
936
|
+
|
|
937
|
+
React Grab adds AI-powered context selection to your React application,
|
|
938
|
+
allowing you to select components and copy their context for AI assistants.
|
|
939
|
+
|
|
940
|
+
The CLI auto-detects your project configuration (framework, package manager,
|
|
941
|
+
router type) and installs React Grab with optional agent integrations.`
|
|
942
|
+
).option("framework", {
|
|
717
943
|
alias: "f",
|
|
718
944
|
type: "string",
|
|
719
945
|
choices: ["next", "vite", "webpack"],
|
|
720
|
-
description: "
|
|
946
|
+
description: "Override detected framework"
|
|
721
947
|
}).option("package-manager", {
|
|
722
948
|
alias: "p",
|
|
723
949
|
type: "string",
|
|
724
950
|
choices: ["npm", "yarn", "pnpm", "bun"],
|
|
725
|
-
description: "
|
|
951
|
+
description: "Override detected package manager"
|
|
726
952
|
}).option("router", {
|
|
727
953
|
alias: "r",
|
|
728
954
|
type: "string",
|
|
729
955
|
choices: ["app", "pages"],
|
|
730
|
-
description: "Next.js router type (
|
|
956
|
+
description: "Next.js router type (only for Next.js projects)"
|
|
731
957
|
}).option("agent", {
|
|
732
958
|
alias: "a",
|
|
733
959
|
type: "string",
|
|
734
960
|
choices: ["claude-code", "cursor", "opencode", "none"],
|
|
735
|
-
description: "Agent integration to
|
|
961
|
+
description: "Agent integration to automatically forward selected elements to agent instead of copying to clipboard"
|
|
736
962
|
}).option("yes", {
|
|
737
963
|
alias: "y",
|
|
738
964
|
type: "boolean",
|
|
739
965
|
default: false,
|
|
740
|
-
description: "Skip all
|
|
966
|
+
description: "Skip all prompts and use auto-detected/default values"
|
|
741
967
|
}).option("skip-install", {
|
|
742
968
|
type: "boolean",
|
|
743
969
|
default: false,
|
|
744
|
-
description: "
|
|
745
|
-
}).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup").example("$0 -y", "Auto-detect and install without prompts").example("$0 -f next -r app
|
|
970
|
+
description: "Only modify config files, skip npm/yarn/pnpm install"
|
|
971
|
+
}).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup with prompts").example("$0 -y", "Auto-detect everything and install without prompts").example("$0 -f next -r app", "Configure for Next.js App Router").example("$0 -a cursor -y", "Add Cursor agent integration non-interactively").example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent").example("$0 --skip-install", "Only modify files, install packages manually").epilog(
|
|
972
|
+
`${pc__default.default.bold("Agent Integrations:")}
|
|
973
|
+
${pc__default.default.cyan("claude-code")} Connect React Grab to Claude Code
|
|
974
|
+
${pc__default.default.cyan("cursor")} Connect React Grab to Cursor IDE
|
|
975
|
+
${pc__default.default.cyan("opencode")} Connect React Grab to Opencode
|
|
976
|
+
|
|
977
|
+
${pc__default.default.bold("Supported Frameworks:")}
|
|
978
|
+
${pc__default.default.cyan("next")} Next.js (App Router & Pages Router)
|
|
979
|
+
${pc__default.default.cyan("vite")} Vite-based React projects
|
|
980
|
+
${pc__default.default.cyan("webpack")} Webpack-based React projects
|
|
981
|
+
|
|
982
|
+
${pc__default.default.bold("Documentation:")} ${pc__default.default.underline(DOCS_URL)}`
|
|
983
|
+
).wrap(Math.min(100, process.stdout.columns || 80)).parse();
|
|
746
984
|
return {
|
|
747
985
|
framework: argv.framework,
|
|
748
986
|
packageManager: argv["package-manager"],
|
|
@@ -769,6 +1007,20 @@ ${pc__default.default.magenta("\u269B")} ${pc__default.default.bold("React Grab"
|
|
|
769
1007
|
console.log(`- Agents: ${pc__default.default.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`);
|
|
770
1008
|
}
|
|
771
1009
|
console.log("");
|
|
1010
|
+
if (projectInfo.unsupportedFramework) {
|
|
1011
|
+
const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
|
|
1012
|
+
console.log(`${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow(`Detected ${frameworkName} - this framework requires manual setup.`)}`);
|
|
1013
|
+
console.log(`${pc__default.default.yellow(" React Grab may not work correctly with auto-configuration.")}
|
|
1014
|
+
`);
|
|
1015
|
+
showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
|
|
1016
|
+
process.exit(0);
|
|
1017
|
+
}
|
|
1018
|
+
if (projectInfo.framework === "unknown") {
|
|
1019
|
+
console.log(`${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("Could not detect framework automatically.")}
|
|
1020
|
+
`);
|
|
1021
|
+
showManualInstructions("unknown");
|
|
1022
|
+
process.exit(0);
|
|
1023
|
+
}
|
|
772
1024
|
let action = "install-all";
|
|
773
1025
|
if (projectInfo.hasReactGrab && !isNonInteractive) {
|
|
774
1026
|
action = await prompts.select({
|
|
@@ -781,6 +1033,11 @@ ${pc__default.default.magenta("\u269B")} ${pc__default.default.bold("React Grab"
|
|
|
781
1033
|
});
|
|
782
1034
|
} else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
|
|
783
1035
|
action = "add-agent";
|
|
1036
|
+
} else if (projectInfo.hasReactGrab && isNonInteractive && !args.agent) {
|
|
1037
|
+
console.log(`${pc__default.default.yellow("\u26A0\uFE0F")} ${pc__default.default.yellow("React Grab is already installed.")}`);
|
|
1038
|
+
console.log(`${pc__default.default.yellow(" Use --agent to add an agent, or run without -y for interactive mode.")}
|
|
1039
|
+
`);
|
|
1040
|
+
action = "reconfigure";
|
|
784
1041
|
}
|
|
785
1042
|
let finalFramework = args.framework || projectInfo.framework;
|
|
786
1043
|
let finalPackageManager = args.packageManager || projectInfo.packageManager;
|
|
@@ -864,25 +1121,46 @@ ${pc__default.default.magenta("\u269B")} Previewing changes...
|
|
|
864
1121
|
agentIntegration,
|
|
865
1122
|
projectInfo.hasReactGrab || action === "add-agent"
|
|
866
1123
|
);
|
|
1124
|
+
const packageJsonResult = previewPackageJsonTransform(
|
|
1125
|
+
projectInfo.projectRoot,
|
|
1126
|
+
agentIntegration,
|
|
1127
|
+
projectInfo.installedAgents
|
|
1128
|
+
);
|
|
867
1129
|
if (!result.success) {
|
|
868
1130
|
console.error(`${pc__default.default.red("Error:")} ${result.message}`);
|
|
869
1131
|
showDocsLink();
|
|
870
1132
|
process.exit(1);
|
|
871
1133
|
}
|
|
872
|
-
|
|
1134
|
+
const hasLayoutChanges = !result.noChanges && result.originalContent && result.newContent;
|
|
1135
|
+
const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
|
|
1136
|
+
if (result.noChanges && packageJsonResult.noChanges) {
|
|
873
1137
|
console.log(`${pc__default.default.cyan("Info:")} ${result.message}`);
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1138
|
+
if (packageJsonResult.message) {
|
|
1139
|
+
console.log(`${pc__default.default.cyan("Info:")} ${packageJsonResult.message}`);
|
|
1140
|
+
}
|
|
1141
|
+
} else {
|
|
1142
|
+
if (hasLayoutChanges) {
|
|
1143
|
+
printDiff(result.filePath, result.originalContent, result.newContent);
|
|
1144
|
+
}
|
|
1145
|
+
if (hasPackageJsonChanges) {
|
|
1146
|
+
if (hasLayoutChanges) {
|
|
1147
|
+
console.log("");
|
|
1148
|
+
}
|
|
1149
|
+
printDiff(packageJsonResult.filePath, packageJsonResult.originalContent, packageJsonResult.newContent);
|
|
1150
|
+
}
|
|
1151
|
+
if (hasLayoutChanges || hasPackageJsonChanges) {
|
|
1152
|
+
showAccuracyWarning();
|
|
1153
|
+
if (!isNonInteractive) {
|
|
1154
|
+
const confirmChanges = await prompts.confirm({
|
|
1155
|
+
message: "Apply these changes?",
|
|
1156
|
+
default: true
|
|
1157
|
+
});
|
|
1158
|
+
if (!confirmChanges) {
|
|
1159
|
+
console.log(`
|
|
883
1160
|
${pc__default.default.yellow("Changes cancelled.")}
|
|
884
1161
|
`);
|
|
885
|
-
|
|
1162
|
+
process.exit(0);
|
|
1163
|
+
}
|
|
886
1164
|
}
|
|
887
1165
|
}
|
|
888
1166
|
const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
|
|
@@ -906,9 +1184,27 @@ ${pc__default.default.red("Failed to install packages:")}`, error);
|
|
|
906
1184
|
}
|
|
907
1185
|
}
|
|
908
1186
|
}
|
|
909
|
-
|
|
910
|
-
|
|
1187
|
+
if (hasLayoutChanges) {
|
|
1188
|
+
const writeResult = applyTransform(result);
|
|
1189
|
+
if (!writeResult.success) {
|
|
1190
|
+
console.error(`
|
|
1191
|
+
${pc__default.default.red("Error:")} ${writeResult.error}`);
|
|
1192
|
+
showDocsLink();
|
|
1193
|
+
process.exit(1);
|
|
1194
|
+
}
|
|
1195
|
+
console.log(`
|
|
911
1196
|
${pc__default.default.green("Applied:")} ${result.filePath}`);
|
|
1197
|
+
}
|
|
1198
|
+
if (hasPackageJsonChanges) {
|
|
1199
|
+
const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
|
|
1200
|
+
if (!packageJsonWriteResult.success) {
|
|
1201
|
+
console.error(`
|
|
1202
|
+
${pc__default.default.red("Error:")} ${packageJsonWriteResult.error}`);
|
|
1203
|
+
showDocsLink();
|
|
1204
|
+
process.exit(1);
|
|
1205
|
+
}
|
|
1206
|
+
console.log(`${pc__default.default.green("Applied:")} ${packageJsonResult.filePath}`);
|
|
1207
|
+
}
|
|
912
1208
|
}
|
|
913
1209
|
}
|
|
914
1210
|
console.log(`
|
|
@@ -917,7 +1213,7 @@ ${pc__default.default.green("Done!")}`);
|
|
|
917
1213
|
Next steps:`);
|
|
918
1214
|
console.log(` - Start your development server`);
|
|
919
1215
|
console.log(` - Select an element to copy its context`);
|
|
920
|
-
console.log(` - Learn more at ${pc__default.default.cyan(
|
|
1216
|
+
console.log(` - Learn more at ${pc__default.default.cyan(DOCS_URL)}
|
|
921
1217
|
`);
|
|
922
1218
|
if (agentIntegration !== "none") {
|
|
923
1219
|
console.log(`${pc__default.default.magenta("\u269B")} Agent: ${pc__default.default.cyan(AGENT_NAMES[agentIntegration])}`);
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { select, confirm } from '@inquirer/prompts';
|
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import yargs from 'yargs';
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
|
-
import { writeFileSync,
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, accessSync, constants } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { detect } from '@antfu/ni';
|
|
9
9
|
import { execSync } from 'child_process';
|
|
@@ -20,21 +20,25 @@ var detectFramework = (projectRoot) => {
|
|
|
20
20
|
if (!existsSync(packageJsonPath)) {
|
|
21
21
|
return "unknown";
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
try {
|
|
24
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
25
|
+
const allDependencies = {
|
|
26
|
+
...packageJson.dependencies,
|
|
27
|
+
...packageJson.devDependencies
|
|
28
|
+
};
|
|
29
|
+
if (allDependencies["next"]) {
|
|
30
|
+
return "next";
|
|
31
|
+
}
|
|
32
|
+
if (allDependencies["vite"]) {
|
|
33
|
+
return "vite";
|
|
34
|
+
}
|
|
35
|
+
if (allDependencies["webpack"]) {
|
|
36
|
+
return "webpack";
|
|
37
|
+
}
|
|
38
|
+
return "unknown";
|
|
39
|
+
} catch {
|
|
40
|
+
return "unknown";
|
|
36
41
|
}
|
|
37
|
-
return "unknown";
|
|
38
42
|
};
|
|
39
43
|
var detectNextRouterType = (projectRoot) => {
|
|
40
44
|
const hasAppDir = existsSync(join(projectRoot, "app"));
|
|
@@ -58,9 +62,13 @@ var detectMonorepo = (projectRoot) => {
|
|
|
58
62
|
}
|
|
59
63
|
const packageJsonPath = join(projectRoot, "package.json");
|
|
60
64
|
if (existsSync(packageJsonPath)) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
try {
|
|
66
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
67
|
+
if (packageJson.workspaces) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
return false;
|
|
@@ -70,27 +78,63 @@ var detectReactGrab = (projectRoot) => {
|
|
|
70
78
|
if (!existsSync(packageJsonPath)) {
|
|
71
79
|
return false;
|
|
72
80
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
try {
|
|
82
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
83
|
+
const allDependencies = {
|
|
84
|
+
...packageJson.dependencies,
|
|
85
|
+
...packageJson.devDependencies
|
|
86
|
+
};
|
|
87
|
+
return Boolean(allDependencies["react-grab"]);
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
79
91
|
};
|
|
80
92
|
var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
|
|
93
|
+
var detectUnsupportedFramework = (projectRoot) => {
|
|
94
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
95
|
+
if (!existsSync(packageJsonPath)) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
100
|
+
const allDependencies = {
|
|
101
|
+
...packageJson.dependencies,
|
|
102
|
+
...packageJson.devDependencies
|
|
103
|
+
};
|
|
104
|
+
if (allDependencies["@remix-run/react"] || allDependencies["remix"]) {
|
|
105
|
+
return "remix";
|
|
106
|
+
}
|
|
107
|
+
if (allDependencies["astro"]) {
|
|
108
|
+
return "astro";
|
|
109
|
+
}
|
|
110
|
+
if (allDependencies["@sveltejs/kit"]) {
|
|
111
|
+
return "sveltekit";
|
|
112
|
+
}
|
|
113
|
+
if (allDependencies["gatsby"]) {
|
|
114
|
+
return "gatsby";
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
81
121
|
var detectInstalledAgents = (projectRoot) => {
|
|
82
122
|
const packageJsonPath = join(projectRoot, "package.json");
|
|
83
123
|
if (!existsSync(packageJsonPath)) {
|
|
84
124
|
return [];
|
|
85
125
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(agent) => agent.
|
|
93
|
-
|
|
126
|
+
try {
|
|
127
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
128
|
+
const allDependencies = {
|
|
129
|
+
...packageJson.dependencies,
|
|
130
|
+
...packageJson.devDependencies
|
|
131
|
+
};
|
|
132
|
+
return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
|
|
133
|
+
(agent) => agent.replace("@react-grab/", "")
|
|
134
|
+
);
|
|
135
|
+
} catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
94
138
|
};
|
|
95
139
|
var detectProject = async (projectRoot = process.cwd()) => {
|
|
96
140
|
const framework = detectFramework(projectRoot);
|
|
@@ -102,7 +146,8 @@ var detectProject = async (projectRoot = process.cwd()) => {
|
|
|
102
146
|
isMonorepo: detectMonorepo(projectRoot),
|
|
103
147
|
projectRoot,
|
|
104
148
|
hasReactGrab: detectReactGrab(projectRoot),
|
|
105
|
-
installedAgents: detectInstalledAgents(projectRoot)
|
|
149
|
+
installedAgents: detectInstalledAgents(projectRoot),
|
|
150
|
+
unsupportedFramework: detectUnsupportedFramework(projectRoot)
|
|
106
151
|
};
|
|
107
152
|
};
|
|
108
153
|
|
|
@@ -546,7 +591,7 @@ var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured)
|
|
|
546
591
|
return {
|
|
547
592
|
success: false,
|
|
548
593
|
filePath: "",
|
|
549
|
-
message:
|
|
594
|
+
message: 'Could not find pages/_document.tsx or pages/_document.jsx.\n\nTo set up React Grab with Pages Router, create pages/_document.tsx with:\n\n import { Html, Head, Main, NextScript } from "next/document";\n import Script from "next/script";\n\n export default function Document() {\n return (\n <Html>\n <Head>\n {process.env.NODE_ENV === "development" && (\n <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />\n )}\n </Head>\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }'
|
|
550
595
|
};
|
|
551
596
|
}
|
|
552
597
|
const originalContent = readFileSync(documentPath, "utf-8");
|
|
@@ -674,14 +719,134 @@ var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrab
|
|
|
674
719
|
};
|
|
675
720
|
}
|
|
676
721
|
};
|
|
722
|
+
var canWriteToFile = (filePath) => {
|
|
723
|
+
try {
|
|
724
|
+
accessSync(filePath, constants.W_OK);
|
|
725
|
+
return true;
|
|
726
|
+
} catch {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
};
|
|
677
730
|
var applyTransform = (result) => {
|
|
678
731
|
if (result.success && result.newContent && result.filePath) {
|
|
679
|
-
|
|
732
|
+
if (!canWriteToFile(result.filePath)) {
|
|
733
|
+
return {
|
|
734
|
+
success: false,
|
|
735
|
+
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
try {
|
|
739
|
+
writeFileSync(result.filePath, result.newContent);
|
|
740
|
+
return { success: true };
|
|
741
|
+
} catch (error) {
|
|
742
|
+
return {
|
|
743
|
+
success: false,
|
|
744
|
+
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
745
|
+
};
|
|
746
|
+
}
|
|
680
747
|
}
|
|
748
|
+
return { success: true };
|
|
749
|
+
};
|
|
750
|
+
var AGENT_PREFIXES = {
|
|
751
|
+
"claude-code": "npx @react-grab/claude-code@latest &&",
|
|
752
|
+
cursor: "npx @react-grab/cursor@latest &&",
|
|
753
|
+
opencode: "npx @react-grab/opencode@latest &&"
|
|
754
|
+
};
|
|
755
|
+
var previewPackageJsonTransform = (projectRoot, agent, installedAgents) => {
|
|
756
|
+
if (agent === "none") {
|
|
757
|
+
return {
|
|
758
|
+
success: true,
|
|
759
|
+
filePath: "",
|
|
760
|
+
message: "No agent selected, skipping package.json modification",
|
|
761
|
+
noChanges: true
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
765
|
+
if (!existsSync(packageJsonPath)) {
|
|
766
|
+
return {
|
|
767
|
+
success: false,
|
|
768
|
+
filePath: "",
|
|
769
|
+
message: "Could not find package.json"
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
const originalContent = readFileSync(packageJsonPath, "utf-8");
|
|
773
|
+
const agentPrefix = AGENT_PREFIXES[agent];
|
|
774
|
+
if (!agentPrefix) {
|
|
775
|
+
return {
|
|
776
|
+
success: false,
|
|
777
|
+
filePath: packageJsonPath,
|
|
778
|
+
message: `Unknown agent: ${agent}`
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
if (originalContent.includes(agentPrefix)) {
|
|
782
|
+
return {
|
|
783
|
+
success: true,
|
|
784
|
+
filePath: packageJsonPath,
|
|
785
|
+
message: `Agent ${agent} dev script is already configured`,
|
|
786
|
+
noChanges: true
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
try {
|
|
790
|
+
const packageJson = JSON.parse(originalContent);
|
|
791
|
+
if (!packageJson.scripts?.dev) {
|
|
792
|
+
return {
|
|
793
|
+
success: false,
|
|
794
|
+
filePath: packageJsonPath,
|
|
795
|
+
message: 'No "dev" script found in package.json'
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
const currentDevScript = packageJson.scripts.dev;
|
|
799
|
+
for (const installedAgent of installedAgents) {
|
|
800
|
+
const existingPrefix = AGENT_PREFIXES[installedAgent];
|
|
801
|
+
if (existingPrefix && currentDevScript.includes(existingPrefix)) {
|
|
802
|
+
return {
|
|
803
|
+
success: true,
|
|
804
|
+
filePath: packageJsonPath,
|
|
805
|
+
message: `Agent ${installedAgent} is already in dev script`,
|
|
806
|
+
noChanges: true
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
packageJson.scripts.dev = `${agentPrefix} ${currentDevScript}`;
|
|
811
|
+
const newContent = JSON.stringify(packageJson, null, 2) + "\n";
|
|
812
|
+
return {
|
|
813
|
+
success: true,
|
|
814
|
+
filePath: packageJsonPath,
|
|
815
|
+
message: `Add ${agent} server to dev script`,
|
|
816
|
+
originalContent,
|
|
817
|
+
newContent
|
|
818
|
+
};
|
|
819
|
+
} catch {
|
|
820
|
+
return {
|
|
821
|
+
success: false,
|
|
822
|
+
filePath: packageJsonPath,
|
|
823
|
+
message: "Failed to parse package.json"
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
var applyPackageJsonTransform = (result) => {
|
|
828
|
+
if (result.success && result.newContent && result.filePath) {
|
|
829
|
+
if (!canWriteToFile(result.filePath)) {
|
|
830
|
+
return {
|
|
831
|
+
success: false,
|
|
832
|
+
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
writeFileSync(result.filePath, result.newContent);
|
|
837
|
+
return { success: true };
|
|
838
|
+
} catch (error) {
|
|
839
|
+
return {
|
|
840
|
+
success: false,
|
|
841
|
+
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return { success: true };
|
|
681
846
|
};
|
|
682
847
|
|
|
683
848
|
// src/cli.ts
|
|
684
|
-
var VERSION = "0.0.
|
|
849
|
+
var VERSION = "0.0.68";
|
|
685
850
|
var FRAMEWORK_NAMES = {
|
|
686
851
|
next: "Next.js",
|
|
687
852
|
vite: "Vite",
|
|
@@ -699,43 +864,116 @@ var AGENT_NAMES = {
|
|
|
699
864
|
cursor: "Cursor",
|
|
700
865
|
opencode: "Opencode"
|
|
701
866
|
};
|
|
702
|
-
var
|
|
867
|
+
var UNSUPPORTED_FRAMEWORK_NAMES = {
|
|
868
|
+
remix: "Remix",
|
|
869
|
+
astro: "Astro",
|
|
870
|
+
sveltekit: "SvelteKit",
|
|
871
|
+
gatsby: "Gatsby"
|
|
872
|
+
};
|
|
873
|
+
var DOCS_URL = "https://github.com/aidenybai/react-grab";
|
|
703
874
|
var showDocsLink = () => {
|
|
704
875
|
console.log("\nFor manual installation instructions, visit:");
|
|
705
|
-
console.log(` ${DOCS_URL}
|
|
876
|
+
console.log(` ${pc.cyan(DOCS_URL)}
|
|
706
877
|
`);
|
|
707
878
|
};
|
|
879
|
+
var showManualInstructions = (framework, nextRouterType) => {
|
|
880
|
+
console.log(`
|
|
881
|
+
${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Manual Setup Instructions:")}
|
|
882
|
+
`);
|
|
883
|
+
if (framework === "next" && nextRouterType === "app") {
|
|
884
|
+
console.log(`${pc.bold("Next.js App Router:")}`);
|
|
885
|
+
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
886
|
+
console.log(` 2. Add to ${pc.cyan("app/layout.tsx")}:`);
|
|
887
|
+
console.log(` ${pc.gray('import Script from "next/script";')}`);
|
|
888
|
+
console.log(` ${pc.gray("// Inside <head> or after <html>:")}`);
|
|
889
|
+
console.log(` ${pc.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
|
|
890
|
+
} else if (framework === "next" && nextRouterType === "pages") {
|
|
891
|
+
console.log(`${pc.bold("Next.js Pages Router:")}`);
|
|
892
|
+
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
893
|
+
console.log(` 2. Create or edit ${pc.cyan("pages/_document.tsx")}:`);
|
|
894
|
+
console.log(` ${pc.gray('import Script from "next/script";')}`);
|
|
895
|
+
console.log(` ${pc.gray("// Inside <Head>:")}`);
|
|
896
|
+
console.log(` ${pc.gray('<Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`);
|
|
897
|
+
} else if (framework === "vite") {
|
|
898
|
+
console.log(`${pc.bold("Vite:")}`);
|
|
899
|
+
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
900
|
+
console.log(` 2. Add to ${pc.cyan("index.html")} inside <head>:`);
|
|
901
|
+
console.log(` ${pc.gray('<script type="module">')}`);
|
|
902
|
+
console.log(` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`);
|
|
903
|
+
console.log(` ${pc.gray("</script>")}`);
|
|
904
|
+
} else if (framework === "webpack") {
|
|
905
|
+
console.log(`${pc.bold("Webpack:")}`);
|
|
906
|
+
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
907
|
+
console.log(` 2. Add to your entry file (e.g., ${pc.cyan("src/index.tsx")}):`);
|
|
908
|
+
console.log(` ${pc.gray('if (process.env.NODE_ENV === "development") {')}`);
|
|
909
|
+
console.log(` ${pc.gray(' import("react-grab");')}`);
|
|
910
|
+
console.log(` ${pc.gray("}")}`);
|
|
911
|
+
} else {
|
|
912
|
+
console.log(`${pc.bold("Generic Setup:")}`);
|
|
913
|
+
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
914
|
+
console.log(` 2. Add the script to your HTML or entry file.`);
|
|
915
|
+
console.log(` 3. See docs for framework-specific instructions.`);
|
|
916
|
+
}
|
|
917
|
+
showDocsLink();
|
|
918
|
+
};
|
|
919
|
+
var showAccuracyWarning = () => {
|
|
920
|
+
console.log(`
|
|
921
|
+
${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Auto-detection may not be 100% accurate.")}`);
|
|
922
|
+
console.log(`${pc.yellow(" Please verify the changes in your file before committing.")}`);
|
|
923
|
+
};
|
|
708
924
|
var parseArgs = async () => {
|
|
709
|
-
const argv = await yargs(hideBin(process.argv)).
|
|
925
|
+
const argv = await yargs(hideBin(process.argv)).scriptName("react-grab").usage(
|
|
926
|
+
`${pc.magenta("\u269B")} ${pc.bold("React Grab CLI")} ${pc.gray(`v${VERSION}`)}
|
|
927
|
+
|
|
928
|
+
${pc.cyan("Usage:")} $0 [options]
|
|
929
|
+
|
|
930
|
+
React Grab adds AI-powered context selection to your React application,
|
|
931
|
+
allowing you to select components and copy their context for AI assistants.
|
|
932
|
+
|
|
933
|
+
The CLI auto-detects your project configuration (framework, package manager,
|
|
934
|
+
router type) and installs React Grab with optional agent integrations.`
|
|
935
|
+
).option("framework", {
|
|
710
936
|
alias: "f",
|
|
711
937
|
type: "string",
|
|
712
938
|
choices: ["next", "vite", "webpack"],
|
|
713
|
-
description: "
|
|
939
|
+
description: "Override detected framework"
|
|
714
940
|
}).option("package-manager", {
|
|
715
941
|
alias: "p",
|
|
716
942
|
type: "string",
|
|
717
943
|
choices: ["npm", "yarn", "pnpm", "bun"],
|
|
718
|
-
description: "
|
|
944
|
+
description: "Override detected package manager"
|
|
719
945
|
}).option("router", {
|
|
720
946
|
alias: "r",
|
|
721
947
|
type: "string",
|
|
722
948
|
choices: ["app", "pages"],
|
|
723
|
-
description: "Next.js router type (
|
|
949
|
+
description: "Next.js router type (only for Next.js projects)"
|
|
724
950
|
}).option("agent", {
|
|
725
951
|
alias: "a",
|
|
726
952
|
type: "string",
|
|
727
953
|
choices: ["claude-code", "cursor", "opencode", "none"],
|
|
728
|
-
description: "Agent integration to
|
|
954
|
+
description: "Agent integration to automatically forward selected elements to agent instead of copying to clipboard"
|
|
729
955
|
}).option("yes", {
|
|
730
956
|
alias: "y",
|
|
731
957
|
type: "boolean",
|
|
732
958
|
default: false,
|
|
733
|
-
description: "Skip all
|
|
959
|
+
description: "Skip all prompts and use auto-detected/default values"
|
|
734
960
|
}).option("skip-install", {
|
|
735
961
|
type: "boolean",
|
|
736
962
|
default: false,
|
|
737
|
-
description: "
|
|
738
|
-
}).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup").example("$0 -y", "Auto-detect and install without prompts").example("$0 -f next -r app
|
|
963
|
+
description: "Only modify config files, skip npm/yarn/pnpm install"
|
|
964
|
+
}).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup with prompts").example("$0 -y", "Auto-detect everything and install without prompts").example("$0 -f next -r app", "Configure for Next.js App Router").example("$0 -a cursor -y", "Add Cursor agent integration non-interactively").example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent").example("$0 --skip-install", "Only modify files, install packages manually").epilog(
|
|
965
|
+
`${pc.bold("Agent Integrations:")}
|
|
966
|
+
${pc.cyan("claude-code")} Connect React Grab to Claude Code
|
|
967
|
+
${pc.cyan("cursor")} Connect React Grab to Cursor IDE
|
|
968
|
+
${pc.cyan("opencode")} Connect React Grab to Opencode
|
|
969
|
+
|
|
970
|
+
${pc.bold("Supported Frameworks:")}
|
|
971
|
+
${pc.cyan("next")} Next.js (App Router & Pages Router)
|
|
972
|
+
${pc.cyan("vite")} Vite-based React projects
|
|
973
|
+
${pc.cyan("webpack")} Webpack-based React projects
|
|
974
|
+
|
|
975
|
+
${pc.bold("Documentation:")} ${pc.underline(DOCS_URL)}`
|
|
976
|
+
).wrap(Math.min(100, process.stdout.columns || 80)).parse();
|
|
739
977
|
return {
|
|
740
978
|
framework: argv.framework,
|
|
741
979
|
packageManager: argv["package-manager"],
|
|
@@ -762,6 +1000,20 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
|
|
|
762
1000
|
console.log(`- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`);
|
|
763
1001
|
}
|
|
764
1002
|
console.log("");
|
|
1003
|
+
if (projectInfo.unsupportedFramework) {
|
|
1004
|
+
const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
|
|
1005
|
+
console.log(`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow(`Detected ${frameworkName} - this framework requires manual setup.`)}`);
|
|
1006
|
+
console.log(`${pc.yellow(" React Grab may not work correctly with auto-configuration.")}
|
|
1007
|
+
`);
|
|
1008
|
+
showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
|
|
1009
|
+
process.exit(0);
|
|
1010
|
+
}
|
|
1011
|
+
if (projectInfo.framework === "unknown") {
|
|
1012
|
+
console.log(`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
|
|
1013
|
+
`);
|
|
1014
|
+
showManualInstructions("unknown");
|
|
1015
|
+
process.exit(0);
|
|
1016
|
+
}
|
|
765
1017
|
let action = "install-all";
|
|
766
1018
|
if (projectInfo.hasReactGrab && !isNonInteractive) {
|
|
767
1019
|
action = await select({
|
|
@@ -774,6 +1026,11 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
|
|
|
774
1026
|
});
|
|
775
1027
|
} else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
|
|
776
1028
|
action = "add-agent";
|
|
1029
|
+
} else if (projectInfo.hasReactGrab && isNonInteractive && !args.agent) {
|
|
1030
|
+
console.log(`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("React Grab is already installed.")}`);
|
|
1031
|
+
console.log(`${pc.yellow(" Use --agent to add an agent, or run without -y for interactive mode.")}
|
|
1032
|
+
`);
|
|
1033
|
+
action = "reconfigure";
|
|
777
1034
|
}
|
|
778
1035
|
let finalFramework = args.framework || projectInfo.framework;
|
|
779
1036
|
let finalPackageManager = args.packageManager || projectInfo.packageManager;
|
|
@@ -857,25 +1114,46 @@ ${pc.magenta("\u269B")} Previewing changes...
|
|
|
857
1114
|
agentIntegration,
|
|
858
1115
|
projectInfo.hasReactGrab || action === "add-agent"
|
|
859
1116
|
);
|
|
1117
|
+
const packageJsonResult = previewPackageJsonTransform(
|
|
1118
|
+
projectInfo.projectRoot,
|
|
1119
|
+
agentIntegration,
|
|
1120
|
+
projectInfo.installedAgents
|
|
1121
|
+
);
|
|
860
1122
|
if (!result.success) {
|
|
861
1123
|
console.error(`${pc.red("Error:")} ${result.message}`);
|
|
862
1124
|
showDocsLink();
|
|
863
1125
|
process.exit(1);
|
|
864
1126
|
}
|
|
865
|
-
|
|
1127
|
+
const hasLayoutChanges = !result.noChanges && result.originalContent && result.newContent;
|
|
1128
|
+
const hasPackageJsonChanges = packageJsonResult.success && !packageJsonResult.noChanges && packageJsonResult.originalContent && packageJsonResult.newContent;
|
|
1129
|
+
if (result.noChanges && packageJsonResult.noChanges) {
|
|
866
1130
|
console.log(`${pc.cyan("Info:")} ${result.message}`);
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1131
|
+
if (packageJsonResult.message) {
|
|
1132
|
+
console.log(`${pc.cyan("Info:")} ${packageJsonResult.message}`);
|
|
1133
|
+
}
|
|
1134
|
+
} else {
|
|
1135
|
+
if (hasLayoutChanges) {
|
|
1136
|
+
printDiff(result.filePath, result.originalContent, result.newContent);
|
|
1137
|
+
}
|
|
1138
|
+
if (hasPackageJsonChanges) {
|
|
1139
|
+
if (hasLayoutChanges) {
|
|
1140
|
+
console.log("");
|
|
1141
|
+
}
|
|
1142
|
+
printDiff(packageJsonResult.filePath, packageJsonResult.originalContent, packageJsonResult.newContent);
|
|
1143
|
+
}
|
|
1144
|
+
if (hasLayoutChanges || hasPackageJsonChanges) {
|
|
1145
|
+
showAccuracyWarning();
|
|
1146
|
+
if (!isNonInteractive) {
|
|
1147
|
+
const confirmChanges = await confirm({
|
|
1148
|
+
message: "Apply these changes?",
|
|
1149
|
+
default: true
|
|
1150
|
+
});
|
|
1151
|
+
if (!confirmChanges) {
|
|
1152
|
+
console.log(`
|
|
876
1153
|
${pc.yellow("Changes cancelled.")}
|
|
877
1154
|
`);
|
|
878
|
-
|
|
1155
|
+
process.exit(0);
|
|
1156
|
+
}
|
|
879
1157
|
}
|
|
880
1158
|
}
|
|
881
1159
|
const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
|
|
@@ -899,9 +1177,27 @@ ${pc.red("Failed to install packages:")}`, error);
|
|
|
899
1177
|
}
|
|
900
1178
|
}
|
|
901
1179
|
}
|
|
902
|
-
|
|
903
|
-
|
|
1180
|
+
if (hasLayoutChanges) {
|
|
1181
|
+
const writeResult = applyTransform(result);
|
|
1182
|
+
if (!writeResult.success) {
|
|
1183
|
+
console.error(`
|
|
1184
|
+
${pc.red("Error:")} ${writeResult.error}`);
|
|
1185
|
+
showDocsLink();
|
|
1186
|
+
process.exit(1);
|
|
1187
|
+
}
|
|
1188
|
+
console.log(`
|
|
904
1189
|
${pc.green("Applied:")} ${result.filePath}`);
|
|
1190
|
+
}
|
|
1191
|
+
if (hasPackageJsonChanges) {
|
|
1192
|
+
const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
|
|
1193
|
+
if (!packageJsonWriteResult.success) {
|
|
1194
|
+
console.error(`
|
|
1195
|
+
${pc.red("Error:")} ${packageJsonWriteResult.error}`);
|
|
1196
|
+
showDocsLink();
|
|
1197
|
+
process.exit(1);
|
|
1198
|
+
}
|
|
1199
|
+
console.log(`${pc.green("Applied:")} ${packageJsonResult.filePath}`);
|
|
1200
|
+
}
|
|
905
1201
|
}
|
|
906
1202
|
}
|
|
907
1203
|
console.log(`
|
|
@@ -910,7 +1206,7 @@ ${pc.green("Done!")}`);
|
|
|
910
1206
|
Next steps:`);
|
|
911
1207
|
console.log(` - Start your development server`);
|
|
912
1208
|
console.log(` - Select an element to copy its context`);
|
|
913
|
-
console.log(` - Learn more at ${pc.cyan(
|
|
1209
|
+
console.log(` - Learn more at ${pc.cyan(DOCS_URL)}
|
|
914
1210
|
`);
|
|
915
1211
|
if (agentIntegration !== "none") {
|
|
916
1212
|
console.log(`${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`);
|