UNPKG

@vibe-dev-kit/cli

Version:

Advanced Command-line toolkit that analyzes your codebase and deploys project-aware rules, memories, commands and agents to any AI coding assistant - VDK is the world's first Vibe Development Kit

322 lines (283 loc) 10.2 kB
/** * Generic IDE Integration Module * --------------------------- * Provides integration with multiple IDEs through unified detection and configuration. * Handles VS Code, Cursor, Windsurf, JetBrains, and other editors. */ import fs from 'fs'; import path from 'path'; import { detectIDEs, ensureRuleDirectory, getIDEConfigById, IDE_CONFIGURATIONS, } from '../shared/ide-configuration.js'; import { BaseIntegration } from './base-integration.js'; /** * Generic IDE integration that detects and manages multiple IDEs */ export class GenericIDEIntegration extends BaseIntegration { constructor(projectPath = process.cwd()) { super('Generic IDE', projectPath); this.detectedIDEs = []; this.ideConfigurations = IDE_CONFIGURATIONS; } /** * Detect IDE usage in the project using modernized detection * @returns {Promise<Object>} Detection result with details */ async detectUsage() { const detection = { isUsed: false, confidence: 'none', indicators: [], recommendations: [], detectedIDEs: [], }; try { // Use the modernized detectIDEs function const detectedIDEConfigs = await detectIDEs(this.projectPath); for (const ideConfig of detectedIDEConfigs) { const ideDetection = await this.detectSpecificIDE(ideConfig); if (ideDetection.isUsed) { detection.detectedIDEs.push(ideDetection); detection.isUsed = true; detection.indicators.push(...ideDetection.indicators); // Upgrade confidence based on strongest detection if (ideDetection.confidence === 'high' && detection.confidence !== 'high') { detection.confidence = 'high'; } else if (ideDetection.confidence === 'medium' && detection.confidence === 'none') { detection.confidence = 'medium'; } else if (ideDetection.confidence === 'low' && detection.confidence === 'none') { detection.confidence = 'low'; } } } } catch (error) { console.warn(`IDE detection error: ${error.message}`); } // Generate recommendations if (detection.detectedIDEs.length === 0) { detection.recommendations.push( 'No IDE configurations detected. VDK works with VS Code, Cursor, Windsurf, and other editors' ); detection.recommendations.push('Run: vdk init to set up rules for your preferred IDE'); } else if (detection.confidence === 'low') { detection.recommendations.push('IDE configurations detected but not fully configured'); detection.recommendations.push('Run: vdk init --ide-integration to set up IDE rules'); } else if (detection.confidence === 'medium') { detection.recommendations.push('IDE configurations found - consider optimizing rule setup'); detection.recommendations.push( 'Review generated rules in your IDE for optimal AI assistance' ); } else { detection.recommendations.push('IDE integrations are well configured'); detection.recommendations.push( 'Consider updating rules periodically as your project evolves' ); } return detection; } /** * Detect usage of a specific IDE using modern async methods * @param {Object} ide - IDE configuration object * @returns {Promise<Object>} Detection result for this IDE */ async detectSpecificIDE(ide) { const detection = { id: ide.id, name: ide.name, isUsed: false, confidence: 'none', indicators: [], configPath: null, rulesPath: null, }; // Check for config folder const configPath = path.join(this.projectPath, ide.configFolder); if (await this.directoryExistsAsync(configPath)) { detection.indicators.push(`${ide.name} config directory found`); detection.configPath = configPath; detection.isUsed = true; detection.confidence = 'medium'; } // Check for specific config files if (ide.configFiles) { for (const configFile of ide.configFiles) { const fullPath = configFile.startsWith('~') ? this.expandPath(configFile) : path.join(this.projectPath, configFile); if (await this.fileExistsAsync(fullPath)) { detection.indicators.push(`${ide.name} config file: ${configFile}`); detection.isUsed = true; if (detection.confidence === 'none') { detection.confidence = 'low'; } } } } // Check for rules directory const rulesPath = path.join(this.projectPath, ide.rulesFolder); if (await this.directoryExistsAsync(rulesPath)) { detection.indicators.push(`${ide.name} rules directory found`); detection.rulesPath = rulesPath; detection.confidence = 'high'; // Check if rules directory has content try { const ruleFiles = await fs.promises.readdir(rulesPath); const mdFiles = ruleFiles.filter((f) => f.endsWith('.md')); if (mdFiles.length > 0) { detection.indicators.push(`${ide.name} has ${mdFiles.length} rule files`); } } catch (error) { // Ignore read errors } } // Check for IDE-specific ignore files if (ide.ignoreFile) { const ignorePath = path.join(this.projectPath, ide.ignoreFile); if (await this.fileExistsAsync(ignorePath)) { detection.indicators.push(`${ide.name} ignore file found`); if (detection.confidence === 'none') { detection.confidence = 'low'; } } } return detection; } /** * Get configuration paths for detected IDEs * @returns {Object} Configuration paths grouped by IDE */ getConfigPaths() { const paths = {}; const detectionResult = this.getCachedDetection(); if (detectionResult.detectedIDEs) { for (const ideDetection of detectionResult.detectedIDEs) { const ide = this.ideConfigurations.find((i) => i.id === ideDetection.id); if (ide) { paths[ide.id] = { name: ide.name, configFolder: path.join(this.projectPath, ide.configFolder), rulesFolder: path.join(this.projectPath, ide.rulesFolder), configFiles: ide.configFiles?.map((f) => f.startsWith('~') ? this.expandPath(f) : path.join(this.projectPath, f) ) || [], }; } } } return paths; } /** * Initialize IDE configurations for VDK integration * @param {Object} options - Configuration options * @returns {boolean} Success status */ async initialize(options = {}) { const { verbose = false } = options; const detectionResult = this.getCachedDetection(); if (!detectionResult.isUsed) { if (verbose) { console.log('No IDEs detected - setting up generic configuration'); } // Set up generic .ai/rules configuration await this.setupGenericConfiguration(options); return true; } let success = true; const paths = this.getConfigPaths(); for (const [ideId, idePaths] of Object.entries(paths)) { try { if (verbose) { console.log(`Setting up ${idePaths.name} integration...`); } // Ensure rules directory exists using modernized method await ensureRuleDirectory(ideId, this.projectPath); // Create initial rule structure if needed await this.createInitialRules(idePaths.rulesFolder, ideId); if (verbose) { console.log(`✅ ${idePaths.name} integration configured`); } } catch (error) { if (verbose) { console.log(`❌ Failed to configure ${idePaths.name}: ${error.message}`); } success = false; } } return success; } /** * Set up generic .ai/rules configuration * @param {Object} options - Configuration options */ async setupGenericConfiguration(options = {}) { const genericRulesPath = path.join(this.projectPath, '.ai', 'rules'); await this.ensureDirectory(genericRulesPath); await this.createInitialRules(genericRulesPath, 'generic', options); } /** * Create initial rule structure for an IDE * @param {string} rulesPath - Path to rules directory * @param {string} ideId - IDE identifier * @param {Object} options - Configuration options */ async createInitialRules(rulesPath, ideId) { // Creates basic rules structure as needed const vdkConfigPath = path.join(rulesPath, '.vdk-config.json'); if (!this.fileExists(vdkConfigPath)) { const config = { ide: ideId, rulesFormat: 'md', lastUpdated: new Date().toISOString(), vdkVersion: '1.0.0', }; await this.writeJsonFile(vdkConfigPath, config); } } /** * Get detected IDEs information * @returns {Array} Array of detected IDE objects */ getDetectedIDEs() { const detection = this.getCachedDetection(); return detection.detectedIDEs || []; } /** * Check if a specific IDE is detected * @param {string} ideId - IDE identifier * @returns {boolean} True if IDE is detected */ isIDEDetected(ideId) { const detectedIDEs = this.getDetectedIDEs(); return detectedIDEs.some((ide) => ide.id === ideId); } /** * Get the primary detected IDE (highest confidence) * @returns {Object|null} Primary IDE detection or null */ getPrimaryIDE() { const detectedIDEs = this.getDetectedIDEs(); if (detectedIDEs.length === 0) return null; // Sort by confidence and return the highest const sorted = [...detectedIDEs].sort((a, b) => { const confidenceOrder = { high: 3, medium: 2, low: 1, none: 0 }; return confidenceOrder[b.confidence] - confidenceOrder[a.confidence]; }); return sorted[0]; } /** * Expand tilde paths to absolute paths * @param {string} filePath - Path that may contain tilde * @returns {string} Expanded absolute path */ expandPath(filePath) { if (filePath.startsWith('~')) { const platformPaths = this.getPlatformPaths(); return path.join(platformPaths.home, filePath.substring(1)); } return filePath; } }