@react-grab/cli 0.0.68
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/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/cli.cjs +934 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +927 -0
- package/package.json +35 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,927 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import yargs from 'yargs';
|
|
5
|
+
import { hideBin } from 'yargs/helpers';
|
|
6
|
+
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { detect } from '@antfu/ni';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
|
|
11
|
+
var detectPackageManager = async (projectRoot) => {
|
|
12
|
+
const detected = await detect({ cwd: projectRoot });
|
|
13
|
+
if (detected && ["npm", "yarn", "pnpm", "bun"].includes(detected)) {
|
|
14
|
+
return detected;
|
|
15
|
+
}
|
|
16
|
+
return "npm";
|
|
17
|
+
};
|
|
18
|
+
var detectFramework = (projectRoot) => {
|
|
19
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
20
|
+
if (!existsSync(packageJsonPath)) {
|
|
21
|
+
return "unknown";
|
|
22
|
+
}
|
|
23
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
24
|
+
const allDependencies = {
|
|
25
|
+
...packageJson.dependencies,
|
|
26
|
+
...packageJson.devDependencies
|
|
27
|
+
};
|
|
28
|
+
if (allDependencies["next"]) {
|
|
29
|
+
return "next";
|
|
30
|
+
}
|
|
31
|
+
if (allDependencies["vite"]) {
|
|
32
|
+
return "vite";
|
|
33
|
+
}
|
|
34
|
+
if (allDependencies["webpack"]) {
|
|
35
|
+
return "webpack";
|
|
36
|
+
}
|
|
37
|
+
return "unknown";
|
|
38
|
+
};
|
|
39
|
+
var detectNextRouterType = (projectRoot) => {
|
|
40
|
+
const hasAppDir = existsSync(join(projectRoot, "app"));
|
|
41
|
+
const hasSrcAppDir = existsSync(join(projectRoot, "src", "app"));
|
|
42
|
+
const hasPagesDir = existsSync(join(projectRoot, "pages"));
|
|
43
|
+
const hasSrcPagesDir = existsSync(join(projectRoot, "src", "pages"));
|
|
44
|
+
if (hasAppDir || hasSrcAppDir) {
|
|
45
|
+
return "app";
|
|
46
|
+
}
|
|
47
|
+
if (hasPagesDir || hasSrcPagesDir) {
|
|
48
|
+
return "pages";
|
|
49
|
+
}
|
|
50
|
+
return "unknown";
|
|
51
|
+
};
|
|
52
|
+
var detectMonorepo = (projectRoot) => {
|
|
53
|
+
if (existsSync(join(projectRoot, "pnpm-workspace.yaml"))) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (existsSync(join(projectRoot, "lerna.json"))) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
60
|
+
if (existsSync(packageJsonPath)) {
|
|
61
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
62
|
+
if (packageJson.workspaces) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
};
|
|
68
|
+
var detectReactGrab = (projectRoot) => {
|
|
69
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
70
|
+
if (!existsSync(packageJsonPath)) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
74
|
+
const allDependencies = {
|
|
75
|
+
...packageJson.dependencies,
|
|
76
|
+
...packageJson.devDependencies
|
|
77
|
+
};
|
|
78
|
+
return Boolean(allDependencies["react-grab"]);
|
|
79
|
+
};
|
|
80
|
+
var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
|
|
81
|
+
var detectInstalledAgents = (projectRoot) => {
|
|
82
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
83
|
+
if (!existsSync(packageJsonPath)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
87
|
+
const allDependencies = {
|
|
88
|
+
...packageJson.dependencies,
|
|
89
|
+
...packageJson.devDependencies
|
|
90
|
+
};
|
|
91
|
+
return AGENT_PACKAGES.filter((agent) => Boolean(allDependencies[agent])).map(
|
|
92
|
+
(agent) => agent.replace("@react-grab/", "")
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
var detectProject = async (projectRoot = process.cwd()) => {
|
|
96
|
+
const framework = detectFramework(projectRoot);
|
|
97
|
+
const packageManager = await detectPackageManager(projectRoot);
|
|
98
|
+
return {
|
|
99
|
+
packageManager,
|
|
100
|
+
framework,
|
|
101
|
+
nextRouterType: framework === "next" ? detectNextRouterType(projectRoot) : "unknown",
|
|
102
|
+
isMonorepo: detectMonorepo(projectRoot),
|
|
103
|
+
projectRoot,
|
|
104
|
+
hasReactGrab: detectReactGrab(projectRoot),
|
|
105
|
+
installedAgents: detectInstalledAgents(projectRoot)
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/diff.ts
|
|
110
|
+
var RED = "\x1B[31m";
|
|
111
|
+
var GREEN = "\x1B[32m";
|
|
112
|
+
var GRAY = "\x1B[90m";
|
|
113
|
+
var RESET = "\x1B[0m";
|
|
114
|
+
var BOLD = "\x1B[1m";
|
|
115
|
+
var generateDiff = (originalContent, newContent) => {
|
|
116
|
+
const originalLines = originalContent.split("\n");
|
|
117
|
+
const newLines = newContent.split("\n");
|
|
118
|
+
const diff = [];
|
|
119
|
+
Math.max(originalLines.length, newLines.length);
|
|
120
|
+
let originalIndex = 0;
|
|
121
|
+
let newIndex = 0;
|
|
122
|
+
while (originalIndex < originalLines.length || newIndex < newLines.length) {
|
|
123
|
+
const originalLine = originalLines[originalIndex];
|
|
124
|
+
const newLine = newLines[newIndex];
|
|
125
|
+
if (originalLine === newLine) {
|
|
126
|
+
diff.push({ type: "unchanged", content: originalLine, lineNumber: newIndex + 1 });
|
|
127
|
+
originalIndex++;
|
|
128
|
+
newIndex++;
|
|
129
|
+
} else if (originalLine === void 0) {
|
|
130
|
+
diff.push({ type: "added", content: newLine, lineNumber: newIndex + 1 });
|
|
131
|
+
newIndex++;
|
|
132
|
+
} else if (newLine === void 0) {
|
|
133
|
+
diff.push({ type: "removed", content: originalLine });
|
|
134
|
+
originalIndex++;
|
|
135
|
+
} else {
|
|
136
|
+
const originalInNew = newLines.indexOf(originalLine, newIndex);
|
|
137
|
+
const newInOriginal = originalLines.indexOf(newLine, originalIndex);
|
|
138
|
+
if (originalInNew !== -1 && (newInOriginal === -1 || originalInNew - newIndex < newInOriginal - originalIndex)) {
|
|
139
|
+
while (newIndex < originalInNew) {
|
|
140
|
+
diff.push({ type: "added", content: newLines[newIndex], lineNumber: newIndex + 1 });
|
|
141
|
+
newIndex++;
|
|
142
|
+
}
|
|
143
|
+
} else if (newInOriginal !== -1) {
|
|
144
|
+
while (originalIndex < newInOriginal) {
|
|
145
|
+
diff.push({ type: "removed", content: originalLines[originalIndex] });
|
|
146
|
+
originalIndex++;
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
diff.push({ type: "removed", content: originalLine });
|
|
150
|
+
diff.push({ type: "added", content: newLine, lineNumber: newIndex + 1 });
|
|
151
|
+
originalIndex++;
|
|
152
|
+
newIndex++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return diff;
|
|
157
|
+
};
|
|
158
|
+
var formatDiff = (diff, contextLines = 3) => {
|
|
159
|
+
const lines = [];
|
|
160
|
+
let lastPrintedIndex = -1;
|
|
161
|
+
let hasChanges = false;
|
|
162
|
+
const changedIndices = diff.map((line, index) => line.type !== "unchanged" ? index : -1).filter((index) => index !== -1);
|
|
163
|
+
if (changedIndices.length === 0) {
|
|
164
|
+
return `${GRAY}No changes${RESET}`;
|
|
165
|
+
}
|
|
166
|
+
for (const changedIndex of changedIndices) {
|
|
167
|
+
const startContext = Math.max(0, changedIndex - contextLines);
|
|
168
|
+
const endContext = Math.min(diff.length - 1, changedIndex + contextLines);
|
|
169
|
+
if (startContext > lastPrintedIndex + 1 && lastPrintedIndex !== -1) {
|
|
170
|
+
lines.push(`${GRAY} ...${RESET}`);
|
|
171
|
+
}
|
|
172
|
+
for (let lineIndex = Math.max(startContext, lastPrintedIndex + 1); lineIndex <= endContext; lineIndex++) {
|
|
173
|
+
const diffLine = diff[lineIndex];
|
|
174
|
+
if (diffLine.type === "added") {
|
|
175
|
+
lines.push(`${GREEN}+ ${diffLine.content}${RESET}`);
|
|
176
|
+
hasChanges = true;
|
|
177
|
+
} else if (diffLine.type === "removed") {
|
|
178
|
+
lines.push(`${RED}- ${diffLine.content}${RESET}`);
|
|
179
|
+
hasChanges = true;
|
|
180
|
+
} else {
|
|
181
|
+
lines.push(`${GRAY} ${diffLine.content}${RESET}`);
|
|
182
|
+
}
|
|
183
|
+
lastPrintedIndex = lineIndex;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return hasChanges ? lines.join("\n") : `${GRAY}No changes${RESET}`;
|
|
187
|
+
};
|
|
188
|
+
var printDiff = (filePath, originalContent, newContent) => {
|
|
189
|
+
console.log(`
|
|
190
|
+
${BOLD}File: ${filePath}${RESET}`);
|
|
191
|
+
console.log("\u2500".repeat(60));
|
|
192
|
+
const diff = generateDiff(originalContent, newContent);
|
|
193
|
+
console.log(formatDiff(diff));
|
|
194
|
+
console.log("\u2500".repeat(60));
|
|
195
|
+
};
|
|
196
|
+
var INSTALL_COMMANDS = {
|
|
197
|
+
npm: "npm install",
|
|
198
|
+
yarn: "yarn add",
|
|
199
|
+
pnpm: "pnpm add",
|
|
200
|
+
bun: "bun add"
|
|
201
|
+
};
|
|
202
|
+
var installPackages = (packages, packageManager, projectRoot, isDev = true) => {
|
|
203
|
+
if (packages.length === 0) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const command = INSTALL_COMMANDS[packageManager];
|
|
207
|
+
const devFlag = isDev ? " -D" : "";
|
|
208
|
+
const fullCommand = `${command}${devFlag} ${packages.join(" ")}`;
|
|
209
|
+
console.log(`Running: ${fullCommand}
|
|
210
|
+
`);
|
|
211
|
+
execSync(fullCommand, {
|
|
212
|
+
cwd: projectRoot,
|
|
213
|
+
stdio: "inherit"
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
var getPackagesToInstall = (agent, includeReactGrab = true) => {
|
|
217
|
+
const packages = [];
|
|
218
|
+
if (includeReactGrab) {
|
|
219
|
+
packages.push("react-grab");
|
|
220
|
+
}
|
|
221
|
+
if (agent !== "none") {
|
|
222
|
+
packages.push(`@react-grab/${agent}`);
|
|
223
|
+
}
|
|
224
|
+
return packages;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/templates.ts
|
|
228
|
+
var NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
|
|
229
|
+
<Script
|
|
230
|
+
src="//unpkg.com/react-grab/dist/index.global.js"
|
|
231
|
+
crossOrigin="anonymous"
|
|
232
|
+
strategy="beforeInteractive"
|
|
233
|
+
/>
|
|
234
|
+
)}`;
|
|
235
|
+
var NEXT_APP_ROUTER_SCRIPT_WITH_AGENT = (agent) => {
|
|
236
|
+
if (agent === "none") return NEXT_APP_ROUTER_SCRIPT;
|
|
237
|
+
const agentScript = `<Script
|
|
238
|
+
src="//unpkg.com/@react-grab/${agent}/dist/client.global.js"
|
|
239
|
+
strategy="lazyOnload"
|
|
240
|
+
/>`;
|
|
241
|
+
return `{process.env.NODE_ENV === "development" && (
|
|
242
|
+
<>
|
|
243
|
+
<Script
|
|
244
|
+
src="//unpkg.com/react-grab/dist/index.global.js"
|
|
245
|
+
strategy="beforeInteractive"
|
|
246
|
+
/>
|
|
247
|
+
${agentScript}
|
|
248
|
+
</>
|
|
249
|
+
)}`;
|
|
250
|
+
};
|
|
251
|
+
var NEXT_PAGES_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
|
|
252
|
+
<Script
|
|
253
|
+
src="//unpkg.com/react-grab/dist/index.global.js"
|
|
254
|
+
crossOrigin="anonymous"
|
|
255
|
+
strategy="beforeInteractive"
|
|
256
|
+
/>
|
|
257
|
+
)}`;
|
|
258
|
+
var NEXT_PAGES_ROUTER_SCRIPT_WITH_AGENT = (agent) => {
|
|
259
|
+
if (agent === "none") return NEXT_PAGES_ROUTER_SCRIPT;
|
|
260
|
+
const agentScript = `<Script
|
|
261
|
+
src="//unpkg.com/@react-grab/${agent}/dist/client.global.js"
|
|
262
|
+
strategy="lazyOnload"
|
|
263
|
+
/>`;
|
|
264
|
+
return `{process.env.NODE_ENV === "development" && (
|
|
265
|
+
<>
|
|
266
|
+
<Script
|
|
267
|
+
src="//unpkg.com/react-grab/dist/index.global.js"
|
|
268
|
+
strategy="beforeInteractive"
|
|
269
|
+
/>
|
|
270
|
+
${agentScript}
|
|
271
|
+
</>
|
|
272
|
+
)}`;
|
|
273
|
+
};
|
|
274
|
+
var VITE_SCRIPT = `<script type="module">
|
|
275
|
+
if (import.meta.env.DEV) {
|
|
276
|
+
import("react-grab");
|
|
277
|
+
}
|
|
278
|
+
</script>`;
|
|
279
|
+
var VITE_SCRIPT_WITH_AGENT = (agent) => {
|
|
280
|
+
if (agent === "none") return VITE_SCRIPT;
|
|
281
|
+
return `<script type="module">
|
|
282
|
+
if (import.meta.env.DEV) {
|
|
283
|
+
import("react-grab");
|
|
284
|
+
import("@react-grab/${agent}/client");
|
|
285
|
+
}
|
|
286
|
+
</script>`;
|
|
287
|
+
};
|
|
288
|
+
var WEBPACK_IMPORT = `if (process.env.NODE_ENV === "development") {
|
|
289
|
+
import("react-grab");
|
|
290
|
+
}`;
|
|
291
|
+
var WEBPACK_IMPORT_WITH_AGENT = (agent) => {
|
|
292
|
+
if (agent === "none") return WEBPACK_IMPORT;
|
|
293
|
+
return `if (process.env.NODE_ENV === "development") {
|
|
294
|
+
import("react-grab");
|
|
295
|
+
import("@react-grab/${agent}/client");
|
|
296
|
+
}`;
|
|
297
|
+
};
|
|
298
|
+
var SCRIPT_IMPORT = 'import Script from "next/script";';
|
|
299
|
+
|
|
300
|
+
// src/transform.ts
|
|
301
|
+
var findLayoutFile = (projectRoot) => {
|
|
302
|
+
const possiblePaths = [
|
|
303
|
+
join(projectRoot, "app", "layout.tsx"),
|
|
304
|
+
join(projectRoot, "app", "layout.jsx"),
|
|
305
|
+
join(projectRoot, "src", "app", "layout.tsx"),
|
|
306
|
+
join(projectRoot, "src", "app", "layout.jsx")
|
|
307
|
+
];
|
|
308
|
+
for (const filePath of possiblePaths) {
|
|
309
|
+
if (existsSync(filePath)) {
|
|
310
|
+
return filePath;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
};
|
|
315
|
+
var findDocumentFile = (projectRoot) => {
|
|
316
|
+
const possiblePaths = [
|
|
317
|
+
join(projectRoot, "pages", "_document.tsx"),
|
|
318
|
+
join(projectRoot, "pages", "_document.jsx"),
|
|
319
|
+
join(projectRoot, "src", "pages", "_document.tsx"),
|
|
320
|
+
join(projectRoot, "src", "pages", "_document.jsx")
|
|
321
|
+
];
|
|
322
|
+
for (const filePath of possiblePaths) {
|
|
323
|
+
if (existsSync(filePath)) {
|
|
324
|
+
return filePath;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
};
|
|
329
|
+
var findIndexHtml = (projectRoot) => {
|
|
330
|
+
const possiblePaths = [
|
|
331
|
+
join(projectRoot, "index.html"),
|
|
332
|
+
join(projectRoot, "public", "index.html")
|
|
333
|
+
];
|
|
334
|
+
for (const filePath of possiblePaths) {
|
|
335
|
+
if (existsSync(filePath)) {
|
|
336
|
+
return filePath;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
};
|
|
341
|
+
var findEntryFile = (projectRoot) => {
|
|
342
|
+
const possiblePaths = [
|
|
343
|
+
join(projectRoot, "src", "index.tsx"),
|
|
344
|
+
join(projectRoot, "src", "index.jsx"),
|
|
345
|
+
join(projectRoot, "src", "index.ts"),
|
|
346
|
+
join(projectRoot, "src", "index.js"),
|
|
347
|
+
join(projectRoot, "src", "main.tsx"),
|
|
348
|
+
join(projectRoot, "src", "main.jsx"),
|
|
349
|
+
join(projectRoot, "src", "main.ts"),
|
|
350
|
+
join(projectRoot, "src", "main.js")
|
|
351
|
+
];
|
|
352
|
+
for (const filePath of possiblePaths) {
|
|
353
|
+
if (existsSync(filePath)) {
|
|
354
|
+
return filePath;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
};
|
|
359
|
+
var addAgentToExistingNextApp = (originalContent, agent, filePath) => {
|
|
360
|
+
if (agent === "none") {
|
|
361
|
+
return {
|
|
362
|
+
success: true,
|
|
363
|
+
filePath,
|
|
364
|
+
message: "React Grab is already configured",
|
|
365
|
+
noChanges: true
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const agentPackage = `@react-grab/${agent}`;
|
|
369
|
+
if (originalContent.includes(agentPackage)) {
|
|
370
|
+
return {
|
|
371
|
+
success: true,
|
|
372
|
+
filePath,
|
|
373
|
+
message: `Agent ${agent} is already configured`,
|
|
374
|
+
noChanges: true
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const agentScript = `<Script
|
|
378
|
+
src="//unpkg.com/${agentPackage}/dist/client.global.js"
|
|
379
|
+
strategy="lazyOnload"
|
|
380
|
+
/>`;
|
|
381
|
+
const reactGrabScriptMatch = originalContent.match(
|
|
382
|
+
/<Script[^>]*src="[^"]*react-grab[^"]*"[^>]*\/?>/
|
|
383
|
+
);
|
|
384
|
+
if (reactGrabScriptMatch) {
|
|
385
|
+
const newContent = originalContent.replace(
|
|
386
|
+
reactGrabScriptMatch[0],
|
|
387
|
+
`${reactGrabScriptMatch[0]}
|
|
388
|
+
${agentScript}`
|
|
389
|
+
);
|
|
390
|
+
return {
|
|
391
|
+
success: true,
|
|
392
|
+
filePath,
|
|
393
|
+
message: `Add ${agent} agent`,
|
|
394
|
+
originalContent,
|
|
395
|
+
newContent
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
success: false,
|
|
400
|
+
filePath,
|
|
401
|
+
message: "Could not find React Grab script to add agent after"
|
|
402
|
+
};
|
|
403
|
+
};
|
|
404
|
+
var addAgentToExistingVite = (originalContent, agent, filePath) => {
|
|
405
|
+
if (agent === "none") {
|
|
406
|
+
return {
|
|
407
|
+
success: true,
|
|
408
|
+
filePath,
|
|
409
|
+
message: "React Grab is already configured",
|
|
410
|
+
noChanges: true
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const agentPackage = `@react-grab/${agent}`;
|
|
414
|
+
if (originalContent.includes(agentPackage)) {
|
|
415
|
+
return {
|
|
416
|
+
success: true,
|
|
417
|
+
filePath,
|
|
418
|
+
message: `Agent ${agent} is already configured`,
|
|
419
|
+
noChanges: true
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const agentImport = `import("${agentPackage}/client");`;
|
|
423
|
+
const reactGrabImportMatch = originalContent.match(/import\s*\(\s*["']react-grab["']\s*\)/);
|
|
424
|
+
if (reactGrabImportMatch) {
|
|
425
|
+
const newContent = originalContent.replace(
|
|
426
|
+
reactGrabImportMatch[0],
|
|
427
|
+
`${reactGrabImportMatch[0]};
|
|
428
|
+
${agentImport}`
|
|
429
|
+
);
|
|
430
|
+
return {
|
|
431
|
+
success: true,
|
|
432
|
+
filePath,
|
|
433
|
+
message: `Add ${agent} agent`,
|
|
434
|
+
originalContent,
|
|
435
|
+
newContent
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
success: false,
|
|
440
|
+
filePath,
|
|
441
|
+
message: "Could not find React Grab import to add agent after"
|
|
442
|
+
};
|
|
443
|
+
};
|
|
444
|
+
var addAgentToExistingWebpack = (originalContent, agent, filePath) => {
|
|
445
|
+
if (agent === "none") {
|
|
446
|
+
return {
|
|
447
|
+
success: true,
|
|
448
|
+
filePath,
|
|
449
|
+
message: "React Grab is already configured",
|
|
450
|
+
noChanges: true
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
const agentPackage = `@react-grab/${agent}`;
|
|
454
|
+
if (originalContent.includes(agentPackage)) {
|
|
455
|
+
return {
|
|
456
|
+
success: true,
|
|
457
|
+
filePath,
|
|
458
|
+
message: `Agent ${agent} is already configured`,
|
|
459
|
+
noChanges: true
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
const agentImport = `import("${agentPackage}/client");`;
|
|
463
|
+
const reactGrabImportMatch = originalContent.match(/import\s*\(\s*["']react-grab["']\s*\)/);
|
|
464
|
+
if (reactGrabImportMatch) {
|
|
465
|
+
const newContent = originalContent.replace(
|
|
466
|
+
reactGrabImportMatch[0],
|
|
467
|
+
`${reactGrabImportMatch[0]};
|
|
468
|
+
${agentImport}`
|
|
469
|
+
);
|
|
470
|
+
return {
|
|
471
|
+
success: true,
|
|
472
|
+
filePath,
|
|
473
|
+
message: `Add ${agent} agent`,
|
|
474
|
+
originalContent,
|
|
475
|
+
newContent
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
success: false,
|
|
480
|
+
filePath,
|
|
481
|
+
message: "Could not find React Grab import to add agent after"
|
|
482
|
+
};
|
|
483
|
+
};
|
|
484
|
+
var transformNextAppRouter = (projectRoot, agent, reactGrabAlreadyConfigured) => {
|
|
485
|
+
const layoutPath = findLayoutFile(projectRoot);
|
|
486
|
+
if (!layoutPath) {
|
|
487
|
+
return {
|
|
488
|
+
success: false,
|
|
489
|
+
filePath: "",
|
|
490
|
+
message: "Could not find app/layout.tsx or app/layout.jsx"
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const originalContent = readFileSync(layoutPath, "utf-8");
|
|
494
|
+
let newContent = originalContent;
|
|
495
|
+
const hasReactGrabInFile = originalContent.includes("react-grab");
|
|
496
|
+
if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
|
|
497
|
+
return addAgentToExistingNextApp(originalContent, agent, layoutPath);
|
|
498
|
+
}
|
|
499
|
+
if (hasReactGrabInFile) {
|
|
500
|
+
return {
|
|
501
|
+
success: true,
|
|
502
|
+
filePath: layoutPath,
|
|
503
|
+
message: "React Grab is already installed in this file",
|
|
504
|
+
noChanges: true
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
if (!newContent.includes('import Script from "next/script"')) {
|
|
508
|
+
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
509
|
+
if (importMatch) {
|
|
510
|
+
newContent = newContent.replace(importMatch[0], `${importMatch[0]}
|
|
511
|
+
${SCRIPT_IMPORT}`);
|
|
512
|
+
} else {
|
|
513
|
+
newContent = `${SCRIPT_IMPORT}
|
|
514
|
+
|
|
515
|
+
${newContent}`;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const scriptBlock = NEXT_APP_ROUTER_SCRIPT_WITH_AGENT(agent);
|
|
519
|
+
const headMatch = newContent.match(/<head[^>]*>/);
|
|
520
|
+
if (headMatch) {
|
|
521
|
+
newContent = newContent.replace(headMatch[0], `${headMatch[0]}
|
|
522
|
+
${scriptBlock}`);
|
|
523
|
+
} else {
|
|
524
|
+
const htmlMatch = newContent.match(/<html[^>]*>/);
|
|
525
|
+
if (htmlMatch) {
|
|
526
|
+
newContent = newContent.replace(
|
|
527
|
+
htmlMatch[0],
|
|
528
|
+
`${htmlMatch[0]}
|
|
529
|
+
<head>
|
|
530
|
+
${scriptBlock}
|
|
531
|
+
</head>`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
success: true,
|
|
537
|
+
filePath: layoutPath,
|
|
538
|
+
message: "Add React Grab" + (agent !== "none" ? ` with ${agent} agent` : ""),
|
|
539
|
+
originalContent,
|
|
540
|
+
newContent
|
|
541
|
+
};
|
|
542
|
+
};
|
|
543
|
+
var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured) => {
|
|
544
|
+
const documentPath = findDocumentFile(projectRoot);
|
|
545
|
+
if (!documentPath) {
|
|
546
|
+
return {
|
|
547
|
+
success: false,
|
|
548
|
+
filePath: "",
|
|
549
|
+
message: "Could not find pages/_document.tsx. Please create one or add React Grab manually."
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const originalContent = readFileSync(documentPath, "utf-8");
|
|
553
|
+
let newContent = originalContent;
|
|
554
|
+
const hasReactGrabInFile = originalContent.includes("react-grab");
|
|
555
|
+
if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
|
|
556
|
+
return addAgentToExistingNextApp(originalContent, agent, documentPath);
|
|
557
|
+
}
|
|
558
|
+
if (hasReactGrabInFile) {
|
|
559
|
+
return {
|
|
560
|
+
success: true,
|
|
561
|
+
filePath: documentPath,
|
|
562
|
+
message: "React Grab is already installed in this file",
|
|
563
|
+
noChanges: true
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
if (!newContent.includes('import Script from "next/script"')) {
|
|
567
|
+
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
568
|
+
if (importMatch) {
|
|
569
|
+
newContent = newContent.replace(importMatch[0], `${importMatch[0]}
|
|
570
|
+
${SCRIPT_IMPORT}`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const scriptBlock = NEXT_PAGES_ROUTER_SCRIPT_WITH_AGENT(agent);
|
|
574
|
+
const headMatch = newContent.match(/<Head[^>]*>/);
|
|
575
|
+
if (headMatch) {
|
|
576
|
+
newContent = newContent.replace(headMatch[0], `${headMatch[0]}
|
|
577
|
+
${scriptBlock}`);
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
success: true,
|
|
581
|
+
filePath: documentPath,
|
|
582
|
+
message: "Add React Grab" + (agent !== "none" ? ` with ${agent} agent` : ""),
|
|
583
|
+
originalContent,
|
|
584
|
+
newContent
|
|
585
|
+
};
|
|
586
|
+
};
|
|
587
|
+
var transformVite = (projectRoot, agent, reactGrabAlreadyConfigured) => {
|
|
588
|
+
const indexPath = findIndexHtml(projectRoot);
|
|
589
|
+
if (!indexPath) {
|
|
590
|
+
return {
|
|
591
|
+
success: false,
|
|
592
|
+
filePath: "",
|
|
593
|
+
message: "Could not find index.html"
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
const originalContent = readFileSync(indexPath, "utf-8");
|
|
597
|
+
let newContent = originalContent;
|
|
598
|
+
const hasReactGrabInFile = originalContent.includes("react-grab");
|
|
599
|
+
if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
|
|
600
|
+
return addAgentToExistingVite(originalContent, agent, indexPath);
|
|
601
|
+
}
|
|
602
|
+
if (hasReactGrabInFile) {
|
|
603
|
+
return {
|
|
604
|
+
success: true,
|
|
605
|
+
filePath: indexPath,
|
|
606
|
+
message: "React Grab is already installed in this file",
|
|
607
|
+
noChanges: true
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const scriptBlock = VITE_SCRIPT_WITH_AGENT(agent);
|
|
611
|
+
const headMatch = newContent.match(/<head[^>]*>/i);
|
|
612
|
+
if (headMatch) {
|
|
613
|
+
newContent = newContent.replace(headMatch[0], `${headMatch[0]}
|
|
614
|
+
${scriptBlock}`);
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
success: true,
|
|
618
|
+
filePath: indexPath,
|
|
619
|
+
message: "Add React Grab" + (agent !== "none" ? ` with ${agent} agent` : ""),
|
|
620
|
+
originalContent,
|
|
621
|
+
newContent
|
|
622
|
+
};
|
|
623
|
+
};
|
|
624
|
+
var transformWebpack = (projectRoot, agent, reactGrabAlreadyConfigured) => {
|
|
625
|
+
const entryPath = findEntryFile(projectRoot);
|
|
626
|
+
if (!entryPath) {
|
|
627
|
+
return {
|
|
628
|
+
success: false,
|
|
629
|
+
filePath: "",
|
|
630
|
+
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
const originalContent = readFileSync(entryPath, "utf-8");
|
|
634
|
+
const hasReactGrabInFile = originalContent.includes("react-grab");
|
|
635
|
+
if (hasReactGrabInFile && reactGrabAlreadyConfigured) {
|
|
636
|
+
return addAgentToExistingWebpack(originalContent, agent, entryPath);
|
|
637
|
+
}
|
|
638
|
+
if (hasReactGrabInFile) {
|
|
639
|
+
return {
|
|
640
|
+
success: true,
|
|
641
|
+
filePath: entryPath,
|
|
642
|
+
message: "React Grab is already installed in this file",
|
|
643
|
+
noChanges: true
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
const importBlock = WEBPACK_IMPORT_WITH_AGENT(agent);
|
|
647
|
+
const newContent = `${importBlock}
|
|
648
|
+
|
|
649
|
+
${originalContent}`;
|
|
650
|
+
return {
|
|
651
|
+
success: true,
|
|
652
|
+
filePath: entryPath,
|
|
653
|
+
message: "Add React Grab" + (agent !== "none" ? ` with ${agent} agent` : ""),
|
|
654
|
+
originalContent,
|
|
655
|
+
newContent
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
var previewTransform = (projectRoot, framework, nextRouterType, agent, reactGrabAlreadyConfigured = false) => {
|
|
659
|
+
switch (framework) {
|
|
660
|
+
case "next":
|
|
661
|
+
if (nextRouterType === "app") {
|
|
662
|
+
return transformNextAppRouter(projectRoot, agent, reactGrabAlreadyConfigured);
|
|
663
|
+
}
|
|
664
|
+
return transformNextPagesRouter(projectRoot, agent, reactGrabAlreadyConfigured);
|
|
665
|
+
case "vite":
|
|
666
|
+
return transformVite(projectRoot, agent, reactGrabAlreadyConfigured);
|
|
667
|
+
case "webpack":
|
|
668
|
+
return transformWebpack(projectRoot, agent, reactGrabAlreadyConfigured);
|
|
669
|
+
default:
|
|
670
|
+
return {
|
|
671
|
+
success: false,
|
|
672
|
+
filePath: "",
|
|
673
|
+
message: `Unknown framework: ${framework}. Please add React Grab manually.`
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
var applyTransform = (result) => {
|
|
678
|
+
if (result.success && result.newContent && result.filePath) {
|
|
679
|
+
writeFileSync(result.filePath, result.newContent);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// src/cli.ts
|
|
684
|
+
var VERSION = "0.0.1";
|
|
685
|
+
var FRAMEWORK_NAMES = {
|
|
686
|
+
next: "Next.js",
|
|
687
|
+
vite: "Vite",
|
|
688
|
+
webpack: "Webpack",
|
|
689
|
+
unknown: "Unknown"
|
|
690
|
+
};
|
|
691
|
+
var PACKAGE_MANAGER_NAMES = {
|
|
692
|
+
npm: "npm",
|
|
693
|
+
yarn: "Yarn",
|
|
694
|
+
pnpm: "pnpm",
|
|
695
|
+
bun: "Bun"
|
|
696
|
+
};
|
|
697
|
+
var AGENT_NAMES = {
|
|
698
|
+
"claude-code": "Claude Code",
|
|
699
|
+
cursor: "Cursor",
|
|
700
|
+
opencode: "Opencode"
|
|
701
|
+
};
|
|
702
|
+
var DOCS_URL = "https://react-grab.com/docs";
|
|
703
|
+
var showDocsLink = () => {
|
|
704
|
+
console.log("\nFor manual installation instructions, visit:");
|
|
705
|
+
console.log(` ${DOCS_URL}
|
|
706
|
+
`);
|
|
707
|
+
};
|
|
708
|
+
var parseArgs = async () => {
|
|
709
|
+
const argv = await yargs(hideBin(process.argv)).usage("Usage: $0 [options]").option("framework", {
|
|
710
|
+
alias: "f",
|
|
711
|
+
type: "string",
|
|
712
|
+
choices: ["next", "vite", "webpack"],
|
|
713
|
+
description: "Framework to configure (next, vite, webpack)"
|
|
714
|
+
}).option("package-manager", {
|
|
715
|
+
alias: "p",
|
|
716
|
+
type: "string",
|
|
717
|
+
choices: ["npm", "yarn", "pnpm", "bun"],
|
|
718
|
+
description: "Package manager to use"
|
|
719
|
+
}).option("router", {
|
|
720
|
+
alias: "r",
|
|
721
|
+
type: "string",
|
|
722
|
+
choices: ["app", "pages"],
|
|
723
|
+
description: "Next.js router type (app or pages)"
|
|
724
|
+
}).option("agent", {
|
|
725
|
+
alias: "a",
|
|
726
|
+
type: "string",
|
|
727
|
+
choices: ["claude-code", "cursor", "opencode", "none"],
|
|
728
|
+
description: "Agent integration to add"
|
|
729
|
+
}).option("yes", {
|
|
730
|
+
alias: "y",
|
|
731
|
+
type: "boolean",
|
|
732
|
+
default: false,
|
|
733
|
+
description: "Skip all confirmation prompts"
|
|
734
|
+
}).option("skip-install", {
|
|
735
|
+
type: "boolean",
|
|
736
|
+
default: false,
|
|
737
|
+
description: "Skip package installation (only modify files)"
|
|
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 -a cursor", "Install for Next.js App Router with Cursor agent").example("$0 -p pnpm -a claude-code -y", "Use pnpm and add Claude Code agent").parse();
|
|
739
|
+
return {
|
|
740
|
+
framework: argv.framework,
|
|
741
|
+
packageManager: argv["package-manager"],
|
|
742
|
+
router: argv.router,
|
|
743
|
+
agent: argv.agent,
|
|
744
|
+
yes: argv.yes,
|
|
745
|
+
skipInstall: argv["skip-install"]
|
|
746
|
+
};
|
|
747
|
+
};
|
|
748
|
+
var main = async () => {
|
|
749
|
+
const args = await parseArgs();
|
|
750
|
+
const isNonInteractive = args.yes;
|
|
751
|
+
console.log(`
|
|
752
|
+
${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
|
|
753
|
+
const projectInfo = await detectProject(process.cwd());
|
|
754
|
+
console.log(`- Framework: ${pc.cyan(FRAMEWORK_NAMES[projectInfo.framework])}`);
|
|
755
|
+
console.log(`- Package Manager: ${pc.cyan(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}`);
|
|
756
|
+
if (projectInfo.framework === "next") {
|
|
757
|
+
console.log(`- Router Type: ${pc.cyan(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`);
|
|
758
|
+
}
|
|
759
|
+
console.log(`- Monorepo: ${pc.cyan(projectInfo.isMonorepo ? "Yes" : "No")}`);
|
|
760
|
+
console.log(`- React Grab: ${projectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`);
|
|
761
|
+
if (projectInfo.installedAgents.length > 0) {
|
|
762
|
+
console.log(`- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`);
|
|
763
|
+
}
|
|
764
|
+
console.log("");
|
|
765
|
+
let action = "install-all";
|
|
766
|
+
if (projectInfo.hasReactGrab && !isNonInteractive) {
|
|
767
|
+
action = await select({
|
|
768
|
+
message: "React Grab is already installed. What would you like to do?",
|
|
769
|
+
choices: [
|
|
770
|
+
{ name: "Add an agent integration", value: "add-agent" },
|
|
771
|
+
{ name: "Reconfigure project files", value: "reconfigure" },
|
|
772
|
+
{ name: "Reinstall everything", value: "install-all" }
|
|
773
|
+
]
|
|
774
|
+
});
|
|
775
|
+
} else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
|
|
776
|
+
action = "add-agent";
|
|
777
|
+
}
|
|
778
|
+
let finalFramework = args.framework || projectInfo.framework;
|
|
779
|
+
let finalPackageManager = args.packageManager || projectInfo.packageManager;
|
|
780
|
+
let finalNextRouterType = args.router || projectInfo.nextRouterType;
|
|
781
|
+
if (!isNonInteractive && !args.framework) {
|
|
782
|
+
const confirmSettings = await confirm({
|
|
783
|
+
message: "Are these settings correct?",
|
|
784
|
+
default: true
|
|
785
|
+
});
|
|
786
|
+
if (!confirmSettings) {
|
|
787
|
+
finalFramework = await select({
|
|
788
|
+
message: "Select your framework:",
|
|
789
|
+
choices: [
|
|
790
|
+
{ name: "Next.js", value: "next" },
|
|
791
|
+
{ name: "Vite", value: "vite" },
|
|
792
|
+
{ name: "Webpack", value: "webpack" }
|
|
793
|
+
],
|
|
794
|
+
default: projectInfo.framework
|
|
795
|
+
});
|
|
796
|
+
finalPackageManager = await select({
|
|
797
|
+
message: "Select your package manager:",
|
|
798
|
+
choices: [
|
|
799
|
+
{ name: "npm", value: "npm" },
|
|
800
|
+
{ name: "Yarn", value: "yarn" },
|
|
801
|
+
{ name: "pnpm", value: "pnpm" },
|
|
802
|
+
{ name: "Bun", value: "bun" }
|
|
803
|
+
],
|
|
804
|
+
default: projectInfo.packageManager
|
|
805
|
+
});
|
|
806
|
+
if (finalFramework === "next") {
|
|
807
|
+
finalNextRouterType = await select({
|
|
808
|
+
message: "Select your Next.js router type:",
|
|
809
|
+
choices: [
|
|
810
|
+
{ name: "App Router", value: "app" },
|
|
811
|
+
{ name: "Pages Router", value: "pages" }
|
|
812
|
+
],
|
|
813
|
+
default: projectInfo.nextRouterType === "app" ? "app" : "pages"
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
let agentIntegration = args.agent || "none";
|
|
819
|
+
const shouldAskForAgent = (action === "install-all" || action === "add-agent") && !args.agent;
|
|
820
|
+
if (shouldAskForAgent && !isNonInteractive) {
|
|
821
|
+
const availableAgents = [
|
|
822
|
+
{ name: "Claude Code", value: "claude-code" },
|
|
823
|
+
{ name: "Cursor", value: "cursor" },
|
|
824
|
+
{ name: "Opencode", value: "opencode" }
|
|
825
|
+
].filter((agent) => !projectInfo.installedAgents.includes(agent.value));
|
|
826
|
+
if (availableAgents.length === 0) {
|
|
827
|
+
console.log(`
|
|
828
|
+
${pc.green("All agent integrations are already installed.")}
|
|
829
|
+
`);
|
|
830
|
+
} else if (action === "add-agent") {
|
|
831
|
+
agentIntegration = await select({
|
|
832
|
+
message: "Select an agent integration to add:",
|
|
833
|
+
choices: availableAgents
|
|
834
|
+
});
|
|
835
|
+
} else {
|
|
836
|
+
const wantAgentIntegration = await confirm({
|
|
837
|
+
message: "Do you want to add an agent integration (Claude Code, Cursor, or Opencode)?",
|
|
838
|
+
default: false
|
|
839
|
+
});
|
|
840
|
+
if (wantAgentIntegration) {
|
|
841
|
+
agentIntegration = await select({
|
|
842
|
+
message: "Select an agent integration:",
|
|
843
|
+
choices: availableAgents
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const shouldTransform = action === "reconfigure" || action === "install-all" || action === "add-agent" && agentIntegration !== "none";
|
|
849
|
+
if (shouldTransform) {
|
|
850
|
+
console.log(`
|
|
851
|
+
${pc.magenta("\u269B")} Previewing changes...
|
|
852
|
+
`);
|
|
853
|
+
const result = previewTransform(
|
|
854
|
+
projectInfo.projectRoot,
|
|
855
|
+
finalFramework,
|
|
856
|
+
finalNextRouterType,
|
|
857
|
+
agentIntegration,
|
|
858
|
+
projectInfo.hasReactGrab || action === "add-agent"
|
|
859
|
+
);
|
|
860
|
+
if (!result.success) {
|
|
861
|
+
console.error(`${pc.red("Error:")} ${result.message}`);
|
|
862
|
+
showDocsLink();
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
865
|
+
if (result.noChanges) {
|
|
866
|
+
console.log(`${pc.cyan("Info:")} ${result.message}`);
|
|
867
|
+
} else if (result.originalContent && result.newContent) {
|
|
868
|
+
printDiff(result.filePath, result.originalContent, result.newContent);
|
|
869
|
+
if (!isNonInteractive) {
|
|
870
|
+
const confirmChanges = await confirm({
|
|
871
|
+
message: "Apply these changes?",
|
|
872
|
+
default: true
|
|
873
|
+
});
|
|
874
|
+
if (!confirmChanges) {
|
|
875
|
+
console.log(`
|
|
876
|
+
${pc.yellow("Changes cancelled.")}
|
|
877
|
+
`);
|
|
878
|
+
process.exit(0);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
|
|
882
|
+
const shouldInstallAgent = agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration);
|
|
883
|
+
if (!args.skipInstall && (shouldInstallReactGrab || shouldInstallAgent)) {
|
|
884
|
+
const packages = getPackagesToInstall(agentIntegration, shouldInstallReactGrab);
|
|
885
|
+
if (packages.length > 0) {
|
|
886
|
+
console.log(`
|
|
887
|
+
${pc.magenta("\u269B")} Installing: ${pc.cyan(packages.join(", "))}
|
|
888
|
+
`);
|
|
889
|
+
try {
|
|
890
|
+
installPackages(packages, finalPackageManager, projectInfo.projectRoot);
|
|
891
|
+
console.log(`
|
|
892
|
+
${pc.green("Packages installed successfully!")}
|
|
893
|
+
`);
|
|
894
|
+
} catch (error) {
|
|
895
|
+
console.error(`
|
|
896
|
+
${pc.red("Failed to install packages:")}`, error);
|
|
897
|
+
showDocsLink();
|
|
898
|
+
process.exit(1);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
applyTransform(result);
|
|
903
|
+
console.log(`
|
|
904
|
+
${pc.green("Applied:")} ${result.filePath}`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
console.log(`
|
|
908
|
+
${pc.green("Done!")}`);
|
|
909
|
+
console.log(`
|
|
910
|
+
Next steps:`);
|
|
911
|
+
console.log(` - Start your development server`);
|
|
912
|
+
console.log(` - Select an element to copy its context`);
|
|
913
|
+
console.log(` - Learn more at ${pc.cyan("https://react-grab.com")}
|
|
914
|
+
`);
|
|
915
|
+
if (agentIntegration !== "none") {
|
|
916
|
+
console.log(`${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`);
|
|
917
|
+
console.log(` Make sure to start the agent server before using it.
|
|
918
|
+
`);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
main().catch((error) => {
|
|
922
|
+
console.error(`${pc.red("Error:")}`, error);
|
|
923
|
+
console.log("\nFor manual installation instructions, visit:");
|
|
924
|
+
console.log(` ${DOCS_URL}
|
|
925
|
+
`);
|
|
926
|
+
process.exit(1);
|
|
927
|
+
});
|