task-engine-ai-core
Version:
Revolutionary AI-driven task management system with complete transformation trilogy: Frontend v0.1.0, Backend v0.2.0, CLI v0.3.0 - Enterprise-grade performance with 95% improvements
319 lines (285 loc) • 11.7 kB
JavaScript
/**
* IDE Detection Utilities
* Provides utilities for detecting and connecting to various IDEs
*/
import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import { spawn } from 'child_process';
import logger from '../../mcp-server/src/logger.js';
export class IDEDetection {
constructor() {
this.detectionCache = new Map();
this.cacheTimeout = 30000; // 30 seconds
}
/**
* Auto-detect available IDEs
*/
async detectAvailableIDEs() {
const cacheKey = 'available_ides';
const cached = this.detectionCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
const detectionResults = {
cursor: await this.detectCursor(),
vscode: await this.detectVSCode(),
windsurf: await this.detectWindsurf()
};
// Cache results
this.detectionCache.set(cacheKey, {
data: detectionResults,
timestamp: Date.now()
});
return detectionResults;
}
/**
* Detect Cursor IDE
*/
async detectCursor() {
try {
const homeDir = os.homedir();
const possiblePaths = [
path.join(homeDir, '.cursor'),
path.join(homeDir, 'Library', 'Application Support', 'Cursor'), // macOS
path.join(homeDir, 'AppData', 'Roaming', 'Cursor'), // Windows
path.join(homeDir, '.config', 'cursor') // Linux
];
for (const cursorPath of possiblePaths) {
try {
const stats = await fs.stat(cursorPath);
if (stats.isDirectory()) {
// Check for config file
const configPath = path.join(cursorPath, 'config.json');
try {
await fs.access(configPath);
const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
return {
detected: true,
path: cursorPath,
configPath,
config,
version: config.version || 'unknown',
apiPort: config.apiPort || 42000,
confidence: 0.9
};
} catch (configError) {
// Directory exists but no config - still likely Cursor
return {
detected: true,
path: cursorPath,
configPath: null,
config: null,
version: 'unknown',
apiPort: 42000,
confidence: 0.7
};
}
}
} catch (error) {
continue;
}
}
// Try to detect running Cursor process
const runningCursor = await this.detectRunningProcess('cursor');
if (runningCursor) {
return {
detected: true,
path: null,
configPath: null,
config: null,
version: 'unknown',
apiPort: 42000,
confidence: 0.6,
processInfo: runningCursor
};
}
return { detected: false, confidence: 0 };
} catch (error) {
logger.debug('Error detecting Cursor:', error);
return { detected: false, confidence: 0, error: error.message };
}
}
/**
* Detect VS Code
*/
async detectVSCode() {
try {
const homeDir = os.homedir();
const possiblePaths = [
path.join(homeDir, '.vscode'),
path.join(homeDir, 'Library', 'Application Support', 'Code'), // macOS
path.join(homeDir, 'AppData', 'Roaming', 'Code'), // Windows
path.join(homeDir, '.config', 'Code') // Linux
];
for (const vscodePath of possiblePaths) {
try {
const stats = await fs.stat(vscodePath);
if (stats.isDirectory()) {
// Check for extensions directory
const extensionsPath = path.join(vscodePath, 'extensions');
try {
const extensions = await fs.readdir(extensionsPath);
const aiExtensions = extensions.filter(ext =>
ext.includes('github.copilot') ||
ext.includes('ms-vscode.vscode-ai') ||
ext.includes('continue.continue')
);
return {
detected: true,
path: vscodePath,
extensionsPath,
aiExtensions,
confidence: aiExtensions.length > 0 ? 0.9 : 0.6
};
} catch (extError) {
return {
detected: true,
path: vscodePath,
extensionsPath: null,
aiExtensions: [],
confidence: 0.5
};
}
}
} catch (error) {
continue;
}
}
// Try to detect running VS Code process
const runningVSCode = await this.detectRunningProcess('code');
if (runningVSCode) {
return {
detected: true,
path: null,
extensionsPath: null,
aiExtensions: [],
confidence: 0.4,
processInfo: runningVSCode
};
}
return { detected: false, confidence: 0 };
} catch (error) {
logger.debug('Error detecting VS Code:', error);
return { detected: false, confidence: 0, error: error.message };
}
}
/**
* Detect Windsurf IDE
*/
async detectWindsurf() {
try {
const homeDir = os.homedir();
const possiblePaths = [
path.join(homeDir, '.windsurf'),
path.join(homeDir, 'Library', 'Application Support', 'Windsurf'), // macOS
path.join(homeDir, 'AppData', 'Roaming', 'Windsurf'), // Windows
path.join(homeDir, '.config', 'windsurf') // Linux
];
for (const windsurfPath of possiblePaths) {
try {
const stats = await fs.stat(windsurfPath);
if (stats.isDirectory()) {
// Check for config file
const configPath = path.join(windsurfPath, 'config.json');
try {
await fs.access(configPath);
const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
return {
detected: true,
path: windsurfPath,
configPath,
config,
version: config.version || 'unknown',
cascadePort: config.cascadePort || 43000,
confidence: 0.9
};
} catch (configError) {
return {
detected: true,
path: windsurfPath,
configPath: null,
config: null,
version: 'unknown',
cascadePort: 43000,
confidence: 0.7
};
}
}
} catch (error) {
continue;
}
}
// Try to detect running Windsurf process
const runningWindsurf = await this.detectRunningProcess('windsurf');
if (runningWindsurf) {
return {
detected: true,
path: null,
configPath: null,
config: null,
version: 'unknown',
cascadePort: 43000,
confidence: 0.6,
processInfo: runningWindsurf
};
}
return { detected: false, confidence: 0 };
} catch (error) {
logger.debug('Error detecting Windsurf:', error);
return { detected: false, confidence: 0, error: error.message };
}
}
/**
* Detect running process by name
*/
async detectRunningProcess(processName) {
return new Promise((resolve) => {
const isWindows = process.platform === 'win32';
const command = isWindows ? 'tasklist' : 'ps';
const args = isWindows ? ['/FI', `IMAGENAME eq ${processName}*`] : ['aux'];
const proc = spawn(command, args, { stdio: 'pipe' });
let output = '';
proc.stdout.on('data', (data) => {
output += data.toString();
});
proc.on('close', (code) => {
if (code === 0 && output.toLowerCase().includes(processName.toLowerCase())) {
resolve({
found: true,
processName,
output: output.split('\n').filter(line =>
line.toLowerCase().includes(processName.toLowerCase())
)
});
} else {
resolve(null);
}
});
proc.on('error', () => {
resolve(null);
});
// Timeout after 5 seconds
setTimeout(() => {
proc.kill();
resolve(null);
}, 5000);
});
}
/**
* Get the best IDE candidate
*/
async getBestIDE() {
const ides = await this.detectAvailableIDEs();
// Sort by confidence score
const candidates = Object.entries(ides)
.filter(([_, info]) => info.detected)
.sort(([_, a], [__, b]) => b.confidence - a.confidence);
if (candidates.length === 0) {
return { type: null, info: null };
}
const [type, info] = candidates[0];
return { type, info };
}
}
export default IDEDetection;