@inspecto-dev/cli 0.2.0-alpha.0 → 0.2.0-alpha.2
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 +20 -19
- package/.turbo/turbo-test.log +5 -5
- package/CHANGELOG.md +22 -0
- package/README.md +6 -6
- package/dist/bin.js +50 -68
- package/dist/{chunk-4RR7PTRN.js → chunk-V57BJXGZ.js} +615 -435
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +3 -2
- package/src/bin.ts +84 -70
- package/src/commands/doctor.ts +41 -28
- package/src/commands/init.ts +106 -226
- package/src/commands/teardown.ts +13 -23
- package/src/detect/build-tool.ts +89 -21
- package/src/detect/ide.ts +42 -21
- package/src/detect/package-manager.ts +10 -3
- package/src/detect/provider.ts +151 -0
- package/src/inject/ast-injector.ts +70 -231
- package/src/inject/extension.ts +49 -34
- package/src/inject/gitignore.ts +1 -1
- package/src/inject/strategies/esbuild.ts +35 -0
- package/src/inject/strategies/index.ts +16 -0
- package/src/inject/strategies/rollup.ts +35 -0
- package/src/inject/strategies/rsbuild.ts +29 -0
- package/src/inject/strategies/rspack.ts +34 -0
- package/src/inject/strategies/types.ts +35 -0
- package/src/inject/strategies/vite.ts +30 -0
- package/src/inject/strategies/webpack.ts +36 -0
- package/src/instructions.ts +55 -0
- package/src/prompts.ts +115 -0
- package/src/types.ts +4 -1
- package/tests/ide.test.ts +3 -3
- package/src/detect/ai-tool.ts +0 -127
package/src/inject/extension.ts
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
// Level 4: Print manual installation instructions
|
|
10
10
|
// ============================================================
|
|
11
11
|
|
|
12
|
-
import path from 'node:path'
|
|
13
12
|
import { which, run, shell } from '../utils/exec.js'
|
|
14
13
|
import { exists } from '../utils/fs.js'
|
|
15
14
|
import { log } from '../utils/logger.js'
|
|
@@ -67,51 +66,67 @@ async function tryOpenURI(uri: string): Promise<boolean> {
|
|
|
67
66
|
/**
|
|
68
67
|
* Attempt to install the VS Code extension using waterfall degradation.
|
|
69
68
|
*/
|
|
70
|
-
export async function installExtension(dryRun: boolean): Promise<Mutation | null> {
|
|
69
|
+
export async function installExtension(dryRun: boolean, ide?: string): Promise<Mutation | null> {
|
|
71
70
|
if (dryRun) {
|
|
72
71
|
log.dryRun('Would attempt to install VS Code extension')
|
|
73
72
|
return null
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
const isVSCode = !ide || ide === 'vscode'
|
|
76
|
+
|
|
77
|
+
if (isVSCode) {
|
|
78
|
+
// Level 1: Direct `code` command
|
|
79
|
+
if (await which('code')) {
|
|
80
|
+
try {
|
|
81
|
+
await run('code', ['--install-extension', EXTENSION_ID])
|
|
82
|
+
log.success('VS Code extension installed via CLI')
|
|
83
|
+
return { type: 'extension_installed', id: EXTENSION_ID }
|
|
84
|
+
} catch {
|
|
85
|
+
// Fall through to next level
|
|
86
|
+
}
|
|
84
87
|
}
|
|
85
|
-
}
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
// Level 2: Find VS Code binary at known paths
|
|
90
|
+
const codePath = await findVSCodeBinary()
|
|
91
|
+
if (codePath) {
|
|
92
|
+
try {
|
|
93
|
+
await run(codePath, ['--install-extension', EXTENSION_ID])
|
|
94
|
+
log.success('VS Code extension installed via binary path')
|
|
95
|
+
log.info(
|
|
96
|
+
'Tip: Add "code" to your PATH to help Inspecto detect other AI tools in the future',
|
|
97
|
+
)
|
|
98
|
+
return { type: 'extension_installed', id: EXTENSION_ID }
|
|
99
|
+
} catch {
|
|
100
|
+
// Fall through to next level
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Level 3: URI scheme
|
|
105
|
+
const uri = `vscode:extension/${EXTENSION_ID}`
|
|
106
|
+
if (await tryOpenURI(uri)) {
|
|
107
|
+
log.warn('Opened extension page in VS Code')
|
|
108
|
+
log.hint('Please click "Install" in the opened VS Code window to complete setup.')
|
|
109
|
+
return { type: 'extension_installed', id: EXTENSION_ID, manual_action_required: true }
|
|
97
110
|
}
|
|
98
|
-
}
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
log.
|
|
104
|
-
log.hint('
|
|
105
|
-
|
|
112
|
+
// Level 4: Manual fallback
|
|
113
|
+
log.warn('Could not auto-install VS Code extension')
|
|
114
|
+
log.hint('Please install it manually to enable Inspector features:')
|
|
115
|
+
log.hint(' 1. Open VS Code')
|
|
116
|
+
log.hint(' 2. Press Ctrl+Shift+X (or Cmd+Shift+X)')
|
|
117
|
+
log.hint(' 3. Search for "Inspecto"')
|
|
118
|
+
log.hint(` Or visit: https://marketplace.visualstudio.com/items?itemName=${EXTENSION_ID}`)
|
|
119
|
+
return null
|
|
106
120
|
}
|
|
107
121
|
|
|
108
|
-
//
|
|
109
|
-
log.warn(
|
|
122
|
+
// Other IDEs: Prompt to install via VSIX
|
|
123
|
+
log.warn(`Could not auto-install extension for ${ide}`)
|
|
110
124
|
log.hint('Please install it manually to enable Inspector features:')
|
|
111
|
-
log.hint(' 1.
|
|
112
|
-
log.hint(
|
|
113
|
-
log.hint(' 3.
|
|
114
|
-
log.hint(
|
|
125
|
+
log.hint(' 1. Download the latest .vsix file from Inspecto releases')
|
|
126
|
+
log.hint(` 2. Open ${ide}`)
|
|
127
|
+
log.hint(' 3. Open the Command Palette (Ctrl+Shift+P or Cmd+Shift+P)')
|
|
128
|
+
log.hint(' 4. Type and select "Extensions: Install from VSIX..."')
|
|
129
|
+
log.hint(' 5. Select the downloaded .vsix file')
|
|
115
130
|
return null
|
|
116
131
|
}
|
|
117
132
|
|
package/src/inject/gitignore.ts
CHANGED
|
@@ -34,7 +34,7 @@ export async function updateGitignore(
|
|
|
34
34
|
if (!dryRun) {
|
|
35
35
|
await writeFile(gitignorePath, content)
|
|
36
36
|
}
|
|
37
|
-
log.success('Updated .gitignore: .inspecto/
|
|
37
|
+
log.success('Updated .gitignore: .inspecto/ is no longer fully ignored')
|
|
38
38
|
return
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { InjectStrategy, InjectOptions } from './types.js'
|
|
2
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
export class EsbuildStrategy implements InjectStrategy {
|
|
5
|
+
name = 'esbuild'
|
|
6
|
+
|
|
7
|
+
supports(tool: BuildTool): boolean {
|
|
8
|
+
return tool === 'esbuild'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
inject(options: InjectOptions): void {
|
|
12
|
+
throw new Error('Esbuild requires manual plugin configuration')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[] {
|
|
16
|
+
return [
|
|
17
|
+
`1. Update your esbuild config (${detection.configPath}):`,
|
|
18
|
+
`import { esbuildPlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
19
|
+
'',
|
|
20
|
+
'// Add to your plugins array:',
|
|
21
|
+
`plugins: [`,
|
|
22
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
23
|
+
` ...otherPlugins`,
|
|
24
|
+
`].filter(Boolean)`,
|
|
25
|
+
'',
|
|
26
|
+
'2. Initialize the client in your app entry (e.g., main.js / index.js):',
|
|
27
|
+
`import { mountInspector } from '@inspecto-dev/core'`,
|
|
28
|
+
'',
|
|
29
|
+
'// Call this before your app renders',
|
|
30
|
+
`if (process.env.NODE_ENV !== 'production') {`,
|
|
31
|
+
` mountInspector()`,
|
|
32
|
+
`}`,
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ViteStrategy } from './vite.js'
|
|
2
|
+
import { WebpackStrategy } from './webpack.js'
|
|
3
|
+
import { RspackStrategy } from './rspack.js'
|
|
4
|
+
import { RsbuildStrategy } from './rsbuild.js'
|
|
5
|
+
import { EsbuildStrategy } from './esbuild.js'
|
|
6
|
+
import { RollupStrategy } from './rollup.js'
|
|
7
|
+
import type { InjectStrategy } from './types.js'
|
|
8
|
+
|
|
9
|
+
export const STRATEGIES: InjectStrategy[] = [
|
|
10
|
+
new ViteStrategy(),
|
|
11
|
+
new WebpackStrategy(),
|
|
12
|
+
new RspackStrategy(),
|
|
13
|
+
new RsbuildStrategy(),
|
|
14
|
+
new EsbuildStrategy(),
|
|
15
|
+
new RollupStrategy(),
|
|
16
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { InjectStrategy, InjectOptions } from './types.js'
|
|
2
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
export class RollupStrategy implements InjectStrategy {
|
|
5
|
+
name = 'Rollup'
|
|
6
|
+
|
|
7
|
+
supports(tool: BuildTool): boolean {
|
|
8
|
+
return tool === 'rollup'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
inject(options: InjectOptions): void {
|
|
12
|
+
throw new Error('Rollup requires manual plugin configuration')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[] {
|
|
16
|
+
return [
|
|
17
|
+
`1. Update your rollup config (${detection.configPath}):`,
|
|
18
|
+
`import { rollupPlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
19
|
+
'',
|
|
20
|
+
'// Add to your plugins array:',
|
|
21
|
+
`plugins: [`,
|
|
22
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
23
|
+
` ...otherPlugins`,
|
|
24
|
+
`].filter(Boolean)`,
|
|
25
|
+
'',
|
|
26
|
+
'2. Initialize the client in your app entry (e.g., main.js / index.js):',
|
|
27
|
+
`import { mountInspector } from '@inspecto-dev/core'`,
|
|
28
|
+
'',
|
|
29
|
+
'// Call this before your app renders',
|
|
30
|
+
`if (process.env.NODE_ENV !== 'production') {`,
|
|
31
|
+
` mountInspector()`,
|
|
32
|
+
`}`,
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { InjectStrategy, InjectOptions } from './types.js'
|
|
2
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
export class RsbuildStrategy implements InjectStrategy {
|
|
5
|
+
name = 'Rsbuild'
|
|
6
|
+
|
|
7
|
+
supports(tool: BuildTool): boolean {
|
|
8
|
+
return tool === 'rsbuild'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
inject(options: InjectOptions): void {
|
|
12
|
+
throw new Error('Rsbuild requires manual plugin configuration due to nested structure')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[] {
|
|
16
|
+
return [
|
|
17
|
+
`import { rspackPlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
18
|
+
'',
|
|
19
|
+
'// Add to tools.rspack:',
|
|
20
|
+
`tools: {`,
|
|
21
|
+
` rspack: {`,
|
|
22
|
+
` plugins: [`,
|
|
23
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
24
|
+
` ]`,
|
|
25
|
+
` }`,
|
|
26
|
+
`}`,
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { InjectStrategy, InjectOptions } from './types.js'
|
|
2
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
export class RspackStrategy implements InjectStrategy {
|
|
5
|
+
name = 'Rspack'
|
|
6
|
+
|
|
7
|
+
supports(tool: BuildTool): boolean {
|
|
8
|
+
return tool === 'rspack'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
inject(options: InjectOptions): void {
|
|
12
|
+
throw new Error('Rspack requires manual plugin configuration')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[] {
|
|
16
|
+
const importPkg = detection.isLegacyRspack
|
|
17
|
+
? '@inspecto-dev/plugin/legacy/rspack'
|
|
18
|
+
: '@inspecto-dev/plugin'
|
|
19
|
+
|
|
20
|
+
const pluginCall = detection.isLegacyRspack
|
|
21
|
+
? `process.env.NODE_ENV !== 'production' && inspecto({\n pathType: 'absolute',\n escapeTags: ['Transition', 'AnimatePresence'],\n })`
|
|
22
|
+
: `process.env.NODE_ENV !== 'production' && inspecto()`
|
|
23
|
+
|
|
24
|
+
return [
|
|
25
|
+
`import { rspackPlugin as inspecto } from '${importPkg}'`,
|
|
26
|
+
'',
|
|
27
|
+
'// Add to your plugins array:',
|
|
28
|
+
`plugins: [`,
|
|
29
|
+
` ${pluginCall},`,
|
|
30
|
+
` ...otherPlugins`,
|
|
31
|
+
`].filter(Boolean)`,
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ProxifiedModule } from 'magicast'
|
|
2
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
export interface InjectOptions {
|
|
5
|
+
/** The magicast proxified module */
|
|
6
|
+
mod: ProxifiedModule<any>
|
|
7
|
+
/** The build tool detection result */
|
|
8
|
+
detection: BuildToolDetection
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface InjectStrategy {
|
|
12
|
+
/** Name of the strategy for logging */
|
|
13
|
+
name: string
|
|
14
|
+
|
|
15
|
+
/** Check if this strategy can handle the given build tool */
|
|
16
|
+
supports(tool: BuildTool): boolean
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Perform the AST injection.
|
|
20
|
+
* Note: Some tools (like webpack) might throw an error here to trigger manual fallback
|
|
21
|
+
* because AST manipulation for them is too complex/brittle in v1.
|
|
22
|
+
*/
|
|
23
|
+
inject(options: InjectOptions): void
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Perform the AST removal (for teardown).
|
|
27
|
+
* Returns true if successful, false if it couldn't be removed automatically.
|
|
28
|
+
*/
|
|
29
|
+
remove?(options: InjectOptions): boolean
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Return manual fallback instructions if automatic injection fails or is unsupported.
|
|
33
|
+
*/
|
|
34
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[]
|
|
35
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { addVitePlugin } from 'magicast/helpers'
|
|
2
|
+
import type { InjectStrategy, InjectOptions } from './types.js'
|
|
3
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
4
|
+
|
|
5
|
+
export class ViteStrategy implements InjectStrategy {
|
|
6
|
+
name = 'Vite'
|
|
7
|
+
|
|
8
|
+
supports(tool: BuildTool): boolean {
|
|
9
|
+
return tool === 'vite'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
inject({ mod, detection }: InjectOptions): void {
|
|
13
|
+
addVitePlugin(mod, {
|
|
14
|
+
from: '@inspecto-dev/plugin',
|
|
15
|
+
constructor: 'vitePlugin',
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[] {
|
|
20
|
+
return [
|
|
21
|
+
`import { vitePlugin as inspecto } from '@inspecto-dev/plugin'`,
|
|
22
|
+
'',
|
|
23
|
+
'// Add to your plugins array:',
|
|
24
|
+
`plugins: [`,
|
|
25
|
+
` process.env.NODE_ENV !== 'production' && inspecto(),`,
|
|
26
|
+
` ...otherPlugins`,
|
|
27
|
+
`].filter(Boolean)`,
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { InjectStrategy, InjectOptions } from './types.js'
|
|
2
|
+
import type { BuildTool, BuildToolDetection } from '../../types.js'
|
|
3
|
+
|
|
4
|
+
export class WebpackStrategy implements InjectStrategy {
|
|
5
|
+
name = 'Webpack'
|
|
6
|
+
|
|
7
|
+
supports(tool: BuildTool): boolean {
|
|
8
|
+
return tool === 'webpack'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
inject(options: InjectOptions): void {
|
|
12
|
+
// AST manipulation for Webpack configs (often CommonJS or complex objects) is brittle in v1
|
|
13
|
+
throw new Error('Webpack requires manual plugin configuration')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getManualInstructions(detection: BuildToolDetection, reason: string): string[] {
|
|
17
|
+
const importPkg = detection.isLegacyWebpack
|
|
18
|
+
? '@inspecto-dev/plugin/legacy/webpack4'
|
|
19
|
+
: '@inspecto-dev/plugin'
|
|
20
|
+
const pluginName = detection.isLegacyWebpack ? 'webpack4Plugin' : 'webpackPlugin'
|
|
21
|
+
|
|
22
|
+
const pluginCall = detection.isLegacyWebpack
|
|
23
|
+
? `process.env.NODE_ENV !== 'production' && inspecto({\n pathType: 'absolute',\n escapeTags: ['Transition', 'AnimatePresence'],\n })`
|
|
24
|
+
: `process.env.NODE_ENV !== 'production' && inspecto()`
|
|
25
|
+
|
|
26
|
+
return [
|
|
27
|
+
`import { ${pluginName} as inspecto } from '${importPkg}'`,
|
|
28
|
+
'',
|
|
29
|
+
'// Add to your plugins array:',
|
|
30
|
+
`plugins: [`,
|
|
31
|
+
` ${pluginCall},`,
|
|
32
|
+
` ...otherPlugins`,
|
|
33
|
+
`].filter(Boolean)`,
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { log } from './utils/logger.js'
|
|
2
|
+
|
|
3
|
+
export function printNuxtManualInstructions() {
|
|
4
|
+
log.blank()
|
|
5
|
+
log.hint('To enable Inspecto in Nuxt, update your nuxt.config.ts:')
|
|
6
|
+
console.log(`\x1b[36m
|
|
7
|
+
import { vitePlugin as inspecto } from '@inspecto-dev/plugin'
|
|
8
|
+
export default defineNuxtConfig({
|
|
9
|
+
vite: {
|
|
10
|
+
plugins: [inspecto()]
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
\x1b[0m`)
|
|
14
|
+
log.hint('And create a Nuxt plugin at plugins/inspecto.client.ts:')
|
|
15
|
+
console.log(`\x1b[36m
|
|
16
|
+
export default defineNuxtPlugin(() => {
|
|
17
|
+
if (import.meta.dev) {
|
|
18
|
+
import('@inspecto-dev/core').then(({ mountInspector }) => {
|
|
19
|
+
mountInspector()
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
\x1b[0m`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function printNextJsManualInstructions() {
|
|
27
|
+
log.blank()
|
|
28
|
+
log.hint('To enable Inspecto in Next.js, update your next.config.mjs:')
|
|
29
|
+
console.log(`\x1b[36m
|
|
30
|
+
import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'
|
|
31
|
+
const nextConfig = {
|
|
32
|
+
webpack: (config, { dev, isServer }) => {
|
|
33
|
+
if (dev && !isServer) config.plugins.push(inspecto())
|
|
34
|
+
return config
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default nextConfig
|
|
38
|
+
\x1b[0m`)
|
|
39
|
+
log.hint('And initialize the client dynamically in your app/layout.tsx (or pages/_app.tsx):')
|
|
40
|
+
console.log(`\x1b[36m
|
|
41
|
+
'use client'
|
|
42
|
+
import { useEffect } from 'react'
|
|
43
|
+
|
|
44
|
+
export default function RootLayout({ children }) {
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
47
|
+
import('@inspecto-dev/core').then(({ mountInspector }) => {
|
|
48
|
+
mountInspector({ serverUrl: 'http://127.0.0.1:5678' })
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}, [])
|
|
52
|
+
return <html><body>{children}</body></html>
|
|
53
|
+
}
|
|
54
|
+
\x1b[0m`)
|
|
55
|
+
}
|
package/src/prompts.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import prompts from 'prompts'
|
|
2
|
+
import { log } from './utils/logger.js'
|
|
3
|
+
import type { ProviderDetection } from './detect/provider.js'
|
|
4
|
+
import type { BuildToolDetection } from './types.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Interactive prompt for IDE choice.
|
|
8
|
+
*/
|
|
9
|
+
export async function promptIDEChoice(
|
|
10
|
+
detections: { ide: string; supported: boolean }[],
|
|
11
|
+
): Promise<{ ide: string; supported: boolean } | null> {
|
|
12
|
+
if (!process.stdin.isTTY) {
|
|
13
|
+
log.warn('Multiple IDEs detected but stdin is not interactive')
|
|
14
|
+
log.hint(`Using: ${detections[0]!.ide} (first match)`)
|
|
15
|
+
return detections[0]!
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { choice } = await prompts({
|
|
19
|
+
type: 'select',
|
|
20
|
+
name: 'choice',
|
|
21
|
+
message: 'Detected multiple IDEs, please choose one:',
|
|
22
|
+
choices: detections.map((d, i) => ({
|
|
23
|
+
title: `${d.ide} ${d.supported ? '(supported)' : '(unsupported/limited)'}`,
|
|
24
|
+
value: i,
|
|
25
|
+
})),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (choice === undefined) return null
|
|
29
|
+
return detections[choice]!
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Interactive prompt for AI tool choice.
|
|
34
|
+
*/
|
|
35
|
+
export async function promptProviderChoice(
|
|
36
|
+
detections: ProviderDetection[],
|
|
37
|
+
): Promise<ProviderDetection | null> {
|
|
38
|
+
if (!process.stdin.isTTY) {
|
|
39
|
+
log.warn('Multiple AI tools detected but stdin is not interactive')
|
|
40
|
+
log.hint(`Using: ${detections[0]!.label} (first match)`)
|
|
41
|
+
return detections[0]!
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { choice } = await prompts({
|
|
45
|
+
type: 'select',
|
|
46
|
+
name: 'choice',
|
|
47
|
+
message: 'Detected multiple providers, please choose one:',
|
|
48
|
+
choices: detections.map((d, i) => {
|
|
49
|
+
const modeLabels = d.providerModes.map(mode =>
|
|
50
|
+
mode === 'extension' ? 'VS Code Extension' : 'Terminal CLI',
|
|
51
|
+
)
|
|
52
|
+
const modeStr = modeLabels.join(' & ')
|
|
53
|
+
return {
|
|
54
|
+
title: `${d.label} ${d.supported ? `(supported ${modeStr})` : '(unsupported/limited)'}`,
|
|
55
|
+
value: i,
|
|
56
|
+
}
|
|
57
|
+
}),
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (choice === undefined) return null
|
|
61
|
+
return detections[choice]!
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Interactive prompt for Build Tool Config choice.
|
|
66
|
+
*/
|
|
67
|
+
export async function promptConfigChoice(
|
|
68
|
+
detections: BuildToolDetection[],
|
|
69
|
+
): Promise<BuildToolDetection | null> {
|
|
70
|
+
if (!process.stdin.isTTY) {
|
|
71
|
+
log.warn('Multiple config files detected but stdin is not interactive')
|
|
72
|
+
log.hint(`Using: ${detections[0]!.label} (first match)`)
|
|
73
|
+
return detections[0]!
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const choices = detections.map((d, i) => ({
|
|
77
|
+
title: d.label,
|
|
78
|
+
value: i,
|
|
79
|
+
}))
|
|
80
|
+
|
|
81
|
+
choices.push({
|
|
82
|
+
title: "Skip (I'll configure manually)",
|
|
83
|
+
value: -1,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const { choice } = await prompts({
|
|
87
|
+
type: 'select',
|
|
88
|
+
name: 'choice',
|
|
89
|
+
message: 'Detected multiple build tool configs, please choose one to inject:',
|
|
90
|
+
choices,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (choice === undefined || choice === -1) return null
|
|
94
|
+
return detections[choice]!
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Interactive prompt for continuing with unsupported frameworks.
|
|
99
|
+
*/
|
|
100
|
+
export async function promptUnsupportedFrameworkContinue(): Promise<boolean> {
|
|
101
|
+
if (!process.stdin.isTTY) {
|
|
102
|
+
log.error('Unsupported framework detected in non-interactive environment.')
|
|
103
|
+
log.hint('Use --force to skip this check and continue anyway.')
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { confirm } = await prompts({
|
|
108
|
+
type: 'confirm',
|
|
109
|
+
name: 'confirm',
|
|
110
|
+
message: 'Inspecto may not work properly. Do you want to continue anyway?',
|
|
111
|
+
initial: false,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return !!confirm
|
|
115
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -16,6 +16,8 @@ export interface BuildToolDetection {
|
|
|
16
16
|
label: string
|
|
17
17
|
/** Whether this is a legacy rspack version (< 0.4.0) */
|
|
18
18
|
isLegacyRspack?: boolean
|
|
19
|
+
/** Whether this is Webpack 4.x */
|
|
20
|
+
isLegacyWebpack?: boolean
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/** Options passed to `inspecto init` */
|
|
@@ -23,9 +25,10 @@ export interface InitOptions {
|
|
|
23
25
|
shared: boolean
|
|
24
26
|
skipInstall: boolean
|
|
25
27
|
dryRun: boolean
|
|
26
|
-
|
|
28
|
+
provider?: string
|
|
27
29
|
noExtension: boolean
|
|
28
30
|
packages?: string[]
|
|
31
|
+
force: boolean
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/** A single mutation recorded in install.lock */
|
package/tests/ide.test.ts
CHANGED
|
@@ -23,7 +23,7 @@ describe('detectIDE', () => {
|
|
|
23
23
|
vi.mocked(fsUtils.exists).mockResolvedValue(false)
|
|
24
24
|
|
|
25
25
|
const result = await detectIDE('/mock/root')
|
|
26
|
-
expect(result.detected).toEqual([{ ide: '
|
|
26
|
+
expect(result.detected).toEqual([{ ide: 'trae', supported: true }])
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
it('detects Cursor from environment variables', async () => {
|
|
@@ -31,7 +31,7 @@ describe('detectIDE', () => {
|
|
|
31
31
|
vi.mocked(fsUtils.exists).mockResolvedValue(false)
|
|
32
32
|
|
|
33
33
|
const result = await detectIDE('/mock/root')
|
|
34
|
-
expect(result.detected).toEqual([{ ide: '
|
|
34
|
+
expect(result.detected).toEqual([{ ide: 'cursor', supported: true }])
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
it('detects VS Code from environment variables without false positives', async () => {
|
|
@@ -89,6 +89,6 @@ describe('detectIDE', () => {
|
|
|
89
89
|
|
|
90
90
|
const result = await detectIDE('/mock/root')
|
|
91
91
|
// VS Code shouldn't be added since it shares TERM_PROGRAM with Trae
|
|
92
|
-
expect(result.detected).toEqual([{ ide: '
|
|
92
|
+
expect(result.detected).toEqual([{ ide: 'trae', supported: true }])
|
|
93
93
|
})
|
|
94
94
|
})
|