vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
224 lines (223 loc) • 7.49 kB
JavaScript
import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import chalk from 'chalk';
import logger from '../../logger.js';
const DEFAULT_CONFIG = {
display: {
enableMarkdown: true,
enableColors: true,
enableEmoji: false,
maxLineWidth: 80,
theme: 'default'
},
session: {
autoSave: true,
autoSaveInterval: 1,
sessionDirectory: path.join(os.homedir(), '.vibe', 'sessions'),
maxSessionHistory: 1000,
preserveContext: true
},
history: {
maxSize: 1000,
persistent: true,
historyFile: path.join(os.homedir(), '.vibe', 'history.json')
},
commands: {
aliasEnabled: true,
aliases: {
'q': '/quit',
'h': '/help',
'c': '/clear',
's': '/save',
't': '/tools'
},
customCommands: {}
},
performance: {
requestTimeout: 60000,
maxConcurrentRequests: 3,
cacheResponses: true,
cacheSize: 50
},
developer: {
debugMode: false,
showTimings: false,
showTokenUsage: false,
verboseErrors: false
},
tools: {
defaultTimeout: 300000,
preferredRouting: 'semantic',
enabledTools: [],
disabledTools: []
}
};
export class ConfigurationManager {
config;
configPath;
configDir;
hasChanges = false;
constructor() {
this.configDir = path.join(os.homedir(), '.vibe');
this.configPath = path.join(this.configDir, 'config.json');
this.config = { ...DEFAULT_CONFIG };
}
async initialize() {
try {
await this.ensureConfigDirectory();
await this.loadConfig();
logger.info('Configuration loaded successfully');
}
catch (error) {
logger.warn({ err: error }, 'Failed to load configuration, using defaults');
}
}
async ensureConfigDirectory() {
try {
await fs.mkdir(this.configDir, { recursive: true });
}
catch (error) {
logger.error({ err: error }, 'Failed to create config directory');
throw error;
}
}
async loadConfig() {
try {
const data = await fs.readFile(this.configPath, 'utf-8');
const userConfig = JSON.parse(data);
this.config = this.mergeConfig(DEFAULT_CONFIG, userConfig);
}
catch {
await this.saveConfig();
}
}
mergeConfig(defaults, user) {
const merged = { ...defaults };
for (const key in user) {
const k = key;
if (user[k] !== undefined) {
if (typeof user[k] === 'object' && !Array.isArray(user[k])) {
merged[k] = { ...defaults[k], ...user[k] };
}
else {
merged[k] = user[k];
}
}
}
return merged;
}
async saveConfig() {
try {
const data = JSON.stringify(this.config, null, 2);
await fs.writeFile(this.configPath, data, 'utf-8');
this.hasChanges = false;
logger.info('Configuration saved successfully');
}
catch (error) {
logger.error({ err: error }, 'Failed to save configuration');
throw error;
}
}
get(section, property) {
if (property !== undefined) {
return this.config[section][property];
}
return this.config[section];
}
set(section, propertyOrValue, value) {
if (value !== undefined) {
const prop = propertyOrValue;
this.config[section][prop] = value;
}
else {
this.config[section] = propertyOrValue;
}
this.hasChanges = true;
}
getAll() {
return { ...this.config };
}
async reset() {
this.config = { ...DEFAULT_CONFIG };
await this.saveConfig();
}
hasUnsavedChanges() {
return this.hasChanges;
}
async autoSave() {
if (this.hasChanges && this.config.session.autoSave) {
await this.saveConfig();
}
}
async loadFrom(configPath) {
try {
const data = await fs.readFile(configPath, 'utf-8');
const userConfig = JSON.parse(data);
this.config = this.mergeConfig(DEFAULT_CONFIG, userConfig);
this.hasChanges = true;
logger.info({ path: configPath }, 'Configuration loaded from custom path');
}
catch (error) {
logger.error({ err: error, path: configPath }, 'Failed to load configuration from path');
throw error;
}
}
async exportTo(exportPath) {
try {
const data = JSON.stringify(this.config, null, 2);
await fs.writeFile(exportPath, data, 'utf-8');
logger.info({ path: exportPath }, 'Configuration exported');
}
catch (error) {
logger.error({ err: error, path: exportPath }, 'Failed to export configuration');
throw error;
}
}
printConfig() {
const lines = [];
lines.push(chalk.yellow.bold('Current Configuration:'));
lines.push(chalk.gray('─'.repeat(50)));
for (const [section, values] of Object.entries(this.config)) {
lines.push(chalk.cyan(`\n${section}:`));
for (const [key, value] of Object.entries(values)) {
const formattedValue = typeof value === 'object'
? JSON.stringify(value, null, 2).split('\n').join('\n ')
: value;
lines.push(` ${chalk.green(key)}: ${formattedValue}`);
}
}
lines.push(chalk.gray('\n─'.repeat(50)));
lines.push(chalk.gray(`Config file: ${this.configPath}`));
return lines.join('\n');
}
validate() {
const errors = [];
if (this.config.display.maxLineWidth < 40 || this.config.display.maxLineWidth > 200) {
errors.push('maxLineWidth must be between 40 and 200');
}
if (this.config.session.autoSaveInterval < 0.5 || this.config.session.autoSaveInterval > 60) {
errors.push('autoSaveInterval must be between 0.5 and 60 minutes');
}
if (this.config.session.maxSessionHistory < 100 || this.config.session.maxSessionHistory > 10000) {
errors.push('maxSessionHistory must be between 100 and 10000');
}
if (this.config.history.maxSize < 100 || this.config.history.maxSize > 10000) {
errors.push('history.maxSize must be between 100 and 10000');
}
if (this.config.performance.requestTimeout < 5000 || this.config.performance.requestTimeout > 600000) {
errors.push('requestTimeout must be between 5000ms and 600000ms');
}
if (this.config.performance.maxConcurrentRequests < 1 || this.config.performance.maxConcurrentRequests > 10) {
errors.push('maxConcurrentRequests must be between 1 and 10');
}
if (this.config.performance.cacheSize < 10 || this.config.performance.cacheSize > 1000) {
errors.push('cacheSize must be between 10MB and 1000MB');
}
return {
valid: errors.length === 0,
errors
};
}
}
export const configManager = new ConfigurationManager();