@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
@@ -2,6 +2,11 @@ import path from 'node:path'
2
2
  import { detectFrameworks } from '../detect/framework.js'
3
3
  import { detectIDE } from '../detect/ide.js'
4
4
  import { detectProviders } from '../detect/provider.js'
5
+ import {
6
+ getHostIdeBinaryName,
7
+ isSupportedHostIde,
8
+ type SupportedHostIde,
9
+ } from '../integrations/capabilities.js'
5
10
  import { applyOnboardingPlan, type ApplyOnboardingResult } from './apply.js'
6
11
  import { buildOnboardingContext } from './context.js'
7
12
  import { createPlanResult, planManualFollowUp } from './planner.js'
@@ -61,9 +66,10 @@ async function buildVerification(
61
66
  projectRoot: string,
62
67
  packageManager: OnboardingContext['packageManager'],
63
68
  ): Promise<OnboardingVerification> {
64
- const packageJson = await readJSON<{ scripts?: Record<string, string> }>(
65
- path.join(projectRoot, 'package.json'),
66
- )
69
+ const packageJson = await readJSON<{
70
+ scripts?: Record<string, string>
71
+ dependencies?: Record<string, string>
72
+ }>(path.join(projectRoot, 'package.json'))
67
73
 
68
74
  if (packageJson?.scripts?.dev) {
69
75
  const devCommand = getVerificationCommand(packageManager)
@@ -74,13 +80,34 @@ async function buildVerification(
74
80
  }
75
81
  }
76
82
 
83
+ if (packageJson?.scripts?.start && packageJson?.dependencies?.next) {
84
+ const devCommand = packageManager === 'bun' ? 'bunx next dev' : 'npx next dev'
85
+ return {
86
+ available: true,
87
+ devCommand,
88
+ message: `Start the local dev server with \`${devCommand}\` to verify Inspecto in the browser.`,
89
+ }
90
+ }
91
+
77
92
  return {
78
93
  available: false,
79
94
  message: 'Start your normal local dev server command to verify Inspecto in the browser.',
80
95
  }
81
96
  }
82
97
 
