@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
@@ -41,4 +41,203 @@ describe('detectBuildTools', () => {
41
41
  const result = await detectBuildTools('/repo')
42
42
  expect(result.supported.some(det => det.tool === 'vite')).toBe(true)
43
43
  })
44
+
45
+ it('marks rspack versions below 0.4.0 as legacy', async () => {
46
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
47
+ if (filePath.includes('package.json')) {
48
+ return { devDependencies: { '@rspack/core': '^0.3.14' } }
49
+ }
50
+ return null
51
+ })
52
+
53
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath =>
54
+ filePath.endsWith('rspack.config.ts'),
55
+ )
56
+
57
+ const result = await detectBuildTools('/repo')
58
+ expect(result.supported).toContainEqual(
59
+ expect.objectContaining({
60
+ tool: 'rspack',
61
+ isLegacyRspack: true,
62
+ }),
63
+ )
64
+ })
65
+
66
+ it('treats ranged rspack versions below 0.4.0 as legacy', async () => {
67
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
68
+ if (filePath.includes('package.json')) {
69
+ return { devDependencies: { '@rspack/core': '>=0.3.0 <0.4.0' } }
70
+ }
71
+ return null
72
+ })
73
+
74
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath =>
75
+ filePath.endsWith('rspack.config.ts'),
76
+ )
77
+
78
+ const result = await detectBuildTools('/repo')
79
+ expect(result.supported).toContainEqual(
80
+ expect.objectContaining({
81
+ tool: 'rspack',
82
+ isLegacyRspack: true,
83
+ }),
84
+ )
85
+ })
86
+
87
+ it('treats webpack 4 tilde ranges as legacy', async () => {
88
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
89
+ if (filePath.includes('package.json')) {
90
+ return { devDependencies: { webpack: '~4.46.0' } }
91
+ }
92
+ return null
93
+ })
94
+
95
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath =>
96
+ filePath.endsWith('webpack.config.js'),
97
+ )
98
+
99
+ const result = await detectBuildTools('/repo')
100
+ expect(result.supported).toContainEqual(
101
+ expect.objectContaining({
102
+ tool: 'webpack',
103
+ isLegacyWebpack: true,
104
+ }),
105
+ )
106
+ })
107
+
108
+ it('resolves the real rspack config path from a script entrypoint', async () => {
109
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
110
+ if (filePath.includes('package.json')) {
111
+ return {
112
+ devDependencies: { '@rspack/core': '^0.3.14' },
113
+ scripts: { dev: 'node ./rspack-scripts/dev/start.js dev' },
114
+ }
115
+ }
116
+ return null
117
+ })
118
+
119
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
120
+ return (
121
+ filePath.endsWith('rspack-scripts/dev/start.js') ||
122
+ filePath.endsWith('rspack-config/rspack.config.dev.ts')
123
+ )
124
+ })
125
+
126
+ vi.mocked(fsUtils.readFile).mockImplementation(async filePath => {
127
+ if (filePath.endsWith('rspack-scripts/dev/start.js')) {
128
+ return "const configPath = path.resolve(__dirname, '../../rspack-config/rspack.config.dev.ts')"
129
+ }
130
+ return null
131
+ })
132
+
133
+ const result = await detectBuildTools('/repo', ['finder'])
134
+ expect(result.supported).toContainEqual(
135
+ expect.objectContaining({
136
+ tool: 'rspack',
137
+ configPath: 'finder/rspack-config/rspack.config.dev.ts',
138
+ isLegacyRspack: true,
139
+ }),
140
+ )
141
+ })
142
+
143
+ it('resolves the real rspack config path from rspack serve -c inside a script wrapper', async () => {
144
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
145
+ if (filePath.includes('package.json')) {
146
+ return {
147
+ devDependencies: { '@rspack/core': '^0.3.14' },
148
+ scripts: { dev: 'node ./rspack-scripts/dev/start.js dev' },
149
+ }
150
+ }
151
+ return null
152
+ })
153
+
154
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
155
+ return (
156
+ filePath.endsWith('rspack-scripts/dev/start.js') ||
157
+ filePath.endsWith('rspack-config/rspack.config.dev.ts')
158
+ )
159
+ })
160
+
161
+ vi.mocked(fsUtils.readFile).mockImplementation(async filePath => {
162
+ if (filePath.endsWith('rspack-scripts/dev/start.js')) {
163
+ return 'const cli = `NODE_ENV=development rspack serve -c ./rspack-config/rspack.config.dev.ts`'
164
+ }
165
+ return null
166
+ })
167
+
168
+ const result = await detectBuildTools('/repo', ['finder'])
169
+ expect(result.supported).toContainEqual(
170
+ expect.objectContaining({
171
+ tool: 'rspack',
172
+ configPath: 'finder/rspack-config/rspack.config.dev.ts',
173
+ isLegacyRspack: true,
174
+ }),
175
+ )
176
+ })
177
+
178
+ it('prefers the dev webpack script and resolves a shared webpack base config', async () => {
179
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
180
+ if (filePath.includes('package.json')) {
181
+ return {
182
+ devDependencies: { webpack: '^4.46.0', 'webpack-cli': '^3.0.8' },
183
+ scripts: {
184
+ dll_dev: 'webpack --config webpack.dll.config.js',
185
+ start:
186
+ 'node ./node_modules/.bin/webpack-dev-server --hot --inline --progress --config webpack.config.esbuild.js',
187
+ prod: 'node ./node_modules/.bin/webpack --config webpack.config.prod.js',
188
+ },
189
+ }
190
+ }
191
+ return null
192
+ })
193
+
194
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
195
+ return (
196
+ filePath.endsWith('webpack.config.esbuild.js') ||
197
+ filePath.endsWith('webpack.config.common.js') ||
198
+ filePath.endsWith('webpack.dll.config.js')
199
+ )
200
+ })
201
+
202
+ vi.mocked(fsUtils.readFile).mockImplementation(async filePath => {
203
+ if (filePath.endsWith('webpack.config.esbuild.js')) {
204
+ return "const configPath = './webpack.config.common.js'; const devConfig = require(configPath);"
205
+ }
206
+ return null
207
+ })
208
+
209
+ const result = await detectBuildTools('/repo')
210
+ expect(result.supported).toContainEqual(
211
+ expect.objectContaining({
212
+ tool: 'webpack',
213
+ configPath: 'webpack.config.common.js',
214
+ isLegacyWebpack: true,
215
+ }),
216
+ )
217
+ })
218
+
219
+ it('detects webpack.config.common.js without relying on package scripts', async () => {
220
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
221
+ if (filePath.includes('package.json')) {
222
+ return {
223
+ devDependencies: { webpack: '^4.46.0', 'webpack-cli': '^3.0.8' },
224
+ scripts: {},
225
+ }
226
+ }
227
+ return null
228
+ })
229
+
230
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
231
+ return filePath.endsWith('webpack.config.common.js')
232
+ })
233
+
234
+ const result = await detectBuildTools('/repo')
235
+ expect(result.supported).toContainEqual(
236
+ expect.objectContaining({
237
+ tool: 'webpack',
238
+ configPath: 'webpack.config.common.js',
239
+ isLegacyWebpack: true,
240
+ }),
241
+ )
242
+ })
44
243
  })
