vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
959 lines (958 loc) • 40 kB
JavaScript
import fs from 'fs/promises';
import path from 'path';
import logger from '../../../logger.js';
import { executeCodeMapGeneration } from '../../code-map-generator/index.js';
import { OpenRouterConfigManager } from '../../../utils/openrouter-config-manager.js';
import { getTimeoutManager } from '../utils/timeout-manager.js';
import { getJobTimeoutConfigManager } from '../../../utils/job-timeout-config-manager.js';
export class CodeMapIntegrationService {
static instance;
config;
codeMapCache = new Map();
updateSubscriptions = new Map();
performanceMetrics = new Map();
constructor() {
this.config = {
maxAge: 24 * 60 * 60 * 1000,
autoRefresh: true,
generationTimeout: 5 * 60 * 1000,
enableCaching: true,
enablePerformanceMonitoring: true,
maxCacheSize: 50,
stalenessCheckInterval: 60 * 60 * 1000
};
logger.debug('Code map integration service initialized');
}
static getInstance() {
if (!CodeMapIntegrationService.instance) {
CodeMapIntegrationService.instance = new CodeMapIntegrationService();
}
return CodeMapIntegrationService.instance;
}
async generateCodeMap(projectPath, config) {
const startTime = Date.now();
try {
logger.info({ projectPath }, 'Starting code map generation');
const absoluteProjectPath = path.resolve(projectPath);
await this.validateProjectPath(absoluteProjectPath);
const params = {
allowedMappingDirectory: absoluteProjectPath,
...config
};
const jobId = `codemap-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
const { jobManager } = await import('../../../services/job-manager/index.js');
jobManager.createJobWithId(jobId, 'code-map-generation', params);
logger.debug({ jobId, projectPath }, 'Registered job in job manager for code map integration');
const configManager = OpenRouterConfigManager.getInstance();
const openRouterConfig = await configManager.getOpenRouterConfig();
const timeoutManager = getTimeoutManager();
const jobTimeoutConfigManager = getJobTimeoutConfigManager();
const toolConfig = jobTimeoutConfigManager.getToolTimeoutConfig('map-codebase');
const customTimeout = toolConfig?.customTimeoutMs || 300000;
const result = await timeoutManager.raceWithTimeout('fileOperations', executeCodeMapGeneration(params, openRouterConfig, {
sessionId: `codemap-session-${Date.now()}`,
transportType: 'stdio'
}, jobId), customTimeout);
const generationTime = Date.now() - startTime;
if (result.isError) {
const errorMessage = this.extractErrorMessage(result.content);
logger.error({ projectPath, error: errorMessage }, 'Code map generation failed');
return {
success: false,
error: errorMessage,
generationTime,
jobId
};
}
const contentString = this.extractContentString(result.content);
const filePath = this.extractFilePathFromResult(contentString);
if (filePath) {
if (this.config.enableCaching) {
await this.updateCodeMapCache(projectPath, filePath);
}
logger.info({
projectPath,
filePath,
generationTime,
jobId
}, 'Code map generation completed successfully');
return {
success: true,
filePath,
generationTime,
jobId
};
}
else {
logger.warn({ projectPath, result: result.content }, 'Code map generated but file path not found');
return {
success: false,
error: 'Generated code map but could not determine file path',
generationTime,
jobId
};
}
}
catch (error) {
const generationTime = Date.now() - startTime;
logger.error({ err: error, projectPath }, 'Code map generation failed with exception');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
generationTime
};
}
}
async detectExistingCodeMap(projectPath) {
try {
const absoluteProjectPath = path.resolve(projectPath);
if (this.config.enableCaching && this.codeMapCache.has(absoluteProjectPath)) {
const cached = this.codeMapCache.get(absoluteProjectPath);
try {
await fs.access(cached.filePath);
return cached;
}
catch {
this.codeMapCache.delete(absoluteProjectPath);
}
}
const codeMapFiles = await this.findCodeMapFiles(absoluteProjectPath);
if (codeMapFiles.length === 0) {
return null;
}
const mostRecent = codeMapFiles.sort((a, b) => b.generatedAt.getTime() - a.generatedAt.getTime())[0];
if (this.config.enableCaching) {
this.codeMapCache.set(absoluteProjectPath, mostRecent);
}
return mostRecent;
}
catch (error) {
logger.warn({ err: error, projectPath }, 'Failed to detect existing code map');
return null;
}
}
async isCodeMapStale(projectPath, maxAge) {
try {
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
return true;
}
const ageThreshold = maxAge || this.config.maxAge;
const age = Date.now() - codeMapInfo.generatedAt.getTime();
return age > ageThreshold;
}
catch (error) {
logger.warn({ err: error, projectPath }, 'Failed to check code map staleness');
return true;
}
}
async refreshCodeMap(projectPath, force = false) {
try {
const isStale = force || await this.isCodeMapStale(projectPath);
if (!isStale) {
logger.debug({ projectPath }, 'Code map is fresh, skipping refresh');
const existing = await this.detectExistingCodeMap(projectPath);
return {
success: true,
filePath: existing?.filePath,
generationTime: 0
};
}
logger.info({ projectPath, force }, 'Refreshing code map');
return await this.generateCodeMap(projectPath);
}
catch (error) {
logger.error({ err: error, projectPath }, 'Failed to refresh code map');
return {
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
async extractArchitecturalInfo(projectPath) {
try {
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
throw new Error('No code map found for project');
}
const codeMapContent = await fs.readFile(codeMapInfo.filePath, 'utf-8');
return this.parseArchitecturalInfo(codeMapContent, projectPath);
}
catch (error) {
logger.error({ err: error, projectPath }, 'Failed to extract architectural info');
throw error;
}
}
async extractDependencyInfo(projectPath) {
try {
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
throw new Error('No code map found for project');
}
const codeMapContent = await fs.readFile(codeMapInfo.filePath, 'utf-8');
return this.parseDependencyInfo(codeMapContent);
}
catch (error) {
logger.error({ err: error, projectPath }, 'Failed to extract dependency info');
throw error;
}
}
async extractRelevantFiles(projectPath, taskDescription) {
try {
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
logger.warn({ projectPath }, 'No code map found, cannot extract relevant files');
return [];
}
const codeMapContent = await fs.readFile(codeMapInfo.filePath, 'utf-8');
return this.findRelevantFiles(codeMapContent, taskDescription);
}
catch (error) {
logger.error({ err: error, projectPath, taskDescription }, 'Failed to extract relevant files');
return [];
}
}
async integrateCodeMapContext(projectContext, projectPath) {
try {
if (this.config.autoRefresh && await this.isCodeMapStale(projectPath)) {
await this.refreshCodeMap(projectPath);
}
const [architecturalInfo, dependencyInfo] = await Promise.all([
this.extractArchitecturalInfo(projectPath).catch(() => null),
this.extractDependencyInfo(projectPath).catch(() => [])
]);
const enhancedContext = {
...projectContext,
architecturalPatterns: architecturalInfo?.patterns || [],
entryPoints: architecturalInfo?.entryPoints || [],
frameworks: [
...new Set([
...projectContext.frameworks,
...(architecturalInfo?.frameworks || [])
])
],
languages: [
...new Set([
...projectContext.languages,
...(architecturalInfo?.languages || [])
])
],
codeMapContext: {
hasCodeMap: true,
lastGenerated: (await this.detectExistingCodeMap(projectPath))?.generatedAt,
directoryStructure: architecturalInfo?.directoryStructure || [],
dependencyCount: dependencyInfo.length,
externalDependencies: dependencyInfo.filter(d => d.isExternal).length,
configFiles: architecturalInfo?.configFiles || []
}
};
logger.debug({
projectPath,
enhancedFrameworks: enhancedContext.frameworks.length,
enhancedLanguages: enhancedContext.languages.length,
dependencyCount: dependencyInfo.length
}, 'Integrated code map context');
return enhancedContext;
}
catch (error) {
logger.warn({ err: error, projectPath }, 'Failed to integrate code map context, using original');
return projectContext;
}
}
clearCache() {
this.codeMapCache.clear();
logger.debug('Code map cache cleared');
}
async validateProjectPath(projectPath) {
try {
const stats = await fs.stat(projectPath);
if (!stats || typeof stats.isDirectory !== 'function') {
throw new Error(`Invalid file stats for path: ${projectPath}`);
}
if (!stats.isDirectory()) {
throw new Error(`Path is not a directory: ${projectPath}`);
}
}
catch (error) {
throw new Error(`Invalid project path: ${projectPath} - ${error instanceof Error ? error.message : String(error)}`);
}
}
extractFilePathFromResult(resultContent) {
try {
const patterns = [
/Generated code map: (.+\.md)/,
/Generated Markdown output: (.+\.md)/,
/\*\*Output saved to:\*\* (.+\.md)/,
/Output file: (.+\.md)/,
/Saved to: (.+\.md)/,
/File saved: (.+\.md)/,
/(?:^|\n)(.+\.md)(?:\n|$)/
];
for (const pattern of patterns) {
const match = resultContent.match(pattern);
if (match && match[1]) {
return match[1].trim();
}
}
const lines = resultContent.split('\n');
for (const line of lines) {
if (line.includes('.md') && (line.includes('/') || line.includes('\\'))) {
const trimmed = line.trim();
if (trimmed.endsWith('.md')) {
return trimmed;
}
}
}
return null;
}
catch (error) {
logger.warn({ err: error, resultContent }, 'Failed to extract file path from result');
return null;
}
}
async updateCodeMapCache(projectPath, filePath) {
try {
const stats = await fs.stat(filePath);
const absoluteProjectPath = path.resolve(projectPath);
const codeMapInfo = {
filePath,
generatedAt: stats.mtime,
projectPath: absoluteProjectPath,
fileSize: stats.size,
isStale: false
};
this.codeMapCache.set(absoluteProjectPath, codeMapInfo);
logger.debug({ projectPath: absoluteProjectPath, filePath }, 'Updated code map cache');
}
catch (error) {
logger.warn({ err: error, projectPath, filePath }, 'Failed to update code map cache');
}
}
async findCodeMapFiles(projectPath) {
try {
const outputBaseDir = process.env.VIBE_CODER_OUTPUT_DIR || path.join(process.cwd(), 'VibeCoderOutput');
const codeMapOutputDir = path.join(outputBaseDir, 'code-map-generator');
try {
await fs.access(codeMapOutputDir);
}
catch {
return [];
}
const files = await fs.readdir(codeMapOutputDir, { withFileTypes: true });
if (!Array.isArray(files)) {
logger.warn({ projectPath, filesType: typeof files }, 'readdir returned non-array');
return [];
}
const codeMapFiles = [];
for (const file of files) {
if (!file || typeof file.isFile !== 'function' || typeof file.name !== 'string') {
logger.warn({ projectPath, fileType: typeof file }, 'Invalid file object from readdir');
continue;
}
if (file.isFile() && file.name.endsWith('.md')) {
const filePath = path.join(codeMapOutputDir, file.name);
try {
const stats = await fs.stat(filePath);
if (!stats || typeof stats.mtime === 'undefined' || typeof stats.size === 'undefined') {
logger.warn({ filePath, statsType: typeof stats }, 'Invalid stats object from fs.stat');
continue;
}
const isForProject = await this.isCodeMapForProject(filePath, projectPath);
if (isForProject) {
codeMapFiles.push({
filePath,
generatedAt: stats.mtime,
projectPath,
fileSize: stats.size,
isStale: Date.now() - stats.mtime.getTime() > this.config.maxAge
});
}
}
catch (error) {
logger.warn({ err: error, filePath }, 'Failed to stat code map file');
}
}
}
return codeMapFiles;
}
catch (error) {
logger.warn({ err: error, projectPath }, 'Failed to find code map files');
return [];
}
}
async isCodeMapForProject(filePath, projectPath) {
try {
const content = await fs.readFile(filePath, 'utf-8');
if (typeof content !== 'string') {
logger.warn({ filePath, projectPath, contentType: typeof content }, 'Invalid content type from readFile');
return false;
}
const lines = content.split('\n').slice(0, 20);
const absoluteProjectPath = path.resolve(projectPath);
for (const line of lines) {
if (line.includes(absoluteProjectPath) || line.includes(path.basename(absoluteProjectPath))) {
return true;
}
}
return false;
}
catch (error) {
logger.warn({ err: error, filePath, projectPath }, 'Failed to check if code map is for project');
return false;
}
}
parseArchitecturalInfo(content, _projectPath) {
const info = {
directoryStructure: [],
patterns: [],
entryPoints: [],
configFiles: [],
frameworks: [],
languages: []
};
try {
if (typeof content !== 'string') {
logger.warn({ projectPath: _projectPath, contentType: typeof content }, 'Invalid content type for parseArchitecturalInfo');
return info;
}
const lines = content.split('\n');
let currentSection = '';
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('## ') || trimmed.startsWith('# ')) {
currentSection = trimmed.toLowerCase();
continue;
}
if (currentSection.includes('directory') || currentSection.includes('structure')) {
const dirMatch = trimmed.match(/^[-*]\s*(.+?)(?:\s*\((\d+)\s*files?\))?/);
if (dirMatch) {
info.directoryStructure.push({
path: dirMatch[1],
purpose: this.inferDirectoryPurpose(dirMatch[1]),
fileCount: parseInt(dirMatch[2] || '0', 10)
});
}
}
if (trimmed.includes('framework') || trimmed.includes('library')) {
const frameworks = this.extractFrameworks(trimmed);
info.frameworks.push(...frameworks);
}
if (trimmed.includes('language') || trimmed.includes('extension')) {
const languages = this.extractLanguages(trimmed);
info.languages.push(...languages);
}
if (trimmed.includes('main') || trimmed.includes('index') || trimmed.includes('entry')) {
if (trimmed.includes('.js') || trimmed.includes('.ts') || trimmed.includes('.py')) {
const entryPoint = this.extractFilePath(trimmed);
if (entryPoint) {
info.entryPoints.push(entryPoint);
}
}
}
if (this.isConfigFile(trimmed)) {
const configFile = this.extractFilePath(trimmed);
if (configFile) {
info.configFiles.push(configFile);
}
}
if (trimmed.includes('pattern') || trimmed.includes('architecture')) {
const patterns = this.extractPatterns(trimmed);
info.patterns.push(...patterns);
}
}
info.frameworks = [...new Set(info.frameworks)];
info.languages = [...new Set(info.languages)];
info.entryPoints = [...new Set(info.entryPoints)];
info.configFiles = [...new Set(info.configFiles)];
info.patterns = [...new Set(info.patterns)];
return info;
}
catch (error) {
logger.warn({ err: error }, 'Failed to parse architectural info, returning empty');
return info;
}
}
parseDependencyInfo(content) {
const dependencies = [];
try {
if (typeof content !== 'string') {
logger.warn({ contentType: typeof content }, 'Invalid content type for parseDependencyInfo');
return dependencies;
}
const lines = content.split('\n');
let inDependencySection = false;
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.toLowerCase().includes('import') ||
trimmed.toLowerCase().includes('depend') ||
trimmed.toLowerCase().includes('require')) {
inDependencySection = true;
continue;
}
if (trimmed.startsWith('## ') || trimmed.startsWith('# ')) {
inDependencySection = trimmed.toLowerCase().includes('import') ||
trimmed.toLowerCase().includes('depend');
continue;
}
if (inDependencySection && trimmed) {
const dependency = this.parseDependencyLine(trimmed);
if (dependency) {
dependencies.push(dependency);
}
}
}
return dependencies;
}
catch (error) {
logger.warn({ err: error }, 'Failed to parse dependency info, returning empty');
return [];
}
}
findRelevantFiles(content, taskDescription) {
const relevantFiles = [];
try {
const keywords = this.extractKeywords(taskDescription);
const lines = content.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (this.containsFilePath(trimmed)) {
const filePath = this.extractFilePath(trimmed);
if (filePath && this.isRelevantToKeywords(trimmed, keywords)) {
relevantFiles.push(filePath);
}
}
}
return [...new Set(relevantFiles)];
}
catch (error) {
logger.warn({ err: error, taskDescription }, 'Failed to find relevant files, returning empty');
return [];
}
}
inferDirectoryPurpose(dirPath) {
const name = path.basename(dirPath).toLowerCase();
const purposes = {
'src': 'Source code',
'lib': 'Library code',
'test': 'Test files',
'tests': 'Test files',
'spec': 'Test specifications',
'docs': 'Documentation',
'config': 'Configuration',
'build': 'Build artifacts',
'dist': 'Distribution files',
'public': 'Public assets',
'assets': 'Static assets',
'components': 'UI components',
'services': 'Service layer',
'utils': 'Utility functions',
'types': 'Type definitions',
'models': 'Data models',
'controllers': 'Controllers',
'routes': 'Route definitions',
'middleware': 'Middleware',
'api': 'API endpoints'
};
return purposes[name] || 'General purpose';
}
extractFrameworks(text) {
const frameworks = [];
const frameworkPatterns = [
/react/i, /vue/i, /angular/i, /svelte/i,
/express/i, /fastify/i, /koa/i, /nest/i,
/next/i, /nuxt/i, /gatsby/i,
/django/i, /flask/i, /fastapi/i,
/spring/i, /laravel/i, /rails/i
];
for (const pattern of frameworkPatterns) {
const match = text.match(pattern);
if (match) {
frameworks.push(match[0].toLowerCase());
}
}
return frameworks;
}
extractLanguages(text) {
const languages = [];
const languagePatterns = [
/\.js\b/g, /\.ts\b/g, /\.py\b/g, /\.java\b/g,
/\.cpp?\b/g, /\.cs\b/g, /\.php\b/g, /\.rb\b/g,
/\.go\b/g, /\.rs\b/g, /\.swift\b/g, /\.kt\b/g
];
const languageMap = {
'.js': 'JavaScript',
'.ts': 'TypeScript',
'.py': 'Python',
'.java': 'Java',
'.cpp': 'C++',
'.c': 'C',
'.cs': 'C#',
'.php': 'PHP',
'.rb': 'Ruby',
'.go': 'Go',
'.rs': 'Rust',
'.swift': 'Swift',
'.kt': 'Kotlin'
};
for (const pattern of languagePatterns) {
const matches = text.match(pattern);
if (matches) {
for (const match of matches) {
const language = languageMap[match];
if (language) {
languages.push(language);
}
}
}
}
return languages;
}
extractFilePath(text) {
const pathPatterns = [
/([a-zA-Z0-9_\-./\\]+\.[a-zA-Z0-9]+)/,
/"([^"]+\.[a-zA-Z0-9]+)"/,
/'([^']+\.[a-zA-Z0-9]+)'/,
/`([^`]+\.[a-zA-Z0-9]+)`/
];
for (const pattern of pathPatterns) {
const match = text.match(pattern);
if (match && match[1]) {
return match[1];
}
}
return null;
}
isConfigFile(text) {
const configPatterns = [
/package\.json/i, /tsconfig\.json/i, /webpack\.config/i,
/babel\.config/i, /eslint/i, /prettier/i,
/\.env/i, /config\./i, /settings\./i
];
return configPatterns.some(pattern => pattern.test(text));
}
extractPatterns(text) {
const patterns = [];
const patternKeywords = [
'mvc', 'mvp', 'mvvm', 'microservices', 'monolith',
'layered', 'hexagonal', 'clean architecture',
'repository', 'factory', 'singleton', 'observer'
];
for (const keyword of patternKeywords) {
if (text.toLowerCase().includes(keyword)) {
patterns.push(keyword);
}
}
return patterns;
}
parseDependencyLine(line) {
try {
const importMatch = line.match(/import\s+.*?from\s+['"]([^'"]+)['"]/);
const requireMatch = line.match(/require\(['"]([^'"]+)['"]\)/);
const includeMatch = line.match(/#include\s+[<"]([^>"]+)[>"]/);
let target = null;
let type = 'import';
if (importMatch) {
target = importMatch[1];
type = 'import';
}
else if (requireMatch) {
target = requireMatch[1];
type = 'require';
}
else if (includeMatch) {
target = includeMatch[1];
type = 'include';
}
if (!target) {
return null;
}
const isExternal = !target.startsWith('.') && !target.startsWith('/');
const packageName = isExternal ? target.split('/')[0] : undefined;
return {
source: 'unknown',
target,
type,
isExternal,
packageName
};
}
catch {
return null;
}
}
extractKeywords(taskDescription) {
const words = taskDescription.toLowerCase()
.split(/\s+/)
.filter(word => word.length > 2)
.filter(word => !['the', 'and', 'for', 'with', 'this', 'that'].includes(word));
return words;
}
containsFilePath(text) {
return /[a-zA-Z0-9_\-./\\]+\.[a-zA-Z0-9]+/.test(text);
}
isRelevantToKeywords(text, keywords) {
const lowerText = text.toLowerCase();
return keywords.some(keyword => lowerText.includes(keyword));
}
extractErrorMessage(content) {
if (typeof content === 'string') {
return content;
}
if (Array.isArray(content)) {
const textParts = content
.filter(item => item && typeof item === 'object' && item.type === 'text')
.map(item => item.text)
.filter(text => typeof text === 'string');
return textParts.join('\n') || 'Unknown error occurred';
}
if (content && typeof content === 'object' && 'text' in content && typeof content.text === 'string') {
return content.text;
}
return 'Unknown error occurred';
}
extractContentString(content) {
if (typeof content === 'string') {
return content;
}
if (Array.isArray(content)) {
const textParts = content
.filter(item => item && typeof item === 'object' && item.type === 'text')
.map(item => item.text)
.filter(text => typeof text === 'string');
return textParts.join('\n');
}
if (content && typeof content === 'object' && 'text' in content && typeof content.text === 'string') {
return content.text;
}
return '';
}
async configureCodeMapGeneration(projectPath, config) {
try {
logger.debug(`Configuring code map generation for project: ${projectPath}`);
const configPath = path.join(projectPath, '.vibe-codemap-config.json');
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
logger.info(`Code map configuration saved for project: ${projectPath}`);
}
catch (error) {
logger.error(`Failed to configure code map generation: ${error}`);
throw new Error(`Failed to configure code map generation: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async getCodeMapMetadata(projectPath) {
try {
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
throw new Error('No code map found for project');
}
const stats = await fs.stat(codeMapInfo.filePath);
const content = await fs.readFile(codeMapInfo.filePath, 'utf-8');
const performanceMetrics = this.performanceMetrics.get(projectPath) || {
generationTime: 0,
parseTime: 0,
fileCount: 0,
lineCount: content.split('\n').length
};
const configPath = path.join(projectPath, '.vibe-codemap-config.json');
let generationConfig = {};
try {
const configContent = await fs.readFile(configPath, 'utf-8');
generationConfig = JSON.parse(configContent);
}
catch {
}
return {
filePath: codeMapInfo.filePath,
projectPath,
generatedAt: codeMapInfo.generatedAt,
fileSize: stats.size,
version: '1.0.0',
isOptimized: false,
generationConfig,
performanceMetrics
};
}
catch (error) {
logger.error(`Failed to get code map metadata: ${error}`);
throw new Error(`Failed to get code map metadata: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async validateCodeMapIntegrity(projectPath) {
try {
const startTime = Date.now();
const errors = [];
const warnings = [];
let integrityScore = 1.0;
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
return {
isValid: false,
errors: ['No code map found for project'],
warnings: [],
integrityScore: 0,
validatedAt: new Date()
};
}
try {
const content = await fs.readFile(codeMapInfo.filePath, 'utf-8');
if (content.length === 0) {
errors.push('Code map file is empty');
integrityScore -= 0.5;
}
const requiredSections = ['# Code Map', '## Project Structure', '## Dependencies'];
for (const section of requiredSections) {
if (!content.includes(section)) {
warnings.push(`Missing section: ${section}`);
integrityScore -= 0.1;
}
}
const filePathMatches = content.match(/`[^`]+\.(ts|js|py|java|cpp|c|h|hpp|go|rs|rb|php|cs|swift|kt|scala|clj|ex|elm|hs|ml|fs|vb|pas|d|nim|zig|odin|v|cr|dart|lua|r|jl|m|mm|pl|pm|sh|bash|zsh|fish|ps1|bat|cmd|dockerfile|yaml|yml|json|xml|html|css|scss|sass|less|styl|vue|svelte|jsx|tsx|md|rst|txt|cfg|ini|toml|lock|gitignore|gitattributes|editorconfig|prettierrc|eslintrc|tsconfig|package|requirements|cargo|go\.mod|pom\.xml|build\.gradle|makefile|cmake|dockerfile|docker-compose)`/gi);
if (filePathMatches && filePathMatches.length > 0) {
const samplePaths = filePathMatches.slice(0, 10);
for (const pathMatch of samplePaths) {
const filePath = pathMatch.replace(/`/g, '');
const fullPath = path.resolve(projectPath, filePath);
try {
await fs.access(fullPath);
}
catch {
warnings.push(`Referenced file not found: ${filePath}`);
integrityScore -= 0.05;
}
}
}
}
catch (error) {
errors.push(`Failed to read code map file: ${error instanceof Error ? error.message : 'Unknown error'}`);
integrityScore -= 0.3;
}
if (await this.isCodeMapStale(projectPath)) {
warnings.push('Code map is stale and may need regeneration');
integrityScore -= 0.1;
}
const isValid = errors.length === 0 && integrityScore > 0.5;
logger.debug(`Code map validation completed in ${Date.now() - startTime}ms`);
return {
isValid,
errors,
warnings,
integrityScore: Math.max(0, integrityScore),
validatedAt: new Date()
};
}
catch (error) {
logger.error(`Failed to validate code map integrity: ${error}`);
return {
isValid: false,
errors: [`Validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`],
warnings: [],
integrityScore: 0,
validatedAt: new Date()
};
}
}
async requestCodeMapData(projectPath, dataType) {
try {
logger.debug(`Requesting code map data type: ${dataType} for project: ${projectPath}`);
switch (dataType) {
case 'architectural_info':
return await this.extractArchitecturalInfo(projectPath);
case 'dependency_info':
return await this.extractDependencyInfo(projectPath);
case 'relevant_files':
throw new Error('relevant_files requires task description parameter');
case 'metadata':
return await this.getCodeMapMetadata(projectPath);
case 'full_content': {
const codeMapInfo = await this.detectExistingCodeMap(projectPath);
if (!codeMapInfo) {
throw new Error('No code map found for project');
}
return await fs.readFile(codeMapInfo.filePath, 'utf-8');
}
case 'performance_metrics':
return this.performanceMetrics.get(projectPath) || null;
default:
throw new Error(`Unknown data type: ${dataType}`);
}
}
catch (error) {
logger.error(`Failed to request code map data: ${error}`);
throw new Error(`Failed to request code map data: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
subscribeToCodeMapUpdates(projectPath, callback) {
try {
if (!this.updateSubscriptions.has(projectPath)) {
this.updateSubscriptions.set(projectPath, []);
}
const callbacks = this.updateSubscriptions.get(projectPath);
callbacks.push(callback);
logger.debug(`Subscribed to code map updates for project: ${projectPath}`);
}
catch (error) {
logger.error(`Failed to subscribe to code map updates: ${error}`);
}
}
notifySubscribers(event) {
try {
const callbacks = this.updateSubscriptions.get(event.projectPath);
if (callbacks) {
for (const callback of callbacks) {
try {
callback(event);
}
catch (error) {
logger.error(`Error in update callback: ${error}`);
}
}
}
}
catch (error) {
logger.error(`Failed to notify subscribers: ${error}`);
}
}
async refreshCodeMapWithMonitoring(projectPath, force = false) {
const startTime = Date.now();
try {
this.notifySubscribers({
type: 'generated',
projectPath,
timestamp: new Date(),
data: { status: 'starting' }
});
await this.refreshCodeMap(projectPath, force);
const generationTime = Date.now() - startTime;
if (this.config.enablePerformanceMonitoring) {
const existingMetrics = this.performanceMetrics.get(projectPath) || {
generationTime: 0,
parseTime: 0,
fileCount: 0,
lineCount: 0
};
this.performanceMetrics.set(projectPath, {
...existingMetrics,
generationTime
});
}
this.notifySubscribers({
type: 'refreshed',
projectPath,
timestamp: new Date(),
data: {
status: 'completed',
generationTime
}
});
logger.info(`Code map refresh completed in ${generationTime}ms for project: ${projectPath}`);
}
catch (error) {
this.notifySubscribers({
type: 'error',
projectPath,
timestamp: new Date(),
error: error instanceof Error ? error.message : 'Unknown error'
});
throw error;
}
}
}