cmte
Version:
Design by Committee™ except it's just you and LLMs
103 lines (93 loc) • 3.13 kB
JavaScript
import yaml from 'js-yaml';
import { readFile, fileExists } from "../utils/fs.js";
import { logger } from "../utils/logger.js";
/**
* Validates the configuration object
* @param config Configuration object
* @throws Error if validation fails
*/
function validateConfig(config) {
// Check required top-level properties
const requiredProps = ['project', 'directives', 'services', 'ai', 'process'];
for (const prop of requiredProps) {
if (!config[prop]) {
throw new Error(`Missing required configuration property: ${prop}`);
}
}
// Check project configuration
if (!config.project.name) {
throw new Error('Project name is required');
}
if (!config.project.outputDirectory) {
throw new Error('Project output directory is required');
}
// Check directives
if (!Array.isArray(config.directives) || config.directives.length === 0) {
throw new Error('At least one directive type must be defined');
}
// Check services
if (!Array.isArray(config.services) || config.services.length === 0) {
throw new Error('At least one service must be defined');
}
// Check AI configuration
if (!config.ai.provider) {
throw new Error('AI provider is required');
}
if (!config.ai.defaultModel) {
throw new Error('Default AI model is required');
}
// Check process flows
if (!Array.isArray(config.process.flows) || config.process.flows.length === 0) {
throw new Error('At least one process flow must be defined');
}
// Validate each flow has phases
for (const flow of config.process.flows) {
if (!flow.name) {
throw new Error('All flows must have a name');
}
if (!Array.isArray(flow.phases) || flow.phases.length === 0) {
throw new Error(`Flow "${flow.name}" must have at least one phase`);
}
}
logger.debug('Configuration validation passed');
}
/**
* Loads and validates the configuration from a YAML file
* @param configPath Path to the configuration file
* @returns Validated configuration object
*/
export async function loadConfig(configPath) {
logger.info(`Loading configuration from ${configPath}`);
// Check if the file exists
if (!(await fileExists(configPath))) {
throw new Error(`Configuration file not found: ${configPath}`);
}
try {
// Read the YAML file
const yamlContent = await readFile(configPath);
// Parse YAML
const config = yaml.load(yamlContent);
// Validate the configuration
validateConfig(config);
logger.info('Configuration loaded successfully');
return config;
} catch (error) {
if (error instanceof yaml.YAMLException) {
logger.error('Invalid YAML format', {
error
});
throw new Error(`Invalid YAML format in ${configPath}: ${error.message}`);
} else if (error instanceof Error) {
logger.error('Failed to load configuration', {
error
});
throw error;
} else {
// For any other unknown error types
logger.error('Unknown error loading configuration', {
error
});
throw new Error(`Unknown error loading configuration: ${String(error)}`);
}
}
}