UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

1,225 lines (1,010 loc) 35.1 kB
const fs = require('fs').promises; const path = require('path'); const chalk = require('chalk'); const EventEmitter = require('events'); /** * Pattern learning system for successful modifications * Learns from successful modifications to suggest improvements and automate common patterns */ class PatternLearner extends EventEmitter { constructor(options = {}) { super(); this.rootPath = options.rootPath || process.cwd(); this.patternsDir = path.join(this.rootPath, '.aios', 'patterns'); this.historyFile = path.join(this.patternsDir, 'modification_history.json'); this.patternsFile = path.join(this.patternsDir, 'learned_patterns.json'); this.patterns = new Map(); this.modificationHistory = []; this.learningThreshold = options.learningThreshold || 3; // Minimum occurrences to learn pattern this.similarityThreshold = options.similarityThreshold || 0.8; // 80% similarity } /** * Initialize pattern learner */ async initialize() { await fs.mkdir(this.patternsDir, { recursive: true }); await this.loadHistory(); await this.loadPatterns(); console.log(chalk.green('✅ Pattern learner initialized')); } /** * Record successful modification for learning */ async recordSuccessfulModification(modification) { const record = { id: `mod-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`, timestamp: new Date().toISOString(), componentType: modification.componentType, modificationType: modification.modificationType, patterns: await this.extractPatterns(modification), outcomes: { success: true, metrics: modification.metrics || {}, improvements: modification.improvements || [] }, metadata: { author: modification.author || process.env.USER || 'unknown', duration: modification.duration, complexity: modification.complexity } }; // Add to history this.modificationHistory.push(record); // Learn from this modification await this.learnFromModification(record); // Save updated history await this.saveHistory(); // Emit event for real-time learning this.emit('modification_recorded', record); console.log(chalk.green(`✅ Recorded successful modification: ${record.id}`)); return { recordId: record.id, patternsExtracted: record.patterns.length, learningTriggered: await this.checkLearningThreshold(record.patterns) }; } /** * Extract patterns from modification */ async extractPatterns(modification) { const patterns = []; // Code change patterns if (modification.codeChanges) { patterns.push(...this.extractCodePatterns(modification.codeChanges)); } // Structural patterns if (modification.structuralChanges) { patterns.push(...this.extractStructuralPatterns(modification.structuralChanges)); } // Refactoring patterns if (modification.refactoringType) { patterns.push(...this.extractRefactoringPatterns(modification)); } // Dependency patterns if (modification.dependencyChanges) { patterns.push(...this.extractDependencyPatterns(modification.dependencyChanges)); } // Performance patterns if (modification.performanceImprovements) { patterns.push(...this.extractPerformancePatterns(modification.performanceImprovements)); } return patterns; } /** * Extract code change patterns */ extractCodePatterns(codeChanges) { const patterns = []; for (const change of codeChanges) { // Function transformation patterns if (change.type === 'function_transformation') { patterns.push({ type: 'code_transformation', subtype: 'function', from: this.normalizeCode(change.before), to: this.normalizeCode(change.after), context: change.context, benefits: change.benefits || [] }); } // Error handling patterns if (change.type === 'error_handling') { patterns.push({ type: 'error_handling', subtype: change.handlingType, pattern: change.pattern, improvement: change.improvement }); } // Async/await patterns if (change.type === 'async_transformation') { patterns.push({ type: 'async_pattern', from: change.callbackPattern, to: change.asyncPattern, complexity_reduction: change.complexityReduction }); } // API usage patterns if (change.type === 'api_improvement') { patterns.push({ type: 'api_usage', oldPattern: change.oldUsage, newPattern: change.newUsage, benefits: change.benefits }); } } return patterns; } /** * Extract structural patterns */ extractStructuralPatterns(structuralChanges) { const patterns = []; for (const change of structuralChanges) { patterns.push({ type: 'structural', changeType: change.type, pattern: { before: change.beforeStructure, after: change.afterStructure }, benefits: { modularity: change.modularityImprovement || 0, maintainability: change.maintainabilityImprovement || 0, testability: change.testabilityImprovement || 0 } }); } return patterns; } /** * Extract refactoring patterns */ extractRefactoringPatterns(modification) { const patterns = []; patterns.push({ type: 'refactoring', refactoringType: modification.refactoringType, triggers: modification.triggers || [], steps: modification.steps || [], validation: modification.validation || {}, benefits: modification.measuredBenefits || {} }); return patterns; } /** * Extract dependency patterns */ extractDependencyPatterns(dependencyChanges) { const patterns = []; for (const change of dependencyChanges) { if (change.type === 'consolidation') { patterns.push({ type: 'dependency_consolidation', from: change.originalDependencies, to: change.consolidatedDependency, reduction: change.dependencyReduction }); } if (change.type === 'upgrade') { patterns.push({ type: 'dependency_upgrade', dependency: change.dependency, fromVersion: change.fromVersion, toVersion: change.toVersion, migrationSteps: change.migrationSteps }); } } return patterns; } /** * Extract performance patterns */ extractPerformancePatterns(performanceImprovements) { const patterns = []; for (const improvement of performanceImprovements) { patterns.push({ type: 'performance', optimizationType: improvement.type, technique: improvement.technique, metrics: { before: improvement.metricsBefore, after: improvement.metricsAfter, improvement: improvement.percentageImprovement }, applicableContexts: improvement.contexts || [] }); } return patterns; } /** * Learn from modification */ async learnFromModification(record) { for (const pattern of record.patterns) { const patternKey = this.generatePatternKey(pattern); // Check if similar pattern exists const similarPattern = await this.findSimilarPattern(pattern); if (similarPattern) { // Update existing pattern await this.updatePattern(similarPattern, pattern, record); } else { // Create new pattern entry await this.createPattern(patternKey, pattern, record); } } // Analyze cross-pattern relationships await this.analyzePatternRelationships(record.patterns); // Update pattern rankings await this.updatePatternRankings(); } /** * Find similar pattern */ async findSimilarPattern(pattern) { for (const [key, existingPattern] of this.patterns) { const similarity = await this.calculatePatternSimilarity(pattern, existingPattern); if (similarity >= this.similarityThreshold) { return { key, pattern: existingPattern, similarity }; } } return null; } /** * Calculate pattern similarity */ async calculatePatternSimilarity(pattern1, pattern2) { // Type must match if (pattern1.type !== pattern2.type) return 0; let similarity = 0; let factors = 0; // Type-specific similarity calculation switch (pattern1.type) { case 'code_transformation': similarity += this.calculateCodeSimilarity(pattern1, pattern2) * 0.7; similarity += this.calculateContextSimilarity(pattern1.context, pattern2.context) * 0.3; factors = 1; break; case 'structural': similarity += this.calculateStructuralSimilarity(pattern1.pattern, pattern2.pattern) * 0.6; similarity += this.calculateBenefitSimilarity(pattern1.benefits, pattern2.benefits) * 0.4; factors = 1; break; case 'refactoring': similarity += this.calculateRefactoringSimilarity(pattern1, pattern2); factors = 1; break; case 'performance': similarity += this.calculatePerformanceSimilarity(pattern1, pattern2); factors = 1; break; default: // Generic similarity based on pattern structure similarity = this.calculateGenericSimilarity(pattern1, pattern2); factors = 1; } return factors > 0 ? similarity / factors : 0; } /** * Calculate code similarity */ calculateCodeSimilarity(pattern1, pattern2) { const from1 = this.tokenizeCode(pattern1.from); const from2 = this.tokenizeCode(pattern2.from); const to1 = this.tokenizeCode(pattern1.to); const to2 = this.tokenizeCode(pattern2.to); const fromSimilarity = this.calculateTokenSimilarity(from1, from2); const toSimilarity = this.calculateTokenSimilarity(to1, to2); return (fromSimilarity + toSimilarity) / 2; } /** * Tokenize code for comparison */ tokenizeCode(code) { if (!code) return []; // Simple tokenization - can be enhanced with proper AST parsing return code .replace(/\s+/g, ' ') .replace(/[{}();,]/g, ' $& ') .split(/\s+/) .filter(token => token.length > 0); } /** * Calculate token similarity */ calculateTokenSimilarity(tokens1, tokens2) { const set1 = new Set(tokens1); const set2 = new Set(tokens2); const intersection = new Set([...set1].filter(x => set2.has(x))); const union = new Set([...set1, ...set2]); return union.size > 0 ? intersection.size / union.size : 0; } /** * Update existing pattern */ async updatePattern(similarPattern, newPattern, record) { const existingPattern = similarPattern.pattern; // Update occurrence count existingPattern.occurrences = (existingPattern.occurrences || 0) + 1; // Update success rate existingPattern.successCount = (existingPattern.successCount || 0) + 1; existingPattern.successRate = existingPattern.successCount / existingPattern.occurrences; // Merge benefits/improvements if (newPattern.benefits) { existingPattern.aggregatedBenefits = this.aggregateBenefits( existingPattern.aggregatedBenefits || {}, newPattern.benefits ); } // Add to usage history if (!existingPattern.usageHistory) { existingPattern.usageHistory = []; } existingPattern.usageHistory.push({ recordId: record.id, timestamp: record.timestamp, author: record.metadata.author, outcomes: record.outcomes }); // Update confidence score existingPattern.confidence = this.calculatePatternConfidence(existingPattern); // Check if pattern should be promoted if (existingPattern.occurrences >= this.learningThreshold && existingPattern.confidence > 0.8) { existingPattern.status = 'learned'; existingPattern.learnedAt = new Date().toISOString(); console.log(chalk.green(`✅ Pattern promoted to learned: ${similarPattern.key}`)); this.emit('pattern_learned', existingPattern); } } /** * Create new pattern */ async createPattern(key, pattern, record) { const newPattern = { ...pattern, key: key, occurrences: 1, successCount: 1, successRate: 1.0, firstSeen: record.timestamp, lastSeen: record.timestamp, status: 'candidate', confidence: 0.3, // Initial low confidence usageHistory: [{ recordId: record.id, timestamp: record.timestamp, author: record.metadata.author, outcomes: record.outcomes }] }; this.patterns.set(key, newPattern); console.log(chalk.gray(`New pattern candidate created: ${key}`)); } /** * Calculate pattern confidence */ calculatePatternConfidence(pattern) { let confidence = 0; // Occurrence factor (up to 0.3) const occurrenceFactor = Math.min(pattern.occurrences / 10, 0.3); confidence += occurrenceFactor; // Success rate factor (up to 0.4) confidence += pattern.successRate * 0.4; // Consistency factor (up to 0.2) const consistencyFactor = this.calculateConsistencyFactor(pattern.usageHistory); confidence += consistencyFactor * 0.2; // Recency factor (up to 0.1) const recencyFactor = this.calculateRecencyFactor(pattern.lastSeen); confidence += recencyFactor * 0.1; return Math.min(confidence, 1.0); } /** * Get pattern suggestions for modification */ async getPatternSuggestions(_context) { const suggestions = []; // Filter applicable patterns const applicablePatterns = Array.from(this.patterns.values()).filter(pattern => { return pattern.status === 'learned' && this.isPatternApplicable(pattern, context) && pattern.confidence > 0.7; }); // Sort by relevance and confidence applicablePatterns.sort((a, b) => { const relevanceA = this.calculateRelevance(a, context); const relevanceB = this.calculateRelevance(b, context); return (relevanceB * b.confidence) - (relevanceA * a.confidence); }); // Create suggestions for (const pattern of applicablePatterns.slice(0, 5)) { suggestions.push({ pattern: pattern, relevance: this.calculateRelevance(pattern, context), confidence: pattern.confidence, expectedBenefits: pattern.aggregatedBenefits || {}, applicationGuide: await this.generateApplicationGuide(pattern, context), examples: this.getPatternExamples(pattern) }); } return suggestions; } /** * Check if pattern is applicable */ isPatternApplicable(pattern, context) { // Check component type compatibility if (pattern.componentType && context.componentType) { if (pattern.componentType !== context.componentType && pattern.componentType !== 'any') { return false; } } // Check context requirements if (pattern.requiredContext) { for (const requirement of pattern.requiredContext) { if (!this.meetsContextRequirement(_context, requirement)) { return false; } } } // Check applicability conditions if (pattern.applicableContexts) { return pattern.applicableContexts.some(ctx => this.matchesContext(_context, ctx) ); } return true; } /** * Generate application guide */ async generateApplicationGuide(pattern, context) { const guide = { steps: [], preconditions: [], expectedOutcome: {}, risks: [], alternatives: [] }; // Generate steps based on pattern type switch (pattern.type) { case 'code_transformation': guide.steps = this.generateCodeTransformationSteps(pattern, context); break; case 'refactoring': guide.steps = pattern.steps || []; guide.preconditions = pattern.triggers || []; break; case 'performance': guide.steps = this.generatePerformanceOptimizationSteps(pattern, context); guide.expectedOutcome = pattern.metrics; break; } // Add general guidance guide.confidence = `${Math.round(pattern.confidence * 100)}%`; guide.successRate = `${Math.round(pattern.successRate * 100)}%`; guide.usageCount = pattern.occurrences; return guide; } /** * Analyze pattern relationships */ async analyzePatternRelationships(patterns) { // Find patterns that commonly occur together const coOccurrences = new Map(); for (let i = 0; i < patterns.length; i++) { for (let j = i + 1; j < patterns.length; j++) { const key = this.generateRelationshipKey(patterns[i], patterns[j]); const existing = coOccurrences.get(key) || { count: 0, patterns: [] }; existing.count++; existing.patterns = [patterns[i], patterns[j]]; coOccurrences.set(key, existing); } } // Store significant relationships for (const [key, relationship] of coOccurrences) { if (relationship.count >= 2) { await this.storePatternRelationship(key, relationship); } } } /** * Get pattern analytics */ async getPatternAnalytics() { const analytics = { totalPatterns: this.patterns.size, learnedPatterns: 0, candidatePatterns: 0, patternsByType: {}, topPatterns: [], recentTrends: [], effectivenessMetrics: {} }; // Count patterns by status and type for (const pattern of this.patterns.values()) { if (pattern.status === 'learned') { analytics.learnedPatterns++; } else { analytics.candidatePatterns++; } analytics.patternsByType[pattern.type] = (analytics.patternsByType[pattern.type] || 0) + 1; } // Get top patterns by usage const sortedPatterns = Array.from(this.patterns.values()) .sort((a, b) => b.occurrences - a.occurrences); analytics.topPatterns = sortedPatterns.slice(0, 10).map(p => ({ key: p.key, type: p.type, occurrences: p.occurrences, successRate: p.successRate, confidence: p.confidence })); // Calculate effectiveness metrics analytics.effectivenessMetrics = await this.calculateEffectivenessMetrics(); // Get recent trends analytics.recentTrends = await this.analyzeRecentTrends(); return analytics; } /** * Calculate effectiveness metrics */ async calculateEffectivenessMetrics() { const metrics = { averageSuccessRate: 0, averageConfidence: 0, patternCoverage: 0, learningRate: 0 }; const learnedPatterns = Array.from(this.patterns.values()) .filter(p => p.status === 'learned'); if (learnedPatterns.length > 0) { metrics.averageSuccessRate = learnedPatterns.reduce((sum, p) => sum + p.successRate, 0) / learnedPatterns.length; metrics.averageConfidence = learnedPatterns.reduce((sum, p) => sum + p.confidence, 0) / learnedPatterns.length; } // Calculate pattern coverage const modificationTypes = new Set(this.modificationHistory.map(m => m.modificationType)); const coveredTypes = new Set(learnedPatterns.map(p => p.type)); metrics.patternCoverage = modificationTypes.size > 0 ? coveredTypes.size / modificationTypes.size : 0; // Calculate learning rate const recentHistory = this.modificationHistory.slice(-20); const recentLearned = recentHistory.filter(m => m.patterns.some(p => this.patterns.get(this.generatePatternKey(p))?.status === 'learned') ); metrics.learningRate = recentHistory.length > 0 ? recentLearned.length / recentHistory.length : 0; return metrics; } /** * Helper methods */ normalizeCode(code) { if (!code) return ''; return code.trim().replace(/\s+/g, ' '); } generatePatternKey(pattern) { return `${pattern.type}:${pattern.subtype || 'default'}:${ crypto.createHash('md5').update(JSON.stringify(pattern)).digest('hex').substr(0, 8) }`; } aggregateBenefits(existing, newBenefits) { const aggregated = { ...existing }; for (const [key, value] of Object.entries(newBenefits)) { if (typeof value === 'number') { aggregated[key] = (aggregated[key] || 0) + value; aggregated[`${key}_avg`] = aggregated[key] / ((aggregated[`${key}_count`] || 0) + 1); aggregated[`${key}_count`] = (aggregated[`${key}_count`] || 0) + 1; } } return aggregated; } calculateConsistencyFactor(usageHistory) { if (usageHistory.length < 2) return 1.0; // Check time intervals between uses const intervals = []; for (let i = 1; i < usageHistory.length; i++) { const interval = new Date(usageHistory[i].timestamp) - new Date(usageHistory[i-1].timestamp); intervals.push(interval); } // Calculate variance const avgInterval = intervals.reduce((sum, i) => sum + i, 0) / intervals.length; const variance = intervals.reduce((sum, i) => sum + Math.pow(i - avgInterval, 2), 0) / intervals.length; const stdDev = Math.sqrt(variance); // Lower variance = higher consistency return 1 / (1 + stdDev / avgInterval); } calculateRecencyFactor(lastSeen) { const daysSinceLastSeen = (Date.now() - new Date(lastSeen).getTime()) / (1000 * 60 * 60 * 24); return Math.max(0, 1 - daysSinceLastSeen / 30); // Decay over 30 days } calculateRelevance(pattern, context) { let relevance = 0; // Type match if (pattern.type === context.modificationType) { relevance += 0.3; } // Component type match if (pattern.componentType === context.componentType) { relevance += 0.2; } // Context similarity if (pattern.context && context.currentContext) { relevance += this.calculateContextSimilarity(pattern.context, context.currentContext) * 0.3; } // Goal alignment if (pattern.benefits && context.goals) { relevance += this.calculateGoalAlignment(pattern.benefits, context.goals) * 0.2; } return relevance; } getPatternExamples(pattern) { return pattern.usageHistory .slice(-3) .map(usage => ({ recordId: usage.recordId, timestamp: usage.timestamp, author: usage.author, outcomes: usage.outcomes })); } /** * Save and load methods */ async saveHistory() { await fs.writeFile( this.historyFile, JSON.stringify(this.modificationHistory, null, 2) ); } async loadHistory() { try { const content = await fs.readFile(this.historyFile, 'utf-8'); this.modificationHistory = JSON.parse(content); } catch (_error) { // No history file yet this.modificationHistory = []; } } async savePatterns() { const patternsArray = Array.from(this.patterns.entries()).map(([key, pattern]) => ({ key, ...pattern })); await fs.writeFile( this.patternsFile, JSON.stringify(patternsArray, null, 2) ); } async loadPatterns() { try { const content = await fs.readFile(this.patternsFile, 'utf-8'); const patternsArray = JSON.parse(content); this.patterns.clear(); for (const pattern of patternsArray) { this.patterns.set(pattern.key, pattern); } } catch (_error) { // No patterns file yet } } /** * Check learning threshold */ async checkLearningThreshold(patterns) { let learnedCount = 0; for (const pattern of patterns) { const key = this.generatePatternKey(pattern); const existing = this.patterns.get(key); if (existing && existing.occurrences >= this.learningThreshold) { learnedCount++; } } return learnedCount > 0; } calculateContextSimilarity(context1, context2) { // Simple context similarity - can be enhanced if (!context1 || !context2) return 0; const keys1 = Object.keys(context1); const keys2 = Object.keys(context2); const commonKeys = keys1.filter(k => keys2.includes(k)); if (commonKeys.length === 0) return 0; let similarity = commonKeys.length / Math.max(keys1.length, keys2.length); // Check value similarity for common keys for (const key of commonKeys) { if (context1[key] === context2[key]) { similarity += 0.1; } } return Math.min(similarity, 1.0); } calculateStructuralSimilarity(struct1, struct2) { // Compare structural patterns if (!struct1 || !struct2) return 0; const before1 = JSON.stringify(struct1.before); const before2 = JSON.stringify(struct2.before); const after1 = JSON.stringify(struct1.after); const after2 = JSON.stringify(struct2.after); const beforeSim = before1 === before2 ? 1 : 0.5; const afterSim = after1 === after2 ? 1 : 0.5; return (beforeSim + afterSim) / 2; } calculateBenefitSimilarity(benefits1, benefits2) { if (!benefits1 || !benefits2) return 0; const keys1 = Object.keys(benefits1); const keys2 = Object.keys(benefits2); const allKeys = new Set([...keys1, ...keys2]); let similarity = 0; for (const key of allKeys) { if (benefits1[key] && benefits2[key]) { // Both have the benefit similarity += 1; } } return allKeys.size > 0 ? similarity / allKeys.size : 0; } calculateRefactoringSimilarity(refactor1, refactor2) { if (refactor1.refactoringType !== refactor2.refactoringType) return 0; let similarity = 0.5; // Base similarity for same type // Compare triggers if (refactor1.triggers && refactor2.triggers) { const commonTriggers = refactor1.triggers.filter(t => refactor2.triggers.includes(t) ); similarity += commonTriggers.length / Math.max(refactor1.triggers.length, refactor2.triggers.length) * 0.3; } // Compare steps if (refactor1.steps && refactor2.steps) { const stepSimilarity = Math.min(refactor1.steps.length, refactor2.steps.length) / Math.max(refactor1.steps.length, refactor2.steps.length); similarity += stepSimilarity * 0.2; } return similarity; } calculatePerformanceSimilarity(perf1, perf2) { if (perf1.optimizationType !== perf2.optimizationType) return 0; let similarity = 0.4; // Base similarity for same type if (perf1.technique === perf2.technique) { similarity += 0.3; } // Compare applicable contexts if (perf1.applicableContexts && perf2.applicableContexts) { const commonContexts = perf1.applicableContexts.filter(c => perf2.applicableContexts.includes(c) ); similarity += commonContexts.length / Math.max(perf1.applicableContexts.length, perf2.applicableContexts.length) * 0.3; } return similarity; } calculateGenericSimilarity(pattern1, pattern2) { // Generic JSON similarity const json1 = JSON.stringify(pattern1); const json2 = JSON.stringify(pattern2); if (json1 === json2) return 1.0; // Calculate Levenshtein distance ratio const distance = this.levenshteinDistance(json1, json2); const maxLength = Math.max(json1.length, json2.length); return 1 - (distance / maxLength); } levenshteinDistance(str1, str2) { const matrix = []; for (let i = 0; i <= str2.length; i++) { matrix[i] = [i]; } for (let j = 0; j <= str1.length; j++) { matrix[0][j] = j; } for (let i = 1; i <= str2.length; i++) { for (let j = 1; j <= str1.length; j++) { if (str2.charAt(i - 1) === str1.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min( matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1 ); } } } return matrix[str2.length][str1.length]; } meetsContextRequirement(_context, requirement) { // Check if context meets specific requirement if (requirement.type === 'has_property') { return context[requirement.property] !== undefined; } if (requirement.type === 'property_value') { return context[requirement.property] === requirement.value; } if (requirement.type === 'property_range') { const value = context[requirement.property]; return value >= requirement.min && value <= requirement.max; } return true; } matchesContext(_context, patternContext) { // Check if contexts match for (const [key, value] of Object.entries(patternContext)) { if (context[key] !== value) { return false; } } return true; } generateCodeTransformationSteps(pattern, context) { const steps = []; steps.push({ step: 1, action: 'Identify target code pattern', description: `Look for code matching: ${pattern.from}`, validation: 'Ensure code structure matches the pattern' }); steps.push({ step: 2, action: 'Apply transformation', description: `Transform to: ${pattern.to}`, validation: 'Verify transformation preserves functionality' }); if (pattern.context) { steps.push({ step: 3, action: 'Validate context', description: 'Ensure transformation is appropriate for context', validation: pattern.context }); } steps.push({ step: 4, action: 'Test changes', description: 'Run tests to ensure no regression', validation: 'All tests pass' }); return steps; } generatePerformanceOptimizationSteps(pattern, context) { const steps = []; steps.push({ step: 1, action: 'Measure baseline performance', description: 'Capture current performance metrics', validation: 'Baseline metrics recorded' }); steps.push({ step: 2, action: `Apply ${pattern.technique} optimization`, description: pattern.description || 'Apply performance optimization technique', validation: 'Optimization applied correctly' }); steps.push({ step: 3, action: 'Measure improved performance', description: 'Capture post-optimization metrics', validation: `Expected improvement: ${pattern.metrics.improvement}%` }); steps.push({ step: 4, action: 'Validate functionality', description: 'Ensure optimization didn\'t break functionality', validation: 'All tests pass' }); return steps; } generateRelationshipKey(pattern1, pattern2) { const types = [pattern1.type, pattern2.type].sort(); return `rel:${types.join(':')}`; } async storePatternRelationship(key, relationship) { // Store pattern relationships for future analysis const relationshipsFile = path.join(this.patternsDir, 'relationships.json'); let relationships = {}; try { const content = await fs.readFile(relationshipsFile, 'utf-8'); relationships = JSON.parse(content); } catch (_error) { // No relationships file yet } relationships[key] = relationship; await fs.writeFile(relationshipsFile, JSON.stringify(relationships, null, 2)); } async analyzeRecentTrends() { const recentModifications = this.modificationHistory.slice(-30); const trends = { emergingPatterns: [], decliningPatterns: [], stablePatterns: [] }; // Analyze pattern usage over time const patternUsage = new Map(); for (const mod of recentModifications) { for (const pattern of mod.patterns) { const key = this.generatePatternKey(pattern); const usage = patternUsage.get(key) || { count: 0, recent: 0 }; usage.count++; // Check if in last 10 modifications const modIndex = recentModifications.indexOf(mod); if (modIndex >= recentModifications.length - 10) { usage.recent++; } patternUsage.set(key, usage); } } // Classify patterns for (const [key, usage] of patternUsage) { const pattern = this.patterns.get(key); if (!pattern) continue; const recentRatio = usage.recent / usage.count; if (recentRatio > 0.6) { trends.emergingPatterns.push({ key: key, type: pattern.type, trend: 'emerging', usage: usage }); } else if (recentRatio < 0.2) { trends.decliningPatterns.push({ key: key, type: pattern.type, trend: 'declining', usage: usage }); } else { trends.stablePatterns.push({ key: key, type: pattern.type, trend: 'stable', usage: usage }); } } return trends; } calculateGoalAlignment(benefits, goals) { if (!benefits || !goals) return 0; let alignment = 0; let _matchedGoals = 0; for (const goal of goals) { if (goal.type === 'performance' && benefits.performanceImprovement) { alignment += benefits.performanceImprovement > goal.target ? 1 : 0.5; matchedGoals++; } if (goal.type === 'maintainability' && benefits.maintainability) { alignment += benefits.maintainability > goal.target ? 1 : 0.5; matchedGoals++; } if (goal.type === 'testability' && benefits.testability) { alignment += benefits.testability > goal.target ? 1 : 0.5; matchedGoals++; } } return goals.length > 0 ? alignment / goals.length : 0; } async updatePatternRankings() { // Update pattern rankings based on multiple factors for (const pattern of this.patterns.values()) { pattern.ranking = this.calculatePatternRanking(pattern); } // Save updated patterns await this.savePatterns(); } calculatePatternRanking(pattern) { let ranking = 0; // Success rate (40%) ranking += pattern.successRate * 40; // Usage frequency (30%) const usageScore = Math.min(pattern.occurrences / 20, 1); ranking += usageScore * 30; // Confidence (20%) ranking += pattern.confidence * 20; // Recency (10%) const recencyScore = this.calculateRecencyFactor(pattern.lastSeen); ranking += recencyScore * 10; return ranking; } } module.exports = PatternLearner;