@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.
- package/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-test.log +10594 -4044
- package/CHANGELOG.md +28 -0
- package/dist/bin.js +36 -2
- package/dist/{chunk-LJOKPCPD.js → chunk-7ABJRH3F.js} +1701 -182
- package/dist/index.d.ts +69 -4
- package/dist/index.js +7 -1
- package/package.json +3 -3
- package/src/bin.ts +49 -1
- package/src/commands/dev-config.ts +109 -0
- package/src/commands/doctor.ts +189 -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 +100 -5
- package/src/commands/onboard.ts +80 -15
- package/src/detect/build-tool.ts +212 -15
- 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 +255 -28
- package/src/onboarding/nextjs-guidance.ts +257 -0
- package/src/onboarding/nuxt-guidance.ts +129 -0
- package/src/onboarding/planner.ts +337 -10
- package/src/onboarding/session.ts +127 -31
- package/src/onboarding/target-resolution.ts +79 -3
- package/src/onboarding/umi-guidance.ts +139 -0
- package/src/types.ts +58 -3
- package/tests/apply.test.ts +553 -0
- package/tests/build-tool.test.ts +199 -0
- package/tests/dev-config.test.ts +73 -0
- package/tests/doctor.test.ts +130 -0
- package/tests/init.test.ts +17 -0
- package/tests/install-wrapper.test.ts +56 -0
- package/tests/instructions.test.ts +10 -6
- package/tests/integration-host-ide.test.ts +20 -0
- package/tests/integration-install.test.ts +193 -0
- package/tests/nextjs-guidance.test.ts +128 -0
- package/tests/nuxt-guidance.test.ts +67 -0
- package/tests/onboard.test.ts +511 -0
- package/tests/plan.test.ts +283 -21
- package/tests/runner-script.test.ts +120 -1
- package/tests/session-resolve.test.ts +116 -0
- package/tests/session.test.ts +120 -0
package/tests/build-tool.test.ts
CHANGED
|
@@ -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
|
+
})
|
package/tests/doctor.test.ts
CHANGED
|
@@ -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
|
})
|
package/tests/init.test.ts
CHANGED
|
@@ -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(
|
|
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.
|
|
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
|
|
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(
|
|
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.
|
|
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
|
|
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
|
})
|