@@ -0,0 +1,73 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import * as fsUtils from '../src/utils/fs.js'
3
+ import * as gitignoreUtils from '../src/inject/gitignore.js'
4
+ import { devLink, devStatus, devUnlink } from '../src/commands/dev-config.js'
5
+
6
+ vi.mock('../src/utils/fs.js', () => ({
7
+ exists: vi.fn(),
8
+ readJSON: vi.fn(),
9
+ removeFile: vi.fn(),
10
+ writeJSON: vi.fn(),
11
+ }))
12
+
13
+ vi.mock('../src/inject/gitignore.js', () => ({
14
+ updateGitignore: vi.fn(),
15
+ }))
16
+
17
+ describe('dev config commands', () => {
18
+ beforeEach(() => {
19
+ vi.resetAllMocks()
20
+ vi.spyOn(process, 'cwd').mockReturnValue('/repo')
21
+ })
22
+
23
+ it('writes cliBin into .inspecto/dev.json', async () => {
24
+ vi.mocked(fsUtils.readJSON).mockResolvedValue(null)
25
+
26
+ const result = await devLink({ cliBin: '/tmp/inspecto/bin.js', json: true })
27
+
28
+ expect(gitignoreUtils.updateGitignore).toHaveBeenCalledWith('/repo', false, false, true)
29
+ expect(fsUtils.writeJSON).toHaveBeenCalledWith('/repo/.inspecto/dev.json', {
30
+ cliBin: '/tmp/inspecto/bin.js',
31
+ })
32
+ expect(result.config).toEqual({ cliBin: '/tmp/inspecto/bin.js' })
33
+ })
34
+
35
+ it('writes devRepo into .inspecto/dev.json', async () => {
36
+ vi.mocked(fsUtils.readJSON).mockResolvedValue({ cliBin: '/tmp/old.js' })
37
+
38
+ const result = await devLink({ devRepo: '/tmp/inspecto', json: true })
39
+
40
+ expect(gitignoreUtils.updateGitignore).toHaveBeenCalledWith('/repo', false, false, true)
41
+ expect(fsUtils.writeJSON).toHaveBeenCalledWith('/repo/.inspecto/dev.json', {
42
+ cliBin: '/tmp/old.js',
43
+ devRepo: '/tmp/inspecto',
44
+ })
45
+ expect(result.config).toEqual({
46
+ cliBin: '/tmp/old.js',
47
+ devRepo: '/tmp/inspecto',
48
+ })
49
+ })
50
+
51
+ it('returns current dev config status', async () => {
52
+ vi.mocked(fsUtils.exists).mockResolvedValue(true)
53
+ vi.mocked(fsUtils.readJSON).mockResolvedValue({
54
+ cliBin: '/tmp/inspecto/bin.js',
55
+ devRepo: '/tmp/inspecto',
56
+ })
57
+
58
+ const result = await devStatus(true)
59
+
60
+ expect(result.status).toBe('ok')
61
+ expect(result.config).toEqual({
62
+ cliBin: '/tmp/inspecto/bin.js',
63
+ devRepo: '/tmp/inspecto',
64
+ })
65
+ })
66
+
67
+ it('clears the dev config on unlink', async () => {
68
+ const result = await devUnlink(true)
69
+
70
+ expect(fsUtils.removeFile).toHaveBeenCalledWith('/repo/.inspecto/dev.json')
71
+ expect(result.status).toBe('ok')
72
+ })
73
+ })
@@ -200,6 +200,47 @@ describe('doctor command', () => {
200
200
  )
201
201
  })
