@inspecto-dev/cli 0.2.0-alpha.0 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +20 -19
- package/.turbo/turbo-test.log +5 -5
- package/CHANGELOG.md +22 -0
- package/README.md +6 -6
- package/dist/bin.js +50 -68
- package/dist/{chunk-4RR7PTRN.js → chunk-V57BJXGZ.js} +615 -435
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +3 -2
- package/src/bin.ts +84 -70
- package/src/commands/doctor.ts +41 -28
- package/src/commands/init.ts +106 -226
- package/src/commands/teardown.ts +13 -23
- package/src/detect/build-tool.ts +89 -21
- package/src/detect/ide.ts +42 -21
- package/src/detect/package-manager.ts +10 -3
- package/src/detect/provider.ts +151 -0
- package/src/inject/ast-injector.ts +70 -231
- package/src/inject/extension.ts +49 -34
- package/src/inject/gitignore.ts +1 -1
- package/src/inject/strategies/esbuild.ts +35 -0
- package/src/inject/strategies/index.ts +16 -0
- package/src/inject/strategies/rollup.ts +35 -0
- package/src/inject/strategies/rsbuild.ts +29 -0
- package/src/inject/strategies/rspack.ts +34 -0
- package/src/inject/strategies/types.ts +35 -0
- package/src/inject/strategies/vite.ts +30 -0
- package/src/inject/strategies/webpack.ts +36 -0
- package/src/instructions.ts +55 -0
- package/src/prompts.ts +115 -0
- package/src/types.ts +4 -1
- package/tests/ide.test.ts +3 -3
- package/src/detect/ai-tool.ts +0 -127
|
@@ -77,15 +77,6 @@ async function writeFile(filePath, content) {
|
|
|
77
77
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
78
78
|
await fs.writeFile(filePath, content, "utf-8");
|
|
79
79
|
}
|
|
80
|
-
async function copyFile(src, dest) {
|
|
81
|
-
await fs.copyFile(src, dest);
|
|
82
|
-
}
|
|
83
|
-
async function removeFile(filePath) {
|
|
84
|
-
try {
|
|
85
|
-
await fs.unlink(filePath);
|
|
86
|
-
} catch {
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
80
|
async function removeDir(dirPath) {
|
|
90
81
|
try {
|
|
91
82
|
await fs.rm(dirPath, { recursive: true, force: true });
|
|
@@ -146,9 +137,15 @@ async function detectPackageManager(root) {
|
|
|
146
137
|
["yarn.lock", "yarn"],
|
|
147
138
|
["package-lock.json", "npm"]
|
|
148
139
|
];
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
|
152
149
|
}
|
|
153
150
|
}
|
|
154
151
|
return "npm";
|
|
@@ -203,7 +200,7 @@ var SUPPORTED_PATTERNS = [
|
|
|
203
200
|
},
|
|
204
201
|
{
|
|
205
202
|
tool: "esbuild",
|
|
206
|
-
files: ["esbuild.config.js", "esbuild.config.ts", "esbuild.config.mjs"],
|
|
203
|
+
files: ["esbuild.config.js", "esbuild.config.ts", "esbuild.config.mjs", "build.js", "build.ts"],
|
|
207
204
|
label: "esbuild"
|
|
208
205
|
},
|
|
209
206
|
{
|
|
@@ -224,34 +221,79 @@ async function detectBuildTools(root) {
|
|
|
224
221
|
const unsupported = [];
|
|
225
222
|
const pkg = await readJSON(path3.join(root, "package.json"));
|
|
226
223
|
const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
227
|
-
|
|
224
|
+
const supportedChecks = SUPPORTED_PATTERNS.map(async (pattern) => {
|
|
225
|
+
const hasDep = pattern.tool === "rspack" ? !!(allDeps["@rspack/cli"] || allDeps["@rspack/core"]) : pattern.tool === "webpack" ? !!(allDeps["webpack"] || allDeps["webpack-cli"]) : pattern.tool === "rsbuild" ? !!allDeps["@rsbuild/core"] : !!allDeps[pattern.tool];
|
|
226
|
+
let detectedFile = "";
|
|
227
|
+
if (pattern.tool === "esbuild" && !hasDep) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
228
230
|
for (const file of pattern.files) {
|
|
229
231
|
if (await exists(path3.join(root, file))) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
detectedFile = file;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (hasDep && !detectedFile && (pattern.tool === "esbuild" || pattern.tool === "rollup")) {
|
|
237
|
+
const scripts = pkg?.scripts || {};
|
|
238
|
+
for (const [_, cmd] of Object.entries(scripts)) {
|
|
239
|
+
if (cmd.includes("node ")) {
|
|
240
|
+
const match = cmd.match(/node\s+([^\s]+\.(js|mjs|cjs|ts))/);
|
|
241
|
+
if (match && match[1]) {
|
|
242
|
+
if (await exists(path3.join(root, match[1]))) {
|
|
243
|
+
detectedFile = match[1];
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
235
246
|
}
|
|
247
|
+
} else if (cmd.includes(`${pattern.tool} `)) {
|
|
248
|
+
detectedFile = "package.json (scripts)";
|
|
249
|
+
break;
|
|
236
250
|
}
|
|
237
|
-
supported.push({
|
|
238
|
-
tool: pattern.tool,
|
|
239
|
-
configPath: file,
|
|
240
|
-
label: `${pattern.label} (${file})${isLegacyRspack ? " [Legacy]" : ""}`,
|
|
241
|
-
isLegacyRspack
|
|
242
|
-
});
|
|
243
|
-
break;
|
|
244
251
|
}
|
|
245
252
|
}
|
|
253
|
+
if (detectedFile) {
|
|
254
|
+
let isLegacyRspack = false;
|
|
255
|
+
let isLegacyWebpack = false;
|
|
256
|
+
if (pattern.tool === "rspack") {
|
|
257
|
+
const version = allDeps["@rspack/cli"] || allDeps["@rspack/core"];
|
|
258
|
+
if (version && (version.includes("0.3.") || version.includes("0.2.") || version.includes("0.1."))) {
|
|
259
|
+
isLegacyRspack = true;
|
|
260
|
+
}
|
|
261
|
+
} else if (pattern.tool === "webpack") {
|
|
262
|
+
const version = allDeps["webpack"] || allDeps["webpack-cli"];
|
|
263
|
+
if (version && version.includes("^4") || version?.startsWith("4.")) {
|
|
264
|
+
isLegacyWebpack = true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
tool: pattern.tool,
|
|
269
|
+
configPath: detectedFile,
|
|
270
|
+
label: `${pattern.label} (${detectedFile})${isLegacyRspack ? " [Legacy]" : ""}${isLegacyWebpack ? " [Webpack 4]" : ""}`,
|
|
271
|
+
isLegacyRspack,
|
|
272
|
+
isLegacyWebpack
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
});
|
|
277
|
+
const supportedResults = await Promise.all(supportedChecks);
|
|
278
|
+
for (const result of supportedResults) {
|
|
279
|
+
if (result) {
|
|
280
|
+
supported.push(result);
|
|
281
|
+
}
|
|
246
282
|
}
|
|
247
|
-
|
|
248
|
-
if (!(meta.dep in allDeps))
|
|
283
|
+
const unsupportedChecks = UNSUPPORTED_META.map(async (meta) => {
|
|
284
|
+
if (!(meta.dep in allDeps)) return null;
|
|
249
285
|
for (const file of meta.files) {
|
|
250
286
|
if (await exists(path3.join(root, file))) {
|
|
251
|
-
|
|
252
|
-
break;
|
|
287
|
+
return meta.name;
|
|
253
288
|
}
|
|
254
289
|
}
|
|
290
|
+
return null;
|
|
291
|
+
});
|
|
292
|
+
const unsupportedResults = await Promise.all(unsupportedChecks);
|
|
293
|
+
for (const result of unsupportedResults) {
|
|
294
|
+
if (result) {
|
|
295
|
+
unsupported.push(result);
|
|
296
|
+
}
|
|
255
297
|
}
|
|
256
298
|
return { supported, unsupported };
|
|
257
299
|
}
|
|
@@ -302,39 +344,44 @@ var SUPPORTED_IDE = "vscode";
|
|
|
302
344
|
async function detectIDE(root) {
|
|
303
345
|
const detected = /* @__PURE__ */ new Map();
|
|
304
346
|
if (process.env.CURSOR_TRACE_DIR || process.env.CURSOR_CHANNEL) {
|
|
305
|
-
detected.set("Cursor", { ide: "
|
|
347
|
+
detected.set("Cursor", { ide: "cursor", supported: true });
|
|
306
348
|
}
|
|
307
349
|
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")) {
|
|
308
|
-
detected.set("Trae", { ide: "
|
|
350
|
+
detected.set("Trae", { ide: "trae", supported: true });
|
|
309
351
|
}
|
|
310
352
|
if (process.env.ZED_TERM) {
|
|
311
353
|
detected.set("Zed", { ide: "Zed", supported: false });
|
|
312
354
|
}
|
|
313
|
-
if (process.env.
|
|
314
|
-
|
|
315
|
-
detected.set("vscode", { ide: SUPPORTED_IDE, supported: true });
|
|
316
|
-
}
|
|
355
|
+
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")) {
|
|
356
|
+
detected.set("Windsurf", { ide: "Windsurf", supported: false });
|
|
317
357
|
}
|
|
318
|
-
|
|
319
|
-
|
|
358
|
+
const [hasTrae, hasCursor, hasVscode, hasIdea] = await Promise.all([
|
|
359
|
+
exists(path5.join(root, ".trae")),
|
|
360
|
+
exists(path5.join(root, ".cursor")),
|
|
361
|
+
exists(path5.join(root, ".vscode")),
|
|
362
|
+
exists(path5.join(root, ".idea"))
|
|
363
|
+
]);
|
|
364
|
+
if (hasTrae && !detected.has("Trae")) {
|
|
365
|
+
detected.set("Trae", { ide: "trae", supported: true });
|
|
320
366
|
}
|
|
321
|
-
if (
|
|
322
|
-
detected.set("Cursor", { ide: "
|
|
367
|
+
if (hasCursor && !detected.has("Cursor")) {
|
|
368
|
+
detected.set("Cursor", { ide: "cursor", supported: true });
|
|
323
369
|
}
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
detected.set("vscode", { ide: SUPPORTED_IDE, supported: true });
|
|
327
|
-
}
|
|
370
|
+
if (hasVscode && !detected.has("vscode")) {
|
|
371
|
+
detected.set("vscode", { ide: SUPPORTED_IDE, supported: true });
|
|
328
372
|
}
|
|
329
|
-
if (
|
|
373
|
+
if (hasIdea && !detected.has("JetBrains IDE")) {
|
|
330
374
|
detected.set("JetBrains IDE", { ide: "JetBrains IDE", supported: false });
|
|
331
375
|
}
|
|
376
|
+
if (detected.size === 0 && process.env.TERM_PROGRAM === "vscode") {
|
|
377
|
+
detected.set("vscode", { ide: SUPPORTED_IDE, supported: true });
|
|
378
|
+
}
|
|
332
379
|
return {
|
|
333
380
|
detected: Array.from(detected.values())
|
|
334
381
|
};
|
|
335
382
|
}
|
|
336
383
|
|
|
337
|
-
// src/detect/
|
|
384
|
+
// src/detect/provider.ts
|
|
338
385
|
import path6 from "path";
|
|
339
386
|
var KNOWN_CLI_TOOLS = [
|
|
340
387
|
{ id: "claude-code", bin: "claude", label: "Claude Code", supported: true },
|
|
@@ -344,23 +391,24 @@ var KNOWN_CLI_TOOLS = [
|
|
|
344
391
|
];
|
|
345
392
|
var KNOWN_IDE_PLUGINS = [
|
|
346
393
|
{ id: "claude-code", extId: "anthropic.claude-code", label: "Claude Code", supported: true },
|
|
347
|
-
{ id: "
|
|
394
|
+
{ id: "copilot", extId: "github.copilot", label: "GitHub Copilot", supported: true },
|
|
348
395
|
{ id: "codex", extId: "openai.chatgpt", label: "Codex (ChatGPT)", supported: true },
|
|
349
396
|
{ id: "gemini", extId: "google.geminicodeassist", label: "Gemini Code Assist", supported: true }
|
|
350
397
|
];
|
|
351
|
-
async function
|
|
398
|
+
async function detectProviders(root) {
|
|
352
399
|
const detectedMap = /* @__PURE__ */ new Map();
|
|
353
|
-
|
|
400
|
+
const cliChecks = KNOWN_CLI_TOOLS.map(async (tool) => {
|
|
354
401
|
if (await which(tool.bin)) {
|
|
355
402
|
detectedMap.set(tool.id, {
|
|
356
403
|
id: tool.id,
|
|
357
404
|
label: tool.label,
|
|
358
405
|
supported: tool.supported,
|
|
359
|
-
|
|
406
|
+
providerModes: ["cli"],
|
|
360
407
|
preferredMode: "cli"
|
|
361
408
|
});
|
|
362
409
|
}
|
|
363
|
-
}
|
|
410
|
+
});
|
|
411
|
+
await Promise.all(cliChecks);
|
|
364
412
|
const extensionsJsonPath = path6.join(root, ".vscode", "extensions.json");
|
|
365
413
|
let recommendedExts = [];
|
|
366
414
|
if (await exists(extensionsJsonPath)) {
|
|
@@ -375,35 +423,49 @@ async function detectAITools(root) {
|
|
|
375
423
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
376
424
|
const globalExtDir = path6.join(homeDir, ".vscode", "extensions");
|
|
377
425
|
const globalExtExists = await exists(globalExtDir);
|
|
426
|
+
let installedExtensionFolders = [];
|
|
427
|
+
if (globalExtExists) {
|
|
428
|
+
try {
|
|
429
|
+
const { readdir } = await import("fs/promises");
|
|
430
|
+
installedExtensionFolders = await readdir(globalExtDir);
|
|
431
|
+
const obsoletePath = path6.join(globalExtDir, ".obsolete");
|
|
432
|
+
if (await exists(obsoletePath)) {
|
|
433
|
+
try {
|
|
434
|
+
const obsoleteData = await readJSON(obsoletePath);
|
|
435
|
+
if (obsoleteData) {
|
|
436
|
+
const obsoleteKeys = Object.keys(obsoleteData);
|
|
437
|
+
installedExtensionFolders = installedExtensionFolders.filter((folder) => {
|
|
438
|
+
return !obsoleteKeys.includes(folder);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
}
|
|
378
447
|
for (const plugin of KNOWN_IDE_PLUGINS) {
|
|
379
448
|
let isInstalled = false;
|
|
380
449
|
if (recommendedExts.includes(plugin.extId.toLowerCase())) {
|
|
381
450
|
isInstalled = true;
|
|
382
|
-
} else if (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const lower = f.toLowerCase();
|
|
388
|
-
return lower === plugin.extId.toLowerCase() || lower.startsWith(plugin.extId.toLowerCase() + "-");
|
|
389
|
-
})) {
|
|
390
|
-
isInstalled = true;
|
|
391
|
-
}
|
|
392
|
-
} catch {
|
|
393
|
-
}
|
|
451
|
+
} else if (installedExtensionFolders.some((f) => {
|
|
452
|
+
const lower = f.toLowerCase();
|
|
453
|
+
return lower === plugin.extId.toLowerCase() || lower.startsWith(plugin.extId.toLowerCase() + "-");
|
|
454
|
+
})) {
|
|
455
|
+
isInstalled = true;
|
|
394
456
|
}
|
|
395
457
|
if (isInstalled) {
|
|
396
458
|
const existing = detectedMap.get(plugin.id);
|
|
397
459
|
if (existing) {
|
|
398
|
-
existing.
|
|
399
|
-
existing.preferredMode = "
|
|
460
|
+
existing.providerModes.push("extension");
|
|
461
|
+
existing.preferredMode = "extension";
|
|
400
462
|
} else {
|
|
401
463
|
detectedMap.set(plugin.id, {
|
|
402
464
|
id: plugin.id,
|
|
403
465
|
label: plugin.label,
|
|
404
466
|
supported: plugin.supported,
|
|
405
|
-
|
|
406
|
-
preferredMode: "
|
|
467
|
+
providerModes: ["extension"],
|
|
468
|
+
preferredMode: "extension"
|
|
407
469
|
});
|
|
408
470
|
}
|
|
409
471
|
}
|
|
@@ -413,207 +475,259 @@ async function detectAITools(root) {
|
|
|
413
475
|
|
|
414
476
|
// src/inject/ast-injector.ts
|
|
415
477
|
import path7 from "path";
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
478
|
+
import { loadFile, writeFile as writeAstFile } from "magicast";
|
|
479
|
+
|
|
480
|
+
// src/inject/strategies/vite.ts
|
|
481
|
+
import { addVitePlugin } from "magicast/helpers";
|
|
482
|
+
var ViteStrategy = class {
|
|
483
|
+
name = "Vite";
|
|
484
|
+
supports(tool) {
|
|
485
|
+
return tool === "vite";
|
|
486
|
+
}
|
|
487
|
+
inject({ mod, detection }) {
|
|
488
|
+
addVitePlugin(mod, {
|
|
489
|
+
from: "@inspecto-dev/plugin",
|
|
490
|
+
constructor: "vitePlugin"
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
getManualInstructions(detection, reason) {
|
|
494
|
+
return [
|
|
495
|
+
`import { vitePlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
496
|
+
"",
|
|
497
|
+
"// Add to your plugins array:",
|
|
498
|
+
`plugins: [`,
|
|
499
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
500
|
+
` ...otherPlugins`,
|
|
501
|
+
`].filter(Boolean)`
|
|
502
|
+
];
|
|
503
|
+
}
|
|
423
504
|
};
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
505
|
+
|
|
506
|
+
// src/inject/strategies/webpack.ts
|
|
507
|
+
var WebpackStrategy = class {
|
|
508
|
+
name = "Webpack";
|
|
509
|
+
supports(tool) {
|
|
510
|
+
return tool === "webpack";
|
|
511
|
+
}
|
|
512
|
+
inject(options) {
|
|
513
|
+
throw new Error("Webpack requires manual plugin configuration");
|
|
514
|
+
}
|
|
515
|
+
getManualInstructions(detection, reason) {
|
|
516
|
+
const importPkg = detection.isLegacyWebpack ? "@inspecto-dev/plugin/legacy/webpack4" : "@inspecto-dev/plugin";
|
|
517
|
+
const pluginName = detection.isLegacyWebpack ? "webpack4Plugin" : "webpackPlugin";
|
|
518
|
+
const pluginCall = detection.isLegacyWebpack ? `process.env.NODE_ENV !== 'production' && inspecto({
|
|
519
|
+
pathType: 'absolute',
|
|
520
|
+
escapeTags: ['Transition', 'AnimatePresence'],
|
|
521
|
+
})` : `process.env.NODE_ENV !== 'production' && inspecto()`;
|
|
522
|
+
return [
|
|
523
|
+
`import { ${pluginName} as inspecto } from '${importPkg}'`,
|
|
524
|
+
"",
|
|
525
|
+
"// Add to your plugins array:",
|
|
526
|
+
`plugins: [`,
|
|
527
|
+
` ${pluginCall},`,
|
|
528
|
+
` ...otherPlugins`,
|
|
529
|
+
`].filter(Boolean)`
|
|
530
|
+
];
|
|
427
531
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// src/inject/strategies/rspack.ts
|
|
535
|
+
var RspackStrategy = class {
|
|
536
|
+
name = "Rspack";
|
|
537
|
+
supports(tool) {
|
|
538
|
+
return tool === "rspack";
|
|
539
|
+
}
|
|
540
|
+
inject(options) {
|
|
541
|
+
throw new Error("Rspack requires manual plugin configuration");
|
|
542
|
+
}
|
|
543
|
+
getManualInstructions(detection, reason) {
|
|
544
|
+
const importPkg = detection.isLegacyRspack ? "@inspecto-dev/plugin/legacy/rspack" : "@inspecto-dev/plugin";
|
|
545
|
+
const pluginCall = detection.isLegacyRspack ? `process.env.NODE_ENV !== 'production' && inspecto({
|
|
433
546
|
pathType: 'absolute',
|
|
434
547
|
escapeTags: ['Transition', 'AnimatePresence'],
|
|
435
|
-
})
|
|
548
|
+
})` : `process.env.NODE_ENV !== 'production' && inspecto()`;
|
|
549
|
+
return [
|
|
550
|
+
`import { rspackPlugin as inspecto } from '${importPkg}'`,
|
|
551
|
+
"",
|
|
552
|
+
"// Add to your plugins array:",
|
|
553
|
+
`plugins: [`,
|
|
554
|
+
` ${pluginCall},`,
|
|
555
|
+
` ...otherPlugins`,
|
|
556
|
+
`].filter(Boolean)`
|
|
557
|
+
];
|
|
436
558
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
// src/inject/strategies/rsbuild.ts
|
|
562
|
+
var RsbuildStrategy = class {
|
|
563
|
+
name = "Rsbuild";
|
|
564
|
+
supports(tool) {
|
|
565
|
+
return tool === "rsbuild";
|
|
566
|
+
}
|
|
567
|
+
inject(options) {
|
|
568
|
+
throw new Error("Rsbuild requires manual plugin configuration due to nested structure");
|
|
569
|
+
}
|
|
570
|
+
getManualInstructions(detection, reason) {
|
|
571
|
+
return [
|
|
572
|
+
`import { rspackPlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
448
573
|
"",
|
|
449
574
|
"// Add to tools.rspack:",
|
|
450
575
|
`tools: {`,
|
|
451
576
|
` rspack: {`,
|
|
452
577
|
` plugins: [`,
|
|
453
|
-
`
|
|
578
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
454
579
|
` ]`,
|
|
455
580
|
` }`,
|
|
456
581
|
`}`
|
|
457
|
-
]
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
|
|
582
|
+
];
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
// src/inject/strategies/esbuild.ts
|
|
587
|
+
var EsbuildStrategy = class {
|
|
588
|
+
name = "esbuild";
|
|
589
|
+
supports(tool) {
|
|
590
|
+
return tool === "esbuild";
|
|
591
|
+
}
|
|
592
|
+
inject(options) {
|
|
593
|
+
throw new Error("Esbuild requires manual plugin configuration");
|
|
594
|
+
}
|
|
595
|
+
getManualInstructions(detection, reason) {
|
|
596
|
+
return [
|
|
597
|
+
`1. Update your esbuild config (${detection.configPath}):`,
|
|
598
|
+
`import { esbuildPlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
461
599
|
"",
|
|
462
600
|
"// Add to your plugins array:",
|
|
463
601
|
`plugins: [`,
|
|
464
|
-
`
|
|
602
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
465
603
|
` ...otherPlugins`,
|
|
466
|
-
`].filter(Boolean)
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
let match;
|
|
477
|
-
while ((match = importRegex.exec(content)) !== null) {
|
|
478
|
-
const lineEnd = content.indexOf("\n", match.index);
|
|
479
|
-
if (lineEnd > lastImportEnd) {
|
|
480
|
-
lastImportEnd = lineEnd;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (lastImportEnd === 0) {
|
|
484
|
-
const requireRegex = /^(?:const|let|var)\s.+=\s*require\(.+\).*$/gm;
|
|
485
|
-
while ((match = requireRegex.exec(content)) !== null) {
|
|
486
|
-
const lineEnd = content.indexOf("\n", match.index);
|
|
487
|
-
if (lineEnd > lastImportEnd) {
|
|
488
|
-
lastImportEnd = lineEnd;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
604
|
+
`].filter(Boolean)`,
|
|
605
|
+
"",
|
|
606
|
+
"2. Initialize the client in your app entry (e.g., main.js / index.js):",
|
|
607
|
+
`import { mountInspector } from '@inspecto-dev/core'`,
|
|
608
|
+
"",
|
|
609
|
+
"// Call this before your app renders",
|
|
610
|
+
`if (process.env.NODE_ENV !== 'production') {`,
|
|
611
|
+
` mountInspector()`,
|
|
612
|
+
`}`
|
|
613
|
+
];
|
|
491
614
|
}
|
|
492
|
-
|
|
493
|
-
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
// src/inject/strategies/rollup.ts
|
|
618
|
+
var RollupStrategy = class {
|
|
619
|
+
name = "Rollup";
|
|
620
|
+
supports(tool) {
|
|
621
|
+
return tool === "rollup";
|
|
622
|
+
}
|
|
623
|
+
inject(options) {
|
|
624
|
+
throw new Error("Rollup requires manual plugin configuration");
|
|
625
|
+
}
|
|
626
|
+
getManualInstructions(detection, reason) {
|
|
627
|
+
return [
|
|
628
|
+
`1. Update your rollup config (${detection.configPath}):`,
|
|
629
|
+
`import { rollupPlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
630
|
+
"",
|
|
631
|
+
"// Add to your plugins array:",
|
|
632
|
+
`plugins: [`,
|
|
633
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
634
|
+
` ...otherPlugins`,
|
|
635
|
+
`].filter(Boolean)`,
|
|
636
|
+
"",
|
|
637
|
+
"2. Initialize the client in your app entry (e.g., main.js / index.js):",
|
|
638
|
+
`import { mountInspector } from '@inspecto-dev/core'`,
|
|
639
|
+
"",
|
|
640
|
+
"// Call this before your app renders",
|
|
641
|
+
`if (process.env.NODE_ENV !== 'production') {`,
|
|
642
|
+
` mountInspector()`,
|
|
643
|
+
`}`
|
|
644
|
+
];
|
|
494
645
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// src/inject/strategies/index.ts
|
|
649
|
+
var STRATEGIES = [
|
|
650
|
+
new ViteStrategy(),
|
|
651
|
+
new WebpackStrategy(),
|
|
652
|
+
new RspackStrategy(),
|
|
653
|
+
new RsbuildStrategy(),
|
|
654
|
+
new EsbuildStrategy(),
|
|
655
|
+
new RollupStrategy()
|
|
656
|
+
];
|
|
657
|
+
|
|
658
|
+
// src/inject/ast-injector.ts
|
|
659
|
+
function printManualInstructions(strategy, detection, reason) {
|
|
660
|
+
log.warn(`Could not automatically configure ${detection.configPath}`);
|
|
661
|
+
log.hint(`(reason: ${reason})`);
|
|
662
|
+
log.blank();
|
|
663
|
+
log.hint("Please add the following manually:");
|
|
664
|
+
if (strategy) {
|
|
665
|
+
const instructions = strategy.getManualInstructions(detection, reason);
|
|
666
|
+
log.codeBlock(instructions);
|
|
667
|
+
} else {
|
|
668
|
+
log.error(`Unsupported build tool: ${detection.tool}`);
|
|
517
669
|
}
|
|
518
|
-
const pluginsRegex = /(plugins\s*:\s*\[)/;
|
|
519
|
-
const match = pluginsRegex.exec(content);
|
|
520
|
-
if (!match) return null;
|
|
521
|
-
const insertPos = match.index + match[0].length;
|
|
522
|
-
const pluginExpr = `
|
|
523
|
-
${getPluginExpression(detection.isLegacyRspack)},`;
|
|
524
|
-
return content.slice(0, insertPos) + pluginExpr + content.slice(insertPos);
|
|
525
670
|
}
|
|
526
|
-
function
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const openBrackets = (content.match(/\[/g) || []).length;
|
|
530
|
-
const closeBrackets = (content.match(/\]/g) || []).length;
|
|
531
|
-
return Math.abs(openBraces - closeBraces) <= 1 && Math.abs(openBrackets - closeBrackets) <= 1;
|
|
671
|
+
function isAlreadyInjected(content) {
|
|
672
|
+
return /import\s+.*@inspecto-dev\/plugin/.test(content) || /require\(['"]@inspecto-dev\/plugin['"]\)/.test(content) || /import\s+.*ai-dev-inspector/.test(content) || // Legacy support
|
|
673
|
+
/require\(['"]ai-dev-inspector['"]\)/.test(content);
|
|
532
674
|
}
|
|
533
675
|
async function injectPlugin(root, detection, dryRun) {
|
|
534
676
|
const configPath = path7.join(root, detection.configPath);
|
|
535
|
-
const backupPath = configPath + ".bak";
|
|
536
677
|
const mutations = [];
|
|
678
|
+
const strategy = STRATEGIES.find((s) => s.supports(detection.tool));
|
|
537
679
|
const content = await readFile(configPath);
|
|
538
680
|
if (!content) {
|
|
539
|
-
printManualInstructions(
|
|
540
|
-
detection.tool,
|
|
541
|
-
detection.configPath,
|
|
542
|
-
"config file not readable",
|
|
543
|
-
detection.isLegacyRspack
|
|
544
|
-
);
|
|
681
|
+
printManualInstructions(strategy, detection, "config file not readable");
|
|
545
682
|
return { success: false, mutations, failureReason: "config file not readable" };
|
|
546
683
|
}
|
|
547
684
|
if (isAlreadyInjected(content)) {
|
|
548
|
-
log.success(`Plugin already
|
|
549
|
-
if (await exists(backupPath)) {
|
|
550
|
-
mutations.push({
|
|
551
|
-
type: "file_modified",
|
|
552
|
-
path: detection.configPath,
|
|
553
|
-
backup: detection.configPath + ".bak",
|
|
554
|
-
description: "Previously injected inspecto() plugin"
|
|
555
|
-
});
|
|
556
|
-
} else {
|
|
557
|
-
mutations.push({
|
|
558
|
-
type: "file_modified",
|
|
559
|
-
path: detection.configPath,
|
|
560
|
-
description: "Previously injected inspecto() plugin (no backup)"
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
return { success: true, mutations };
|
|
564
|
-
}
|
|
565
|
-
if (!dryRun) {
|
|
566
|
-
await copyFile(configPath, backupPath);
|
|
685
|
+
log.success(`Plugin already configured in ${detection.configPath} (skipped)`);
|
|
567
686
|
mutations.push({
|
|
568
687
|
type: "file_modified",
|
|
569
688
|
path: detection.configPath,
|
|
570
|
-
|
|
571
|
-
description: "Injected inspecto() plugin"
|
|
689
|
+
description: "Previously configured inspecto() plugin"
|
|
572
690
|
});
|
|
691
|
+
return { success: true, mutations };
|
|
573
692
|
}
|
|
574
|
-
|
|
575
|
-
const injected = injectIntoPluginsArray(content, detection);
|
|
576
|
-
if (!injected) {
|
|
693
|
+
if (!strategy) {
|
|
577
694
|
printManualInstructions(
|
|
578
|
-
|
|
579
|
-
detection
|
|
580
|
-
|
|
581
|
-
detection.isLegacyRspack
|
|
695
|
+
strategy,
|
|
696
|
+
detection,
|
|
697
|
+
`No injection strategy found for ${detection.tool}`
|
|
582
698
|
);
|
|
583
|
-
return {
|
|
584
|
-
success: false,
|
|
585
|
-
mutations,
|
|
586
|
-
failureReason: "could not locate plugins array"
|
|
587
|
-
};
|
|
699
|
+
return { success: false, mutations, failureReason: "No strategy found" };
|
|
588
700
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
701
|
+
if (dryRun) {
|
|
702
|
+
log.dryRun(`Would automatically configure plugin in ${detection.configPath}`);
|
|
703
|
+
return { success: true, mutations: [] };
|
|
704
|
+
}
|
|
705
|
+
try {
|
|
706
|
+
const mod = await loadFile(configPath);
|
|
707
|
+
strategy.inject({
|
|
708
|
+
mod,
|
|
709
|
+
detection
|
|
710
|
+
});
|
|
711
|
+
await writeAstFile(mod, configPath);
|
|
712
|
+
mutations.push({
|
|
713
|
+
type: "file_modified",
|
|
714
|
+
path: detection.configPath,
|
|
715
|
+
description: "Automatically configured inspecto() plugin"
|
|
716
|
+
});
|
|
717
|
+
log.success(`Configured plugin in ${detection.configPath}`);
|
|
718
|
+
return { success: true, mutations };
|
|
719
|
+
} catch (err) {
|
|
597
720
|
printManualInstructions(
|
|
598
|
-
|
|
599
|
-
detection
|
|
600
|
-
|
|
601
|
-
detection.isLegacyRspack
|
|
721
|
+
strategy,
|
|
722
|
+
detection,
|
|
723
|
+
`Automatic configuration unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
602
724
|
);
|
|
603
725
|
return {
|
|
604
726
|
success: false,
|
|
605
|
-
mutations
|
|
606
|
-
|
|
607
|
-
failureReason: "injection produced invalid syntax"
|
|
727
|
+
mutations,
|
|
728
|
+
failureReason: "Automatic configuration unavailable"
|
|
608
729
|
};
|
|
609
730
|
}
|
|
610
|
-
if (dryRun) {
|
|
611
|
-
log.dryRun(`Would inject plugin into ${detection.configPath}`);
|
|
612
|
-
} else {
|
|
613
|
-
await writeFile(configPath, modifiedContent);
|
|
614
|
-
log.success(`Injected plugin into ${detection.configPath}`);
|
|
615
|
-
}
|
|
616
|
-
return { success: true, mutations };
|
|
617
731
|
}
|
|
618
732
|
|
|
619
733
|
// src/inject/gitignore.ts
|
|
@@ -630,7 +744,7 @@ async function updateGitignore(root, shared, dryRun) {
|
|
|
630
744
|
if (!dryRun) {
|
|
631
745
|
await writeFile(gitignorePath, content);
|
|
632
746
|
}
|
|
633
|
-
log.success("Updated .gitignore: .inspecto/
|
|
747
|
+
log.success("Updated .gitignore: .inspecto/ is no longer fully ignored");
|
|
634
748
|
return;
|
|
635
749
|
}
|
|
636
750
|
const missingRules = desiredRules.filter((rule) => !content.includes(rule));
|
|
@@ -692,41 +806,54 @@ async function tryOpenURI(uri) {
|
|
|
692
806
|
return false;
|
|
693
807
|
}
|
|
694
808
|
}
|
|
695
|
-
async function installExtension(dryRun) {
|
|
809
|
+
async function installExtension(dryRun, ide) {
|
|
696
810
|
if (dryRun) {
|
|
697
811
|
log.dryRun("Would attempt to install VS Code extension");
|
|
698
812
|
return null;
|
|
699
813
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
814
|
+
const isVSCode = !ide || ide === "vscode";
|
|
815
|
+
if (isVSCode) {
|
|
816
|
+
if (await which("code")) {
|
|
817
|
+
try {
|
|
818
|
+
await run("code", ["--install-extension", EXTENSION_ID]);
|
|
819
|
+
log.success("VS Code extension installed via CLI");
|
|
820
|
+
return { type: "extension_installed", id: EXTENSION_ID };
|
|
821
|
+
} catch {
|
|
822
|
+
}
|
|
706
823
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
824
|
+
const codePath = await findVSCodeBinary();
|
|
825
|
+
if (codePath) {
|
|
826
|
+
try {
|
|
827
|
+
await run(codePath, ["--install-extension", EXTENSION_ID]);
|
|
828
|
+
log.success("VS Code extension installed via binary path");
|
|
829
|
+
log.info(
|
|
830
|
+
'Tip: Add "code" to your PATH to help Inspecto detect other AI tools in the future'
|
|
831
|
+
);
|
|
832
|
+
return { type: "extension_installed", id: EXTENSION_ID };
|
|
833
|
+
} catch {
|
|
834
|
+
}
|
|
716
835
|
}
|
|
836
|
+
const uri = `vscode:extension/${EXTENSION_ID}`;
|
|
837
|
+
if (await tryOpenURI(uri)) {
|
|
838
|
+
log.warn("Opened extension page in VS Code");
|
|
839
|
+
log.hint('Please click "Install" in the opened VS Code window to complete setup.');
|
|
840
|
+
return { type: "extension_installed", id: EXTENSION_ID, manual_action_required: true };
|
|
841
|
+
}
|
|
842
|
+
log.warn("Could not auto-install VS Code extension");
|
|
843
|
+
log.hint("Please install it manually to enable Inspector features:");
|
|
844
|
+
log.hint(" 1. Open VS Code");
|
|
845
|
+
log.hint(" 2. Press Ctrl+Shift+X (or Cmd+Shift+X)");
|
|
846
|
+
log.hint(' 3. Search for "Inspecto"');
|
|
847
|
+
log.hint(` Or visit: https://marketplace.visualstudio.com/items?itemName=${EXTENSION_ID}`);
|
|
848
|
+
return null;
|
|
717
849
|
}
|
|
718
|
-
|
|
719
|
-
if (await tryOpenURI(uri)) {
|
|
720
|
-
log.warn("Opened extension page in VS Code");
|
|
721
|
-
log.hint('Please click "Install" in the opened VS Code window to complete setup.');
|
|
722
|
-
return { type: "extension_installed", id: EXTENSION_ID, manual_action_required: true };
|
|
723
|
-
}
|
|
724
|
-
log.warn("Could not auto-install VS Code extension");
|
|
850
|
+
log.warn(`Could not auto-install extension for ${ide}`);
|
|
725
851
|
log.hint("Please install it manually to enable Inspector features:");
|
|
726
|
-
log.hint(" 1.
|
|
727
|
-
log.hint(
|
|
728
|
-
log.hint(
|
|
729
|
-
log.hint(
|
|
852
|
+
log.hint(" 1. Download the latest .vsix file from Inspecto releases");
|
|
853
|
+
log.hint(` 2. Open ${ide}`);
|
|
854
|
+
log.hint(" 3. Open the Command Palette (Ctrl+Shift+P or Cmd+Shift+P)");
|
|
855
|
+
log.hint(' 4. Type and select "Extensions: Install from VSIX..."');
|
|
856
|
+
log.hint(" 5. Select the downloaded .vsix file");
|
|
730
857
|
return null;
|
|
731
858
|
}
|
|
732
859
|
async function isExtensionInstalled() {
|
|
@@ -746,70 +873,49 @@ async function isExtensionInstalled() {
|
|
|
746
873
|
}
|
|
747
874
|
}
|
|
748
875
|
|
|
749
|
-
// src/
|
|
876
|
+
// src/prompts.ts
|
|
877
|
+
import prompts from "prompts";
|
|
750
878
|
async function promptIDEChoice(detections) {
|
|
751
879
|
if (!process.stdin.isTTY) {
|
|
752
880
|
log.warn("Multiple IDEs detected but stdin is not interactive");
|
|
753
881
|
log.hint(`Using: ${detections[0].ide} (first match)`);
|
|
754
882
|
return detections[0];
|
|
755
883
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
process.stdout.write(" > Your choice: ");
|
|
765
|
-
process.stdin.resume();
|
|
766
|
-
process.stdin.setEncoding("utf-8");
|
|
767
|
-
const onData = (data) => {
|
|
768
|
-
const choice = parseInt(String(data).trim(), 10);
|
|
769
|
-
process.stdin.off("data", onData);
|
|
770
|
-
process.stdin.pause();
|
|
771
|
-
if (choice >= 1 && choice <= detections.length) {
|
|
772
|
-
resolve(detections[choice - 1]);
|
|
773
|
-
} else {
|
|
774
|
-
resolve(null);
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
process.stdin.on("data", onData);
|
|
884
|
+
const { choice } = await prompts({
|
|
885
|
+
type: "select",
|
|
886
|
+
name: "choice",
|
|
887
|
+
message: "Detected multiple IDEs, please choose one:",
|
|
888
|
+
choices: detections.map((d, i) => ({
|
|
889
|
+
title: `${d.ide} ${d.supported ? "(supported)" : "(unsupported/limited)"}`,
|
|
890
|
+
value: i
|
|
891
|
+
}))
|
|
778
892
|
});
|
|
893
|
+
if (choice === void 0) return null;
|
|
894
|
+
return detections[choice];
|
|
779
895
|
}
|
|
780
|
-
async function
|
|
896
|
+
async function promptProviderChoice(detections) {
|
|
781
897
|
if (!process.stdin.isTTY) {
|
|
782
898
|
log.warn("Multiple AI tools detected but stdin is not interactive");
|
|
783
899
|
log.hint(`Using: ${detections[0].label} (first match)`);
|
|
784
900
|
return detections[0];
|
|
785
901
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
process.stdin.setEncoding("utf-8");
|
|
801
|
-
const onData = (data) => {
|
|
802
|
-
const choice = parseInt(String(data).trim(), 10);
|
|
803
|
-
process.stdin.off("data", onData);
|
|
804
|
-
process.stdin.pause();
|
|
805
|
-
if (choice >= 1 && choice <= detections.length) {
|
|
806
|
-
resolve(detections[choice - 1]);
|
|
807
|
-
} else {
|
|
808
|
-
resolve(null);
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
process.stdin.on("data", onData);
|
|
902
|
+
const { choice } = await prompts({
|
|
903
|
+
type: "select",
|
|
904
|
+
name: "choice",
|
|
905
|
+
message: "Detected multiple providers, please choose one:",
|
|
906
|
+
choices: detections.map((d, i) => {
|
|
907
|
+
const modeLabels = d.providerModes.map(
|
|
908
|
+
(mode) => mode === "extension" ? "VS Code Extension" : "Terminal CLI"
|
|
909
|
+
);
|
|
910
|
+
const modeStr = modeLabels.join(" & ");
|
|
911
|
+
return {
|
|
912
|
+
title: `${d.label} ${d.supported ? `(supported ${modeStr})` : "(unsupported/limited)"}`,
|
|
913
|
+
value: i
|
|
914
|
+
};
|
|
915
|
+
})
|
|
812
916
|
});
|
|
917
|
+
if (choice === void 0) return null;
|
|
918
|
+
return detections[choice];
|
|
813
919
|
}
|
|
814
920
|
async function promptConfigChoice(detections) {
|
|
815
921
|
if (!process.stdin.isTTY) {
|
|
@@ -817,30 +923,93 @@ async function promptConfigChoice(detections) {
|
|
|
817
923
|
log.hint(`Using: ${detections[0].label} (first match)`);
|
|
818
924
|
return detections[0];
|
|
819
925
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
926
|
+
const choices = detections.map((d, i) => ({
|
|
927
|
+
title: d.label,
|
|
928
|
+
value: i
|
|
929
|
+
}));
|
|
930
|
+
choices.push({
|
|
931
|
+
title: "Skip (I'll configure manually)",
|
|
932
|
+
value: -1
|
|
824
933
|
});
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
process.stdin.setEncoding("utf-8");
|
|
831
|
-
const onData = (data) => {
|
|
832
|
-
const choice = parseInt(String(data).trim(), 10);
|
|
833
|
-
process.stdin.off("data", onData);
|
|
834
|
-
process.stdin.pause();
|
|
835
|
-
if (choice >= 1 && choice <= detections.length) {
|
|
836
|
-
resolve(detections[choice - 1]);
|
|
837
|
-
} else {
|
|
838
|
-
resolve(null);
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
process.stdin.on("data", onData);
|
|
934
|
+
const { choice } = await prompts({
|
|
935
|
+
type: "select",
|
|
936
|
+
name: "choice",
|
|
937
|
+
message: "Detected multiple build tool configs, please choose one to inject:",
|
|
938
|
+
choices
|
|
842
939
|
});
|
|
940
|
+
if (choice === void 0 || choice === -1) return null;
|
|
941
|
+
return detections[choice];
|
|
942
|
+
}
|
|
943
|
+
async function promptUnsupportedFrameworkContinue() {
|
|
944
|
+
if (!process.stdin.isTTY) {
|
|
945
|
+
log.error("Unsupported framework detected in non-interactive environment.");
|
|
946
|
+
log.hint("Use --force to skip this check and continue anyway.");
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
const { confirm } = await prompts({
|
|
950
|
+
type: "confirm",
|
|
951
|
+
name: "confirm",
|
|
952
|
+
message: "Inspecto may not work properly. Do you want to continue anyway?",
|
|
953
|
+
initial: false
|
|
954
|
+
});
|
|
955
|
+
return !!confirm;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// src/instructions.ts
|
|
959
|
+
function printNuxtManualInstructions() {
|
|
960
|
+
log.blank();
|
|
961
|
+
log.hint("To enable Inspecto in Nuxt, update your nuxt.config.ts:");
|
|
962
|
+
console.log(`\x1B[36m
|
|
963
|
+
import { vitePlugin as inspecto } from '@inspecto-dev/plugin'
|
|
964
|
+
export default defineNuxtConfig({
|
|
965
|
+
vite: {
|
|
966
|
+
plugins: [inspecto()]
|
|
967
|
+
}
|
|
968
|
+
})
|
|
969
|
+
\x1B[0m`);
|
|
970
|
+
log.hint("And create a Nuxt plugin at plugins/inspecto.client.ts:");
|
|
971
|
+
console.log(`\x1B[36m
|
|
972
|
+
export default defineNuxtPlugin(() => {
|
|
973
|
+
if (import.meta.dev) {
|
|
974
|
+
import('@inspecto-dev/core').then(({ mountInspector }) => {
|
|
975
|
+
mountInspector()
|
|
976
|
+
})
|
|
977
|
+
}
|
|
978
|
+
})
|
|
979
|
+
\x1B[0m`);
|
|
843
980
|
}
|
|
981
|
+
function printNextJsManualInstructions() {
|
|
982
|
+
log.blank();
|
|
983
|
+
log.hint("To enable Inspecto in Next.js, update your next.config.mjs:");
|
|
984
|
+
console.log(`\x1B[36m
|
|
985
|
+
import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'
|
|
986
|
+
const nextConfig = {
|
|
987
|
+
webpack: (config, { dev, isServer }) => {
|
|
988
|
+
if (dev && !isServer) config.plugins.push(inspecto())
|
|
989
|
+
return config
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
export default nextConfig
|
|
993
|
+
\x1B[0m`);
|
|
994
|
+
log.hint("And initialize the client dynamically in your app/layout.tsx (or pages/_app.tsx):");
|
|
995
|
+
console.log(`\x1B[36m
|
|
996
|
+
'use client'
|
|
997
|
+
import { useEffect } from 'react'
|
|
998
|
+
|
|
999
|
+
export default function RootLayout({ children }) {
|
|
1000
|
+
useEffect(() => {
|
|
1001
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1002
|
+
import('@inspecto-dev/core').then(({ mountInspector }) => {
|
|
1003
|
+
mountInspector({ serverUrl: 'http://127.0.0.1:5678' })
|
|
1004
|
+
})
|
|
1005
|
+
}
|
|
1006
|
+
}, [])
|
|
1007
|
+
return <html><body>{children}</body></html>
|
|
1008
|
+
}
|
|
1009
|
+
\x1B[0m`);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// src/commands/init.ts
|
|
844
1013
|
async function init(options) {
|
|
845
1014
|
const root = process.cwd();
|
|
846
1015
|
const mutations = [];
|
|
@@ -850,37 +1019,55 @@ async function init(options) {
|
|
|
850
1019
|
log.hint("Run this command from your project root");
|
|
851
1020
|
return;
|
|
852
1021
|
}
|
|
853
|
-
const pm = await
|
|
1022
|
+
const [pm, frameworkResult, buildResult, ideProbe, providerProbe] = await Promise.all([
|
|
1023
|
+
detectPackageManager(root),
|
|
1024
|
+
detectFrameworks(root),
|
|
1025
|
+
detectBuildTools(root),
|
|
1026
|
+
detectIDE(root),
|
|
1027
|
+
detectProviders(root)
|
|
1028
|
+
]);
|
|
854
1029
|
log.success(`Detected package manager: ${pm}`);
|
|
855
|
-
const frameworkResult = await detectFrameworks(root);
|
|
856
1030
|
if (frameworkResult.supported.length > 0) {
|
|
857
1031
|
log.success(`Detected framework: ${frameworkResult.supported.join(", ")}`);
|
|
858
1032
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1033
|
+
const hasUnsupportedFramework = frameworkResult.unsupported.length > 0;
|
|
1034
|
+
const hasNoFramework = frameworkResult.supported.length === 0 && frameworkResult.unsupported.length === 0;
|
|
1035
|
+
if (hasUnsupportedFramework || hasNoFramework) {
|
|
1036
|
+
if (hasUnsupportedFramework) {
|
|
1037
|
+
const names = frameworkResult.unsupported.map((f) => f.name).join(", ");
|
|
1038
|
+
log.warn(`Detected ${names} \u2014 not supported in v1 (React / Vue only)`);
|
|
1039
|
+
} else {
|
|
1040
|
+
log.warn("No frontend framework detected");
|
|
1041
|
+
log.hint("Inspecto current version supports React and Vue projects");
|
|
1042
|
+
}
|
|
1043
|
+
if (!options.force) {
|
|
1044
|
+
const shouldContinue = await promptUnsupportedFrameworkContinue();
|
|
1045
|
+
if (!shouldContinue) {
|
|
1046
|
+
log.warn("Initialization aborted.");
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
} else {
|
|
1050
|
+
log.warn("Continuing anyway (--force)");
|
|
1051
|
+
}
|
|
867
1052
|
}
|
|
868
|
-
|
|
1053
|
+
let manualConfigRequiredFor = "";
|
|
869
1054
|
if (buildResult.supported.length > 0) {
|
|
870
1055
|
buildResult.supported.forEach((bt) => log.success(`Detected: ${bt.label}`));
|
|
871
1056
|
}
|
|
872
1057
|
if (buildResult.unsupported.length > 0) {
|
|
873
1058
|
const names = buildResult.unsupported.join(", ");
|
|
874
|
-
|
|
875
|
-
log.
|
|
876
|
-
log.hint("
|
|
1059
|
+
manualConfigRequiredFor = buildResult.unsupported[0] || "";
|
|
1060
|
+
log.warn(`Detected ${names} \u2014 automatic plugin injection is not supported in current version`);
|
|
1061
|
+
log.hint("You can still manually configure it by modifying your configuration file");
|
|
877
1062
|
}
|
|
878
1063
|
if (buildResult.supported.length === 0 && buildResult.unsupported.length === 0) {
|
|
879
1064
|
log.warn("No recognized build tool detected");
|
|
880
|
-
log.hint("
|
|
1065
|
+
log.hint("current version supports: Vite, Webpack, Rspack, esbuild, Rollup");
|
|
881
1066
|
log.hint("Dependency will be installed but plugin injection will be skipped");
|
|
1067
|
+
log.hint(
|
|
1068
|
+
"Please refer to the manual setup guide: https://inspecto.dev/docs/getting-started/manual-setup"
|
|
1069
|
+
);
|
|
882
1070
|
}
|
|
883
|
-
const ideProbe = await detectIDE(root);
|
|
884
1071
|
let selectedIDE = null;
|
|
885
1072
|
if (ideProbe.detected.length === 0) {
|
|
886
1073
|
log.error("No IDE detected in current project");
|
|
@@ -901,21 +1088,20 @@ async function init(options) {
|
|
|
901
1088
|
log.hint(`Features may be severely limited or unavailable in ${selectedIDE.ide}.`);
|
|
902
1089
|
}
|
|
903
1090
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
if (aiToolProbe.detected.length === 0) {
|
|
1091
|
+
let selectedProvider = null;
|
|
1092
|
+
if (!options.provider) {
|
|
1093
|
+
if (providerProbe.detected.length === 0) {
|
|
908
1094
|
log.warn("No supported AI tools detected");
|
|
909
1095
|
log.hint("Inspecto works best with Claude Code, Trae CLI, or GitHub Copilot");
|
|
910
|
-
} else if (
|
|
911
|
-
|
|
912
|
-
if (
|
|
913
|
-
log.success(`Detected AI tool: ${
|
|
1096
|
+
} else if (providerProbe.detected.length === 1) {
|
|
1097
|
+
selectedProvider = providerProbe.detected[0];
|
|
1098
|
+
if (selectedProvider.supported) {
|
|
1099
|
+
log.success(`Detected AI tool: ${selectedProvider.label}`);
|
|
914
1100
|
}
|
|
915
1101
|
} else {
|
|
916
|
-
|
|
917
|
-
if (
|
|
918
|
-
log.success(`Selected
|
|
1102
|
+
selectedProvider = await promptProviderChoice(providerProbe.detected);
|
|
1103
|
+
if (selectedProvider) {
|
|
1104
|
+
log.success(`Selected provider: ${selectedProvider.label}`);
|
|
919
1105
|
}
|
|
920
1106
|
}
|
|
921
1107
|
}
|
|
@@ -923,7 +1109,7 @@ async function init(options) {
|
|
|
923
1109
|
if (options.skipInstall) {
|
|
924
1110
|
log.warn("Skipping dependency installation (--skip-install)");
|
|
925
1111
|
} else {
|
|
926
|
-
const installCmd = getInstallCommand(pm, "@inspecto-dev/plugin");
|
|
1112
|
+
const installCmd = getInstallCommand(pm, "@inspecto-dev/plugin @inspecto-dev/core");
|
|
927
1113
|
if (options.dryRun) {
|
|
928
1114
|
log.dryRun(`Would run: ${installCmd}`);
|
|
929
1115
|
} else {
|
|
@@ -932,12 +1118,9 @@ async function init(options) {
|
|
|
932
1118
|
if (result.stderr && result.stderr.toLowerCase().includes("error")) {
|
|
933
1119
|
throw new Error(result.stderr);
|
|
934
1120
|
}
|
|
935
|
-
log.success("Installed @inspecto-dev/plugin as
|
|
936
|
-
mutations.push({
|
|
937
|
-
|
|
938
|
-
name: "@inspecto-dev/plugin",
|
|
939
|
-
dev: true
|
|
940
|
-
});
|
|
1121
|
+
log.success("Installed @inspecto-dev/plugin and @inspecto-dev/core as devDependencies");
|
|
1122
|
+
mutations.push({ type: "dependency_added", name: "@inspecto-dev/plugin", dev: true });
|
|
1123
|
+
mutations.push({ type: "dependency_added", name: "@inspecto-dev/core", dev: true });
|
|
941
1124
|
} catch (err) {
|
|
942
1125
|
installFailed = true;
|
|
943
1126
|
log.error(`Failed to install dependency: ${err?.message || "Unknown error"}`);
|
|
@@ -964,55 +1147,50 @@ async function init(options) {
|
|
|
964
1147
|
}
|
|
965
1148
|
}
|
|
966
1149
|
const settingsDir = path9.join(root, ".inspecto");
|
|
967
|
-
const
|
|
968
|
-
const
|
|
1150
|
+
const settingsFileName = options.shared ? "settings.json" : "settings.local.json";
|
|
1151
|
+
const promptsFileName = options.shared ? "prompts.json" : "prompts.local.json";
|
|
1152
|
+
const settingsPath = path9.join(settingsDir, settingsFileName);
|
|
1153
|
+
const promptsPath = path9.join(settingsDir, promptsFileName);
|
|
969
1154
|
if (await exists(settingsPath)) {
|
|
970
1155
|
const existingSettings = await readJSON(settingsPath);
|
|
971
1156
|
if (existingSettings === null) {
|
|
972
|
-
log.warn(
|
|
1157
|
+
log.warn(`.inspecto/${settingsFileName} exists but contains invalid JSON`);
|
|
973
1158
|
log.hint("Please fix the syntax errors manually, or delete it and re-run init");
|
|
974
1159
|
} else {
|
|
975
|
-
log.success(
|
|
1160
|
+
log.success(`.inspecto/${settingsFileName} already exists (skipped)`);
|
|
976
1161
|
}
|
|
977
1162
|
} else {
|
|
978
1163
|
const defaultSettings = {};
|
|
979
1164
|
if (selectedIDE && selectedIDE.supported) {
|
|
980
|
-
defaultSettings.ide = selectedIDE.ide === "vscode" ? "vscode" : selectedIDE.ide;
|
|
1165
|
+
defaultSettings.ide = selectedIDE.ide.toLowerCase() === "vscode" ? "vscode" : selectedIDE.ide.toLowerCase();
|
|
981
1166
|
}
|
|
982
|
-
if (options.
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
defaultSettings.
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
}
|
|
1167
|
+
if (options.provider) {
|
|
1168
|
+
const tool = options.provider;
|
|
1169
|
+
const mode = tool === "coco" ? "cli" : "extension";
|
|
1170
|
+
defaultSettings["provider.default"] = `${tool}.${mode}`;
|
|
1171
|
+
} else if (selectedProvider) {
|
|
1172
|
+
const toolId = selectedProvider.id;
|
|
1173
|
+
const mode = selectedProvider.preferredMode === "cli" ? "cli" : "extension";
|
|
1174
|
+
defaultSettings["provider.default"] = `${toolId}.${mode}`;
|
|
993
1175
|
}
|
|
994
1176
|
if (options.dryRun) {
|
|
995
|
-
log.dryRun(
|
|
1177
|
+
log.dryRun(`Would create .inspecto/${settingsFileName}`);
|
|
996
1178
|
} else {
|
|
997
1179
|
await writeJSON(settingsPath, defaultSettings);
|
|
998
|
-
log.success(
|
|
999
|
-
mutations.push({ type: "file_created", path:
|
|
1180
|
+
log.success(`Created .inspecto/${settingsFileName}`);
|
|
1181
|
+
mutations.push({ type: "file_created", path: `.inspecto/${settingsFileName}` });
|
|
1000
1182
|
}
|
|
1001
1183
|
}
|
|
1002
1184
|
if (await exists(promptsPath)) {
|
|
1003
|
-
log.success(
|
|
1185
|
+
log.success(`.inspecto/${promptsFileName} already exists (skipped)`);
|
|
1004
1186
|
} else {
|
|
1005
|
-
const defaultPrompts = [
|
|
1006
|
-
{ id: "code-review", enabled: false },
|
|
1007
|
-
{ id: "generate-test", enabled: false },
|
|
1008
|
-
{ id: "performance", enabled: false }
|
|
1009
|
-
];
|
|
1187
|
+
const defaultPrompts = [];
|
|
1010
1188
|
if (options.dryRun) {
|
|
1011
|
-
log.dryRun(
|
|
1189
|
+
log.dryRun(`Would create .inspecto/${promptsFileName}`);
|
|
1012
1190
|
} else {
|
|
1013
1191
|
await writeJSON(promptsPath, defaultPrompts);
|
|
1014
|
-
log.success(
|
|
1015
|
-
mutations.push({ type: "file_created", path:
|
|
1192
|
+
log.success(`Created .inspecto/${promptsFileName}`);
|
|
1193
|
+
mutations.push({ type: "file_created", path: `.inspecto/${promptsFileName}` });
|
|
1016
1194
|
}
|
|
1017
1195
|
}
|
|
1018
1196
|
if (!options.dryRun) {
|
|
@@ -1036,10 +1214,10 @@ async function init(options) {
|
|
|
1036
1214
|
const shouldInstallExt = !options.noExtension && (!selectedIDE || selectedIDE && selectedIDE.supported);
|
|
1037
1215
|
let manualExtensionInstallNeeded = false;
|
|
1038
1216
|
if (options.noExtension) {
|
|
1039
|
-
log.warn("Skipping
|
|
1217
|
+
log.warn("Skipping IDE extension (--no-extension)");
|
|
1040
1218
|
} else if (!shouldInstallExt) {
|
|
1041
1219
|
} else {
|
|
1042
|
-
const extMutation = await installExtension(options.dryRun);
|
|
1220
|
+
const extMutation = await installExtension(options.dryRun, selectedIDE?.ide);
|
|
1043
1221
|
if (extMutation && !options.dryRun) {
|
|
1044
1222
|
mutations.push(extMutation);
|
|
1045
1223
|
if (extMutation.manual_action_required) {
|
|
@@ -1058,10 +1236,16 @@ async function init(options) {
|
|
|
1058
1236
|
if (options.dryRun) {
|
|
1059
1237
|
log.blank();
|
|
1060
1238
|
log.warn("Dry run complete. No files were modified.");
|
|
1061
|
-
} else if (installFailed || injectionFailed || manualExtensionInstallNeeded) {
|
|
1239
|
+
} else if (installFailed || injectionFailed || manualExtensionInstallNeeded || manualConfigRequiredFor) {
|
|
1062
1240
|
log.blank();
|
|
1063
1241
|
log.warn("Setup completed with some manual steps required.");
|
|
1064
|
-
|
|
1242
|
+
if (manualConfigRequiredFor === "Nuxt") {
|
|
1243
|
+
printNuxtManualInstructions();
|
|
1244
|
+
} else if (manualConfigRequiredFor === "Next.js") {
|
|
1245
|
+
printNextJsManualInstructions();
|
|
1246
|
+
} else {
|
|
1247
|
+
log.hint("Please check the logs above and complete the manual steps.");
|
|
1248
|
+
}
|
|
1065
1249
|
log.blank();
|
|
1066
1250
|
} else {
|
|
1067
1251
|
log.ready("Ready! Hold Alt + Click any element to inspect.");
|
|
@@ -1079,7 +1263,14 @@ async function doctor() {
|
|
|
1079
1263
|
log.hint("Run this command from your project root");
|
|
1080
1264
|
return;
|
|
1081
1265
|
}
|
|
1082
|
-
const ideProbe = await
|
|
1266
|
+
const [ideProbe, frameworkResult, providerProbe, pm, buildResult, extInstalled] = await Promise.all([
|
|
1267
|
+
detectIDE(root),
|
|
1268
|
+
detectFrameworks(root),
|
|
1269
|
+
detectProviders(root),
|
|
1270
|
+
detectPackageManager(root),
|
|
1271
|
+
detectBuildTools(root),
|
|
1272
|
+
isExtensionInstalled()
|
|
1273
|
+
]);
|
|
1083
1274
|
if (ideProbe.detected.length === 0) {
|
|
1084
1275
|
log.warn("IDE: not detected");
|
|
1085
1276
|
result.warnings++;
|
|
@@ -1091,11 +1282,10 @@ async function doctor() {
|
|
|
1091
1282
|
);
|
|
1092
1283
|
} else {
|
|
1093
1284
|
const names = ideProbe.detected.map((d) => d.ide).join(", ");
|
|
1094
|
-
log.warn(`IDE: ${names} (not supported in v1, VS Code only)`);
|
|
1285
|
+
log.warn(`IDE: ${names} (not supported in v1, VS Code, Cursor, Trae only)`);
|
|
1095
1286
|
result.warnings++;
|
|
1096
1287
|
}
|
|
1097
1288
|
}
|
|
1098
|
-
const frameworkResult = await detectFrameworks(root);
|
|
1099
1289
|
if (frameworkResult.supported.length > 0) {
|
|
1100
1290
|
log.success(`Framework: ${frameworkResult.supported.join(", ")}`);
|
|
1101
1291
|
} else if (frameworkResult.unsupported.length > 0) {
|
|
@@ -1106,57 +1296,54 @@ async function doctor() {
|
|
|
1106
1296
|
log.warn("Framework: not detected (React / Vue expected)");
|
|
1107
1297
|
result.warnings++;
|
|
1108
1298
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
log.warn("AI Tool: none detected");
|
|
1299
|
+
if (providerProbe.detected.length === 0) {
|
|
1300
|
+
log.warn("Provider: none detected");
|
|
1112
1301
|
log.hint("Inspecto works best with Claude Code, Trae CLI, or GitHub Copilot");
|
|
1113
1302
|
result.warnings++;
|
|
1114
1303
|
} else {
|
|
1115
|
-
const aiNames =
|
|
1116
|
-
const modeLabels = d.
|
|
1117
|
-
(mode) => mode === "
|
|
1304
|
+
const aiNames = providerProbe.detected.map((d) => {
|
|
1305
|
+
const modeLabels = d.providerModes.map(
|
|
1306
|
+
(mode) => mode === "extension" ? "VS Code Extension" : "Terminal CLI"
|
|
1118
1307
|
);
|
|
1119
1308
|
return `${d.label} (${modeLabels.join(" & ")})`;
|
|
1120
1309
|
}).join(", ");
|
|
1121
|
-
log.success(`
|
|
1310
|
+
log.success(`Provider: ${aiNames}`);
|
|
1122
1311
|
}
|
|
1123
|
-
const pluginPath = path10.join(root, "node_modules", "@inspecto", "plugin");
|
|
1312
|
+
const pluginPath = path10.join(root, "node_modules", "@inspecto-dev", "plugin");
|
|
1124
1313
|
if (await exists(pluginPath)) {
|
|
1125
1314
|
const pkgJson = await readJSON(path10.join(pluginPath, "package.json"));
|
|
1126
1315
|
const version = pkgJson?.version ?? "unknown";
|
|
1127
1316
|
log.success(`@inspecto-dev/plugin@${version} installed`);
|
|
1128
1317
|
} else {
|
|
1129
1318
|
log.error("@inspecto-dev/plugin not installed");
|
|
1130
|
-
const
|
|
1131
|
-
log.hint(`Fix: ${getInstallCommand(
|
|
1319
|
+
const pm2 = await detectPackageManager(root);
|
|
1320
|
+
log.hint(`Fix: ${getInstallCommand(pm2, "@inspecto-dev/plugin")}`);
|
|
1132
1321
|
result.errors++;
|
|
1133
1322
|
}
|
|
1134
|
-
const buildResult = await detectBuildTools(root);
|
|
1135
1323
|
if (buildResult.supported.length > 0) {
|
|
1136
1324
|
let injected = false;
|
|
1137
1325
|
for (const bt of buildResult.supported) {
|
|
1138
1326
|
const content = await readFile(path10.join(root, bt.configPath));
|
|
1139
1327
|
if (content && content.includes("@inspecto-dev/plugin")) {
|
|
1140
|
-
log.success(`Plugin
|
|
1328
|
+
log.success(`Plugin configured in ${bt.configPath}`);
|
|
1141
1329
|
injected = true;
|
|
1142
1330
|
break;
|
|
1143
1331
|
}
|
|
1144
1332
|
}
|
|
1145
1333
|
if (!injected) {
|
|
1146
|
-
log.error("Plugin not
|
|
1147
|
-
log.hint("Fix: npx inspecto init");
|
|
1334
|
+
log.error("Plugin not configured in any build config");
|
|
1335
|
+
log.hint("Fix: npx @inspecto-dev/cli init");
|
|
1148
1336
|
result.errors++;
|
|
1149
1337
|
}
|
|
1150
1338
|
} else if (buildResult.unsupported.length > 0) {
|
|
1151
1339
|
const names = buildResult.unsupported.join(", ");
|
|
1152
1340
|
log.warn(`Build tool: ${names} (not supported in v1)`);
|
|
1153
|
-
log.hint("
|
|
1341
|
+
log.hint("current version supports: Vite, Webpack, Rspack, esbuild, Rollup");
|
|
1154
1342
|
result.warnings++;
|
|
1155
1343
|
} else {
|
|
1156
1344
|
log.warn("No recognized build config found");
|
|
1157
1345
|
result.warnings++;
|
|
1158
1346
|
}
|
|
1159
|
-
const extInstalled = await isExtensionInstalled();
|
|
1160
1347
|
if (extInstalled) {
|
|
1161
1348
|
log.success("VS Code extension detected");
|
|
1162
1349
|
} else {
|
|
@@ -1170,21 +1357,26 @@ async function doctor() {
|
|
|
1170
1357
|
result.errors++;
|
|
1171
1358
|
}
|
|
1172
1359
|
}
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1360
|
+
const settingsJsonPath = path10.join(root, ".inspecto", "settings.json");
|
|
1361
|
+
const settingsLocalPath = path10.join(root, ".inspecto", "settings.local.json");
|
|
1362
|
+
const hasSettingsJson = await exists(settingsJsonPath);
|
|
1363
|
+
const hasSettingsLocal = await exists(settingsLocalPath);
|
|
1364
|
+
if (hasSettingsJson || hasSettingsLocal) {
|
|
1365
|
+
const targetPath = hasSettingsLocal ? settingsLocalPath : settingsJsonPath;
|
|
1366
|
+
const fileName = hasSettingsLocal ? "settings.local.json" : "settings.json";
|
|
1367
|
+
const settings = await readJSON(targetPath);
|
|
1176
1368
|
if (settings) {
|
|
1177
|
-
log.success(
|
|
1369
|
+
log.success(`.inspecto/${fileName} valid`);
|
|
1178
1370
|
} else {
|
|
1179
|
-
log.error(
|
|
1371
|
+
log.error(`.inspecto/${fileName} has invalid JSON`);
|
|
1180
1372
|
log.hint(
|
|
1181
|
-
"Fix: Manually correct the syntax errors, or delete the file and re-run npx inspecto init"
|
|
1373
|
+
"Fix: Manually correct the syntax errors, or delete the file and re-run npx @inspecto-dev/cli init"
|
|
1182
1374
|
);
|
|
1183
1375
|
result.errors++;
|
|
1184
1376
|
}
|
|
1185
1377
|
} else {
|
|
1186
|
-
log.warn(".inspecto/settings.json
|
|
1187
|
-
log.hint("Optional: npx inspecto init");
|
|
1378
|
+
log.warn("No .inspecto/settings.json or settings.local.json found (using defaults)");
|
|
1379
|
+
log.hint("Optional: npx @inspecto-dev/cli init");
|
|
1188
1380
|
result.warnings++;
|
|
1189
1381
|
}
|
|
1190
1382
|
const gitignoreContent = await readFile(path10.join(root, ".gitignore"));
|
|
@@ -1234,7 +1426,7 @@ async function teardown() {
|
|
|
1234
1426
|
}
|
|
1235
1427
|
await cleanGitignore(root);
|
|
1236
1428
|
log.success("Cleaned .gitignore entries");
|
|
1237
|
-
log.warn("Cannot restore build config
|
|
1429
|
+
log.warn("Cannot restore build config auto-magically");
|
|
1238
1430
|
log.hint("Please manually remove the inspecto() plugin from your build config");
|
|
1239
1431
|
log.blank();
|
|
1240
1432
|
return;
|
|
@@ -1244,24 +1436,12 @@ async function teardown() {
|
|
|
1244
1436
|
for (const mutation of lock.mutations) {
|
|
1245
1437
|
switch (mutation.type) {
|
|
1246
1438
|
case "file_modified": {
|
|
1247
|
-
if (mutation.
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
await removeFile(backupPath);
|
|
1254
|
-
} else if (await exists(backupPath)) {
|
|
1255
|
-
await copyFile(backupPath, targetPath);
|
|
1256
|
-
await removeFile(backupPath);
|
|
1257
|
-
log.success(`Restored ${mutation.path} from backup`);
|
|
1258
|
-
} else {
|
|
1259
|
-
log.warn(`Backup not found: ${mutation.backup}`);
|
|
1260
|
-
log.hint(`Please manually remove inspecto from ${mutation.path}`);
|
|
1261
|
-
}
|
|
1262
|
-
} else if (mutation.path && mutation.path !== ".gitignore") {
|
|
1263
|
-
log.warn(`Cannot auto-restore ${mutation.path} (no backup recorded)`);
|
|
1264
|
-
log.hint(`Please manually remove the inspecto() plugin from ${mutation.path}`);
|
|
1439
|
+
if (!mutation.path) break;
|
|
1440
|
+
if (mutation.path === ".gitignore") {
|
|
1441
|
+
log.success("Cleaned .gitignore entries");
|
|
1442
|
+
} else if (mutation.path) {
|
|
1443
|
+
log.warn(`Cannot auto-restore ${mutation.path}`);
|
|
1444
|
+
log.hint(`Please manually remove the inspecto plugin from ${mutation.path}`);
|
|
1265
1445
|
}
|
|
1266
1446
|
break;
|
|
1267
1447
|
}
|