superaugment
Version:
Enterprise-grade MCP server with world-class C++ analysis, robust error handling, and production-ready architecture for VS Code Augment
343 lines • 12.8 kB
JavaScript
import { readFile } from 'fs/promises';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import YAML from 'yaml';
import { z } from 'zod';
import { logger } from '../utils/logger.js';
import { ConfigValidator } from './ConfigValidator.js';
import { ConfigWatcher } from './ConfigWatcher.js';
import { ConfigurationError, ErrorCode, } from '../errors/ErrorTypes.js';
// Get the directory of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '..', '..');
// Configuration schemas
const PersonaSchema = z.object({
name: z.string(),
description: z.string(),
expertise: z.array(z.string()),
approach: z.string(),
tools: z.array(z.string()).optional(),
});
const ToolConfigSchema = z.object({
name: z.string(),
description: z.string(),
category: z.string(),
parameters: z.record(z.any()),
personas: z.array(z.string()).optional(),
examples: z.array(z.any()).optional(),
});
const ConfigSchema = z.object({
personas: z.array(PersonaSchema),
tools: z.array(ToolConfigSchema),
patterns: z.record(z.any()).optional(),
settings: z.record(z.any()).optional(),
});
/**
* Enhanced configuration manager with validation and hot reload capabilities
*/
export class ConfigManager {
config = null;
configPath;
validator;
watcher = null;
isInitialized = false;
lastValidationResult = null;
constructor() {
this.configPath = join(PROJECT_ROOT, 'config');
this.validator = new ConfigValidator();
}
/**
* Initialize the configuration manager with validation and optional hot reload
*/
async initialize(options = {}) {
const { enableHotReload = true, validateOnLoad = true } = options;
try {
logger.info('Initializing SuperAugment configuration manager...');
// Validate configuration first if enabled
if (validateOnLoad) {
logger.info('Validating configuration files...');
this.lastValidationResult = await this.validator.validateAll(this.configPath);
if (!this.lastValidationResult.isValid) {
const errorCount = this.lastValidationResult.errors.length;
const warningCount = this.lastValidationResult.warnings.length;
logger.error(`Configuration validation failed with ${errorCount} errors and ${warningCount} warnings`);
// Log first few errors for immediate visibility
this.lastValidationResult.errors.slice(0, 3).forEach(error => {
logger.error(`Config Error [${error.code}]: ${error.message}`, {
path: error.path,
suggestion: error.suggestion,
});
});
throw new ConfigurationError(`Configuration validation failed with ${errorCount} errors. Please fix configuration files before starting.`, ErrorCode.CONFIG_VALIDATION_FAILED, {
additionalInfo: {
errors: this.lastValidationResult.errors,
warnings: this.lastValidationResult.warnings,
configPath: this.configPath
}
});
}
if (this.lastValidationResult.warnings.length > 0) {
logger.warn(`Configuration loaded with ${this.lastValidationResult.warnings.length} warnings`);
this.lastValidationResult.warnings.forEach(warning => {
logger.warn(`Config Warning [${warning.code}]: ${warning.message}`, {
path: warning.path,
suggestion: warning.suggestion,
});
});
}
logger.info('Configuration validation passed successfully');
}
// Load main configuration files
const personas = await this.loadPersonas();
const tools = await this.loadTools();
const patterns = await this.loadPatterns();
const settings = await this.loadSettings();
// Combine into main config
this.config = {
personas,
tools,
patterns,
settings,
};
// Final schema validation
try {
ConfigSchema.parse(this.config);
}
catch (error) {
throw new ConfigurationError(`Configuration schema validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, ErrorCode.CONFIG_VALIDATION_FAILED, { additionalInfo: { schemaError: error } }, error instanceof Error ? error : undefined);
}
// Setup hot reload if enabled
if (enableHotReload) {
await this.setupHotReload();
}
this.isInitialized = true;
logger.info(`Configuration manager initialized successfully`, {
personas: personas.length,
tools: tools.length,
hotReload: enableHotReload,
validation: validateOnLoad,
});
}
catch (error) {
logger.error('Failed to initialize configuration manager:', error);
throw error;
}
}
/**
* Get the current configuration
*/
getConfig() {
if (!this.config) {
throw new Error('Configuration not initialized');
}
return this.config;
}
/**
* Get all personas
*/
getPersonas() {
return this.getConfig().personas;
}
/**
* Get a specific persona by name
*/
getPersona(name) {
return this.getPersonas().find(p => p.name === name);
}
/**
* Get all tool configurations
*/
getToolConfigs() {
return this.getConfig().tools;
}
/**
* Get a specific tool configuration by name
*/
getToolConfig(name) {
return this.getToolConfigs().find(t => t.name === name);
}
/**
* Load personas from configuration
*/
async loadPersonas() {
const personasPath = join(this.configPath, 'personas.yml');
const content = await readFile(personasPath, 'utf-8');
const data = YAML.parse(content);
return data.personas || [];
}
/**
* Load tool configurations
*/
async loadTools() {
const toolsPath = join(this.configPath, 'tools.yml');
const content = await readFile(toolsPath, 'utf-8');
const data = YAML.parse(content);
return data.tools || [];
}
/**
* Load patterns configuration
*/
async loadPatterns() {
try {
const patternsPath = join(this.configPath, 'patterns.yml');
const content = await readFile(patternsPath, 'utf-8');
return YAML.parse(content) || {};
}
catch (error) {
logger.warn('No patterns configuration found, using empty patterns');
return {};
}
}
/**
* Load settings configuration
*/
async loadSettings() {
try {
const settingsPath = join(this.configPath, 'settings.yml');
const content = await readFile(settingsPath, 'utf-8');
return YAML.parse(content) || {};
}
catch (error) {
logger.warn('No settings configuration found, using default settings');
return {};
}
}
/**
* Setup hot reload functionality
*/
async setupHotReload() {
try {
this.watcher = new ConfigWatcher(this.configPath, {
enableHotReload: true,
debounceMs: 1000,
validateOnChange: true,
backupOnChange: true,
maxBackups: 5,
});
// Handle configuration changes
this.watcher.on('reload', async (event) => {
logger.info(`Hot reloading configuration due to ${event.file} change`);
try {
await this.reloadConfiguration();
logger.info('Configuration hot reload completed successfully');
}
catch (error) {
logger.error('Configuration hot reload failed:', error);
}
});
// Handle validation failures
this.watcher.on('validation_failed', (event) => {
logger.error(`Configuration validation failed for ${event.file}:`, {
errors: event.validationResult?.errors.length || 0,
warnings: event.validationResult?.warnings.length || 0,
});
});
// Handle validation success
this.watcher.on('validation_passed', (event) => {
logger.info(`Configuration validation passed for ${event.file}`);
});
await this.watcher.startWatching();
logger.info('Configuration hot reload enabled');
}
catch (error) {
logger.warn('Failed to setup configuration hot reload:', error);
// Don't throw error, hot reload is optional
}
}
/**
* Reload configuration from files
*/
async reloadConfiguration() {
try {
// Validate first
const validationResult = await this.validator.validateAll(this.configPath);
if (!validationResult.isValid) {
throw new ConfigurationError(`Cannot reload invalid configuration: ${validationResult.errors.length} errors found`, ErrorCode.CONFIG_VALIDATION_FAILED, { additionalInfo: { errors: validationResult.errors } });
}
// Load new configuration
const personas = await this.loadPersonas();
const tools = await this.loadTools();
const patterns = await this.loadPatterns();
const settings = await this.loadSettings();
// Update config
this.config = {
personas,
tools,
patterns,
settings,
};
this.lastValidationResult = validationResult;
logger.info('Configuration reloaded successfully', {
personas: personas.length,
tools: tools.length,
});
}
catch (error) {
logger.error('Failed to reload configuration:', error);
throw error;
}
}
/**
* Validate current configuration
*/
async validateConfiguration() {
try {
const result = await this.validator.validateAll(this.configPath);
this.lastValidationResult = result;
return result;
}
catch (error) {
throw new ConfigurationError(`Configuration validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, ErrorCode.CONFIG_VALIDATION_FAILED, { additionalInfo: { configPath: this.configPath } }, error instanceof Error ? error : undefined);
}
}
/**
* Get last validation result
*/
getLastValidationResult() {
return this.lastValidationResult;
}
/**
* Check if configuration manager is initialized
*/
isConfigInitialized() {
return this.isInitialized;
}
/**
* Get configuration health status
*/
getHealthStatus() {
const result = {
initialized: this.isInitialized,
valid: this.lastValidationResult?.isValid ?? false,
hotReloadEnabled: this.watcher !== null,
errors: this.lastValidationResult?.errors.length ?? 0,
warnings: this.lastValidationResult?.warnings.length ?? 0,
};
if (this.lastValidationResult?.metadata.validatedAt) {
result.lastValidation = this.lastValidationResult.metadata.validatedAt;
}
return result;
}
/**
* Force reload configuration (useful for testing or manual refresh)
*/
async forceReload() {
if (!this.isInitialized) {
throw new ConfigurationError('Configuration manager not initialized', ErrorCode.INITIALIZATION_FAILED);
}
await this.reloadConfiguration();
}
/**
* Cleanup resources
*/
async cleanup() {
if (this.watcher) {
await this.watcher.stopWatching();
this.watcher = null;
}
this.isInitialized = false;
logger.info('Configuration manager cleaned up');
}
}
//# sourceMappingURL=ConfigManager.js.map