UNPKG

code-time-machine-mcp-server

Version:

Revolutionary MCP server that analyzes code evolution, predicts bugs, and provides historical insights about code patterns and development trends

265 lines • 11.2 kB
#!/usr/bin/env node /** * Test script for the Code Time Machine MCP Server * This demonstrates the server's capabilities by analyzing our own codebase */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; // ES module compatibility const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Import our CodeTimeMachine class for direct testing class CodeTimeMachineTest { /** * Test the bug hotspot prediction on our own MCP server code */ async testBugHotspotPrediction() { console.log('šŸ•°ļø Code Time Machine - Bug Hotspot Analysis Test\n'); // Read our own source code from the src directory const sourceCode = fs.readFileSync(path.join(__dirname, '..', 'src', 'index.ts'), 'utf8'); // Simulate the bug hotspot analysis const bugPatterns = [ 'catch.*{\\s*}', // Empty catch blocks 'TODO|FIXME|HACK', // Technical debt markers 'any.*=', // Usage of 'any' type (TypeScript anti-pattern) 'console\\.log', // Debug prints '==.*null', // Loose null comparison ]; const bugRisks = []; const lines = sourceCode.split('\n'); // Analyze each line for patterns lines.forEach((line, index) => { bugPatterns.forEach(pattern => { const regex = new RegExp(pattern, 'gi'); if (regex.test(line)) { bugRisks.push({ line: index + 1, pattern: pattern, risk: this.calculateRiskScore(pattern), code: line.trim(), suggestion: this.getSuggestion(pattern) }); } }); }); // Calculate complexity const complexity = this.calculateComplexity(sourceCode); // Generate report console.log('šŸ“Š Analysis Results:'); console.log('='.repeat(50)); console.log(`šŸ“ File: src/index.ts`); console.log(`šŸ“ Lines of Code: ${lines.length}`); console.log(`šŸ”¢ Cyclomatic Complexity: ${complexity}`); console.log(`āš ļø Risk Patterns Found: ${bugRisks.length}\n`); if (bugRisks.length > 0) { console.log('šŸ” Detected Issues:'); bugRisks.forEach((risk, i) => { console.log(`\n${i + 1}. Line ${risk.line}: ${risk.pattern}`); console.log(` Risk Level: ${risk.risk}/10`); console.log(` Code: ${risk.code}`); console.log(` šŸ’” Suggestion: ${risk.suggestion}`); }); } else { console.log('āœ… No high-risk patterns detected!'); } // Overall assessment const overallScore = this.calculateOverallScore(bugRisks, complexity); console.log('\n' + '='.repeat(50)); console.log(`šŸŽÆ Overall Code Quality Score: ${overallScore}/100`); console.log(`šŸ“ˆ Assessment: ${this.getQualityRating(overallScore)}`); return { bugRisks, complexity, overallScore, linesOfCode: lines.length }; } /** * Test the code entropy analysis */ async testCodeEntropyAnalysis() { console.log('\nšŸŒŖļø Code Entropy Analysis Test\n'); const sourceCode = fs.readFileSync(path.join(__dirname, '..', 'src', 'index.ts'), 'utf8'); // Calculate different entropy metrics const entropy = { structural: this.calculateStructuralEntropy(sourceCode), lexical: this.calculateLexicalEntropy(sourceCode), semantic: this.calculateSemanticEntropy(sourceCode), overall: 0 }; entropy.overall = (entropy.structural + entropy.lexical + entropy.semantic) / 3; console.log('šŸ“Š Entropy Metrics:'); console.log(`šŸ—ļø Structural Entropy: ${entropy.structural.toFixed(2)}`); console.log(`šŸ“ Lexical Entropy: ${entropy.lexical.toFixed(2)}`); console.log(`🧠 Semantic Entropy: ${entropy.semantic.toFixed(2)}`); console.log(`🌟 Overall Entropy: ${entropy.overall.toFixed(2)}`); const refactoringUrgency = this.calculateRefactoringUrgency(entropy.overall); console.log(`\nšŸ”„ Refactoring Urgency: ${refactoringUrgency}/100`); console.log(`šŸ“‹ Recommendation: ${this.getRefactoringRecommendation(refactoringUrgency)}`); return entropy; } /** * Test technical debt analysis */ async testTechnicalDebtAnalysis() { console.log('\nšŸ—ļø Technical Debt Analysis Test\n'); const sourceCode = fs.readFileSync(path.join(__dirname, '..', 'src', 'index.ts'), 'utf8'); const debt = { total: 0, codeSmells: 0, complexity: 0, documentation: 0 }; // Analyze code smells const codeSmells = [ /\/\/ TODO/gi, /\/\/ FIXME/gi, /\/\/ HACK/gi, /any\s*:/gi, /console\.log/gi ]; codeSmells.forEach(smell => { const matches = sourceCode.match(smell); if (matches) { debt.codeSmells += matches.length; } }); // Calculate complexity debt debt.complexity = this.calculateComplexity(sourceCode); // Documentation assessment const commentLines = sourceCode.split('\n').filter(line => line.trim().startsWith('//') || line.trim().startsWith('/*') || line.trim().startsWith('*')).length; const totalLines = sourceCode.split('\n').length; debt.documentation = Math.round((commentLines / totalLines) * 100); // Calculate total debt score debt.total = Math.min((debt.codeSmells * 5) + (debt.complexity * 2) + (Math.max(0, 30 - debt.documentation)), 100); console.log('šŸ“Š Technical Debt Profile:'); console.log(`šŸŽÆ Total Debt Score: ${debt.total}/100`); console.log(`šŸ‘ƒ Code Smells: ${debt.codeSmells}`); console.log(`šŸ”¢ Complexity Debt: ${debt.complexity}/10`); console.log(`šŸ“– Documentation: ${debt.documentation}%`); console.log(`\nšŸ’” Debt Level: ${this.getDebtLevel(debt.total)}`); return debt; } // Helper methods calculateRiskScore(pattern) { const riskMap = { 'catch.*{\\s*}': 9, 'TODO|FIXME|HACK': 4, 'any.*=': 6, 'console\\.log': 3, '==.*null': 7 }; return riskMap[pattern] || 5; } getSuggestion(pattern) { const suggestions = { 'catch.*{\\s*}': 'Add proper error handling in catch blocks', 'TODO|FIXME|HACK': 'Address technical debt markers', 'any.*=': 'Use specific types instead of any', 'console\\.log': 'Remove debug logs before production', '==.*null': 'Use strict equality (===) for null checks' }; return suggestions[pattern] || 'Review this pattern'; } calculateComplexity(code) { const keywords = ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||']; let complexity = 1; keywords.forEach(keyword => { const regex = new RegExp(`\\b${keyword}\\b`, 'gi'); const matches = code.match(regex); if (matches) { complexity += matches.length; } }); return complexity; } calculateOverallScore(bugRisks, complexity) { const riskScore = bugRisks.reduce((sum, risk) => sum + risk.risk, 0); const complexityPenalty = Math.min(complexity * 2, 30); const score = Math.max(0, 100 - riskScore * 2 - complexityPenalty); return Math.round(score); } getQualityRating(score) { if (score >= 80) return '🟢 Excellent'; if (score >= 60) return '🟔 Good'; if (score >= 40) return '🟠 Needs Improvement'; return 'šŸ”“ Poor - Immediate Attention Required'; } calculateStructuralEntropy(code) { const nestingLevels = code.split('\n').map(line => { const leading = line.match(/^\s*/)?.[0].length || 0; return Math.floor(leading / 2); }); const maxNesting = Math.max(...nestingLevels); return Math.min(maxNesting / 5, 5); // Normalize to 0-5 scale } calculateLexicalEntropy(code) { const words = code.match(/\b\w+\b/g) || []; const uniqueWords = new Set(words); return Math.min(words.length / uniqueWords.size / 10, 5); // Normalize to 0-5 scale } calculateSemanticEntropy(code) { const functions = code.match(/\bfunction\b|\b=>\b/g) || []; const classes = code.match(/\bclass\b/g) || []; const interfaces = code.match(/\binterface\b/g) || []; const totalStructures = functions.length + classes.length + interfaces.length; const lines = code.split('\n').length; return Math.min((lines / Math.max(totalStructures, 1)) / 50, 5); // Normalize to 0-5 scale } calculateRefactoringUrgency(entropy) { return Math.round(entropy * 20); // Convert 0-5 scale to 0-100 } getRefactoringRecommendation(urgency) { if (urgency > 70) return 'Immediate refactoring needed'; if (urgency > 40) return 'Refactoring recommended'; return 'Code structure is acceptable'; } getDebtLevel(debt) { if (debt > 70) return 'šŸ”“ High Debt - Urgent Action Required'; if (debt > 40) return '🟔 Moderate Debt - Plan Improvements'; return '🟢 Low Debt - Well Maintained'; } } // Run the tests async function runTests() { console.log('šŸš€ Starting Code Time Machine Tests...\n'); const tester = new CodeTimeMachineTest(); try { // Test 1: Bug Hotspot Analysis const bugResults = await tester.testBugHotspotPrediction(); // Test 2: Code Entropy Analysis const entropyResults = await tester.testCodeEntropyAnalysis(); // Test 3: Technical Debt Analysis const debtResults = await tester.testTechnicalDebtAnalysis(); // Summary console.log('\n' + '='.repeat(60)); console.log('šŸŽ‰ Code Time Machine Analysis Complete!'); console.log('='.repeat(60)); console.log('šŸ“Š Summary:'); console.log(` Bug Risk Patterns: ${bugResults.bugRisks.length}`); console.log(` Code Quality Score: ${bugResults.overallScore}/100`); console.log(` Overall Entropy: ${entropyResults.overall.toFixed(2)}`); console.log(` Technical Debt: ${debtResults.total}/100`); console.log('\n✨ The Code Time Machine MCP Server is working perfectly!'); } catch (error) { console.error('āŒ Test failed:', error); } } // Run tests automatically runTests(); export { CodeTimeMachineTest, runTests }; //# sourceMappingURL=test.js.map