UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

318 lines (253 loc) 8.71 kB
import { StorageManager } from '../../storage/storage-manager.js'; import { ConfigManager } from '../../config/config-manager.js'; import { AnalysisResult, CodeReview, MetricsHistory, HistoryEntry, CodeMetrics, } from './types.js'; export class CodeAnalysisStore { private storageManager: StorageManager; private moduleName = 'code-analysis'; constructor(configManager: ConfigManager) { this.storageManager = new StorageManager(); } async initialize(): Promise<void> { await this.storageManager.ensureStorageDirectories(); } // Analysis History Management async saveAnalysisResult(result: AnalysisResult): Promise<void> { let history: AnalysisResult[] = []; try { history = await this.storageManager.loadData(this.moduleName, 'analysis-history.json') || []; } catch { // File doesn't exist yet } history.push(result); // Keep only last 50 analyses if (history.length > 50) { history = history.slice(-50); } await this.storageManager.saveData(this.moduleName, 'analysis-history.json', history); } async getAnalysisHistory(limit?: number): Promise<AnalysisResult[]> { try { const history = await this.storageManager.loadData(this.moduleName, 'analysis-history.json') as AnalysisResult[]; if (limit && limit > 0) { return (history || []).slice(-limit); } return history || []; } catch { return []; } } async getLatestAnalysis(): Promise<AnalysisResult | null> { const history = await this.getAnalysisHistory(1); return history.length > 0 ? history[0] : null; } // Code Review Management async saveCodeReview(review: CodeReview): Promise<void> { let reviews: CodeReview[] = []; try { reviews = await this.storageManager.loadData(this.moduleName, 'code-reviews.json') || []; } catch { // File doesn't exist yet } reviews.push(review); // Keep only last 100 reviews if (reviews.length > 100) { reviews = reviews.slice(-100); } await this.storageManager.saveData(this.moduleName, 'code-reviews.json', reviews); } async getCodeReviews(fileFilter?: string): Promise<CodeReview[]> { try { const reviews = await this.storageManager.loadData(this.moduleName, 'code-reviews.json') as CodeReview[]; if (fileFilter) { return (reviews || []).filter(review => review.file.includes(fileFilter)); } return reviews || []; } catch { return []; } } // Metrics History Management async saveMetricsHistory(projectId: string, entry: HistoryEntry): Promise<void> { let history: MetricsHistory; try { history = await this.storageManager.loadData(this.moduleName, `metrics-${projectId}.json`) || { projectId, entries: [], baseline: undefined, }; } catch { history = { projectId, entries: [], baseline: undefined, trends: [], }; } history.entries.push(entry); // Keep only last 30 days of entries const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); history.entries = history.entries.filter(e => new Date(e.timestamp) > thirtyDaysAgo ); await this.storageManager.saveData(this.moduleName, `metrics-${projectId}.json`, history); } async getMetricsHistory(projectId: string): Promise<MetricsHistory | null> { try { return await this.storageManager.loadData(this.moduleName, `metrics-${projectId}.json`); } catch { return null; } } async setBaseline(projectId: string, commit: string, metrics: CodeMetrics): Promise<void> { let history = await this.getMetricsHistory(projectId); if (!history) { history = { projectId, entries: [], baseline: undefined, trends: [], }; } history.baseline = { timestamp: new Date().toISOString(), branch: 'main', commit, metrics, }; await this.storageManager.saveData(this.moduleName, `metrics-${projectId}.json`, history); } // Trend Analysis async getMetricsTrend( projectId: string, metricPath: string, days: number = 7 ): Promise<Array<{ timestamp: string; value: number }>> { const history = await this.getMetricsHistory(projectId); if (!history) { return []; } const startDate = new Date(); startDate.setDate(startDate.getDate() - days); const trend = history.entries .filter(entry => new Date(entry.timestamp) > startDate) .map(entry => { const value = this.getNestedValue(entry.metrics, metricPath); return { timestamp: entry.timestamp, value: typeof value === 'number' ? value : 0, }; }) .filter(item => item.value !== 0); return trend; } async getQualityScore(projectId: string): Promise<number> { const history = await this.getMetricsHistory(projectId); if (!history || history.entries.length === 0) { return 0; } const latestEntry = history.entries[history.entries.length - 1]; const metrics = latestEntry.metrics; // Calculate weighted quality score const complexityScore = Math.max(0, 100 - metrics.complexity.average * 5); const maintainabilityScore = metrics.maintainability.average; const duplicationScore = Math.max(0, 100 - metrics.duplication.percentage); const issuesScore = Math.max(0, 100 - metrics.quality.issues.total * 2); const qualityScore = ( complexityScore * 0.25 + maintainabilityScore * 0.35 + duplicationScore * 0.2 + issuesScore * 0.2 ); return Math.round(qualityScore); } private getNestedValue(obj: any, path: string): any { const keys = path.split('.'); let current = obj; for (const key of keys) { if (current && typeof current === 'object' && key in current) { current = current[key]; } else { return undefined; } } return current; } // Custom Rules Management async saveCustomRules(projectId: string, rules: any[]): Promise<void> { await this.storageManager.saveData(this.moduleName, `rules-${projectId}.json`, rules); } async getCustomRules(projectId: string): Promise<any[]> { try { return await this.storageManager.loadData(this.moduleName, `rules-${projectId}.json`) || []; } catch { return []; } } // Export/Import async exportAnalysisData(projectId: string): Promise<any> { const [ history, reviews, metrics, rules, ] = await Promise.all([ this.getAnalysisHistory(), this.getCodeReviews(), this.getMetricsHistory(projectId), this.getCustomRules(projectId), ]); return { analysisHistory: history, codeReviews: reviews, metricsHistory: metrics, customRules: rules, exportedAt: new Date().toISOString(), }; } async importAnalysisData(projectId: string, data: any): Promise<void> { if (data.analysisHistory) { await this.storageManager.saveData(this.moduleName, 'analysis-history.json', data.analysisHistory); } if (data.codeReviews) { await this.storageManager.saveData(this.moduleName, 'code-reviews.json', data.codeReviews); } if (data.metricsHistory) { await this.storageManager.saveData(this.moduleName, `metrics-${projectId}.json`, data.metricsHistory); } if (data.customRules) { await this.storageManager.saveData(this.moduleName, `rules-${projectId}.json`, data.customRules); } } // Cleanup async cleanupOldData(daysToKeep: number = 30): Promise<number> { let cleanedItems = 0; // Clean analysis history const history = await this.getAnalysisHistory(); const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysToKeep); const filteredHistory = history.filter(item => new Date(item.timestamp) > cutoffDate ); if (filteredHistory.length < history.length) { await this.storageManager.saveData(this.moduleName, 'analysis-history.json', filteredHistory); cleanedItems += history.length - filteredHistory.length; } // Clean code reviews const reviews = await this.getCodeReviews(); const filteredReviews = reviews.filter(review => new Date(review.timestamp) > cutoffDate ); if (filteredReviews.length < reviews.length) { await this.storageManager.saveData(this.moduleName, 'code-reviews.json', filteredReviews); cleanedItems += reviews.length - filteredReviews.length; } return cleanedItems; } }