vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
195 lines (194 loc) • 7.46 kB
JavaScript
import { getStorageManager } from '../core/storage/storage-manager.js';
import { getEpicContextResolver } from '../services/epic-context-resolver.js';
import logger from '../../../logger.js';
export class EpicValidator {
static instance;
constructor() { }
static getInstance() {
if (!EpicValidator.instance) {
EpicValidator.instance = new EpicValidator();
}
return EpicValidator.instance;
}
async validateAndEnsureEpic(epicId, projectId, taskContext) {
try {
logger.debug({ epicId, projectId }, 'Validating epic existence');
const storageManager = await getStorageManager();
const epicExists = await storageManager.epicExists(epicId);
if (epicExists) {
return {
valid: true,
epicId,
exists: true,
created: false
};
}
logger.info({ epicId, projectId }, 'Epic does not exist, attempting to create');
const creationResult = await this.createMissingEpic(epicId, projectId, taskContext);
if (creationResult.valid) {
return creationResult;
}
const contextResolver = getEpicContextResolver();
const resolverParams = {
projectId,
taskContext
};
const contextResult = await contextResolver.resolveEpicContext(resolverParams);
return {
valid: true,
epicId: contextResult.epicId,
exists: contextResult.source === 'existing',
created: contextResult.created || false
};
}
catch (error) {
logger.error({ err: error, epicId, projectId }, 'Epic validation failed');
return {
valid: false,
epicId,
exists: false,
created: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
async validateEpicForTask(task) {
if (!task.epicId || !task.projectId) {
return {
valid: false,
epicId: task.epicId || 'unknown',
exists: false,
created: false,
error: 'Missing epic ID or project ID'
};
}
const taskContext = task.title && task.description ? {
title: task.title,
description: task.description,
type: task.type || 'development',
tags: task.tags || []
} : undefined;
return this.validateAndEnsureEpic(task.epicId, task.projectId, taskContext);
}
async batchValidateEpics(tasks) {
const results = new Map();
const uniqueEpics = new Map();
for (const task of tasks) {
if (task.epicId && task.projectId) {
const key = `${task.projectId}:${task.epicId}`;
if (!uniqueEpics.has(key)) {
const taskContext = task.title && task.description ? {
title: task.title,
description: task.description,
type: task.type || 'development',
tags: task.tags || []
} : undefined;
uniqueEpics.set(key, {
epicId: task.epicId,
projectId: task.projectId,
taskContext
});
}
}
}
for (const [key, epicInfo] of uniqueEpics) {
try {
const result = await this.validateAndEnsureEpic(epicInfo.epicId, epicInfo.projectId, epicInfo.taskContext);
results.set(key, result);
}
catch (error) {
logger.error({ err: error, key, epicInfo }, 'Batch epic validation failed');
results.set(key, {
valid: false,
epicId: epicInfo.epicId,
exists: false,
created: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
}
return results;
}
async createMissingEpic(epicId, projectId, taskContext) {
try {
const functionalArea = this.extractFunctionalAreaFromEpicId(epicId);
if (functionalArea) {
const contextResolver = getEpicContextResolver();
const resolverParams = {
projectId,
functionalArea,
taskContext
};
const contextResult = await contextResolver.resolveEpicContext(resolverParams);
return {
valid: true,
epicId: contextResult.epicId,
exists: false,
created: contextResult.created || false
};
}
const contextResolver = getEpicContextResolver();
const resolverParams = {
projectId,
taskContext
};
const contextResult = await contextResolver.resolveEpicContext(resolverParams);
return {
valid: true,
epicId: contextResult.epicId,
exists: contextResult.source === 'existing',
created: contextResult.created || false
};
}
catch (error) {
logger.warn({ err: error, epicId, projectId }, 'Failed to create missing epic');
return {
valid: false,
epicId,
exists: false,
created: false,
error: error instanceof Error ? error.message : 'Epic creation failed'
};
}
}
extractFunctionalAreaFromEpicId(epicId) {
const match = epicId.match(/^.+-(.+)-epic$/);
if (match && match[1]) {
const functionalArea = match[1].toLowerCase();
const knownAreas = [
'auth', 'video', 'api', 'docs', 'ui', 'database',
'test', 'config', 'security', 'multilingual',
'accessibility', 'interactive', 'main'
];
if (knownAreas.includes(functionalArea)) {
return functionalArea;
}
}
return null;
}
isValidEpicIdFormat(epicId) {
return /^E\d{3}$/.test(epicId) || /^.+-\w+-epic$/.test(epicId);
}
suggestEpicId(projectId, taskContext) {
if (!taskContext) {
return `${projectId}-main-epic`;
}
const contextResolver = getEpicContextResolver();
const functionalArea = contextResolver.extractFunctionalArea(taskContext);
if (functionalArea) {
return `${projectId}-${functionalArea}-epic`;
}
return `${projectId}-main-epic`;
}
}
export function getEpicValidator() {
return EpicValidator.getInstance();
}
export async function validateAndEnsureEpic(epicId, projectId, taskContext) {
const validator = getEpicValidator();
return validator.validateAndEnsureEpic(epicId, projectId, taskContext);
}
export async function validateEpicForTask(task) {
const validator = getEpicValidator();
return validator.validateEpicForTask(task);
}