202
202
 
203
+ it('warns when configured ide and detected ide differ, and explains that config wins', async () => {
204
+ mockFailingInstall()
205
+ vi.mocked(fsUtils.exists).mockImplementation(async (filePath: string) => {
206
+ const existingPaths = new Set([
207
+ '/repo/package.json',
208
+ '/repo/vite.config.ts',
209
+ '/repo/node_modules/@inspecto-dev/plugin',
210
+ '/repo/.inspecto/settings.local.json',
211
+ '/repo/.gitignore',
212
+ ])
213
+ return existingPaths.has(filePath)
214
+ })
215
+ vi.mocked(fsUtils.readJSON).mockImplementation(async (filePath: string) => {
216
+ if (filePath === '/repo/node_modules/@inspecto-dev/plugin/package.json') {
217
+ return { version: '1.2.3' }
218
+ }
219
+ if (filePath === '/repo/.inspecto/settings.local.json') {
220
+ return { ide: 'trae-cn', 'provider.default': 'coco.cli' }
221
+ }
222
+ return null
223
+ })
224
+ vi.mocked(ide.detectIDE).mockResolvedValue({
225
+ detected: [{ ide: 'trae', supported: true }],
226
+ })
227
+
228
+ const result = await collectDoctorResult('/repo')
229
+
230
+ expect(result.warnings).toEqual(
231
+ expect.arrayContaining([
232
+ expect.objectContaining({
233
+ code: 'settings-ide-mismatch',
234
+ message:
235
+ '.inspecto/settings.local.json sets ide=trae-cn, but the current environment looks like trae. Inspecto will use the configured IDE from settings.local.json.',
236
+ hints: [
237
+ 'Update .inspecto/settings.local.json if you want Inspecto to target the currently detected IDE instead.',
238
+ ],
239
+ }),
240
+ ]),
241
+ )
242
+ })
243
+
203
244
  it('preserves the human-readable doctor output in text mode', async () => {
204
245
  mockFailingInstall()
205
246
 
@@ -221,4 +262,93 @@ describe('doctor command', () => {
221
262
  expect(output).not.toContain('"status"')
222
263
  expect(output).not.toContain('"summary"')
223
264
  })
265
+
266
+ it('reports Next.js as guided onboarding instead of unsupported', async () => {
267
+ vi.mocked(fsUtils.exists).mockImplementation(async (filePath: string) => {
268
+ const existingPaths = new Set([
269
+ '/repo/package.json',
270
+ '/repo/node_modules/@inspecto-dev/plugin',
271
+ '/repo/next.config.ts',
272
+ '/repo/.inspecto/settings.local.json',
273
+ '/repo/.gitignore',
274
+ ])
275
+ return existingPaths.has(filePath)
276
+ })
277
+ vi.mocked(fsUtils.readJSON).mockImplementation(async (filePath: string) => {
278
+ if (filePath === '/repo/package.json') {
279
+ return { scripts: { dev: 'next dev --webpack' } }
280
+ }
281
+ if (filePath === '/repo/node_modules/@inspecto-dev/plugin/package.json') {
282
+ return { version: '1.2.3' }
283
+ }
284
+ if (filePath === '/repo/.inspecto/settings.local.json') {
285
+ return { ide: 'trae', 'provider.default': 'coco.cli' }
286
+ }
287
+ return null
288
+ })
289
+ vi.mocked(fsUtils.readFile).mockImplementation(async filePath => {
290
+ if (filePath === '/repo/next.config.ts') {
291
+ return "import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'\nexport default {}"
292
+ }
293
+ if (filePath === '/repo/.gitignore') {
294
+ return '.inspecto/install.lock\n.inspecto/dev.json\n'
295
+ }
296
+ return null
297
+ })
298
+ vi.mocked(packageManager.detectPackageManager).mockResolvedValue('pnpm')
299
+ vi.mocked(packageManager.getInstallCommand).mockReturnValue('pnpm add -D @inspecto-dev/plugin')
300
+ vi.mocked(ide.detectIDE).mockResolvedValue({
301
+ detected: [{ ide: 'trae', supported: true }],
302
+ })
303
+ vi.mocked(framework.detectFrameworks).mockResolvedValue({
304
+ supported: ['react'],
305
+ unsupported: [],
306
+ })
307
+ vi.mocked(provider.detectProviders).mockResolvedValue({
308
+ detected: [{ label: 'Trae CLI (Coco)', providerModes: ['cli'] }],
309
+ })
310
+ vi.mocked(buildTool.detectBuildTools).mockResolvedValue({
311
+ supported: [],
312
+ unsupported: ['Next.js'],
313
+ })
314
+ vi.mocked(extension.isExtensionInstalled).mockResolvedValue(true)
315
+
316
+ const result = await collectDoctorResult('/repo')
317
+
318
+ expect(result.status).toBe('warning')
319
+ expect(result.errors).toEqual([])
320
+ expect(result.warnings).toEqual(
321
+ expect.arrayContaining([
322
+ expect.objectContaining({
323
+ code: 'build-tool-guided',
324
+ message: 'Build tool: Next.js (guided onboarding available)',
325
+ hints: expect.arrayContaining([
326
+ 'Run `inspecto onboard --json` to generate the remaining patch plan and assistant handoff.',
327
+ expect.stringContaining('Review the generated Next.js patch plan'),
328
+ ]),
329
+ details: expect.objectContaining({
330
+ metaFrameworks: ['Next.js'],
331
+ patchCount: expect.any(Number),
332
+ pendingSteps: expect.arrayContaining([
333
+ expect.stringContaining('Review the generated Next.js patch plan'),
334
+ ]),
335
+ assistantPrompt: expect.stringContaining(
336
+ 'Complete the remaining Inspecto onboarding for this Next.js project.',
337
+ ),
338
+ }),
339
+ }),
340
+ ]),
341
+ )
342
+ expect(result.warnings).toEqual(
343
+ expect.not.arrayContaining([expect.objectContaining({ code: 'build-tool-unsupported' })]),
344
+ )
345
+ expect(result.checks).toEqual(
346
+ expect.arrayContaining([
347
+ expect.objectContaining({
348
+ code: 'guided-config-patch-pending',
349
+ message: 'Guided config patch still needs review in next.config.js',
350
+ }),
351
+ ]),
352
+ )
353
+ })
224
354
  })
