@inspecto-dev/cli 0.3.3 → 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.
Files changed (46) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.turbo/turbo-test.log +10594 -4044
  3. package/CHANGELOG.md +28 -0
  4. package/dist/bin.js +36 -2
  5. package/dist/{chunk-LJOKPCPD.js → chunk-7ABJRH3F.js} +1701 -182
  6. package/dist/index.d.ts +69 -4
  7. package/dist/index.js +7 -1
  8. package/package.json +3 -3
  9. package/src/bin.ts +49 -1
  10. package/src/commands/dev-config.ts +109 -0
  11. package/src/commands/doctor.ts +189 -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 +100 -5
  16. package/src/commands/onboard.ts +80 -15
  17. package/src/detect/build-tool.ts +212 -15
  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 +255 -28
  24. package/src/onboarding/nextjs-guidance.ts +257 -0
  25. package/src/onboarding/nuxt-guidance.ts +129 -0
  26. package/src/onboarding/planner.ts +337 -10
  27. package/src/onboarding/session.ts +127 -31
  28. package/src/onboarding/target-resolution.ts +79 -3
  29. package/src/onboarding/umi-guidance.ts +139 -0
  30. package/src/types.ts +58 -3
  31. package/tests/apply.test.ts +553 -0
  32. package/tests/build-tool.test.ts +199 -0
  33. package/tests/dev-config.test.ts +73 -0
  34. package/tests/doctor.test.ts +130 -0
  35. package/tests/init.test.ts +17 -0
  36. package/tests/install-wrapper.test.ts +56 -0
  37. package/tests/instructions.test.ts +10 -6
  38. package/tests/integration-host-ide.test.ts +20 -0
  39. package/tests/integration-install.test.ts +193 -0
  40. package/tests/nextjs-guidance.test.ts +128 -0
  41. package/tests/nuxt-guidance.test.ts +67 -0
  42. package/tests/onboard.test.ts +511 -0
  43. package/tests/plan.test.ts +283 -21
  44. package/tests/runner-script.test.ts +120 -1
  45. package/tests/session-resolve.test.ts +116 -0
  46. package/tests/session.test.ts +120 -0
package/dist/index.d.ts CHANGED
@@ -49,17 +49,24 @@ interface OnboardingContext {
49
49
  providers: OnboardingProvider[];
50
50
  }
51
51
  interface OnboardingTargetCandidate {
52
+ id?: string;
53
+ candidateId?: string;
52
54
  packagePath: string;
53
55
  configPath: string;
56
+ label?: string;
54
57
  buildTool: BuildTool;
58
+ isLegacyRspack?: boolean;
59
+ isLegacyWebpack?: boolean;
55
60
  frameworks: string[];
56
61
  automaticInjection: boolean;
57
62
  }
58
63
  interface OnboardingTargetResolution {
59
- status: 'resolved' | 'needs_selection';
64
+ status: 'resolved' | 'needs_selection' | 'guided';
60
65
  selected?: OnboardingTargetCandidate;
61
66
  candidates: OnboardingTargetCandidate[];
62
67
  reason: string;
68
+ selectionPurpose?: string;
69
+ selectionInstructions?: string;
63
70
  }
