UNPKG

@moikas/code-audit-mcp

Version:

AI-powered code auditing via MCP using local Ollama models for security, performance, and quality analysis

526 lines 17.4 kB
/** * Config command - Manage configuration settings */ import chalk from 'chalk'; import ora from 'ora'; import boxen from 'boxen'; import inquirer from 'inquirer'; import { getConfig, getConfigValue, setConfigValue, resetConfig, getConfigManager, } from '../utils/config.js'; /** * Config command handler */ export async function configCommand(options) { try { // Handle --show option if (options.show) { await showConfiguration(); return; } // Handle --reset option if (options.reset) { await resetConfiguration(); return; } // Handle --set option if (options.set) { await setConfiguration(options.set); return; } // Handle --get option if (options.get) { await getConfiguration(options.get); return; } // If no options provided, show interactive config menu await interactiveConfig(); } catch (error) { console.error(chalk.red('Configuration error:'), error instanceof Error ? error.message : 'Unknown error'); process.exit(1); } } /** * Show current configuration */ async function showConfiguration() { const spinner = ora('Loading configuration...').start(); try { const config = await getConfig(); const configManager = getConfigManager(); const paths = configManager.getConfigPaths(); const validation = configManager.validateConfig(); spinner.succeed('Configuration loaded'); console.log(); console.log(chalk.blue.bold('📋 Current Configuration')); console.log(); // Show configuration source info console.log(chalk.cyan('Configuration Files:')); console.log(` Global: ${chalk.gray(paths.global)}`); if (paths.project) { console.log(` Project: ${chalk.gray(paths.project)}`); } else { console.log(` Project: ${chalk.gray('None')}`); } console.log(); // Show validation status const statusIcon = validation.isValid ? '✅' : '❌'; const statusColor = validation.isValid ? chalk.green : chalk.red; console.log(`${statusIcon} Status: ${statusColor(validation.isValid ? 'Valid' : 'Invalid')}`); if (!validation.isValid) { console.log(chalk.red(' Errors:')); validation.errors.forEach((error) => { console.log(chalk.red(` • ${error}`)); }); } console.log(); // Display configuration sections displayConfigSection('Ollama Settings', config.ollama); displayConfigSection('Audit Rules', config.audit.rules); displayConfigSection('Output Settings', config.audit.output); displayConfigSection('Server Settings', config.server); displayConfigSection('Update Settings', config.updates); displayConfigSection('Telemetry', config.telemetry); } catch (error) { spinner.fail('Failed to load configuration'); throw error; } } /** * Display a configuration section */ function displayConfigSection(title, section) { console.log(chalk.cyan.bold(title + ':')); if (typeof section === 'object' && section !== null) { for (const [key, value] of Object.entries(section)) { const formattedValue = formatConfigValue(value); console.log(` ${key}: ${formattedValue}`); } } console.log(); } /** * Format configuration value for display */ function formatConfigValue(value) { if (value === null || value === undefined) { return chalk.gray('not set'); } if (typeof value === 'boolean') { return value ? chalk.green('✓') : chalk.red('✗'); } if (Array.isArray(value)) { return chalk.yellow(`[${value.join(', ')}]`); } if (typeof value === 'string') { return chalk.yellow(value); } return chalk.yellow(String(value)); } /** * Reset configuration to defaults */ async function resetConfiguration() { const spinner = ora('Preparing to reset configuration...').start(); spinner.stop(); console.log(); console.log(chalk.yellow.bold('⚠️ Configuration Reset')); console.log(chalk.gray('This will reset your global configuration to default values.')); console.log(); const { confirmed } = await inquirer.prompt([ { type: 'confirm', name: 'confirmed', message: 'Are you sure you want to reset the configuration?', default: false, }, ]); if (!confirmed) { console.log(chalk.gray('Reset cancelled.')); return; } const resetSpinner = ora('Resetting configuration...').start(); try { const success = await resetConfig(async () => true); if (success) { resetSpinner.succeed('Configuration reset successfully'); console.log(); console.log(chalk.green('✅ Configuration has been reset to defaults')); console.log(chalk.gray('Run "code-audit config --show" to view the default settings')); } else { resetSpinner.fail('Reset was cancelled'); } } catch (error) { resetSpinner.fail('Failed to reset configuration'); throw error; } } /** * Set configuration value */ async function setConfiguration(keyValue) { const [key, ...valueParts] = keyValue.split('='); const value = valueParts.join('='); if (!key || value === undefined) { throw new Error('Invalid format. Use: --set key=value'); } const spinner = ora(`Setting ${key}...`).start(); try { // Parse value based on expected type const parsedValue = parseConfigValue(value); await setConfigValue(key.trim(), parsedValue); spinner.succeed(`Configuration updated: ${key} = ${value}`); console.log(); console.log(chalk.green('✅ Configuration value updated successfully')); console.log(chalk.gray('Run "code-audit config --show" to view all settings')); } catch (error) { spinner.fail(`Failed to set ${key}`); throw error; } } /** * Parse configuration value from string */ function parseConfigValue(value) { const trimmed = value.trim(); // Boolean values if (trimmed === 'true') return true; if (trimmed === 'false') return false; // Null/undefined if (trimmed === 'null') return null; if (trimmed === 'undefined') return undefined; // Numbers if (/^\d+$/.test(trimmed)) { return parseInt(trimmed, 10); } if (/^\d+\.\d+$/.test(trimmed)) { return parseFloat(trimmed); } // Arrays (simple comma-separated) if (trimmed.startsWith('[') && trimmed.endsWith(']')) { const arrayContent = trimmed.slice(1, -1); if (arrayContent.trim() === '') return []; return arrayContent.split(',').map((item) => item.trim()); } // Objects (simple JSON) if (trimmed.startsWith('{') && trimmed.endsWith('}')) { try { return JSON.parse(trimmed); } catch { throw new Error(`Invalid JSON object: ${trimmed}`); } } // Default to string return trimmed; } /** * Get configuration value */ async function getConfiguration(key) { const spinner = ora(`Getting ${key}...`).start(); try { const value = await getConfigValue(key.trim()); spinner.succeed(`Retrieved ${key}`); console.log(); if (value === undefined) { console.log(chalk.gray(`Configuration key "${key}" is not set`)); } else { console.log(chalk.cyan('Key:'), chalk.yellow(key)); console.log(chalk.cyan('Value:'), formatConfigValue(value)); console.log(chalk.cyan('Type:'), chalk.gray(typeof value)); } } catch (error) { spinner.fail(`Failed to get ${key}`); throw error; } } /** * Interactive configuration menu */ async function interactiveConfig() { console.log(chalk.blue.bold('⚙️ Configuration Manager')); console.log(); const { action } = await inquirer.prompt([ { type: 'list', name: 'action', message: 'What would you like to do?', choices: [ { name: 'View current configuration', value: 'show' }, { name: 'Edit Ollama settings', value: 'ollama' }, { name: 'Configure audit rules', value: 'audit' }, { name: 'Server settings', value: 'server' }, { name: 'Reset to defaults', value: 'reset' }, { name: 'Export configuration', value: 'export' }, { name: 'Cancel', value: 'cancel' }, ], }, ]); switch (action) { case 'show': await showConfiguration(); break; case 'ollama': await configureOllama(); break; case 'audit': await configureAudit(); break; case 'server': await configureServer(); break; case 'reset': await resetConfiguration(); break; case 'export': await exportConfiguration(); break; case 'cancel': console.log(chalk.gray('Configuration cancelled.')); break; } } /** * Configure Ollama settings */ async function configureOllama() { const config = await getConfig(); console.log(); console.log(chalk.cyan.bold('🦙 Ollama Configuration')); console.log(); const answers = await inquirer.prompt([ { type: 'input', name: 'host', message: 'Ollama host URL:', default: config.ollama.host, validate: (input) => { if (!input.trim()) return 'Host URL is required'; try { new URL(input); return true; } catch { return 'Invalid URL format'; } }, }, { type: 'number', name: 'timeout', message: 'Timeout (milliseconds):', default: config.ollama.timeout, validate: (input) => (input && input >= 1000) || 'Timeout must be at least 1000ms', }, { type: 'input', name: 'primary', message: 'Primary model:', default: config.ollama.models.primary, validate: (input) => (input.trim() ? true : 'Primary model is required'), }, ]); const spinner = ora('Updating Ollama configuration...').start(); try { await setConfigValue('ollama.host', answers.host); await setConfigValue('ollama.timeout', answers.timeout); await setConfigValue('ollama.models.primary', answers.primary); spinner.succeed('Ollama configuration updated'); console.log(chalk.green('✅ Ollama settings saved successfully')); } catch (error) { spinner.fail('Failed to update Ollama configuration'); throw error; } } /** * Configure audit settings */ async function configureAudit() { const config = await getConfig(); console.log(); console.log(chalk.cyan.bold('🔍 Audit Configuration')); console.log(); const auditRules = await inquirer.prompt([ { type: 'checkbox', name: 'rules', message: 'Select audit rules to enable:', choices: [ { name: 'Security Analysis', value: 'security', checked: config.audit.rules.security, }, { name: 'Performance Analysis', value: 'performance', checked: config.audit.rules.performance, }, { name: 'Code Quality', value: 'quality', checked: config.audit.rules.quality, }, { name: 'Documentation Check', value: 'documentation', checked: config.audit.rules.documentation, }, { name: 'Test Coverage', value: 'testing', checked: config.audit.rules.testing, }, { name: 'Architecture Review', value: 'architecture', checked: config.audit.rules.architecture, }, { name: 'Completeness Check', value: 'completeness', checked: config.audit.rules.completeness, }, ], }, ]); const outputSettings = await inquirer.prompt([ { type: 'list', name: 'format', message: 'Output format:', choices: ['json', 'markdown', 'html'], default: config.audit.output.format, }, { type: 'list', name: 'verbosity', message: 'Verbosity level:', choices: ['minimal', 'normal', 'detailed'], default: config.audit.output.verbosity, }, { type: 'confirm', name: 'includeMetrics', message: 'Include metrics in output:', default: config.audit.output.includeMetrics, }, ]); const spinner = ora('Updating audit configuration...').start(); try { // Update audit rules for (const rule of [ 'security', 'performance', 'quality', 'documentation', 'testing', 'architecture', 'completeness', ]) { await setConfigValue(`audit.rules.${rule}`, auditRules.rules.includes(rule)); } // Update output settings await setConfigValue('audit.output.format', outputSettings.format); await setConfigValue('audit.output.verbosity', outputSettings.verbosity); await setConfigValue('audit.output.includeMetrics', outputSettings.includeMetrics); spinner.succeed('Audit configuration updated'); console.log(chalk.green('✅ Audit settings saved successfully')); } catch (error) { spinner.fail('Failed to update audit configuration'); throw error; } } /** * Configure server settings */ async function configureServer() { const config = await getConfig(); console.log(); console.log(chalk.cyan.bold('🖥️ Server Configuration')); console.log(); const answers = await inquirer.prompt([ { type: 'list', name: 'transport', message: 'Transport method:', choices: [ { name: 'Standard I/O (recommended)', value: 'stdio' }, { name: 'HTTP', value: 'http' }, ], default: config.server.transport, }, { type: 'number', name: 'port', message: 'HTTP port (if using HTTP transport):', default: config.server.port, when: (answers) => answers.transport === 'http', validate: (input) => (input && input >= 1 && input <= 65535) || 'Port must be between 1 and 65535', }, { type: 'list', name: 'logLevel', message: 'Log level:', choices: ['error', 'warn', 'info', 'debug'], default: config.server.logLevel, }, ]); const spinner = ora('Updating server configuration...').start(); try { await setConfigValue('server.transport', answers.transport); if (answers.port) { await setConfigValue('server.port', answers.port); } await setConfigValue('server.logLevel', answers.logLevel); spinner.succeed('Server configuration updated'); console.log(chalk.green('✅ Server settings saved successfully')); } catch (error) { spinner.fail('Failed to update server configuration'); throw error; } } /** * Export configuration */ async function exportConfiguration() { const spinner = ora('Exporting configuration...').start(); try { const configManager = getConfigManager(); const exported = configManager.exportConfig(); spinner.succeed('Configuration exported'); console.log(); console.log(chalk.cyan.bold('📤 Configuration Export')); console.log(); console.log(boxen(JSON.stringify(exported, null, 2), { padding: 1, borderColor: 'cyan', title: 'Configuration Data', titleAlignment: 'center', })); console.log(); console.log(chalk.gray('Copy this data to backup or transfer your configuration.')); } catch (error) { spinner.fail('Failed to export configuration'); throw error; } } //# sourceMappingURL=config.js.map