@inspecto-dev/cli 0.2.0-alpha.5 → 0.3.0-alpha.1
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 +19 -20
- package/CHANGELOG.md +22 -0
- package/README.md +93 -11
- package/bin/inspecto.js +5 -1
- package/dist/bin.d.ts +5 -1
- package/dist/bin.js +530 -49
- package/dist/chunk-FZS2TLXQ.js +3140 -0
- package/dist/index.d.ts +233 -2
- package/dist/index.js +17 -3
- package/package.json +3 -2
- package/src/bin.ts +286 -66
- package/src/commands/apply.ts +118 -0
- package/src/commands/detect.ts +59 -0
- package/src/commands/doctor.ts +225 -72
- package/src/commands/init.ts +143 -183
- package/src/commands/integration-install.ts +452 -0
- package/src/commands/onboard.ts +50 -0
- package/src/commands/plan.ts +41 -0
- package/src/detect/build-tool.ts +107 -3
- package/src/index.ts +17 -2
- package/src/inject/ast-injector.ts +17 -6
- package/src/inject/extension.ts +40 -22
- package/src/inject/gitignore.ts +10 -3
- package/src/instructions.ts +60 -46
- package/src/onboarding/apply.ts +364 -0
- package/src/onboarding/context.ts +36 -0
- package/src/onboarding/planner.ts +284 -0
- package/src/onboarding/session.ts +434 -0
- package/src/onboarding/target-resolution.ts +116 -0
- package/src/prompts.ts +54 -11
- package/src/types.ts +184 -0
- package/src/utils/fs.ts +2 -1
- package/src/utils/logger.ts +9 -0
- package/src/utils/output.ts +40 -0
- package/tests/apply.test.ts +583 -0
- package/tests/ast-injector.test.ts +50 -0
- package/tests/build-tool.test.ts +3 -5
- package/tests/detect.test.ts +94 -0
- package/tests/doctor.test.ts +224 -0
- package/tests/init.test.ts +364 -0
- package/tests/install-wrapper.test.ts +76 -0
- package/tests/instructions.test.ts +61 -0
- package/tests/integration-install.test.ts +294 -0
- package/tests/logger.test.ts +100 -0
- package/tests/onboard.test.ts +258 -0
- package/tests/plan.test.ts +713 -0
- package/tests/workspace-build-tool.test.ts +75 -0
- package/.turbo/turbo-test.log +0 -16
- package/dist/chunk-MIHQGC3L.js +0 -1720
package/src/index.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
export { apply } from './commands/apply.js'
|
|
2
|
+
export { detect } from './commands/detect.js'
|
|
1
3
|
export { init } from './commands/init.js'
|
|
2
|
-
export { doctor } from './commands/doctor.js'
|
|
4
|
+
export { collectDoctorResult, doctor } from './commands/doctor.js'
|
|
5
|
+
export { onboard } from './commands/onboard.js'
|
|
6
|
+
export { plan } from './commands/plan.js'
|
|
3
7
|
export { teardown } from './commands/teardown.js'
|
|
4
|
-
export
|
|
8
|
+
export { writeCommandOutput, reportCommandError } from './utils/output.js'
|
|
9
|
+
export type {
|
|
10
|
+
InitOptions,
|
|
11
|
+
BuildTool,
|
|
12
|
+
PackageManager,
|
|
13
|
+
InstallLock,
|
|
14
|
+
DoctorDiagnostic,
|
|
15
|
+
DoctorResult,
|
|
16
|
+
OnboardStatus,
|
|
17
|
+
OnboardCommandResult,
|
|
18
|
+
ResolvedOnboardingSession,
|
|
19
|
+
} from './types.js'
|
|
5
20
|
export type { Framework } from './detect/framework.js'
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import path from 'node:path'
|
|
17
17
|
import { loadFile, writeFile as writeAstFile } from 'magicast'
|
|
18
|
-
import {
|
|
18
|
+
import { readFile } from '../utils/fs.js'
|
|
19
19
|
import { log } from '../utils/logger.js'
|
|
20
20
|
import type { BuildToolDetection, Mutation } from '../types.js'
|
|
21
21
|
import { STRATEGIES } from './strategies/index.js'
|
|
@@ -25,7 +25,9 @@ function printManualInstructions(
|
|
|
25
25
|
strategy: InjectStrategy | undefined,
|
|
26
26
|
detection: BuildToolDetection,
|
|
27
27
|
reason: string,
|
|
28
|
+
quiet = false,
|
|
28
29
|
) {
|
|
30
|
+
if (quiet) return
|
|
29
31
|
log.warn(`Could not automatically configure ${detection.configPath}`)
|
|
30
32
|
log.hint(`(reason: ${reason})`)
|
|
31
33
|
log.blank()
|
|
@@ -33,7 +35,7 @@ function printManualInstructions(
|
|
|
33
35
|
|
|
34
36
|
if (strategy) {
|
|
35
37
|
const instructions = strategy.getManualInstructions(detection, reason)
|
|
36
|
-
log.
|
|
38
|
+
log.copyableCodeBlock(instructions)
|
|
37
39
|
} else {
|
|
38
40
|
log.error(`Unsupported build tool: ${detection.tool}`)
|
|
39
41
|
}
|
|
@@ -73,6 +75,7 @@ export async function injectPlugin(
|
|
|
73
75
|
root: string,
|
|
74
76
|
detection: BuildToolDetection,
|
|
75
77
|
dryRun: boolean,
|
|
78
|
+
quiet = false,
|
|
76
79
|
): Promise<InjectionResult> {
|
|
77
80
|
const configPath = path.join(root, detection.configPath)
|
|
78
81
|
const mutations: Mutation[] = []
|
|
@@ -82,13 +85,15 @@ export async function injectPlugin(
|
|
|
82
85
|
// Step 1: Read config file to check existence and idempotency
|
|
83
86
|
const content = await readFile(configPath)
|
|
84
87
|
if (!content) {
|
|
85
|
-
printManualInstructions(strategy, detection, 'config file not readable')
|
|
88
|
+
printManualInstructions(strategy, detection, 'config file not readable', quiet)
|
|
86
89
|
return { success: false, mutations, failureReason: 'config file not readable' }
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
// Step 2: Idempotency check
|
|
90
93
|
if (isAlreadyInjected(content)) {
|
|
91
|
-
|
|
94
|
+
if (!quiet) {
|
|
95
|
+
log.success(`Plugin already configured in ${detection.configPath} (skipped)`)
|
|
96
|
+
}
|
|
92
97
|
|
|
93
98
|
mutations.push({
|
|
94
99
|
type: 'file_modified',
|
|
@@ -104,13 +109,16 @@ export async function injectPlugin(
|
|
|
104
109
|
strategy,
|
|
105
110
|
detection,
|
|
106
111
|
`No injection strategy found for ${detection.tool}`,
|
|
112
|
+
quiet,
|
|
107
113
|
)
|
|
108
114
|
return { success: false, mutations, failureReason: 'No strategy found' }
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
// Step 3: Automatic configuration
|
|
112
118
|
if (dryRun) {
|
|
113
|
-
|
|
119
|
+
if (!quiet) {
|
|
120
|
+
log.dryRun(`Would automatically configure plugin in ${detection.configPath}`)
|
|
121
|
+
}
|
|
114
122
|
return { success: true, mutations: [] }
|
|
115
123
|
}
|
|
116
124
|
|
|
@@ -132,7 +140,9 @@ export async function injectPlugin(
|
|
|
132
140
|
description: 'Automatically configured inspecto() plugin',
|
|
133
141
|
})
|
|
134
142
|
|
|
135
|
-
|
|
143
|
+
if (!quiet) {
|
|
144
|
+
log.success(`Configured plugin in ${detection.configPath}`)
|
|
145
|
+
}
|
|
136
146
|
return { success: true, mutations }
|
|
137
147
|
} catch (err) {
|
|
138
148
|
// Graceback degradation
|
|
@@ -140,6 +150,7 @@ export async function injectPlugin(
|
|
|
140
150
|
strategy,
|
|
141
151
|
detection,
|
|
142
152
|
`Automatic configuration unavailable: ${err instanceof Error ? err.message : String(err)}`,
|
|
153
|
+
quiet,
|
|
143
154
|
)
|
|
144
155
|
return {
|
|
145
156
|
success: false,
|
package/src/inject/extension.ts
CHANGED
|
@@ -69,9 +69,15 @@ async function tryOpenURI(uri: string): Promise<boolean> {
|
|
|
69
69
|
/**
|
|
70
70
|
* Attempt to install the VS Code extension using waterfall degradation.
|
|
71
71
|
*/
|
|
72
|
-
export async function installExtension(
|
|
72
|
+
export async function installExtension(
|
|
73
|
+
dryRun: boolean,
|
|
74
|
+
ide?: string,
|
|
75
|
+
quiet = false,
|
|
76
|
+
): Promise<Mutation | null> {
|
|
73
77
|
if (dryRun) {
|
|
74
|
-
|
|
78
|
+
if (!quiet) {
|
|
79
|
+
log.dryRun('Would attempt to install VS Code extension')
|
|
80
|
+
}
|
|
75
81
|
return null
|
|
76
82
|
}
|
|
77
83
|
|
|
@@ -82,7 +88,9 @@ export async function installExtension(dryRun: boolean, ide?: string): Promise<M
|
|
|
82
88
|
if (await which('code')) {
|
|
83
89
|
try {
|
|
84
90
|
await run('code', ['--install-extension', EXTENSION_ID])
|
|
85
|
-
|
|
91
|
+
if (!quiet) {
|
|
92
|
+
log.success('VS Code extension installed via CLI')
|
|
93
|
+
}
|
|
86
94
|
return { type: 'extension_installed', id: EXTENSION_ID }
|
|
87
95
|
} catch {
|
|
88
96
|
// Fall through to next level
|
|
@@ -94,10 +102,12 @@ export async function installExtension(dryRun: boolean, ide?: string): Promise<M
|
|
|
94
102
|
if (codePath) {
|
|
95
103
|
try {
|
|
96
104
|
await run(codePath, ['--install-extension', EXTENSION_ID])
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
if (!quiet) {
|
|
106
|
+
log.success('VS Code extension installed via binary path')
|
|
107
|
+
log.info(
|
|
108
|
+
'Tip: Add "code" to your PATH to help Inspecto detect other AI tools in the future',
|
|
109
|
+
)
|
|
110
|
+
}
|
|
101
111
|
return { type: 'extension_installed', id: EXTENSION_ID }
|
|
102
112
|
} catch {
|
|
103
113
|
// Fall through to next level
|
|
@@ -107,29 +117,37 @@ export async function installExtension(dryRun: boolean, ide?: string): Promise<M
|
|
|
107
117
|
// Level 3: URI scheme
|
|
108
118
|
const uri = `vscode:extension/${EXTENSION_ID}`
|
|
109
119
|
if (await tryOpenURI(uri)) {
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
if (!quiet) {
|
|
121
|
+
log.warn('Opened extension page in VS Code')
|
|
122
|
+
log.hint('Please click "Install" in the opened VS Code window to complete setup.')
|
|
123
|
+
}
|
|
112
124
|
return { type: 'extension_installed', id: EXTENSION_ID, manual_action_required: true }
|
|
113
125
|
}
|
|
114
126
|
|
|
115
127
|
// Level 4: Manual fallback
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
if (!quiet) {
|
|
129
|
+
log.warn('Could not auto-install VS Code extension')
|
|
130
|
+
log.hint('Please install it manually to enable Inspector features:')
|
|
131
|
+
log.hint(' 1. Open VS Code')
|
|
132
|
+
log.hint(' 2. Press Ctrl+Shift+X (or Cmd+Shift+X)')
|
|
133
|
+
log.hint(' 3. Search for "Inspecto"')
|
|
134
|
+
log.hint(` Or visit: https://marketplace.visualstudio.com/items?itemName=${EXTENSION_ID}`)
|
|
135
|
+
}
|
|
122
136
|
return null
|
|
123
137
|
}
|
|
124
138
|
|
|
125
139
|
// Other IDEs: Prompt to install via VSIX
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
if (!quiet) {
|
|
141
|
+
log.warn(`Could not auto-install extension for ${ide}`)
|
|
142
|
+
log.hint('Please install it manually to enable Inspector features:')
|
|
143
|
+
log.hint(
|
|
144
|
+
' 1. Download the latest .vsix file (Open VSX: https://open-vsx.org/extension/inspecto/inspecto)',
|
|
145
|
+
)
|
|
146
|
+
log.hint(` 2. Open ${ide}`)
|
|
147
|
+
log.hint(' 3. Open the Command Palette (Ctrl+Shift+P or Cmd+Shift+P)')
|
|
148
|
+
log.hint(' 4. Type and select "Extensions: Install from VSIX..."')
|
|
149
|
+
log.hint(' 5. Select the downloaded .vsix file')
|
|
150
|
+
}
|
|
133
151
|
return null
|
|
134
152
|
}
|
|
135
153
|
|
package/src/inject/gitignore.ts
CHANGED
|
@@ -21,6 +21,7 @@ export async function updateGitignore(
|
|
|
21
21
|
root: string,
|
|
22
22
|
shared: boolean,
|
|
23
23
|
dryRun: boolean,
|
|
24
|
+
quiet = false,
|
|
24
25
|
): Promise<void> {
|
|
25
26
|
const gitignorePath = path.join(root, '.gitignore')
|
|
26
27
|
let content = (await readFile(gitignorePath)) ?? ''
|
|
@@ -34,7 +35,9 @@ export async function updateGitignore(
|
|
|
34
35
|
if (!dryRun) {
|
|
35
36
|
await writeFile(gitignorePath, content)
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
if (!quiet) {
|
|
39
|
+
log.success('Updated .gitignore: .inspecto/ is no longer fully ignored')
|
|
40
|
+
}
|
|
38
41
|
return
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -49,10 +52,14 @@ export async function updateGitignore(
|
|
|
49
52
|
content = content.trimEnd() + '\n' + section
|
|
50
53
|
|
|
51
54
|
if (dryRun) {
|
|
52
|
-
|
|
55
|
+
if (!quiet) {
|
|
56
|
+
log.dryRun(`Would update .gitignore with: ${missingRules.join(', ')}`)
|
|
57
|
+
}
|
|
53
58
|
} else {
|
|
54
59
|
await writeFile(gitignorePath, content)
|
|
55
|
-
|
|
60
|
+
if (!quiet) {
|
|
61
|
+
log.success('Updated .gitignore')
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
|
package/src/instructions.ts
CHANGED
|
@@ -2,54 +2,68 @@ import { log } from './utils/logger.js'
|
|
|
2
2
|
|
|
3
3
|
export function printNuxtManualInstructions() {
|
|
4
4
|
log.blank()
|
|
5
|
-
log.hint('
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
log.hint('Nuxt requires manual setup in the current version.')
|
|
6
|
+
log.hint('1. Update `nuxt.config.ts` to register the Inspecto Vite plugin:')
|
|
7
|
+
log.copyableCodeBlock([
|
|
8
|
+
"import { vitePlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
9
|
+
'',
|
|
10
|
+
'export default defineNuxtConfig({',
|
|
11
|
+
' vite: {',
|
|
12
|
+
' plugins: [inspecto()],',
|
|
13
|
+
' },',
|
|
14
|
+
'})',
|
|
15
|
+
])
|
|
16
|
+
log.hint('2. Create `plugins/inspecto.client.ts` to mount `@inspecto-dev/core` in development:')
|
|
17
|
+
log.copyableCodeBlock([
|
|
18
|
+
'export default defineNuxtPlugin(() => {',
|
|
19
|
+
' if (import.meta.dev) {',
|
|
20
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
21
|
+
' mountInspector()',
|
|
22
|
+
' })',
|
|
23
|
+
' }',
|
|
24
|
+
'})',
|
|
25
|
+
])
|
|
26
|
+
log.hint('3. Restart your Nuxt dev server after updating the config.')
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export function printNextJsManualInstructions() {
|
|
27
30
|
log.blank()
|
|
28
|
-
log.hint('
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
31
|
+
log.hint('Next.js requires manual setup in the current version.')
|
|
32
|
+
log.hint('1. Update `next.config.mjs` to register the Inspecto webpack plugin:')
|
|
33
|
+
log.copyableCodeBlock([
|
|
34
|
+
"import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
35
|
+
'',
|
|
36
|
+
"/** @type {import('next').NextConfig} */",
|
|
37
|
+
'const nextConfig = {',
|
|
38
|
+
' webpack: (config, { dev, isServer }) => {',
|
|
39
|
+
' if (dev && !isServer) {',
|
|
40
|
+
' config.plugins.push(inspecto())',
|
|
41
|
+
' }',
|
|
42
|
+
' return config',
|
|
43
|
+
' },',
|
|
44
|
+
'}',
|
|
45
|
+
'',
|
|
46
|
+
'export default nextConfig',
|
|
47
|
+
])
|
|
48
|
+
log.hint(
|
|
49
|
+
'2. Initialize `@inspecto-dev/core` from a client component such as `app/layout.tsx` or `pages/_app.tsx`:',
|
|
50
|
+
)
|
|
51
|
+
log.copyableCodeBlock([
|
|
52
|
+
"'use client'",
|
|
53
|
+
'',
|
|
54
|
+
"import { useEffect } from 'react'",
|
|
55
|
+
'',
|
|
56
|
+
'export default function RootLayout({ children }) {',
|
|
57
|
+
' useEffect(() => {',
|
|
58
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
59
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
60
|
+
" mountInspector({ serverUrl: 'http://127.0.0.1:5678' })",
|
|
61
|
+
' })',
|
|
62
|
+
' }',
|
|
63
|
+
' }, [])',
|
|
64
|
+
'',
|
|
65
|
+
' return <html><body>{children}</body></html>',
|
|
66
|
+
'}',
|
|
67
|
+
])
|
|
68
|
+
log.hint('3. Restart your Next.js dev server after updating the config.')
|
|
55
69
|
}
|