@@ -244,6 +244,23 @@ describe('init in monorepo roots', () => {
244
244
  expect(instructionUtils.printNuxtManualInstructions).not.toHaveBeenCalled()
245
245
  })
246
246
 
247
+ it('prints detailed Nuxt guided instructions when Nuxt is detected', async () => {
248
+ vi.mocked(buildToolUtils.detectBuildTools).mockResolvedValue({
249
+ supported: [],
250
+ unsupported: ['Nuxt'],
251
+ })
252
+
253
+ await init({
254
+ shared: false,
255
+ skipInstall: true,
256
+ dryRun: true,
257
+ noExtension: true,
258
+ force: false,
259
+ })
260
+
261
+ expect(instructionUtils.printNuxtManualInstructions).toHaveBeenCalled()
262
+ })
263
+
247
264
  it('preserves detected preferred mode for an explicit provider override', async () => {
248
265
  vi.mocked(buildToolUtils.detectBuildTools).mockResolvedValue({
249
266
  supported: [
@@ -111,4 +111,60 @@ describe('assistant integration bootstrap wrapper', () => {
111
111
  'project',
112
112
  ])
113
113
  })
114
+
115
+ it('installs coco raw assets into the trae skill directory', async () => {
116
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'inspecto-wrapper-'))
117
+ tempDirs.push(tempRoot)
118
+
119
+ const fakeBin = path.join(tempRoot, 'bin')
120
+ await fs.mkdir(fakeBin, { recursive: true })
121
+
122
+ await fs.writeFile(path.join(fakeBin, 'npx'), '#!/usr/bin/env bash\nexit 127\n', 'utf8')
123
+ await fs.chmod(path.join(fakeBin, 'npx'), 0o755)
124
+
125
+ await fs.writeFile(
126
+ path.join(fakeBin, 'curl'),
127
+ [
128
+ '#!/usr/bin/env bash',
129
+ 'set -euo pipefail',
130
+ 'out=""',
131
+ 'while [[ $# -gt 0 ]]; do',
132
+ ' case "$1" in',
133
+ ' -o)',
134
+ ' out="$2"',
135
+ ' shift 2',
136
+ ' ;;',
137
+ ' *)',
138
+ ' shift',
139
+ ' ;;',
140
+ ' esac',
141
+ 'done',
142
+ 'printf "downloaded from wrapper\n" > "$out"',
143
+ ].join('\n'),
144
+ 'utf8',
145
+ )
146
+ await fs.chmod(path.join(fakeBin, 'curl'), 0o755)
147
+
148
+ const scriptPath = path.resolve(__dirname, '../../../scripts/install.sh')
149
+
150
+ await execFileAsync('bash', [scriptPath, 'coco'], {
151
+ cwd: tempRoot,
152
+ env: {
153
+ ...process.env,
154
+ PATH: `${fakeBin}:${process.env.PATH ?? ''}`,
155
+ },
156
+ })
157
+
158
+ const installed = await fs.readFile(
159
+ path.join(tempRoot, '.trae/skills/inspecto-onboarding/SKILL.md'),
160
+ 'utf8',
161
+ )
162
+ expect(installed).toBe('downloaded from wrapper\n')
163
+
164
+ const launcher = await fs.readFile(
165
+ path.join(tempRoot, '.trae/skills/inspecto-onboarding/scripts/run-inspecto.sh'),
166
+ 'utf8',
167
+ )
168
+ expect(launcher).toBe('downloaded from wrapper\n')
169
+ })
114
170
  })
