superaugment
Version:
Enterprise-grade MCP server with world-class C++ analysis, robust error handling, and production-ready architecture for VS Code Augment
517 lines • 21.3 kB
JavaScript
import { z } from 'zod';
import { ConfigManager } from '../../config/ConfigManager.js';
import { FileSystemManager } from '../../utils/FileSystemManager.js';
import { logger } from '../../utils/logger.js';
const ReviewCodeInputSchema = z.object({
pullRequest: z.number().optional().describe('Pull request number to review'),
files: z.array(z.string()).optional().describe('Specific files to review (supports glob patterns)'),
diff: z.string().optional().describe('Code diff to review'),
projectPath: z.string().optional().describe('Project root path (defaults to current directory)'),
persona: z.string().optional().describe('Cognitive persona for review approach'),
criteria: z.array(z.string()).optional().describe('Review criteria (e.g., security, performance, style)'),
severity: z.enum(['low', 'medium', 'high', 'all']).default('all').describe('Minimum severity level to report'),
includeMetrics: z.boolean().default(true).describe('Include code quality metrics'),
maxFiles: z.number().default(50).describe('Maximum number of files to review'),
});
/**
* Code review tool with cognitive persona support
*/
export class ReviewCodeTool {
configManager;
name = 'review_code';
description = 'Perform comprehensive code reviews with real file analysis and cognitive persona expertise';
inputSchema = ReviewCodeInputSchema;
fileSystemManager;
constructor(configManager) {
this.configManager = configManager;
this.fileSystemManager = new FileSystemManager();
}
async execute(args) {
try {
logger.info('Starting code review', { args });
const validatedArgs = ReviewCodeInputSchema.parse(args);
const persona = validatedArgs.persona
? this.configManager.getPersona(validatedArgs.persona)
: null;
// Get files to review
const filesToReview = await this.getFilesToReview(validatedArgs);
// Perform comprehensive review
const review = await this.performReview(validatedArgs, filesToReview, persona);
return {
content: [
{
type: 'text',
text: this.formatReviewResult(review, persona),
},
],
};
}
catch (error) {
logger.error('Code review failed:', error);
throw error;
}
}
/**
* Get files to review based on input parameters
*/
async getFilesToReview(args) {
const projectPath = args.projectPath || process.cwd();
if (args.files && args.files.length > 0) {
// Review specific files
const files = await this.fileSystemManager.readFiles(args.files, projectPath);
return files.slice(0, args.maxFiles);
}
else {
// Review common code files
const defaultPatterns = [
'**/*.{js,ts,jsx,tsx,vue,py,java,go,rs,cpp,c,h}',
'!node_modules/**',
'!dist/**',
'!build/**',
'!coverage/**'
];
const files = await this.fileSystemManager.readFiles(defaultPatterns, projectPath);
return files.slice(0, args.maxFiles);
}
}
async performReview(args, files, persona) {
const review = {
summary: '',
files_reviewed: files.length,
total_lines: 0,
findings: [],
recommendations: [],
quality_metrics: {},
persona_insights: null,
};
let totalLines = 0;
const allFindings = [];
// Review each file
for (const file of files) {
if (!file.content)
continue;
const lines = file.content.split('\n');
totalLines += lines.length;
// Perform various code quality checks
const fileFindings = [
...this.checkCodeQuality(file, lines, args.criteria),
...this.checkBestPractices(file, lines),
...this.checkSecurity(file, lines),
...this.checkPerformance(file, lines),
...this.checkMaintainability(file, lines),
];
// Filter by severity
const filteredFindings = fileFindings.filter(finding => args.severity === 'all' || this.getSeverityLevel(finding.severity) >= this.getSeverityLevel(args.severity));
allFindings.push(...filteredFindings);
}
review.total_lines = totalLines;
review.findings = allFindings;
review.summary = `Reviewed ${files.length} files (${totalLines} lines). Found ${allFindings.length} issues.`;
// Generate recommendations
review.recommendations = this.generateRecommendations(allFindings, files, persona);
// Calculate quality metrics
if (args.includeMetrics) {
review.quality_metrics = this.calculateQualityMetrics(files, allFindings);
}
// Apply persona insights
if (persona) {
review.persona_insights = this.applyPersonaInsights(allFindings, persona);
}
return review;
}
formatReviewResult(review, persona) {
let result = '# Code Review Report\n\n';
if (persona) {
result += `**Reviewer Persona**: ${persona.name}\n\n`;
}
result += `## Summary\n${review.summary}\n\n`;
if (review.findings.length > 0) {
result += '## Findings\n';
review.findings.forEach((finding, index) => {
result += `${index + 1}. **${finding.type}** (${finding.severity}): ${finding.message}\n`;
if (finding.file)
result += ` File: ${finding.file}:${finding.line}\n`;
});
result += '\n';
}
if (review.recommendations.length > 0) {
result += '## Recommendations\n';
review.recommendations.forEach((rec, index) => {
result += `${index + 1}. ${rec}\n`;
});
}
return result;
}
/**
* Check code quality issues
*/
checkCodeQuality(file, lines, criteria) {
const findings = [];
const focusOnQuality = !criteria || criteria.includes('quality') || criteria.includes('style');
if (!focusOnQuality)
return findings;
lines.forEach((line, index) => {
const lineNum = index + 1;
const trimmed = line.trim();
// Check for console.log statements
if (trimmed.includes('console.log') || trimmed.includes('console.debug')) {
findings.push({
type: 'code_quality',
severity: 'low',
message: 'Remove console.log statements before production',
file: file.relativePath,
line: lineNum,
suggestion: 'Use proper logging library or remove debug statements',
});
}
// Check for TODO/FIXME comments
if (trimmed.includes('TODO') || trimmed.includes('FIXME') || trimmed.includes('HACK')) {
findings.push({
type: 'code_quality',
severity: 'low',
message: 'Unresolved TODO/FIXME comment',
file: file.relativePath,
line: lineNum,
suggestion: 'Address the TODO item or create a proper issue',
});
}
// Check for long lines
if (line.length > 120) {
findings.push({
type: 'code_quality',
severity: 'low',
message: 'Line too long (>120 characters)',
file: file.relativePath,
line: lineNum,
suggestion: 'Break long lines for better readability',
});
}
// Check for var usage in JavaScript/TypeScript
if ((file.extension === '.js' || file.extension === '.ts') && trimmed.startsWith('var ')) {
findings.push({
type: 'code_quality',
severity: 'medium',
message: 'Use let or const instead of var',
file: file.relativePath,
line: lineNum,
suggestion: 'Replace var with let or const for better scoping',
});
}
});
return findings;
}
/**
* Check best practices
*/
checkBestPractices(file, lines) {
const findings = [];
lines.forEach((line, index) => {
const lineNum = index + 1;
const trimmed = line.trim();
// Check for missing error handling
if (trimmed.includes('JSON.parse(') && !this.hasErrorHandling(lines, index)) {
findings.push({
type: 'best_practice',
severity: 'medium',
message: 'JSON.parse should be wrapped in try-catch',
file: file.relativePath,
line: lineNum,
suggestion: 'Add error handling for JSON parsing',
});
}
// Check for hardcoded URLs
if (trimmed.match(/https?:\/\/[^\s'"]+/)) {
findings.push({
type: 'best_practice',
severity: 'low',
message: 'Hardcoded URL found',
file: file.relativePath,
line: lineNum,
suggestion: 'Move URLs to configuration',
});
}
// Check for magic numbers
const magicNumberMatch = trimmed.match(/\b(\d{2,})\b/);
if (magicNumberMatch && magicNumberMatch[1] && !trimmed.includes('//') && !trimmed.includes('const')) {
const number = parseInt(magicNumberMatch[1]);
if (number > 10 && number !== 100 && number !== 1000) {
findings.push({
type: 'best_practice',
severity: 'low',
message: `Magic number ${number} should be a named constant`,
file: file.relativePath,
line: lineNum,
suggestion: 'Replace magic numbers with named constants',
});
}
}
});
return findings;
}
/**
* Check security issues
*/
checkSecurity(file, lines) {
const findings = [];
lines.forEach((line, index) => {
const lineNum = index + 1;
const trimmed = line.trim();
// Check for eval usage
if (trimmed.includes('eval(')) {
findings.push({
type: 'security',
severity: 'high',
message: 'Use of eval() is dangerous',
file: file.relativePath,
line: lineNum,
suggestion: 'Avoid eval() - use safer alternatives',
});
}
// Check for innerHTML usage
if (trimmed.includes('.innerHTML')) {
findings.push({
type: 'security',
severity: 'medium',
message: 'innerHTML usage may lead to XSS',
file: file.relativePath,
line: lineNum,
suggestion: 'Use textContent or sanitize HTML content',
});
}
// Check for hardcoded passwords/keys
const sensitivePatterns = [
/password\s*[:=]\s*['"][^'"]+['"]/i,
/api[_-]?key\s*[:=]\s*['"][^'"]+['"]/i,
/secret\s*[:=]\s*['"][^'"]+['"]/i,
/token\s*[:=]\s*['"][^'"]+['"]/i,
];
sensitivePatterns.forEach(pattern => {
if (pattern.test(trimmed)) {
findings.push({
type: 'security',
severity: 'high',
message: 'Hardcoded sensitive information detected',
file: file.relativePath,
line: lineNum,
suggestion: 'Move sensitive data to environment variables',
});
}
});
});
return findings;
}
/**
* Check performance issues
*/
checkPerformance(file, lines) {
const findings = [];
lines.forEach((line, index) => {
const lineNum = index + 1;
const trimmed = line.trim();
// Check for inefficient loops
if (trimmed.includes('for (') && trimmed.includes('.length')) {
findings.push({
type: 'performance',
severity: 'low',
message: 'Consider caching array length in loops',
file: file.relativePath,
line: lineNum,
suggestion: 'Cache array.length to avoid repeated property access',
});
}
// Check for synchronous file operations
if (trimmed.includes('readFileSync') || trimmed.includes('writeFileSync')) {
findings.push({
type: 'performance',
severity: 'medium',
message: 'Synchronous file operation blocks event loop',
file: file.relativePath,
line: lineNum,
suggestion: 'Use asynchronous file operations',
});
}
});
return findings;
}
/**
* Check maintainability issues
*/
checkMaintainability(file, lines) {
const findings = [];
// Check function length
let currentFunction = null;
let functionStartLine = 0;
let braceCount = 0;
lines.forEach((line, index) => {
const lineNum = index + 1;
const trimmed = line.trim();
// Simple function detection
if (trimmed.match(/function\s+\w+|const\s+\w+\s*=\s*\(|=>\s*{/)) {
currentFunction = trimmed;
functionStartLine = lineNum;
braceCount = 0;
}
if (currentFunction) {
braceCount += (line.match(/{/g) || []).length;
braceCount -= (line.match(/}/g) || []).length;
if (braceCount === 0 && lineNum > functionStartLine) {
const functionLength = lineNum - functionStartLine;
if (functionLength > 50) {
findings.push({
type: 'maintainability',
severity: 'medium',
message: `Function is too long (${functionLength} lines)`,
file: file.relativePath,
line: functionStartLine,
suggestion: 'Break down large functions into smaller ones',
});
}
currentFunction = null;
}
}
});
return findings;
}
/**
* Check if error handling exists around a line
*/
hasErrorHandling(lines, lineIndex) {
const start = Math.max(0, lineIndex - 5);
const end = Math.min(lines.length, lineIndex + 5);
for (let i = start; i < end; i++) {
const line = lines[i];
if (line && (line.includes('try') || line.includes('catch'))) {
return true;
}
}
return false;
}
/**
* Get severity level as number for comparison
*/
getSeverityLevel(severity) {
switch (severity) {
case 'low': return 1;
case 'medium': return 2;
case 'high': return 3;
default: return 0;
}
}
/**
* Generate recommendations based on findings
*/
generateRecommendations(findings, _files, persona) {
const recommendations = [];
// Count findings by type
const findingsByType = findings.reduce((acc, finding) => {
acc[finding.type] = (acc[finding.type] || 0) + 1;
return acc;
}, {});
// Generate general recommendations
if (findingsByType['security'] > 0) {
recommendations.push('Address security vulnerabilities immediately');
recommendations.push('Consider implementing security linting rules');
}
if (findingsByType['performance'] > 0) {
recommendations.push('Optimize performance bottlenecks');
recommendations.push('Consider performance testing');
}
if (findingsByType['maintainability'] > 0) {
recommendations.push('Refactor complex functions for better maintainability');
recommendations.push('Consider adding code complexity limits');
}
if (findingsByType['code_quality'] > 0) {
recommendations.push('Improve code quality and consistency');
recommendations.push('Set up automated code formatting');
}
// Add persona-specific recommendations
if (persona) {
switch (persona.name) {
case 'security':
recommendations.push('Implement security-first development practices');
recommendations.push('Add security testing to CI/CD pipeline');
break;
case 'performance':
recommendations.push('Add performance monitoring and profiling');
recommendations.push('Implement performance budgets');
break;
case 'architect':
recommendations.push('Review overall system architecture');
recommendations.push('Consider design patterns and SOLID principles');
break;
}
}
return recommendations;
}
/**
* Calculate quality metrics
*/
calculateQualityMetrics(files, findings) {
const totalLines = files.reduce((sum, file) => sum + (file.content ? file.content.split('\n').length : 0), 0);
const issuesPerLine = findings.length / totalLines;
const severityDistribution = findings.reduce((acc, finding) => {
acc[finding.severity] = (acc[finding.severity] || 0) + 1;
return acc;
}, {});
return {
total_lines: totalLines,
total_issues: findings.length,
issues_per_line: Math.round(issuesPerLine * 1000) / 1000,
severity_distribution: severityDistribution,
quality_score: Math.max(0, 100 - (findings.length * 2)),
};
}
/**
* Apply persona-specific insights
*/
applyPersonaInsights(findings, persona) {
const personaFindings = findings.filter(finding => {
switch (persona.name) {
case 'security':
return finding.type === 'security';
case 'performance':
return finding.type === 'performance';
case 'architect':
return finding.type === 'maintainability' || finding.type === 'best_practice';
default:
return true;
}
});
return {
persona_name: persona.name,
focus_area: persona.expertise.join(', '),
relevant_findings: personaFindings.length,
specialized_recommendations: this.getPersonaRecommendations(personaFindings, persona),
};
}
/**
* Get persona-specific recommendations
*/
getPersonaRecommendations(findings, persona) {
const recommendations = [];
switch (persona.name) {
case 'security':
recommendations.push('Implement security code review checklist');
recommendations.push('Add static security analysis tools');
if (findings.some(f => f.type === 'security')) {
recommendations.push('Prioritize fixing security vulnerabilities');
}
break;
case 'performance':
recommendations.push('Add performance monitoring');
recommendations.push('Implement performance testing');
if (findings.some(f => f.type === 'performance')) {
recommendations.push('Profile and optimize performance bottlenecks');
}
break;
case 'qa':
recommendations.push('Increase test coverage');
recommendations.push('Implement automated quality gates');
break;
default:
recommendations.push(`Apply ${persona.name} best practices`);
}
return recommendations;
}
}
//# sourceMappingURL=ReviewCodeTool.js.map