@inspecto-dev/cli 0.3.4 → 0.3.5
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 +7 -7
- package/.turbo/turbo-test.log +10036 -5601
- package/CHANGELOG.md +8 -0
- package/dist/bin.js +32 -1
- package/dist/{chunk-2MOEVONN.js → chunk-7ABJRH3F.js} +1232 -145
- package/dist/index.d.ts +62 -4
- package/dist/index.js +7 -1
- package/package.json +2 -2
- package/src/bin.ts +45 -0
- package/src/commands/dev-config.ts +109 -0
- package/src/commands/doctor.ts +162 -9
- package/src/commands/init.ts +10 -3
- package/src/commands/integration-automation.ts +2 -0
- package/src/commands/integration-host-ide.ts +18 -15
- package/src/commands/integration-install.ts +1 -1
- package/src/commands/onboard.ts +72 -21
- package/src/detect/build-tool.ts +14 -5
- package/src/detect/framework.ts +3 -0
- package/src/detect/package-manager.ts +1 -1
- package/src/index.ts +1 -0
- package/src/inject/gitignore.ts +13 -2
- package/src/instructions.ts +33 -7
- package/src/onboarding/apply.ts +137 -3
- package/src/onboarding/nextjs-guidance.ts +257 -0
- package/src/onboarding/nuxt-guidance.ts +129 -0
- package/src/onboarding/planner.ts +257 -6
- package/src/onboarding/session.ts +117 -27
- package/src/onboarding/target-resolution.ts +3 -3
- package/src/onboarding/umi-guidance.ts +139 -0
- package/src/types.ts +51 -3
- package/tests/apply.test.ts +319 -0
- package/tests/dev-config.test.ts +73 -0
- package/tests/doctor.test.ts +89 -0
- package/tests/init.test.ts +17 -0
- package/tests/instructions.test.ts +10 -6
- package/tests/integration-host-ide.test.ts +20 -0
- package/tests/integration-install.test.ts +65 -0
- package/tests/nextjs-guidance.test.ts +128 -0
- package/tests/nuxt-guidance.test.ts +67 -0
- package/tests/onboard.test.ts +416 -0
- package/tests/plan.test.ts +181 -21
- package/tests/runner-script.test.ts +120 -1
- package/tests/session-resolve.test.ts +116 -0
- package/tests/session.test.ts +120 -0
|
@@ -118,6 +118,12 @@ async function writeFile(filePath, content) {
|
|
|
118
118
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
119
119
|
await fs.writeFile(filePath, content, "utf-8");
|
|
120
120
|
}
|
|
121
|
+
async function removeFile(filePath) {
|
|
122
|
+
try {
|
|
123
|
+
await fs.unlink(filePath);
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
121
127
|
async function removeDir(dirPath) {
|
|
122
128
|
try {
|
|
123
129
|
await fs.rm(dirPath, { recursive: true, force: true });
|
|
@@ -144,8 +150,8 @@ async function detectPackageManager(root) {
|
|
|
144
150
|
["bun.lockb", "bun"],
|
|
145
151
|
["bun.lock", "bun"],
|
|
146
152
|
["pnpm-lock.yaml", "pnpm"],
|
|
147
|
-
["
|
|
148
|
-
["
|
|
153
|
+
["package-lock.json", "npm"],
|
|
154
|
+
["yarn.lock", "yarn"]
|
|
149
155
|
];
|
|
150
156
|
const results = await Promise.all(
|
|
151
157
|
checks.map(async ([file, pm]) => {
|
|
@@ -826,8 +832,18 @@ async function isExtensionInstalled() {
|
|
|
826
832
|
|
|
827
833
|
// src/inject/gitignore.ts
|
|
828
834
|
import path5 from "path";
|
|
829
|
-
var DEFAULT_RULES = [
|
|
830
|
-
|
|
835
|
+
var DEFAULT_RULES = [
|
|
836
|
+
".inspecto/install.lock",
|
|
837
|
+
".inspecto/cache.json",
|
|
838
|
+
".inspecto/*.local.json",
|
|
839
|
+
".inspecto/dev.json"
|
|
840
|
+
];
|
|
841
|
+
var SHARED_RULES = [
|
|
842
|
+
".inspecto/install.lock",
|
|
843
|
+
".inspecto/cache.json",
|
|
844
|
+
".inspecto/*.local.json",
|
|
845
|
+
".inspecto/dev.json"
|
|
846
|
+
];
|
|
831
847
|
async function updateGitignore(root, shared, dryRun, quiet = false) {
|
|
832
848
|
const gitignorePath = path5.join(root, ".gitignore");
|
|
833
849
|
let content = await readFile(gitignorePath) ?? "";
|
|
@@ -864,7 +880,7 @@ async function cleanGitignore(root) {
|
|
|
864
880
|
const gitignorePath = path5.join(root, ".gitignore");
|
|
865
881
|
const content = await readFile(gitignorePath);
|
|
866
882
|
if (!content) return;
|
|
867
|
-
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");
|
|
883
|
+
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(/^\.inspecto\/dev\.json\s*$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
868
884
|
await writeFile(gitignorePath, cleaned);
|
|
869
885
|
}
|
|
870
886
|
|
|
@@ -921,12 +937,120 @@ function resultStatus(nextSteps) {
|
|
|
921
937
|
return nextSteps.length > 0 ? "warning" : "ok";
|
|
922
938
|
}
|
|
923
939
|
function manualPlanSteps(plan2, includeBlockers = true) {
|
|
924
|
-
const steps = plan2.actions.filter(
|
|
940
|
+
const steps = plan2.actions.filter(
|
|
941
|
+
(action) => ["manual_step", "generate_patch_plan", "generate_file", "manual_confirmation"].includes(
|
|
942
|
+
action.type
|
|
943
|
+
)
|
|
944
|
+
).map((action) => action.description);
|
|
925
945
|
if (!includeBlockers) {
|
|
926
946
|
return steps;
|
|
927
947
|
}
|
|
928
948
|
return [...plan2.blockers.map((blocker) => blocker.message), ...steps];
|
|
929
949
|
}
|
|
950
|
+
function applyGuidedPatchContent(source, snippet) {
|
|
951
|
+
if (source.includes("import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'")) {
|
|
952
|
+
return source;
|
|
953
|
+
}
|
|
954
|
+
if (source.includes("import { vitePlugin as inspecto } from '@inspecto-dev/plugin'")) {
|
|
955
|
+
return source;
|
|
956
|
+
}
|
|
957
|
+
const trimmedSource = source.trimEnd();
|
|
958
|
+
if (/export\s+default\s*\{/m.test(source)) {
|
|
959
|
+
const nextSource = trimmedSource.replace(
|
|
960
|
+
/export\s+default\s*\{/m,
|
|
961
|
+
"import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'\n\nexport default {\n webpack(config, { dev, isServer }) {\n if (dev) {\n config.plugins.push(inspecto())\n }\n return config\n },"
|
|
962
|
+
);
|
|
963
|
+
return `${nextSource}
|
|
964
|
+
`;
|
|
965
|
+
}
|
|
966
|
+
if (/module\.exports\s*=\s*\{/m.test(source)) {
|
|
967
|
+
const nextSource = trimmedSource.replace(
|
|
968
|
+
/module\.exports\s*=\s*\{/m,
|
|
969
|
+
"const { webpackPlugin: inspecto } = require('@inspecto-dev/plugin')\n\nmodule.exports = {\n webpack(config, { dev, isServer }) {\n if (dev) {\n config.plugins.push(inspecto())\n }\n return config\n },"
|
|
970
|
+
);
|
|
971
|
+
return `${nextSource}
|
|
972
|
+
`;
|
|
973
|
+
}
|
|
974
|
+
const objectExportVariableMatch = source.match(
|
|
975
|
+
/(const\s+([A-Za-z0-9_$]+)\s*(?::[^=]+)?=\s*\{[\s\S]*?\}\s*;?\s*)export\s+default\s+\2\s*;?/m
|
|
976
|
+
);
|
|
977
|
+
const variableDeclaration = objectExportVariableMatch?.[1];
|
|
978
|
+
const variableName = objectExportVariableMatch?.[2];
|
|
979
|
+
if (variableDeclaration && variableName) {
|
|
980
|
+
const nextDeclaration = variableDeclaration.replace(
|
|
981
|
+
/=\s*\{/m,
|
|
982
|
+
"= {\n webpack(config, { dev, isServer }) {\n if (dev) {\n config.plugins.push(inspecto())\n }\n return config\n },"
|
|
983
|
+
);
|
|
984
|
+
const nextSource = source.replace(
|
|
985
|
+
objectExportVariableMatch[0],
|
|
986
|
+
`import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'
|
|
987
|
+
|
|
988
|
+
${nextDeclaration}export default ${variableName}`
|
|
989
|
+
);
|
|
990
|
+
return `${nextSource.trimEnd()}
|
|
991
|
+
`;
|
|
992
|
+
}
|
|
993
|
+
if (/defineNuxtConfig\s*\(\s*\{/m.test(source)) {
|
|
994
|
+
const trimmedSource2 = source.trimEnd();
|
|
995
|
+
const nextSource = trimmedSource2.replace(
|
|
996
|
+
/defineNuxtConfig\s*\(\s*\{/m,
|
|
997
|
+
"defineNuxtConfig({\n vite: {\n plugins: [inspecto()],\n },"
|
|
998
|
+
);
|
|
999
|
+
return `import { vitePlugin as inspecto } from '@inspecto-dev/plugin'
|
|
1000
|
+
|
|
1001
|
+
${nextSource}
|
|
1002
|
+
`;
|
|
1003
|
+
}
|
|
1004
|
+
const jsdocMatch = source.match(
|
|
1005
|
+
/\/\*\*[\s\S]*?@type\s*\{import\('next'\)\.NextConfig\}[\s\S]*?\*\/[\s\S]*?(export\s+default|module\.exports)\s*=?\s*\{/m
|
|
1006
|
+
);
|
|
1007
|
+
if (jsdocMatch) {
|
|
1008
|
+
const isEsm = jsdocMatch[1] === "export default";
|
|
1009
|
+
const importStatement = isEsm ? "import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'\n\n" : "const { webpackPlugin: inspecto } = require('@inspecto-dev/plugin')\n\n";
|
|
1010
|
+
const replacementPattern = isEsm ? /export\s+default\s*\{/m : /module\.exports\s*=\s*\{/m;
|
|
1011
|
+
const injectConfig = isEsm ? "export default {\n webpack(config, { dev, isServer }) {\n if (dev) {\n config.plugins.push(inspecto())\n }\n return config\n }," : "module.exports = {\n webpack(config, { dev, isServer }) {\n if (dev) {\n config.plugins.push(inspecto())\n }\n return config\n },";
|
|
1012
|
+
const nextSource = source.replace(replacementPattern, injectConfig);
|
|
1013
|
+
return `${importStatement}${nextSource.trimEnd()}
|
|
1014
|
+
`;
|
|
1015
|
+
}
|
|
1016
|
+
return `${trimmedSource}
|
|
1017
|
+
|
|
1018
|
+
${snippet}
|
|
1019
|
+
`;
|
|
1020
|
+
}
|
|
1021
|
+
async function applyGuidedPlanPatches(input, mutations, reporter) {
|
|
1022
|
+
const nextSteps = [];
|
|
1023
|
+
if (!input.plan || input.plan.strategy !== "guided" || !input.plan.patches?.length) {
|
|
1024
|
+
return nextSteps;
|
|
1025
|
+
}
|
|
1026
|
+
for (const patch of input.plan.patches) {
|
|
1027
|
+
if (patch.status !== "planned" || !patch.reason.startsWith("next_config_") && !patch.reason.startsWith("nuxt_config_")) {
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
const patchPath = path6.join(input.projectRoot, patch.path);
|
|
1031
|
+
const existingContent = await readFile(patchPath);
|
|
1032
|
+
if (existingContent === null) {
|
|
1033
|
+
nextSteps.push(`Could not auto-apply the guided patch for ${patch.path}.`);
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
const nextContent = applyGuidedPatchContent(existingContent, patch.snippet);
|
|
1037
|
+
if (nextContent === existingContent) {
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (input.options.dryRun) {
|
|
1041
|
+
reporter.dryRun(`Would apply guided patch to ${patch.path}`);
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
await writeFile(patchPath, nextContent);
|
|
1045
|
+
mutations.push({
|
|
1046
|
+
type: "file_modified",
|
|
1047
|
+
path: patch.path,
|
|
1048
|
+
description: "Automatically configured Inspecto guided Next.js patch"
|
|
1049
|
+
});
|
|
1050
|
+
reporter.success(`Applied guided patch to ${patch.path}`);
|
|
1051
|
+
}
|
|
1052
|
+
return nextSteps;
|
|
1053
|
+
}
|
|
930
1054
|
async function applyOnboardingPlan(input) {
|
|
931
1055
|
return applyOnboardingPlanInternal(input);
|
|
932
1056
|
}
|
|
@@ -989,7 +1113,7 @@ function createSpinner(text, quiet = false) {
|
|
|
989
1113
|
}
|
|
990
1114
|
async function applyOnboardingPlanInternal(input) {
|
|
991
1115
|
const reporter = createReporter(input.options.quiet);
|
|
992
|
-
const additiveManualPlan = input.plan?.strategy === "manual" && input.allowManualPlanApply && input.plan.blockers.length === 0;
|
|
1116
|
+
const additiveManualPlan = (input.plan?.strategy === "manual" || input.plan?.strategy === "guided") && input.allowManualPlanApply && input.plan.blockers.length === 0;
|
|
993
1117
|
if (input.plan && input.plan.strategy !== "supported" && !input.allowManualPlanApply) {
|
|
994
1118
|
return {
|
|
995
1119
|
status: input.plan.status,
|
|
@@ -1038,6 +1162,11 @@ async function applyOnboardingPlanInternal(input) {
|
|
|
1038
1162
|
spinner.fail("Dependency installation failed");
|
|
1039
1163
|
installFailed = true;
|
|
1040
1164
|
reporter.error(`Failed to install dependency: ${error?.message || "Unknown error"}`);
|
|
1165
|
+
if (error?.stderr) {
|
|
1166
|
+
reporter.error(`Details: ${error.stderr}`);
|
|
1167
|
+
} else if (error?.stdout) {
|
|
1168
|
+
reporter.error(`Details: ${error.stdout}`);
|
|
1169
|
+
}
|
|
1041
1170
|
reporter.hint(`Run manually in ${input.projectRoot}: ${installCmd}`);
|
|
1042
1171
|
reporter.hint(
|
|
1043
1172
|
"Setup will continue without dependencies, but Inspecto may not run until installation succeeds."
|
|
@@ -1060,6 +1189,9 @@ async function applyOnboardingPlanInternal(input) {
|
|
|
1060
1189
|
}
|
|
1061
1190
|
}
|
|
1062
1191
|
}
|
|
1192
|
+
if (additiveManualPlan) {
|
|
1193
|
+
nextSteps.push(...await applyGuidedPlanPatches(input, mutations, reporter));
|
|
1194
|
+
}
|
|
1063
1195
|
if (await exists(settingsPath)) {
|
|
1064
1196
|
const existingSettings = await readJSON(settingsPath);
|
|
1065
1197
|
if (existingSettings === null) {
|
|
@@ -1312,6 +1444,11 @@ var SUPPORTED_PATTERNS = [
|
|
|
1312
1444
|
}
|
|
1313
1445
|
];
|
|
1314
1446
|
var UNSUPPORTED_META = [
|
|
1447
|
+
{
|
|
1448
|
+
name: "Umi",
|
|
1449
|
+
dep: "umi",
|
|
1450
|
+
files: [".umirc.ts", ".umirc.js", "config/config.ts", "config/config.js"]
|
|
1451
|
+
},
|
|
1315
1452
|
{ name: "Next.js", dep: "next", files: ["next.config.mjs", "next.config.js", "next.config.ts"] },
|
|
1316
1453
|
{ name: "Nuxt", dep: "nuxt", files: ["nuxt.config.ts", "nuxt.config.js"] },
|
|
1317
1454
|
{ name: "Remix", dep: "@remix-run/dev", files: ["remix.config.js", "remix.config.ts"] },
|
|
@@ -1425,7 +1562,8 @@ async function detectBuildTools(root, packagePaths) {
|
|
|
1425
1562
|
}
|
|
1426
1563
|
}
|
|
1427
1564
|
const unsupportedChecks = UNSUPPORTED_META.map(async (meta) => {
|
|
1428
|
-
|
|
1565
|
+
const hasDep = meta.dep in allDeps || Object.keys(allDeps).some((dep) => dep.includes(meta.dep));
|
|
1566
|
+
if (!hasDep) return null;
|
|
1429
1567
|
for (const file of meta.files) {
|
|
1430
1568
|
if (await exists(path7.join(target.absolutePath, file))) {
|
|
1431
1569
|
return meta.name;
|
|
@@ -1549,6 +1687,8 @@ async function detectPattern({
|
|
|
1549
1687
|
}
|
|
1550
1688
|
} else if (pattern.tool === "rsbuild") {
|
|
1551
1689
|
hasDep = !!allDeps["@rsbuild/core"] || isPackageResolvable("@rsbuild/core", targetRoot);
|
|
1690
|
+
} else if (pattern.tool === "vite") {
|
|
1691
|
+
hasDep = !!allDeps["vite"] || isPackageResolvable("vite", targetRoot);
|
|
1552
1692
|
} else {
|
|
1553
1693
|
hasDep = !!allDeps[pattern.tool] || isPackageResolvable(pattern.tool, targetRoot);
|
|
1554
1694
|
}
|
|
@@ -1624,7 +1764,7 @@ async function detectPattern({
|
|
|
1624
1764
|
tool: pattern.tool,
|
|
1625
1765
|
configPath: "package.json (dependency)",
|
|
1626
1766
|
label: `${pattern.label} (detected via dependency)`,
|
|
1627
|
-
packagePath
|
|
1767
|
+
...packagePath ? { packagePath } : {}
|
|
1628
1768
|
};
|
|
1629
1769
|
}
|
|
1630
1770
|
return null;
|
|
@@ -1646,9 +1786,9 @@ async function detectPattern({
|
|
|
1646
1786
|
tool: pattern.tool,
|
|
1647
1787
|
configPath: relativeConfig,
|
|
1648
1788
|
label: `${pattern.label} (${relativeConfig})${isLegacyRspack ? " [Legacy]" : ""}${isLegacyWebpack ? " [Webpack 4]" : ""}${inferredFromScripts ? " [Scripts Detected]" : ""}`,
|
|
1649
|
-
isLegacyRspack,
|
|
1650
|
-
isLegacyWebpack,
|
|
1651
|
-
packagePath
|
|
1789
|
+
...isLegacyRspack ? { isLegacyRspack: true } : {},
|
|
1790
|
+
...isLegacyWebpack ? { isLegacyWebpack: true } : {},
|
|
1791
|
+
...packagePath ? { packagePath } : {}
|
|
1652
1792
|
};
|
|
1653
1793
|
}
|
|
1654
1794
|
function resolveInjectionTarget(detections) {
|
|
@@ -1678,9 +1818,12 @@ var SUPPORTED_FRAMEWORKS = [
|
|
|
1678
1818
|
var UNSUPPORTED_FRAMEWORKS = [
|
|
1679
1819
|
{ name: "Solid", dep: "solid-js" },
|
|
1680
1820
|
{ name: "Svelte", dep: "svelte" },
|
|
1821
|
+
{ name: "SvelteKit", dep: "@sveltejs/kit" },
|
|
1681
1822
|
{ name: "Angular", dep: "@angular/core" },
|
|
1682
1823
|
{ name: "Preact", dep: "preact" },
|
|
1683
|
-
{ name: "Lit", dep: "lit" }
|
|
1824
|
+
{ name: "Lit", dep: "lit" },
|
|
1825
|
+
{ name: "Qwik", dep: "qwik" },
|
|
1826
|
+
{ name: "Alpine", dep: "lit-html" }
|
|
1684
1827
|
];
|
|
1685
1828
|
function isPackageResolvable2(pkgName, root) {
|
|
1686
1829
|
try {
|
|
@@ -1896,6 +2039,418 @@ async function buildOnboardingContext(root) {
|
|
|
1896
2039
|
};
|
|
1897
2040
|
}
|
|
1898
2041
|
|
|
2042
|
+
// src/onboarding/nextjs-guidance.ts
|
|
2043
|
+
import fs3 from "fs";
|
|
2044
|
+
import path11 from "path";
|
|
2045
|
+
var NEXT_CONFIG_CANDIDATES = ["next.config.ts", "next.config.mjs", "next.config.js"];
|
|
2046
|
+
var APP_ROUTER_LAYOUTS = [
|
|
2047
|
+
"app/layout.tsx",
|
|
2048
|
+
"app/layout.jsx",
|
|
2049
|
+
"src/app/layout.tsx",
|
|
2050
|
+
"src/app/layout.jsx"
|
|
2051
|
+
];
|
|
2052
|
+
var PAGES_ROUTER_APPS = [
|
|
2053
|
+
"pages/_app.tsx",
|
|
2054
|
+
"pages/_app.jsx",
|
|
2055
|
+
"src/pages/_app.tsx",
|
|
2056
|
+
"src/pages/_app.jsx"
|
|
2057
|
+
];
|
|
2058
|
+
var PACKAGE_JSON_PATH = "package.json";
|
|
2059
|
+
function findFirstExisting(root, candidates) {
|
|
2060
|
+
for (const candidate of candidates) {
|
|
2061
|
+
if (fs3.existsSync(path11.join(root, candidate))) {
|
|
2062
|
+
return candidate;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
return void 0;
|
|
2066
|
+
}
|
|
2067
|
+
function detectRouterMode(root) {
|
|
2068
|
+
const hasAppRouter = Boolean(findFirstExisting(root, APP_ROUTER_LAYOUTS));
|
|
2069
|
+
const hasPagesRouter = Boolean(findFirstExisting(root, PAGES_ROUTER_APPS));
|
|
2070
|
+
if (hasAppRouter && hasPagesRouter) return "mixed";
|
|
2071
|
+
if (hasAppRouter) return "app";
|
|
2072
|
+
if (hasPagesRouter) return "pages";
|
|
2073
|
+
return "unknown";
|
|
2074
|
+
}
|
|
2075
|
+
function detectNextConfigPath(root) {
|
|
2076
|
+
return findFirstExisting(root, NEXT_CONFIG_CANDIDATES);
|
|
2077
|
+
}
|
|
2078
|
+
function readConfig(root, relativePath) {
|
|
2079
|
+
if (!relativePath) return "";
|
|
2080
|
+
const filePath = path11.join(root, relativePath);
|
|
2081
|
+
if (!fs3.existsSync(filePath)) return "";
|
|
2082
|
+
return fs3.readFileSync(filePath, "utf8");
|
|
2083
|
+
}
|
|
2084
|
+
function detectPatchShape(source) {
|
|
2085
|
+
if (/export\s+default\s*\{[\s\S]*\}/m.test(source) || /module\.exports\s*=\s*\{[\s\S]*\}/m.test(source) || /const\s+[A-Za-z0-9_$]+\s*(?::[^=]+)?=\s*\{[\s\S]*\}\s*;?\s*export\s+default\s+[A-Za-z0-9_$]+\s*;?/m.test(
|
|
2086
|
+
source
|
|
2087
|
+
) || /\/\*\*[\s\S]*?@type\s*\{import\('next'\)\.NextConfig\}[\s\S]*?\*\/[\s\S]*?(export\s+default|module\.exports)\s*=?\s*\{[\s\S]*\}/m.test(
|
|
2088
|
+
source
|
|
2089
|
+
)) {
|
|
2090
|
+
return {
|
|
2091
|
+
status: "planned",
|
|
2092
|
+
reason: "next_config_object_export",
|
|
2093
|
+
confidence: "high"
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
if (/module\.exports\s*=\s*[A-Za-z0-9_$]+\s*\(/m.test(source) || /export\s+default\s+[A-Za-z0-9_$]+\s*\(/m.test(source)) {
|
|
2097
|
+
return {
|
|
2098
|
+
status: "manual_patch_required",
|
|
2099
|
+
reason: "next_config_wrapped_export",
|
|
2100
|
+
confidence: "medium"
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
if (source.trim().length === 0) {
|
|
2104
|
+
return {
|
|
2105
|
+
status: "manual_patch_required",
|
|
2106
|
+
reason: "next_config_missing",
|
|
2107
|
+
confidence: "low"
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
return {
|
|
2111
|
+
status: "manual_patch_required",
|
|
2112
|
+
reason: "next_config_complex_shape",
|
|
2113
|
+
confidence: "medium"
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
function buildPatchSnippet() {
|
|
2117
|
+
return [
|
|
2118
|
+
"import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
2119
|
+
"",
|
|
2120
|
+
"webpack(config, { dev, isServer }) {",
|
|
2121
|
+
" if (dev) {",
|
|
2122
|
+
" config.plugins.push(inspecto())",
|
|
2123
|
+
" }",
|
|
2124
|
+
" return config",
|
|
2125
|
+
"}"
|
|
2126
|
+
].join("\n");
|
|
2127
|
+
}
|
|
2128
|
+
function buildAppRouterMountSnippet() {
|
|
2129
|
+
return [
|
|
2130
|
+
"import { useEffect } from 'react'",
|
|
2131
|
+
"",
|
|
2132
|
+
"function InspectoProvider() {",
|
|
2133
|
+
" useEffect(() => {",
|
|
2134
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
2135
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
2136
|
+
" mountInspector()",
|
|
2137
|
+
" })",
|
|
2138
|
+
" }",
|
|
2139
|
+
" }, [])",
|
|
2140
|
+
"",
|
|
2141
|
+
" return null",
|
|
2142
|
+
"}",
|
|
2143
|
+
"",
|
|
2144
|
+
"Render <InspectoProvider /> from app/layout.tsx inside the <body> tree."
|
|
2145
|
+
].join("\n");
|
|
2146
|
+
}
|
|
2147
|
+
function buildPagesRouterMountSnippet() {
|
|
2148
|
+
return [
|
|
2149
|
+
"import { useEffect } from 'react'",
|
|
2150
|
+
"",
|
|
2151
|
+
"useEffect(() => {",
|
|
2152
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
2153
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
2154
|
+
" mountInspector()",
|
|
2155
|
+
" })",
|
|
2156
|
+
" }",
|
|
2157
|
+
"}, [])",
|
|
2158
|
+
"",
|
|
2159
|
+
"Add this effect to pages/_app.tsx without changing the existing app wrapper behavior."
|
|
2160
|
+
].join("\n");
|
|
2161
|
+
}
|
|
2162
|
+
function detectWebpackDevScriptPatch(root) {
|
|
2163
|
+
const packageJsonSource = readConfig(root, PACKAGE_JSON_PATH);
|
|
2164
|
+
if (!packageJsonSource) {
|
|
2165
|
+
return void 0;
|
|
2166
|
+
}
|
|
2167
|
+
try {
|
|
2168
|
+
const packageJson = JSON.parse(packageJsonSource);
|
|
2169
|
+
const devScript = packageJson.scripts?.dev;
|
|
2170
|
+
if (!devScript || !/\bnext\s+dev\b/.test(devScript) || /--webpack\b/.test(devScript)) {
|
|
2171
|
+
return void 0;
|
|
2172
|
+
}
|
|
2173
|
+
return {
|
|
2174
|
+
path: PACKAGE_JSON_PATH,
|
|
2175
|
+
status: "manual_patch_required",
|
|
2176
|
+
reason: "next_dev_script_requires_webpack",
|
|
2177
|
+
confidence: "medium",
|
|
2178
|
+
snippet: '"dev": "next dev --webpack"'
|
|
2179
|
+
};
|
|
2180
|
+
} catch {
|
|
2181
|
+
return void 0;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
function buildPendingSteps(routerMode, configPath, needsWebpackDevScript) {
|
|
2185
|
+
const routerHint = routerMode === "app" ? "Complete the remaining client-side mount step for your App Router entry." : routerMode === "pages" ? "Complete the remaining client-side mount step for your Pages Router entry." : routerMode === "mixed" ? "Complete the remaining client-side mount step for the router entry your team actually uses in development." : "Complete the remaining client-side mount step in the appropriate Next.js entry file.";
|
|
2186
|
+
return [
|
|
2187
|
+
`Review the generated Next.js patch plan for ${configPath}.`,
|
|
2188
|
+
...needsWebpackDevScript ? ["Update the Next.js dev script to use webpack mode for Inspecto validation."] : [],
|
|
2189
|
+
"Keep the Inspecto webpack plugin enabled for both server and client development compilers in App Router projects.",
|
|
2190
|
+
routerHint
|
|
2191
|
+
];
|
|
2192
|
+
}
|
|
2193
|
+
function createNextJsGuidance(root) {
|
|
2194
|
+
const configPath = detectNextConfigPath(root) ?? "next.config.js";
|
|
2195
|
+
const configSource = readConfig(root, detectNextConfigPath(root));
|
|
2196
|
+
const patchShape = detectPatchShape(configSource);
|
|
2197
|
+
const routerMode = detectRouterMode(root);
|
|
2198
|
+
const webpackDevScriptPatch = detectWebpackDevScriptPatch(root);
|
|
2199
|
+
return {
|
|
2200
|
+
framework: "react",
|
|
2201
|
+
metaFramework: "Next.js",
|
|
2202
|
+
routerMode,
|
|
2203
|
+
autoApplied: ["dependencies", "inspecto_settings"],
|
|
2204
|
+
pendingSteps: buildPendingSteps(routerMode, configPath, Boolean(webpackDevScriptPatch)),
|
|
2205
|
+
assistantPrompt: "Complete the remaining Inspecto onboarding for this Next.js project. Use the generated patches directly, keep existing app behavior unchanged, avoid unrelated documentation searches unless a patch is insufficient, and finish the client-side mount step safely.",
|
|
2206
|
+
patches: [
|
|
2207
|
+
{
|
|
2208
|
+
path: configPath,
|
|
2209
|
+
status: patchShape.status,
|
|
2210
|
+
reason: patchShape.reason,
|
|
2211
|
+
confidence: patchShape.confidence,
|
|
2212
|
+
snippet: buildPatchSnippet()
|
|
2213
|
+
},
|
|
2214
|
+
...routerMode === "app" || routerMode === "mixed" ? [
|
|
2215
|
+
{
|
|
2216
|
+
path: findFirstExisting(root, APP_ROUTER_LAYOUTS) ?? "app/layout.tsx",
|
|
2217
|
+
status: "manual_patch_required",
|
|
2218
|
+
reason: "next_app_router_mount",
|
|
2219
|
+
confidence: "medium",
|
|
2220
|
+
snippet: buildAppRouterMountSnippet()
|
|
2221
|
+
}
|
|
2222
|
+
] : [],
|
|
2223
|
+
...routerMode === "pages" || routerMode === "mixed" ? [
|
|
2224
|
+
{
|
|
2225
|
+
path: findFirstExisting(root, PAGES_ROUTER_APPS) ?? "pages/_app.tsx",
|
|
2226
|
+
status: "manual_patch_required",
|
|
2227
|
+
reason: "next_pages_router_mount",
|
|
2228
|
+
confidence: "medium",
|
|
2229
|
+
snippet: buildPagesRouterMountSnippet()
|
|
2230
|
+
}
|
|
2231
|
+
] : [],
|
|
2232
|
+
...webpackDevScriptPatch ? [webpackDevScriptPatch] : []
|
|
2233
|
+
]
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
// src/onboarding/nuxt-guidance.ts
|
|
2238
|
+
import fs4 from "fs";
|
|
2239
|
+
import path12 from "path";
|
|
2240
|
+
var NUXT_CONFIG_CANDIDATES = ["nuxt.config.ts", "nuxt.config.js"];
|
|
2241
|
+
function findFirstExisting2(root, candidates) {
|
|
2242
|
+
for (const candidate of candidates) {
|
|
2243
|
+
if (fs4.existsSync(path12.join(root, candidate))) {
|
|
2244
|
+
return candidate;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
return void 0;
|
|
2248
|
+
}
|
|
2249
|
+
function readConfig2(root, relativePath) {
|
|
2250
|
+
if (!relativePath) return "";
|
|
2251
|
+
const filePath = path12.join(root, relativePath);
|
|
2252
|
+
if (!fs4.existsSync(filePath)) return "";
|
|
2253
|
+
return fs4.readFileSync(filePath, "utf8");
|
|
2254
|
+
}
|
|
2255
|
+
function detectPatchShape2(source) {
|
|
2256
|
+
if (/with[A-Za-z0-9_$]*\s*\(\s*defineNuxtConfig/m.test(source)) {
|
|
2257
|
+
return {
|
|
2258
|
+
status: "manual_patch_required",
|
|
2259
|
+
reason: "nuxt_config_wrapped_export",
|
|
2260
|
+
confidence: "medium"
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
if (/defineNuxtConfig\s*\(\s*\{[\s\S]*\}\s*\)/m.test(source)) {
|
|
2264
|
+
return {
|
|
2265
|
+
status: "planned",
|
|
2266
|
+
reason: "nuxt_config_object_export",
|
|
2267
|
+
confidence: "high"
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
if (source.trim().length === 0) {
|
|
2271
|
+
return {
|
|
2272
|
+
status: "manual_patch_required",
|
|
2273
|
+
reason: "nuxt_config_missing",
|
|
2274
|
+
confidence: "low"
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
return {
|
|
2278
|
+
status: "manual_patch_required",
|
|
2279
|
+
reason: "nuxt_config_complex_shape",
|
|
2280
|
+
confidence: "medium"
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
function buildNuxtConfigSnippet() {
|
|
2284
|
+
return [
|
|
2285
|
+
"import { vitePlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
2286
|
+
"",
|
|
2287
|
+
"export default defineNuxtConfig({",
|
|
2288
|
+
" vite: {",
|
|
2289
|
+
" plugins: [inspecto()],",
|
|
2290
|
+
" },",
|
|
2291
|
+
"})"
|
|
2292
|
+
].join("\n");
|
|
2293
|
+
}
|
|
2294
|
+
function buildNuxtPluginSnippet() {
|
|
2295
|
+
return [
|
|
2296
|
+
"export default defineNuxtPlugin(() => {",
|
|
2297
|
+
" if (import.meta.dev) {",
|
|
2298
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
2299
|
+
" mountInspector()",
|
|
2300
|
+
" })",
|
|
2301
|
+
" }",
|
|
2302
|
+
"})"
|
|
2303
|
+
].join("\n");
|
|
2304
|
+
}
|
|
2305
|
+
function createNuxtGuidance(root) {
|
|
2306
|
+
const configPath = findFirstExisting2(root, NUXT_CONFIG_CANDIDATES) ?? "nuxt.config.ts";
|
|
2307
|
+
const configSource = readConfig2(root, findFirstExisting2(root, NUXT_CONFIG_CANDIDATES));
|
|
2308
|
+
const patchShape = detectPatchShape2(configSource);
|
|
2309
|
+
const hasSrcDir = fs4.existsSync(path12.join(root, "src")) && fs4.statSync(path12.join(root, "src")).isDirectory();
|
|
2310
|
+
const pluginPath = hasSrcDir ? "src/plugins/inspecto.client.ts" : "plugins/inspecto.client.ts";
|
|
2311
|
+
return {
|
|
2312
|
+
framework: "vue",
|
|
2313
|
+
metaFramework: "Nuxt",
|
|
2314
|
+
autoApplied: ["dependencies", "inspecto_settings"],
|
|
2315
|
+
pendingSteps: [
|
|
2316
|
+
`Review the generated Nuxt patch plan for ${configPath}.`,
|
|
2317
|
+
`Complete the remaining Nuxt client plugin mount step in ${pluginPath}.`
|
|
2318
|
+
],
|
|
2319
|
+
assistantPrompt: "Complete the remaining Inspecto onboarding for this Nuxt project. Review the generated patch plan, keep existing app behavior unchanged, and finish the client plugin mount step safely.",
|
|
2320
|
+
patches: [
|
|
2321
|
+
{
|
|
2322
|
+
path: configPath,
|
|
2323
|
+
status: patchShape.status,
|
|
2324
|
+
reason: patchShape.reason,
|
|
2325
|
+
confidence: patchShape.confidence,
|
|
2326
|
+
snippet: buildNuxtConfigSnippet()
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
path: pluginPath,
|
|
2330
|
+
status: "manual_patch_required",
|
|
2331
|
+
reason: "nuxt_client_plugin_mount",
|
|
2332
|
+
confidence: "medium",
|
|
2333
|
+
snippet: buildNuxtPluginSnippet()
|
|
2334
|
+
}
|
|
2335
|
+
]
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// src/onboarding/umi-guidance.ts
|
|
2340
|
+
import fs5 from "fs";
|
|
2341
|
+
import path13 from "path";
|
|
2342
|
+
var UMI_CONFIG_CANDIDATES = [
|
|
2343
|
+
".umirc.ts",
|
|
2344
|
+
".umirc.js",
|
|
2345
|
+
"config/config.ts",
|
|
2346
|
+
"config/config.js"
|
|
2347
|
+
];
|
|
2348
|
+
function findFirstExisting3(root, candidates) {
|
|
2349
|
+
for (const candidate of candidates) {
|
|
2350
|
+
if (fs5.existsSync(path13.join(root, candidate))) {
|
|
2351
|
+
return candidate;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return void 0;
|
|
2355
|
+
}
|
|
2356
|
+
function readConfig3(root, relativePath) {
|
|
2357
|
+
if (!relativePath) return "";
|
|
2358
|
+
const filePath = path13.join(root, relativePath);
|
|
2359
|
+
if (!fs5.existsSync(filePath)) return "";
|
|
2360
|
+
return fs5.readFileSync(filePath, "utf8");
|
|
2361
|
+
}
|
|
2362
|
+
function detectPatchShape3(source) {
|
|
2363
|
+
if (/defineConfig\s*\(\s*\{[\s\S]*\}\s*\)/m.test(source)) {
|
|
2364
|
+
return {
|
|
2365
|
+
status: "planned",
|
|
2366
|
+
reason: "umi_config_object_export",
|
|
2367
|
+
confidence: "high"
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
if (source.trim().length === 0) {
|
|
2371
|
+
return {
|
|
2372
|
+
status: "manual_patch_required",
|
|
2373
|
+
reason: "umi_config_missing",
|
|
2374
|
+
confidence: "low"
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
return {
|
|
2378
|
+
status: "manual_patch_required",
|
|
2379
|
+
reason: "umi_config_complex_shape",
|
|
2380
|
+
confidence: "medium"
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
function buildUmiConfigSnippet() {
|
|
2384
|
+
return [
|
|
2385
|
+
"import { defineConfig } from 'umi'",
|
|
2386
|
+
"import { webpack4Plugin } from '@inspecto-dev/plugin/legacy/webpack4'",
|
|
2387
|
+
"",
|
|
2388
|
+
"export default defineConfig({",
|
|
2389
|
+
" chainWebpack(memo) {",
|
|
2390
|
+
" if (process.env.NODE_ENV === 'development') {",
|
|
2391
|
+
" memo.plugin('inspecto').use(webpack4Plugin())",
|
|
2392
|
+
" }",
|
|
2393
|
+
" },",
|
|
2394
|
+
"})"
|
|
2395
|
+
].join("\n");
|
|
2396
|
+
}
|
|
2397
|
+
function buildUmiMountSnippet() {
|
|
2398
|
+
return [
|
|
2399
|
+
"import { useEffect } from 'react'",
|
|
2400
|
+
"",
|
|
2401
|
+
"export function rootContainer(container: React.ReactNode) {",
|
|
2402
|
+
" return (",
|
|
2403
|
+
" <InspectoWrapper>{container}</InspectoWrapper>",
|
|
2404
|
+
" )",
|
|
2405
|
+
"}",
|
|
2406
|
+
"",
|
|
2407
|
+
"function InspectoWrapper({ children }: { children: React.ReactNode }) {",
|
|
2408
|
+
" useEffect(() => {",
|
|
2409
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
2410
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
2411
|
+
" mountInspector({",
|
|
2412
|
+
" serverUrl: 'http://127.0.0.1:' + ((window as any).__AI_INSPECTOR_PORT__ || 5678),",
|
|
2413
|
+
" })",
|
|
2414
|
+
" })",
|
|
2415
|
+
" }",
|
|
2416
|
+
" }, [])",
|
|
2417
|
+
"",
|
|
2418
|
+
" return <>{children}</>",
|
|
2419
|
+
"}"
|
|
2420
|
+
].join("\n");
|
|
2421
|
+
}
|
|
2422
|
+
function createUmiGuidance(root) {
|
|
2423
|
+
const configPath = findFirstExisting3(root, UMI_CONFIG_CANDIDATES) ?? ".umirc.ts";
|
|
2424
|
+
const configSource = readConfig3(root, findFirstExisting3(root, UMI_CONFIG_CANDIDATES));
|
|
2425
|
+
const patchShape = detectPatchShape3(configSource);
|
|
2426
|
+
return {
|
|
2427
|
+
framework: "react",
|
|
2428
|
+
metaFramework: "Umi",
|
|
2429
|
+
autoApplied: ["dependencies", "inspecto_settings"],
|
|
2430
|
+
pendingSteps: [
|
|
2431
|
+
`Review the generated Umi patch plan for ${configPath}.`,
|
|
2432
|
+
"Complete the remaining client-side mount step in src/app.tsx."
|
|
2433
|
+
],
|
|
2434
|
+
assistantPrompt: "Complete the remaining Inspecto onboarding for this Umi project. Review the generated patch plan, keep existing app behavior unchanged, and finish the client-side mount step safely.",
|
|
2435
|
+
patches: [
|
|
2436
|
+
{
|
|
2437
|
+
path: configPath,
|
|
2438
|
+
status: patchShape.status,
|
|
2439
|
+
reason: patchShape.reason,
|
|
2440
|
+
confidence: patchShape.confidence,
|
|
2441
|
+
snippet: buildUmiConfigSnippet()
|
|
2442
|
+
},
|
|
2443
|
+
{
|
|
2444
|
+
path: "src/app.tsx",
|
|
2445
|
+
status: "manual_patch_required",
|
|
2446
|
+
reason: "umi_app_mount",
|
|
2447
|
+
confidence: "medium",
|
|
2448
|
+
snippet: buildUmiMountSnippet()
|
|
2449
|
+
}
|
|
2450
|
+
]
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
|
|
1899
2454
|
// src/onboarding/planner.ts
|
|
1900
2455
|
function message(code, message2) {
|
|
1901
2456
|
return { code, message: message2 };
|
|
@@ -1922,10 +2477,16 @@ function planStatus(warnings, blockers) {
|
|
|
1922
2477
|
function supportedIde(context) {
|
|
1923
2478
|
return context.ides.find((ide) => ide.supported)?.ide;
|
|
1924
2479
|
}
|
|
2480
|
+
function shouldInstallInspectoExtension(ide) {
|
|
2481
|
+
return Boolean(ide && isSupportedHostIde(ide));
|
|
2482
|
+
}
|
|
1925
2483
|
function supportedProvider(context) {
|
|
1926
2484
|
return context.providers.find((provider) => provider.supported)?.id;
|
|
1927
2485
|
}
|
|
1928
2486
|
function buildToolBlockers(context) {
|
|
2487
|
+
if (isGuidedMetaFrameworkScenario(context)) {
|
|
2488
|
+
return [];
|
|
2489
|
+
}
|
|
1929
2490
|
if (context.buildTools.unsupported.length > 0) {
|
|
1930
2491
|
return [
|
|
1931
2492
|
message(
|
|
@@ -2068,6 +2629,46 @@ function manualBuildToolActions(context) {
|
|
|
2068
2629
|
}
|
|
2069
2630
|
];
|
|
2070
2631
|
}
|
|
2632
|
+
function hasUnsupportedBuildTool(context, buildTool) {
|
|
2633
|
+
return context.buildTools.unsupported.includes(buildTool);
|
|
2634
|
+
}
|
|
2635
|
+
function isGuidedNextJsScenario(context) {
|
|
2636
|
+
return context.buildTools.supported.length === 0 && hasUnsupportedBuildTool(context, "Next.js") && context.frameworks.supported.includes("react");
|
|
2637
|
+
}
|
|
2638
|
+
function isGuidedNuxtScenario(context) {
|
|
2639
|
+
return context.buildTools.supported.length === 0 && hasUnsupportedBuildTool(context, "Nuxt") && context.frameworks.supported.includes("vue");
|
|
2640
|
+
}
|
|
2641
|
+
function isGuidedUmiScenario(context) {
|
|
2642
|
+
return hasUnsupportedBuildTool(context, "Umi") && context.frameworks.supported.includes("react");
|
|
2643
|
+
}
|
|
2644
|
+
function isGuidedMetaFrameworkScenario(context) {
|
|
2645
|
+
return isGuidedNextJsScenario(context) || isGuidedNuxtScenario(context) || isGuidedUmiScenario(context);
|
|
2646
|
+
}
|
|
2647
|
+
function guidedBuildToolWarnings(context, guidedBuildTool) {
|
|
2648
|
+
return context.buildTools.unsupported.filter((buildTool) => buildTool !== guidedBuildTool).map(
|
|
2649
|
+
(buildTool) => message(
|
|
2650
|
+
"additional-unsupported-build-tool",
|
|
2651
|
+
`Additional unsupported build tool also detected: ${buildTool}`
|
|
2652
|
+
)
|
|
2653
|
+
);
|
|
2654
|
+
}
|
|
2655
|
+
function guidedFrameworkWarnings(context, framework) {
|
|
2656
|
+
return context.frameworks.unsupported.filter((item) => item !== framework).map(
|
|
2657
|
+
(item) => message(
|
|
2658
|
+
"additional-unsupported-framework",
|
|
2659
|
+
`Additional unsupported framework also detected: ${item}`
|
|
2660
|
+
)
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
function buildGuidedWarnings(context, guidedBuildTool, guidedFramework) {
|
|
2664
|
+
return uniqueMessages([
|
|
2665
|
+
...guidedBuildToolWarnings(context, guidedBuildTool),
|
|
2666
|
+
...guidedFrameworkWarnings(context, guidedFramework),
|
|
2667
|
+
...unsupportedEnvironmentWarnings(context)
|
|
2668
|
+
]).filter(
|
|
2669
|
+
(warning) => warning.message !== `Unsupported framework(s) also detected: ${context.frameworks.unsupported.join(", ")}`
|
|
2670
|
+
);
|
|
2671
|
+
}
|
|
2071
2672
|
function manualFrameworkActions(context) {
|
|
2072
2673
|
if (context.frameworks.unsupported.length > 0) {
|
|
2073
2674
|
return [
|
|
@@ -2114,6 +2715,153 @@ async function createDetectionResult(root) {
|
|
|
2114
2715
|
};
|
|
2115
2716
|
}
|
|
2116
2717
|
function createPlanResult(context) {
|
|
2718
|
+
if (isGuidedNextJsScenario(context)) {
|
|
2719
|
+
const ide2 = supportedIde(context);
|
|
2720
|
+
const provider2 = supportedProvider(context);
|
|
2721
|
+
const guidance = createNextJsGuidance(context.root);
|
|
2722
|
+
const actions2 = [
|
|
2723
|
+
{
|
|
2724
|
+
type: "install_dependency",
|
|
2725
|
+
target: "@inspecto-dev/plugin @inspecto-dev/core",
|
|
2726
|
+
description: `Install the Inspecto runtime packages with ${context.packageManager}.`
|
|
2727
|
+
}
|
|
2728
|
+
];
|
|
2729
|
+
if (shouldInstallInspectoExtension(ide2) && ide2) {
|
|
2730
|
+
actions2.push({
|
|
2731
|
+
type: "install_extension",
|
|
2732
|
+
target: ide2,
|
|
2733
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide2)} extension.`
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2736
|
+
actions2.push(
|
|
2737
|
+
{
|
|
2738
|
+
type: "generate_patch_plan",
|
|
2739
|
+
target: "next.config",
|
|
2740
|
+
description: "Generate a guided patch plan for the Next.js Inspecto webpack integration."
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
type: "manual_confirmation",
|
|
2744
|
+
target: context.root,
|
|
2745
|
+
description: "Complete the remaining client-side Inspecto mount step in your assistant or editor."
|
|
2746
|
+
}
|
|
2747
|
+
);
|
|
2748
|
+
const defaults2 = {
|
|
2749
|
+
shared: false,
|
|
2750
|
+
extension: shouldInstallInspectoExtension(ide2),
|
|
2751
|
+
...provider2 ? { provider: provider2 } : {},
|
|
2752
|
+
...ide2 ? { ide: ide2 } : {}
|
|
2753
|
+
};
|
|
2754
|
+
return {
|
|
2755
|
+
status: "warning",
|
|
2756
|
+
warnings: buildGuidedWarnings(context, "Next.js", "react"),
|
|
2757
|
+
blockers: [],
|
|
2758
|
+
strategy: "guided",
|
|
2759
|
+
actions: actions2,
|
|
2760
|
+
defaults: defaults2,
|
|
2761
|
+
framework: guidance.framework,
|
|
2762
|
+
metaFramework: guidance.metaFramework,
|
|
2763
|
+
routerMode: guidance.routerMode,
|
|
2764
|
+
autoApplied: guidance.autoApplied,
|
|
2765
|
+
pendingSteps: guidance.pendingSteps,
|
|
2766
|
+
assistantPrompt: guidance.assistantPrompt,
|
|
2767
|
+
patches: guidance.patches
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
if (isGuidedNuxtScenario(context)) {
|
|
2771
|
+
const ide2 = supportedIde(context);
|
|
2772
|
+
const provider2 = supportedProvider(context);
|
|
2773
|
+
const guidance = createNuxtGuidance(context.root);
|
|
2774
|
+
const actions2 = [
|
|
2775
|
+
{
|
|
2776
|
+
type: "install_dependency",
|
|
2777
|
+
target: "@inspecto-dev/plugin @inspecto-dev/core",
|
|
2778
|
+
description: `Install the Inspecto runtime packages with ${context.packageManager}.`
|
|
2779
|
+
}
|
|
2780
|
+
];
|
|
2781
|
+
if (shouldInstallInspectoExtension(ide2) && ide2) {
|
|
2782
|
+
actions2.push({
|
|
2783
|
+
type: "install_extension",
|
|
2784
|
+
target: ide2,
|
|
2785
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide2)} extension.`
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
actions2.push(
|
|
2789
|
+
{
|
|
2790
|
+
type: "generate_patch_plan",
|
|
2791
|
+
target: "nuxt.config",
|
|
2792
|
+
description: "Generate a guided patch plan for the Nuxt Inspecto Vite integration."
|
|
2793
|
+
},
|
|
2794
|
+
{
|
|
2795
|
+
type: "manual_confirmation",
|
|
2796
|
+
target: context.root,
|
|
2797
|
+
description: "Complete the remaining Nuxt client plugin mount step in your assistant or editor."
|
|
2798
|
+
}
|
|
2799
|
+
);
|
|
2800
|
+
const defaults2 = {
|
|
2801
|
+
shared: false,
|
|
2802
|
+
extension: shouldInstallInspectoExtension(ide2),
|
|
2803
|
+
...provider2 ? { provider: provider2 } : {},
|
|
2804
|
+
...ide2 ? { ide: ide2 } : {}
|
|
2805
|
+
};
|
|
2806
|
+
return {
|
|
2807
|
+
status: "warning",
|
|
2808
|
+
warnings: buildGuidedWarnings(context, "Nuxt", "vue"),
|
|
2809
|
+
blockers: [],
|
|
2810
|
+
strategy: "guided",
|
|
2811
|
+
actions: actions2,
|
|
2812
|
+
defaults: defaults2,
|
|
2813
|
+
framework: guidance.framework,
|
|
2814
|
+
metaFramework: guidance.metaFramework,
|
|
2815
|
+
autoApplied: guidance.autoApplied,
|
|
2816
|
+
pendingSteps: guidance.pendingSteps,
|
|
2817
|
+
assistantPrompt: guidance.assistantPrompt,
|
|
2818
|
+
patches: guidance.patches
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
if (isGuidedUmiScenario(context)) {
|
|
2822
|
+
const ide2 = supportedIde(context);
|
|
2823
|
+
const provider2 = supportedProvider(context);
|
|
2824
|
+
const guidance = createUmiGuidance(context.root);
|
|
2825
|
+
const actions2 = [
|
|
2826
|
+
{
|
|
2827
|
+
type: "install_dependency",
|
|
2828
|
+
target: "@inspecto-dev/plugin @inspecto-dev/core",
|
|
2829
|
+
description: `Install the Inspecto runtime packages with ${context.packageManager}.`
|
|
2830
|
+
}
|
|
2831
|
+
];
|
|
2832
|
+
if (shouldInstallInspectoExtension(ide2) && ide2) {
|
|
2833
|
+
actions2.push({
|
|
2834
|
+
type: "install_extension",
|
|
2835
|
+
target: ide2,
|
|
2836
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide2)} extension.`
|
|
2837
|
+
});
|
|
2838
|
+
}
|
|
2839
|
+
actions2.push({
|
|
2840
|
+
type: "generate_patch_plan",
|
|
2841
|
+
target: "umi.config",
|
|
2842
|
+
description: "Generate a guided patch plan for the Umi Inspecto webpack integration."
|
|
2843
|
+
});
|
|
2844
|
+
const defaults2 = {
|
|
2845
|
+
shared: false,
|
|
2846
|
+
extension: shouldInstallInspectoExtension(ide2),
|
|
2847
|
+
...provider2 ? { provider: provider2 } : {},
|
|
2848
|
+
...ide2 ? { ide: ide2 } : {}
|
|
2849
|
+
};
|
|
2850
|
+
return {
|
|
2851
|
+
status: "warning",
|
|
2852
|
+
warnings: buildGuidedWarnings(context, "Umi", "react"),
|
|
2853
|
+
blockers: [],
|
|
2854
|
+
strategy: "guided",
|
|
2855
|
+
actions: actions2,
|
|
2856
|
+
defaults: defaults2,
|
|
2857
|
+
framework: guidance.framework,
|
|
2858
|
+
metaFramework: guidance.metaFramework,
|
|
2859
|
+
autoApplied: guidance.autoApplied,
|
|
2860
|
+
pendingSteps: guidance.pendingSteps,
|
|
2861
|
+
assistantPrompt: guidance.assistantPrompt,
|
|
2862
|
+
patches: guidance.patches
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2117
2865
|
const warnings = uniqueMessages([
|
|
2118
2866
|
...unsupportedEnvironmentWarnings(context),
|
|
2119
2867
|
...buildToolWarnings(context)
|
|
@@ -2149,23 +2897,23 @@ function createPlanResult(context) {
|
|
|
2149
2897
|
});
|
|
2150
2898
|
}
|
|
2151
2899
|
const ide2 = supportedIde(context);
|
|
2152
|
-
if (ide2
|
|
2900
|
+
if (shouldInstallInspectoExtension(ide2) && ide2) {
|
|
2153
2901
|
actions.push({
|
|
2154
2902
|
type: "install_extension",
|
|
2155
|
-
target:
|
|
2156
|
-
description:
|
|
2903
|
+
target: ide2,
|
|
2904
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide2)} extension.`
|
|
2157
2905
|
});
|
|
2158
2906
|
}
|
|
2159
2907
|
}
|
|
2908
|
+
const ide = supportedIde(context);
|
|
2160
2909
|
const defaults = {
|
|
2161
2910
|
shared: false,
|
|
2162
|
-
extension:
|
|
2911
|
+
extension: shouldInstallInspectoExtension(ide)
|
|
2163
2912
|
};
|
|
2164
2913
|
const provider = supportedProvider(context);
|
|
2165
2914
|
if (provider) {
|
|
2166
2915
|
defaults.provider = provider;
|
|
2167
2916
|
}
|
|
2168
|
-
const ide = supportedIde(context);
|
|
2169
2917
|
if (ide) {
|
|
2170
2918
|
defaults.ide = ide;
|
|
2171
2919
|
}
|
|
@@ -2179,7 +2927,11 @@ function createPlanResult(context) {
|
|
|
2179
2927
|
};
|
|
2180
2928
|
}
|
|
2181
2929
|
function planManualFollowUp(result) {
|
|
2182
|
-
return result.actions.filter(
|
|
2930
|
+
return result.actions.filter(
|
|
2931
|
+
(action) => ["manual_step", "generate_patch_plan", "generate_file", "manual_confirmation"].includes(
|
|
2932
|
+
action.type
|
|
2933
|
+
)
|
|
2934
|
+
).map((action) => action.description);
|
|
2183
2935
|
}
|
|
2184
2936
|
|
|
2185
2937
|
// src/commands/apply.ts
|
|
@@ -2310,8 +3062,85 @@ async function detect(json = false) {
|
|
|
2310
3062
|
return writeCommandOutput(result, json, printDetectionResult);
|
|
2311
3063
|
}
|
|
2312
3064
|
|
|
3065
|
+
// src/commands/dev-config.ts
|
|
3066
|
+
import path14 from "path";
|
|
3067
|
+
var DEV_CONFIG_PATH = path14.join(".inspecto", "dev.json");
|
|
3068
|
+
function absoluteDevConfigPath(root) {
|
|
3069
|
+
return path14.join(root, DEV_CONFIG_PATH);
|
|
3070
|
+
}
|
|
3071
|
+
function printDevConfigResult(result) {
|
|
3072
|
+
log.header("Inspecto Dev");
|
|
3073
|
+
log.info(`Config: ${result.configPath}`);
|
|
3074
|
+
if (result.config.cliBin) {
|
|
3075
|
+
log.hint(`cliBin: ${result.config.cliBin}`);
|
|
3076
|
+
}
|
|
3077
|
+
if (result.config.devRepo) {
|
|
3078
|
+
log.hint(`devRepo: ${result.config.devRepo}`);
|
|
3079
|
+
}
|
|
3080
|
+
if (!result.config.cliBin && !result.config.devRepo) {
|
|
3081
|
+
log.hint("No local dev overrides are configured.");
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
async function readExistingConfig(root) {
|
|
3085
|
+
const configPath = absoluteDevConfigPath(root);
|
|
3086
|
+
const config = await readJSON(configPath);
|
|
3087
|
+
if (!config || typeof config !== "object") {
|
|
3088
|
+
return {};
|
|
3089
|
+
}
|
|
3090
|
+
return config;
|
|
3091
|
+
}
|
|
3092
|
+
async function devLink(options) {
|
|
3093
|
+
const root = process.cwd();
|
|
3094
|
+
const configPath = absoluteDevConfigPath(root);
|
|
3095
|
+
const existing = await readExistingConfig(root);
|
|
3096
|
+
const nextConfig = {
|
|
3097
|
+
...existing,
|
|
3098
|
+
...options.cliBin ? { cliBin: options.cliBin } : {},
|
|
3099
|
+
...options.devRepo ? { devRepo: options.devRepo } : {}
|
|
3100
|
+
};
|
|
3101
|
+
await writeJSON(configPath, nextConfig);
|
|
3102
|
+
await updateGitignore(root, false, false, true);
|
|
3103
|
+
return writeCommandOutput(
|
|
3104
|
+
{
|
|
3105
|
+
status: "ok",
|
|
3106
|
+
configPath,
|
|
3107
|
+
config: nextConfig
|
|
3108
|
+
},
|
|
3109
|
+
options.json ?? false,
|
|
3110
|
+
printDevConfigResult
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
async function devStatus(json = false) {
|
|
3114
|
+
const root = process.cwd();
|
|
3115
|
+
const configPath = absoluteDevConfigPath(root);
|
|
3116
|
+
const config = await readExistingConfig(root);
|
|
3117
|
+
return writeCommandOutput(
|
|
3118
|
+
{
|
|
3119
|
+
status: "ok",
|
|
3120
|
+
configPath,
|
|
3121
|
+
config
|
|
3122
|
+
},
|
|
3123
|
+
json,
|
|
3124
|
+
printDevConfigResult
|
|
3125
|
+
);
|
|
3126
|
+
}
|
|
3127
|
+
async function devUnlink(json = false) {
|
|
3128
|
+
const root = process.cwd();
|
|
3129
|
+
const configPath = absoluteDevConfigPath(root);
|
|
3130
|
+
await removeFile(configPath);
|
|
3131
|
+
return writeCommandOutput(
|
|
3132
|
+
{
|
|
3133
|
+
status: "ok",
|
|
3134
|
+
configPath,
|
|
3135
|
+
config: {}
|
|
3136
|
+
},
|
|
3137
|
+
json,
|
|
3138
|
+
printDevConfigResult
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
|
|
2313
3142
|
// src/commands/init.ts
|
|
2314
|
-
import
|
|
3143
|
+
import path15 from "path";
|
|
2315
3144
|
|
|
2316
3145
|
// src/onboarding/target-resolution.ts
|
|
2317
3146
|
function buildCandidateId(candidate) {
|
|
@@ -2349,8 +3178,8 @@ function buildCandidates(input) {
|
|
|
2349
3178
|
configPath: buildTool.configPath,
|
|
2350
3179
|
label: buildTool.label,
|
|
2351
3180
|
buildTool: buildTool.tool,
|
|
2352
|
-
isLegacyRspack:
|
|
2353
|
-
isLegacyWebpack:
|
|
3181
|
+
...buildTool.isLegacyRspack ? { isLegacyRspack: true } : {},
|
|
3182
|
+
...buildTool.isLegacyWebpack ? { isLegacyWebpack: true } : {},
|
|
2354
3183
|
frameworks: input.frameworkSupportByPackage[packagePath] ?? [],
|
|
2355
3184
|
automaticInjection: true
|
|
2356
3185
|
};
|
|
@@ -2572,7 +3401,9 @@ async function promptUnsupportedFrameworkContinue() {
|
|
|
2572
3401
|
// src/instructions.ts
|
|
2573
3402
|
function printNuxtManualInstructions() {
|
|
2574
3403
|
log.blank();
|
|
2575
|
-
log.hint(
|
|
3404
|
+
log.hint(
|
|
3405
|
+
"Nuxt supports guided setup in the current version. Inspecto can prepare the config patch, but the client plugin mount step still needs review."
|
|
3406
|
+
);
|
|
2576
3407
|
log.hint("1. Update `nuxt.config.ts` to register the Inspecto Vite plugin:");
|
|
2577
3408
|
log.copyableCodeBlock([
|
|
2578
3409
|
"import { vitePlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
@@ -2583,7 +3414,7 @@ function printNuxtManualInstructions() {
|
|
|
2583
3414
|
" },",
|
|
2584
3415
|
"})"
|
|
2585
3416
|
]);
|
|
2586
|
-
log.hint("2.
|
|
3417
|
+
log.hint("2. Complete the remaining client plugin mount step in `plugins/inspecto.client.ts`:");
|
|
2587
3418
|
log.copyableCodeBlock([
|
|
2588
3419
|
"export default defineNuxtPlugin(() => {",
|
|
2589
3420
|
" if (import.meta.dev) {",
|
|
@@ -2593,11 +3424,31 @@ function printNuxtManualInstructions() {
|
|
|
2593
3424
|
" }",
|
|
2594
3425
|
"})"
|
|
2595
3426
|
]);
|
|
2596
|
-
log.hint("3. Restart your Nuxt dev server after
|
|
3427
|
+
log.hint("3. Restart your Nuxt dev server after applying the guided patches.");
|
|
3428
|
+
}
|
|
3429
|
+
function printUmiManualInstructions() {
|
|
3430
|
+
log.blank();
|
|
3431
|
+
log.hint("Umi supports guided setup in the current version.");
|
|
3432
|
+
log.hint("1. Update `config/config.ts` or `.umirc.ts` to register the Inspecto webpack plugin:");
|
|
3433
|
+
log.copyableCodeBlock([
|
|
3434
|
+
"import { defineConfig } from 'umi'",
|
|
3435
|
+
"import { webpack4Plugin } from '@inspecto-dev/plugin/legacy/webpack4'",
|
|
3436
|
+
"",
|
|
3437
|
+
"export default defineConfig({",
|
|
3438
|
+
" chainWebpack(memo) {",
|
|
3439
|
+
" if (process.env.NODE_ENV === 'development') {",
|
|
3440
|
+
" memo.plugin('inspecto').use(webpack4Plugin())",
|
|
3441
|
+
" }",
|
|
3442
|
+
" },",
|
|
3443
|
+
"})"
|
|
3444
|
+
]);
|
|
3445
|
+
log.hint("2. Restart your Umi dev server after applying the guided patches.");
|
|
2597
3446
|
}
|
|
2598
3447
|
function printNextJsManualInstructions() {
|
|
2599
3448
|
log.blank();
|
|
2600
|
-
log.hint(
|
|
3449
|
+
log.hint(
|
|
3450
|
+
"Next.js supports guided setup in the current version. Inspecto can prepare the config patch, but the client-side mount step still needs review."
|
|
3451
|
+
);
|
|
2601
3452
|
log.hint("1. Update `next.config.mjs` to register the Inspecto webpack plugin:");
|
|
2602
3453
|
log.copyableCodeBlock([
|
|
2603
3454
|
"import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
@@ -2605,7 +3456,7 @@ function printNextJsManualInstructions() {
|
|
|
2605
3456
|
"/** @type {import('next').NextConfig} */",
|
|
2606
3457
|
"const nextConfig = {",
|
|
2607
3458
|
" webpack: (config, { dev, isServer }) => {",
|
|
2608
|
-
" if (dev
|
|
3459
|
+
" if (dev) {",
|
|
2609
3460
|
" config.plugins.push(inspecto())",
|
|
2610
3461
|
" }",
|
|
2611
3462
|
" return config",
|
|
@@ -2615,7 +3466,10 @@ function printNextJsManualInstructions() {
|
|
|
2615
3466
|
"export default nextConfig"
|
|
2616
3467
|
]);
|
|
2617
3468
|
log.hint(
|
|
2618
|
-
"
|
|
3469
|
+
"Keep the plugin enabled for both server and client development compilers so App Router server components also receive Inspecto transforms."
|
|
3470
|
+
);
|
|
3471
|
+
log.hint(
|
|
3472
|
+
"2. Complete the remaining client-side mount step in `app/layout.tsx` or `pages/_app.tsx`:"
|
|
2619
3473
|
);
|
|
2620
3474
|
log.copyableCodeBlock([
|
|
2621
3475
|
"'use client'",
|
|
@@ -2634,7 +3488,7 @@ function printNextJsManualInstructions() {
|
|
|
2634
3488
|
" return <html><body>{children}</body></html>",
|
|
2635
3489
|
"}"
|
|
2636
3490
|
]);
|
|
2637
|
-
log.hint("3. Restart your Next.js dev server after
|
|
3491
|
+
log.hint("3. Restart your Next.js dev server after applying the guided patches.");
|
|
2638
3492
|
}
|
|
2639
3493
|
|
|
2640
3494
|
// src/commands/init.ts
|
|
@@ -2649,7 +3503,7 @@ async function init(options) {
|
|
|
2649
3503
|
verifiedPackages.push(pkg);
|
|
2650
3504
|
continue;
|
|
2651
3505
|
}
|
|
2652
|
-
const absolutePath =
|
|
3506
|
+
const absolutePath = path15.join(repoRoot, pkg);
|
|
2653
3507
|
if (await exists(absolutePath)) {
|
|
2654
3508
|
verifiedPackages.push(pkg);
|
|
2655
3509
|
} else {
|
|
@@ -2662,7 +3516,7 @@ async function init(options) {
|
|
|
2662
3516
|
return;
|
|
2663
3517
|
}
|
|
2664
3518
|
log.header("Inspecto Setup");
|
|
2665
|
-
if (!await exists(
|
|
3519
|
+
if (!await exists(path15.join(repoRoot, "package.json"))) {
|
|
2666
3520
|
log.error("No package.json found in current directory");
|
|
2667
3521
|
log.hint("Run this command from your project root");
|
|
2668
3522
|
return;
|
|
@@ -2697,12 +3551,12 @@ async function init(options) {
|
|
|
2697
3551
|
log.hint("Run `inspecto init` inside the target app, or pass --packages <app-path>.");
|
|
2698
3552
|
return;
|
|
2699
3553
|
}
|
|
2700
|
-
projectRoot =
|
|
3554
|
+
projectRoot = path15.join(repoRoot, selectedPackage);
|
|
2701
3555
|
buildResult = await detectBuildTools(repoRoot, [selectedPackage]);
|
|
2702
3556
|
log.info(`Continuing initialization in ${selectedPackage}`);
|
|
2703
3557
|
} else if (targetResolution.selected?.packagePath) {
|
|
2704
3558
|
const selectedPackage = targetResolution.selected.packagePath;
|
|
2705
|
-
projectRoot =
|
|
3559
|
+
projectRoot = path15.join(repoRoot, selectedPackage);
|
|
2706
3560
|
buildResult = await detectBuildTools(repoRoot, [selectedPackage]);
|
|
2707
3561
|
log.warn(`Monorepo root detected. Using the only candidate app: ${selectedPackage}`);
|
|
2708
3562
|
log.hint("Run `inspecto init` inside that app next time to skip this prompt.");
|
|
@@ -2762,14 +3616,17 @@ async function init(options) {
|
|
|
2762
3616
|
if (buildResult.unsupported.length > 0) {
|
|
2763
3617
|
const names = buildResult.unsupported.join(", ");
|
|
2764
3618
|
manualConfigRequiredFor = buildResult.unsupported[0] || "";
|
|
2765
|
-
log.warn(`Detected ${names} \u2014
|
|
2766
|
-
log.hint("
|
|
3619
|
+
log.warn(`Detected ${names} \u2014 guided onboarding is available in the current version`);
|
|
3620
|
+
log.hint("Inspecto can prepare the remaining patch plan and assistant handoff for this stack.");
|
|
2767
3621
|
if (buildResult.unsupported.includes("Next.js")) {
|
|
2768
3622
|
printNextJsManualInstructions();
|
|
2769
3623
|
}
|
|
2770
3624
|
if (buildResult.unsupported.includes("Nuxt")) {
|
|
2771
3625
|
printNuxtManualInstructions();
|
|
2772
3626
|
}
|
|
3627
|
+
if (buildResult.unsupported.includes("Umi")) {
|
|
3628
|
+
printUmiManualInstructions();
|
|
3629
|
+
}
|
|
2773
3630
|
}
|
|
2774
3631
|
if (buildResult.supported.length === 0 && buildResult.unsupported.length === 0) {
|
|
2775
3632
|
log.warn("No recognized build tool detected");
|
|
@@ -2921,7 +3778,7 @@ async function detectFrameworkSupportByPackage(repoRoot, buildTools) {
|
|
|
2921
3778
|
const supportByPackage = {};
|
|
2922
3779
|
await Promise.all(
|
|
2923
3780
|
packagePaths.map(async (packagePath) => {
|
|
2924
|
-
const result = await detectFrameworks(
|
|
3781
|
+
const result = await detectFrameworks(path15.join(repoRoot, packagePath));
|
|
2925
3782
|
supportByPackage[packagePath] = result.supported;
|
|
2926
3783
|
})
|
|
2927
3784
|
);
|
|
@@ -2929,7 +3786,7 @@ async function detectFrameworkSupportByPackage(repoRoot, buildTools) {
|
|
|
2929
3786
|
}
|
|
2930
3787
|
|
|
2931
3788
|
// src/commands/doctor.ts
|
|
2932
|
-
import
|
|
3789
|
+
import path16 from "path";
|
|
2933
3790
|
function createDiagnostic(code, status, message2, hints = [], details) {
|
|
2934
3791
|
return {
|
|
2935
3792
|
code,
|
|
@@ -2944,6 +3801,94 @@ function doctorStatus(errors, warnings) {
|
|
|
2944
3801
|
if (warnings > 0) return "warning";
|
|
2945
3802
|
return "ok";
|
|
2946
3803
|
}
|
|
3804
|
+
function isGuidedMetaFramework(buildTool) {
|
|
3805
|
+
return buildTool === "Next.js" || buildTool === "Nuxt";
|
|
3806
|
+
}
|
|
3807
|
+
function buildDoctorOnboardingContext(input) {
|
|
3808
|
+
return {
|
|
3809
|
+
root: input.root,
|
|
3810
|
+
packageManager: input.packageManager,
|
|
3811
|
+
buildTools: input.buildTools,
|
|
3812
|
+
frameworks: {
|
|
3813
|
+
supported: input.frameworks.supported,
|
|
3814
|
+
unsupported: input.frameworks.unsupported.map((item) => item.name)
|
|
3815
|
+
},
|
|
3816
|
+
ides: input.ides.detected.map(({ ide, supported }) => ({ ide, supported })),
|
|
3817
|
+
providers: input.providers.detected.map(({ id, label, supported, preferredMode }) => ({
|
|
3818
|
+
id,
|
|
3819
|
+
label,
|
|
3820
|
+
supported,
|
|
3821
|
+
preferredMode
|
|
3822
|
+
}))
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
function isConfigPatchReason(reason) {
|
|
3826
|
+
return reason.startsWith("next_config_") || reason.startsWith("nuxt_config_");
|
|
3827
|
+
}
|
|
3828
|
+
function isNextWebpackDevPatchReason(reason) {
|
|
3829
|
+
return reason === "next_dev_script_requires_webpack";
|
|
3830
|
+
}
|
|
3831
|
+
async function collectGuidedPatchDiagnostics(root, plan2) {
|
|
3832
|
+
if (plan2.strategy !== "guided" || !plan2.patches?.length) {
|
|
3833
|
+
return [];
|
|
3834
|
+
}
|
|
3835
|
+
const diagnostics = [];
|
|
3836
|
+
for (const patch of plan2.patches) {
|
|
3837
|
+
if (isConfigPatchReason(patch.reason)) {
|
|
3838
|
+
const content = await readFile(path16.join(root, patch.path));
|
|
3839
|
+
if (content?.includes("@inspecto-dev/plugin")) {
|
|
3840
|
+
diagnostics.push(
|
|
3841
|
+
createDiagnostic(
|
|
3842
|
+
"guided-config-patch-detected",
|
|
3843
|
+
"ok",
|
|
3844
|
+
`Guided config patch appears to be applied in ${patch.path}`,
|
|
3845
|
+
[],
|
|
3846
|
+
{ path: patch.path, reason: patch.reason }
|
|
3847
|
+
)
|
|
3848
|
+
);
|
|
3849
|
+
} else {
|
|
3850
|
+
diagnostics.push(
|
|
3851
|
+
createDiagnostic(
|
|
3852
|
+
"guided-config-patch-pending",
|
|
3853
|
+
"warning",
|
|
3854
|
+
`Guided config patch still needs review in ${patch.path}`,
|
|
3855
|
+
["Run `inspecto onboard --json` to review the generated patch details."],
|
|
3856
|
+
{ path: patch.path, reason: patch.reason }
|
|
3857
|
+
)
|
|
3858
|
+
);
|
|
3859
|
+
}
|
|
3860
|
+
continue;
|
|
3861
|
+
}
|
|
3862
|
+
if (isNextWebpackDevPatchReason(patch.reason)) {
|
|
3863
|
+
const packageJson = await readJSON(
|
|
3864
|
+
path16.join(root, "package.json")
|
|
3865
|
+
);
|
|
3866
|
+
const devScript = packageJson?.scripts?.dev ?? "";
|
|
3867
|
+
if (/next\s+dev\b/.test(devScript) && /--webpack\b/.test(devScript)) {
|
|
3868
|
+
diagnostics.push(
|
|
3869
|
+
createDiagnostic(
|
|
3870
|
+
"guided-dev-script-configured",
|
|
3871
|
+
"ok",
|
|
3872
|
+
"Next.js dev script is configured for webpack mode",
|
|
3873
|
+
[],
|
|
3874
|
+
{ script: devScript }
|
|
3875
|
+
)
|
|
3876
|
+
);
|
|
3877
|
+
} else {
|
|
3878
|
+
diagnostics.push(
|
|
3879
|
+
createDiagnostic(
|
|
3880
|
+
"guided-dev-script-pending",
|
|
3881
|
+
"warning",
|
|
3882
|
+
"Next.js dev script still needs webpack mode for Inspecto validation",
|
|
3883
|
+
["Update the `dev` script to `next dev --webpack` before browser validation."],
|
|
3884
|
+
{ script: devScript }
|
|
3885
|
+
)
|
|
3886
|
+
);
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
return diagnostics;
|
|
3891
|
+
}
|
|
2947
3892
|
function printDoctorResult(result) {
|
|
2948
3893
|
log.header("Inspecto Doctor");
|
|
2949
3894
|
for (const check of result.checks) {
|
|
@@ -2973,7 +3918,7 @@ function printDoctorResult(result) {
|
|
|
2973
3918
|
}
|
|
2974
3919
|
async function collectDoctorResult(root = process.cwd()) {
|
|
2975
3920
|
const checks = [];
|
|
2976
|
-
if (!await exists(
|
|
3921
|
+
if (!await exists(path16.join(root, "package.json"))) {
|
|
2977
3922
|
const diagnostic = createDiagnostic("missing-package-json", "error", "No package.json found", [
|
|
2978
3923
|
"Run this command from your project root"
|
|
2979
3924
|
]);
|
|
@@ -2995,6 +3940,15 @@ async function collectDoctorResult(root = process.cwd()) {
|
|
|
2995
3940
|
detectBuildTools(root),
|
|
2996
3941
|
isExtensionInstalled()
|
|
2997
3942
|
]);
|
|
3943
|
+
const onboardingContext = buildDoctorOnboardingContext({
|
|
3944
|
+
root,
|
|
3945
|
+
packageManager: pm,
|
|
3946
|
+
buildTools: buildResult,
|
|
3947
|
+
frameworks: frameworkResult,
|
|
3948
|
+
ides: ideProbe,
|
|
3949
|
+
providers: providerProbe
|
|
3950
|
+
});
|
|
3951
|
+
const onboardingPlan = createPlanResult(onboardingContext);
|
|
2998
3952
|
if (ideProbe.detected.length === 0) {
|
|
2999
3953
|
checks.push(createDiagnostic("ide-not-detected", "warning", "IDE: not detected"));
|
|
3000
3954
|
} else {
|
|
@@ -3067,9 +4021,9 @@ async function collectDoctorResult(root = process.cwd()) {
|
|
|
3067
4021
|
}).join(", ");
|
|
3068
4022
|
checks.push(createDiagnostic("provider-detected", "ok", `Provider: ${aiNames}`));
|
|
3069
4023
|
}
|
|
3070
|
-
const pluginPath =
|
|
4024
|
+
const pluginPath = path16.join(root, "node_modules", "@inspecto-dev", "plugin");
|
|
3071
4025
|
if (await exists(pluginPath)) {
|
|
3072
|
-
const pkgJson = await readJSON(
|
|
4026
|
+
const pkgJson = await readJSON(path16.join(pluginPath, "package.json"));
|
|
3073
4027
|
const version = pkgJson?.version ?? "unknown";
|
|
3074
4028
|
checks.push(
|
|
3075
4029
|
createDiagnostic("plugin-installed", "ok", `@inspecto-dev/plugin@${version} installed`, [], {
|
|
@@ -3086,7 +4040,7 @@ async function collectDoctorResult(root = process.cwd()) {
|
|
|
3086
4040
|
if (buildResult.supported.length > 0) {
|
|
3087
4041
|
let injected = false;
|
|
3088
4042
|
for (const bt of buildResult.supported) {
|
|
3089
|
-
const content = await readFile(
|
|
4043
|
+
const content = await readFile(path16.join(root, bt.configPath));
|
|
3090
4044
|
if (content && content.includes("@inspecto-dev/plugin")) {
|
|
3091
4045
|
checks.push(
|
|
3092
4046
|
createDiagnostic("plugin-configured", "ok", `Plugin configured in ${bt.configPath}`, [], {
|
|
@@ -3109,15 +4063,40 @@ async function collectDoctorResult(root = process.cwd()) {
|
|
|
3109
4063
|
);
|
|
3110
4064
|
}
|
|
3111
4065
|
} else if (buildResult.unsupported.length > 0) {
|
|
3112
|
-
const
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
`build-tool-unsupported`,
|
|
3116
|
-
"warning",
|
|
3117
|
-
`Build tool: ${names} (not supported in v1)`,
|
|
3118
|
-
["current version supports: Vite, Webpack, Rspack, esbuild, Rollup"]
|
|
3119
|
-
)
|
|
4066
|
+
const guidedBuildTools = buildResult.unsupported.filter(isGuidedMetaFramework);
|
|
4067
|
+
const trulyUnsupportedBuildTools = buildResult.unsupported.filter(
|
|
4068
|
+
(buildTool) => !isGuidedMetaFramework(buildTool)
|
|
3120
4069
|
);
|
|
4070
|
+
if (guidedBuildTools.length > 0) {
|
|
4071
|
+
checks.push(
|
|
4072
|
+
createDiagnostic(
|
|
4073
|
+
"build-tool-guided",
|
|
4074
|
+
"warning",
|
|
4075
|
+
`Build tool: ${guidedBuildTools.join(", ")} (guided onboarding available)`,
|
|
4076
|
+
[
|
|
4077
|
+
"Run `inspecto onboard --json` to generate the remaining patch plan and assistant handoff.",
|
|
4078
|
+
...onboardingPlan.pendingSteps ?? []
|
|
4079
|
+
],
|
|
4080
|
+
{
|
|
4081
|
+
metaFrameworks: guidedBuildTools,
|
|
4082
|
+
...onboardingPlan.pendingSteps ? { pendingSteps: onboardingPlan.pendingSteps } : {},
|
|
4083
|
+
...onboardingPlan.assistantPrompt ? { assistantPrompt: onboardingPlan.assistantPrompt } : {},
|
|
4084
|
+
...onboardingPlan.patches ? { patchCount: onboardingPlan.patches.length } : {}
|
|
4085
|
+
}
|
|
4086
|
+
)
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
4089
|
+
checks.push(...await collectGuidedPatchDiagnostics(root, onboardingPlan));
|
|
4090
|
+
if (trulyUnsupportedBuildTools.length > 0) {
|
|
4091
|
+
checks.push(
|
|
4092
|
+
createDiagnostic(
|
|
4093
|
+
"build-tool-unsupported",
|
|
4094
|
+
"warning",
|
|
4095
|
+
`Build tool: ${trulyUnsupportedBuildTools.join(", ")} (not supported in v1)`,
|
|
4096
|
+
["current version supports: Vite, Webpack, Rspack, esbuild, Rollup"]
|
|
4097
|
+
)
|
|
4098
|
+
);
|
|
4099
|
+
}
|
|
3121
4100
|
} else {
|
|
3122
4101
|
checks.push(
|
|
3123
4102
|
createDiagnostic("build-tool-missing", "warning", "No recognized build config found")
|
|
@@ -3145,8 +4124,8 @@ async function collectDoctorResult(root = process.cwd()) {
|
|
|
3145
4124
|
);
|
|
3146
4125
|
}
|
|
3147
4126
|
}
|
|
3148
|
-
const settingsJsonPath =
|
|
3149
|
-
const settingsLocalPath =
|
|
4127
|
+
const settingsJsonPath = path16.join(root, ".inspecto", "settings.json");
|
|
4128
|
+
const settingsLocalPath = path16.join(root, ".inspecto", "settings.local.json");
|
|
3150
4129
|
const hasSettingsJson = await exists(settingsJsonPath);
|
|
3151
4130
|
const hasSettingsLocal = await exists(settingsLocalPath);
|
|
3152
4131
|
if (hasSettingsJson || hasSettingsLocal) {
|
|
@@ -3199,7 +4178,7 @@ async function collectDoctorResult(root = process.cwd()) {
|
|
|
3199
4178
|
)
|
|
3200
4179
|
);
|
|
3201
4180
|
}
|
|
3202
|
-
const gitignoreContent = await readFile(
|
|
4181
|
+
const gitignoreContent = await readFile(path16.join(root, ".gitignore"));
|
|
3203
4182
|
if (gitignoreContent) {
|
|
3204
4183
|
const hasLockIgnore = gitignoreContent.includes(".inspecto/install.lock") || gitignoreContent.includes(".inspecto/");
|
|
3205
4184
|
if (!hasLockIgnore) {
|
|
@@ -3237,7 +4216,7 @@ async function doctor(options = {}) {
|
|
|
3237
4216
|
}
|
|
3238
4217
|
|
|
3239
4218
|
// src/onboarding/session.ts
|
|
3240
|
-
import
|
|
4219
|
+
import path17 from "path";
|
|
3241
4220
|
function normalizePackagePath2(packagePath) {
|
|
3242
4221
|
if (!packagePath || packagePath === ".") return "";
|
|
3243
4222
|
return packagePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
@@ -3261,9 +4240,7 @@ function getVerificationCommand(packageManager) {
|
|
|
3261
4240
|
}
|
|
3262
4241
|
}
|
|
3263
4242
|
async function buildVerification(projectRoot, packageManager) {
|
|
3264
|
-
const packageJson = await readJSON(
|
|
3265
|
-
path13.join(projectRoot, "package.json")
|
|
3266
|
-
);
|
|
4243
|
+
const packageJson = await readJSON(path17.join(projectRoot, "package.json"));
|
|
3267
4244
|
if (packageJson?.scripts?.dev) {
|
|
3268
4245
|
const devCommand = getVerificationCommand(packageManager);
|
|
3269
4246
|
return {
|
|
@@ -3272,11 +4249,28 @@ async function buildVerification(projectRoot, packageManager) {
|
|
|
3272
4249
|
message: `Start the local dev server with \`${devCommand}\` to verify Inspecto in the browser.`
|
|
3273
4250
|
};
|
|
3274
4251
|
}
|
|
4252
|
+
if (packageJson?.scripts?.start && packageJson?.dependencies?.next) {
|
|
4253
|
+
const devCommand = packageManager === "bun" ? "bunx next dev" : "npx next dev";
|
|
4254
|
+
return {
|
|
4255
|
+
available: true,
|
|
4256
|
+
devCommand,
|
|
4257
|
+
message: `Start the local dev server with \`${devCommand}\` to verify Inspecto in the browser.`
|
|
4258
|
+
};
|
|
4259
|
+
}
|
|
3275
4260
|
return {
|
|
3276
4261
|
available: false,
|
|
3277
4262
|
message: "Start your normal local dev server command to verify Inspecto in the browser."
|
|
3278
4263
|
};
|
|
3279
4264
|
}
|
|
4265
|
+
function buildExtensionInstallCommand(ide) {
|
|
4266
|
+
if (ide && isSupportedHostIde(ide)) {
|
|
4267
|
+
const binaryName = getHostIdeBinaryName(ide);
|
|
4268
|
+
if (binaryName) {
|
|
4269
|
+
return `${binaryName} --install-extension inspecto.inspecto`;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
return "code --install-extension inspecto.inspecto";
|
|
4273
|
+
}
|
|
3280
4274
|
function buildIdeExtensionStatus(input) {
|
|
3281
4275
|
if (!input.required) {
|
|
3282
4276
|
return {
|
|
@@ -3289,8 +4283,10 @@ function buildIdeExtensionStatus(input) {
|
|
|
3289
4283
|
required: true,
|
|
3290
4284
|
installed: input.installed,
|
|
3291
4285
|
manualRequired: input.manualRequired,
|
|
3292
|
-
installCommand:
|
|
3293
|
-
|
|
4286
|
+
installCommand: buildExtensionInstallCommand(input.ide),
|
|
4287
|
+
...input.ide === "vscode" ? {
|
|
4288
|
+
marketplaceUrl: "https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto"
|
|
4289
|
+
} : {},
|
|
3294
4290
|
openVsxUrl: "https://open-vsx.org/extension/inspecto/inspecto"
|
|
3295
4291
|
};
|
|
3296
4292
|
}
|
|
@@ -3302,7 +4298,7 @@ async function detectFrameworkSupportByPackage2(repoRoot, context) {
|
|
|
3302
4298
|
await Promise.all(
|
|
3303
4299
|
Array.from(packagePaths).map(async (packagePath) => {
|
|
3304
4300
|
const frameworkResult = await detectFrameworks(
|
|
3305
|
-
packagePath ?
|
|
4301
|
+
packagePath ? path17.join(repoRoot, packagePath) : repoRoot
|
|
3306
4302
|
);
|
|
3307
4303
|
supportByPackage[packagePath] = frameworkResult.supported;
|
|
3308
4304
|
})
|
|
@@ -3311,7 +4307,7 @@ async function detectFrameworkSupportByPackage2(repoRoot, context) {
|
|
|
3311
4307
|
}
|
|
3312
4308
|
async function buildTargetedContext(rootContext, target) {
|
|
3313
4309
|
const packagePath = normalizePackagePath2(target.packagePath);
|
|
3314
|
-
const projectRoot = packagePath ?
|
|
4310
|
+
const projectRoot = packagePath ? path17.join(rootContext.root, packagePath) : rootContext.root;
|
|
3315
4311
|
const [frameworks, ides, providers] = await Promise.all([
|
|
3316
4312
|
detectFrameworks(projectRoot),
|
|
3317
4313
|
detectIDE(projectRoot),
|
|
@@ -3344,7 +4340,11 @@ async function buildTargetedContext(rootContext, target) {
|
|
|
3344
4340
|
};
|
|
3345
4341
|
}
|
|
3346
4342
|
function buildOnboardingSummary(plan2, projectRoot) {
|
|
3347
|
-
const changes = plan2.actions.filter(
|
|
4343
|
+
const changes = plan2.actions.filter(
|
|
4344
|
+
(action) => !["manual_step", "generate_patch_plan", "generate_file", "manual_confirmation"].includes(
|
|
4345
|
+
action.type
|
|
4346
|
+
)
|
|
4347
|
+
).map((action) => action.description);
|
|
3348
4348
|
const risks = [...plan2.warnings.map((item) => item.message)];
|
|
3349
4349
|
const manualFollowUp = planManualFollowUp(plan2);
|
|
3350
4350
|
let headline = `Inspecto is ready to onboard ${projectRoot}.`;
|
|
@@ -3388,18 +4388,27 @@ function buildPreApplyResult(status, session) {
|
|
|
3388
4388
|
errors: session.plan.blockers.map((item) => item.message),
|
|
3389
4389
|
nextSteps: session.summary.manualFollowUp
|
|
3390
4390
|
} : void 0;
|
|
4391
|
+
const ideExtension = buildIdeExtensionStatus({
|
|
4392
|
+
...session.selectedIDE?.ide ? { ide: session.selectedIDE.ide } : {},
|
|
4393
|
+
required: session.plan.defaults.extension,
|
|
4394
|
+
installed: false,
|
|
4395
|
+
manualRequired: session.plan.defaults.extension
|
|
4396
|
+
});
|
|
3391
4397
|
return {
|
|
3392
4398
|
status,
|
|
3393
4399
|
target: session.target,
|
|
3394
4400
|
summary: session.summary,
|
|
3395
4401
|
confirmation: session.confirmation,
|
|
3396
|
-
ideExtension
|
|
3397
|
-
required: session.plan.defaults.extension,
|
|
3398
|
-
installed: false,
|
|
3399
|
-
manualRequired: session.plan.defaults.extension
|
|
3400
|
-
}),
|
|
4402
|
+
ideExtension,
|
|
3401
4403
|
verification: session.verification,
|
|
3402
|
-
|
|
4404
|
+
...session.framework ? { framework: session.framework } : {},
|
|
4405
|
+
...session.metaFramework ? { metaFramework: session.metaFramework } : {},
|
|
4406
|
+
...session.routerMode ? { routerMode: session.routerMode } : {},
|
|
4407
|
+
...session.autoApplied ? { autoApplied: session.autoApplied } : {},
|
|
4408
|
+
...session.pendingSteps ? { pendingSteps: session.pendingSteps } : {},
|
|
4409
|
+
...session.assistantPrompt ? { assistantPrompt: session.assistantPrompt } : {},
|
|
4410
|
+
...session.patches ? { patches: session.patches } : {},
|
|
4411
|
+
...diagnostics ? { diagnostics } : {}
|
|
3403
4412
|
};
|
|
3404
4413
|
}
|
|
3405
4414
|
function buildExecutionResult(session, applyResult) {
|
|
@@ -3410,8 +4419,8 @@ function buildExecutionResult(session, applyResult) {
|
|
|
3410
4419
|
)
|
|
3411
4420
|
),
|
|
3412
4421
|
installedDependencies: applyResult.mutations.map((item) => item.name).filter((value) => !!value),
|
|
3413
|
-
selectedProviderDefault: session.providerDefault,
|
|
3414
|
-
selectedIDE: session.selectedIDE
|
|
4422
|
+
...session.providerDefault ? { selectedProviderDefault: session.providerDefault } : {},
|
|
4423
|
+
...session.selectedIDE?.ide ? { selectedIDE: session.selectedIDE.ide } : {},
|
|
3415
4424
|
mutations: applyResult.mutations
|
|
3416
4425
|
};
|
|
3417
4426
|
}
|
|
@@ -3444,19 +4453,35 @@ async function resolveOnboardingSession(root, options = {}) {
|
|
|
3444
4453
|
repoRoot: root,
|
|
3445
4454
|
buildTools: rootContext.buildTools.supported,
|
|
3446
4455
|
frameworkSupportByPackage,
|
|
3447
|
-
selectedPackagePath: options.target
|
|
4456
|
+
...options.target ? { selectedPackagePath: options.target } : {}
|
|
3448
4457
|
});
|
|
3449
|
-
|
|
4458
|
+
const isGuided = rootContext.buildTools.unsupported.some(
|
|
4459
|
+
(t) => ["Next.js", "Nuxt", "Umi"].includes(t)
|
|
4460
|
+
);
|
|
4461
|
+
if (target.candidates.length === 0 || isGuided) {
|
|
3450
4462
|
const plan3 = createPlanResult(rootContext);
|
|
4463
|
+
const guidedStatus = plan3.strategy === "guided" ? "partial_success" : "error";
|
|
4464
|
+
const resolvedTarget = plan3.strategy === "guided" ? {
|
|
4465
|
+
status: "guided",
|
|
4466
|
+
candidates: [],
|
|
4467
|
+
reason: `Guided onboarding is available for ${plan3.metaFramework ?? "this project"}.`
|
|
4468
|
+
} : target;
|
|
3451
4469
|
return {
|
|
3452
|
-
status:
|
|
3453
|
-
target,
|
|
4470
|
+
status: guidedStatus,
|
|
4471
|
+
target: resolvedTarget,
|
|
3454
4472
|
summary: buildOnboardingSummary(plan3, root),
|
|
3455
4473
|
confirmation: { required: false },
|
|
3456
4474
|
verification: rootVerification,
|
|
3457
4475
|
context: rootContext,
|
|
3458
4476
|
plan: plan3,
|
|
3459
|
-
projectRoot: root
|
|
4477
|
+
projectRoot: root,
|
|
4478
|
+
...plan3.framework ? { framework: plan3.framework } : {},
|
|
4479
|
+
...plan3.metaFramework ? { metaFramework: plan3.metaFramework } : {},
|
|
4480
|
+
...plan3.routerMode ? { routerMode: plan3.routerMode } : {},
|
|
4481
|
+
...plan3.autoApplied ? { autoApplied: plan3.autoApplied } : {},
|
|
4482
|
+
...plan3.pendingSteps ? { pendingSteps: plan3.pendingSteps } : {},
|
|
4483
|
+
...plan3.assistantPrompt ? { assistantPrompt: plan3.assistantPrompt } : {},
|
|
4484
|
+
...plan3.patches ? { patches: plan3.patches } : {}
|
|
3460
4485
|
};
|
|
3461
4486
|
}
|
|
3462
4487
|
if (target.status === "needs_selection") {
|
|
@@ -3502,6 +4527,10 @@ async function resolveOnboardingSession(root, options = {}) {
|
|
|
3502
4527
|
} else if (summary.manualFollowUp.length > 0 || plan2.warnings.length > 0) {
|
|
3503
4528
|
status = "partial_success";
|
|
3504
4529
|
}
|
|
4530
|
+
const providerDefault = getProviderDefault2(
|
|
4531
|
+
plan2.defaults.provider,
|
|
4532
|
+
selectedProvider?.preferredMode
|
|
4533
|
+
);
|
|
3505
4534
|
return {
|
|
3506
4535
|
status,
|
|
3507
4536
|
target,
|
|
@@ -3512,7 +4541,14 @@ async function resolveOnboardingSession(root, options = {}) {
|
|
|
3512
4541
|
plan: plan2,
|
|
3513
4542
|
projectRoot: context.root,
|
|
3514
4543
|
selectedIDE,
|
|
3515
|
-
providerDefault:
|
|
4544
|
+
...providerDefault ? { providerDefault } : {},
|
|
4545
|
+
...plan2.framework ? { framework: plan2.framework } : {},
|
|
4546
|
+
...plan2.metaFramework ? { metaFramework: plan2.metaFramework } : {},
|
|
4547
|
+
...plan2.routerMode ? { routerMode: plan2.routerMode } : {},
|
|
4548
|
+
...plan2.autoApplied ? { autoApplied: plan2.autoApplied } : {},
|
|
4549
|
+
...plan2.pendingSteps ? { pendingSteps: plan2.pendingSteps } : {},
|
|
4550
|
+
...plan2.assistantPrompt ? { assistantPrompt: plan2.assistantPrompt } : {},
|
|
4551
|
+
...plan2.patches ? { patches: plan2.patches } : {}
|
|
3516
4552
|
};
|
|
3517
4553
|
}
|
|
3518
4554
|
async function applyResolvedOnboardingSession(session, options = {}) {
|
|
@@ -3532,23 +4568,32 @@ async function applyResolvedOnboardingSession(session, options = {}) {
|
|
|
3532
4568
|
selectedIDE: session.selectedIDE,
|
|
3533
4569
|
providerDefault: session.providerDefault,
|
|
3534
4570
|
plan: session.plan,
|
|
3535
|
-
allowManualPlanApply: session.plan.strategy === "manual" && session.plan.blockers.length === 0
|
|
4571
|
+
allowManualPlanApply: (session.plan.strategy === "manual" || session.plan.strategy === "guided") && session.plan.blockers.length === 0
|
|
3536
4572
|
});
|
|
3537
4573
|
const diagnostics = buildExecutionDiagnostics(session, applyResult);
|
|
3538
4574
|
const status = applyResult.postInstall.installFailed && session.context.buildTools.supported.length === 0 ? "error" : diagnostics?.nextSteps.length || diagnostics?.errors.length || diagnostics?.warnings.length ? "partial_success" : "success";
|
|
4575
|
+
const ideExtension = buildIdeExtensionStatus({
|
|
4576
|
+
...session.selectedIDE?.ide ? { ide: session.selectedIDE.ide } : {},
|
|
4577
|
+
required: session.plan.defaults.extension,
|
|
4578
|
+
installed: session.plan.defaults.extension && !applyResult.postInstall.manualExtensionInstallNeeded,
|
|
4579
|
+
manualRequired: applyResult.postInstall.manualExtensionInstallNeeded
|
|
4580
|
+
});
|
|
3539
4581
|
return {
|
|
3540
4582
|
status,
|
|
3541
4583
|
target: session.target,
|
|
3542
4584
|
summary: session.summary,
|
|
3543
4585
|
confirmation: session.confirmation,
|
|
3544
|
-
ideExtension
|
|
3545
|
-
required: session.plan.defaults.extension,
|
|
3546
|
-
installed: session.plan.defaults.extension && !applyResult.postInstall.manualExtensionInstallNeeded,
|
|
3547
|
-
manualRequired: applyResult.postInstall.manualExtensionInstallNeeded
|
|
3548
|
-
}),
|
|
4586
|
+
ideExtension,
|
|
3549
4587
|
verification,
|
|
3550
4588
|
result: buildExecutionResult(session, applyResult),
|
|
3551
|
-
|
|
4589
|
+
...session.framework ? { framework: session.framework } : {},
|
|
4590
|
+
...session.metaFramework ? { metaFramework: session.metaFramework } : {},
|
|
4591
|
+
...session.routerMode ? { routerMode: session.routerMode } : {},
|
|
4592
|
+
...session.autoApplied ? { autoApplied: session.autoApplied } : {},
|
|
4593
|
+
...session.pendingSteps ? { pendingSteps: session.pendingSteps } : {},
|
|
4594
|
+
...session.assistantPrompt ? { assistantPrompt: session.assistantPrompt } : {},
|
|
4595
|
+
...session.patches ? { patches: session.patches } : {},
|
|
4596
|
+
...diagnostics ? { diagnostics } : {}
|
|
3552
4597
|
};
|
|
3553
4598
|
}
|
|
3554
4599
|
function buildDeferredOnboardResult(session) {
|
|
@@ -3571,36 +4616,72 @@ function printManualExtensionGuidance(result) {
|
|
|
3571
4616
|
log.hint(result.ideExtension.openVsxUrl);
|
|
3572
4617
|
}
|
|
3573
4618
|
}
|
|
4619
|
+
function buildAssistantHandoff(result) {
|
|
4620
|
+
if (!result.framework && !result.metaFramework && !result.routerMode && !result.autoApplied && !result.pendingSteps && !result.assistantPrompt && !result.patches) {
|
|
4621
|
+
return result.handoff;
|
|
4622
|
+
}
|
|
4623
|
+
return {
|
|
4624
|
+
...result.framework ? { framework: result.framework } : {},
|
|
4625
|
+
...result.metaFramework ? { metaFramework: result.metaFramework } : {},
|
|
4626
|
+
...result.routerMode ? { routerMode: result.routerMode } : {},
|
|
4627
|
+
...result.autoApplied ? { autoApplied: result.autoApplied } : {},
|
|
4628
|
+
...result.pendingSteps ? { pendingSteps: result.pendingSteps } : {},
|
|
4629
|
+
...result.assistantPrompt ? { assistantPrompt: result.assistantPrompt } : {},
|
|
4630
|
+
...result.patches ? { patches: result.patches } : {}
|
|
4631
|
+
};
|
|
4632
|
+
}
|
|
4633
|
+
function normalizeOnboardResult(result) {
|
|
4634
|
+
const handoff = buildAssistantHandoff(result);
|
|
4635
|
+
if (!handoff) {
|
|
4636
|
+
return result;
|
|
4637
|
+
}
|
|
4638
|
+
return {
|
|
4639
|
+
...result,
|
|
4640
|
+
handoff
|
|
4641
|
+
};
|
|
4642
|
+
}
|
|
4643
|
+
function collectDisplayNextSteps(result) {
|
|
4644
|
+
return Array.from(
|
|
4645
|
+
/* @__PURE__ */ new Set([...result.diagnostics?.nextSteps ?? [], ...result.handoff?.pendingSteps ?? []])
|
|
4646
|
+
);
|
|
4647
|
+
}
|
|
3574
4648
|
function printOnboardResult(result) {
|
|
4649
|
+
const normalized = normalizeOnboardResult(result);
|
|
3575
4650
|
log.header("Inspecto Onboard");
|
|
3576
|
-
log.info(`Status: ${
|
|
3577
|
-
log.info(
|
|
3578
|
-
if (
|
|
3579
|
-
if (
|
|
3580
|
-
log.warn(
|
|
4651
|
+
log.info(`Status: ${normalized.status}`);
|
|
4652
|
+
log.info(normalized.summary.headline);
|
|
4653
|
+
if (normalized.status === "needs_target_selection") {
|
|
4654
|
+
if (normalized.target.selectionPurpose) {
|
|
4655
|
+
log.warn(normalized.target.selectionPurpose);
|
|
3581
4656
|
}
|
|
3582
|
-
for (const candidate of
|
|
4657
|
+
for (const candidate of normalized.target.candidates) {
|
|
3583
4658
|
const identifier = candidate.candidateId ?? candidate.id ?? candidate.configPath;
|
|
3584
4659
|
const label = candidate.label ?? candidate.configPath;
|
|
3585
4660
|
log.hint(`${identifier}: ${label}`);
|
|
3586
4661
|
}
|
|
3587
|
-
if (
|
|
3588
|
-
log.hint(
|
|
4662
|
+
if (normalized.target.selectionInstructions) {
|
|
4663
|
+
log.hint(normalized.target.selectionInstructions);
|
|
3589
4664
|
}
|
|
3590
4665
|
}
|
|
3591
|
-
for (const change of
|
|
4666
|
+
for (const change of normalized.summary.changes) {
|
|
3592
4667
|
log.hint(change);
|
|
3593
4668
|
}
|
|
3594
|
-
for (const step of
|
|
4669
|
+
for (const step of collectDisplayNextSteps(normalized)) {
|
|
3595
4670
|
log.warn(step);
|
|
3596
4671
|
}
|
|
3597
|
-
|
|
3598
|
-
log.
|
|
4672
|
+
for (const patch of normalized.handoff?.patches ?? []) {
|
|
4673
|
+
log.hint(`Patch target: ${patch.path} (${patch.reason})`);
|
|
4674
|
+
}
|
|
4675
|
+
if (normalized.handoff?.assistantPrompt) {
|
|
4676
|
+
log.hint(normalized.handoff.assistantPrompt);
|
|
3599
4677
|
}
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
4678
|
+
if (normalized.confirmation.required && normalized.confirmation.question) {
|
|
4679
|
+
log.warn(normalized.confirmation.question);
|
|
4680
|
+
}
|
|
4681
|
+
printManualExtensionGuidance(normalized);
|
|
4682
|
+
const extensionReady = !normalized.ideExtension?.required || normalized.ideExtension.installed && !normalized.ideExtension.manualRequired;
|
|
4683
|
+
if (extensionReady && (normalized.status === "success" || normalized.status === "partial_success") && normalized.verification?.message) {
|
|
4684
|
+
log.info(normalized.verification.message);
|
|
3604
4685
|
}
|
|
3605
4686
|
}
|
|
3606
4687
|
async function onboard(options = {}) {
|
|
@@ -3608,12 +4689,12 @@ async function onboard(options = {}) {
|
|
|
3608
4689
|
const session = await resolveOnboardingSession(root, options);
|
|
3609
4690
|
if (session.status === "error" || session.status === "needs_target_selection" || session.status === "needs_confirmation") {
|
|
3610
4691
|
return writeCommandOutput(
|
|
3611
|
-
buildDeferredOnboardResult(session),
|
|
4692
|
+
normalizeOnboardResult(buildDeferredOnboardResult(session)),
|
|
3612
4693
|
options.json ?? false,
|
|
3613
4694
|
printOnboardResult
|
|
3614
4695
|
);
|
|
3615
4696
|
}
|
|
3616
|
-
const result = await applyResolvedOnboardingSession(session, options);
|
|
4697
|
+
const result = normalizeOnboardResult(await applyResolvedOnboardingSession(session, options));
|
|
3617
4698
|
return writeCommandOutput(result, options.json ?? false, printOnboardResult);
|
|
3618
4699
|
}
|
|
3619
4700
|
|
|
@@ -3651,11 +4732,11 @@ async function plan(json = false) {
|
|
|
3651
4732
|
}
|
|
3652
4733
|
|
|
3653
4734
|
// src/commands/teardown.ts
|
|
3654
|
-
import
|
|
4735
|
+
import path18 from "path";
|
|
3655
4736
|
async function teardown() {
|
|
3656
4737
|
const root = process.cwd();
|
|
3657
4738
|
log.header("Inspecto Teardown");
|
|
3658
|
-
const lockPath =
|
|
4739
|
+
const lockPath = path18.join(root, ".inspecto", "install.lock");
|
|
3659
4740
|
const lock = await readJSON(lockPath);
|
|
3660
4741
|
if (!lock) {
|
|
3661
4742
|
log.warn("No .inspecto/install.lock found. Running in best-effort mode.");
|
|
@@ -3668,8 +4749,8 @@ async function teardown() {
|
|
|
3668
4749
|
} catch {
|
|
3669
4750
|
log.warn("Could not remove @inspecto-dev/plugin (may not be installed)");
|
|
3670
4751
|
}
|
|
3671
|
-
if (await exists(
|
|
3672
|
-
await removeDir(
|
|
4752
|
+
if (await exists(path18.join(root, ".inspecto"))) {
|
|
4753
|
+
await removeDir(path18.join(root, ".inspecto"));
|
|
3673
4754
|
log.success("Deleted .inspecto/ directory");
|
|
3674
4755
|
}
|
|
3675
4756
|
await cleanGitignore(root);
|
|
@@ -3718,7 +4799,7 @@ async function teardown() {
|
|
|
3718
4799
|
}
|
|
3719
4800
|
}
|
|
3720
4801
|
}
|
|
3721
|
-
await removeDir(
|
|
4802
|
+
await removeDir(path18.join(root, ".inspecto"));
|
|
3722
4803
|
log.success("Deleted .inspecto/ directory");
|
|
3723
4804
|
await cleanGitignore(root);
|
|
3724
4805
|
log.blank();
|
|
@@ -3727,13 +4808,13 @@ async function teardown() {
|
|
|
3727
4808
|
}
|
|
3728
4809
|
|
|
3729
4810
|
// src/commands/integration-install.ts
|
|
3730
|
-
import
|
|
4811
|
+
import fs6 from "fs/promises";
|
|
3731
4812
|
import { homedir as homedir2 } from "os";
|
|
3732
|
-
import
|
|
4813
|
+
import path21 from "path";
|
|
3733
4814
|
import { fileURLToPath } from "url";
|
|
3734
4815
|
|
|
3735
4816
|
// src/commands/integration-host-ide.ts
|
|
3736
|
-
import
|
|
4817
|
+
import path19 from "path";
|
|
3737
4818
|
async function resolveIntegrationHostIde(options = {}) {
|
|
3738
4819
|
if (isSupportedHostIde(options.explicitIde)) {
|
|
3739
4820
|
return {
|
|
@@ -3770,22 +4851,24 @@ async function resolveIntegrationHostIde(options = {}) {
|
|
|
3770
4851
|
candidates: envCandidates
|
|
3771
4852
|
};
|
|
3772
4853
|
}
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
4854
|
+
if (!options.ignoreProjectArtifacts) {
|
|
4855
|
+
const artifactCandidates = await detectArtifactHostIdes(cwd);
|
|
4856
|
+
if (artifactCandidates.length === 1) {
|
|
4857
|
+
return {
|
|
4858
|
+
ide: artifactCandidates[0],
|
|
4859
|
+
confidence: "medium",
|
|
4860
|
+
source: "artifact",
|
|
4861
|
+
candidates: artifactCandidates
|
|
4862
|
+
};
|
|
4863
|
+
}
|
|
4864
|
+
if (artifactCandidates.length > 1) {
|
|
4865
|
+
return {
|
|
4866
|
+
ide: null,
|
|
4867
|
+
confidence: "low",
|
|
4868
|
+
source: "ambiguous",
|
|
4869
|
+
candidates: artifactCandidates
|
|
4870
|
+
};
|
|
4871
|
+
}
|
|
3789
4872
|
}
|
|
3790
4873
|
return {
|
|
3791
4874
|
ide: null,
|
|
@@ -3796,8 +4879,8 @@ async function resolveIntegrationHostIde(options = {}) {
|
|
|
3796
4879
|
}
|
|
3797
4880
|
async function resolveConfiguredIde(cwd) {
|
|
3798
4881
|
const settingsPaths = [
|
|
3799
|
-
|
|
3800
|
-
|
|
4882
|
+
path19.join(cwd, ".inspecto", "settings.local.json"),
|
|
4883
|
+
path19.join(cwd, ".inspecto", "settings.json")
|
|
3801
4884
|
];
|
|
3802
4885
|
for (const settingsPath of settingsPaths) {
|
|
3803
4886
|
const settings = await readJSON(settingsPath);
|
|
@@ -3837,7 +4920,7 @@ async function detectArtifactHostIdes(cwd) {
|
|
|
3837
4920
|
|
|
3838
4921
|
// src/commands/integration-dispatch-mode.ts
|
|
3839
4922
|
import { homedir } from "os";
|
|
3840
|
-
import
|
|
4923
|
+
import path20 from "path";
|
|
3841
4924
|
async function resolveIntegrationDispatchMode(options) {
|
|
3842
4925
|
const assistantRule = getDualModeAssistantCapability(options.assistant);
|
|
3843
4926
|
const home = options.homeDir ?? homedir();
|
|
@@ -3880,7 +4963,7 @@ async function isIdeExtensionInstalled(extensionId, extensionsDir) {
|
|
|
3880
4963
|
} catch {
|
|
3881
4964
|
return false;
|
|
3882
4965
|
}
|
|
3883
|
-
const obsoletePath =
|
|
4966
|
+
const obsoletePath = path20.join(extensionsDir, ".obsolete");
|
|
3884
4967
|
let obsoleteFolders = /* @__PURE__ */ new Set();
|
|
3885
4968
|
if (await exists(obsoletePath)) {
|
|
3886
4969
|
const obsolete = await readJSON(obsoletePath);
|
|
@@ -3919,7 +5002,8 @@ async function runIntegrationAutomation(assistant, options = {}, cwd) {
|
|
|
3919
5002
|
const silent = options.silent ?? false;
|
|
3920
5003
|
const resolvedHostIde = await resolveIntegrationHostIde({
|
|
3921
5004
|
...options.ide ? { explicitIde: options.ide } : {},
|
|
3922
|
-
...cwd ? { cwd } : {}
|
|
5005
|
+
...cwd ? { cwd } : {},
|
|
5006
|
+
...options.ignoreProjectArtifacts ? { ignoreProjectArtifacts: true } : {}
|
|
3923
5007
|
});
|
|
3924
5008
|
const details = {
|
|
3925
5009
|
hostIde: {
|
|
@@ -4332,7 +5416,7 @@ async function installIntegration(assistant, options = {}) {
|
|
|
4332
5416
|
if (await exists(asset.target)) {
|
|
4333
5417
|
if (options.force) {
|
|
4334
5418
|
} else if (manifest.type === "context-template") {
|
|
4335
|
-
const originalContent = await
|
|
5419
|
+
const originalContent = await fs6.readFile(asset.target, "utf-8");
|
|
4336
5420
|
existingFiles.set(asset.target, originalContent);
|
|
4337
5421
|
if (!silent) {
|
|
4338
5422
|
log.info(`File ${asset.target} already exists. Content will be appended safely.`);
|
|
@@ -4369,7 +5453,7 @@ ${content}`;
|
|
|
4369
5453
|
for (const { asset, content } of downloadedAssets) {
|
|
4370
5454
|
await writeFile(asset.target, content);
|
|
4371
5455
|
if (asset.executable) {
|
|
4372
|
-
await
|
|
5456
|
+
await fs6.chmod(asset.target, 493);
|
|
4373
5457
|
}
|
|
4374
5458
|
}
|
|
4375
5459
|
}
|
|
@@ -4421,7 +5505,7 @@ ${content}`;
|
|
|
4421
5505
|
}
|
|
4422
5506
|
const automationResult = await runIntegrationAutomation(
|
|
4423
5507
|
assistant,
|
|
4424
|
-
{ ...options, silent },
|
|
5508
|
+
{ ...options, silent, ignoreProjectArtifacts: true },
|
|
4425
5509
|
process.cwd()
|
|
4426
5510
|
);
|
|
4427
5511
|
const result = {
|
|
@@ -4484,7 +5568,7 @@ async function resolveProviderDefaultForAssistant(assistant, ide) {
|
|
|
4484
5568
|
return `${assistant}.${mode}`;
|
|
4485
5569
|
}
|
|
4486
5570
|
async function persistProjectOnboardingDefaults(assistant, options) {
|
|
4487
|
-
const settingsPath =
|
|
5571
|
+
const settingsPath = path21.join(process.cwd(), ".inspecto", "settings.local.json");
|
|
4488
5572
|
const existingSettings = await readJSON(settingsPath);
|
|
4489
5573
|
const resolvedHostIde = await resolveIntegrationHostIde({
|
|
4490
5574
|
explicitIde: options.ide,
|
|
@@ -4630,22 +5714,22 @@ function resolveCodexPlan(options) {
|
|
|
4630
5714
|
if (options.mode !== void 0) {
|
|
4631
5715
|
throw new Error("`--mode` is not supported for codex.");
|
|
4632
5716
|
}
|
|
4633
|
-
const baseDir = scope === "user" ?
|
|
5717
|
+
const baseDir = scope === "user" ? path21.join(homedir2(), ".agents/skills/inspecto-onboarding-codex") : ".agents/skills/inspecto-onboarding-codex";
|
|
4634
5718
|
return {
|
|
4635
5719
|
assets: [
|
|
4636
5720
|
{
|
|
4637
5721
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-codex/SKILL.md`,
|
|
4638
|
-
target:
|
|
5722
|
+
target: path21.join(baseDir, "SKILL.md"),
|
|
4639
5723
|
localSource: "skills/inspecto-onboarding-codex/SKILL.md"
|
|
4640
5724
|
},
|
|
4641
5725
|
{
|
|
4642
5726
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-codex/agents/openai.yaml`,
|
|
4643
|
-
target:
|
|
5727
|
+
target: path21.join(baseDir, "agents/openai.yaml"),
|
|
4644
5728
|
localSource: "skills/inspecto-onboarding-codex/agents/openai.yaml"
|
|
4645
5729
|
},
|
|
4646
5730
|
{
|
|
4647
5731
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-codex/scripts/run-inspecto.sh`,
|
|
4648
|
-
target:
|
|
5732
|
+
target: path21.join(baseDir, "scripts/run-inspecto.sh"),
|
|
4649
5733
|
executable: true,
|
|
4650
5734
|
localSource: "skills/inspecto-onboarding-codex/scripts/run-inspecto.sh"
|
|
4651
5735
|
}
|
|
@@ -4665,22 +5749,22 @@ function resolveClaudeCodePlan(options) {
|
|
|
4665
5749
|
if (scope !== "project" && scope !== "user") {
|
|
4666
5750
|
throw new Error(`Unknown Claude Code scope: ${scope}`);
|
|
4667
5751
|
}
|
|
4668
|
-
const baseDir = scope === "user" ?
|
|
5752
|
+
const baseDir = scope === "user" ? path21.join(homedir2(), ".claude/skills/inspecto-onboarding-claude-code") : ".claude/skills/inspecto-onboarding-claude-code";
|
|
4669
5753
|
return {
|
|
4670
5754
|
assets: [
|
|
4671
5755
|
{
|
|
4672
5756
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-claude-code/SKILL.md`,
|
|
4673
|
-
target:
|
|
5757
|
+
target: path21.join(baseDir, "SKILL.md"),
|
|
4674
5758
|
localSource: "skills/inspecto-onboarding-claude-code/SKILL.md"
|
|
4675
5759
|
},
|
|
4676
5760
|
{
|
|
4677
5761
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-claude-code/agents/openai.yaml`,
|
|
4678
|
-
target:
|
|
5762
|
+
target: path21.join(baseDir, "agents/openai.yaml"),
|
|
4679
5763
|
localSource: "skills/inspecto-onboarding-claude-code/agents/openai.yaml"
|
|
4680
5764
|
},
|
|
4681
5765
|
{
|
|
4682
5766
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-claude-code/scripts/run-inspecto.sh`,
|
|
4683
|
-
target:
|
|
5767
|
+
target: path21.join(baseDir, "scripts/run-inspecto.sh"),
|
|
4684
5768
|
executable: true,
|
|
4685
5769
|
localSource: "skills/inspecto-onboarding-claude-code/scripts/run-inspecto.sh"
|
|
4686
5770
|
}
|
|
@@ -4745,20 +5829,20 @@ async function loadAsset(asset) {
|
|
|
4745
5829
|
if (asset.localSource) {
|
|
4746
5830
|
const localPath = await resolveBundledAssetPath(asset.localSource);
|
|
4747
5831
|
if (localPath) {
|
|
4748
|
-
return await
|
|
5832
|
+
return await fs6.readFile(localPath, "utf-8");
|
|
4749
5833
|
}
|
|
4750
5834
|
}
|
|
4751
5835
|
return await downloadAsset(asset.source);
|
|
4752
5836
|
}
|
|
4753
5837
|
async function resolveBundledAssetPath(relativePath) {
|
|
4754
|
-
const startDir =
|
|
5838
|
+
const startDir = path21.dirname(fileURLToPath(import.meta.url));
|
|
4755
5839
|
let currentDir = startDir;
|
|
4756
5840
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
4757
|
-
const candidate =
|
|
5841
|
+
const candidate = path21.join(currentDir, relativePath);
|
|
4758
5842
|
if (await exists(candidate)) {
|
|
4759
5843
|
return candidate;
|
|
4760
5844
|
}
|
|
4761
|
-
const parent =
|
|
5845
|
+
const parent = path21.dirname(currentDir);
|
|
4762
5846
|
if (parent === currentDir) break;
|
|
4763
5847
|
currentDir = parent;
|
|
4764
5848
|
}
|
|
@@ -4866,6 +5950,9 @@ export {
|
|
|
4866
5950
|
reportCommandError,
|
|
4867
5951
|
apply,
|
|
4868
5952
|
detect,
|
|
5953
|
+
devLink,
|
|
5954
|
+
devStatus,
|
|
5955
|
+
devUnlink,
|
|
4869
5956
|
init,
|
|
4870
5957
|
collectDoctorResult,
|
|
4871
5958
|
doctor,
|