@inspecto-dev/cli 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.turbo/turbo-test.log +205 -7275
  3. package/CHANGELOG.md +14 -0
  4. package/dist/bin.js +32 -1
  5. package/dist/{chunk-2MOEVONN.js → chunk-7ABJRH3F.js} +1232 -145
  6. package/dist/index.d.ts +62 -4
  7. package/dist/index.js +7 -1
  8. package/package.json +2 -2
  9. package/src/bin.ts +45 -0
  10. package/src/commands/dev-config.ts +109 -0
  11. package/src/commands/doctor.ts +162 -9
  12. package/src/commands/init.ts +10 -3
  13. package/src/commands/integration-automation.ts +2 -0
  14. package/src/commands/integration-host-ide.ts +18 -15
  15. package/src/commands/integration-install.ts +1 -1
  16. package/src/commands/onboard.ts +72 -21
  17. package/src/detect/build-tool.ts +14 -5
  18. package/src/detect/framework.ts +3 -0
  19. package/src/detect/package-manager.ts +1 -1
  20. package/src/index.ts +1 -0
  21. package/src/inject/gitignore.ts +13 -2
  22. package/src/instructions.ts +33 -7
  23. package/src/onboarding/apply.ts +137 -3
  24. package/src/onboarding/nextjs-guidance.ts +257 -0
  25. package/src/onboarding/nuxt-guidance.ts +129 -0
  26. package/src/onboarding/planner.ts +257 -6
  27. package/src/onboarding/session.ts +117 -27
  28. package/src/onboarding/target-resolution.ts +3 -3
  29. package/src/onboarding/umi-guidance.ts +139 -0
  30. package/src/types.ts +51 -3
  31. package/tests/apply.test.ts +319 -0
  32. package/tests/dev-config.test.ts +73 -0
  33. package/tests/doctor.test.ts +89 -0
  34. package/tests/init.test.ts +17 -0
  35. package/tests/instructions.test.ts +10 -6
  36. package/tests/integration-host-ide.test.ts +20 -0
  37. package/tests/integration-install.test.ts +65 -0
  38. package/tests/nextjs-guidance.test.ts +128 -0
  39. package/tests/nuxt-guidance.test.ts +67 -0
  40. package/tests/onboard.test.ts +416 -0
  41. package/tests/plan.test.ts +181 -21
  42. package/tests/runner-script.test.ts +120 -1
  43. package/tests/session-resolve.test.ts +116 -0
  44. 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
