UNPKG

@wiber/ccs

Version:

Turn any codebase into an AI-aware environment. Claude launches with full context, asks smart questions, and gets better with every interaction.

473 lines (397 loc) 14.1 kB
/** * Pattern Analyzer Tool - Analyzes patterns in logs and data */ class PatternAnalyzerTool { constructor(options = {}) { this.projectPath = options.projectPath || process.cwd(); this.patterns = this.getDefaultPatterns(); } async execute(params, context) { console.log('🔍 Analyzing patterns...'); const focus = params.focus || 'errors-and-performance'; const data = params.data || this.extractDataFromContext(context); const analysis = { focus, timestamp: new Date().toISOString(), patterns: {}, insights: [], metrics: {} }; // Analyze different pattern types based on focus switch (focus) { case 'errors-and-performance': analysis.patterns.errors = this.analyzeErrorPatterns(data); analysis.patterns.performance = this.analyzePerformancePatterns(data); break; case 'nuclear-approach': analysis.patterns.nuclear = this.analyzeNuclearPatterns(data); analysis.patterns.timing = this.analyzeTimingPatterns(data); break; case 'user-journey': analysis.patterns.userFlow = this.analyzeUserFlowPatterns(data); analysis.patterns.dropoffs = this.analyzeDropoffPatterns(data); break; default: analysis.patterns.general = this.analyzeGeneralPatterns(data); } // Generate insights analysis.insights = this.generateInsights(analysis.patterns, focus); // Calculate metrics analysis.metrics = this.calculateMetrics(analysis.patterns, data); console.log(`✅ Pattern analysis completed: ${analysis.insights.length} insights found`); return analysis; } extractDataFromContext(context) { const data = { logs: [], apiResults: [], documents: [] }; // Extract log data if (context.logs) { for (const [source, logData] of Object.entries(context.logs)) { data.logs.push({ source, content: logData.content || '', lineCount: logData.lineCount || 0, lastModified: logData.lastModified }); } } // Extract API test results if (context.stepResults && context.stepResults.results) { data.apiResults = context.stepResults.results; } // Extract document data if (context.businessDocuments) { for (const [path, docData] of Object.entries(context.businessDocuments)) { data.documents.push({ path, content: docData.content || '', size: docData.size || 0 }); } } return data; } analyzeErrorPatterns(data) { const patterns = { errorTypes: {}, errorFrequency: {}, criticalErrors: [], errorTrends: [] }; for (const log of data.logs) { const content = log.content; // Error type analysis const errorMatches = [ { type: 'HTTP_ERROR', regex: /HTTP\s+(\d{3})/gi }, { type: 'CRITICAL', regex: /CRITICAL|FATAL/gi }, { type: 'ERROR', regex: /ERROR/gi }, { type: 'FAILED', regex: /FAILED|Failed/gi }, { type: 'TIMEOUT', regex: /TIMEOUT|timeout/gi }, { type: 'CONNECTION', regex: /connection\s+(refused|reset|timeout)/gi } ]; for (const { type, regex } of errorMatches) { const matches = content.match(regex) || []; patterns.errorTypes[type] = (patterns.errorTypes[type] || 0) + matches.length; if (matches.length > 0) { patterns.errorFrequency[`${log.source}:${type}`] = matches.length; } } // Critical error detection const criticalPattern = /CRITICAL|FATAL|500|failed.*critical/gi; const criticalMatches = content.match(criticalPattern) || []; if (criticalMatches.length > 0) { patterns.criticalErrors.push({ source: log.source, count: criticalMatches.length, sample: this.extractSample(content, criticalPattern) }); } } return patterns; } analyzePerformancePatterns(data) { const patterns = { responseTimes: [], slowQueries: [], bottlenecks: [], throughput: {} }; for (const log of data.logs) { const content = log.content; // Response time analysis const timeMatches = content.match(/(\d+)ms/g) || []; const times = timeMatches.map(match => parseInt(match.replace('ms', ''))); patterns.responseTimes.push(...times); // Slow query detection const slowQueryPattern = /took\s+(\d+\.?\d*)s/gi; const slowQueries = content.match(slowQueryPattern) || []; if (slowQueries.length > 0) { patterns.slowQueries.push({ source: log.source, queries: slowQueries, count: slowQueries.length }); } // Request throughput const requestPattern = /\[(GET|POST|PUT|DELETE)\]/gi; const requests = content.match(requestPattern) || []; patterns.throughput[log.source] = requests.length; } // Calculate performance metrics if (patterns.responseTimes.length > 0) { patterns.responseTimeStats = { min: Math.min(...patterns.responseTimes), max: Math.max(...patterns.responseTimes), avg: Math.round(patterns.responseTimes.reduce((a, b) => a + b, 0) / patterns.responseTimes.length), p95: this.calculatePercentile(patterns.responseTimes, 0.95) }; } return patterns; } analyzeNuclearPatterns(data) { const patterns = { nuclearTriggers: [], emailResponses: [], timing: [], success: 0, failures: 0 }; for (const log of data.logs) { const content = log.content; // Nuclear approach specific patterns const nuclearPattern = /NUCLEAR|nuclear.*trigger|immediate.*email/gi; const nuclearMatches = content.match(nuclearPattern) || []; if (nuclearMatches.length > 0) { patterns.nuclearTriggers.push({ source: log.source, count: nuclearMatches.length, samples: this.extractSamples(content, nuclearPattern, 3) }); } // Email response timing const emailPattern = /email.*sent.*(\d+)ms/gi; const emailMatches = [...content.matchAll(emailPattern)]; for (const match of emailMatches) { const timing = parseInt(match[1]); patterns.timing.push(timing); if (timing <= 3000) { // 3 second threshold patterns.success++; } else { patterns.failures++; } } // Track open events const trackPattern = /\[Track Open\]/gi; const trackMatches = content.match(trackPattern) || []; patterns.emailResponses.push(...trackMatches.map(() => ({ source: log.source, timestamp: new Date().toISOString() // Would parse actual timestamp }))); } return patterns; } analyzeUserFlowPatterns(data) { const patterns = { userJourneys: {}, dropoffPoints: [], conversionFunnels: {}, sessionFlow: [] }; for (const log of data.logs) { const content = log.content; // Session tracking const sessionPattern = /session[:\s]+([a-zA-Z0-9-]+)/gi; const sessionMatches = [...content.matchAll(sessionPattern)]; for (const match of sessionMatches) { const sessionId = match[1]; if (!patterns.userJourneys[sessionId]) { patterns.userJourneys[sessionId] = []; } // Extract user actions for this session const actionPattern = new RegExp(`${sessionId}.*?(GET|POST|PUT|DELETE)\\s+([^\\s]+)`, 'gi'); const actionMatches = [...content.matchAll(actionPattern)]; for (const actionMatch of actionMatches) { patterns.userJourneys[sessionId].push({ method: actionMatch[1], path: actionMatch[2], timestamp: new Date().toISOString() // Would parse actual timestamp }); } } // 404 patterns (potential dropoff points) const notFoundPattern = /404.*?([^\s]+)/gi; const notFoundMatches = [...content.matchAll(notFoundPattern)]; patterns.dropoffPoints.push(...notFoundMatches.map(match => ({ path: match[1], source: log.source }))); } return patterns; } analyzeGeneralPatterns(data) { const patterns = { logLevels: {}, sources: {}, timestamps: [], keywords: {} }; for (const log of data.logs) { const content = log.content; // Log level distribution const levels = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL']; for (const level of levels) { const regex = new RegExp(level, 'gi'); const matches = content.match(regex) || []; patterns.logLevels[level] = (patterns.logLevels[level] || 0) + matches.length; } // Source analysis patterns.sources[log.source] = { lineCount: log.lineCount, size: content.length, lastModified: log.lastModified }; // Common keywords const words = content.toLowerCase().split(/\s+/); const significantWords = words.filter(word => word.length > 3 && !this.isCommonWord(word) && /^[a-z]+$/.test(word) ); for (const word of significantWords) { patterns.keywords[word] = (patterns.keywords[word] || 0) + 1; } } // Sort keywords by frequency patterns.topKeywords = Object.entries(patterns.keywords) .sort(([,a], [,b]) => b - a) .slice(0, 10) .map(([word, count]) => ({ word, count })); return patterns; } generateInsights(patterns, focus) { const insights = []; // Error insights if (patterns.errors) { const totalErrors = Object.values(patterns.errors.errorTypes).reduce((a, b) => a + b, 0); if (totalErrors > 10) { insights.push({ type: 'warning', category: 'errors', message: `High error volume detected: ${totalErrors} errors found`, priority: 'high' }); } if (patterns.errors.criticalErrors.length > 0) { insights.push({ type: 'critical', category: 'errors', message: `Critical errors found in ${patterns.errors.criticalErrors.length} sources`, priority: 'critical' }); } } // Performance insights if (patterns.performance && patterns.performance.responseTimeStats) { const stats = patterns.performance.responseTimeStats; if (stats.avg > 1000) { insights.push({ type: 'warning', category: 'performance', message: `High average response time: ${stats.avg}ms`, priority: 'medium' }); } if (stats.p95 > 5000) { insights.push({ type: 'warning', category: 'performance', message: `95th percentile response time is high: ${stats.p95}ms`, priority: 'high' }); } } // Nuclear approach insights if (patterns.nuclear) { const successRate = patterns.nuclear.success / (patterns.nuclear.success + patterns.nuclear.failures); if (successRate < 0.95) { insights.push({ type: 'warning', category: 'nuclear', message: `Nuclear approach success rate below target: ${(successRate * 100).toFixed(1)}%`, priority: 'high' }); } if (patterns.nuclear.timing.length > 0) { const avgTiming = patterns.nuclear.timing.reduce((a, b) => a + b, 0) / patterns.nuclear.timing.length; if (avgTiming > 3000) { insights.push({ type: 'warning', category: 'nuclear', message: `Nuclear approach timing above 3s threshold: ${avgTiming.toFixed(0)}ms`, priority: 'high' }); } } } return insights; } calculateMetrics(patterns, data) { const metrics = { totalLogLines: data.logs.reduce((sum, log) => sum + log.lineCount, 0), totalLogSources: data.logs.length, analysisTimestamp: new Date().toISOString() }; if (patterns.errors) { metrics.errorRate = Object.values(patterns.errors.errorTypes).reduce((a, b) => a + b, 0) / metrics.totalLogLines; } if (patterns.performance && patterns.performance.responseTimeStats) { metrics.averageResponseTime = patterns.performance.responseTimeStats.avg; } if (patterns.nuclear) { metrics.nuclearSuccessRate = patterns.nuclear.success / (patterns.nuclear.success + patterns.nuclear.failures); } return metrics; } // Utility methods extractSample(content, pattern) { const lines = content.split('\n'); for (const line of lines) { if (pattern.test(line)) { return line.trim().substring(0, 200); } } return null; } extractSamples(content, pattern, count = 3) { const lines = content.split('\n'); const samples = []; for (const line of lines) { if (pattern.test(line) && samples.length < count) { samples.push(line.trim().substring(0, 200)); } } return samples; } calculatePercentile(values, percentile) { const sorted = values.slice().sort((a, b) => a - b); const index = Math.ceil(sorted.length * percentile) - 1; return sorted[index] || 0; } isCommonWord(word) { const commonWords = ['the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'man', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'boy', 'did', 'its', 'let', 'put', 'say', 'she', 'too', 'use']; return commonWords.includes(word); } getDefaultPatterns() { return { error: /ERROR|CRITICAL|FATAL/gi, warning: /WARN|WARNING/gi, performance: /(\d+)ms|took\s+(\d+\.?\d*)s/gi, nuclear: /NUCLEAR|nuclear.*trigger/gi, session: /session[:\s]+([a-zA-Z0-9-]+)/gi }; } } module.exports = PatternAnalyzerTool;