@inspecto-dev/cli 0.2.0-alpha.5 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.turbo/turbo-build.log +19 -20
  2. package/CHANGELOG.md +22 -0
  3. package/README.md +93 -11
  4. package/bin/inspecto.js +5 -1
  5. package/dist/bin.d.ts +5 -1
  6. package/dist/bin.js +530 -49
  7. package/dist/chunk-FZS2TLXQ.js +3140 -0
  8. package/dist/index.d.ts +233 -2
  9. package/dist/index.js +17 -3
  10. package/package.json +3 -2
  11. package/src/bin.ts +286 -66
  12. package/src/commands/apply.ts +118 -0
  13. package/src/commands/detect.ts +59 -0
  14. package/src/commands/doctor.ts +225 -72
  15. package/src/commands/init.ts +143 -183
  16. package/src/commands/integration-install.ts +452 -0
  17. package/src/commands/onboard.ts +50 -0
  18. package/src/commands/plan.ts +41 -0
  19. package/src/detect/build-tool.ts +107 -3
  20. package/src/index.ts +17 -2
  21. package/src/inject/ast-injector.ts +17 -6
  22. package/src/inject/extension.ts +40 -22
  23. package/src/inject/gitignore.ts +10 -3
  24. package/src/instructions.ts +60 -46
  25. package/src/onboarding/apply.ts +364 -0
  26. package/src/onboarding/context.ts +36 -0
  27. package/src/onboarding/planner.ts +284 -0
  28. package/src/onboarding/session.ts +434 -0
  29. package/src/onboarding/target-resolution.ts +116 -0
  30. package/src/prompts.ts +54 -11
  31. package/src/types.ts +184 -0
  32. package/src/utils/fs.ts +2 -1
  33. package/src/utils/logger.ts +9 -0
  34. package/src/utils/output.ts +40 -0
  35. package/tests/apply.test.ts +583 -0
  36. package/tests/ast-injector.test.ts +50 -0
  37. package/tests/build-tool.test.ts +3 -5
  38. package/tests/detect.test.ts +94 -0
  39. package/tests/doctor.test.ts +224 -0
  40. package/tests/init.test.ts +364 -0
  41. package/tests/install-wrapper.test.ts +76 -0
  42. package/tests/instructions.test.ts +61 -0
  43. package/tests/integration-install.test.ts +294 -0
  44. package/tests/logger.test.ts +100 -0
  45. package/tests/onboard.test.ts +258 -0
  46. package/tests/plan.test.ts +713 -0
  47. package/tests/workspace-build-tool.test.ts +75 -0
  48. package/.turbo/turbo-test.log +0 -16
  49. package/dist/chunk-MIHQGC3L.js +0 -1720