- ["yarn.lock", "yarn"],
148
- ["package-lock.json", "npm"]
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 = [".inspecto/install.lock", ".inspecto/cache.json", ".inspecto/*.local.json"];
830
- var SHARED_RULES = [".inspecto/install.lock", ".inspecto/cache.json", ".inspecto/*.local.json"];
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((action) => action.type === "manual_step").map((action) => action.description);
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
- if (!(meta.dep in allDeps)) return null;
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: packagePath || void 0
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: packagePath || void 0
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 === "vscode") {
2900
+ if (shouldInstallInspectoExtension(ide2) && ide2) {
2153
2901
  actions.push({
2154
2902
  type: "install_extension",
2155
- target: "vscode",
2156
- description: "Install the Inspecto VS Code extension."
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: supportedIde(context) === "vscode"
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((action) => action.type === "manual_step").map((action) => action.description);
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 path11 from "path";
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: buildTool.isLegacyRspack,
2353
- isLegacyWebpack: buildTool.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("Nuxt requires manual setup in the current version.");
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. Create `plugins/inspecto.client.ts` to mount `@inspecto-dev/core` in development:");
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 updating the config.");
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("Next.js requires manual setup in the current version.");
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 && !isServer) {",
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
- "2. Initialize `@inspecto-dev/core` from a client component such as `app/layout.tsx` or `pages/_app.tsx`:"
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 updating the config.");
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 = path11.join(repoRoot, pkg);
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(path11.join(repoRoot, "package.json"))) {
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 = path11.join(repoRoot, selectedPackage);
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 = path11.join(repoRoot, selectedPackage);
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 automatic plugin injection is not supported in current version`);
2766
- log.hint("You can still manually configure it by modifying your configuration file");
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(path11.join(repoRoot, packagePath));
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 path12 from "path";
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(path12.join(root, "package.json"))) {
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 = path12.join(root, "node_modules", "@inspecto-dev", "plugin");
4024
+ const pluginPath = path16.join(root, "node_modules", "@inspecto-dev", "plugin");
3071
4025
  if (await exists(pluginPath)) {
3072
- const pkgJson = await readJSON(path12.join(pluginPath, "package.json"));
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(path12.join(root, bt.configPath));
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 names = buildResult.unsupported.join(", ");
3113
- checks.push(
3114
- createDiagnostic(
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 = path12.join(root, ".inspecto", "settings.json");
3149
- const settingsLocalPath = path12.join(root, ".inspecto", "settings.local.json");
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(path12.join(root, ".gitignore"));
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 path13 from "path";
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: "code --install-extension inspecto.inspecto",
3293
- marketplaceUrl: "https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto",
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 ? path13.join(repoRoot, packagePath) : repoRoot
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 ? path13.join(rootContext.root, packagePath) : rootContext.root;
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((action) => action.type !== "manual_step").map((action) => action.description);
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: buildIdeExtensionStatus({
3397
- required: session.plan.defaults.extension,
3398
- installed: false,
3399
- manualRequired: session.plan.defaults.extension
3400
- }),
4402
+ ideExtension,
3401
4403
  verification: session.verification,
3402
- diagnostics
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?.ide,
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
- if (target.candidates.length === 0) {
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: "error",
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: getProviderDefault2(plan2.defaults.provider, selectedProvider?.preferredMode)
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: buildIdeExtensionStatus({
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
- diagnostics
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: ${result.status}`);
3577
- log.info(result.summary.headline);
3578
- if (result.status === "needs_target_selection") {
3579
- if (result.target.selectionPurpose) {
3580
- log.warn(result.target.selectionPurpose);
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 result.target.candidates) {
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 (result.target.selectionInstructions) {
3588
- log.hint(result.target.selectionInstructions);
4662
+ if (normalized.target.selectionInstructions) {
4663
+ log.hint(normalized.target.selectionInstructions);
3589
4664
  }
3590
4665
  }
3591
- for (const change of result.summary.changes) {
4666
+ for (const change of normalized.summary.changes) {
3592
4667
  log.hint(change);
3593
4668
  }
3594
- for (const step of result.diagnostics?.nextSteps ?? []) {
4669
+ for (const step of collectDisplayNextSteps(normalized)) {
3595
4670
  log.warn(step);
3596
4671
  }
3597
- if (result.confirmation.required && result.confirmation.question) {
3598
- log.warn(result.confirmation.question);
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
- printManualExtensionGuidance(result);
3601
- const extensionReady = !result.ideExtension?.required || result.ideExtension.installed && !result.ideExtension.manualRequired;
3602
- if (extensionReady && (result.status === "success" || result.status === "partial_success") && result.verification?.message) {
3603
- log.info(result.verification.message);
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 path14 from "path";
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 = path14.join(root, ".inspecto", "install.lock");
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(path14.join(root, ".inspecto"))) {
3672
- await removeDir(path14.join(root, ".inspecto"));
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(path14.join(root, ".inspecto"));
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 fs3 from "fs/promises";
4811
+ import fs6 from "fs/promises";
3731
4812
  import { homedir as homedir2 } from "os";
3732
- import path17 from "path";
4813
+ import path21 from "path";
3733
4814
  import { fileURLToPath } from "url";
3734
4815
 
3735
4816
  // src/commands/integration-host-ide.ts
3736
- import path15 from "path";
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
- const artifactCandidates = await detectArtifactHostIdes(cwd);
3774
- if (artifactCandidates.length === 1) {
3775
- return {
3776
- ide: artifactCandidates[0],
3777
- confidence: "medium",
3778
- source: "artifact",
3779
- candidates: artifactCandidates
3780
- };
3781
- }
3782
- if (artifactCandidates.length > 1) {
3783
- return {
3784
- ide: null,
3785
- confidence: "low",
3786
- source: "ambiguous",
3787
- candidates: artifactCandidates
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
- path15.join(cwd, ".inspecto", "settings.local.json"),
3800
- path15.join(cwd, ".inspecto", "settings.json")
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 path16 from "path";
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 = path16.join(extensionsDir, ".obsolete");
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 fs3.readFile(asset.target, "utf-8");
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 fs3.chmod(asset.target, 493);
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 = path17.join(process.cwd(), ".inspecto", "settings.local.json");
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" ? path17.join(homedir2(), ".agents/skills/inspecto-onboarding-codex") : ".agents/skills/inspecto-onboarding-codex";
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: path17.join(baseDir, "SKILL.md"),
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: path17.join(baseDir, "agents/openai.yaml"),
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: path17.join(baseDir, "scripts/run-inspecto.sh"),
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" ? path17.join(homedir2(), ".claude/skills/inspecto-onboarding-claude-code") : ".claude/skills/inspecto-onboarding-claude-code";
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: path17.join(baseDir, "SKILL.md"),
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: path17.join(baseDir, "agents/openai.yaml"),
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: path17.join(baseDir, "scripts/run-inspecto.sh"),
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 fs3.readFile(localPath, "utf-8");
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 = path17.dirname(fileURLToPath(import.meta.url));
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 = path17.join(currentDir, relativePath);
5841
+ const candidate = path21.join(currentDir, relativePath);
4758
5842
  if (await exists(candidate)) {
4759
5843
  return candidate;
4760
5844
  }
4761
- const parent = path17.dirname(currentDir);
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,