64
71
  interface OnboardingSummary {
65
72
  headline: string;
@@ -67,6 +74,13 @@ interface OnboardingSummary {
67
74
  risks: string[];
68
75
  manualFollowUp: string[];
69
76
  }
77
+ interface OnboardingPatchPlan {
78
+ path: string;
79
+ status: 'planned' | 'manual_patch_required';
80
+ reason: string;
81
+ snippet: string;
82
+ confidence: 'high' | 'medium' | 'low';
83
+ }
70
84
  interface OnboardingConfirmation {
71
85
  required: boolean;
72
86
  reason?: string;
@@ -97,6 +111,15 @@ interface OnboardingVerification {
97
111
  devCommand?: string;
98
112
  message: string;
99
113
  }
114
+ interface OnboardingAssistantHandoff {
115
+ framework?: string;
116
+ metaFramework?: string;
117
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown';
118
+ autoApplied?: string[];
119
+ pendingSteps?: string[];
120
+ assistantPrompt?: string;
121
+ patches?: OnboardingPatchPlan[];
122
+ }
100
123
  interface ResolvedOnboardingSession {
101
124
  status: OnboardStatus;
102
125
  target: OnboardingTargetResolution;
@@ -111,6 +134,14 @@ interface ResolvedOnboardingSession {
111
134
  supported: boolean;
112
135
  } | null;
113
136
  providerDefault?: string;
137
+ framework?: string;
138
+ metaFramework?: string;
139
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown';
140
+ autoApplied?: string[];
141
+ pendingSteps?: string[];
142
+ assistantPrompt?: string;
143
+ patches?: OnboardingPatchPlan[];
144
+ handoff?: OnboardingAssistantHandoff;
114
145
  }
115
146
  interface OnboardCommandResult {
116
147
  status: OnboardStatus;
@@ -121,6 +152,14 @@ interface OnboardCommandResult {
121
152
  verification?: OnboardingVerification;
122
153
  result?: OnboardingExecutionResult;
123
154
  diagnostics?: OnboardingDiagnostics;
155
+ framework?: string;
156
+ metaFramework?: string;
157
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown';
158
+ autoApplied?: string[];
159
+ pendingSteps?: string[];
160
+ assistantPrompt?: string;
161
+ patches?: OnboardingPatchPlan[];
162
+ handoff?: OnboardingAssistantHandoff;
124
163
  }
125
164
  /** Machine-readable detection output for skill-first onboarding */
126
165
  interface DetectionResult {
@@ -148,9 +187,9 @@ interface PlanResult {
148
187
  status: CommandStatus;
149
188
  warnings: CommandMessage[];
150
189
  blockers: CommandMessage[];
151
- strategy: 'supported' | 'manual' | 'unsupported';
190
+ strategy: 'supported' | 'guided' | 'manual' | 'unsupported';
152
191
  actions: Array<{
153
- type: 'install_dependency' | 'modify_file' | 'install_extension' | 'manual_step';
192
+ type: 'install_dependency' | 'modify_file' | 'install_extension' | 'manual_step' | 'generate_patch_plan' | 'generate_file' | 'manual_confirmation';
154
193
  target: string;
155
194
  description: string;
156
195
  }>;
@@ -160,6 +199,13 @@ interface PlanResult {
160
199
  shared: boolean;
161
200
  extension: boolean;
162
201
  };
202
+ framework?: string;
203
+ metaFramework?: string;
204
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown';
205
+ autoApplied?: string[];
206
+ pendingSteps?: string[];
207
+ assistantPrompt?: string;
208
+ patches?: OnboardingPatchPlan[];
163
209
  }
164
210
  /** A single doctor diagnostic check/result */
165
211
  interface DoctorDiagnostic {
@@ -237,6 +283,24 @@ declare function apply(options?: ApplyCommandOptions): Promise<ApplyCommandResul
237
283
 
238
284
  declare function detect(json?: boolean): Promise<DetectionResult>;
239
285
 
286
+ interface InspectoDevConfig {
287
+ cliBin?: string;
288
+ devRepo?: string;
289
+ }
290
+ interface DevConfigCommandResult {
291
+ status: 'ok';
292
+ configPath: string;
293
+ config: InspectoDevConfig;
294
+ }
295
+ interface DevLinkOptions {
296
+ cliBin?: string;
297
+ devRepo?: string;
298
+ json?: boolean;
299
+ }
300
+ declare function devLink(options: DevLinkOptions): Promise<DevConfigCommandResult>;
301
+ declare function devStatus(json?: boolean): Promise<DevConfigCommandResult>;
302
+ declare function devUnlink(json?: boolean): Promise<DevConfigCommandResult>;
303
+
240
304
  declare function init(options: InitOptions): Promise<void>;
241
305
 
242
306
  interface DoctorCommandOptions {
@@ -250,6 +314,7 @@ interface IntegrationAutomationOptions {
250
314
  inspectoVsix?: string;
251
315
  preview?: boolean;
252
316
  silent?: boolean;
317
+ ignoreProjectArtifacts?: boolean;
253
318
  }
254
319
  interface IntegrationAutomationDetails {
255
320
  hostIde?: {
@@ -342,4 +407,4 @@ declare function reportCommandError(error: unknown, options?: ReportCommandError
342
407
 
343
408
  type Framework = 'react' | 'vue';
344
409
 
345
- export { type BuildTool, type DoctorDiagnostic, type DoctorResult, type Framework, type InitOptions, type InstallLock, type OnboardCommandResult, type OnboardStatus, type PackageManager, type ResolvedOnboardingSession, apply, collectDoctorResult, detect, doctor, init, integrationDoctor, onboard, plan, reportCommandError, teardown, writeCommandOutput };
410
+ export { type BuildTool, type DoctorDiagnostic, type DoctorResult, type Framework, type InitOptions, type InstallLock, type OnboardCommandResult, type OnboardStatus, type PackageManager, type ResolvedOnboardingSession, apply, collectDoctorResult, detect, devLink, devStatus, devUnlink, doctor, init, integrationDoctor, onboard, plan, reportCommandError, teardown, writeCommandOutput };
package/dist/index.js CHANGED
@@ -2,6 +2,9 @@ import {
2
2
  apply,
3
3
  collectDoctorResult,
4
4
  detect,
5
+ devLink,
6
+ devStatus,
7
+ devUnlink,
5
8
  doctor,
6
9
  init,
7
10
  integrationDoctor,
@@ -10,11 +13,14 @@ import {
10
13
  reportCommandError,
11
14
  teardown,
12
15
  writeCommandOutput
13
- } from "./chunk-LJOKPCPD.js";
16
+ } from "./chunk-7ABJRH3F.js";
14
17
  export {
15
18
  apply,
16
19
  collectDoctorResult,
17
20
  detect,
21
+ devLink,
22
+ devStatus,
23
+ devUnlink,
18
24
  doctor,
19
25
  init,
20
26
  integrationDoctor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inspecto-dev/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "CLI tools for Inspecto onboarding and lifecycle management",
5
5
  "keywords": [
6
6
  "inspecto",
@@ -20,10 +20,10 @@
20
20
  "ora": "^9.3.0",
21
21
  "picocolors": "^1.0.0",
22
22
  "prompts": "^2.4.2",
23
- "@inspecto-dev/types": "0.3.3"
23
+ "@inspecto-dev/types": "0.3.5"
24
24
  },
25
25
  "devDependencies": {
26
- "@types/node": "^20.0.0",
26
+ "@types/node": "^20.19.39",
27
27
  "@types/prompts": "^2.4.9",
28
28
  "tsup": "^8.0.2",
29
29
  "typescript": "^5.4.5",
package/src/bin.ts CHANGED
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url'
8
8
  import { createRequire } from 'node:module'
9
9
  import { apply } from './commands/apply.js'
10
10
  import { detect } from './commands/detect.js'
11
+ import { devLink, devStatus, devUnlink } from './commands/dev-config.js'
11
12
  import { init } from './commands/init.js'
12
13
  import { doctor } from './commands/doctor.js'
13
14
  import { onboard } from './commands/onboard.js'
@@ -69,6 +70,11 @@ interface IntegrationCommandOptions extends JsonCommandOptions {
69
70
  force?: boolean
70
71
  }
71
72
 
73
+ interface DevCommandOptions extends JsonCommandOptions {
74
+ cliBin?: string
75
+ repo?: string
76
+ }
77
+
72
78
  const integrationScopes = ['project', 'user'] as const
73
79
  const integrationModes = ['skills', 'instructions', 'agents', 'rules'] as const
74
80
 
@@ -83,6 +89,45 @@ function exitWithError(error: unknown, options: JsonCommandOptions = {}): never
83
89
  export function createCli(_argv: readonly string[] = process.argv): CAC {
84
90
  const cli: CAC = cac('inspecto')
85
91
 
92
+ cli
93
+ .command('dev <subcommand>', 'Manage project-local Inspecto development overrides')
94
+ .option('--cli-bin <path>', 'Point this project at a local CLI dist/bin.js')
95
+ .option('--repo <path>', 'Point this project at a local Inspecto repository root')
96
+ .option('--json', 'Print machine-readable JSON output', { default: false })
97
+ .option('--debug', 'Enable debug mode to show full error traces', { default: false })
98
+ .action(async (subcommand: string, options: DevCommandOptions) => {
99
+ try {
100
+ if (subcommand === 'link') {
101
+ if (!options.cliBin && !options.repo) {
102
+ throw new Error('The `dev link` subcommand requires --cli-bin <path> or --repo <path>.')
103
+ }
104
+
105
+ await devLink({
106
+ ...(options.cliBin ? { cliBin: options.cliBin } : {}),
107
+ ...(options.repo ? { devRepo: options.repo } : {}),
108
+ json: options.json ?? false,
109
+ })
110
+ return
111
+ }
112
+
113
+ if (subcommand === 'status') {
114
+ await devStatus(options.json ?? false)
115
+ return
116
+ }
117
+
118
+ if (subcommand === 'unlink') {
119
+ await devUnlink(options.json ?? false)
120
+ return
121
+ }
122
+
123
+ throw new Error(
124
+ 'Usage:\n inspecto dev link [--cli-bin <path>] [--repo <path>]\n inspecto dev status [--json]\n inspecto dev unlink [--json]',
125
+ )
126
+ } catch (error) {
127
+ exitWithError(error, options)
128
+ }
129
+ })
130
+
86
131
  cli
87
132
  .command('init', 'Set up Inspecto in your project')
88
133
  .option('--shared', 'Share .inspecto/settings.json with your team via Git', { default: false })
@@ -128,7 +173,10 @@ export function createCli(_argv: readonly string[] = process.argv): CAC {
128
173
  cli
129
174
  .command('onboard', 'Run assistant-oriented Inspecto onboarding in one structured flow')
130
175
  .option('--json', 'Print machine-readable JSON output', { default: false })
131
- .option('--target <packagePath>', 'Select a monorepo target package explicitly')
176
+ .option(
177
+ '--target <candidateIdOrPath>',
178
+ 'Select the build target to onboard using a returned candidateId or compatible config path',
179
+ )
132
180
  .option('--yes', 'Accept a lightweight confirmation gate automatically', { default: false })
133
181
  .option('--shared', 'Write shared Inspecto settings instead of local-only settings')
134
182
  .option('--skip-install', 'Skip npm dependency installation')
@@ -0,0 +1,109 @@
1
+ import path from 'node:path'
2
+ import { readJSON, removeFile, writeJSON } from '../utils/fs.js'
3
+ import { updateGitignore } from '../inject/gitignore.js'
4
+ import { log } from '../utils/logger.js'
5
+ import { writeCommandOutput } from '../utils/output.js'
6
+
7
+ export interface InspectoDevConfig {
8
+ cliBin?: string
9
+ devRepo?: string
10
+ }
11
+
12
+ export interface DevConfigCommandResult {
13
+ status: 'ok'
14
+ configPath: string
15
+ config: InspectoDevConfig
16
+ }
17
+
18
+ interface DevLinkOptions {
19
+ cliBin?: string
20
+ devRepo?: string
21
+ json?: boolean
22
+ }
23
+
24
+ const DEV_CONFIG_PATH = path.join('.inspecto', 'dev.json')
25
+
26
+ function absoluteDevConfigPath(root: string): string {
27
+ return path.join(root, DEV_CONFIG_PATH)
28
+ }
29
+
30
+ function printDevConfigResult(result: DevConfigCommandResult): void {
31
+ log.header('Inspecto Dev')
32
+ log.info(`Config: ${result.configPath}`)
33
+ if (result.config.cliBin) {
34
+ log.hint(`cliBin: ${result.config.cliBin}`)
35
+ }
36
+ if (result.config.devRepo) {
37
+ log.hint(`devRepo: ${result.config.devRepo}`)
38
+ }
39
+ if (!result.config.cliBin && !result.config.devRepo) {
40
+ log.hint('No local dev overrides are configured.')
41
+ }
42
+ }
43
+
44
+ async function readExistingConfig(root: string): Promise<InspectoDevConfig> {
45
+ const configPath = absoluteDevConfigPath(root)
46
+ const config = await readJSON<InspectoDevConfig>(configPath)
47
+ if (!config || typeof config !== 'object') {
48
+ return {}
49
+ }
50
+
51
+ return config
52
+ }
53
+
54
+ export async function devLink(options: DevLinkOptions): Promise<DevConfigCommandResult> {
55
+ const root = process.cwd()
56
+ const configPath = absoluteDevConfigPath(root)
57
+ const existing = await readExistingConfig(root)
58
+ const nextConfig: InspectoDevConfig = {
59
+ ...existing,
60
+ ...(options.cliBin ? { cliBin: options.cliBin } : {}),
61
+ ...(options.devRepo ? { devRepo: options.devRepo } : {}),
62
+ }
63
+
64
+ await writeJSON(configPath, nextConfig)
65
+ await updateGitignore(root, false, false, true)
66
+
67
+ return writeCommandOutput(
68
+ {
69
+ status: 'ok',
70
+ configPath,
71
+ config: nextConfig,
72
+ },
73
+ options.json ?? false,
74
+ printDevConfigResult,
75
+ )
76
+ }
77
+
78
+ export async function devStatus(json = false): Promise<DevConfigCommandResult> {
79
+ const root = process.cwd()
80
+ const configPath = absoluteDevConfigPath(root)
81
+ const config = await readExistingConfig(root)
82
+
83
+ return writeCommandOutput(
84
+ {
85
+ status: 'ok',
86
+ configPath,
87
+ config,
88
+ },
89
+ json,
90
+ printDevConfigResult,
91
+ )
92
+ }
93
+
94
+ export async function devUnlink(json = false): Promise<DevConfigCommandResult> {
95
+ const root = process.cwd()
96
+ const configPath = absoluteDevConfigPath(root)
97
+
98
+ await removeFile(configPath)
99
+
100
+ return writeCommandOutput(
101
+ {
102
+ status: 'ok',
103
+ configPath,
104
+ config: {},
105
+ },
106
+ json,
107
+ printDevConfigResult,
108
+ )
109
+ }
@@ -10,8 +10,15 @@ import { detectFrameworks } from '../detect/framework.js'
10
10
  import { detectIDE } from '../detect/ide.js'
11
11
  import { detectProviders } from '../detect/provider.js'
12
12
  import { isExtensionInstalled } from '../inject/extension.js'
13
+ import { createPlanResult } from '../onboarding/planner.js'
13
14
  import { writeCommandOutput } from '../utils/output.js'
14
- import type { CommandStatus, DoctorDiagnostic, DoctorResult } from '../types.js'
15
+ import type {
16
+ CommandStatus,
17
+ DoctorDiagnostic,
18
+ DoctorResult,
19
+ OnboardingContext,
20
+ PlanResult,
21
+ } from '../types.js'
15
22
 
16
23
  export interface DoctorCommandOptions {
17
24
  json?: boolean
@@ -39,6 +46,113 @@ function doctorStatus(errors: number, warnings: number): CommandStatus {
39
46
  return 'ok'
40
47
  }
41
48
 
49
+ function isGuidedMetaFramework(buildTool: string): boolean {
50
+ return buildTool === 'Next.js' || buildTool === 'Nuxt'
51
+ }
52
+
53
+ function buildDoctorOnboardingContext(input: {
54
+ root: string
55
+ packageManager: NonNullable<DoctorResult['project']['packageManager']>
56
+ buildTools: Awaited<ReturnType<typeof detectBuildTools>>
57
+ frameworks: Awaited<ReturnType<typeof detectFrameworks>>
58
+ ides: Awaited<ReturnType<typeof detectIDE>>
59
+ providers: Awaited<ReturnType<typeof detectProviders>>
60
+ }): OnboardingContext {
61
+ return {
62
+ root: input.root,
63
+ packageManager: input.packageManager,
64
+ buildTools: input.buildTools,
65
+ frameworks: {
66
+ supported: input.frameworks.supported,
67
+ unsupported: input.frameworks.unsupported.map(item => item.name),
68
+ },
69
+ ides: input.ides.detected.map(({ ide, supported }) => ({ ide, supported })),
70
+ providers: input.providers.detected.map(({ id, label, supported, preferredMode }) => ({
71
+ id,
72
+ label,
73
+ supported,
74
+ preferredMode,
75
+ })),
76
+ }
77
+ }
78
+
79
+ function isConfigPatchReason(reason: string): boolean {
80
+ return reason.startsWith('next_config_') || reason.startsWith('nuxt_config_')
81
+ }
82
+
83
+ function isNextWebpackDevPatchReason(reason: string): boolean {
84
+ return reason === 'next_dev_script_requires_webpack'
85
+ }
86
+
87
+ async function collectGuidedPatchDiagnostics(
88
+ root: string,
89
+ plan: PlanResult,
90
+ ): Promise<DoctorDiagnostic[]> {
91
+ if (plan.strategy !== 'guided' || !plan.patches?.length) {
92
+ return []
93
+ }
94
+
95
+ const diagnostics: DoctorDiagnostic[] = []
96
+
97
+ for (const patch of plan.patches) {
98
+ if (isConfigPatchReason(patch.reason)) {
99
+ const content = await readFile(path.join(root, patch.path))
100
+ if (content?.includes('@inspecto-dev/plugin')) {
101
+ diagnostics.push(
102
+ createDiagnostic(
103
+ 'guided-config-patch-detected',
104
+ 'ok',
105
+ `Guided config patch appears to be applied in ${patch.path}`,
106
+ [],
107
+ { path: patch.path, reason: patch.reason },
108
+ ),
109
+ )
110
+ } else {
111
+ diagnostics.push(
112
+ createDiagnostic(
113
+ 'guided-config-patch-pending',
114
+ 'warning',
115
+ `Guided config patch still needs review in ${patch.path}`,
116
+ ['Run `inspecto onboard --json` to review the generated patch details.'],
117
+ { path: patch.path, reason: patch.reason },
118
+ ),
119
+ )
120
+ }
121
+ continue
122
+ }
123
+
124
+ if (isNextWebpackDevPatchReason(patch.reason)) {
125
+ const packageJson = await readJSON<{ scripts?: Record<string, string> }>(
126
+ path.join(root, 'package.json'),
127
+ )
128
+ const devScript = packageJson?.scripts?.dev ?? ''
129
+ if (/next\s+dev\b/.test(devScript) && /--webpack\b/.test(devScript)) {
130
+ diagnostics.push(
131
+ createDiagnostic(
132
+ 'guided-dev-script-configured',
133
+ 'ok',
134
+ 'Next.js dev script is configured for webpack mode',
135
+ [],
136
+ { script: devScript },
137
+ ),
138
+ )
139
+ } else {
140
+ diagnostics.push(
141
+ createDiagnostic(
142
+ 'guided-dev-script-pending',
143
+ 'warning',
144
+ 'Next.js dev script still needs webpack mode for Inspecto validation',
145
+ ['Update the `dev` script to `next dev --webpack` before browser validation.'],
146
+ { script: devScript },
147
+ ),
148
+ )
149
+ }
150
+ }
151
+ }
152
+
153
+ return diagnostics
154
+ }
155
+
42
156
  function printDoctorResult(result: DoctorResult): void {
43
157
  log.header('Inspecto Doctor')
44
158
 
@@ -99,6 +213,15 @@ export async function collectDoctorResult(root = process.cwd()): Promise<DoctorR
99
213
  detectBuildTools(root),
100
214
  isExtensionInstalled(),
101
215
  ])
216
+ const onboardingContext = buildDoctorOnboardingContext({
217
+ root,
218
+ packageManager: pm,
219
+ buildTools: buildResult,
220
+ frameworks: frameworkResult,
221
+ ides: ideProbe,
222
+ providers: providerProbe,
223
+ })
224
+ const onboardingPlan = createPlanResult(onboardingContext)
102
225
 
103
226
  // Check 2: IDE
104
227
  if (ideProbe.detected.length === 0) {
@@ -229,15 +352,45 @@ export async function collectDoctorResult(root = process.cwd()): Promise<DoctorR
229
352
  )
230
353
  }
231
354
  } else if (buildResult.unsupported.length > 0) {
232
- const names = buildResult.unsupported.join(', ')
233
- checks.push(
234
- createDiagnostic(
235
- `build-tool-unsupported`,
236
- 'warning',
237
- `Build tool: ${names} (not supported in v1)`,
238
- ['current version supports: Vite, Webpack, Rspack, esbuild, Rollup'],
239
- ),
355
+ const guidedBuildTools = buildResult.unsupported.filter(isGuidedMetaFramework)
356
+ const trulyUnsupportedBuildTools = buildResult.unsupported.filter(
357
+ buildTool => !isGuidedMetaFramework(buildTool),
240
358
  )
359
+
360
+ if (guidedBuildTools.length > 0) {
361
+ checks.push(
362
+ createDiagnostic(
363
+ 'build-tool-guided',
364
+ 'warning',
365
+ `Build tool: ${guidedBuildTools.join(', ')} (guided onboarding available)`,
366
+ [
367
+ 'Run `inspecto onboard --json` to generate the remaining patch plan and assistant handoff.',
368
+ ...(onboardingPlan.pendingSteps ?? []),
369
+ ],
370
+ {
371
+ metaFrameworks: guidedBuildTools,
372
+ ...(onboardingPlan.pendingSteps ? { pendingSteps: onboardingPlan.pendingSteps } : {}),
373
+ ...(onboardingPlan.assistantPrompt
374
+ ? { assistantPrompt: onboardingPlan.assistantPrompt }
375
+ : {}),
376
+ ...(onboardingPlan.patches ? { patchCount: onboardingPlan.patches.length } : {}),
377
+ },
378
+ ),
379
+ )
380
+ }
381
+
382
+ checks.push(...(await collectGuidedPatchDiagnostics(root, onboardingPlan)))
383
+
384
+ if (trulyUnsupportedBuildTools.length > 0) {
385
+ checks.push(
386
+ createDiagnostic(
387
+ 'build-tool-unsupported',
388
+ 'warning',
389
+ `Build tool: ${trulyUnsupportedBuildTools.join(', ')} (not supported in v1)`,
390
+ ['current version supports: Vite, Webpack, Rspack, esbuild, Rollup'],
391
+ ),
392
+ )
393
+ }
241
394
  } else {
242
395
  checks.push(
243
396
  createDiagnostic('build-tool-missing', 'warning', 'No recognized build config found'),
@@ -282,6 +435,33 @@ export async function collectDoctorResult(root = process.cwd()): Promise<DoctorR
282
435
  const settings = await readJSON(targetPath)
283
436
  if (settings) {
284
437
  checks.push(createDiagnostic('settings-valid', 'ok', `.inspecto/${fileName} valid`))
438
+
439
+ const configuredIde =
440
+ typeof (settings as Record<string, unknown>).ide === 'string'
441
+ ? ((settings as Record<string, unknown>).ide as string)
442
+ : undefined
443
+ const detectedIdeCandidates = ideProbe.detected.map(item => item.ide)
444
+ if (
445
+ configuredIde &&
446
+ detectedIdeCandidates.length > 0 &&
447
+ !detectedIdeCandidates.includes(configuredIde)
448
+ ) {
449
+ checks.push(
450
+ createDiagnostic(
451
+ 'settings-ide-mismatch',
452
+ 'warning',
453
+ `.inspecto/${fileName} sets ide=${configuredIde}, but the current environment looks like ${detectedIdeCandidates.join(', ')}. Inspecto will use the configured IDE from ${fileName}.`,
454
+ [
455
+ `Update .inspecto/${fileName} if you want Inspecto to target the currently detected IDE instead.`,
456
+ ],
457
+ {
458
+ configuredIde,
459
+ detectedIdeCandidates,
460
+ precedence: `configured ide from ${fileName}`,
461
+ },
462
+ ),
463
+ )
464
+ }
285
465
  } else {
286
466
  checks.push(
287
467
  createDiagnostic(
@@ -24,7 +24,11 @@ import {
24
24
  promptMonorepoPackageChoice,
25
25
  promptUnsupportedFrameworkContinue,
26
26
  } from '../prompts.js'
27
- import { printNextJsManualInstructions, printNuxtManualInstructions } from '../instructions.js'
27
+ import {
28
+ printNextJsManualInstructions,
29
+ printNuxtManualInstructions,
30
+ printUmiManualInstructions,
31
+ } from '../instructions.js'
28
32
 
29
33
  export async function init(options: InitOptions): Promise<void> {
30
34
  const repoRoot = process.cwd()
@@ -175,8 +179,8 @@ export async function init(options: InitOptions): Promise<void> {
175
179
  if (buildResult.unsupported.length > 0) {
176
180
  const names = buildResult.unsupported.join(', ')
177
181
  manualConfigRequiredFor = buildResult.unsupported[0] || ''
178
- log.warn(`Detected ${names} — automatic plugin injection is not supported in current version`)
179
- log.hint('You can still manually configure it by modifying your configuration file')
182
+ log.warn(`Detected ${names} — guided onboarding is available in the current version`)
183
+ log.hint('Inspecto can prepare the remaining patch plan and assistant handoff for this stack.')
180
184
 
181
185
  if (buildResult.unsupported.includes('Next.js')) {
182
186
  printNextJsManualInstructions()
@@ -184,6 +188,9 @@ export async function init(options: InitOptions): Promise<void> {
184
188
  if (buildResult.unsupported.includes('Nuxt')) {
185
189
  printNuxtManualInstructions()
186
190
  }
191
+ if (buildResult.unsupported.includes('Umi')) {
192
+ printUmiManualInstructions()
193
+ }
187
194
  }
188
195
  if (buildResult.supported.length === 0 && buildResult.unsupported.length === 0) {
189
196
  log.warn('No recognized build tool detected')
@@ -24,6 +24,7 @@ interface IntegrationAutomationOptions {
24
24
  inspectoVsix?: string
25
25
  preview?: boolean
26
26
  silent?: boolean
27
+ ignoreProjectArtifacts?: boolean
27
28
  }
28
29
 
29
30
  interface IntegrationAutomationDetails {
@@ -93,6 +94,7 @@ export async function runIntegrationAutomation(
93
94
  const resolvedHostIde = await resolveIntegrationHostIde({
94
95
  ...(options.ide ? { explicitIde: options.ide } : {}),
95
96
  ...(cwd ? { cwd } : {}),
97
+ ...(options.ignoreProjectArtifacts ? { ignoreProjectArtifacts: true } : {}),
96
98
  })
97
99
 
98
100
  const details: IntegrationAutomationDetails = {