@@ -19,7 +19,9 @@ describe('manual framework instructions', () => {
19
19
  printNextJsManualInstructions()
20
20
 
21
21
  expect(log.blank).toHaveBeenCalled()
22
- expect(log.hint).toHaveBeenCalledWith('Next.js requires manual setup in the current version.')
22
+ expect(log.hint).toHaveBeenCalledWith(
23
+ 'Next.js supports guided setup in the current version. Inspecto can prepare the config patch, but the client-side mount step still needs review.',
24
+ )
23
25
  expect(log.hint).toHaveBeenCalledWith(
24
26
  '1. Update `next.config.mjs` to register the Inspecto webpack plugin:',
25
27
  )
@@ -30,10 +32,10 @@ describe('manual framework instructions', () => {
30
32
  ]),
31
33
  )
32
34
  expect(log.hint).toHaveBeenCalledWith(
33
- '2. Initialize `@inspecto-dev/core` from a client component such as `app/layout.tsx` or `pages/_app.tsx`:',
35
+ '2. Complete the remaining client-side mount step in `app/layout.tsx` or `pages/_app.tsx`:',
34
36
  )
35
37
  expect(log.hint).toHaveBeenCalledWith(
36
- '3. Restart your Next.js dev server after updating the config.',
38
+ '3. Restart your Next.js dev server after applying the guided patches.',
37
39
  )
38
40
  })
