UNPKG

ripple-ai-detector

Version:

🌊 Ripple AI Bug Detector - Built by an AI that knows its flaws. Catch AI-generated bugs before you commit.

185 lines • 7.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RippleAnalyzer = void 0; const function_signature_detector_1 = require("../detectors/function-signature-detector"); const ai_detector_1 = require("../detectors/ai-detector"); const file_utils_1 = require("../utils/file-utils"); class RippleAnalyzer { config; functionDetector; aiDetector; constructor(config) { this.config = config; this.functionDetector = new function_signature_detector_1.FunctionSignatureDetector(); this.aiDetector = new ai_detector_1.AIDetector(); } // Main analysis method async analyze(files) { const startTime = Date.now(); // Filter and validate files const validFiles = await this.filterValidFiles(files); if (validFiles.length === 0) { return this.createEmptyResult(startTime); } // Limit number of files to analyze const filesToAnalyze = validFiles.slice(0, this.config.analysis.maxFiles); const issues = []; // Run function signature detection if (this.config.rules.functionSignatureChange.enabled) { const functionIssues = await this.functionDetector.detect(filesToAnalyze); issues.push(...functionIssues); } // Run AI detection let aiGenerated = false; let aiConfidence = 0; if (this.config.aiDetection.enabled) { const aiResult = await this.aiDetector.detect(filesToAnalyze); aiGenerated = aiResult.isAIGenerated; aiConfidence = aiResult.confidence; // Add AI detection as an issue if confidence is high if (aiGenerated && aiConfidence > 0.7) { issues.push({ id: `ai-detection-${Date.now()}`, type: 'ai-pattern-detected', severity: 'warning', message: `AI-generated changes detected (${Math.round(aiConfidence * 100)}% confidence)`, file: filesToAnalyze[0], // Primary file details: { aiPatterns: aiResult.patterns.map(p => p.type), aiConfidence, context: aiResult.reasoning.join('; ') }, suggestions: [ 'Review changes carefully before committing', 'Test all affected functionality', 'Consider running additional validation' ], confidence: aiConfidence }); } } const endTime = Date.now(); // Calculate overall confidence const overallConfidence = this.calculateOverallConfidence(issues, aiConfidence); // Create summary const summary = { filesAnalyzed: filesToAnalyze.length, errors: issues.filter(i => i.severity === 'error').length, warnings: issues.filter(i => i.severity === 'warning').length, aiDetections: aiGenerated ? 1 : 0, timeMs: endTime - startTime }; // Create metadata const metadata = { version: '1.0.0', timestamp: new Date().toISOString(), aiDetectionEnabled: this.config.aiDetection.enabled, rulesUsed: this.getEnabledRules() }; return { success: issues.filter(i => i.severity === 'error').length === 0, confidence: overallConfidence, aiGenerated, issues, summary, metadata }; } // Filter files to only include valid, supported files async filterValidFiles(files) { const validFiles = []; for (const file of files) { try { const exists = await file_utils_1.FileUtils.exists(file); const isSupported = file_utils_1.FileUtils.isSupportedFile(file); const shouldInclude = this.shouldIncludeFile(file); // Check if file exists and is supported if (exists && isSupported) { // Check against include/exclude patterns if (shouldInclude) { validFiles.push(file); } } } catch (error) { // Skip files that can't be accessed } } return validFiles; } // Check if file should be included based on config patterns shouldIncludeFile(file) { const relativePath = file_utils_1.FileUtils.getRelativePath(file); // For MVP: Simple check for JS/TS files return relativePath.endsWith('.js') || relativePath.endsWith('.ts') || relativePath.endsWith('.jsx') || relativePath.endsWith('.tsx'); } // Simple pattern matching (in production, use a proper glob library) matchesPattern(filePath, pattern) { // Handle {js,ts,jsx,tsx} syntax let regexPattern = pattern; // Expand {js,ts,jsx,tsx} to (js|ts|jsx|tsx) regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (match, group) => { const options = group.split(',').map((s) => s.trim()); return `(${options.join('|')})`; }); // Convert glob pattern to regex (order matters!) regexPattern = regexPattern .replace(/\*\*/g, '__DOUBLE_STAR__') // Temporarily replace ** .replace(/\./g, '\\.') // Escape dots first .replace(/__DOUBLE_STAR__/g, '.*') // Then replace ** with .* .replace(/\*/g, '[^/]*'); // Finally replace single * with [^/]* const regex = new RegExp(`^${regexPattern}$`); return regex.test(filePath); } // Calculate overall confidence score calculateOverallConfidence(issues, aiConfidence) { if (issues.length === 0) { return 0.95; // High confidence when no issues found } // Average confidence of all issues const issueConfidences = issues.map(i => i.confidence); const avgIssueConfidence = issueConfidences.reduce((sum, conf) => sum + conf, 0) / issueConfidences.length; // Combine with AI detection confidence const combinedConfidence = (avgIssueConfidence + aiConfidence) / 2; return Math.min(combinedConfidence, 0.99); // Cap at 99% } // Get list of enabled rules getEnabledRules() { const rules = []; if (this.config.rules.functionSignatureChange.enabled) { rules.push('function-signature-change'); } if (this.config.rules.importExportMismatch.enabled) { rules.push('import-export-mismatch'); } if (this.config.rules.typeMismatch.enabled) { rules.push('type-mismatch'); } return rules; } // Create empty result when no files to analyze createEmptyResult(startTime) { return { success: true, confidence: 1.0, aiGenerated: false, issues: [], summary: { filesAnalyzed: 0, errors: 0, warnings: 0, aiDetections: 0, timeMs: Date.now() - startTime }, metadata: { version: '1.0.0', timestamp: new Date().toISOString(), aiDetectionEnabled: this.config.aiDetection.enabled, rulesUsed: this.getEnabledRules() } }; } } exports.RippleAnalyzer = RippleAnalyzer; //# sourceMappingURL=analyzer.js.map