@inspecto-dev/cli 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-test.log +10036 -5601
- package/CHANGELOG.md +8 -0
- package/dist/bin.js +32 -1
- package/dist/{chunk-2MOEVONN.js → chunk-7ABJRH3F.js} +1232 -145
- package/dist/index.d.ts +62 -4
- package/dist/index.js +7 -1
- package/package.json +2 -2
- package/src/bin.ts +45 -0
- package/src/commands/dev-config.ts +109 -0
- package/src/commands/doctor.ts +162 -9
- package/src/commands/init.ts +10 -3
- package/src/commands/integration-automation.ts +2 -0
- package/src/commands/integration-host-ide.ts +18 -15
- package/src/commands/integration-install.ts +1 -1
- package/src/commands/onboard.ts +72 -21
- package/src/detect/build-tool.ts +14 -5
- package/src/detect/framework.ts +3 -0
- package/src/detect/package-manager.ts +1 -1
- package/src/index.ts +1 -0
- package/src/inject/gitignore.ts +13 -2
- package/src/instructions.ts +33 -7
- package/src/onboarding/apply.ts +137 -3
- package/src/onboarding/nextjs-guidance.ts +257 -0
- package/src/onboarding/nuxt-guidance.ts +129 -0
- package/src/onboarding/planner.ts +257 -6
- package/src/onboarding/session.ts +117 -27
- package/src/onboarding/target-resolution.ts +3 -3
- package/src/onboarding/umi-guidance.ts +139 -0
- package/src/types.ts +51 -3
- package/tests/apply.test.ts +319 -0
- package/tests/dev-config.test.ts +73 -0
- package/tests/doctor.test.ts +89 -0
- package/tests/init.test.ts +17 -0
- package/tests/instructions.test.ts +10 -6
- package/tests/integration-host-ide.test.ts +20 -0
- package/tests/integration-install.test.ts +65 -0
- package/tests/nextjs-guidance.test.ts +128 -0
- package/tests/nuxt-guidance.test.ts +67 -0
- package/tests/onboard.test.ts +416 -0
- package/tests/plan.test.ts +181 -21
- package/tests/runner-script.test.ts +120 -1
- package/tests/session-resolve.test.ts +116 -0
- package/tests/session.test.ts +120 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { createNextJsGuidance } from '../src/onboarding/nextjs-guidance.js'
|
|
6
|
+
|
|
7
|
+
const tempDirs: string[] = []
|
|
8
|
+
|
|
9
|
+
async function createTempProject(files: Record<string, string>) {
|
|
10
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'inspecto-nextjs-'))
|
|
11
|
+
tempDirs.push(root)
|
|
12
|
+
|
|
13
|
+
for (const [relativePath, contents] of Object.entries(files)) {
|
|
14
|
+
const filePath = path.join(root, relativePath)
|
|
15
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
16
|
+
await fs.writeFile(filePath, contents)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return root
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('createNextJsGuidance', () => {
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })))
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('creates a high-confidence patch plan for object-export next.config.mjs', async () => {
|
|
28
|
+
const root = await createTempProject({
|
|
29
|
+
'next.config.mjs': `export default {\n reactStrictMode: true,\n}\n`,
|
|
30
|
+
'app/layout.tsx': `export default function RootLayout({ children }) { return <html><body>{children}</body></html> }\n`,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const guidance = createNextJsGuidance(root)
|
|
34
|
+
|
|
35
|
+
expect(guidance.metaFramework).toBe('Next.js')
|
|
36
|
+
expect(guidance.routerMode).toBe('app')
|
|
37
|
+
expect(guidance.patches).toEqual(
|
|
38
|
+
expect.arrayContaining([
|
|
39
|
+
expect.objectContaining({
|
|
40
|
+
path: 'next.config.mjs',
|
|
41
|
+
confidence: 'high',
|
|
42
|
+
reason: 'next_config_object_export',
|
|
43
|
+
}),
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
path: 'app/layout.tsx',
|
|
46
|
+
status: 'manual_patch_required',
|
|
47
|
+
reason: 'next_app_router_mount',
|
|
48
|
+
}),
|
|
49
|
+
]),
|
|
50
|
+
)
|
|
51
|
+
expect(guidance.assistantPrompt).toContain('Next.js')
|
|
52
|
+
expect(guidance.patches[0]?.snippet).toContain('if (dev) {')
|
|
53
|
+
expect(guidance.patches[0]?.snippet).not.toContain('!isServer')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('treats exported nextConfig objects as high-confidence patches', async () => {
|
|
57
|
+
const root = await createTempProject({
|
|
58
|
+
'next.config.ts': `import type { NextConfig } from 'next'\nconst nextConfig: NextConfig = {\n reactStrictMode: true,\n}\n\nexport default nextConfig\n`,
|
|
59
|
+
'src/app/layout.tsx': `export default function RootLayout({ children }) { return <html><body>{children}</body></html> }\n`,
|
|
60
|
+
'package.json': `{\n "scripts": {\n "dev": "next dev"\n }\n}\n`,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const guidance = createNextJsGuidance(root)
|
|
64
|
+
|
|
65
|
+
expect(guidance.patches).toEqual(
|
|
66
|
+
expect.arrayContaining([
|
|
67
|
+
expect.objectContaining({
|
|
68
|
+
path: 'next.config.ts',
|
|
69
|
+
status: 'planned',
|
|
70
|
+
confidence: 'high',
|
|
71
|
+
reason: 'next_config_object_export',
|
|
72
|
+
}),
|
|
73
|
+
]),
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('degrades to a medium-confidence manual patch plan for wrapped next config', async () => {
|
|
78
|
+
const root = await createTempProject({
|
|
79
|
+
'next.config.js': `const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: true })\nmodule.exports = withBundleAnalyzer({ reactStrictMode: true })\n`,
|
|
80
|
+
'pages/_app.tsx': `export default function App({ Component, pageProps }) { return <Component {...pageProps} /> }\n`,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const guidance = createNextJsGuidance(root)
|
|
84
|
+
|
|
85
|
+
expect(guidance.routerMode).toBe('pages')
|
|
86
|
+
expect(guidance.patches[0]).toMatchObject({
|
|
87
|
+
path: 'next.config.js',
|
|
88
|
+
confidence: 'medium',
|
|
89
|
+
status: 'manual_patch_required',
|
|
90
|
+
reason: 'next_config_wrapped_export',
|
|
91
|
+
})
|
|
92
|
+
expect(guidance.patches).toEqual(
|
|
93
|
+
expect.arrayContaining([
|
|
94
|
+
expect.objectContaining({
|
|
95
|
+
path: 'pages/_app.tsx',
|
|
96
|
+
status: 'manual_patch_required',
|
|
97
|
+
reason: 'next_pages_router_mount',
|
|
98
|
+
}),
|
|
99
|
+
]),
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('adds a webpack dev-server follow-up when package.json still runs next dev without --webpack', async () => {
|
|
104
|
+
const root = await createTempProject({
|
|
105
|
+
'next.config.ts': `import type { NextConfig } from 'next'\nconst nextConfig: NextConfig = {\n reactStrictMode: true,\n}\n\nexport default nextConfig\n`,
|
|
106
|
+
'app/layout.tsx': `export default function RootLayout({ children }) { return <html><body>{children}</body></html> }\n`,
|
|
107
|
+
'package.json': `{\n "scripts": {\n "dev": "next dev"\n }\n}\n`,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const guidance = createNextJsGuidance(root)
|
|
111
|
+
|
|
112
|
+
expect(guidance.pendingSteps).toEqual(
|
|
113
|
+
expect.arrayContaining([
|
|
114
|
+
'Update the Next.js dev script to use webpack mode for Inspecto validation.',
|
|
115
|
+
]),
|
|
116
|
+
)
|
|
117
|
+
expect(guidance.patches).toEqual(
|
|
118
|
+
expect.arrayContaining([
|
|
119
|
+
expect.objectContaining({
|
|
120
|
+
path: 'package.json',
|
|
121
|
+
status: 'manual_patch_required',
|
|
122
|
+
reason: 'next_dev_script_requires_webpack',
|
|
123
|
+
}),
|
|
124
|
+
]),
|
|
125
|
+
)
|
|
126
|
+
expect(guidance.assistantPrompt).toContain('Use the generated patches directly')
|
|
127
|
+
})
|
|
128
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { createNuxtGuidance } from '../src/onboarding/nuxt-guidance.js'
|
|
6
|
+
|
|
7
|
+
const tempDirs: string[] = []
|
|
8
|
+
|
|
9
|
+
async function createTempProject(files: Record<string, string>) {
|
|
10
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'inspecto-nuxt-'))
|
|
11
|
+
tempDirs.push(root)
|
|
12
|
+
|
|
13
|
+
for (const [relativePath, contents] of Object.entries(files)) {
|
|
14
|
+
const filePath = path.join(root, relativePath)
|
|
15
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
16
|
+
await fs.writeFile(filePath, contents)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return root
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('createNuxtGuidance', () => {
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })))
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('creates a high-confidence patch plan for object-export nuxt.config.ts', async () => {
|
|
28
|
+
const root = await createTempProject({
|
|
29
|
+
'nuxt.config.ts': `export default defineNuxtConfig({\n devtools: { enabled: true },\n})\n`,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const guidance = createNuxtGuidance(root)
|
|
33
|
+
|
|
34
|
+
expect(guidance.metaFramework).toBe('Nuxt')
|
|
35
|
+
expect(guidance.framework).toBe('vue')
|
|
36
|
+
expect(guidance.patches).toEqual(
|
|
37
|
+
expect.arrayContaining([
|
|
38
|
+
expect.objectContaining({
|
|
39
|
+
path: 'nuxt.config.ts',
|
|
40
|
+
confidence: 'high',
|
|
41
|
+
reason: 'nuxt_config_object_export',
|
|
42
|
+
status: 'planned',
|
|
43
|
+
}),
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
path: 'plugins/inspecto.client.ts',
|
|
46
|
+
reason: 'nuxt_client_plugin_mount',
|
|
47
|
+
status: 'manual_patch_required',
|
|
48
|
+
}),
|
|
49
|
+
]),
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('degrades to a medium-confidence manual patch plan for wrapped nuxt config', async () => {
|
|
54
|
+
const root = await createTempProject({
|
|
55
|
+
'nuxt.config.js': `export default withFoo(defineNuxtConfig({ ssr: true }))\n`,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const guidance = createNuxtGuidance(root)
|
|
59
|
+
|
|
60
|
+
expect(guidance.patches[0]).toMatchObject({
|
|
61
|
+
path: 'nuxt.config.js',
|
|
62
|
+
confidence: 'medium',
|
|
63
|
+
status: 'manual_patch_required',
|
|
64
|
+
reason: 'nuxt_config_wrapped_export',
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
})
|
package/tests/onboard.test.ts
CHANGED
|
@@ -267,6 +267,173 @@ describe('onboard command', () => {
|
|
|
267
267
|
expect(result.verification?.devCommand).toBe('pnpm dev')
|
|
268
268
|
})
|
|
269
269
|
|
|
270
|
+
it('keeps guided Next.js patch metadata in deferred JSON output', async () => {
|
|
271
|
+
const session: ResolvedOnboardingSession = {
|
|
272
|
+
status: 'needs_confirmation',
|
|
273
|
+
target: {
|
|
274
|
+
status: 'guided',
|
|
275
|
+
candidates: [],
|
|
276
|
+
reason: 'Awaiting confirmation before guided onboarding.',
|
|
277
|
+
},
|
|
278
|
+
summary: {
|
|
279
|
+
headline: 'Inspecto can partially onboard /repo, but manual follow-up remains.',
|
|
280
|
+
changes: ['Install the Inspecto runtime packages with pnpm.'],
|
|
281
|
+
risks: [],
|
|
282
|
+
manualFollowUp: [
|
|
283
|
+
'Generate a guided patch plan for the Next.js Inspecto webpack integration.',
|
|
284
|
+
'Complete the remaining client-side Inspecto mount step in your assistant or editor.',
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
confirmation: {
|
|
288
|
+
required: true,
|
|
289
|
+
question:
|
|
290
|
+
'Proceed with Inspecto onboarding using the proposed default target and settings?',
|
|
291
|
+
},
|
|
292
|
+
verification: {
|
|
293
|
+
available: true,
|
|
294
|
+
devCommand: 'pnpm dev',
|
|
295
|
+
message: 'Start the local dev server with `pnpm dev` to verify Inspecto in the browser.',
|
|
296
|
+
},
|
|
297
|
+
context: {
|
|
298
|
+
root: '/repo',
|
|
299
|
+
packageManager: 'pnpm',
|
|
300
|
+
buildTools: { supported: [], unsupported: ['Next.js'] },
|
|
301
|
+
frameworks: { supported: ['react'], unsupported: [] },
|
|
302
|
+
ides: [{ ide: 'vscode', supported: true }],
|
|
303
|
+
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
|
304
|
+
},
|
|
305
|
+
plan: {
|
|
306
|
+
status: 'warning',
|
|
307
|
+
warnings: [],
|
|
308
|
+
blockers: [],
|
|
309
|
+
strategy: 'guided',
|
|
310
|
+
actions: [],
|
|
311
|
+
defaults: {
|
|
312
|
+
provider: 'codex',
|
|
313
|
+
ide: 'vscode',
|
|
314
|
+
shared: false,
|
|
315
|
+
extension: true,
|
|
316
|
+
},
|
|
317
|
+
framework: 'react',
|
|
318
|
+
metaFramework: 'Next.js',
|
|
319
|
+
routerMode: 'app',
|
|
320
|
+
autoApplied: ['dependencies', 'inspecto_settings'],
|
|
321
|
+
pendingSteps: [
|
|
322
|
+
'Review the generated Next.js patch plan for next.config.mjs.',
|
|
323
|
+
'Complete the remaining client-side mount step for your App Router entry.',
|
|
324
|
+
],
|
|
325
|
+
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Next.js project.',
|
|
326
|
+
patches: [
|
|
327
|
+
{
|
|
328
|
+
path: 'next.config.mjs',
|
|
329
|
+
status: 'planned',
|
|
330
|
+
reason: 'next_config_object_export',
|
|
331
|
+
confidence: 'high',
|
|
332
|
+
snippet: '...',
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
path: 'app/layout.tsx',
|
|
336
|
+
status: 'manual_patch_required',
|
|
337
|
+
reason: 'next_app_router_mount',
|
|
338
|
+
confidence: 'medium',
|
|
339
|
+
snippet: '...',
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
projectRoot: '/repo',
|
|
344
|
+
selectedIDE: { ide: 'vscode', supported: true },
|
|
345
|
+
providerDefault: 'codex.cli',
|
|
346
|
+
framework: 'react',
|
|
347
|
+
metaFramework: 'Next.js',
|
|
348
|
+
routerMode: 'app',
|
|
349
|
+
autoApplied: ['dependencies', 'inspecto_settings'],
|
|
350
|
+
pendingSteps: [
|
|
351
|
+
'Review the generated Next.js patch plan for next.config.mjs.',
|
|
352
|
+
'Complete the remaining client-side mount step for your App Router entry.',
|
|
353
|
+
],
|
|
354
|
+
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Next.js project.',
|
|
355
|
+
patches: [
|
|
356
|
+
{
|
|
357
|
+
path: 'next.config.mjs',
|
|
358
|
+
status: 'planned',
|
|
359
|
+
reason: 'next_config_object_export',
|
|
360
|
+
confidence: 'high',
|
|
361
|
+
snippet: '...',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
path: 'app/layout.tsx',
|
|
365
|
+
status: 'manual_patch_required',
|
|
366
|
+
reason: 'next_app_router_mount',
|
|
367
|
+
confidence: 'medium',
|
|
368
|
+
snippet: '...',
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const deferred: OnboardCommandResult = {
|
|
374
|
+
status: 'needs_confirmation',
|
|
375
|
+
target: session.target,
|
|
376
|
+
summary: session.summary,
|
|
377
|
+
confirmation: session.confirmation,
|
|
378
|
+
ideExtension: {
|
|
379
|
+
required: true,
|
|
380
|
+
installed: false,
|
|
381
|
+
manualRequired: true,
|
|
382
|
+
installCommand: 'code --install-extension inspecto.inspecto',
|
|
383
|
+
marketplaceUrl: 'https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto',
|
|
384
|
+
openVsxUrl: 'https://open-vsx.org/extension/inspecto/inspecto',
|
|
385
|
+
},
|
|
386
|
+
verification: session.verification,
|
|
387
|
+
framework: 'react',
|
|
388
|
+
metaFramework: 'Next.js',
|
|
389
|
+
routerMode: 'app',
|
|
390
|
+
autoApplied: ['dependencies', 'inspecto_settings'],
|
|
391
|
+
pendingSteps: [
|
|
392
|
+
'Review the generated Next.js patch plan for next.config.mjs.',
|
|
393
|
+
'Complete the remaining client-side mount step for your App Router entry.',
|
|
394
|
+
],
|
|
395
|
+
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Next.js project.',
|
|
396
|
+
patches: [
|
|
397
|
+
{
|
|
398
|
+
path: 'next.config.mjs',
|
|
399
|
+
status: 'planned',
|
|
400
|
+
reason: 'next_config_object_export',
|
|
401
|
+
confidence: 'high',
|
|
402
|
+
snippet: '...',
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
path: 'app/layout.tsx',
|
|
406
|
+
status: 'manual_patch_required',
|
|
407
|
+
reason: 'next_app_router_mount',
|
|
408
|
+
confidence: 'medium',
|
|
409
|
+
snippet: '...',
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
vi.mocked(resolveOnboardingSession).mockResolvedValue(session)
|
|
415
|
+
vi.mocked(buildDeferredOnboardResult).mockReturnValue(deferred)
|
|
416
|
+
|
|
417
|
+
const result = await onboard({ json: true })
|
|
418
|
+
|
|
419
|
+
expect(result.metaFramework).toBe('Next.js')
|
|
420
|
+
expect(result.routerMode).toBe('app')
|
|
421
|
+
expect(result.handoff?.metaFramework).toBe('Next.js')
|
|
422
|
+
expect(result.handoff?.pendingSteps).toEqual(
|
|
423
|
+
expect.arrayContaining([
|
|
424
|
+
'Review the generated Next.js patch plan for next.config.mjs.',
|
|
425
|
+
'Complete the remaining client-side mount step for your App Router entry.',
|
|
426
|
+
]),
|
|
427
|
+
)
|
|
428
|
+
expect(result.assistantPrompt).toContain('Complete the remaining Inspecto onboarding')
|
|
429
|
+
expect(result.patches).toEqual(
|
|
430
|
+
expect.arrayContaining([
|
|
431
|
+
expect.objectContaining({ path: 'next.config.mjs', reason: 'next_config_object_export' }),
|
|
432
|
+
expect.objectContaining({ path: 'app/layout.tsx', reason: 'next_app_router_mount' }),
|
|
433
|
+
]),
|
|
434
|
+
)
|
|
435
|
+
})
|
|
436
|
+
|
|
270
437
|
it('returns needs_target_selection without applying when target selection is ambiguous', async () => {
|
|
271
438
|
const session: ResolvedOnboardingSession = {
|
|
272
439
|
status: 'needs_target_selection',
|
|
@@ -454,6 +621,255 @@ describe('onboard command', () => {
|
|
|
454
621
|
).toBe(false)
|
|
455
622
|
})
|
|
456
623
|
|
|
624
|
+
it('prints guided patch targets and assistant handoff in text mode', async () => {
|
|
625
|
+
const session: ResolvedOnboardingSession = {
|
|
626
|
+
status: 'partial_success',
|
|
627
|
+
target: {
|
|
628
|
+
status: 'resolved',
|
|
629
|
+
selected: {
|
|
630
|
+
packagePath: '',
|
|
631
|
+
configPath: 'next.config.mjs',
|
|
632
|
+
buildTool: 'webpack',
|
|
633
|
+
frameworks: ['react'],
|
|
634
|
+
automaticInjection: false,
|
|
635
|
+
},
|
|
636
|
+
candidates: [],
|
|
637
|
+
reason: 'Guided onboarding is available.',
|
|
638
|
+
},
|
|
639
|
+
summary: {
|
|
640
|
+
headline: 'Inspecto can partially onboard /repo, but manual follow-up remains.',
|
|
641
|
+
changes: ['Install the Inspecto runtime packages with pnpm.'],
|
|
642
|
+
risks: [],
|
|
643
|
+
manualFollowUp: [
|
|
644
|
+
'Generate a guided patch plan for the Next.js Inspecto webpack integration.',
|
|
645
|
+
'Complete the remaining client-side Inspecto mount step in your assistant or editor.',
|
|
646
|
+
],
|
|
647
|
+
},
|
|
648
|
+
confirmation: { required: false },
|
|
649
|
+
verification: {
|
|
650
|
+
available: true,
|
|
651
|
+
devCommand: 'pnpm dev',
|
|
652
|
+
message: 'Start the local dev server with `pnpm dev` to verify Inspecto in the browser.',
|
|
653
|
+
},
|
|
654
|
+
context: {
|
|
655
|
+
root: '/repo',
|
|
656
|
+
packageManager: 'pnpm',
|
|
657
|
+
buildTools: { supported: [], unsupported: ['Next.js'] },
|
|
658
|
+
frameworks: { supported: ['react'], unsupported: [] },
|
|
659
|
+
ides: [{ ide: 'vscode', supported: true }],
|
|
660
|
+
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
|
661
|
+
},
|
|
662
|
+
plan: {
|
|
663
|
+
status: 'warning',
|
|
664
|
+
warnings: [],
|
|
665
|
+
blockers: [],
|
|
666
|
+
strategy: 'guided',
|
|
667
|
+
actions: [],
|
|
668
|
+
defaults: {
|
|
669
|
+
provider: 'codex',
|
|
670
|
+
ide: 'vscode',
|
|
671
|
+
shared: false,
|
|
672
|
+
extension: true,
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
projectRoot: '/repo',
|
|
676
|
+
selectedIDE: { ide: 'vscode', supported: true },
|
|
677
|
+
providerDefault: 'codex.cli',
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const applied: OnboardCommandResult = {
|
|
681
|
+
status: 'partial_success',
|
|
682
|
+
target: session.target,
|
|
683
|
+
summary: session.summary,
|
|
684
|
+
confirmation: session.confirmation,
|
|
685
|
+
ideExtension: {
|
|
686
|
+
required: true,
|
|
687
|
+
installed: true,
|
|
688
|
+
manualRequired: false,
|
|
689
|
+
installCommand: 'code --install-extension inspecto.inspecto',
|
|
690
|
+
marketplaceUrl: 'https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto',
|
|
691
|
+
openVsxUrl: 'https://open-vsx.org/extension/inspecto/inspecto',
|
|
692
|
+
},
|
|
693
|
+
verification: session.verification,
|
|
694
|
+
diagnostics: {
|
|
695
|
+
warnings: [],
|
|
696
|
+
errors: [],
|
|
697
|
+
nextSteps: [
|
|
698
|
+
'Generate a guided patch plan for the Next.js Inspecto webpack integration.',
|
|
699
|
+
'Complete the remaining client-side Inspecto mount step in your assistant or editor.',
|
|
700
|
+
],
|
|
701
|
+
},
|
|
702
|
+
metaFramework: 'Next.js',
|
|
703
|
+
routerMode: 'app',
|
|
704
|
+
pendingSteps: [
|
|
705
|
+
'Review the generated Next.js patch plan for next.config.mjs.',
|
|
706
|
+
'Complete the remaining client-side mount step for your App Router entry.',
|
|
707
|
+
],
|
|
708
|
+
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Next.js project.',
|
|
709
|
+
patches: [
|
|
710
|
+
{
|
|
711
|
+
path: 'next.config.mjs',
|
|
712
|
+
status: 'planned',
|
|
713
|
+
reason: 'next_config_object_export',
|
|
714
|
+
confidence: 'high',
|
|
715
|
+
snippet: '...',
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
path: 'app/layout.tsx',
|
|
719
|
+
status: 'manual_patch_required',
|
|
720
|
+
reason: 'next_app_router_mount',
|
|
721
|
+
confidence: 'medium',
|
|
722
|
+
snippet: '...',
|
|
723
|
+
},
|
|
724
|
+
],
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
vi.mocked(resolveOnboardingSession).mockResolvedValue(session)
|
|
728
|
+
vi.mocked(applyResolvedOnboardingSession).mockResolvedValue(applied)
|
|
729
|
+
|
|
730
|
+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
731
|
+
|
|
732
|
+
await onboard({ json: false })
|
|
733
|
+
|
|
734
|
+
expect(consoleSpy.mock.calls.some(call => String(call[0]).includes('next.config.mjs'))).toBe(
|
|
735
|
+
true,
|
|
736
|
+
)
|
|
737
|
+
expect(
|
|
738
|
+
consoleSpy.mock.calls.some(call =>
|
|
739
|
+
String(call[0]).includes('Complete the remaining Inspecto onboarding'),
|
|
740
|
+
),
|
|
741
|
+
).toBe(true)
|
|
742
|
+
expect(
|
|
743
|
+
consoleSpy.mock.calls.some(call =>
|
|
744
|
+
String(call[0]).includes(
|
|
745
|
+
'Complete the remaining client-side mount step for your App Router entry.',
|
|
746
|
+
),
|
|
747
|
+
),
|
|
748
|
+
).toBe(true)
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
it('keeps guided Nuxt handoff metadata in applied JSON output', async () => {
|
|
752
|
+
const session: ResolvedOnboardingSession = {
|
|
753
|
+
status: 'partial_success',
|
|
754
|
+
target: {
|
|
755
|
+
status: 'resolved',
|
|
756
|
+
selected: {
|
|
757
|
+
packagePath: '',
|
|
758
|
+
configPath: 'nuxt.config.ts',
|
|
759
|
+
buildTool: 'vite',
|
|
760
|
+
frameworks: ['vue'],
|
|
761
|
+
automaticInjection: false,
|
|
762
|
+
},
|
|
763
|
+
candidates: [],
|
|
764
|
+
reason: 'Guided onboarding is available.',
|
|
765
|
+
},
|
|
766
|
+
summary: {
|
|
767
|
+
headline: 'Inspecto can partially onboard /repo, but manual follow-up remains.',
|
|
768
|
+
changes: ['Install the Inspecto runtime packages with pnpm.'],
|
|
769
|
+
risks: [],
|
|
770
|
+
manualFollowUp: [
|
|
771
|
+
'Generate a guided patch plan for the Nuxt Inspecto Vite integration.',
|
|
772
|
+
'Complete the remaining Nuxt client plugin mount step in your assistant or editor.',
|
|
773
|
+
],
|
|
774
|
+
},
|
|
775
|
+
confirmation: { required: false },
|
|
776
|
+
verification: {
|
|
777
|
+
available: true,
|
|
778
|
+
devCommand: 'pnpm dev',
|
|
779
|
+
message: 'Start the local dev server with `pnpm dev` to verify Inspecto in the browser.',
|
|
780
|
+
},
|
|
781
|
+
context: {
|
|
782
|
+
root: '/repo',
|
|
783
|
+
packageManager: 'pnpm',
|
|
784
|
+
buildTools: { supported: [], unsupported: ['Nuxt'] },
|
|
785
|
+
frameworks: { supported: ['vue'], unsupported: [] },
|
|
786
|
+
ides: [{ ide: 'vscode', supported: true }],
|
|
787
|
+
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
|
788
|
+
},
|
|
789
|
+
plan: {
|
|
790
|
+
status: 'warning',
|
|
791
|
+
warnings: [],
|
|
792
|
+
blockers: [],
|
|
793
|
+
strategy: 'guided',
|
|
794
|
+
actions: [],
|
|
795
|
+
defaults: {
|
|
796
|
+
provider: 'codex',
|
|
797
|
+
ide: 'vscode',
|
|
798
|
+
shared: false,
|
|
799
|
+
extension: true,
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
projectRoot: '/repo',
|
|
803
|
+
selectedIDE: { ide: 'vscode', supported: true },
|
|
804
|
+
providerDefault: 'codex.cli',
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const applied: OnboardCommandResult = {
|
|
808
|
+
status: 'partial_success',
|
|
809
|
+
target: session.target,
|
|
810
|
+
summary: session.summary,
|
|
811
|
+
confirmation: session.confirmation,
|
|
812
|
+
ideExtension: {
|
|
813
|
+
required: true,
|
|
814
|
+
installed: true,
|
|
815
|
+
manualRequired: false,
|
|
816
|
+
installCommand: 'code --install-extension inspecto.inspecto',
|
|
817
|
+
marketplaceUrl: 'https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto',
|
|
818
|
+
openVsxUrl: 'https://open-vsx.org/extension/inspecto/inspecto',
|
|
819
|
+
},
|
|
820
|
+
verification: session.verification,
|
|
821
|
+
diagnostics: {
|
|
822
|
+
warnings: [],
|
|
823
|
+
errors: [],
|
|
824
|
+
nextSteps: [
|
|
825
|
+
'Generate a guided patch plan for the Nuxt Inspecto Vite integration.',
|
|
826
|
+
'Complete the remaining Nuxt client plugin mount step in your assistant or editor.',
|
|
827
|
+
],
|
|
828
|
+
},
|
|
829
|
+
framework: 'vue',
|
|
830
|
+
metaFramework: 'Nuxt',
|
|
831
|
+
autoApplied: ['dependencies', 'inspecto_settings'],
|
|
832
|
+
pendingSteps: [
|
|
833
|
+
'Review the generated Nuxt patch plan for nuxt.config.ts.',
|
|
834
|
+
'Complete the remaining Nuxt client plugin mount step in plugins/inspecto.client.ts.',
|
|
835
|
+
],
|
|
836
|
+
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Nuxt project.',
|
|
837
|
+
patches: [
|
|
838
|
+
{
|
|
839
|
+
path: 'nuxt.config.ts',
|
|
840
|
+
status: 'planned',
|
|
841
|
+
reason: 'nuxt_config_object_export',
|
|
842
|
+
confidence: 'high',
|
|
843
|
+
snippet: '...',
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
path: 'plugins/inspecto.client.ts',
|
|
847
|
+
status: 'manual_patch_required',
|
|
848
|
+
reason: 'nuxt_client_plugin_mount',
|
|
849
|
+
confidence: 'medium',
|
|
850
|
+
snippet: '...',
|
|
851
|
+
},
|
|
852
|
+
],
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
vi.mocked(resolveOnboardingSession).mockResolvedValue(session)
|
|
856
|
+
vi.mocked(applyResolvedOnboardingSession).mockResolvedValue(applied)
|
|
857
|
+
|
|
858
|
+
const result = await onboard({ json: true })
|
|
859
|
+
|
|
860
|
+
expect(result.handoff?.metaFramework).toBe('Nuxt')
|
|
861
|
+
expect(result.handoff?.framework).toBe('vue')
|
|
862
|
+
expect(result.handoff?.patches).toEqual(
|
|
863
|
+
expect.arrayContaining([
|
|
864
|
+
expect.objectContaining({ path: 'nuxt.config.ts', reason: 'nuxt_config_object_export' }),
|
|
865
|
+
expect.objectContaining({
|
|
866
|
+
path: 'plugins/inspecto.client.ts',
|
|
867
|
+
reason: 'nuxt_client_plugin_mount',
|
|
868
|
+
}),
|
|
869
|
+
]),
|
|
870
|
+
)
|
|
871
|
+
})
|
|
872
|
+
|
|
457
873
|
it('prints manual extension guidance only when the IDE extension still needs manual completion', async () => {
|
|
458
874
|
const session: ResolvedOnboardingSession = {
|
|
459
875
|
status: 'success',
|