UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

218 lines (193 loc) 6.85 kB
/** * MCP Link Command * * Creates symlink/junction from project to global MCP config. * * @module cli/commands/mcp/link * @version 1.0.0 * @story 2.11 - MCP System Global */ const { Command } = require('commander'); const { createLink, removeLink, checkLinkStatus, LINK_STATUS, getProjectMcpPath, } = require('../../../core/mcp/symlink-manager'); const { globalConfigExists, createGlobalStructure, createGlobalConfig, } = require('../../../core/mcp/global-config-manager'); const { getGlobalMcpDir, getLinkType, isWindows, } = require('../../../core/mcp/os-detector'); const { analyzeMigration, executeMigration, MIGRATION_OPTION, } = require('../../../core/mcp/config-migrator'); /** * Create the link command * @returns {Command} Commander command instance */ function createLinkCommand() { const link = new Command('link'); link .description('Create project symlink to global MCP config') .option('-f, --force', 'Force link creation (overwrite existing)') .option('--migrate', 'Migrate existing project config to global') .option('--merge', 'Merge project config with global config') .option('--unlink', 'Remove the link (restore project independence)') .option('-v, --verbose', 'Show detailed output') .action(async (options) => { await executeLink(options); }); return link; } /** * Execute the link command * @param {Object} options - Command options */ async function executeLink(options) { const projectRoot = process.cwd(); // Handle unlink if (options.unlink) { console.log('Removing project link to global MCP config...\n'); const result = removeLink(projectRoot); if (result.success) { if (result.alreadyRemoved) { console.log('✓ No link exists to remove'); } else { console.log(`✓ Removed link at ${result.linkPath}`); } console.log('\n✅ Project is now independent from global MCP config'); } else { console.error(`✗ ${result.error}`); process.exit(1); } return; } console.log('Linking project to global MCP config...\n'); // Check current status const status = checkLinkStatus(projectRoot); if (options.verbose) { console.log('Current status:'); console.log(` Link path: ${status.linkPath}`); console.log(` Global path: ${status.globalPath}`); console.log(` Status: ${status.status}`); console.log(` Type: ${status.type}`); console.log(''); } // Already linked if (status.status === LINK_STATUS.LINKED) { console.log('✓ Already linked to global MCP config'); console.log(` ${status.linkPath}${status.target}`); console.log('\n✅ No changes needed'); return; } // Ensure global config exists if (!globalConfigExists()) { console.log('Global MCP config not found. Creating...'); const structureResult = createGlobalStructure(); if (!structureResult.success) { console.error('✗ Could not create global structure'); process.exit(1); } console.log(' ✓ Created global directory structure'); const configResult = createGlobalConfig(); if (!configResult.success) { console.error(`✗ Could not create config: ${configResult.error}`); process.exit(1); } console.log(' ✓ Created global config file'); console.log(''); } // Analyze migration scenario const analysis = analyzeMigration(projectRoot); if (options.verbose) { console.log('Migration analysis:'); console.log(` Scenario: ${analysis.scenario}`); console.log(` ${analysis.message}`); console.log(''); } // Handle existing directory with config if (status.status === LINK_STATUS.DIRECTORY) { if (analysis.projectConfig.found) { console.log('Existing MCP configuration found at project level.'); console.log(` ${analysis.projectConfig.serverCount} servers configured\n`); if (options.migrate) { console.log('Migrating to global config...'); const migrationResult = executeMigration(projectRoot, MIGRATION_OPTION.MIGRATE); if (migrationResult.success) { console.log(` ✓ Migrated ${migrationResult.results.serversMigrated} servers`); if (migrationResult.results.backupPath) { console.log(` ✓ Backup created at ${migrationResult.results.backupPath}`); } console.log(' ✓ Created link'); } else { console.error(`✗ Migration failed: ${migrationResult.errors.join(', ')}`); process.exit(1); } } else if (options.merge) { console.log('Merging with global config...'); const migrationResult = executeMigration(projectRoot, MIGRATION_OPTION.MERGE); if (migrationResult.success) { console.log(` ✓ Added ${migrationResult.results.serversMigrated} new servers`); if (migrationResult.results.mergeStats) { const stats = migrationResult.results.mergeStats; if (stats.skipped > 0) { console.log(` ⚠ Skipped ${stats.skipped} duplicate servers`); } } if (migrationResult.results.backupPath) { console.log(` ✓ Backup created at ${migrationResult.results.backupPath}`); } console.log(' ✓ Created link'); } else { console.error(`✗ Merge failed: ${migrationResult.errors.join(', ')}`); process.exit(1); } } else if (!options.force) { console.log('Options:'); console.log(' 1. --migrate Move project config to global'); console.log(' 2. --merge Merge both configs'); console.log(' 3. --force Overwrite project config (creates backup)'); console.log(''); console.log('Run again with one of these options.'); process.exit(0); } } } // Create the link if not done by migration if (status.status !== LINK_STATUS.LINKED) { const linkResult = createLink(projectRoot, { force: options.force }); if (linkResult.success) { if (linkResult.alreadyLinked) { console.log('✓ Already linked'); } else { console.log(`✓ Created ${getLinkType()}: ${linkResult.linkPath}`); console.log(` → ${linkResult.globalPath}`); } } else { console.error(`✗ ${linkResult.error}`); if (linkResult.hint) { console.log(` Hint: ${linkResult.hint}`); } if (linkResult.backup) { console.log(` Backup: ${linkResult.backup}`); } process.exit(1); } } console.log('\n✅ Project linked to global MCP!'); console.log('\nMCP servers are now shared across all linked projects.'); console.log('Run "aios mcp status" to see configuration details.'); } module.exports = { createLinkCommand, executeLink, };