UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

306 lines 12.3 kB
import fs from 'fs-extra'; import * as path from 'path'; import simpleGit from 'simple-git'; import * as glob from 'glob'; export class PatternAnalyzer { projectPath; patternsFile; patterns; git; constructor(projectPath) { this.projectPath = projectPath; this.patternsFile = path.join(projectPath, '.mira', 'patterns', 'history.json'); this.patterns = new Map(); this.git = simpleGit(projectPath); this.loadPatterns(); } loadPatterns() { try { if (fs.existsSync(this.patternsFile)) { const data = fs.readJsonSync(this.patternsFile); for (const pattern of data.patterns || []) { this.patterns.set(pattern.id, { ...pattern, lastSeen: new Date(pattern.lastSeen) }); } } } catch (error) { console.error('Error loading patterns:', error); } } savePatterns() { const dir = path.dirname(this.patternsFile); fs.ensureDirSync(dir); const data = { patterns: Array.from(this.patterns.values()), lastUpdated: new Date().toISOString() }; fs.writeJsonSync(this.patternsFile, data, { spaces: 2 }); } async analyzeGitPatterns() { const patterns = []; try { // Analyze commit patterns const log = await this.git.log({ maxCount: 100 }); const commitPatterns = this.analyzeCommitMessages([...log.all]); patterns.push(...commitPatterns); // Analyze file change patterns const filePatterns = await this.analyzeFileChangePatterns(); patterns.push(...filePatterns); // Analyze development cycle patterns const cyclePatterns = await this.analyzeDevelopmentCycles([...log.all]); patterns.push(...cyclePatterns); } catch (error) { console.error('Error analyzing git patterns:', error); } // Update stored patterns for (const pattern of patterns) { this.patterns.set(pattern.id, pattern); } this.savePatterns(); return patterns; } analyzeCommitMessages(commits) { const patterns = []; const prefixCount = {}; const timeOfDayCount = {}; for (const commit of commits) { // Analyze commit prefixes const prefix = commit.message.split(':')[0].toLowerCase(); if (prefix.length < 20) { prefixCount[prefix] = (prefixCount[prefix] || 0) + 1; } // Analyze commit times const hour = new Date(commit.date).getHours(); const timeOfDay = hour < 6 ? 'night' : hour < 12 ? 'morning' : hour < 18 ? 'afternoon' : 'evening'; timeOfDayCount[timeOfDay] = (timeOfDayCount[timeOfDay] || 0) + 1; } // Create patterns from analysis for (const [prefix, count] of Object.entries(prefixCount)) { if (count >= 3) { patterns.push({ id: `commit-prefix-${prefix}`, type: 'commit', description: `Frequent use of "${prefix}" commit prefix`, frequency: count, lastSeen: new Date(), data: { prefix, count } }); } } // Most active time pattern const mostActiveTime = Object.entries(timeOfDayCount) .sort((a, b) => b[1] - a[1])[0]; if (mostActiveTime) { patterns.push({ id: 'commit-time-pattern', type: 'commit', description: `Most commits during ${mostActiveTime[0]}`, frequency: mostActiveTime[1], lastSeen: new Date(), data: { timeOfDay: mostActiveTime[0], distribution: timeOfDayCount } }); } return patterns; } async analyzeFileChangePatterns() { const patterns = []; try { const status = await this.git.status(); const modifiedFiles = status.modified; // Analyze frequently modified files const fileModCount = {}; // Get file modification history for (const file of modifiedFiles) { const log = await this.git.log({ file, maxCount: 20 }); fileModCount[file] = log.total; } // Create patterns for frequently modified files for (const [file, count] of Object.entries(fileModCount)) { if (count >= 5) { patterns.push({ id: `file-hotspot-${file.replace(/[\/\\]/g, '-')}`, type: 'file_change', description: `${file} is frequently modified`, frequency: count, lastSeen: new Date(), data: { file, modificationCount: count } }); } } } catch (error) { console.error('Error analyzing file patterns:', error); } return patterns; } async analyzeDevelopmentCycles(commits) { const patterns = []; // Analyze commit frequency patterns const commitsByDay = {}; const commitsByDayOfWeek = {}; for (const commit of commits) { const date = new Date(commit.date); const dayKey = date.toISOString().split('T')[0]; const dayOfWeek = date.getDay(); commitsByDay[dayKey] = (commitsByDay[dayKey] || 0) + 1; commitsByDayOfWeek[dayOfWeek] = (commitsByDayOfWeek[dayOfWeek] || 0) + 1; } // Find most productive day of week const mostProductiveDay = Object.entries(commitsByDayOfWeek) .sort((a, b) => b[1] - a[1])[0]; if (mostProductiveDay) { const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; patterns.push({ id: 'productive-day-pattern', type: 'development_cycle', description: `Most productive on ${days[parseInt(mostProductiveDay[0])]}s`, frequency: mostProductiveDay[1], lastSeen: new Date(), data: { dayOfWeek: parseInt(mostProductiveDay[0]), distribution: commitsByDayOfWeek } }); } // Analyze burst patterns const sortedDays = Object.entries(commitsByDay).sort((a, b) => a[0].localeCompare(b[0])); let currentBurst = 0; let maxBurst = 0; for (let i = 1; i < sortedDays.length; i++) { const prevDate = new Date(sortedDays[i - 1][0]); const currDate = new Date(sortedDays[i][0]); const daysDiff = (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24); if (daysDiff === 1) { currentBurst++; maxBurst = Math.max(maxBurst, currentBurst); } else { currentBurst = 0; } } if (maxBurst >= 3) { patterns.push({ id: 'burst-development-pattern', type: 'development_cycle', description: `Tends to work in bursts of ${maxBurst}+ consecutive days`, frequency: maxBurst, lastSeen: new Date(), data: { maxConsecutiveDays: maxBurst } }); } return patterns; } async analyzeCodePatterns() { const patterns = []; try { // Analyze code structure patterns const codePatterns = await this.analyzeCodeStructure(); patterns.push(...codePatterns); // Analyze import patterns const importPatterns = await this.analyzeImportPatterns(); patterns.push(...importPatterns); } catch (error) { console.error('Error analyzing code patterns:', error); } return patterns; } async analyzeCodeStructure() { const patterns = []; const extensions = ['.js', '.ts', '.py', '.java', '.cs', '.cpp']; for (const ext of extensions) { const files = glob.sync(`**/*${ext}`, { cwd: this.projectPath, ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**'] }); if (files.length >= 5) { // Analyze common patterns in these files let totalLines = 0; let filesWithClasses = 0; let filesWithFunctions = 0; for (const file of files.slice(0, 20)) { // Sample first 20 files try { const content = fs.readFileSync(path.join(this.projectPath, file), 'utf-8'); const lines = content.split('\n'); totalLines += lines.length; if (content.match(/class\s+\w+/)) filesWithClasses++; if (content.match(/function\s+\w+|const\s+\w+\s*=\s*\(|def\s+\w+/)) filesWithFunctions++; } catch (error) { // Skip files that can't be read } } const avgLines = Math.round(totalLines / Math.min(files.length, 20)); patterns.push({ id: `code-structure-${ext}`, type: 'file_change', description: `${ext} files average ${avgLines} lines`, frequency: files.length, lastSeen: new Date(), data: { extension: ext, fileCount: files.length, avgLines, classUsage: filesWithClasses, functionUsage: filesWithFunctions } }); } } return patterns; } async analyzeImportPatterns() { const patterns = []; const importCounts = {}; // Analyze JavaScript/TypeScript imports const jsFiles = glob.sync('**/*.{js,ts,jsx,tsx}', { cwd: this.projectPath, ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**'] }); for (const file of jsFiles.slice(0, 50)) { // Sample first 50 files try { const content = fs.readFileSync(path.join(this.projectPath, file), 'utf-8'); const imports = content.match(/import\s+.*?from\s+['"](.+?)['"]/g) || []; for (const imp of imports) { const match = imp.match(/from\s+['"](.+?)['"]/); if (match) { const module = match[1]; const baseModule = module.startsWith('.') ? 'relative' : module.split('/')[0]; importCounts[baseModule] = (importCounts[baseModule] || 0) + 1; } } } catch (error) { // Skip files that can't be read } } // Create patterns for frequently used imports for (const [module, count] of Object.entries(importCounts)) { if (count >= 10 && module !== 'relative') { patterns.push({ id: `import-pattern-${module}`, type: 'file_change', description: `Frequently imports ${module}`, frequency: count, lastSeen: new Date(), data: { module, count } }); } } return patterns; } getPatterns() { return Array.from(this.patterns.values()); } getPatternById(id) { return this.patterns.get(id); } recordPattern(pattern) { this.patterns.set(pattern.id, pattern); this.savePatterns(); } } //# sourceMappingURL=PatternAnalyzer.js.map