@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
453 lines (398 loc) • 12.9 kB
JavaScript
/**
* Integration Manager
* ------------------
* Central manager for all VDK integrations (IDEs, AI tools, platforms)
* Handles discovery, registration, and coordination of integrations.
*/
import chalk from 'chalk'
import { BaseIntegration } from './base-integration.js'
/**
* Central manager for all VDK integrations
*/
export class IntegrationManager {
constructor(projectPath = process.cwd()) {
this.projectPath = projectPath
this.integrations = new Map()
this.detectionResults = new Map()
this.lastScan = null
}
/**
* Register an integration
* @param {BaseIntegration} integration - Integration instance
*/
register(integration) {
if (!(integration instanceof BaseIntegration)) {
throw new Error('Integration must extend BaseIntegration')
}
this.integrations.set(integration.name, integration)
}
/**
* Register multiple integrations
* @param {Array<BaseIntegration>} integrations - Array of integration instances
*/
registerMultiple(integrations) {
integrations.forEach((integration) => this.register(integration))
}
/**
* Discover and register all available integrations
* @param {Object} options - Discovery options
* @returns {Object} Discovery results
*/
async discoverIntegrations(options = {}) {
const { verbose = false } = options
// Dynamically import and register all integrations
const integrationModules = [
'./claude-code-integration.js',
'./cursor-integration.js',
'./windsurf-integration.js',
'./github-copilot-integration.js',
'./generic-ide-integration.js',
]
const results = {
loaded: [],
failed: [],
registered: 0,
}
for (const modulePath of integrationModules) {
try {
const module = await import(modulePath)
let foundIntegration = false
// Look for integration class exports
for (const [exportName, exportValue] of Object.entries(module)) {
if (
exportName.includes('Integration') &&
typeof exportValue === 'function' &&
exportValue.prototype instanceof BaseIntegration
) {
try {
const integration = new exportValue(this.projectPath)
this.register(integration)
results.loaded.push({
module: modulePath,
class: exportName,
name: integration.name,
})
results.registered++
foundIntegration = true
} catch (constructorError) {
results.failed.push({
module: modulePath,
class: exportName,
error: `Constructor failed: ${constructorError.message}`,
})
}
}
}
if (!foundIntegration) {
results.failed.push({
module: modulePath,
error: 'No valid Integration class found in module',
})
}
} catch (error) {
// Integration module doesn't exist or failed to load - that's OK
// We only register integrations that are available
results.failed.push({
module: modulePath,
error: error.message,
})
if (process.env.VDK_DEBUG || verbose) {
console.warn(
chalk.yellow(`Failed to load integration module ${modulePath}: ${error.message}`)
)
}
}
}
if (verbose) {
console.log(chalk.blue(`Discovery complete: ${results.registered} integrations registered`))
if (results.failed.length > 0 && process.env.VDK_DEBUG) {
console.log(chalk.gray(`Failed modules: ${results.failed.length}`))
}
}
return results
}
/**
* Scan all registered integrations for usage
* @param {Object} options - Scan options
* @returns {Object} Scan results
*/
async scanAll(options = {}) {
const { verbose = false, force = false } = options
if (verbose) {
console.log(chalk.blue('🔍 Scanning for integration usage...'))
}
const results = {
active: [],
inactive: [],
recommendations: [],
errors: [],
summary: {},
}
// Ensure we have integrations to scan
if (this.integrations.size === 0) {
if (verbose) {
console.log(chalk.yellow('⚠️ No integrations registered. Run discoverIntegrations() first.'))
}
results.errors.push('No integrations registered')
return results
}
for (const [name, integration] of this.integrations) {
try {
// Validate integration instance
if (!integration || typeof integration.getCachedDetection !== 'function') {
throw new Error(`Invalid integration instance for ${name}`)
}
const detection = integration.getCachedDetection(force)
this.detectionResults.set(name, detection)
const integrationResult = {
name,
isActive: integration.isActive(),
confidence: integration.getConfidence(),
indicators: integration.getIndicators(),
recommendations: integration.getRecommendations(),
detection,
}
if (integrationResult.isActive) {
results.active.push(integrationResult)
} else {
results.inactive.push(integrationResult)
}
// Collect all recommendations
results.recommendations.push(...integrationResult.recommendations)
if (verbose && integrationResult.indicators.length > 0) {
console.log(chalk.gray(` • ${name}: ${integrationResult.confidence} confidence`))
integrationResult.indicators.forEach((indicator) => {
console.log(chalk.gray(` - ${indicator}`))
})
}
} catch (error) {
const errorInfo = {
integration: name,
error: error.message,
timestamp: new Date().toISOString(),
}
results.errors.push(errorInfo)
if (verbose) {
console.log(chalk.red(`❌ Failed to scan ${name}: ${error.message}`))
if (process.env.VDK_DEBUG) {
console.log(chalk.gray(`Debug: ${error.stack}`))
}
}
// Add to inactive with error indicator
results.inactive.push({
name,
isActive: false,
confidence: 'error',
indicators: [`Scan failed: ${error.message}`],
recommendations: [`Fix ${name} integration configuration`],
detection: { isUsed: false, confidence: 'error', indicators: [], recommendations: [] },
})
}
}
// Generate summary
results.summary = {
totalIntegrations: this.integrations.size,
activeIntegrations: results.active.length,
highConfidenceIntegrations: results.active.filter((r) => r.confidence === 'high').length,
recommendationCount: results.recommendations.length,
scanTime: new Date().toISOString(),
}
this.lastScan = results
return results
}
/**
* Get integration by name
* @param {string} name - Integration name
* @returns {BaseIntegration|null} Integration instance or null
*/
getIntegration(name) {
return this.integrations.get(name) || null
}
/**
* Get all registered integration names
* @returns {Array<string>} Array of integration names
*/
getIntegrationNames() {
return Array.from(this.integrations.keys())
}
/**
* Get all registered integrations
* @returns {Array<BaseIntegration>} Array of integration instances
*/
getAllIntegrations() {
return Array.from(this.integrations.values())
}
/**
* Get active integrations (high confidence)
* @returns {Array<Object>} Array of active integration results
*/
getActiveIntegrations() {
if (!this.lastScan) {
return []
}
return this.lastScan.active.filter((integration) => integration.confidence === 'high')
}
/**
* Get recommendations for all integrations
* @returns {Array<string>} Array of recommendations
*/
getAllRecommendations() {
if (!this.lastScan) {
return []
}
return this.lastScan.recommendations
}
/**
* Initialize active integrations with VDK configuration
* @param {Object} options - Initialization options
* @returns {Object} Initialization results
*/
async initializeActive(options = {}) {
const { verbose = false } = options
const results = {
successful: [],
failed: [],
skipped: [],
}
const activeIntegrations = this.getActiveIntegrations()
if (activeIntegrations.length === 0) {
if (verbose) {
console.log(chalk.yellow('No active integrations found to initialize'))
}
return results
}
for (const integrationResult of activeIntegrations) {
const integration = this.getIntegration(integrationResult.name)
if (!integration) {
results.failed.push({
name: integrationResult.name,
error: 'Integration not found',
})
continue
}
try {
if (verbose) {
console.log(chalk.blue(`Initializing ${integration.name} integration...`))
}
const success = await integration.initialize({
...options,
projectPath: this.projectPath,
})
if (success) {
results.successful.push({
name: integration.name,
confidence: integration.getConfidence(),
})
if (verbose) {
console.log(chalk.green(`✅ ${integration.name} initialized successfully`))
}
} else {
results.failed.push({
name: integration.name,
error: 'Initialization returned false',
})
}
} catch (error) {
results.failed.push({
name: integration.name,
error: error.message,
})
if (verbose) {
console.log(chalk.red(`❌ Failed to initialize ${integration.name}: ${error.message}`))
}
}
}
return results
}
/**
* Get integration status summary for display
* @returns {Object} Status summary
*/
getStatusSummary() {
if (!this.lastScan) {
return {
message: 'No integrations scanned yet',
integrations: [],
}
}
const { summary, active, inactive } = this.lastScan
let message = ''
if (summary.activeIntegrations === 0) {
message = 'No active integrations detected'
} else if (summary.highConfidenceIntegrations > 0) {
message = `${summary.highConfidenceIntegrations} high-confidence integration(s) detected`
} else {
message = `${summary.activeIntegrations} integration(s) detected with varying confidence`
}
return {
message,
summary,
active: active.map((i) => ({
name: i.name,
confidence: i.confidence,
indicatorCount: i.indicators.length,
})),
inactive: inactive.map((i) => ({
name: i.name,
confidence: i.confidence,
})),
}
}
/**
* Clear all cached detection results
*/
clearCache() {
for (const integration of this.integrations.values()) {
integration._detectionCache = null
integration._detectionCacheTime = null
}
this.detectionResults.clear()
this.lastScan = null
}
/**
* Get integration-specific recommendations
* @param {string} integrationName - Name of integration
* @returns {Array<string>} Array of recommendations for this integration
*/
getIntegrationRecommendations(integrationName) {
const integration = this.getIntegration(integrationName)
if (!integration) {
return []
}
return integration.getRecommendations()
}
/**
* Check if a specific integration is active
* @param {string} integrationName - Name of integration
* @returns {boolean} True if integration is active
*/
isIntegrationActive(integrationName) {
const integration = this.getIntegration(integrationName)
if (!integration) {
return false
}
return integration.isActive()
}
/**
* Get detailed integration information
* @param {string} integrationName - Name of integration
* @returns {Object|null} Detailed integration info or null
*/
getIntegrationDetails(integrationName) {
const integration = this.getIntegration(integrationName)
if (!integration) {
return null
}
const detection = integration.getCachedDetection()
return {
name: integration.name,
isActive: integration.isActive(),
confidence: integration.getConfidence(),
indicators: integration.getIndicators(),
recommendations: integration.getRecommendations(),
configPaths: integration.getConfigPaths(),
summary: integration.getSummary(),
detection,
}
}
}