98
+ function buildExtensionInstallCommand(ide?: string): string {
99
+ if (ide && isSupportedHostIde(ide)) {
100
+ const binaryName = getHostIdeBinaryName(ide as SupportedHostIde)
101
+ if (binaryName) {
102
+ return `${binaryName} --install-extension inspecto.inspecto`
103
+ }
104
+ }
105
+
106
+ return 'code --install-extension inspecto.inspecto'
107
+ }
108
+
83
109
  function buildIdeExtensionStatus(input: {
110
+ ide?: string
84
111
  required: boolean
85
112
  installed: boolean
86
113
  manualRequired: boolean
@@ -97,8 +124,12 @@ function buildIdeExtensionStatus(input: {
97
124
  required: true,
98
125
  installed: input.installed,
99
126
  manualRequired: input.manualRequired,
100
- installCommand: 'code --install-extension inspecto.inspecto',
101
- marketplaceUrl: 'https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto',
127
+ installCommand: buildExtensionInstallCommand(input.ide),
128
+ ...(input.ide === 'vscode'
129
+ ? {
130
+ marketplaceUrl: 'https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto',
131
+ }
132
+ : {}),
102
133
  openVsxUrl: 'https://open-vsx.org/extension/inspecto/inspecto',
103
134
  }
104
135
  }
@@ -165,7 +196,12 @@ async function buildTargetedContext(
165
196
 
166
197
  function buildOnboardingSummary(plan: PlanResult, projectRoot: string): OnboardingSummary {
167
198
  const changes = plan.actions
168
- .filter(action => action.type !== 'manual_step')
199
+ .filter(
200
+ action =>
201
+ !['manual_step', 'generate_patch_plan', 'generate_file', 'manual_confirmation'].includes(
202
+ action.type,
203
+ ),
204
+ )
169
205
  .map(action => action.description)
170
206
  const risks = [...plan.warnings.map(item => item.message)]
171
207
  const manualFollowUp = planManualFollowUp(plan)
@@ -235,18 +271,28 @@ function buildPreApplyResult(
235
271
  }
236
272
  : undefined
237
273
 
274
+ const ideExtension = buildIdeExtensionStatus({
275
+ ...(session.selectedIDE?.ide ? { ide: session.selectedIDE.ide } : {}),
276
+ required: session.plan.defaults.extension,
277
+ installed: false,
278
+ manualRequired: session.plan.defaults.extension,
279
+ })
280
+
238
281
  return {
239
282
  status,
240
283
  target: session.target,
241
284
  summary: session.summary,
242
285
  confirmation: session.confirmation,
243
- ideExtension: buildIdeExtensionStatus({
244
- required: session.plan.defaults.extension,
245
- installed: false,
246
- manualRequired: session.plan.defaults.extension,
247
- }),
286
+ ideExtension,
248
287
  verification: session.verification,
249
- diagnostics,
288
+ ...(session.framework ? { framework: session.framework } : {}),
289
+ ...(session.metaFramework ? { metaFramework: session.metaFramework } : {}),
290
+ ...(session.routerMode ? { routerMode: session.routerMode } : {}),
291
+ ...(session.autoApplied ? { autoApplied: session.autoApplied } : {}),
292
+ ...(session.pendingSteps ? { pendingSteps: session.pendingSteps } : {}),
293
+ ...(session.assistantPrompt ? { assistantPrompt: session.assistantPrompt } : {}),
294
+ ...(session.patches ? { patches: session.patches } : {}),
295
+ ...(diagnostics ? { diagnostics } : {}),
250
296
  }
251
297
  }
252
298
 
@@ -263,8 +309,8 @@ function buildExecutionResult(
263
309
  installedDependencies: applyResult.mutations
264
310
  .map(item => item.name)
265
311
  .filter((value): value is string => !!value),
266
- selectedProviderDefault: session.providerDefault,
267
- selectedIDE: session.selectedIDE?.ide,
312
+ ...(session.providerDefault ? { selectedProviderDefault: session.providerDefault } : {}),
313
+ ...(session.selectedIDE?.ide ? { selectedIDE: session.selectedIDE.ide } : {}),
268
314
  mutations: applyResult.mutations,
269
315
  }
270
316
  }
@@ -312,20 +358,40 @@ export async function resolveOnboardingSession(
312
358
  repoRoot: root,
313
359
  buildTools: rootContext.buildTools.supported,
314
360
  frameworkSupportByPackage,
315
- selectedPackagePath: options.target,
361
+ ...(options.target ? { selectedPackagePath: options.target } : {}),
316
362
  })
317
363
 
318
- if (target.candidates.length === 0) {
364
+ const isGuided = rootContext.buildTools.unsupported.some(t =>
365
+ ['Next.js', 'Nuxt', 'Umi'].includes(t),
366
+ )
367
+
368
+ if (target.candidates.length === 0 || isGuided) {
319
369
  const plan = createPlanResult(rootContext)
370
+ const guidedStatus = plan.strategy === 'guided' ? 'partial_success' : 'error'
371
+ const resolvedTarget =
372
+ plan.strategy === 'guided'
373
+ ? {
374
+ status: 'guided' as const,
375
+ candidates: [],
376
+ reason: `Guided onboarding is available for ${plan.metaFramework ?? 'this project'}.`,
377
+ }
378
+ : target
320
379
  return {
321
- status: 'error',
322
- target,
380
+ status: guidedStatus,
381
+ target: resolvedTarget,
323
382
  summary: buildOnboardingSummary(plan, root),
324
383
  confirmation: { required: false },
325
384
  verification: rootVerification,
326
385
  context: rootContext,
327
386
  plan,
328
387
  projectRoot: root,
388
+ ...(plan.framework ? { framework: plan.framework } : {}),
389
+ ...(plan.metaFramework ? { metaFramework: plan.metaFramework } : {}),
390
+ ...(plan.routerMode ? { routerMode: plan.routerMode } : {}),
391
+ ...(plan.autoApplied ? { autoApplied: plan.autoApplied } : {}),
392
+ ...(plan.pendingSteps ? { pendingSteps: plan.pendingSteps } : {}),
393
+ ...(plan.assistantPrompt ? { assistantPrompt: plan.assistantPrompt } : {}),
394
+ ...(plan.patches ? { patches: plan.patches } : {}),
329
395
  }
330
396
  }
331
397
 
@@ -380,6 +446,11 @@ export async function resolveOnboardingSession(
380
446
  status = 'partial_success'
381
447
  }
382
448
 
449
+ const providerDefault = getProviderDefault(
450
+ plan.defaults.provider,
451
+ selectedProvider?.preferredMode,
452
+ )
453
+
383
454
  return {
384
455
  status,
385
456
  target,
@@ -390,7 +461,14 @@ export async function resolveOnboardingSession(
390
461
  plan,
391
462
  projectRoot: context.root,
392
463
  selectedIDE,
393
- providerDefault: getProviderDefault(plan.defaults.provider, selectedProvider?.preferredMode),
464
+ ...(providerDefault ? { providerDefault } : {}),
465
+ ...(plan.framework ? { framework: plan.framework } : {}),
466
+ ...(plan.metaFramework ? { metaFramework: plan.metaFramework } : {}),
467
+ ...(plan.routerMode ? { routerMode: plan.routerMode } : {}),
468
+ ...(plan.autoApplied ? { autoApplied: plan.autoApplied } : {}),
469
+ ...(plan.pendingSteps ? { pendingSteps: plan.pendingSteps } : {}),
470
+ ...(plan.assistantPrompt ? { assistantPrompt: plan.assistantPrompt } : {}),
471
+ ...(plan.patches ? { patches: plan.patches } : {}),
394
472
  }
395
473
  }
396
474
 
@@ -414,7 +492,9 @@ export async function applyResolvedOnboardingSession(
414
492
  selectedIDE: session.selectedIDE,
415
493
  providerDefault: session.providerDefault,
416
494
  plan: session.plan,
417
- allowManualPlanApply: session.plan.strategy === 'manual' && session.plan.blockers.length === 0,
495
+ allowManualPlanApply:
496
+ (session.plan.strategy === 'manual' || session.plan.strategy === 'guided') &&
497
+ session.plan.blockers.length === 0,
418
498
  })
419
499
 
420
500
  const diagnostics = buildExecutionDiagnostics(session, applyResult)
@@ -425,20 +505,30 @@ export async function applyResolvedOnboardingSession(
425
505
  ? 'partial_success'
426
506
  : 'success'
427
507
 
508
+ const ideExtension = buildIdeExtensionStatus({
509
+ ...(session.selectedIDE?.ide ? { ide: session.selectedIDE.ide } : {}),
510
+ required: session.plan.defaults.extension,
511
+ installed:
512
+ session.plan.defaults.extension && !applyResult.postInstall.manualExtensionInstallNeeded,
513
+ manualRequired: applyResult.postInstall.manualExtensionInstallNeeded,
514
+ })
515
+
428
516
  return {
429
517
  status,
430
518
  target: session.target,
431
519
  summary: session.summary,
432
520
  confirmation: session.confirmation,
433
- ideExtension: buildIdeExtensionStatus({
434
- required: session.plan.defaults.extension,
435
- installed:
436
- session.plan.defaults.extension && !applyResult.postInstall.manualExtensionInstallNeeded,
437
- manualRequired: applyResult.postInstall.manualExtensionInstallNeeded,
438
- }),
521
+ ideExtension,
439
522
  verification,
440
523
  result: buildExecutionResult(session, applyResult),
441
- diagnostics,
524
+ ...(session.framework ? { framework: session.framework } : {}),
525
+ ...(session.metaFramework ? { metaFramework: session.metaFramework } : {}),
526
+ ...(session.routerMode ? { routerMode: session.routerMode } : {}),
527
+ ...(session.autoApplied ? { autoApplied: session.autoApplied } : {}),
528
+ ...(session.pendingSteps ? { pendingSteps: session.pendingSteps } : {}),
529
+ ...(session.assistantPrompt ? { assistantPrompt: session.assistantPrompt } : {}),
530
+ ...(session.patches ? { patches: session.patches } : {}),
531
+ ...(diagnostics ? { diagnostics } : {}),
442
532
  }
443
533
  }
444
534
 
@@ -63,8 +63,8 @@ function buildCandidates(input: ResolveOnboardingTargetInput): OnboardingTargetC
63
63
  configPath: buildTool.configPath,
64
64
  label: buildTool.label,
65
65
  buildTool: buildTool.tool,
66
- isLegacyRspack: buildTool.isLegacyRspack,
67
- isLegacyWebpack: buildTool.isLegacyWebpack,
66
+ ...(buildTool.isLegacyRspack ? { isLegacyRspack: true } : {}),
67
+ ...(buildTool.isLegacyWebpack ? { isLegacyWebpack: true } : {}),
68
68
  frameworks: input.frameworkSupportByPackage[packagePath] ?? [],
69
69
  automaticInjection: true,
70
70
  }
