@react-grab/cli 0.1.46 → 0.1.47-dev.d5e8fc8

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.
@@ -0,0 +1,1004 @@
1
+ #!/usr/bin/env node
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let node_path = require("node:path");
25
+ let node_fs = require("node:fs");
26
+ let package_manager_detector_detect = require("package-manager-detector/detect");
27
+ let ignore = require("ignore");
28
+ ignore = __toESM(ignore, 1);
29
+ let agent_install_skill = require("agent-install/skill");
30
+ let node_url = require("node:url");
31
+ let tinyexec = require("tinyexec");
32
+ //#region src/utils/react-grab-code.ts
33
+ const REACT_GRAB_SPECIFIER_PATTERN = String.raw`react-grab(?:\/[^"']+)?`;
34
+ const stripComments = (content) => content.replace(/<!--[\s\S]*?-->/g, "").replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
35
+ const stripTypeOnlyReactGrabImports = (content) => {
36
+ return content.replace(new RegExp(String.raw`import\s+type\s+[^;]+from\s+["']${REACT_GRAB_SPECIFIER_PATTERN}["'];?`, "g"), "").replace(new RegExp(String.raw`import\s*\{\s*type\s+[^,}]+(?:\s*,\s*type\s+[^,}]+)*\s*,?\s*\}\s*from\s+["']${REACT_GRAB_SPECIFIER_PATTERN}["'];?`, "g"), "");
37
+ };
38
+ const hasReactGrabSetupCode = (content) => {
39
+ const setupCandidateContent = stripTypeOnlyReactGrabImports(stripComments(content));
40
+ return [
41
+ new RegExp(String.raw`import\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
42
+ new RegExp(String.raw`import\s+(?!type\b)(?:[^"';]+from\s+)?["']${REACT_GRAB_SPECIFIER_PATTERN}["']`),
43
+ new RegExp(String.raw`require\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
44
+ /<Script[\s\S]*?src\s*=\s*(?:["'][^"']*react-grab[^"']*["']|\{(?:["'][^"']*react-grab[^"']*["']|`[^`]*react-grab[^`]*`)\})/i,
45
+ /<script[\s\S]*?src\s*=\s*["'][^"']*react-grab[^"']*["']/i
46
+ ].some((pattern) => pattern.test(setupCandidateContent));
47
+ };
48
+ //#endregion
49
+ //#region src/utils/react-grab-setup-files.ts
50
+ const COMPONENT_EXTENSIONS = [
51
+ "tsx",
52
+ "jsx",
53
+ "ts",
54
+ "js"
55
+ ];
56
+ const INSTRUMENTATION_EXTENSIONS = [
57
+ "ts",
58
+ "tsx",
59
+ "js",
60
+ "jsx",
61
+ "mts",
62
+ "cts",
63
+ "mjs",
64
+ "cjs"
65
+ ];
66
+ const ROUTE_EXTENSIONS = ["tsx", "jsx"];
67
+ const createFileCandidates = (projectRoot, directories, baseName, extensions) => {
68
+ const fileCandidates = [];
69
+ for (const directory of directories) for (const extension of extensions) fileCandidates.push((0, node_path.join)(projectRoot, directory, `${baseName}.${extension}`));
70
+ return fileCandidates;
71
+ };
72
+ const findExistingFile = (fileCandidates) => {
73
+ for (const filePath of fileCandidates) if ((0, node_fs.existsSync)(filePath)) return filePath;
74
+ return null;
75
+ };
76
+ const getLayoutFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["app", "src/app"], "layout", COMPONENT_EXTENSIONS);
77
+ const getDocumentFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["pages", "src/pages"], "_document", COMPONENT_EXTENSIONS);
78
+ const getInstrumentationFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["", "src"], "instrumentation-client", INSTRUMENTATION_EXTENSIONS);
79
+ const getIndexHtmlCandidates = (projectRoot) => [(0, node_path.join)(projectRoot, "index.html"), (0, node_path.join)(projectRoot, "public", "index.html")];
80
+ const getEntryFileCandidates = (projectRoot) => [...createFileCandidates(projectRoot, ["src"], "index", COMPONENT_EXTENSIONS), ...createFileCandidates(projectRoot, ["src"], "main", COMPONENT_EXTENSIONS)];
81
+ const getTanStackRootFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["src/routes", "app/routes"], "__root", ROUTE_EXTENSIONS);
82
+ const getReactGrabSetupFileCandidates = (projectRoot) => [
83
+ ...getLayoutFileCandidates(projectRoot),
84
+ ...getDocumentFileCandidates(projectRoot),
85
+ ...getInstrumentationFileCandidates(projectRoot),
86
+ ...getIndexHtmlCandidates(projectRoot),
87
+ ...getEntryFileCandidates(projectRoot),
88
+ ...getTanStackRootFileCandidates(projectRoot)
89
+ ];
90
+ const findLayoutFile = (projectRoot) => findExistingFile(getLayoutFileCandidates(projectRoot));
91
+ const findDocumentFile = (projectRoot) => findExistingFile(getDocumentFileCandidates(projectRoot));
92
+ const findIndexHtml = (projectRoot) => findExistingFile(getIndexHtmlCandidates(projectRoot));
93
+ const findEntryFile = (projectRoot) => findExistingFile(getEntryFileCandidates(projectRoot));
94
+ const findTanStackRootFile = (projectRoot) => findExistingFile(getTanStackRootFileCandidates(projectRoot));
95
+ const isInstrumentationFile = (filePath) => /(?:^|[/\\])instrumentation-client\.[cm]?[jt]sx?$/.test(filePath);
96
+ //#endregion
97
+ //#region src/utils/detect.ts
98
+ const VALID_PACKAGE_MANAGERS = new Set([
99
+ "npm",
100
+ "yarn",
101
+ "pnpm",
102
+ "bun"
103
+ ]);
104
+ const detectPackageManager = async (projectRoot) => {
105
+ const result = await (0, package_manager_detector_detect.detect)({ cwd: projectRoot });
106
+ if (result?.agent) {
107
+ const managerName = result.agent.split("@")[0];
108
+ if (VALID_PACKAGE_MANAGERS.has(managerName)) return managerName;
109
+ }
110
+ return "npm";
111
+ };
112
+ const CONFIG_EXTENSIONS = [
113
+ "ts",
114
+ "mts",
115
+ "cts",
116
+ "js",
117
+ "mjs",
118
+ "cjs"
119
+ ];
120
+ const hasConfigFile = (projectRoot, configBaseName) => CONFIG_EXTENSIONS.some((extension) => (0, node_fs.existsSync)((0, node_path.join)(projectRoot, `${configBaseName}.${extension}`)));
121
+ const readMergedDependencies = (projectRoot) => {
122
+ const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
123
+ if (!(0, node_fs.existsSync)(packageJsonPath)) return null;
124
+ try {
125
+ const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
126
+ return {
127
+ ...packageJson.dependencies,
128
+ ...packageJson.devDependencies
129
+ };
130
+ } catch {
131
+ return null;
132
+ }
133
+ };
134
+ const detectFrameworkFromDependencies = (dependencies) => {
135
+ if (!dependencies) return "unknown";
136
+ if (dependencies["next"]) return "next";
137
+ if (dependencies["@tanstack/react-start"]) return "tanstack";
138
+ if (dependencies["vite"]) return "vite";
139
+ if (dependencies["webpack"]) return "webpack";
140
+ return "unknown";
141
+ };
142
+ const detectFrameworkFromConfigFiles = (projectRoot) => {
143
+ if (hasConfigFile(projectRoot, "next.config")) return "next";
144
+ if (hasConfigFile(projectRoot, "app.config")) return "tanstack";
145
+ if (hasConfigFile(projectRoot, "vite.config")) return "vite";
146
+ if (hasConfigFile(projectRoot, "webpack.config")) return "webpack";
147
+ return "unknown";
148
+ };
149
+ const findEnclosingMonorepoRoot = (projectRoot) => {
150
+ let currentDirectory = (0, node_path.dirname)(projectRoot);
151
+ while (currentDirectory !== (0, node_path.dirname)(currentDirectory)) {
152
+ if (detectMonorepo(currentDirectory)) return currentDirectory;
153
+ currentDirectory = (0, node_path.dirname)(currentDirectory);
154
+ }
155
+ return null;
156
+ };
157
+ const detectFramework = (projectRoot) => {
158
+ const localFramework = detectFrameworkFromDependencies(readMergedDependencies(projectRoot));
159
+ if (localFramework !== "unknown") return localFramework;
160
+ return detectFrameworkFromConfigFiles(projectRoot);
161
+ };
162
+ const detectFrameworkFromMonorepoRoot = (projectRoot) => {
163
+ const monorepoRoot = findEnclosingMonorepoRoot(projectRoot);
164
+ if (!monorepoRoot) return "unknown";
165
+ return detectFrameworkFromDependencies(readMergedDependencies(monorepoRoot));
166
+ };
167
+ const detectNextRouterType = (projectRoot) => {
168
+ const hasAppDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "app"));
169
+ const hasSrcAppDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "src", "app"));
170
+ const hasPagesDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "pages"));
171
+ const hasSrcPagesDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "src", "pages"));
172
+ if (hasAppDir || hasSrcAppDir) return "app";
173
+ if (hasPagesDir || hasSrcPagesDir) return "pages";
174
+ return "unknown";
175
+ };
176
+ const detectMonorepo = (projectRoot) => {
177
+ if ((0, node_fs.existsSync)((0, node_path.join)(projectRoot, "pnpm-workspace.yaml"))) return true;
178
+ if ((0, node_fs.existsSync)((0, node_path.join)(projectRoot, "lerna.json"))) return true;
179
+ const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
180
+ if ((0, node_fs.existsSync)(packageJsonPath)) try {
181
+ if (JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")).workspaces) return true;
182
+ } catch {
183
+ return false;
184
+ }
185
+ return false;
186
+ };
187
+ const getWorkspacePatterns = (projectRoot) => {
188
+ const patterns = [];
189
+ const pnpmWorkspacePath = (0, node_path.join)(projectRoot, "pnpm-workspace.yaml");
190
+ if ((0, node_fs.existsSync)(pnpmWorkspacePath)) {
191
+ const lines = (0, node_fs.readFileSync)(pnpmWorkspacePath, "utf-8").split("\n");
192
+ let inPackages = false;
193
+ for (const line of lines) {
194
+ if (line.match(/^packages:\s*$/)) {
195
+ inPackages = true;
196
+ continue;
197
+ }
198
+ if (inPackages) {
199
+ if (line.match(/^[a-zA-Z]/) || line.trim() === "") {
200
+ if (line.match(/^[a-zA-Z]/)) inPackages = false;
201
+ continue;
202
+ }
203
+ const match = line.match(/^\s*-\s*['"]?([^'"#\n]+?)['"]?\s*$/);
204
+ if (match) patterns.push(match[1].trim());
205
+ }
206
+ }
207
+ }
208
+ const lernaJsonPath = (0, node_path.join)(projectRoot, "lerna.json");
209
+ if ((0, node_fs.existsSync)(lernaJsonPath)) try {
210
+ const lernaJson = JSON.parse((0, node_fs.readFileSync)(lernaJsonPath, "utf-8"));
211
+ if (Array.isArray(lernaJson.packages)) patterns.push(...lernaJson.packages);
212
+ } catch {}
213
+ const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
214
+ if ((0, node_fs.existsSync)(packageJsonPath)) try {
215
+ const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
216
+ if (Array.isArray(packageJson.workspaces)) patterns.push(...packageJson.workspaces);
217
+ else if (packageJson.workspaces?.packages) patterns.push(...packageJson.workspaces.packages);
218
+ } catch {}
219
+ return [...new Set(patterns)];
220
+ };
221
+ const expandWorkspacePattern = (projectRoot, pattern) => {
222
+ const isGlob = pattern.endsWith("/*");
223
+ const basePath = (0, node_path.join)(projectRoot, pattern.replace(/\/\*$/, ""));
224
+ if (!(0, node_fs.existsSync)(basePath)) return [];
225
+ if (!isGlob) return (0, node_fs.existsSync)((0, node_path.join)(basePath, "package.json")) ? [basePath] : [];
226
+ const results = [];
227
+ try {
228
+ const entries = (0, node_fs.readdirSync)(basePath, { withFileTypes: true });
229
+ for (const entry of entries) {
230
+ if (!entry.isDirectory()) continue;
231
+ if ((0, node_fs.existsSync)((0, node_path.join)(basePath, entry.name, "package.json"))) results.push((0, node_path.join)(basePath, entry.name));
232
+ }
233
+ } catch {
234
+ return results;
235
+ }
236
+ return results;
237
+ };
238
+ const hasReactDependency = (projectPath) => {
239
+ const dependencies = readMergedDependencies(projectPath);
240
+ if (!dependencies) return false;
241
+ return Boolean(dependencies["react"] || dependencies["react-dom"]);
242
+ };
243
+ const buildReactProject = (projectPath) => {
244
+ const framework = detectFramework(projectPath);
245
+ if (!hasReactDependency(projectPath) && framework === "unknown") return null;
246
+ let name = (0, node_path.basename)(projectPath);
247
+ const packageJsonPath = (0, node_path.join)(projectPath, "package.json");
248
+ try {
249
+ name = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")).name || name;
250
+ } catch {}
251
+ return {
252
+ name,
253
+ path: projectPath,
254
+ framework
255
+ };
256
+ };
257
+ const findWorkspaceProjects = (projectRoot) => {
258
+ const patterns = getWorkspacePatterns(projectRoot);
259
+ const projects = [];
260
+ for (const pattern of patterns) for (const projectPath of expandWorkspacePattern(projectRoot, pattern)) {
261
+ const project = buildReactProject(projectPath);
262
+ if (project) projects.push(project);
263
+ }
264
+ return projects;
265
+ };
266
+ const ALWAYS_IGNORED_DIRECTORIES = [
267
+ "node_modules",
268
+ ".git",
269
+ ".next",
270
+ ".cache",
271
+ ".turbo",
272
+ "dist",
273
+ "build",
274
+ "coverage",
275
+ "test-results"
276
+ ];
277
+ const loadGitignore = (projectRoot) => {
278
+ const ignorer = (0, ignore.default)().add(ALWAYS_IGNORED_DIRECTORIES);
279
+ const gitignorePath = (0, node_path.join)(projectRoot, ".gitignore");
280
+ if ((0, node_fs.existsSync)(gitignorePath)) try {
281
+ ignorer.add((0, node_fs.readFileSync)(gitignorePath, "utf-8"));
282
+ } catch {}
283
+ return ignorer;
284
+ };
285
+ const scanDirectoryForProjects = (rootDirectory, ignorer, maxDepth, currentDepth = 0) => {
286
+ if (currentDepth >= maxDepth) return [];
287
+ if (!(0, node_fs.existsSync)(rootDirectory)) return [];
288
+ const projects = [];
289
+ try {
290
+ const entries = (0, node_fs.readdirSync)(rootDirectory, { withFileTypes: true });
291
+ for (const entry of entries) {
292
+ if (!entry.isDirectory()) continue;
293
+ if (ignorer.ignores(entry.name)) continue;
294
+ const entryPath = (0, node_path.join)(rootDirectory, entry.name);
295
+ if ((0, node_fs.existsSync)((0, node_path.join)(entryPath, "package.json"))) {
296
+ const project = buildReactProject(entryPath);
297
+ if (project) {
298
+ projects.push(project);
299
+ continue;
300
+ }
301
+ }
302
+ projects.push(...scanDirectoryForProjects(entryPath, ignorer, maxDepth, currentDepth + 1));
303
+ }
304
+ } catch {
305
+ return projects;
306
+ }
307
+ return projects;
308
+ };
309
+ const MAX_SCAN_DEPTH = 2;
310
+ const normalizePathForComparison = (filePath) => filePath.replace(/\\/g, "/");
311
+ const findReactProjects = (projectRoot) => {
312
+ const monorepoRoot = detectMonorepo(projectRoot) ? projectRoot : findEnclosingMonorepoRoot(projectRoot);
313
+ if (monorepoRoot) {
314
+ const workspaceProjects = findWorkspaceProjects(monorepoRoot);
315
+ const localProject = projectRoot === monorepoRoot ? null : buildReactProject(projectRoot);
316
+ const projects = localProject ? [localProject, ...workspaceProjects.filter((project) => normalizePathForComparison(project.path) !== normalizePathForComparison(localProject.path))] : workspaceProjects;
317
+ if (projects.length > 0) return projects;
318
+ }
319
+ const scannedProjects = scanDirectoryForProjects(projectRoot, loadGitignore(projectRoot), MAX_SCAN_DEPTH);
320
+ if (scannedProjects.length > 0) return scannedProjects;
321
+ let currentDirectory = (0, node_path.dirname)(projectRoot);
322
+ while (currentDirectory !== (0, node_path.dirname)(currentDirectory)) {
323
+ const parentProject = buildReactProject(currentDirectory);
324
+ if (parentProject) return [parentProject];
325
+ currentDirectory = (0, node_path.dirname)(currentDirectory);
326
+ }
327
+ return [];
328
+ };
329
+ const hasReactGrabSetupInFile = (filePath) => {
330
+ if (!(0, node_fs.existsSync)(filePath)) return false;
331
+ try {
332
+ return hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"));
333
+ } catch {
334
+ return false;
335
+ }
336
+ };
337
+ const detectReactGrabDependency = (projectRoot) => {
338
+ const dependencies = readMergedDependencies(projectRoot);
339
+ return Boolean(dependencies?.["react-grab"]);
340
+ };
341
+ const detectReactGrabConfigured = (projectRoot) => {
342
+ return getReactGrabSetupFileCandidates(projectRoot).some(hasReactGrabSetupInFile);
343
+ };
344
+ const detectReactGrab = (projectRoot) => detectReactGrabDependency(projectRoot) || detectReactGrabConfigured(projectRoot);
345
+ const detectUnsupportedFramework = (projectRoot) => {
346
+ const dependencies = readMergedDependencies(projectRoot);
347
+ if (!dependencies) return null;
348
+ if (dependencies["@remix-run/react"] || dependencies["remix"]) return "remix";
349
+ if (dependencies["astro"]) return "astro";
350
+ if (dependencies["@sveltejs/kit"]) return "sveltekit";
351
+ if (dependencies["gatsby"]) return "gatsby";
352
+ return null;
353
+ };
354
+ const detectReactGrabVersion = (projectRoot) => {
355
+ const installedPackageJsonPath = (0, node_path.join)(projectRoot, "node_modules", "react-grab", "package.json");
356
+ if ((0, node_fs.existsSync)(installedPackageJsonPath)) try {
357
+ return JSON.parse((0, node_fs.readFileSync)(installedPackageJsonPath, "utf-8")).version ?? null;
358
+ } catch {}
359
+ return null;
360
+ };
361
+ const detectProject = async (projectRoot = process.cwd()) => {
362
+ const localFramework = detectFramework(projectRoot);
363
+ const framework = localFramework === "unknown" ? detectFrameworkFromMonorepoRoot(projectRoot) : localFramework;
364
+ const packageManager = await detectPackageManager(projectRoot);
365
+ const isMonorepo = detectMonorepo(projectRoot) || findEnclosingMonorepoRoot(projectRoot) !== null;
366
+ const isReactGrabConfigured = detectReactGrabConfigured(projectRoot);
367
+ return {
368
+ packageManager,
369
+ framework,
370
+ nextRouterType: framework === "next" ? detectNextRouterType(projectRoot) : "unknown",
371
+ isMonorepo,
372
+ projectRoot,
373
+ hasReactGrab: detectReactGrabDependency(projectRoot) || isReactGrabConfigured,
374
+ isReactGrabConfigured,
375
+ reactGrabVersion: detectReactGrabVersion(projectRoot),
376
+ unsupportedFramework: detectUnsupportedFramework(projectRoot)
377
+ };
378
+ };
379
+ //#endregion
380
+ //#region src/utils/detect-agents.ts
381
+ const PATH_BINARIES = {
382
+ "claude-code": ["claude"],
383
+ codex: ["codex"],
384
+ cursor: ["cursor", "cursor-agent"],
385
+ droid: ["droid"],
386
+ "gemini-cli": ["gemini"],
387
+ "github-copilot": ["copilot"],
388
+ opencode: ["opencode"],
389
+ pi: ["pi", "omegon"]
390
+ };
391
+ const isCommandAvailable = (command) => {
392
+ const pathDirectories = (process.env.PATH ?? "").split(node_path.delimiter).filter(Boolean);
393
+ for (const directory of pathDirectories) {
394
+ const binaryPath = (0, node_path.join)(directory, command);
395
+ try {
396
+ if ((0, node_fs.statSync)(binaryPath).isFile()) {
397
+ (0, node_fs.accessSync)(binaryPath, node_fs.constants.X_OK);
398
+ return true;
399
+ }
400
+ } catch {}
401
+ }
402
+ return false;
403
+ };
404
+ const detectAvailableAgents = async () => {
405
+ const installedAgents = new Set(await (0, agent_install_skill.detectInstalledSkillAgents)());
406
+ return (0, agent_install_skill.getSkillAgentTypes)().filter((agent) => {
407
+ if (agent === "universal") return false;
408
+ if (installedAgents.has(agent)) return true;
409
+ return PATH_BINARIES[agent]?.some(isCommandAvailable) ?? false;
410
+ });
411
+ };
412
+ //#endregion
413
+ //#region src/utils/install-skill.ts
414
+ const SKILL_NAME = "react-grab";
415
+ const SKILL_SOURCE = (0, node_url.fileURLToPath)(new URL("../skills/react-grab", require("url").pathToFileURL(__filename).href));
416
+ const agentLabel = (agent) => (0, agent_install_skill.getSkillAgentConfig)(agent).displayName;
417
+ const installedSkillDir = (agent, global, cwd) => (0, node_path.join)((0, agent_install_skill.isUniversalSkillAgent)(agent) ? (0, agent_install_skill.getCanonicalSkillsDir)(global, cwd) : (0, agent_install_skill.getSkillAgentDir)(agent, {
418
+ global,
419
+ cwd
420
+ }), SKILL_NAME);
421
+ const installSkill = async ({ agents, global = false, cwd = process.cwd() } = {}) => {
422
+ return (0, agent_install_skill.add)({
423
+ source: SKILL_SOURCE,
424
+ agents: agents ?? await detectAvailableAgents(),
425
+ global,
426
+ cwd,
427
+ mode: "copy"
428
+ });
429
+ };
430
+ const removeSkill = async ({ cwd = process.cwd(), global = false } = {}) => {
431
+ const agents = await detectAvailableAgents();
432
+ const removedAgents = [];
433
+ const dirsToRemove = /* @__PURE__ */ new Set();
434
+ for (const agent of agents) {
435
+ const skillDir = installedSkillDir(agent, global, cwd);
436
+ if (!(0, node_fs.existsSync)(skillDir)) continue;
437
+ removedAgents.push(agent);
438
+ dirsToRemove.add(skillDir);
439
+ }
440
+ for (const skillDir of dirsToRemove) (0, node_fs.rmSync)(skillDir, {
441
+ recursive: true,
442
+ force: true
443
+ });
444
+ return removedAgents;
445
+ };
446
+ //#endregion
447
+ //#region src/utils/templates.ts
448
+ const NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
449
+ <Script
450
+ src="//unpkg.com/react-grab/dist/index.global.js"
451
+ crossOrigin="anonymous"
452
+ strategy="beforeInteractive"
453
+ />
454
+ )}`;
455
+ const VITE_IMPORT = `if (import.meta.env.DEV) {
456
+ import("react-grab");
457
+ }`;
458
+ const WEBPACK_IMPORT = `if (process.env.NODE_ENV === "development") {
459
+ import("react-grab");
460
+ }`;
461
+ const TANSTACK_EFFECT = `useEffect(() => {
462
+ if (import.meta.env.DEV) {
463
+ void import("react-grab");
464
+ }
465
+ }, []);`;
466
+ const SCRIPT_IMPORT = "import Script from \"next/script\";";
467
+ //#endregion
468
+ //#region src/utils/transform.ts
469
+ const hasReactGrabInInstrumentation = (projectRoot) => {
470
+ return findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot)) !== null;
471
+ };
472
+ const findFileWithReactGrabSetup = (fileCandidates) => {
473
+ for (const filePath of fileCandidates) {
474
+ if (!(0, node_fs.existsSync)(filePath)) continue;
475
+ if (hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"))) return filePath;
476
+ }
477
+ return null;
478
+ };
479
+ const alreadyConfiguredResult = (filePath) => ({
480
+ success: true,
481
+ filePath,
482
+ message: "React Grab is already configured",
483
+ noChanges: true
484
+ });
485
+ const transformNextAppRouter = (projectRoot, reactGrabAlreadyConfigured) => {
486
+ const layoutPath = findLayoutFile(projectRoot);
487
+ if (!layoutPath) return {
488
+ success: false,
489
+ filePath: "",
490
+ message: "Could not find app/layout.tsx, app/layout.jsx, app/layout.ts, or app/layout.js"
491
+ };
492
+ const originalContent = (0, node_fs.readFileSync)(layoutPath, "utf-8");
493
+ let newContent = originalContent;
494
+ const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
495
+ const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
496
+ if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(layoutPath);
497
+ if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
498
+ success: true,
499
+ filePath: layoutPath,
500
+ message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
501
+ noChanges: true
502
+ };
503
+ if (!newContent.includes("import Script from \"next/script\"")) {
504
+ const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
505
+ if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
506
+ else newContent = `${SCRIPT_IMPORT}\n\n${newContent}`;
507
+ }
508
+ const headMatch = newContent.match(/<head[^>]*>/);
509
+ if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
510
+ else {
511
+ const htmlMatch = newContent.match(/<html[^>]*>/);
512
+ if (htmlMatch) newContent = newContent.replace(htmlMatch[0], `${htmlMatch[0]}\n <head>\n ${NEXT_APP_ROUTER_SCRIPT}\n </head>`);
513
+ }
514
+ return {
515
+ success: true,
516
+ filePath: layoutPath,
517
+ message: "Add React Grab",
518
+ originalContent,
519
+ newContent
520
+ };
521
+ };
522
+ const transformNextPagesRouter = (projectRoot, reactGrabAlreadyConfigured) => {
523
+ const documentPath = findDocumentFile(projectRoot);
524
+ if (!documentPath) return {
525
+ success: false,
526
+ filePath: "",
527
+ message: "Could not find pages/_document.tsx, pages/_document.jsx, pages/_document.ts, or pages/_document.js.\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 }"
528
+ };
529
+ const originalContent = (0, node_fs.readFileSync)(documentPath, "utf-8");
530
+ let newContent = originalContent;
531
+ const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
532
+ const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
533
+ if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(documentPath);
534
+ if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
535
+ success: true,
536
+ filePath: documentPath,
537
+ message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
538
+ noChanges: true
539
+ };
540
+ if (!newContent.includes("import Script from \"next/script\"")) {
541
+ const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
542
+ if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
543
+ }
544
+ const headMatch = newContent.match(/<Head[^>]*>/);
545
+ if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
546
+ return {
547
+ success: true,
548
+ filePath: documentPath,
549
+ message: "Add React Grab",
550
+ originalContent,
551
+ newContent
552
+ };
553
+ };
554
+ const checkExistingInstallation = (filePath, reactGrabAlreadyConfigured) => {
555
+ if (!hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"))) return null;
556
+ return {
557
+ success: true,
558
+ filePath,
559
+ message: reactGrabAlreadyConfigured ? "React Grab is already configured" : "React Grab is already installed in this file",
560
+ noChanges: true
561
+ };
562
+ };
563
+ const transformVite = (projectRoot, reactGrabAlreadyConfigured) => {
564
+ const entryPath = findEntryFile(projectRoot);
565
+ const indexPath = findIndexHtml(projectRoot);
566
+ if (indexPath) {
567
+ const existingResult = checkExistingInstallation(indexPath, reactGrabAlreadyConfigured);
568
+ if (existingResult) return existingResult;
569
+ }
570
+ if (!entryPath) return {
571
+ success: false,
572
+ filePath: "",
573
+ message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
574
+ };
575
+ const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
576
+ if (existingResult) return existingResult;
577
+ const originalContent = (0, node_fs.readFileSync)(entryPath, "utf-8");
578
+ return {
579
+ success: true,
580
+ filePath: entryPath,
581
+ message: "Add React Grab",
582
+ originalContent,
583
+ newContent: `${VITE_IMPORT}\n\n${originalContent}`
584
+ };
585
+ };
586
+ const transformWebpack = (projectRoot, reactGrabAlreadyConfigured) => {
587
+ const entryPath = findEntryFile(projectRoot);
588
+ if (!entryPath) return {
589
+ success: false,
590
+ filePath: "",
591
+ message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
592
+ };
593
+ const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
594
+ if (existingResult) return existingResult;
595
+ const originalContent = (0, node_fs.readFileSync)(entryPath, "utf-8");
596
+ return {
597
+ success: true,
598
+ filePath: entryPath,
599
+ message: "Add React Grab",
600
+ originalContent,
601
+ newContent: `${WEBPACK_IMPORT}\n\n${originalContent}`
602
+ };
603
+ };
604
+ const transformTanStack = (projectRoot, reactGrabAlreadyConfigured) => {
605
+ const rootPath = findTanStackRootFile(projectRoot);
606
+ if (!rootPath) return {
607
+ success: false,
608
+ filePath: "",
609
+ message: "Could not find src/routes/__root.tsx or app/routes/__root.tsx.\n\nTo set up React Grab with TanStack Start, add this to your root route component:\n\n import { useEffect } from \"react\";\n\n useEffect(() => {\n if (import.meta.env.DEV) {\n void import(\"react-grab\");\n }\n }, []);"
610
+ };
611
+ const originalContent = (0, node_fs.readFileSync)(rootPath, "utf-8");
612
+ let newContent = originalContent;
613
+ const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
614
+ if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(rootPath);
615
+ if (hasReactGrabInFile) return {
616
+ success: true,
617
+ filePath: rootPath,
618
+ message: "React Grab is already installed in this file",
619
+ noChanges: true
620
+ };
621
+ if (!/import\s+\{[^}]*useEffect[^}]*\}\s+from\s+["']react["']/.test(newContent)) {
622
+ const reactImportMatch = newContent.match(/import\s+\{([^}]*)\}\s+from\s+["']react["'];?/);
623
+ if (reactImportMatch) {
624
+ const existingImports = reactImportMatch[1];
625
+ newContent = newContent.replace(reactImportMatch[0], `import { ${existingImports.trim()}, useEffect } from "react";`);
626
+ } else {
627
+ const firstImportMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
628
+ if (firstImportMatch) newContent = newContent.replace(firstImportMatch[0], `import { useEffect } from "react";\n${firstImportMatch[0]}`);
629
+ else newContent = `import { useEffect } from "react";\n\n${newContent}`;
630
+ }
631
+ }
632
+ const componentMatch = newContent.match(/function\s+(\w+)\s*\([^)]*\)\s*\{/);
633
+ if (componentMatch) {
634
+ const insertPosition = componentMatch.index + componentMatch[0].length;
635
+ newContent = newContent.slice(0, insertPosition) + `\n ${TANSTACK_EFFECT}\n` + newContent.slice(insertPosition);
636
+ } else return {
637
+ success: false,
638
+ filePath: rootPath,
639
+ message: "Could not find a component function in the root file"
640
+ };
641
+ return {
642
+ success: true,
643
+ filePath: rootPath,
644
+ message: "Add React Grab",
645
+ originalContent,
646
+ newContent
647
+ };
648
+ };
649
+ const hasFrameworkEntryPoint = (projectRoot, framework, nextRouterType) => {
650
+ switch (framework) {
651
+ case "next": return nextRouterType === "app" ? findLayoutFile(projectRoot) !== null : findDocumentFile(projectRoot) !== null;
652
+ case "vite":
653
+ case "webpack": return findEntryFile(projectRoot) !== null;
654
+ case "tanstack": return findTanStackRootFile(projectRoot) !== null;
655
+ default: return false;
656
+ }
657
+ };
658
+ const previewTransform = (projectRoot, framework, nextRouterType, reactGrabAlreadyConfigured = false) => {
659
+ switch (framework) {
660
+ case "next":
661
+ if (nextRouterType === "app") return transformNextAppRouter(projectRoot, reactGrabAlreadyConfigured);
662
+ return transformNextPagesRouter(projectRoot, reactGrabAlreadyConfigured);
663
+ case "vite": return transformVite(projectRoot, reactGrabAlreadyConfigured);
664
+ case "tanstack": return transformTanStack(projectRoot, reactGrabAlreadyConfigured);
665
+ case "webpack": return transformWebpack(projectRoot, reactGrabAlreadyConfigured);
666
+ default: return {
667
+ success: false,
668
+ filePath: "",
669
+ message: `Unknown framework: ${framework}. Please add React Grab manually.`
670
+ };
671
+ }
672
+ };
673
+ const canWriteToFile = (filePath) => {
674
+ try {
675
+ (0, node_fs.accessSync)(filePath, node_fs.constants.W_OK);
676
+ return true;
677
+ } catch {
678
+ return false;
679
+ }
680
+ };
681
+ const applyTransform = (result) => {
682
+ if (result.success && result.newContent && result.filePath) {
683
+ if (!canWriteToFile(result.filePath)) return {
684
+ success: false,
685
+ error: `Cannot write to ${result.filePath}. Check file permissions.`
686
+ };
687
+ try {
688
+ (0, node_fs.writeFileSync)(result.filePath, result.newContent);
689
+ return { success: true };
690
+ } catch (error) {
691
+ return {
692
+ success: false,
693
+ error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
694
+ };
695
+ }
696
+ }
697
+ return { success: true };
698
+ };
699
+ const formatOptionsForNextjs = (options) => {
700
+ const parts = [];
701
+ if (options.activationKey) parts.push(`activationKey: ${JSON.stringify(options.activationKey)}`);
702
+ if (options.activationMode) parts.push(`activationMode: "${options.activationMode}"`);
703
+ if (options.keyHoldDuration !== void 0) parts.push(`keyHoldDuration: ${options.keyHoldDuration}`);
704
+ if (options.allowActivationInsideInput !== void 0) parts.push(`allowActivationInsideInput: ${options.allowActivationInsideInput}`);
705
+ if (options.maxContextLines !== void 0) parts.push(`maxContextLines: ${options.maxContextLines}`);
706
+ return `{ ${parts.join(", ")} }`;
707
+ };
708
+ const formatOptionsAsJson = (options) => {
709
+ const cleanOptions = {};
710
+ if (options.activationKey) cleanOptions.activationKey = options.activationKey;
711
+ if (options.activationMode) cleanOptions.activationMode = options.activationMode;
712
+ if (options.keyHoldDuration !== void 0) cleanOptions.keyHoldDuration = options.keyHoldDuration;
713
+ if (options.allowActivationInsideInput !== void 0) cleanOptions.allowActivationInsideInput = options.allowActivationInsideInput;
714
+ if (options.maxContextLines !== void 0) cleanOptions.maxContextLines = options.maxContextLines;
715
+ return JSON.stringify(cleanOptions);
716
+ };
717
+ const findReactGrabFile = (projectRoot, framework, nextRouterType) => {
718
+ switch (framework) {
719
+ case "next": {
720
+ const primaryFile = nextRouterType === "app" ? findLayoutFile(projectRoot) : findDocumentFile(projectRoot);
721
+ const primarySetupFile = findFileWithReactGrabSetup(nextRouterType === "app" ? getLayoutFileCandidates(projectRoot) : getDocumentFileCandidates(projectRoot));
722
+ if (primarySetupFile) return primarySetupFile;
723
+ const instrumentationFile = findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot));
724
+ if (instrumentationFile) return instrumentationFile;
725
+ return primaryFile;
726
+ }
727
+ case "vite": {
728
+ const entryFile = findEntryFile(projectRoot);
729
+ const entrySetupFile = findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot));
730
+ if (entrySetupFile) return entrySetupFile;
731
+ const indexHtml = findFileWithReactGrabSetup(getIndexHtmlCandidates(projectRoot));
732
+ if (indexHtml) return indexHtml;
733
+ return entryFile;
734
+ }
735
+ case "tanstack": return findFileWithReactGrabSetup(getTanStackRootFileCandidates(projectRoot)) ?? findTanStackRootFile(projectRoot);
736
+ case "webpack": return findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot)) ?? findEntryFile(projectRoot);
737
+ default: return null;
738
+ }
739
+ };
740
+ const addOptionsToNextScript = (originalContent, options, filePath) => {
741
+ const reactGrabScriptMatch = originalContent.match(/(<Script[\s\S]*?react-grab[\s\S]*?)\s*(\/?>)/i);
742
+ if (!reactGrabScriptMatch) return {
743
+ success: false,
744
+ filePath,
745
+ message: "Could not find React Grab Script tag"
746
+ };
747
+ const scriptTag = reactGrabScriptMatch[0];
748
+ const scriptOpening = reactGrabScriptMatch[1];
749
+ const scriptClosing = reactGrabScriptMatch[2];
750
+ const existingDataOptionsMatch = scriptTag.match(/data-options=\{JSON\.stringify\([^)]+\)\}/);
751
+ const dataOptionsAttr = `data-options={JSON.stringify(\n ${formatOptionsForNextjs(options)}\n )}`;
752
+ let newScriptTag;
753
+ if (existingDataOptionsMatch) newScriptTag = scriptTag.replace(existingDataOptionsMatch[0], dataOptionsAttr);
754
+ else newScriptTag = `${scriptOpening}\n ${dataOptionsAttr}\n ${scriptClosing}`;
755
+ return {
756
+ success: true,
757
+ filePath,
758
+ message: "Update React Grab options",
759
+ originalContent,
760
+ newContent: originalContent.replace(scriptTag, newScriptTag)
761
+ };
762
+ };
763
+ const addOptionsToDynamicImport = (originalContent, options, filePath) => {
764
+ const reactGrabImportWithInitMatch = originalContent.match(/(void\s+)?import\s*\(\s*["']react-grab(?:\/[^"']+)?["']\s*\)(?:\.then\s*\(\s*(?:\(m\)\s*=>\s*m\.init\s*\([^)]*\)|\(\{\s*init\s*\}\)\s*=>\s*init\s*\([^)]*\))\s*\))?/);
765
+ if (!reactGrabImportWithInitMatch) return {
766
+ success: false,
767
+ filePath,
768
+ message: "Could not find React Grab import"
769
+ };
770
+ const optionsJson = formatOptionsAsJson(options);
771
+ const newImport = `${reactGrabImportWithInitMatch[1] ?? ""}import("react-grab").then((m) => m.init(${optionsJson}))`;
772
+ return {
773
+ success: true,
774
+ filePath,
775
+ message: "Update React Grab options",
776
+ originalContent,
777
+ newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
778
+ };
779
+ };
780
+ const addOptionsToTanStackImport = (originalContent, options, filePath) => {
781
+ const reactGrabImportWithInitMatch = originalContent.match(/(?:(void\s+)?import\s*\(\s*["']react-grab\/core["']\s*\)\.then\s*\(\s*(?:\(\s*\{\s*init\s*\}\s*\)\s*=>\s*init\s*\([^)]*\)|\(m\)\s*=>\s*m\.init\s*\([^)]*\))\s*\)|(void\s+)?import\s*\(\s*["']react-grab(?!\/core)(?:\/[^"']+)?["']\s*\))/);
782
+ if (!reactGrabImportWithInitMatch) return {
783
+ success: false,
784
+ filePath,
785
+ message: "Could not find React Grab import"
786
+ };
787
+ const optionsJson = formatOptionsAsJson(options);
788
+ const newImport = `${reactGrabImportWithInitMatch[1] ?? reactGrabImportWithInitMatch[2] ?? ""}import("react-grab/core").then(({ init }) => init(${optionsJson}))`;
789
+ return {
790
+ success: true,
791
+ filePath,
792
+ message: "Update React Grab options",
793
+ originalContent,
794
+ newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
795
+ };
796
+ };
797
+ const addOptionsToAnyImport = (originalContent, options, filePath) => {
798
+ const dynamicImportResult = addOptionsToDynamicImport(originalContent, options, filePath);
799
+ if (dynamicImportResult.success) return dynamicImportResult;
800
+ return addOptionsToTanStackImport(originalContent, options, filePath);
801
+ };
802
+ const previewOptionsTransform = (projectRoot, framework, nextRouterType, options) => {
803
+ const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
804
+ if (!filePath) return {
805
+ success: false,
806
+ filePath: "",
807
+ message: "Could not find file containing React Grab configuration"
808
+ };
809
+ const originalContent = (0, node_fs.readFileSync)(filePath, "utf-8");
810
+ if (!hasReactGrabSetupCode(originalContent)) return {
811
+ success: false,
812
+ filePath,
813
+ message: "Could not find React Grab code in the file"
814
+ };
815
+ switch (framework) {
816
+ case "next":
817
+ if (isInstrumentationFile(filePath)) return addOptionsToAnyImport(originalContent, options, filePath);
818
+ return addOptionsToNextScript(originalContent, options, filePath);
819
+ case "vite": return addOptionsToDynamicImport(originalContent, options, filePath);
820
+ case "tanstack": return addOptionsToTanStackImport(originalContent, options, filePath);
821
+ case "webpack": return addOptionsToDynamicImport(originalContent, options, filePath);
822
+ default: return {
823
+ success: false,
824
+ filePath,
825
+ message: `Unknown framework: ${framework}`
826
+ };
827
+ }
828
+ };
829
+ const previewCdnTransform = (projectRoot, framework, nextRouterType, targetCdnDomain) => {
830
+ const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
831
+ if (!filePath) return {
832
+ success: false,
833
+ filePath: "",
834
+ message: "Could not find React Grab file"
835
+ };
836
+ const originalContent = (0, node_fs.readFileSync)(filePath, "utf-8");
837
+ const newContent = originalContent.replace(/(https?:)?\/\/[^/\s"']+(?=\/(?:@?react-grab))/g, `//${targetCdnDomain}`).replace(/(https?:)?\/\/[^/\s"']*react-grab[^/\s"']*\.com(?=\/script\.js)/g, `//${targetCdnDomain}`);
838
+ if (newContent === originalContent) return {
839
+ success: true,
840
+ filePath,
841
+ message: "CDN already set",
842
+ noChanges: true
843
+ };
844
+ return {
845
+ success: true,
846
+ filePath,
847
+ message: "Update CDN",
848
+ originalContent,
849
+ newContent
850
+ };
851
+ };
852
+ //#endregion
853
+ //#region src/utils/install.ts
854
+ const installPackages = async (packages, options = {}) => {
855
+ if (packages.length === 0) return;
856
+ const detectedAgent = options.packageManager ?? await detectPackageManager(options.cwd ?? process.cwd());
857
+ const args = [];
858
+ if (options.preferOffline) args.push("--prefer-offline");
859
+ if (detectedAgent === "pnpm") {
860
+ args.push("--prod=false");
861
+ if ((0, node_fs.existsSync)((0, node_path.resolve)(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) args.push("-w");
862
+ }
863
+ if (options.additionalArgs) args.push(...options.additionalArgs);
864
+ await (0, tinyexec.x)(detectedAgent, [
865
+ detectedAgent === "npm" ? "install" : "add",
866
+ ...options.isDev !== false ? ["-D"] : [],
867
+ ...args,
868
+ ...packages
869
+ ], {
870
+ nodeOptions: {
871
+ stdio: options.silent ? "ignore" : "inherit",
872
+ cwd: options.cwd,
873
+ env: {
874
+ ...process.env,
875
+ REACT_GRAB_INIT: "1"
876
+ }
877
+ },
878
+ throwOnError: true
879
+ });
880
+ };
881
+ const getPackagesToInstall = (includeReactGrab = true) => {
882
+ return includeReactGrab ? ["react-grab"] : [];
883
+ };
884
+ //#endregion
885
+ Object.defineProperty(exports, "__toESM", {
886
+ enumerable: true,
887
+ get: function() {
888
+ return __toESM;
889
+ }
890
+ });
891
+ Object.defineProperty(exports, "agentLabel", {
892
+ enumerable: true,
893
+ get: function() {
894
+ return agentLabel;
895
+ }
896
+ });
897
+ Object.defineProperty(exports, "applyTransform", {
898
+ enumerable: true,
899
+ get: function() {
900
+ return applyTransform;
901
+ }
902
+ });
903
+ Object.defineProperty(exports, "detectAvailableAgents", {
904
+ enumerable: true,
905
+ get: function() {
906
+ return detectAvailableAgents;
907
+ }
908
+ });
909
+ Object.defineProperty(exports, "detectFramework", {
910
+ enumerable: true,
911
+ get: function() {
912
+ return detectFramework;
913
+ }
914
+ });
915
+ Object.defineProperty(exports, "detectNextRouterType", {
916
+ enumerable: true,
917
+ get: function() {
918
+ return detectNextRouterType;
919
+ }
920
+ });
921
+ Object.defineProperty(exports, "detectPackageManager", {
922
+ enumerable: true,
923
+ get: function() {
924
+ return detectPackageManager;
925
+ }
926
+ });
927
+ Object.defineProperty(exports, "detectProject", {
928
+ enumerable: true,
929
+ get: function() {
930
+ return detectProject;
931
+ }
932
+ });
933
+ Object.defineProperty(exports, "detectReactGrab", {
934
+ enumerable: true,
935
+ get: function() {
936
+ return detectReactGrab;
937
+ }
938
+ });
939
+ Object.defineProperty(exports, "detectReactGrabConfigured", {
940
+ enumerable: true,
941
+ get: function() {
942
+ return detectReactGrabConfigured;
943
+ }
944
+ });
945
+ Object.defineProperty(exports, "detectUnsupportedFramework", {
946
+ enumerable: true,
947
+ get: function() {
948
+ return detectUnsupportedFramework;
949
+ }
950
+ });
951
+ Object.defineProperty(exports, "findReactProjects", {
952
+ enumerable: true,
953
+ get: function() {
954
+ return findReactProjects;
955
+ }
956
+ });
957
+ Object.defineProperty(exports, "getPackagesToInstall", {
958
+ enumerable: true,
959
+ get: function() {
960
+ return getPackagesToInstall;
961
+ }
962
+ });
963
+ Object.defineProperty(exports, "hasFrameworkEntryPoint", {
964
+ enumerable: true,
965
+ get: function() {
966
+ return hasFrameworkEntryPoint;
967
+ }
968
+ });
969
+ Object.defineProperty(exports, "installPackages", {
970
+ enumerable: true,
971
+ get: function() {
972
+ return installPackages;
973
+ }
974
+ });
975
+ Object.defineProperty(exports, "installSkill", {
976
+ enumerable: true,
977
+ get: function() {
978
+ return installSkill;
979
+ }
980
+ });
981
+ Object.defineProperty(exports, "previewCdnTransform", {
982
+ enumerable: true,
983
+ get: function() {
984
+ return previewCdnTransform;
985
+ }
986
+ });
987
+ Object.defineProperty(exports, "previewOptionsTransform", {
988
+ enumerable: true,
989
+ get: function() {
990
+ return previewOptionsTransform;
991
+ }
992
+ });
993
+ Object.defineProperty(exports, "previewTransform", {
994
+ enumerable: true,
995
+ get: function() {
996
+ return previewTransform;
997
+ }
998
+ });
999
+ Object.defineProperty(exports, "removeSkill", {
1000
+ enumerable: true,
1001
+ get: function() {
1002
+ return removeSkill;
1003
+ }
1004
+ });