39
41
 
@@ -41,7 +43,9 @@ describe('manual framework instructions', () => {
41
43
  printNuxtManualInstructions()
42
44
 
43
45
  expect(log.blank).toHaveBeenCalled()
44
- expect(log.hint).toHaveBeenCalledWith('Nuxt requires manual setup in the current version.')
46
+ expect(log.hint).toHaveBeenCalledWith(
47
+ 'Nuxt supports guided setup in the current version. Inspecto can prepare the config patch, but the client plugin mount step still needs review.',
48
+ )
45
49
  expect(log.hint).toHaveBeenCalledWith(
46
50
  '1. Update `nuxt.config.ts` to register the Inspecto Vite plugin:',
47
51
  )
@@ -52,10 +56,10 @@ describe('manual framework instructions', () => {
52
56
  ]),
53
57
  )
54
58
  expect(log.hint).toHaveBeenCalledWith(
55
- '2. Create `plugins/inspecto.client.ts` to mount `@inspecto-dev/core` in development:',
59
+ '2. Complete the remaining client plugin mount step in `plugins/inspecto.client.ts`:',
56
60
  )
57
61
  expect(log.hint).toHaveBeenCalledWith(
58
- '3. Restart your Nuxt dev server after updating the config.',
62
+ '3. Restart your Nuxt dev server after applying the guided patches.',
59
63
  )
60
64
  })
61
65
  })
@@ -169,4 +169,24 @@ describe('resolveIntegrationHostIde', () => {
169
169
  candidates: ['cursor', 'vscode'],
170
170
  })
171
171
  })
172
+
173
+ it('ignores project artifacts when requested by integration install flows', async () => {
174
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
175
+ return filePath === '/repo/.trae'
176
+ })
177
+
178
+ const { resolveIntegrationHostIde } = await import('../src/commands/integration-host-ide.js')
179
+
180
+ await expect(
181
+ resolveIntegrationHostIde({
182
+ cwd: '/repo',
183
+ ignoreProjectArtifacts: true,
184
+ }),
185
+ ).resolves.toMatchObject({
186
+ ide: null,
187
+ confidence: 'low',
188
+ source: 'none',
189
+ candidates: [],
190
+ })
191
+ })
172
192
  })