@inspecto-dev/cli 0.2.0-alpha.0
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 -0
- package/.turbo/turbo-test.log +15 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/TESTING.md +109 -0
- package/bin/inspecto.js +3 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +83 -0
- package/dist/chunk-4RR7PTRN.js +1306 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +10 -0
- package/package.json +38 -0
- package/src/bin.ts +89 -0
- package/src/commands/doctor.ts +185 -0
- package/src/commands/init.ts +447 -0
- package/src/commands/teardown.ts +124 -0
- package/src/detect/ai-tool.ts +127 -0
- package/src/detect/build-tool.ts +123 -0
- package/src/detect/framework.ts +65 -0
- package/src/detect/ide.ts +78 -0
- package/src/detect/package-manager.ts +56 -0
- package/src/index.ts +5 -0
- package/src/inject/ast-injector.ts +300 -0
- package/src/inject/extension.ts +140 -0
- package/src/inject/gitignore.ts +76 -0
- package/src/types.ts +48 -0
- package/src/utils/exec.ts +44 -0
- package/src/utils/fs.ts +69 -0
- package/src/utils/logger.ts +64 -0
- package/tests/framework.test.ts +65 -0
- package/tests/ide.test.ts +94 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +10 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Package manager detection result */
|
|
2
|
+
type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';
|
|
3
|
+
/** Supported build tools (v1) */
|
|
4
|
+
type BuildTool = 'vite' | 'webpack' | 'rspack' | 'rsbuild' | 'esbuild' | 'rollup';
|
|
5
|
+
/** Options passed to `inspecto init` */
|
|
6
|
+
interface InitOptions {
|
|
7
|
+
shared: boolean;
|
|
8
|
+
skipInstall: boolean;
|
|
9
|
+
dryRun: boolean;
|
|
10
|
+
prefer?: string;
|
|
11
|
+
noExtension: boolean;
|
|
12
|
+
packages?: string[];
|
|
13
|
+
}
|
|
14
|
+
/** A single mutation recorded in install.lock */
|
|
15
|
+
interface Mutation {
|
|
16
|
+
type: 'file_modified' | 'file_created' | 'dependency_added' | 'extension_installed';
|
|
17
|
+
path?: string;
|
|
18
|
+
backup?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
id?: string;
|
|
21
|
+
dev?: boolean;
|
|
22
|
+
description?: string;
|
|
23
|
+
manual_action_required?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Structure of .inspecto/install.lock */
|
|
26
|
+
interface InstallLock {
|
|
27
|
+
version: string;
|
|
28
|
+
created_at: string;
|
|
29
|
+
mutations: Mutation[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare function init(options: InitOptions): Promise<void>;
|
|
33
|
+
|
|
34
|
+
declare function doctor(): Promise<void>;
|
|
35
|
+
|
|
36
|
+
declare function teardown(): Promise<void>;
|
|
37
|
+
|
|
38
|
+
type Framework = 'react' | 'vue';
|
|
39
|
+
|
|
40
|
+
export { type BuildTool, type Framework, type InitOptions, type InstallLock, type PackageManager, doctor, init, teardown };
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inspecto-dev/cli",
|
|
3
|
+
"version": "0.2.0-alpha.0",
|
|
4
|
+
"description": "CLI tools for Inspecto onboarding and lifecycle management",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"inspecto",
|
|
7
|
+
"cli",
|
|
8
|
+
"ai",
|
|
9
|
+
"devtools",
|
|
10
|
+
"scaffold",
|
|
11
|
+
"init"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"inspecto": "./bin/inspecto.js"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"cac": "^6.7.14",
|
|
19
|
+
"picocolors": "^1.0.0",
|
|
20
|
+
"prompts": "^2.4.2",
|
|
21
|
+
"@inspecto-dev/types": "0.2.0-alpha.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"@types/prompts": "^2.4.9",
|
|
26
|
+
"tsup": "^8.0.2",
|
|
27
|
+
"typescript": "^5.4.5",
|
|
28
|
+
"vitest": "^1.6.0"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"dev": "tsup --watch",
|
|
36
|
+
"test": "vitest run --passWithNoTests"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// src/bin.ts — CLI entry point
|
|
3
|
+
//
|
|
4
|
+
// v1 scope: VS Code | React + Vue | Vite + Webpack + Rspack + esbuild + Rollup
|
|
5
|
+
// ============================================================
|
|
6
|
+
import { parseArgs } from 'node:util'
|
|
7
|
+
import { init } from './commands/init.js'
|
|
8
|
+
import { doctor } from './commands/doctor.js'
|
|
9
|
+
import { teardown } from './commands/teardown.js'
|
|
10
|
+
import { log } from './utils/logger.js'
|
|
11
|
+
|
|
12
|
+
const HELP = `
|
|
13
|
+
✦ Inspecto CLI (v1)
|
|
14
|
+
|
|
15
|
+
Supported: VS Code | React + Vue | Vite + Webpack + Rspack + esbuild + Rollup
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
inspecto init Set up Inspecto in your project
|
|
19
|
+
inspecto doctor Diagnose your Inspecto installation
|
|
20
|
+
inspecto teardown Remove Inspecto from your project
|
|
21
|
+
|
|
22
|
+
Init Options:
|
|
23
|
+
--shared Share .inspecto/settings.json with your team via Git
|
|
24
|
+
--skip-install Skip npm dependency installation
|
|
25
|
+
--dry-run Preview changes without modifying files
|
|
26
|
+
--prefer <tool> Set default AI tool (e.g. github-copilot, claude-code)
|
|
27
|
+
--no-extension Skip VS Code extension installation
|
|
28
|
+
--packages <names> (Monorepo) Comma-separated list of packages to inject
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
npx inspecto init
|
|
32
|
+
npx inspecto init --shared --prefer github-copilot
|
|
33
|
+
npx inspecto init --dry-run
|
|
34
|
+
npx inspecto doctor
|
|
35
|
+
npx inspecto teardown
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
const args = process.argv.slice(2)
|
|
40
|
+
const command = args[0]
|
|
41
|
+
|
|
42
|
+
if (!command || command === '--help' || command === '-h') {
|
|
43
|
+
console.log(HELP)
|
|
44
|
+
process.exit(0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
switch (command) {
|
|
49
|
+
case 'init': {
|
|
50
|
+
const { values } = parseArgs({
|
|
51
|
+
args: args.slice(1),
|
|
52
|
+
options: {
|
|
53
|
+
shared: { type: 'boolean', default: false },
|
|
54
|
+
'skip-install': { type: 'boolean', default: false },
|
|
55
|
+
'dry-run': { type: 'boolean', default: false },
|
|
56
|
+
prefer: { type: 'string' },
|
|
57
|
+
'no-extension': { type: 'boolean', default: false },
|
|
58
|
+
packages: { type: 'string' },
|
|
59
|
+
},
|
|
60
|
+
strict: true,
|
|
61
|
+
})
|
|
62
|
+
await init({
|
|
63
|
+
shared: values.shared ?? false,
|
|
64
|
+
skipInstall: values['skip-install'] ?? false,
|
|
65
|
+
dryRun: values['dry-run'] ?? false,
|
|
66
|
+
...(values.prefer && { prefer: values.prefer }),
|
|
67
|
+
noExtension: values['no-extension'] ?? false,
|
|
68
|
+
...(values.packages && { packages: values.packages.split(',').map(s => s.trim()) }),
|
|
69
|
+
})
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
case 'doctor':
|
|
73
|
+
await doctor()
|
|
74
|
+
break
|
|
75
|
+
case 'teardown':
|
|
76
|
+
await teardown()
|
|
77
|
+
break
|
|
78
|
+
default:
|
|
79
|
+
log.error(`Unknown command: ${command}`)
|
|
80
|
+
console.log(HELP)
|
|
81
|
+
process.exit(1)
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
log.error(err instanceof Error ? err.message : String(err))
|
|
85
|
+
process.exit(1)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main()
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// src/commands/doctor.ts — Installation diagnostics (v1)
|
|
3
|
+
// ============================================================
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { log } from '../utils/logger.js'
|
|
6
|
+
import { exists, readJSON, readFile } from '../utils/fs.js'
|
|
7
|
+
import { detectPackageManager, getInstallCommand } from '../detect/package-manager.js'
|
|
8
|
+
import { detectBuildTools } from '../detect/build-tool.js'
|
|
9
|
+
import { detectFrameworks } from '../detect/framework.js'
|
|
10
|
+
import { detectIDE } from '../detect/ide.js'
|
|
11
|
+
import { detectAITools } from '../detect/ai-tool.js'
|
|
12
|
+
import { isExtensionInstalled } from '../inject/extension.js'
|
|
13
|
+
|
|
14
|
+
interface DiagResult {
|
|
15
|
+
errors: number
|
|
16
|
+
warnings: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function doctor(): Promise<void> {
|
|
20
|
+
const root = process.cwd()
|
|
21
|
+
const result: DiagResult = { errors: 0, warnings: 0 }
|
|
22
|
+
|
|
23
|
+
log.header('Inspecto Doctor')
|
|
24
|
+
|
|
25
|
+
// Check 1: package.json exists
|
|
26
|
+
if (!(await exists(path.join(root, 'package.json')))) {
|
|
27
|
+
log.error('No package.json found')
|
|
28
|
+
log.hint('Run this command from your project root')
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check 2: IDE
|
|
33
|
+
const ideProbe = await detectIDE(root)
|
|
34
|
+
if (ideProbe.detected.length === 0) {
|
|
35
|
+
log.warn('IDE: not detected')
|
|
36
|
+
result.warnings++
|
|
37
|
+
} else {
|
|
38
|
+
// If we have at least one supported IDE, it's a pass
|
|
39
|
+
const hasSupported = ideProbe.detected.some(d => d.supported)
|
|
40
|
+
if (hasSupported) {
|
|
41
|
+
log.success(
|
|
42
|
+
`IDE: ${ideProbe.detected
|
|
43
|
+
.filter(d => d.supported)
|
|
44
|
+
.map(d => d.ide)
|
|
45
|
+
.join(', ')}`,
|
|
46
|
+
)
|
|
47
|
+
} else {
|
|
48
|
+
const names = ideProbe.detected.map(d => d.ide).join(', ')
|
|
49
|
+
log.warn(`IDE: ${names} (not supported in v1, VS Code only)`)
|
|
50
|
+
result.warnings++
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check 3: Supported framework
|
|
55
|
+
const frameworkResult = await detectFrameworks(root)
|
|
56
|
+
if (frameworkResult.supported.length > 0) {
|
|
57
|
+
log.success(`Framework: ${frameworkResult.supported.join(', ')}`)
|
|
58
|
+
} else if (frameworkResult.unsupported.length > 0) {
|
|
59
|
+
const names = frameworkResult.unsupported.map(f => f.name).join(', ')
|
|
60
|
+
log.warn(`Framework: ${names} (not supported in v1, React/Vue only)`)
|
|
61
|
+
result.warnings++
|
|
62
|
+
} else {
|
|
63
|
+
log.warn('Framework: not detected (React / Vue expected)')
|
|
64
|
+
result.warnings++
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check 3.5: AI Tools
|
|
68
|
+
const aiProbe = await detectAITools(root)
|
|
69
|
+
if (aiProbe.detected.length === 0) {
|
|
70
|
+
log.warn('AI Tool: none detected')
|
|
71
|
+
log.hint('Inspecto works best with Claude Code, Trae CLI, or GitHub Copilot')
|
|
72
|
+
result.warnings++
|
|
73
|
+
} else {
|
|
74
|
+
const aiNames = aiProbe.detected
|
|
75
|
+
.map(d => {
|
|
76
|
+
const modeLabels = d.toolModes.map(mode =>
|
|
77
|
+
mode === 'plugin' ? 'VS Code Extension' : 'Terminal CLI',
|
|
78
|
+
)
|
|
79
|
+
return `${d.label} (${modeLabels.join(' & ')})`
|
|
80
|
+
})
|
|
81
|
+
.join(', ')
|
|
82
|
+
log.success(`AI Tool: ${aiNames}`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check 4: @inspecto-dev/plugin installed
|
|
86
|
+
const pluginPath = path.join(root, 'node_modules', '@inspecto', 'plugin')
|
|
87
|
+
if (await exists(pluginPath)) {
|
|
88
|
+
const pkgJson = await readJSON<{ version?: string }>(path.join(pluginPath, 'package.json'))
|
|
89
|
+
const version = pkgJson?.version ?? 'unknown'
|
|
90
|
+
log.success(`@inspecto-dev/plugin@${version} installed`)
|
|
91
|
+
} else {
|
|
92
|
+
log.error('@inspecto-dev/plugin not installed')
|
|
93
|
+
const pm = await detectPackageManager(root)
|
|
94
|
+
log.hint(`Fix: ${getInstallCommand(pm, '@inspecto-dev/plugin')}`)
|
|
95
|
+
result.errors++
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check 5: Plugin injected in build config
|
|
99
|
+
const buildResult = await detectBuildTools(root)
|
|
100
|
+
if (buildResult.supported.length > 0) {
|
|
101
|
+
let injected = false
|
|
102
|
+
for (const bt of buildResult.supported) {
|
|
103
|
+
const content = await readFile(path.join(root, bt.configPath))
|
|
104
|
+
if (content && content.includes('@inspecto-dev/plugin')) {
|
|
105
|
+
log.success(`Plugin injected in ${bt.configPath}`)
|
|
106
|
+
injected = true
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!injected) {
|
|
111
|
+
log.error('Plugin not injected in any build config')
|
|
112
|
+
log.hint('Fix: npx inspecto init')
|
|
113
|
+
result.errors++
|
|
114
|
+
}
|
|
115
|
+
} else if (buildResult.unsupported.length > 0) {
|
|
116
|
+
const names = buildResult.unsupported.join(', ')
|
|
117
|
+
log.warn(`Build tool: ${names} (not supported in v1)`)
|
|
118
|
+
log.hint('v1 supports: Vite, Webpack, Rspack, esbuild, Rollup')
|
|
119
|
+
result.warnings++
|
|
120
|
+
} else {
|
|
121
|
+
log.warn('No recognized build config found')
|
|
122
|
+
result.warnings++
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check 6: VS Code extension
|
|
126
|
+
const extInstalled = await isExtensionInstalled()
|
|
127
|
+
if (extInstalled) {
|
|
128
|
+
log.success('VS Code extension detected')
|
|
129
|
+
} else {
|
|
130
|
+
const hasSupported = ideProbe.detected.some(d => d.supported)
|
|
131
|
+
if (ideProbe.detected.length > 0 && !hasSupported) {
|
|
132
|
+
log.warn('VS Code extension not applicable (non-VS Code IDE)')
|
|
133
|
+
} else {
|
|
134
|
+
log.error('VS Code extension not found')
|
|
135
|
+
log.hint('Fix: code --install-extension inspecto.inspecto')
|
|
136
|
+
log.hint('Or: https://marketplace.visualstudio.com/items?itemName=inspecto.inspecto')
|
|
137
|
+
result.errors++
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check 7: settings.json
|
|
142
|
+
const settingsPath = path.join(root, '.inspecto', 'settings.json')
|
|
143
|
+
if (await exists(settingsPath)) {
|
|
144
|
+
const settings = await readJSON(settingsPath)
|
|
145
|
+
if (settings) {
|
|
146
|
+
log.success('.inspecto/settings.json valid')
|
|
147
|
+
} else {
|
|
148
|
+
log.error('.inspecto/settings.json has invalid JSON')
|
|
149
|
+
log.hint(
|
|
150
|
+
'Fix: Manually correct the syntax errors, or delete the file and re-run npx inspecto init',
|
|
151
|
+
)
|
|
152
|
+
result.errors++
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
log.warn('.inspecto/settings.json not found (using defaults)')
|
|
156
|
+
log.hint('Optional: npx inspecto init')
|
|
157
|
+
result.warnings++
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check 8: .gitignore status
|
|
161
|
+
const gitignoreContent = await readFile(path.join(root, '.gitignore'))
|
|
162
|
+
if (gitignoreContent) {
|
|
163
|
+
const hasLockIgnore =
|
|
164
|
+
gitignoreContent.includes('.inspecto/install.lock') || gitignoreContent.includes('.inspecto/')
|
|
165
|
+
if (!hasLockIgnore) {
|
|
166
|
+
log.warn('.inspecto/install.lock not in .gitignore')
|
|
167
|
+
log.hint('install.lock contains local machine state and should not be committed')
|
|
168
|
+
result.warnings++
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Summary
|
|
173
|
+
log.blank()
|
|
174
|
+
if (result.errors === 0 && result.warnings === 0) {
|
|
175
|
+
log.success('All checks passed. Hold Alt + Click to start!')
|
|
176
|
+
} else {
|
|
177
|
+
const parts: string[] = []
|
|
178
|
+
if (result.errors > 0) parts.push(`${result.errors} error(s)`)
|
|
179
|
+
if (result.warnings > 0) parts.push(`${result.warnings} warning(s)`)
|
|
180
|
+
console.log(
|
|
181
|
+
` ${parts.join(', ')}. ${result.errors > 0 ? 'Fix the errors above to get started.' : ''}`,
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
log.blank()
|
|
185
|
+
}
|