@@ -162,7 +162,7 @@ export function resolveOnboardingTarget(
162
162
  if (candidates.length === 1) {
163
163
  return {
164
164
  status: 'resolved',
165
- selected: candidates[0],
165
+ selected: candidates[0]!,
166
166
  candidates,
167
167
  reason: 'Only one supported target was detected.',
168
168
  selectionPurpose: buildSelectionPurpose(),
@@ -0,0 +1,139 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import type { OnboardingPatchPlan } from '../types.js'
4
+
5
+ export interface UmiGuidance {
6
+ framework: 'react'
7
+ metaFramework: 'Umi'
8
+ autoApplied: string[]
9
+ pendingSteps: string[]
10
+ assistantPrompt: string
11
+ patches: OnboardingPatchPlan[]
12
+ }
13
+
14
+ const UMI_CONFIG_CANDIDATES = [
15
+ '.umirc.ts',
16
+ '.umirc.js',
17
+ 'config/config.ts',
18
+ 'config/config.js',
19
+ ] as const
20
+
21
+ function findFirstExisting(root: string, candidates: readonly string[]): string | undefined {
22
+ for (const candidate of candidates) {
23
+ if (fs.existsSync(path.join(root, candidate))) {
24
+ return candidate
25
+ }
26
+ }
27
+
28
+ return undefined
29
+ }
30
+
31
+ function readConfig(root: string, relativePath?: string): string {
32
+ if (!relativePath) return ''
33
+ const filePath = path.join(root, relativePath)
34
+ if (!fs.existsSync(filePath)) return ''
35
+ return fs.readFileSync(filePath, 'utf8')
36
+ }
37
+
38
+ function detectPatchShape(source: string): {
39
+ status: OnboardingPatchPlan['status']
40
+ reason: OnboardingPatchPlan['reason']
41
+ confidence: OnboardingPatchPlan['confidence']
42
+ } {
43
+ if (/defineConfig\s*\(\s*\{[\s\S]*\}\s*\)/m.test(source)) {
44
+ return {
45
+ status: 'planned',
46
+ reason: 'umi_config_object_export',
47
+ confidence: 'high',
48
+ }
49
+ }
50
+
51
+ if (source.trim().length === 0) {
52
+ return {
53
+ status: 'manual_patch_required',
54
+ reason: 'umi_config_missing',
55
+ confidence: 'low',
56
+ }
57
+ }
58
+
59
+ return {
60
+ status: 'manual_patch_required',
61
+ reason: 'umi_config_complex_shape',
62
+ confidence: 'medium',
63
+ }
64
+ }
65
+
66
+ function buildUmiConfigSnippet(): string {
67
+ return [
68
+ "import { defineConfig } from 'umi'",
69
+ "import { webpack4Plugin } from '@inspecto-dev/plugin/legacy/webpack4'",
70
+ '',
71
+ 'export default defineConfig({',
72
+ ' chainWebpack(memo) {',
73
+ " if (process.env.NODE_ENV === 'development') {",
74
+ " memo.plugin('inspecto').use(webpack4Plugin())",
75
+ ' }',
76
+ ' },',
77
+ '})',
78
+ ].join('\n')
79
+ }
80
+
81
+ function buildUmiMountSnippet(): string {
82
+ return [
83
+ "import { useEffect } from 'react'",
84
+ '',
85
+ 'export function rootContainer(container: React.ReactNode) {',
86
+ ' return (',
87
+ ' <InspectoWrapper>{container}</InspectoWrapper>',
88
+ ' )',
89
+ '}',
90
+ '',
91
+ 'function InspectoWrapper({ children }: { children: React.ReactNode }) {',
92
+ ' useEffect(() => {',
93
+ " if (process.env.NODE_ENV !== 'production') {",
94
+ " import('@inspecto-dev/core').then(({ mountInspector }) => {",
95
+ ' mountInspector({',
96
+ " serverUrl: 'http://127.0.0.1:' + ((window as any).__AI_INSPECTOR_PORT__ || 5678),",
97
+ ' })',
98
+ ' })',
99
+ ' }',
100
+ ' }, [])',
101
+ '',
102
+ ' return <>{children}</>',
103
+ '}',
104
+ ].join('\n')
105
+ }
106
+
107
+ export function createUmiGuidance(root: string): UmiGuidance {
108
+ const configPath = findFirstExisting(root, UMI_CONFIG_CANDIDATES) ?? '.umirc.ts'
109
+ const configSource = readConfig(root, findFirstExisting(root, UMI_CONFIG_CANDIDATES))
110
+ const patchShape = detectPatchShape(configSource)
111
+
112
+ return {
113
+ framework: 'react',
114
+ metaFramework: 'Umi',
115
+ autoApplied: ['dependencies', 'inspecto_settings'],
116
+ pendingSteps: [
117
+ `Review the generated Umi patch plan for ${configPath}.`,
118
+ 'Complete the remaining client-side mount step in src/app.tsx.',
119
+ ],
120
+ assistantPrompt:
121
+ '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.',
122
+ patches: [
123
+ {
124
+ path: configPath,
125
+ status: patchShape.status,
126
+ reason: patchShape.reason,
127
+ confidence: patchShape.confidence,
128
+ snippet: buildUmiConfigSnippet(),
129
+ },
130
+ {
131
+ path: 'src/app.tsx',
132
+ status: 'manual_patch_required',
133
+ reason: 'umi_app_mount',
134
+ confidence: 'medium',
135
+ snippet: buildUmiMountSnippet(),
136
+ },
137
+ ],
138
+ }
139
+ }
package/src/types.ts CHANGED
@@ -76,7 +76,7 @@ export interface OnboardingTargetCandidate {
76
76
  }
77
77
 
78
78
  export interface OnboardingTargetResolution {
79
- status: 'resolved' | 'needs_selection'
79
+ status: 'resolved' | 'needs_selection' | 'guided'
80
80
  selected?: OnboardingTargetCandidate
81
81
  candidates: OnboardingTargetCandidate[]
82
82
  reason: string
@@ -91,6 +91,14 @@ export interface OnboardingSummary {
91
91
  manualFollowUp: string[]
92
92
  }
93
93
 
94
+ export interface OnboardingPatchPlan {
95
+ path: string
96
+ status: 'planned' | 'manual_patch_required'
97
+ reason: string
98
+ snippet: string
99
+ confidence: 'high' | 'medium' | 'low'
100
+ }
101
+
94
102
  export interface OnboardingConfirmation {
95
103
  required: boolean
96
104
  reason?: string
@@ -126,6 +134,16 @@ export interface OnboardingVerification {
126
134
  message: string
127
135
  }
128
136
 
137
+ export interface OnboardingAssistantHandoff {
138
+ framework?: string
139
+ metaFramework?: string
140
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown'
141
+ autoApplied?: string[]
142
+ pendingSteps?: string[]
143
+ assistantPrompt?: string
144
+ patches?: OnboardingPatchPlan[]
145
+ }
146
+
129
147
  export interface ResolvedOnboardingSession {
130
148
  status: OnboardStatus
131
149
  target: OnboardingTargetResolution
@@ -137,6 +155,14 @@ export interface ResolvedOnboardingSession {
137
155
  projectRoot: string
138
156
  selectedIDE?: { ide: string; supported: boolean } | null
139
157
  providerDefault?: string
158
+ framework?: string
159
+ metaFramework?: string
160
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown'
161
+ autoApplied?: string[]
162
+ pendingSteps?: string[]
163
+ assistantPrompt?: string
164
+ patches?: OnboardingPatchPlan[]
165
+ handoff?: OnboardingAssistantHandoff
140
166
  }
141
167
 
142
168
  export interface OnboardCommandResult {
@@ -148,6 +174,14 @@ export interface OnboardCommandResult {
148
174
  verification?: OnboardingVerification
149
175
  result?: OnboardingExecutionResult
150
176
  diagnostics?: OnboardingDiagnostics
177
+ framework?: string
178
+ metaFramework?: string
179
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown'
180
+ autoApplied?: string[]
181
+ pendingSteps?: string[]
182
+ assistantPrompt?: string
183
+ patches?: OnboardingPatchPlan[]
184
+ handoff?: OnboardingAssistantHandoff
151
185
  }
152
186
 
153
187
  /** Machine-readable detection output for skill-first onboarding */
@@ -174,9 +208,16 @@ export interface PlanResult {
174
208
  status: CommandStatus
175
209
  warnings: CommandMessage[]
176
210
  blockers: CommandMessage[]
177
- strategy: 'supported' | 'manual' | 'unsupported'
211
+ strategy: 'supported' | 'guided' | 'manual' | 'unsupported'
178
212
  actions: Array<{
179
- type: 'install_dependency' | 'modify_file' | 'install_extension' | 'manual_step'
213
+ type:
214
+ | 'install_dependency'
215
+ | 'modify_file'
216
+ | 'install_extension'
217
+ | 'manual_step'
218
+ | 'generate_patch_plan'
219
+ | 'generate_file'
220
+ | 'manual_confirmation'
180
221
  target: string
181
222
  description: string
182
223
  }>
@@ -186,6 +227,13 @@ export interface PlanResult {
186
227
  shared: boolean
187
228
  extension: boolean
188
229
  }
230
+ framework?: string
231
+ metaFramework?: string
232
+ routerMode?: 'app' | 'pages' | 'mixed' | 'unknown'
233
+ autoApplied?: string[]
234
+ pendingSteps?: string[]
235
+ assistantPrompt?: string
236
+ patches?: OnboardingPatchPlan[]
189
237
  }
190
238
 
191
239
  /** A single doctor diagnostic check/result */