@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.
@@ -1,127 +0,0 @@
1
- // ============================================================
2
- // src/detect/ai-tool.ts — AI Tool detection (v1)
3
- //
4
- // Detects installed AI tools via PATH (CLI) or IDE extensions (Plugin).
5
- // ============================================================
6
- import path from 'node:path'
7
- import { exists, readJSON } from '../utils/fs.js'
8
- import { which } from '../utils/exec.js'
9
- import type { AiTool } from '@inspecto-dev/types'
10
-
11
- export interface AIToolDetection {
12
- id: AiTool
13
- label: string
14
- supported: boolean
15
- toolModes: Array<'cli' | 'plugin'>
16
- // The primary mode that will be written to settings if selected
17
- preferredMode: 'cli' | 'plugin'
18
- }
19
-
20
- const KNOWN_CLI_TOOLS: { id: AiTool; bin: string; label: string; supported: boolean }[] = [
21
- { id: 'claude-code', bin: 'claude', label: 'Claude Code', supported: true },
22
- { id: 'coco', bin: 'coco', label: 'Trae CLI (Coco)', supported: true },
23
- { id: 'codex', bin: 'codex', label: 'Codex CLI', supported: true },
24
- { id: 'gemini', bin: 'gemini', label: 'Gemini CLI', supported: true },
25
- ]
26
-
27
- const KNOWN_IDE_PLUGINS: { id: AiTool; extId: string; label: string; supported: boolean }[] = [
28
- { id: 'claude-code', extId: 'anthropic.claude-code', label: 'Claude Code', supported: true },
29
- { id: 'github-copilot', extId: 'github.copilot', label: 'GitHub Copilot', supported: true },
30
- { id: 'codex', extId: 'openai.chatgpt', label: 'Codex (ChatGPT)', supported: true },
31
- { id: 'gemini', extId: 'google.geminicodeassist', label: 'Gemini Code Assist', supported: true },
32
- ]
33
-
34
- export interface AIToolProbeResult {
35
- detected: AIToolDetection[]
36
- }
37
-
38
- /**
39
- * Detect all installed AI tools by checking PATH binaries and IDE extensions.
40
- */
41
- export async function detectAITools(root: string): Promise<AIToolProbeResult> {
42
- // Use a map to merge duplicate CLI/Plugin detections for the same AI tool
43
- const detectedMap = new Map<AiTool, AIToolDetection>()
44
-
45
- // 1. Detect CLI tools
46
- for (const tool of KNOWN_CLI_TOOLS) {
47
- if (await which(tool.bin)) {
48
- detectedMap.set(tool.id, {
49
- id: tool.id,
50
- label: tool.label,
51
- supported: tool.supported,
52
- toolModes: ['cli'],
53
- preferredMode: 'cli',
54
- })
55
- }
56
- }
57
-
58
- // 2. Detect IDE plugins (VS Code extensions)
59
- // Check the local workspace .vscode/extensions.json first (recommendations)
60
- const extensionsJsonPath = path.join(root, '.vscode', 'extensions.json')
61
- let recommendedExts: string[] = []
62
- if (await exists(extensionsJsonPath)) {
63
- try {
64
- const extData = await readJSON<{ recommendations?: string[] }>(extensionsJsonPath)
65
- if (extData && Array.isArray(extData.recommendations)) {
66
- recommendedExts = extData.recommendations.map(e => e.toLowerCase())
67
- }
68
- } catch {
69
- // ignore JSON parse errors here
70
- }
71
- }
72
-
73
- // Check user's global VS Code extensions folder
74
- const homeDir = process.env.HOME || process.env.USERPROFILE || ''
75
- const globalExtDir = path.join(homeDir, '.vscode', 'extensions')
76
- const globalExtExists = await exists(globalExtDir)
77
-
78
- // Wait for all plugin checks to resolve
79
- for (const plugin of KNOWN_IDE_PLUGINS) {
80
- let isInstalled = false
81
-
82
- // Check if it's explicitly recommended in the workspace
83
- if (recommendedExts.includes(plugin.extId.toLowerCase())) {
84
- isInstalled = true
85
- }
86
- // Otherwise try to find it in the global extensions folder by prefix
87
- else if (globalExtExists) {
88
- try {
89
- const { readdir } = await import('node:fs/promises')
90
- const folders = await readdir(globalExtDir)
91
- if (
92
- folders.some(f => {
93
- const lower = f.toLowerCase()
94
- return (
95
- lower === plugin.extId.toLowerCase() ||
96
- lower.startsWith(plugin.extId.toLowerCase() + '-')
97
- )
98
- })
99
- ) {
100
- isInstalled = true
101
- }
102
- } catch {
103
- // Fallback or ignore
104
- }
105
- }
106
-
107
- if (isInstalled) {
108
- // If we already detected the CLI version of this tool, we append 'plugin' to the modes
109
- // and set the preferredMode to 'plugin' since plugin integration is generally more seamless.
110
- const existing = detectedMap.get(plugin.id)
111
- if (existing) {
112
- existing.toolModes.push('plugin')
113
- existing.preferredMode = 'plugin'
114
- } else {
115
- detectedMap.set(plugin.id, {
116
- id: plugin.id,
117
- label: plugin.label,
118
- supported: plugin.supported,
119
- toolModes: ['plugin'],
120
- preferredMode: 'plugin',
121
- })
122
- }
123
- }
124
- }
125
-
126
- return { detected: Array.from(detectedMap.values()) }
127
- }