Skip to main content
Glama
quality-agent.js9.53 kB
// QUALITY AGENT // Reviews code quality, creates summaries, suggests improvements // Provides inventory summaries for the orchestrator import { promises as fs } from 'fs'; import path from 'path'; import { actionLogger } from '../../logging/agent-action-logger.js'; export class QualityAgent { constructor() { this.name = 'QualityAgent'; this.role = 'Code review, quality assessment, and summarization'; } // Review file quality async review(filePath) { console.log(`🔍 ${this.name}: Reviewing ${path.basename(filePath)}...`); const sessionId = await actionLogger.startSession( this.name, `Review file: ${filePath}` ); try { const content = await fs.readFile(filePath, 'utf-8'); const review = { filePath: filePath, fileName: path.basename(filePath), timestamp: new Date().toISOString(), quality: {}, improvements: [], summary: '' }; // Check 1: Code structure review.quality.structure = this.assessStructure(content); // Check 2: Documentation review.quality.documentation = this.assessDocumentation(content); // Check 3: Error handling review.quality.errorHandling = this.assessErrorHandling(content); // Check 4: Dependencies review.quality.dependencies = this.assessDependencies(content); // Generate improvements review.improvements = this.generateImprovements(review.quality); // Generate summary review.summary = this.generateSummary(content, filePath); // Calculate overall score review.overallScore = this.calculateScore(review.quality); await actionLogger.endSession('completed'); console.log(` Score: ${review.overallScore}/100`); console.log(` Improvements: ${review.improvements.length}`); return review; } catch (error) { await actionLogger.endSession('failed'); console.error(` ❌ Review failed:`, error); throw error; } } // Assess code structure assessStructure(content) { const score = { score: 0, maxScore: 25, notes: [] }; // Has exports if (/export (class|const|function)/.test(content)) { score.score += 10; } else { score.notes.push('No exports found'); } // Has proper class structure if (/class \w+/.test(content)) { score.score += 10; } // Has constructor if (/constructor\s*\(/.test(content)) { score.score += 5; } return score; } // Assess documentation assessDocumentation(content) { const score = { score: 0, maxScore: 25, notes: [] }; // Has file-level comments if (/^\/\//.test(content)) { score.score += 10; } else { score.notes.push('Missing file-level comments'); } // Has function comments const functionCommentRatio = this.countFunctionComments(content); score.score += Math.min(15, Math.floor(functionCommentRatio * 15)); if (functionCommentRatio < 0.5) { score.notes.push(`Only ${Math.floor(functionCommentRatio * 100)}% of functions documented`); } return score; } // Assess error handling assessErrorHandling(content) { const score = { score: 0, maxScore: 25, notes: [] }; // Has try-catch blocks const tryCatchCount = (content.match(/try\s*{/g) || []).length; if (tryCatchCount > 0) { score.score += Math.min(15, tryCatchCount * 5); } else { score.notes.push('No error handling found'); } // Has error logging if (/console\.(error|warn)/.test(content)) { score.score += 10; } else { score.notes.push('No error logging'); } return score; } // Assess dependencies assessDependencies(content) { const score = { score: 0, maxScore: 25, notes: [] }; // Count imports const imports = (content.match(/import .* from/g) || []).length; if (imports === 0) { score.score += 25; // No dependencies is good for standalone files score.notes.push('No external dependencies'); } else if (imports <= 5) { score.score += 20; score.notes.push(`${imports} dependencies - good`); } else { score.score += 10; score.notes.push(`${imports} dependencies - consider reducing`); } return score; } // Count function documentation ratio countFunctionComments(content) { const functions = (content.match(/(?:async\s+)?(?:function\s+)?\w+\s*\([^)]*\)\s*{/g) || []).length; const comments = (content.match(/\/\/.*(?:function|method|param|return)/gi) || []).length; return functions > 0 ? comments / functions : 0; } // Generate improvement suggestions generateImprovements(quality) { const improvements = []; for (const [category, assessment] of Object.entries(quality)) { if (assessment.score < assessment.maxScore * 0.7) { improvements.push({ category: category, priority: 'medium', suggestions: assessment.notes }); } } return improvements; } // Generate file summary generateSummary(content, filePath) { const fileName = path.basename(filePath); const lines = content.split('\n').length; // Extract class name const classMatch = content.match(/export class (\w+)/); const className = classMatch ? classMatch[1] : null; // Extract main functions const functions = []; const funcMatches = content.matchAll(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*{/g); for (const match of funcMatches) { if (match[1] !== 'constructor') { functions.push(match[1]); } } // Build summary let summary = `File: ${fileName} (${lines} lines)\n`; if (className) { summary += `Class: ${className}\n`; } if (functions.length > 0) { summary += `Main functions: ${functions.slice(0, 5).join(', ')}`; if (functions.length > 5) { summary += ` (+${functions.length - 5} more)`; } } return summary; } // Calculate overall score calculateScore(quality) { let total = 0; let max = 0; for (const assessment of Object.values(quality)) { total += assessment.score; max += assessment.maxScore; } return Math.round((total / max) * 100); } // Create inventory summary for orchestrator async createInventorySummary(inventoryPath) { console.log(`📊 ${this.name}: Creating inventory summary...`); try { const data = await fs.readFile(inventoryPath, 'utf-8'); const inventory = JSON.parse(data); const summary = { totalAgents: inventory.totalAgents || 0, totalTools: inventory.totalTools || 0, agentsByCapability: {}, toolsByCategory: {}, reusableComponents: inventory.reusableComponents || [], recommendations: [] }; // Group agents by capability for (const agent of Object.values(inventory.agents || {})) { for (const capability of agent.capabilities || []) { if (!summary.agentsByCapability[capability]) { summary.agentsByCapability[capability] = []; } summary.agentsByCapability[capability].push(agent.name); } } // Generate recommendations if (summary.totalAgents < 5) { summary.recommendations.push('Consider creating more specialized agents'); } if (summary.reusableComponents.length === 0) { summary.recommendations.push('No reusable components identified yet'); } console.log(` ✅ Summary created`); console.log(` Agents: ${summary.totalAgents}`); console.log(` Tools: ${summary.totalTools}`); return summary; } catch (error) { console.error(` ❌ Summary failed:`, error); throw error; } } } export const qualityAgent = new QualityAgent();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bermingham85/mcp-puppet-pipeline'

If you have feedback or need assistance with the MCP directory API, please join our Discord server