@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
@@ -1,4 +1,8 @@
1
1
  import { buildOnboardingContext } from './context.js'
2
+ import { getHostIdeLabel, isSupportedHostIde } from '../integrations/capabilities.js'
3
+ import { createNextJsGuidance } from './nextjs-guidance.js'
4
+ import { createNuxtGuidance } from './nuxt-guidance.js'
5
+ import { createUmiGuidance } from './umi-guidance.js'
2
6
  import type {
3
7
  CommandMessage,
4
8
  CommandStatus,
@@ -37,11 +41,19 @@ function supportedIde(context: OnboardingContext): string | undefined {
37
41
  return context.ides.find(ide => ide.supported)?.ide
38
42
  }
39
43
 
44
+ function shouldInstallInspectoExtension(ide?: string): boolean {
45
+ return Boolean(ide && isSupportedHostIde(ide))
46
+ }
47
+
40
48
  function supportedProvider(context: OnboardingContext): string | undefined {
41
49
  return context.providers.find(provider => provider.supported)?.id
42
50
  }
43
51
 
44
52
  function buildToolBlockers(context: OnboardingContext): CommandMessage[] {
53
+ if (isGuidedMetaFrameworkScenario(context)) {
54
+ return []
55
+ }
56
+
45
57
  if (context.buildTools.unsupported.length > 0) {
46
58
  return [
47
59
  message(
@@ -71,6 +83,31 @@ function buildToolBlockers(context: OnboardingContext): CommandMessage[] {
71
83
  return [message('missing-build-tool', 'No supported build tool detected')]
72
84
  }
73
85
 
86
+ function buildToolWarnings(context: OnboardingContext): CommandMessage[] {
87
+ const warnings: CommandMessage[] = []
88
+
89
+ if (context.buildTools.supported.length === 1) {
90
+ const buildTool = context.buildTools.supported[0]!
91
+ if (buildTool.tool === 'rspack' && buildTool.isLegacyRspack) {
92
+ warnings.push(
93
+ message(
94
+ 'legacy-rspack-requires-manual-config',
95
+ `Legacy Rspack detected at ${buildTool.configPath}. Inspecto must use the legacy Rspack plugin entry and manual config steps.`,
96
+ ),
97
+ )
98
+ } else if (buildTool.tool === 'webpack' && buildTool.isLegacyWebpack) {
99
+ warnings.push(
100
+ message(
101
+ 'legacy-webpack4-requires-manual-config',
102
+ `Webpack 4 detected at ${buildTool.configPath}. Inspecto must use the legacy Webpack 4 plugin entry and manual config steps.`,
103
+ ),
104
+ )
105
+ }
106
+ }
107
+
108
+ return warnings
109
+ }
110
+
74
111
  function frameworkBlockers(context: OnboardingContext): CommandMessage[] {
75
112
  if (context.frameworks.supported.length > 0) {
76
113
  return []
@@ -149,6 +186,36 @@ function manualBuildToolActions(context: OnboardingContext): PlanResult['actions
149
186
  ]
150
187
  }
151
188
 
189
+ const buildTool = context.buildTools.supported[0]
190
+ if (buildTool?.tool === 'rspack' && buildTool.isLegacyRspack) {
191
+ return [
192
+ {
193
+ type: 'install_dependency',
194
+ target: '@inspecto-dev/plugin @inspecto-dev/core',
195
+ description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
196
+ },
197
+ {
198
+ type: 'manual_step',
199
+ target: buildTool.configPath,
200
+ description: `Update ${buildTool.configPath} to import \`rspackPlugin\` from \`@inspecto-dev/plugin/legacy/rspack\` and add it to the Rspack plugins array.`,
201
+ },
202
+ ]
203
+ }
204
+ if (buildTool?.tool === 'webpack' && buildTool.isLegacyWebpack) {
205
+ return [
206
+ {
207
+ type: 'install_dependency',
208
+ target: '@inspecto-dev/plugin @inspecto-dev/core',
209
+ description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
210
+ },
211
+ {
212
+ type: 'manual_step',
213
+ target: buildTool.configPath,
214
+ description: `Update ${buildTool.configPath} to import \`webpackPlugin\` from \`@inspecto-dev/plugin/legacy/webpack4\` and add it to the Webpack plugins array.`,
215
+ },
216
+ ]
217
+ }
218
+
152
219
  return [
153
220
  {
154
221
  type: 'manual_step',
@@ -159,6 +226,77 @@ function manualBuildToolActions(context: OnboardingContext): PlanResult['actions
159
226
  ]
160
227
  }
161
228
 
229
+ function hasUnsupportedBuildTool(context: OnboardingContext, buildTool: string): boolean {
230
+ return context.buildTools.unsupported.includes(buildTool)
231
+ }
232
+
233
+ function isGuidedNextJsScenario(context: OnboardingContext): boolean {
234
+ return (
235
+ context.buildTools.supported.length === 0 &&
236
+ hasUnsupportedBuildTool(context, 'Next.js') &&
237
+ context.frameworks.supported.includes('react')
238
+ )
239
+ }
240
+
241
+ function isGuidedNuxtScenario(context: OnboardingContext): boolean {
242
+ return (
243
+ context.buildTools.supported.length === 0 &&
244
+ hasUnsupportedBuildTool(context, 'Nuxt') &&
245
+ context.frameworks.supported.includes('vue')
246
+ )
247
+ }
248
+
249
+ function isGuidedUmiScenario(context: OnboardingContext): boolean {
250
+ return hasUnsupportedBuildTool(context, 'Umi') && context.frameworks.supported.includes('react')
251
+ }
252
+
253
+ function isGuidedMetaFrameworkScenario(context: OnboardingContext): boolean {
254
+ return (
255
+ isGuidedNextJsScenario(context) || isGuidedNuxtScenario(context) || isGuidedUmiScenario(context)
256
+ )
257
+ }
258
+
259
+ function guidedBuildToolWarnings(
260
+ context: OnboardingContext,
261
+ guidedBuildTool: string,
262
+ ): CommandMessage[] {
263
+ return context.buildTools.unsupported
264
+ .filter(buildTool => buildTool !== guidedBuildTool)
265
+ .map(buildTool =>
266
+ message(
267
+ 'additional-unsupported-build-tool',
268
+ `Additional unsupported build tool also detected: ${buildTool}`,
269
+ ),
270
+ )
271
+ }
272
+
273
+ function guidedFrameworkWarnings(context: OnboardingContext, framework: string): CommandMessage[] {
274
+ return context.frameworks.unsupported
275
+ .filter(item => item !== framework)
276
+ .map(item =>
277
+ message(
278
+ 'additional-unsupported-framework',
279
+ `Additional unsupported framework also detected: ${item}`,
280
+ ),
281
+ )
282
+ }
283
+
284
+ function buildGuidedWarnings(
285
+ context: OnboardingContext,
286
+ guidedBuildTool: string,
287
+ guidedFramework: string,
288
+ ): CommandMessage[] {
289
+ return uniqueMessages([
290
+ ...guidedBuildToolWarnings(context, guidedBuildTool),
291
+ ...guidedFrameworkWarnings(context, guidedFramework),
292
+ ...unsupportedEnvironmentWarnings(context),
293
+ ]).filter(
294
+ warning =>
295
+ warning.message !==
296
+ `Unsupported framework(s) also detected: ${context.frameworks.unsupported.join(', ')}`,
297
+ )
298
+ }
299
+
162
300
  function manualFrameworkActions(context: OnboardingContext): PlanResult['actions'] {
163
301
  if (context.frameworks.unsupported.length > 0) {
164
302
  return [
@@ -183,7 +321,10 @@ function manualFrameworkActions(context: OnboardingContext): PlanResult['actions
183
321
 
184
322
  export async function createDetectionResult(root: string): Promise<DetectionResult> {
185
323
  const context = await buildOnboardingContext(root)
186
- const warnings = uniqueMessages([...unsupportedEnvironmentWarnings(context)])
324
+ const warnings = uniqueMessages([
325
+ ...unsupportedEnvironmentWarnings(context),
326
+ ...buildToolWarnings(context),
327
+ ])
187
328
 
188
329
  const buildToolResult = buildToolBlockers(context)
189
330
  const frameworkResult = frameworkBlockers(context)
@@ -209,18 +350,200 @@ export async function createDetectionResult(root: string): Promise<DetectionResu
209
350
  }
210
351
 
211
352
  export function createPlanResult(context: OnboardingContext): PlanResult {
212
- const warnings = uniqueMessages(unsupportedEnvironmentWarnings(context))
353
+ if (isGuidedNextJsScenario(context)) {
354
+ const ide = supportedIde(context)
355
+ const provider = supportedProvider(context)
356
+ const guidance = createNextJsGuidance(context.root)
357
+ const actions: PlanResult['actions'] = [
358
+ {
359
+ type: 'install_dependency',
360
+ target: '@inspecto-dev/plugin @inspecto-dev/core',
361
+ description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
362
+ },
363
+ ]
364
+
365
+ if (shouldInstallInspectoExtension(ide) && ide) {
366
+ actions.push({
367
+ type: 'install_extension',
368
+ target: ide,
369
+ description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
370
+ })
371
+ }
372
+
373
+ actions.push(
374
+ {
375
+ type: 'generate_patch_plan',
376
+ target: 'next.config',
377
+ description: 'Generate a guided patch plan for the Next.js Inspecto webpack integration.',
378
+ },
379
+ {
380
+ type: 'manual_confirmation',
381
+ target: context.root,
382
+ description:
383
+ 'Complete the remaining client-side Inspecto mount step in your assistant or editor.',
384
+ },
385
+ )
386
+
387
+ const defaults: PlanResult['defaults'] = {
388
+ shared: false,
389
+ extension: shouldInstallInspectoExtension(ide),
390
+ ...(provider ? { provider } : {}),
391
+ ...(ide ? { ide } : {}),
392
+ }
393
+
394
+ return {
395
+ status: 'warning',
396
+ warnings: buildGuidedWarnings(context, 'Next.js', 'react'),
397
+ blockers: [],
398
+ strategy: 'guided',
399
+ actions,
400
+ defaults,
401
+ framework: guidance.framework,
402
+ metaFramework: guidance.metaFramework,
403
+ routerMode: guidance.routerMode,
404
+ autoApplied: guidance.autoApplied,
405
+ pendingSteps: guidance.pendingSteps,
406
+ assistantPrompt: guidance.assistantPrompt,
407
+ patches: guidance.patches,
408
+ }
409
+ }
410
+
411
+ if (isGuidedNuxtScenario(context)) {
412
+ const ide = supportedIde(context)
413
+ const provider = supportedProvider(context)
414
+ const guidance = createNuxtGuidance(context.root)
415
+ const actions: PlanResult['actions'] = [
416
+ {
417
+ type: 'install_dependency',
418
+ target: '@inspecto-dev/plugin @inspecto-dev/core',
419
+ description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
420
+ },
421
+ ]
422
+
423
+ if (shouldInstallInspectoExtension(ide) && ide) {
424
+ actions.push({
425
+ type: 'install_extension',
426
+ target: ide,
427
+ description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
428
+ })
429
+ }
430
+
431
+ actions.push(
432
+ {
433
+ type: 'generate_patch_plan',
434
+ target: 'nuxt.config',
435
+ description: 'Generate a guided patch plan for the Nuxt Inspecto Vite integration.',
436
+ },
437
+ {
438
+ type: 'manual_confirmation',
439
+ target: context.root,
440
+ description:
441
+ 'Complete the remaining Nuxt client plugin mount step in your assistant or editor.',
442
+ },
443
+ )
444
+
445
+ const defaults: PlanResult['defaults'] = {
446
+ shared: false,
447
+ extension: shouldInstallInspectoExtension(ide),
448
+ ...(provider ? { provider } : {}),
449
+ ...(ide ? { ide } : {}),
450
+ }
451
+
452
+ return {
453
+ status: 'warning',
454
+ warnings: buildGuidedWarnings(context, 'Nuxt', 'vue'),
455
+ blockers: [],
456
+ strategy: 'guided',
457
+ actions,
458
+ defaults,
459
+ framework: guidance.framework,
460
+ metaFramework: guidance.metaFramework,
461
+ autoApplied: guidance.autoApplied,
462
+ pendingSteps: guidance.pendingSteps,
463
+ assistantPrompt: guidance.assistantPrompt,
464
+ patches: guidance.patches,
465
+ }
466
+ }
467
+
468
+ if (isGuidedUmiScenario(context)) {
469
+ const ide = supportedIde(context)
470
+ const provider = supportedProvider(context)
471
+ const guidance = createUmiGuidance(context.root)
472
+ const actions: PlanResult['actions'] = [
473
+ {
474
+ type: 'install_dependency',
475
+ target: '@inspecto-dev/plugin @inspecto-dev/core',
476
+ description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
477
+ },
478
+ ]
479
+
480
+ if (shouldInstallInspectoExtension(ide) && ide) {
481
+ actions.push({
482
+ type: 'install_extension',
483
+ target: ide,
484
+ description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
485
+ })
486
+ }
487
+
488
+ actions.push({
489
+ type: 'generate_patch_plan',
490
+ target: 'umi.config',
491
+ description: 'Generate a guided patch plan for the Umi Inspecto webpack integration.',
492
+ })
493
+
494
+ const defaults: PlanResult['defaults'] = {
495
+ shared: false,
496
+ extension: shouldInstallInspectoExtension(ide),
497
+ ...(provider ? { provider } : {}),
498
+ ...(ide ? { ide } : {}),
499
+ }
500
+
501
+ return {
502
+ status: 'warning',
503
+ warnings: buildGuidedWarnings(context, 'Umi', 'react'),
504
+ blockers: [],
505
+ strategy: 'guided',
506
+ actions,
507
+ defaults,
508
+ framework: guidance.framework,
509
+ metaFramework: guidance.metaFramework,
510
+ autoApplied: guidance.autoApplied,
511
+ pendingSteps: guidance.pendingSteps,
512
+ assistantPrompt: guidance.assistantPrompt,
513
+ patches: guidance.patches,
514
+ }
515
+ }
516
+
517
+ const warnings = uniqueMessages([
518
+ ...unsupportedEnvironmentWarnings(context),
519
+ ...buildToolWarnings(context),
520
+ ])
213
521
  const blockers = uniqueMessages([...buildToolBlockers(context), ...frameworkBlockers(context)])
214
522
  const actions: PlanResult['actions'] = []
215
523
 
216
524
  let strategy: PlanResult['strategy'] = 'supported'
217
525
 
218
- if (blockers.length > 0) {
526
+ const hasLegacyRspackManualPlan =
527
+ context.buildTools.supported.length === 1 &&
528
+ context.buildTools.supported[0]?.tool === 'rspack' &&
529
+ context.buildTools.supported[0]?.isLegacyRspack
530
+ const hasLegacyWebpackManualPlan =
531
+ context.buildTools.supported.length === 1 &&
532
+ context.buildTools.supported[0]?.tool === 'webpack' &&
533
+ context.buildTools.supported[0]?.isLegacyWebpack
534
+
535
+ if (blockers.length > 0 || hasLegacyRspackManualPlan || hasLegacyWebpackManualPlan) {
219
536
  strategy = 'manual'
220
537
  if (
221
538
  context.buildTools.unsupported.length > 0 ||
222
539
  context.buildTools.supported.length === 0 ||
223
- context.buildTools.supported.length > 1
540
+ context.buildTools.supported.length > 1 ||
541
+ context.buildTools.supported.some(
542
+ buildTool => buildTool.tool === 'rspack' && buildTool.isLegacyRspack,
543
+ ) ||
544
+ context.buildTools.supported.some(
545
+ buildTool => buildTool.tool === 'webpack' && buildTool.isLegacyWebpack,
546
+ )
224
547
  ) {
225
548
  actions.push(...manualBuildToolActions(context))
226
549
  }
@@ -243,18 +566,19 @@ export function createPlanResult(context: OnboardingContext): PlanResult {
243
566
  }
244
567
 
245
568
  const ide = supportedIde(context)
246
- if (ide === 'vscode') {
569
+ if (shouldInstallInspectoExtension(ide) && ide) {
247
570
  actions.push({
248
571
  type: 'install_extension',
249
- target: 'vscode',
250
- description: 'Install the Inspecto VS Code extension.',
572
+ target: ide,
573
+ description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
251
574
  })
252
575
  }
253
576
  }
254
577
 
578
+ const ide = supportedIde(context)
255
579
  const defaults: PlanResult['defaults'] = {
256
580
  shared: false,
257
- extension: supportedIde(context) === 'vscode',
581
+ extension: shouldInstallInspectoExtension(ide),
258
582
  }
259
583
 
260
584
  const provider = supportedProvider(context)
@@ -262,7 +586,6 @@ export function createPlanResult(context: OnboardingContext): PlanResult {
262
586
  defaults.provider = provider
263
587
  }
264
588
 
265
- const ide = supportedIde(context)
266
589
  if (ide) {
267
590
  defaults.ide = ide
268
591
  }
@@ -279,6 +602,10 @@ export function createPlanResult(context: OnboardingContext): PlanResult {
279
602
 
280
603
  export function planManualFollowUp(result: PlanResult): string[] {
281
604
  return result.actions
282
- .filter(action => action.type === 'manual_step')
605
+ .filter(action =>
606
+ ['manual_step', 'generate_patch_plan', 'generate_file', 'manual_confirmation'].includes(
607
+ action.type,
608
+ ),
609
+ )
283
610
  .map(action => action.description)
284
611
  }