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
JavaScript
/**
* 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