@@ -1,1720 +0,0 @@
1
- // src/utils/logger.ts
2
- import pc from "picocolors";
3
- var log = {
4
- /** Section header */
5
- header(text) {
6
- console.log();
7
- console.log(` ${pc.bold(pc.cyan("\u2726"))} ${pc.bold(text)}`);
8
- console.log();
9
- },
10
- /** Info item (used for actionable but not fully successful states) */
11
- info(text) {
12
- console.log(` ${pc.blue("\u2139")} ${text}`);
13
- },
14
- success(text) {
15
- console.log(` ${pc.green("\u2714")} ${text}`);
16
- },
17
- /** Warning item */
18
- warn(text) {
19
- console.log(` ${pc.yellow("\u26A0")} ${text}`);
20
- },
21
- /** Error item */
22
- error(text) {
23
- console.log(` ${pc.red("\u2718")} ${text}`);
24
- },
25
- /** Indented hint line */
26
- hint(text) {
27
- console.log(` ${pc.dim("\u2192")} ${text}`);
28
- },
29
- /** Blank line */
30
- blank() {
31
- console.log();
32
- },
33
- /** Final ready message */
34
- ready(text) {
35
- console.log();
36
- console.log(` ${pc.bold(pc.green("\u26A1"))} ${pc.bold(text)}`);
37
- console.log();
38
- },
39
- /** Code block for manual instructions */
40
- codeBlock(lines) {
41
- console.log();
42
- console.log(` ${pc.dim("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")}`);
43
- for (const line of lines) {
44
- console.log(` ${pc.dim("\u2502")} ${line.padEnd(48)}${pc.dim("\u2502")}`);
45
- }
46
- console.log(` ${pc.dim("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518")}`);
47
- console.log();
48
- },
49
- /** Dry-run prefix */
50
- dryRun(text) {
51
- console.log(` ${pc.blue("[dry-run]")} ${text}`);
52
- }
53
- };
54
-
55
- // src/commands/init.ts
56
- import path9 from "path";
57
-
58
- // src/utils/fs.ts
59
- import fs from "fs/promises";
60
- import path from "path";
61
- async function exists(filePath) {
62
- try {
63
- await fs.access(filePath);
64
- return true;
65
- } catch {
66
- return false;
67
- }
68
- }
69
- async function readFile(filePath) {
70
- try {
71
- return await fs.readFile(filePath, "utf-8");
72
- } catch {
73
- return null;
74
- }
75
- }
76
- async function writeFile(filePath, content) {
77
- await fs.mkdir(path.dirname(filePath), { recursive: true });
78
- await fs.writeFile(filePath, content, "utf-8");
79
- }
80
- async function removeDir(dirPath) {
81
- try {
82
- await fs.rm(dirPath, { recursive: true, force: true });
83
- } catch {
84
- }
85
- }
86
- async function readJSON(filePath) {
87
- const text = await readFile(filePath);
88
- if (!text) return null;
89
- try {
90
- return JSON.parse(text);
91
- } catch {
92
- return null;
93
- }
94
- }
95
- async function writeJSON(filePath, data) {
96
- await writeFile(filePath, JSON.stringify(data, null, 2) + "\n");
97
- }
98
-
99
- // src/utils/exec.ts
100
- import { execFile, exec as execCb } from "child_process";
101
- import { promisify } from "util";
102
- var execFileAsync = promisify(execFile);
103
- var execAsync = promisify(execCb);
104
- async function run(command, args, cwd) {
105
- const result = await execFileAsync(command, args, {
106
- cwd,
107
- timeout: 6e4,
108
- env: { ...process.env }
109
- });
110
- return { stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
111
- }
112
- async function shell(command, cwd) {
113
- const result = await execAsync(command, {
114
- cwd,
115
- timeout: 6e4,
116
- env: { ...process.env }
117
- });
118
- return { stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
119
- }
120
- async function which(bin) {
121
- try {
122
- const cmd = process.platform === "win32" ? "where" : "which";
123
- await run(cmd, [bin]);
124
- return true;
125
- } catch {
126
- return false;
127
- }
128
- }
129
-
130
- // src/detect/package-manager.ts
131
- import path2 from "path";
132
- async function detectPackageManager(root) {
133
- const checks = [
134
- ["bun.lockb", "bun"],
135
- ["bun.lock", "bun"],
136
- ["pnpm-lock.yaml", "pnpm"],
137
- ["yarn.lock", "yarn"],
138
- ["package-lock.json", "npm"]
139
- ];
140
- const results = await Promise.all(
141
- checks.map(async ([file, pm]) => {
142
- const isExist = await exists(path2.join(root, file));
143
- return { isExist, pm };
144
- })
145
- );
146
- for (const result of results) {
147
- if (result.isExist) {
148
- return result.pm;
149
- }
150
- }
151
- return "npm";
152
- }
153
- function getInstallCommand(pm, pkg) {
154
- switch (pm) {
155
- case "bun":
156
- return `bun add -D ${pkg}`;
157
- case "pnpm":
158
- return `pnpm add -D ${pkg}`;
159
- case "yarn":
160
- return `yarn add -D ${pkg}`;
161
- case "npm":
162
- return `npm install -D ${pkg}`;
163
- }
164
- }
165
- function getUninstallCommand(pm, pkg) {
166
- switch (pm) {
167
- case "bun":
168
- return `bun remove ${pkg}`;
169
- case "pnpm":
170
- return `pnpm remove ${pkg}`;
171
- case "yarn":
172
- return `yarn remove ${pkg}`;
173
- case "npm":
174
- return `npm uninstall ${pkg}`;
175
- }
176
- }
177
-
178
- // src/detect/build-tool.ts
179
- import path3 from "path";
180
- import { createRequire } from "module";
181
- function isPackageResolvable(pkgName, root) {
182
- try {
183
- const require2 = createRequire(path3.join(root, "package.json"));
184
- try {
185
- require2.resolve(`${pkgName}/package.json`, { paths: [root] });
186
- return true;
187
- } catch {
188
- require2.resolve(pkgName, { paths: [root] });
189
- return true;
190
- }
191
- } catch {
192
- return false;
193
- }
194
- }
195
- async function getResolvedPackageVersion(pkgName, root) {
196
- try {
197
- const require2 = createRequire(path3.join(root, "package.json"));
198
- const pkgJsonPath = require2.resolve(`${pkgName}/package.json`, { paths: [root] });
199
- const pkg = await readJSON(pkgJsonPath);
200
- return pkg?.version || null;
201
- } catch {
202
- return null;
203
- }
204
- }
205
- var SUPPORTED_PATTERNS = [
206
- {
207
- tool: "vite",
208
- files: [
209
- "vite.config.ts",
210
- "vite.config.js",
211
- "vite.config.mts",
212
- "vite.config.mjs",
213
- "vite.config.cjs",
214
- "vite.config.cts"
215
- ],
216
- label: "Vite"
217
- },
218
- {
219
- tool: "rspack",
220
- files: ["rspack.config.js", "rspack.config.ts", "rspack.config.mjs"],
221
- label: "Rspack"
222
- },
223
- {
224
- tool: "rsbuild",
225
- files: ["rsbuild.config.js", "rsbuild.config.ts", "rsbuild.config.mjs"],
226
- label: "Rsbuild"
227
- },
228
- {
229
- tool: "webpack",
230
- files: ["webpack.config.js", "webpack.config.ts", "webpack.config.mjs", "webpack.config.cjs"],
231
- label: "Webpack"
232
- },
233
- {
234
- tool: "esbuild",
235
- files: ["esbuild.config.js", "esbuild.config.ts", "esbuild.config.mjs", "build.js", "build.ts"],
236
- label: "esbuild"
237
- },
238
- {
239
- tool: "rollup",
240
- files: ["rollup.config.js", "rollup.config.ts", "rollup.config.mjs"],
241
- label: "Rollup"
242
- }
243
- ];
244
- var UNSUPPORTED_META = [
245
- { name: "Next.js", dep: "next", files: ["next.config.mjs", "next.config.js", "next.config.ts"] },
246
- { name: "Nuxt", dep: "nuxt", files: ["nuxt.config.ts", "nuxt.config.js"] },
247
- { name: "Remix", dep: "@remix-run/dev", files: ["remix.config.js", "remix.config.ts"] },
248
- { name: "Astro", dep: "astro", files: ["astro.config.mjs", "astro.config.ts"] },
249
- { name: "SvelteKit", dep: "@sveltejs/kit", files: ["svelte.config.js", "svelte.config.ts"] }
250
- ];
251
- function normalizeRelativePath(root, filePath) {
252
- const relative = path3.relative(root, filePath);
253
- const normalized = relative.split(path3.sep).join("/");
254
- return normalized || path3.basename(filePath);
255
- }
256
- function createTargets(root, packagePaths) {
257
- if (!packagePaths || packagePaths.length === 0) {
258
- return [{ packagePath: "", absolutePath: root }];
259
- }
260
- return packagePaths.map((pkg) => ({
261
- packagePath: pkg,
262
- absolutePath: pkg ? path3.join(root, pkg) : root
263
- }));
264
- }
265
- async function detectBuildTools(root, packagePaths) {
266
- const supported = [];
267
- const unsupported = /* @__PURE__ */ new Set();
268
- const targets = createTargets(root, packagePaths);
269
- for (const target of targets) {
270
- const pkg = await readJSON(path3.join(target.absolutePath, "package.json"));
271
- const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
272
- const scripts = pkg?.scripts || {};
273
- const supportedChecks = SUPPORTED_PATTERNS.map(
274
- (pattern) => detectPattern({
275
- pattern,
276
- workspaceRoot: root,
277
- targetRoot: target.absolutePath,
278
- packagePath: target.packagePath,
279
- allDeps,
280
- scripts
281
- })
282
- );
283
- const supportedResults = await Promise.all(supportedChecks);
284
- for (const result of supportedResults) {
285
- if (result) {
286
- supported.push(result);
287
- }
288
- }
289
- const unsupportedChecks = UNSUPPORTED_META.map(async (meta) => {
290
- if (!(meta.dep in allDeps)) return null;
291
- for (const file of meta.files) {
292
- if (await exists(path3.join(target.absolutePath, file))) {
293
- return meta.name;
294
- }
295
- }
296
- return null;
297
- });
298
- const unsupportedResults = await Promise.all(unsupportedChecks);
299
- for (const result of unsupportedResults) {
300
- if (result) {
301
- unsupported.add(result);
302
- }
303
- }
304
- }
305
- return { supported, unsupported: Array.from(unsupported) };
306
- }
307
- async function detectPattern({
308
- pattern,
309
- workspaceRoot,
310
- targetRoot,
311
- packagePath,
312
- allDeps,
313
- scripts
314
- }) {
315
- let hasDep;
316
- let resolvedVersion = null;
317
- if (pattern.tool === "rspack") {
318
- const depName = allDeps["@rspack/cli"] ? "@rspack/cli" : "@rspack/core";
319
- hasDep = !!allDeps["@rspack/cli"] || !!allDeps["@rspack/core"] || isPackageResolvable("@rspack/core", targetRoot);
320
- if (hasDep) {
321
- resolvedVersion = allDeps[depName] || await getResolvedPackageVersion("@rspack/core", targetRoot);
322
- }
323
- } else if (pattern.tool === "webpack") {
324
- const depName = allDeps["webpack"] ? "webpack" : "webpack-cli";
325
- hasDep = !!allDeps["webpack"] || !!allDeps["webpack-cli"] || isPackageResolvable("webpack", targetRoot);
326
- if (hasDep) {
327
- resolvedVersion = allDeps[depName] || await getResolvedPackageVersion("webpack", targetRoot);
328
- }
329
- } else if (pattern.tool === "rsbuild") {
330
- hasDep = !!allDeps["@rsbuild/core"] || isPackageResolvable("@rsbuild/core", targetRoot);
331
- } else {
332
- hasDep = !!allDeps[pattern.tool] || isPackageResolvable(pattern.tool, targetRoot);
333
- }
334
- let detectedFile = "";
335
- if (pattern.tool === "esbuild" && !hasDep) {
336
- return null;
337
- }
338
- for (const file of pattern.files) {
339
- if (await exists(path3.join(targetRoot, file))) {
340
- detectedFile = file;
341
- break;
342
- }
343
- }
344
- if (hasDep && !detectedFile && (pattern.tool === "esbuild" || pattern.tool === "rollup" || pattern.tool === "webpack" || pattern.tool === "rspack" || pattern.tool === "rsbuild")) {
345
- for (const cmd of Object.values(scripts)) {
346
- if (cmd.includes("node ")) {
347
- const match = cmd.match(/node\s+([^\s]+\.(js|mjs|cjs|ts))/);
348
- if (match && match[1]) {
349
- if (await exists(path3.join(targetRoot, match[1]))) {
350
- if (cmd.includes(pattern.tool) || match[1].includes(pattern.tool)) {
351
- detectedFile = match[1];
352
- break;
353
- }
354
- }
355
- }
356
- } else if (cmd.includes(`${pattern.tool} `)) {
357
- if (pattern.tool === "webpack" || pattern.tool === "rspack") {
358
- const configMatch = cmd.match(/--config\s+([^\s]+)/);
359
- if (configMatch && configMatch[1]) {
360
- if (await exists(path3.join(targetRoot, configMatch[1]))) {
361
- detectedFile = configMatch[1];
362
- break;
363
- }
364
- }
365
- }
366
- if (!detectedFile) {
367
- detectedFile = "package.json (scripts)";
368
- break;
369
- }
370
- }
371
- }
372
- }
373
- if (!detectedFile) {
374
- return null;
375
- }
376
- let isLegacyRspack = false;
377
- let isLegacyWebpack = false;
378
- if (pattern.tool === "rspack") {
379
- const version = resolvedVersion;
380
- if (version && (version.includes("0.3.") || version.includes("0.2.") || version.includes("0.1."))) {
381
- isLegacyRspack = true;
382
- }
383
- } else if (pattern.tool === "webpack") {
384
- const version = resolvedVersion;
385
- if (version && version.includes("^4") || version?.startsWith("4.")) {
386
- isLegacyWebpack = true;
387
- }
388
- }
389
- const absoluteConfig = path3.join(targetRoot, detectedFile);
390
- const relativeConfig = normalizeRelativePath(workspaceRoot, absoluteConfig);
391
- return {
392
- tool: pattern.tool,
393
- configPath: relativeConfig,
394
- label: `${pattern.label} (${relativeConfig})${isLegacyRspack ? " [Legacy]" : ""}${isLegacyWebpack ? " [Webpack 4]" : ""}`,
395
- isLegacyRspack,
396
- isLegacyWebpack,
397
- packagePath: packagePath || void 0
398
- };
399
- }
400
- function resolveInjectionTarget(detections) {
401
- if (detections.length === 0) return null;
402
- if (detections.length === 1) return detections[0];
403
- return "ambiguous";
404
- }
405
-
406
- // src/detect/framework.ts
407
- import path4 from "path";
408
- import { createRequire as createRequire2 } from "module";
409
- var META_FRAMEWORK_MAP = {
410
- next: "react",
411
- nuxt: "vue",
412
- "@remix-run/react": "react",
413
- "@remix-run/dev": "react",
414
- "@vue/nuxt": "vue",
415
- "vite-plugin-vue": "vue",
416
- "@vitejs/plugin-vue": "vue",
417
- "@vitejs/plugin-react": "react",
418
- "@vitejs/plugin-react-swc": "react"
419
- };
420
- var SUPPORTED_FRAMEWORKS = [
421
- { framework: "react", deps: ["react", "react-dom"] },
422
- { framework: "vue", deps: ["vue"] }
423
- ];
424
- var UNSUPPORTED_FRAMEWORKS = [
425
- { name: "Solid", dep: "solid-js" },
426
- { name: "Svelte", dep: "svelte" },
427
- { name: "Angular", dep: "@angular/core" },
428
- { name: "Preact", dep: "preact" },
429
- { name: "Lit", dep: "lit" }
430
- ];
431
- function isPackageResolvable2(pkgName, root) {
432
- try {
433
- const require2 = createRequire2(path4.join(root, "package.json"));
434
- try {
435
- require2.resolve(`${pkgName}/package.json`, { paths: [root] });
436
- return true;
437
- } catch {
438
- require2.resolve(pkgName, { paths: [root] });
439
- return true;
440
- }
441
- } catch {
442
- return false;
443
- }
444
- }
445
- async function detectFrameworks(root) {
446
- const pkg = await readJSON(path4.join(root, "package.json"));
447
- const allDeps = {
448
- ...pkg?.dependencies || {},
449
- ...pkg?.devDependencies || {},
450
- ...pkg?.peerDependencies || {}
451
- };
452
- const supportedSet = /* @__PURE__ */ new Set();
453
- const unsupported = [];
454
- const isTest = root.includes("/mock/root");
455
- for (const [metaPkg, framework] of Object.entries(META_FRAMEWORK_MAP)) {
456
- if (metaPkg in allDeps || !isTest && isPackageResolvable2(metaPkg, root)) {
457
- supportedSet.add(framework);
458
- }
459
- }
460
- for (const { framework, deps } of SUPPORTED_FRAMEWORKS) {
461
- if (supportedSet.has(framework)) continue;
462
- for (const dep of deps) {
463
- if (dep in allDeps || !isTest && isPackageResolvable2(dep, root)) {
464
- supportedSet.add(framework);
465
- break;
466
- }
467
- }
468
- }
469
- for (const fw of UNSUPPORTED_FRAMEWORKS) {
470
- if (fw.dep in allDeps || !isTest && isPackageResolvable2(fw.dep, root)) {
471
- unsupported.push(fw);
472
- }
473
- }
474
- return {
475
- supported: Array.from(supportedSet),
476
- unsupported
477
- };
478
- }
479
-
480
- // src/detect/ide.ts
481
- import path5 from "path";
482
- var SUPPORTED_IDE = "vscode";
483
- async function detectIDE(root) {
484
- const detected = /* @__PURE__ */ new Map();
485
- if (process.env.CURSOR_TRACE_DIR || process.env.CURSOR_CHANNEL) {
486
- detected.set("Cursor", { ide: "cursor", supported: true });
487
- }
488
- if (process.env.TRAE_APP_DIR || process.env.__CFBundleIdentifier === "com.byteocean.trae" || process.env.COCO_IDE_PLUGIN_TYPE === "Trae" || process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes("trae")) {
489
- detected.set("Trae", { ide: "trae", supported: true });
490
- }
491
- if (process.env.ZED_TERM) {
492
- detected.set("Zed", { ide: "Zed", supported: false });
493
- }
494
- if (process.env.WINDSURF_APP_DIR || process.env.WINDSURF_CHANNEL || process.env.__CFBundleIdentifier === "com.codeium.windsurf" || process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes("windsurf")) {
495
- detected.set("Windsurf", { ide: "Windsurf", supported: false });
496
- }
497
- const [hasTrae, hasCursor, hasVscode, hasIdea] = await Promise.all([
498
- exists(path5.join(root, ".trae")),
499
- exists(path5.join(root, ".cursor")),
500
- exists(path5.join(root, ".vscode")),
501
- exists(path5.join(root, ".idea"))
502
- ]);
503
- if (hasTrae && !detected.has("Trae")) {
504
- detected.set("Trae", { ide: "trae", supported: true });
505
- }
506
- if (hasCursor && !detected.has("Cursor")) {
507
- detected.set("Cursor", { ide: "cursor", supported: true });
508
- }
509
- if (hasVscode && !detected.has("vscode")) {
510
- detected.set("vscode", { ide: SUPPORTED_IDE, supported: true });
511
- }
512
- if (hasIdea && !detected.has("JetBrains IDE")) {
513
- detected.set("JetBrains IDE", { ide: "JetBrains IDE", supported: false });
514
- }
515
- if (detected.size === 0 && process.env.TERM_PROGRAM === "vscode") {
516
- detected.set("vscode", { ide: SUPPORTED_IDE, supported: true });
517
- }
518
- return {
519
- detected: Array.from(detected.values())
520
- };
521
- }
522
-
523
- // src/detect/provider.ts
524
- import path6 from "path";
525
- var KNOWN_CLI_TOOLS = [
526
- { id: "claude-code", bin: "claude", label: "Claude Code", supported: true },
527
- { id: "coco", bin: "coco", label: "Trae CLI (Coco)", supported: true },
528
- { id: "codex", bin: "codex", label: "Codex CLI", supported: true },
529
- { id: "gemini", bin: "gemini", label: "Gemini CLI", supported: true }
530
- ];
531
- var KNOWN_IDE_PLUGINS = [
532
- { id: "claude-code", extId: "anthropic.claude-code", label: "Claude Code", supported: true },
533
- { id: "copilot", extId: "github.copilot", label: "GitHub Copilot", supported: true },
534
- { id: "codex", extId: "openai.chatgpt", label: "Codex (ChatGPT)", supported: true },
535
- { id: "gemini", extId: "google.geminicodeassist", label: "Gemini Code Assist", supported: true }
536
- ];
537
- async function detectProviders(root) {
538
- const detectedMap = /* @__PURE__ */ new Map();
539
- const cliChecks = KNOWN_CLI_TOOLS.map(async (tool) => {
540
- if (await which(tool.bin)) {
541
- detectedMap.set(tool.id, {
542
- id: tool.id,
543
- label: tool.label,
544
- supported: tool.supported,
545
- providerModes: ["cli"],
546
- preferredMode: "cli"
547
- });
548
- }
549
- });
550
- await Promise.all(cliChecks);
551
- const extensionsJsonPath = path6.join(root, ".vscode", "extensions.json");
552
- let recommendedExts = [];
553
- if (await exists(extensionsJsonPath)) {
554
- try {
555
- const extData = await readJSON(extensionsJsonPath);
556
- if (extData && Array.isArray(extData.recommendations)) {
557
- recommendedExts = extData.recommendations.map((e) => e.toLowerCase());
558
- }
559
- } catch {
560
- }
561
- }
562
- const homeDir = process.env.HOME || process.env.USERPROFILE || "";
563
- const globalExtDir = path6.join(homeDir, ".vscode", "extensions");
564
- const globalExtExists = await exists(globalExtDir);
565
- let installedExtensionFolders = [];
566
- if (globalExtExists) {
567
- try {
568
- const { readdir } = await import("fs/promises");
569
- installedExtensionFolders = await readdir(globalExtDir);
570
- const obsoletePath = path6.join(globalExtDir, ".obsolete");
571
- if (await exists(obsoletePath)) {
572
- try {
573
- const obsoleteData = await readJSON(obsoletePath);
574
- if (obsoleteData) {
575
- const obsoleteKeys = Object.keys(obsoleteData);
576
- installedExtensionFolders = installedExtensionFolders.filter((folder) => {
577
- return !obsoleteKeys.includes(folder);
578
- });
579
- }
580
- } catch {
581
- }
582
- }
583
- } catch {
584
- }
585
- }
586
- for (const plugin of KNOWN_IDE_PLUGINS) {
587
- let isInstalled = false;
588
- if (recommendedExts.includes(plugin.extId.toLowerCase())) {
589
- isInstalled = true;
590
- } else if (installedExtensionFolders.some((f) => {
591
- const lower = f.toLowerCase();
592
- return lower === plugin.extId.toLowerCase() || lower.startsWith(plugin.extId.toLowerCase() + "-");
593
- })) {
594
- isInstalled = true;
595
- }
596
- if (isInstalled) {
597
- const existing = detectedMap.get(plugin.id);
598
- if (existing) {
599
- existing.providerModes.push("extension");
600
- existing.preferredMode = "extension";
601
- } else {
602
- detectedMap.set(plugin.id, {
603
- id: plugin.id,
604
- label: plugin.label,
605
- supported: plugin.supported,
606
- providerModes: ["extension"],
607
- preferredMode: "extension"
608
- });
609
- }
610
- }
611
- }
612
- return { detected: Array.from(detectedMap.values()) };
613
- }
614
-
615
- // src/inject/ast-injector.ts
616
- import path7 from "path";
617
- import { loadFile, writeFile as writeAstFile } from "magicast";
618
-
619
- // src/inject/strategies/vite.ts
620
- import { addVitePlugin } from "magicast/helpers";
621
- var ViteStrategy = class {
622
- name = "Vite";
623
- supports(tool) {
624
- return tool === "vite";
625
- }
626
- inject({ mod, detection }) {
627
- addVitePlugin(mod, {
628
- from: "@inspecto-dev/plugin",
629
- constructor: "inspecto",
630
- imported: "vitePlugin"
631
- });
632
- }
633
- getManualInstructions(detection, reason) {
634
- return [
635
- `import { vitePlugin as inspecto } from '@inspecto-dev/plugin'`,
636
- "",
637
- "// Add to your plugins array:",
638
- `plugins: [`,
639
- ` process.env.NODE_ENV !== 'production' && inspecto(),`,
640
- ` ...otherPlugins`,
641
- `].filter(Boolean)`
642
- ];
643
- }
644
- };
645
-
646
- // src/inject/strategies/webpack.ts
647
- var WebpackStrategy = class {
648
- name = "Webpack";
649
- supports(tool) {
650
- return tool === "webpack";
651
- }
652
- inject(options) {
653
- throw new Error("Webpack requires manual plugin configuration");
654
- }
655
- getManualInstructions(detection, reason) {
656
- const importPkg = detection.isLegacyWebpack ? "@inspecto-dev/plugin/legacy/webpack4" : "@inspecto-dev/plugin";
657
- const pluginName = detection.isLegacyWebpack ? "webpack4Plugin" : "webpackPlugin";
658
- const pluginCall = detection.isLegacyWebpack ? `process.env.NODE_ENV !== 'production' && inspecto({
659
- pathType: 'absolute',
660
- escapeTags: ['Transition', 'AnimatePresence'],
661
- })` : `process.env.NODE_ENV !== 'production' && inspecto()`;
662
- return [
663
- `import { ${pluginName} as inspecto } from '${importPkg}'`,
664
- "",
665
- "// Add to your plugins array:",
666
- `plugins: [`,
667
- ` ${pluginCall},`,
668
- ` ...otherPlugins`,
669
- `].filter(Boolean)`
670
- ];
671
- }
672
- };
673
-
674
- // src/inject/strategies/rspack.ts
675
- var RspackStrategy = class {
676
- name = "Rspack";
677
- supports(tool) {
678
- return tool === "rspack";
679
- }
680
- inject(options) {
681
- throw new Error("Rspack requires manual plugin configuration");
682
- }
683
- getManualInstructions(detection, reason) {
684
- const importPkg = detection.isLegacyRspack ? "@inspecto-dev/plugin/legacy/rspack" : "@inspecto-dev/plugin";
685
- const pluginCall = detection.isLegacyRspack ? `process.env.NODE_ENV !== 'production' && inspecto({
686
- pathType: 'absolute',
687
- escapeTags: ['Transition', 'AnimatePresence'],
688
- })` : `process.env.NODE_ENV !== 'production' && inspecto()`;
689
- return [
690
- `import { rspackPlugin as inspecto } from '${importPkg}'`,
691
- "",
692
- "// Add to your plugins array:",
693
- `plugins: [`,
694
- ` ${pluginCall},`,
695
- ` ...otherPlugins`,
696
- `].filter(Boolean)`
697
- ];
698
- }
699
- };
700
-
701
- // src/inject/strategies/rsbuild.ts
702
- var RsbuildStrategy = class {
703
- name = "Rsbuild";
704
- supports(tool) {
705
- return tool === "rsbuild";
706
- }
707
- inject(options) {
708
- throw new Error("Rsbuild requires manual plugin configuration due to nested structure");
709
- }
710
- getManualInstructions(detection, reason) {
711
- return [
712
- `import { rspackPlugin as inspecto } from '@inspecto-dev/plugin'`,
713
- "",
714
- "// Add to tools.rspack:",
715
- `tools: {`,
716
- ` rspack: {`,
717
- ` plugins: [`,
718
- ` process.env.NODE_ENV !== 'production' && inspecto(),`,
719
- ` ]`,
720
- ` }`,
721
- `}`
722
- ];
723
- }
724
- };
725
-
726
- // src/inject/strategies/esbuild.ts
727
- var EsbuildStrategy = class {
728
- name = "esbuild";
729
- supports(tool) {
730
- return tool === "esbuild";
731
- }
732
- inject(options) {
733
- throw new Error("Esbuild requires manual plugin configuration");
734
- }
735
- getManualInstructions(detection, reason) {
736
- return [
737
- `1. Update your esbuild config (${detection.configPath}):`,
738
- `import { esbuildPlugin as inspecto } from '@inspecto-dev/plugin'`,
739
- "",
740
- "// Add to your plugins array:",
741
- `plugins: [`,
742
- ` process.env.NODE_ENV !== 'production' && inspecto(),`,
743
- ` ...otherPlugins`,
744
- `].filter(Boolean)`,
745
- "",
746
- "2. Initialize the client in your app entry (e.g., main.js / index.js):",
747
- `import { mountInspector } from '@inspecto-dev/core'`,
748
- "",
749
- "// Call this before your app renders",
750
- `if (process.env.NODE_ENV !== 'production') {`,
751
- ` mountInspector()`,
752
- `}`
753
- ];
754
- }
755
- };
756
-
757
- // src/inject/strategies/rollup.ts
758
- var RollupStrategy = class {
759
- name = "Rollup";
760
- supports(tool) {
761
- return tool === "rollup";
762
- }
763
- inject(options) {
764
- throw new Error("Rollup requires manual plugin configuration");
765
- }
766
- getManualInstructions(detection, reason) {
767
- return [
768
- `1. Update your rollup config (${detection.configPath}):`,
769
- `import { rollupPlugin as inspecto } from '@inspecto-dev/plugin'`,
770
- "",
771
- "// Add to your plugins array:",
772
- `plugins: [`,
773
- ` process.env.NODE_ENV !== 'production' && inspecto(),`,
774
- ` ...otherPlugins`,
775
- `].filter(Boolean)`,
776
- "",
777
- "2. Initialize the client in your app entry (e.g., main.js / index.js):",
778
- `import { mountInspector } from '@inspecto-dev/core'`,
779
- "",
780
- "// Call this before your app renders",
781
- `if (process.env.NODE_ENV !== 'production') {`,
782
- ` mountInspector()`,
783
- `}`
784
- ];
785
- }
786
- };
787
-
788
- // src/inject/strategies/index.ts
789
- var STRATEGIES = [
790
- new ViteStrategy(),
791
- new WebpackStrategy(),
792
- new RspackStrategy(),
793
- new RsbuildStrategy(),
794
- new EsbuildStrategy(),
795
- new RollupStrategy()
796
- ];
797
-
798
- // src/inject/ast-injector.ts
799
- function printManualInstructions(strategy, detection, reason) {
800
- log.warn(`Could not automatically configure ${detection.configPath}`);
801
- log.hint(`(reason: ${reason})`);
802
- log.blank();
803
- log.hint("Please add the following manually:");
804
- if (strategy) {
805
- const instructions = strategy.getManualInstructions(detection, reason);
806
- log.codeBlock(instructions);
807
- } else {
808
- log.error(`Unsupported build tool: ${detection.tool}`);
809
- }
810
- }
811
- function isAlreadyInjected(content) {
812
- const normalized = content.replace(/\s+/g, " ");
813
- const importPlugin = /import\s+(.+?)\s+from\s+['"]@inspecto-dev\/plugin['"]/g;
814
- const requirePlugin = /require\(['"]@inspecto-dev\/plugin['"]\)/;
815
- const legacyImport = /import\s+.*ai-dev-inspector/.test(normalized);
816
- const legacyRequire = /require\(['"]ai-dev-inspector['"]\)/.test(normalized);
817
- if (legacyImport || legacyRequire || requirePlugin.test(normalized)) return true;
818
- let match;
819
- importPlugin.lastIndex = 0;
820
- while (match = importPlugin.exec(normalized)) {
821
- const importClause = match[1] || "";
822
- if (/inspecto/.test(importClause) || /vitePlugin/.test(importClause)) {
823
- return true;
824
- }
825
- }
826
- return false;
827
- }
828
- async function injectPlugin(root, detection, dryRun) {
829
- const configPath = path7.join(root, detection.configPath);
830
- const mutations = [];
831
- const strategy = STRATEGIES.find((s) => s.supports(detection.tool));
832
- const content = await readFile(configPath);
833
- if (!content) {
834
- printManualInstructions(strategy, detection, "config file not readable");
835
- return { success: false, mutations, failureReason: "config file not readable" };
836
- }
837
- if (isAlreadyInjected(content)) {
838
- log.success(`Plugin already configured in ${detection.configPath} (skipped)`);
839
- mutations.push({
840
- type: "file_modified",
841
- path: detection.configPath,
842
- description: "Previously configured inspecto() plugin"
843
- });
844
- return { success: true, mutations };
845
- }
846
- if (!strategy) {
847
- printManualInstructions(
848
- strategy,
849
- detection,
850
- `No injection strategy found for ${detection.tool}`
851
- );
852
- return { success: false, mutations, failureReason: "No strategy found" };
853
- }
854
- if (dryRun) {
855
- log.dryRun(`Would automatically configure plugin in ${detection.configPath}`);
856
- return { success: true, mutations: [] };
857
- }
858
- try {
859
- const mod = await loadFile(configPath);
860
- strategy.inject({
861
- mod,
862
- detection
863
- });
864
- await writeAstFile(mod, configPath);
865
- mutations.push({
866
- type: "file_modified",
867
- path: detection.configPath,
868
- description: "Automatically configured inspecto() plugin"
869
- });
870
- log.success(`Configured plugin in ${detection.configPath}`);
871
- return { success: true, mutations };
872
- } catch (err) {
873
- printManualInstructions(
874
- strategy,
875
- detection,
876
- `Automatic configuration unavailable: ${err instanceof Error ? err.message : String(err)}`
877
- );
878
- return {
879
- success: false,
880
- mutations,
881
- failureReason: "Automatic configuration unavailable"
882
- };
883
- }
884
- }
885
-
886
- // src/inject/gitignore.ts
887
- import path8 from "path";
888
- var DEFAULT_RULES = [".inspecto/install.lock", ".inspecto/cache.json", ".inspecto/*.local.json"];
889
- var SHARED_RULES = [".inspecto/install.lock", ".inspecto/cache.json", ".inspecto/*.local.json"];
890
- async function updateGitignore(root, shared, dryRun) {
891
- const gitignorePath = path8.join(root, ".gitignore");
892
- let content = await readFile(gitignorePath) ?? "";
893
- const desiredRules = shared ? SHARED_RULES : DEFAULT_RULES;
894
- const hasGlobalRule = content.match(/^\.inspecto\/\s*$/m) !== null;
895
- if (hasGlobalRule) {
896
- content = content.replace(/^\.inspecto\/\s*$/gm, SHARED_RULES.join("\n"));
897
- if (!dryRun) {
898
- await writeFile(gitignorePath, content);
899
- }
900
- log.success("Updated .gitignore: .inspecto/ is no longer fully ignored");
901
- return;
902
- }
903
- const missingRules = desiredRules.filter((rule) => !content.includes(rule));
904
- if (missingRules.length === 0) {
905
- return;
906
- }
907
- const section = "\n# Inspecto\n" + missingRules.join("\n") + "\n";
908
- content = content.trimEnd() + "\n" + section;
909
- if (dryRun) {
910
- log.dryRun(`Would update .gitignore with: ${missingRules.join(", ")}`);
911
- } else {
912
- await writeFile(gitignorePath, content);
913
- log.success("Updated .gitignore");
914
- }
915
- }
916
- async function cleanGitignore(root) {
917
- const gitignorePath = path8.join(root, ".gitignore");
918
- const content = await readFile(gitignorePath);
919
- if (!content) return;
920
- const cleaned = content.replace(/^# Inspecto\s*$/m, "").replace(/^\.inspecto\/?\s*$/gm, "").replace(/^\.inspecto\/install\.lock\s*$/gm, "").replace(/^\.inspecto\/cache\.json\s*$/gm, "").replace(/^\.inspecto\/\*\.local\.json\s*$/gm, "").replace(/\n{3,}/g, "\n\n");
921
- await writeFile(gitignorePath, cleaned);
922
- }
923
-
924
- // src/inject/extension.ts
925
- var EXTENSION_ID = "inspecto.inspecto";
926
- var VSCODE_PATHS = {
927
- darwin: [
928
- "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
929
- "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders",
930
- `${process.env.HOME}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`
931
- ],
932
- linux: ["/usr/bin/code", "/usr/share/code/bin/code", "/snap/bin/code", "/usr/bin/code-insiders"],
933
- win32: [
934
- `${process.env.LOCALAPPDATA}\\Programs\\Microsoft VS Code\\bin\\code.cmd`,
935
- `${process.env.LOCALAPPDATA}\\Programs\\Microsoft VS Code Insiders\\bin\\code-insiders.cmd`,
936
- `${process.env.PROGRAMFILES}\\Microsoft VS Code\\bin\\code.cmd`
937
- ]
938
- };
939
- async function findVSCodeBinary() {
940
- const platform = process.platform;
941
- const candidates = VSCODE_PATHS[platform] || [];
942
- for (const candidate of candidates) {
943
- if (await exists(candidate)) {
944
- return candidate;
945
- }
946
- }
947
- if (await which("code-insiders")) {
948
- return "code-insiders";
949
- }
950
- return null;
951
- }
952
- async function tryOpenURI(uri) {
953
- try {
954
- const platform = process.platform;
955
- if (platform === "win32") {
956
- await shell(`cmd /c start "" "${uri}"`);
957
- } else {
958
- const openCmd = platform === "darwin" ? "open" : "xdg-open";
959
- await shell(`${openCmd} "${uri}"`);
960
- }
961
- return true;
962
- } catch {
963
- return false;
964
- }
965
- }
966
- async function installExtension(dryRun, ide) {
967
- if (dryRun) {
968
- log.dryRun("Would attempt to install VS Code extension");
969
- return null;
970
- }
971
- const isVSCode = !ide || ide === "vscode";
972
- if (isVSCode) {
973
- if (await which("code")) {
974
- try {
975
- await run("code", ["--install-extension", EXTENSION_ID]);
976
- log.success("VS Code extension installed via CLI");
977
- return { type: "extension_installed", id: EXTENSION_ID };
978
- } catch {
979
- }
980
- }
981
- const codePath = await findVSCodeBinary();
982
- if (codePath) {
983
- try {
984
- await run(codePath, ["--install-extension", EXTENSION_ID]);
985
- log.success("VS Code extension installed via binary path");
986
- log.info(
987
- 'Tip: Add "code" to your PATH to help Inspecto detect other AI tools in the future'
988
- );
989
- return { type: "extension_installed", id: EXTENSION_ID };
990
- } catch {
991
- }
992
- }
993
- const uri = `vscode:extension/${EXTENSION_ID}`;
994
- if (await tryOpenURI(uri)) {
995
- log.warn("Opened extension page in VS Code");
996
- log.hint('Please click "Install" in the opened VS Code window to complete setup.');
997
- return { type: "extension_installed", id: EXTENSION_ID, manual_action_required: true };
998
- }
999
- log.warn("Could not auto-install VS Code extension");
1000
- log.hint("Please install it manually to enable Inspector features:");
1001
- log.hint(" 1. Open VS Code");
1002
- log.hint(" 2. Press Ctrl+Shift+X (or Cmd+Shift+X)");
1003
- log.hint(' 3. Search for "Inspecto"');
1004
- log.hint(` Or visit: https://marketplace.visualstudio.com/items?itemName=${EXTENSION_ID}`);
1005
- return null;
1006
- }
1007
- log.warn(`Could not auto-install extension for ${ide}`);
1008
- log.hint("Please install it manually to enable Inspector features:");
1009
- log.hint(" 1. Download the latest .vsix file from Inspecto releases");
1010
- log.hint(` 2. Open ${ide}`);
1011
- log.hint(" 3. Open the Command Palette (Ctrl+Shift+P or Cmd+Shift+P)");
1012
- log.hint(' 4. Type and select "Extensions: Install from VSIX..."');
1013
- log.hint(" 5. Select the downloaded .vsix file");
1014
- return null;
1015
- }
1016
- async function isExtensionInstalled() {
1017
- try {
1018
- if (await which("code")) {
1019
- const { stdout } = await run("code", ["--list-extensions"]);
1020
- return stdout.toLowerCase().includes(EXTENSION_ID);
1021
- }
1022
- const codePath = await findVSCodeBinary();
1023
- if (codePath) {
1024
- const { stdout } = await run(codePath, ["--list-extensions"]);
1025
- return stdout.toLowerCase().includes(EXTENSION_ID);
1026
- }
1027
- return false;
1028
- } catch {
1029
- return false;
1030
- }
1031
- }
1032
-
1033
- // src/prompts.ts
1034
- import prompts from "prompts";
1035
- async function promptIDEChoice(detections) {
1036
- if (!process.stdin.isTTY) {
1037
- log.warn("Multiple IDEs detected but stdin is not interactive");
1038
- log.hint(`Using: ${detections[0].ide} (first match)`);
1039
- return detections[0];
1040
- }
1041
- const { choice } = await prompts({
1042
- type: "select",
1043
- name: "choice",
1044
- message: "Detected multiple IDEs, please choose one:",
1045
- choices: detections.map((d, i) => ({
1046
- title: `${d.ide} ${d.supported ? "(supported)" : "(unsupported/limited)"}`,
1047
- value: i
1048
- }))
1049
- });
1050
- if (choice === void 0) return null;
1051
- return detections[choice];
1052
- }
1053
- async function promptProviderChoice(detections) {
1054
- if (!process.stdin.isTTY) {
1055
- log.warn("Multiple AI tools detected but stdin is not interactive");
1056
- log.hint(`Using: ${detections[0].label} (first match)`);
1057
- return detections[0];
1058
- }
1059
- const { choice } = await prompts({
1060
- type: "select",
1061
- name: "choice",
1062
- message: "Detected multiple providers, please choose one:",
1063
- choices: detections.map((d, i) => {
1064
- const modeLabels = d.providerModes.map(
1065
- (mode) => mode === "extension" ? "VS Code Extension" : "Terminal CLI"
1066
- );
1067
- const modeStr = modeLabels.join(" & ");
1068
- return {
1069
- title: `${d.label} ${d.supported ? `(supported ${modeStr})` : "(unsupported/limited)"}`,
1070
- value: i
1071
- };
1072
- })
1073
- });
1074
- if (choice === void 0) return null;
1075
- return detections[choice];
1076
- }
1077
- async function promptConfigChoice(detections) {
1078
- if (!process.stdin.isTTY) {
1079
- log.warn("Multiple config files detected but stdin is not interactive");
1080
- log.hint(`Using: ${detections[0].label} (first match)`);
1081
- return detections[0];
1082
- }
1083
- const choices = detections.map((d, i) => ({
1084
- title: d.label,
1085
- value: i
1086
- }));
1087
- choices.push({
1088
- title: "Skip (I'll configure manually)",
1089
- value: -1
1090
- });
1091
- const { choice } = await prompts({
1092
- type: "select",
1093
- name: "choice",
1094
- message: "Detected multiple build tool configs, please choose one to inject:",
1095
- choices
1096
- });
1097
- if (choice === void 0 || choice === -1) return null;
1098
- return detections[choice];
1099
- }
1100
- async function promptUnsupportedFrameworkContinue() {
1101
- if (!process.stdin.isTTY) {
1102
- log.error("Unsupported framework detected in non-interactive environment.");
1103
- log.hint("Use --force to skip this check and continue anyway.");
1104
- return false;
1105
- }
1106
- const { confirm } = await prompts({
1107
- type: "confirm",
1108
- name: "confirm",
1109
- message: "Inspecto may not work properly. Do you want to continue anyway?",
1110
- initial: false
1111
- });
1112
- return !!confirm;
1113
- }
1114
-
1115
- // src/instructions.ts
1116
- function printNuxtManualInstructions() {
1117
- log.blank();
1118
- log.hint("To enable Inspecto in Nuxt, update your nuxt.config.ts:");
1119
- console.log(`\x1B[36m
1120
- import { vitePlugin as inspecto } from '@inspecto-dev/plugin'
1121
- export default defineNuxtConfig({
1122
- vite: {
1123
- plugins: [inspecto()]
1124
- }
1125
- })
1126
- \x1B[0m`);
1127
- log.hint("And create a Nuxt plugin at plugins/inspecto.client.ts:");
1128
- console.log(`\x1B[36m
1129
- export default defineNuxtPlugin(() => {
1130
- if (import.meta.dev) {
1131
- import('@inspecto-dev/core').then(({ mountInspector }) => {
1132
- mountInspector()
1133
- })
1134
- }
1135
- })
1136
- \x1B[0m`);
1137
- }
1138
- function printNextJsManualInstructions() {
1139
- log.blank();
1140
- log.hint("To enable Inspecto in Next.js, update your next.config.mjs:");
1141
- console.log(`\x1B[36m
1142
- import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'
1143
- const nextConfig = {
1144
- webpack: (config, { dev, isServer }) => {
1145
- if (dev && !isServer) config.plugins.push(inspecto())
1146
- return config
1147
- }
1148
- }
1149
- export default nextConfig
1150
- \x1B[0m`);
1151
- log.hint("And initialize the client dynamically in your app/layout.tsx (or pages/_app.tsx):");
1152
- console.log(`\x1B[36m
1153
- 'use client'
1154
- import { useEffect } from 'react'
1155
-
1156
- export default function RootLayout({ children }) {
1157
- useEffect(() => {
1158
- if (process.env.NODE_ENV !== 'production') {
1159
- import('@inspecto-dev/core').then(({ mountInspector }) => {
1160
- mountInspector({ serverUrl: 'http://127.0.0.1:5678' })
1161
- })
1162
- }
1163
- }, [])
1164
- return <html><body>{children}</body></html>
1165
- }
1166
- \x1B[0m`);
1167
- }
1168
-
1169
- // src/commands/init.ts
1170
- async function init(options) {
1171
- const root = process.cwd();
1172
- const mutations = [];
1173
- const normalizedPackages = normalizePackageList(options.packages);
1174
- const verifiedPackages = [];
1175
- for (const pkg of normalizedPackages) {
1176
- if (!pkg) {
1177
- verifiedPackages.push(pkg);
1178
- continue;
1179
- }
1180
- const absolutePath = path9.join(root, pkg);
1181
- if (await exists(absolutePath)) {
1182
- verifiedPackages.push(pkg);
1183
- } else {
1184
- log.warn(`Package path "${pkg}" not found (skipping)`);
1185
- log.hint("Ensure --packages values are relative to the project root");
1186
- }
1187
- }
1188
- if (normalizedPackages.length > 0 && verifiedPackages.length === 0) {
1189
- log.error("No valid packages found from --packages input");
1190
- return;
1191
- }
1192
- log.header("Inspecto Setup");
1193
- if (!await exists(path9.join(root, "package.json"))) {
1194
- log.error("No package.json found in current directory");
1195
- log.hint("Run this command from your project root");
1196
- return;
1197
- }
1198
- const [pm, frameworkResult, buildResult, ideProbe, providerProbe] = await Promise.all([
1199
- detectPackageManager(root),
1200
- detectFrameworks(root),
1201
- detectBuildTools(root, verifiedPackages.length > 0 ? verifiedPackages : void 0),
1202
- detectIDE(root),
1203
- detectProviders(root)
1204
- ]);
1205
- log.success(`Detected package manager: ${pm}`);
1206
- if (frameworkResult.supported.length > 0) {
1207
- log.success(`Detected framework: ${frameworkResult.supported.join(", ")}`);
1208
- }
1209
- const isSupported = frameworkResult.supported.length > 0;
1210
- const hasUnsupported = frameworkResult.unsupported.length > 0;
1211
- if (!isSupported) {
1212
- if (hasUnsupported) {
1213
- const names = frameworkResult.unsupported.map((f) => f.name).join(", ");
1214
- log.warn(`Detected ${names} \u2014 not supported in v1 (React / Vue only)`);
1215
- } else {
1216
- log.warn("No frontend framework detected");
1217
- log.hint("Inspecto current version supports React and Vue projects");
1218
- }
1219
- if (!options.force) {
1220
- const shouldContinue = await promptUnsupportedFrameworkContinue();
1221
- if (!shouldContinue) {
1222
- log.warn("Initialization aborted.");
1223
- return;
1224
- }
1225
- } else {
1226
- log.warn("Continuing anyway (--force)");
1227
- }
1228
- } else if (hasUnsupported) {
1229
- const names = frameworkResult.unsupported.map((f) => f.name).join(", ");
1230
- log.hint(
1231
- `Note: Inspecto will be configured for ${frameworkResult.supported.join(", ")}. Other detected frameworks (${names}) will be ignored.`
1232
- );
1233
- }
1234
- let manualConfigRequiredFor = "";
1235
- if (verifiedPackages.length > 0 && buildResult.supported.length === 0) {
1236
- log.warn(
1237
- `No supported build configs detected for: ${verifiedPackages.map((pkg) => pkg ? pkg : ".").join(", ")}`
1238
- );
1239
- log.hint("Double-check the --packages values or run without the flag to scan the repo root");
1240
- }
1241
- if (buildResult.supported.length > 0) {
1242
- buildResult.supported.forEach((bt) => log.success(`Detected: ${bt.label}`));
1243
- }
1244
- if (buildResult.unsupported.length > 0) {
1245
- const names = buildResult.unsupported.join(", ");
1246
- manualConfigRequiredFor = buildResult.unsupported[0] || "";
1247
- log.warn(`Detected ${names} \u2014 automatic plugin injection is not supported in current version`);
1248
- log.hint("You can still manually configure it by modifying your configuration file");
1249
- }
1250
- if (buildResult.supported.length === 0 && buildResult.unsupported.length === 0) {
1251
- log.warn("No recognized build tool detected");
1252
- log.hint("current version supports: Vite, Webpack, Rspack, esbuild, Rollup");
1253
- log.hint("Dependency will be installed but plugin injection will be skipped");
1254
- log.hint(
1255
- "Please refer to the manual setup guide: https://inspecto-dev.github.io/inspecto/guide/manual-installation"
1256
- );
1257
- }
1258
- let selectedIDE = null;
1259
- if (ideProbe.detected.length === 0) {
1260
- log.error("No IDE detected in current project");
1261
- log.hint("Please open this project in a supported IDE (like VS Code)");
1262
- } else if (ideProbe.detected.length === 1) {
1263
- selectedIDE = ideProbe.detected[0];
1264
- } else {
1265
- selectedIDE = await promptIDEChoice(ideProbe.detected);
1266
- }
1267
- if (selectedIDE) {
1268
- if (selectedIDE.supported) {
1269
- log.success(`Selected IDE: ${selectedIDE.ide}`);
1270
- } else {
1271
- log.warn(`Selected IDE: ${selectedIDE.ide}`);
1272
- log.hint(
1273
- `Note: Inspecto currently requires VS Code (or compatible forks) to function properly.`
1274
- );
1275
- log.hint(`Features may be severely limited or unavailable in ${selectedIDE.ide}.`);
1276
- }
1277
- }
1278
- let selectedProvider = null;
1279
- if (!options.provider) {
1280
- if (providerProbe.detected.length === 0) {
1281
- log.warn("No supported AI tools detected");
1282
- log.hint("Inspecto works best with Claude Code, Trae CLI, or GitHub Copilot");
1283
- } else if (providerProbe.detected.length === 1) {
1284
- selectedProvider = providerProbe.detected[0];
1285
- if (selectedProvider.supported) {
1286
- log.success(`Detected AI tool: ${selectedProvider.label}`);
1287
- }
1288
- } else {
1289
- selectedProvider = await promptProviderChoice(providerProbe.detected);
1290
- if (selectedProvider) {
1291
- log.success(`Selected provider: ${selectedProvider.label}`);
1292
- }
1293
- }
1294
- }
1295
- let installFailed = false;
1296
- if (options.skipInstall) {
1297
- log.warn("Skipping dependency installation (--skip-install)");
1298
- } else {
1299
- const installCmd = getInstallCommand(pm, "@inspecto-dev/plugin @inspecto-dev/core");
1300
- if (options.dryRun) {
1301
- log.dryRun(`Would run: ${installCmd}`);
1302
- } else {
1303
- try {
1304
- const result = await shell(installCmd, root);
1305
- if (result.stderr && result.stderr.toLowerCase().includes("error")) {
1306
- throw new Error(result.stderr);
1307
- }
1308
- log.success("Installed @inspecto-dev/plugin and @inspecto-dev/core as devDependencies");
1309
- mutations.push({ type: "dependency_added", name: "@inspecto-dev/plugin", dev: true });
1310
- mutations.push({ type: "dependency_added", name: "@inspecto-dev/core", dev: true });
1311
- } catch (err) {
1312
- installFailed = true;
1313
- log.error(`Failed to install dependency: ${err?.message || "Unknown error"}`);
1314
- log.hint(`Run manually: ${installCmd}`);
1315
- }
1316
- }
1317
- }
1318
- let injectionFailed = false;
1319
- if (buildResult.supported.length > 0) {
1320
- if (verifiedPackages.length > 0) {
1321
- const targets = buildResult.supported.filter(
1322
- (detection) => matchesAnyPackage(detection, verifiedPackages)
1323
- );
1324
- const unmatchedPackages = verifiedPackages.filter(
1325
- (pkg) => !buildResult.supported.some((detection) => matchesPackage(detection, pkg))
1326
- );
1327
- if (unmatchedPackages.length > 0) {
1328
- log.warn(
1329
- `No supported build configs detected for: ${unmatchedPackages.map((pkg) => pkg ? pkg : ".").join(", ")}`
1330
- );
1331
- log.hint("Check the package paths or run without --packages to inspect the repo root");
1332
- }
1333
- if (targets.length === 0) {
1334
- injectionFailed = true;
1335
- }
1336
- for (const target of targets) {
1337
- const result = await injectPlugin(root, target, options.dryRun);
1338
- if (result.success) {
1339
- mutations.push(...result.mutations);
1340
- } else {
1341
- injectionFailed = true;
1342
- }
1343
- }
1344
- } else {
1345
- let target = resolveInjectionTarget(buildResult.supported);
1346
- if (target === "ambiguous") {
1347
- target = await promptConfigChoice(buildResult.supported);
1348
- }
1349
- if (target) {
1350
- const result = await injectPlugin(root, target, options.dryRun);
1351
- if (result.success) {
1352
- mutations.push(...result.mutations);
1353
- } else {
1354
- injectionFailed = true;
1355
- }
1356
- } else {
1357
- injectionFailed = true;
1358
- log.warn("Skipping plugin injection (manual configuration required)");
1359
- }
1360
- }
1361
- }
1362
- const settingsDir = path9.join(root, ".inspecto");
1363
- const settingsFileName = options.shared ? "settings.json" : "settings.local.json";
1364
- const promptsFileName = options.shared ? "prompts.json" : "prompts.local.json";
1365
- const settingsPath = path9.join(settingsDir, settingsFileName);
1366
- const promptsPath = path9.join(settingsDir, promptsFileName);
1367
- if (await exists(settingsPath)) {
1368
- const existingSettings = await readJSON(settingsPath);
1369
- if (existingSettings === null) {
1370
- log.warn(`.inspecto/${settingsFileName} exists but contains invalid JSON`);
1371
- log.hint("Please fix the syntax errors manually, or delete it and re-run init");
1372
- } else {
1373
- log.success(`.inspecto/${settingsFileName} already exists (skipped)`);
1374
- }
1375
- } else {
1376
- const defaultSettings = {};
1377
- if (selectedIDE && selectedIDE.supported) {
1378
- defaultSettings.ide = selectedIDE.ide.toLowerCase() === "vscode" ? "vscode" : selectedIDE.ide.toLowerCase();
1379
- }
1380
- if (options.provider) {
1381
- const tool = options.provider;
1382
- const mode = tool === "coco" ? "cli" : "extension";
1383
- defaultSettings["provider.default"] = `${tool}.${mode}`;
1384
- } else if (selectedProvider) {
1385
- const toolId = selectedProvider.id;
1386
- const mode = selectedProvider.preferredMode === "cli" ? "cli" : "extension";
1387
- defaultSettings["provider.default"] = `${toolId}.${mode}`;
1388
- }
1389
- if (options.dryRun) {
1390
- log.dryRun(`Would create .inspecto/${settingsFileName}`);
1391
- } else {
1392
- await writeJSON(settingsPath, defaultSettings);
1393
- log.success(`Created .inspecto/${settingsFileName}`);
1394
- mutations.push({ type: "file_created", path: `.inspecto/${settingsFileName}` });
1395
- }
1396
- }
1397
- if (await exists(promptsPath)) {
1398
- log.success(`.inspecto/${promptsFileName} already exists (skipped)`);
1399
- } else {
1400
- const defaultPrompts = [];
1401
- if (options.dryRun) {
1402
- log.dryRun(`Would create .inspecto/${promptsFileName}`);
1403
- } else {
1404
- await writeJSON(promptsPath, defaultPrompts);
1405
- log.success(`Created .inspecto/${promptsFileName}`);
1406
- mutations.push({ type: "file_created", path: `.inspecto/${promptsFileName}` });
1407
- }
1408
- }
1409
- if (!options.dryRun) {
1410
- await updateGitignore(root, options.shared, options.dryRun);
1411
- mutations.push({
1412
- type: "file_modified",
1413
- path: ".gitignore",
1414
- description: "Appended .inspecto/ ignore rules"
1415
- });
1416
- } else {
1417
- log.dryRun("Would update .gitignore");
1418
- }
1419
- if (!options.dryRun && mutations.length > 0) {
1420
- const lock = {
1421
- version: "1.0.0",
1422
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
1423
- mutations
1424
- };
1425
- await writeJSON(path9.join(settingsDir, "install.lock"), lock);
1426
- }
1427
- const shouldInstallExt = !options.noExtension && (!selectedIDE || selectedIDE && selectedIDE.supported);
1428
- let manualExtensionInstallNeeded = false;
1429
- if (options.noExtension) {
1430
- log.warn("Skipping IDE extension (--no-extension)");
1431
- } else if (!shouldInstallExt) {
1432
- } else {
1433
- const extMutation = await installExtension(options.dryRun, selectedIDE?.ide);
1434
- if (extMutation && !options.dryRun) {
1435
- mutations.push(extMutation);
1436
- if (extMutation.manual_action_required) {
1437
- manualExtensionInstallNeeded = true;
1438
- }
1439
- const lockPath = path9.join(settingsDir, "install.lock");
1440
- const lock = await readJSON(lockPath);
1441
- if (lock) {
1442
- lock.mutations = mutations;
1443
- await writeJSON(lockPath, lock);
1444
- }
1445
- } else if (extMutation === null && !options.dryRun) {
1446
- manualExtensionInstallNeeded = true;
1447
- }
1448
- }
1449
- if (options.dryRun) {
1450
- log.blank();
1451
- log.warn("Dry run complete. No files were modified.");
1452
- } else if (installFailed || injectionFailed || manualExtensionInstallNeeded || manualConfigRequiredFor) {
1453
- log.blank();
1454
- log.warn("Setup completed with some manual steps required.");
1455
- if (manualConfigRequiredFor === "Nuxt") {
1456
- printNuxtManualInstructions();
1457
- } else if (manualConfigRequiredFor === "Next.js") {
1458
- printNextJsManualInstructions();
1459
- } else {
1460
- log.hint("Please check the logs above and complete the manual steps.");
1461
- }
1462
- log.blank();
1463
- } else {
1464
- log.ready("Ready! Hold Alt + Click any element to inspect.");
1465
- }
1466
- }
1467
- function normalizePackageList(packages) {
1468
- if (!packages || packages.length === 0) return [];
1469
- const normalized = packages.map((pkg) => {
1470
- const trimmed = pkg.trim();
1471
- if (trimmed === "") return null;
1472
- if (trimmed === "." || trimmed === "./") return "";
1473
- return trimmed.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
1474
- }).filter((value) => value !== null);
1475
- return Array.from(new Set(normalized));
1476
- }
1477
- function matchesPackage(detection, pkg) {
1478
- const configPath = detection.configPath.replace(/\\/g, "/");
1479
- if (!pkg) {
1480
- return !configPath.includes("/");
1481
- }
1482
- return configPath === pkg || configPath.startsWith(`${pkg}/`);
1483
- }
1484
- function matchesAnyPackage(detection, packages) {
1485
- if (packages.length === 0) return true;
1486
- return packages.some((pkg) => matchesPackage(detection, pkg));
1487
- }
1488
-
1489
- // src/commands/doctor.ts
1490
- import path10 from "path";
1491
- async function doctor() {
1492
- const root = process.cwd();
1493
- const result = { errors: 0, warnings: 0 };
1494
- log.header("Inspecto Doctor");
1495
- if (!await exists(path10.join(root, "package.json"))) {
1496
- log.error("No package.json found");
1497
- log.hint("Run this command from your project root");
1498
- return;
1499
- }
1500
- const [ideProbe, frameworkResult, providerProbe, pm, buildResult, extInstalled] = await Promise.all([
1501
- detectIDE(root),
1502
- detectFrameworks(root),
1503
- detectProviders(root),
1504
- detectPackageManager(root),
1505
- detectBuildTools(root),
1506
- isExtensionInstalled()
1507
- ]);
1508
- if (ideProbe.detected.length === 0) {
1509
- log.warn("IDE: not detected");
1510
- result.warnings++;
1511
- } else {
1512
- const hasSupported = ideProbe.detected.some((d) => d.supported);
1513
- if (hasSupported) {
1514
- log.success(
1515
- `IDE: ${ideProbe.detected.filter((d) => d.supported).map((d) => d.ide).join(", ")}`
1516
- );
1517
- } else {
1518
- const names = ideProbe.detected.map((d) => d.ide).join(", ");
1519
- log.warn(`IDE: ${names} (not supported in v1, VS Code, Cursor, Trae only)`);
1520
- result.warnings++;
1521
- }
1522
- }
1523
- if (frameworkResult.supported.length > 0) {
1524
- log.success(`Framework: ${frameworkResult.supported.join(", ")}`);
1525
- } else if (frameworkResult.unsupported.length > 0) {
1526
- const names = frameworkResult.unsupported.map((f) => f.name).join(", ");
1527
- log.warn(`Framework: ${names} (not supported in v1, React/Vue only)`);
1528
- result.warnings++;
1529
- } else {
1530
- log.warn("Framework: not detected (React / Vue expected)");
1531
- result.warnings++;
1532
- }
1533
- if (providerProbe.detected.length === 0) {
1534
- log.warn("Provider: none detected");
1535
- log.hint("Inspecto works best with Claude Code, Trae CLI, or GitHub Copilot");
1536
- result.warnings++;
1537
- } else {
1538
- const aiNames = providerProbe.detected.map((d) => {
1539
- const modeLabels = d.providerModes.map(
1540
- (mode) => mode === "extension" ? "VS Code Extension" : "Terminal CLI"
1541
- );
1542
- return `${d.label} (${modeLabels.join(" & ")})`;
1543
- }).join(", ");
1544
- log.success(`Provider: ${aiNames}`);
1545
- }
1546
- const pluginPath = path10.join(root, "node_modules", "@inspecto-dev", "plugin");
1547
- if (await exists(pluginPath)) {
1548
- const pkgJson = await readJSON(path10.join(pluginPath, "package.json"));
1549
- const version = pkgJson?.version ?? "unknown";
1550
- log.success(`@inspecto-dev/plugin@${version} installed`);
1551
- } else {
1552
- log.error("@inspecto-dev/plugin not installed");
1553
- const pm2 = await detectPackageManager(root);
1554
- log.hint(`Fix: ${getInstallCommand(pm2, "@inspecto-dev/plugin")}`);
1555
- result.errors++;
1556
- }
1557
- if (buildResult.supported.length > 0) {
1558
- let injected = false;
1559
- for (const bt of buildResult.supported) {
1560
- const content = await readFile(path10.join(root, bt.configPath));
1561
- if (content && content.includes("@inspecto-dev/plugin")) {
1562
- log.success(`Plugin configured in ${bt.configPath}`);
1563
- injected = true;
1564
- break;
1565
- }
1566
- }
1567
- if (!injected) {
1568
- log.error("Plugin not configured in any build config");
1569
- log.hint("Fix: npx @inspecto-dev/cli init");
1570
- result.errors++;
1571
- }
1572
- } else if (buildResult.unsupported.length > 0) {
1573
- const names = buildResult.unsupported.join(", ");
1574
- log.warn(`Build tool: ${names} (not supported in v1)`);
1575
- log.hint("current version supports: Vite, Webpack, Rspack, esbuild, Rollup");
1576
- result.warnings++;
1577
- } else {
1578
- log.warn("No recognized build config found");
1579
- result.warnings++;
1580
- }
1581
- if (extInstalled) {
1582
- log.success("VS Code extension detected");
1583
- } else {
1584
- const hasSupported = ideProbe.detected.some((d) => d.supported);
1585
- if (ideProbe.detected.length > 0 && !hasSupported) {
1586
- log.warn("VS Code extension not applicable (non-VS Code IDE)");
1587
- } else {
1588
- log.error("VS Code extension not found");
1589
- log.hint("Fix: code --install-extension inspecto.inspecto");
1590
- log.hint("Or: https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto");
1591
- result.errors++;
1592
- }
1593
- }
1594
- const settingsJsonPath = path10.join(root, ".inspecto", "settings.json");
1595
- const settingsLocalPath = path10.join(root, ".inspecto", "settings.local.json");
1596
- const hasSettingsJson = await exists(settingsJsonPath);
1597
- const hasSettingsLocal = await exists(settingsLocalPath);
1598
- if (hasSettingsJson || hasSettingsLocal) {
1599
- const targetPath = hasSettingsLocal ? settingsLocalPath : settingsJsonPath;
1600
- const fileName = hasSettingsLocal ? "settings.local.json" : "settings.json";
1601
- const settings = await readJSON(targetPath);
1602
- if (settings) {
1603
- log.success(`.inspecto/${fileName} valid`);
1604
- } else {
1605
- log.error(`.inspecto/${fileName} has invalid JSON`);
1606
- log.hint(
1607
- "Fix: Manually correct the syntax errors, or delete the file and re-run npx @inspecto-dev/cli init"
1608
- );
1609
- result.errors++;
1610
- }
1611
- } else {
1612
- log.warn("No .inspecto/settings.json or settings.local.json found (using defaults)");
1613
- log.hint("Optional: npx @inspecto-dev/cli init");
1614
- result.warnings++;
1615
- }
1616
- const gitignoreContent = await readFile(path10.join(root, ".gitignore"));
1617
- if (gitignoreContent) {
1618
- const hasLockIgnore = gitignoreContent.includes(".inspecto/install.lock") || gitignoreContent.includes(".inspecto/");
1619
- if (!hasLockIgnore) {
1620
- log.warn(".inspecto/install.lock not in .gitignore");
1621
- log.hint("install.lock contains local machine state and should not be committed");
1622
- result.warnings++;
1623
- }
1624
- }
1625
- log.blank();
1626
- if (result.errors === 0 && result.warnings === 0) {
1627
- log.success("All checks passed. Hold Alt + Click to start!");
1628
- } else {
1629
- const parts = [];
1630
- if (result.errors > 0) parts.push(`${result.errors} error(s)`);
1631
- if (result.warnings > 0) parts.push(`${result.warnings} warning(s)`);
1632
- console.log(
1633
- ` ${parts.join(", ")}. ${result.errors > 0 ? "Fix the errors above to get started." : ""}`
1634
- );
1635
- }
1636
- log.blank();
1637
- }
1638
-
1639
- // src/commands/teardown.ts
1640
- import path11 from "path";
1641
- async function teardown() {
1642
- const root = process.cwd();
1643
- log.header("Inspecto Teardown");
1644
- const lockPath = path11.join(root, ".inspecto", "install.lock");
1645
- const lock = await readJSON(lockPath);
1646
- if (!lock) {
1647
- log.warn("No .inspecto/install.lock found. Running in best-effort mode.");
1648
- log.blank();
1649
- const pm = await detectPackageManager(root);
1650
- try {
1651
- const cmd = getUninstallCommand(pm, "@inspecto-dev/plugin");
1652
- await shell(cmd, root);
1653
- log.success("Removed @inspecto-dev/plugin from devDependencies");
1654
- } catch {
1655
- log.warn("Could not remove @inspecto-dev/plugin (may not be installed)");
1656
- }
1657
- if (await exists(path11.join(root, ".inspecto"))) {
1658
- await removeDir(path11.join(root, ".inspecto"));
1659
- log.success("Deleted .inspecto/ directory");
1660
- }
1661
- await cleanGitignore(root);
1662
- log.success("Cleaned .gitignore entries");
1663
- log.warn("Cannot restore build config auto-magically");
1664
- log.hint("Please manually remove the inspecto() plugin from your build config");
1665
- log.blank();
1666
- return;
1667
- }
1668
- log.success("Reading .inspecto/install.lock...");
1669
- log.blank();
1670
- for (const mutation of lock.mutations) {
1671
- switch (mutation.type) {
1672
- case "file_modified": {
1673
- if (!mutation.path) break;
1674
- if (mutation.path === ".gitignore") {
1675
- log.success("Cleaned .gitignore entries");
1676
- } else if (mutation.path) {
1677
- log.warn(`Cannot auto-restore ${mutation.path}`);
1678
- log.hint(`Please manually remove the inspecto plugin from ${mutation.path}`);
1679
- }
1680
- break;
1681
- }
1682
- case "file_created": {
1683
- break;
1684
- }
1685
- case "dependency_added": {
1686
- if (mutation.name) {
1687
- const pm = await detectPackageManager(root);
1688
- try {
1689
- const cmd = getUninstallCommand(pm, mutation.name);
1690
- await shell(cmd, root);
1691
- log.success(`Removed ${mutation.name} from devDependencies`);
1692
- } catch {
1693
- log.warn(`Could not remove ${mutation.name}`);
1694
- }
1695
- }
1696
- break;
1697
- }
1698
- case "extension_installed": {
1699
- if (mutation.id) {
1700
- log.warn(`VS Code extension not auto-uninstalled`);
1701
- log.hint(`Run: code --uninstall-extension ${mutation.id}`);
1702
- }
1703
- break;
1704
- }
1705
- }
1706
- }
1707
- await removeDir(path11.join(root, ".inspecto"));
1708
- log.success("Deleted .inspecto/ directory");
1709
- await cleanGitignore(root);
1710
- log.blank();
1711
- log.success("Done. All Inspecto traces removed.");
1712
- log.blank();
1713
- }
1714
-
1715
- export {
1716
- log,
1717
- init,
1718
- doctor,
1719
- teardown
1720
- };