Skip to main content
Glama
formatters.ts8.83 kB
import type { CodeQualityMetrics, DependencyReport, LintResult, ScrapedData, APIDiscoveryResult, AnalysisReport, } from '../types/index.js'; export class Formatters { /** * Format code quality metrics as markdown */ static formatCodeQualityMetrics(metrics: CodeQualityMetrics): string { let output = '# Code Quality Metrics\n\n'; output += `**Complexity:** ${metrics.complexity}\n`; output += `**Maintainability Index:** ${metrics.maintainabilityIndex}\n`; output += `**Cyclomatic Complexity:** ${metrics.cyclomaticComplexity ?? 'N/A'}\n`; output += `**Lines of Code:** ${metrics.linesOfCode}\n`; output += `**Technical Debt:** ${metrics.technicalDebt}\n\n`; if (metrics.codeSmells.length > 0) { output += '## Code Smells\n\n'; for (const smell of metrics.codeSmells) { output += `- **${smell.type}** (${smell.severity}): ${smell.description}\n`; output += ` - Location: ${smell.location}${smell.line ? `:${smell.line}` : ''}\n`; if (smell.suggestion) { output += ` - Suggestion: ${smell.suggestion}\n`; } } output += '\n'; } if (metrics.duplications.length > 0) { output += '## Code Duplications\n\n'; for (const dup of metrics.duplications) { output += `- ${dup.lines} lines duplicated between:\n`; output += ` - ${dup.firstFile} (${dup.startLine1}-${dup.endLine1})\n`; output += ` - ${dup.secondFile} (${dup.startLine2}-${dup.endLine2})\n`; } } return output; } /** * Format dependency report as markdown */ static formatDependencyReport(report: DependencyReport): string { let output = '# Dependency Analysis Report\n\n'; output += `**Total Dependencies:** ${report.totalDependencies}\n\n`; if (report.unused.length > 0) { output += '## Unused Dependencies\n\n'; for (const dep of report.unused) { output += `- ${dep}\n`; } output += '\n'; } if (report.outdated.length > 0) { output += '## Outdated Packages\n\n'; for (const pkg of report.outdated) { output += `- **${pkg.name}**: ${pkg.current} → ${pkg.latest} (wanted: ${pkg.wanted})\n`; output += ` - Location: ${pkg.location}\n`; } output += '\n'; } if (report.vulnerabilities.length > 0) { output += '## Security Vulnerabilities\n\n'; for (const vuln of report.vulnerabilities) { output += `- **${vuln.name}** (${vuln.severity}): ${vuln.title}\n`; output += ` - URL: ${vuln.url}\n`; if (vuln.dependencyOf) { output += ` - Dependency of: ${vuln.dependencyOf}\n`; } if (vuln.fixAvailable) { output += ` - Fix available: Yes\n`; } } output += '\n'; } if (report.bundleSize) { output += `## Bundle Size\n\n`; output += `**Estimated Bundle Size:** ${this.formatBytes(report.bundleSize)}\n`; } return output; } /** * Format lint results as markdown */ static formatLintResults(results: LintResult[]): string { let output = '# Linting Results\n\n'; const totalErrors = results.reduce((sum, r) => sum + r.errorCount, 0); const totalWarnings = results.reduce((sum, r) => sum + r.warningCount, 0); output += `**Total Errors:** ${totalErrors}\n`; output += `**Total Warnings:** ${totalWarnings}\n\n`; for (const result of results) { if (result.messages.length === 0) continue; output += `## ${result.file}\n\n`; output += `Errors: ${result.errorCount}, Warnings: ${result.warningCount}\n\n`; for (const message of result.messages) { const severity = message.severity === 2 ? '❌ Error' : message.severity === 1 ? '⚠️ Warning' : 'ℹ️ Info'; output += `- ${severity} (${message.line}:${message.column})`; if (message.ruleId) { output += ` [${message.ruleId}]`; } output += `: ${message.message}\n`; } output += '\n'; } return output; } /** * Format scraped data as markdown */ static formatScrapedData(data: ScrapedData): string { let output = `# Scraped Data: ${data.url}\n\n`; output += `**Scraped At:** ${data.scrapedAt.toISOString()}\n\n`; if (data.title) { output += `**Title:** ${data.title}\n\n`; } if (data.text) { output += `## Text Content\n\n${data.text.substring(0, 1000)}${data.text.length > 1000 ? '...' : ''}\n\n`; } if (data.links && data.links.length > 0) { output += `## Links (${data.links.length})\n\n`; for (const link of data.links.slice(0, 20)) { output += `- ${link}\n`; } if (data.links.length > 20) { output += `\n... and ${data.links.length - 20} more links\n`; } output += '\n'; } if (data.images && data.images.length > 0) { output += `## Images (${data.images.length})\n\n`; for (const img of data.images.slice(0, 10)) { output += `- ${img}\n`; } if (data.images.length > 10) { output += `\n... and ${data.images.length - 10} more images\n`; } output += '\n'; } if (data.tables && data.tables.length > 0) { output += `## Tables (${data.tables.length})\n\n`; for (const table of data.tables) { if (table.caption) { output += `### ${table.caption}\n\n`; } if (table.headers.length > 0) { output += '| ' + table.headers.join(' | ') + ' |\n'; output += '|' + table.headers.map(() => '---').join('|') + '|\n'; for (const row of table.rows.slice(0, 10)) { output += '| ' + row.join(' | ') + ' |\n'; } if (table.rows.length > 10) { output += `\n... and ${table.rows.length - 10} more rows\n`; } } output += '\n'; } } return output; } /** * Format API discovery results as markdown */ static formatAPIDiscoveryResults(result: APIDiscoveryResult): string { let output = '# API Discovery Results\n\n'; if (result.baseUrl) { output += `**Base URL:** ${result.baseUrl}\n\n`; } if (result.authentication) { output += `## Authentication\n\n`; output += `- Type: ${result.authentication.type}\n`; if (result.authentication.location) { output += `- Location: ${result.authentication.location}\n`; } if (result.authentication.name) { output += `- Name: ${result.authentication.name}\n`; } output += '\n'; } if (result.endpoints.length > 0) { output += `## Endpoints (${result.endpoints.length})\n\n`; for (const endpoint of result.endpoints) { output += `### ${endpoint.method} ${endpoint.path}\n\n`; output += `- **Full URL:** ${endpoint.fullUrl}\n`; if (endpoint.statusCode) { output += `- **Status:** ${endpoint.statusCode}\n`; } if (endpoint.parameters && endpoint.parameters.length > 0) { output += `- **Parameters:**\n`; for (const param of endpoint.parameters) { output += ` - ${param.name} (${param.type}, ${param.required ? 'required' : 'optional'}) - ${param.location}\n`; } } output += '\n'; } } return output; } /** * Format analysis report */ static formatAnalysisReport(report: AnalysisReport): string { let output = `# ${report.type.charAt(0).toUpperCase() + report.type.slice(1)} Analysis Report\n\n`; output += `**Generated At:** ${report.generatedAt.toISOString()}\n\n`; output += `## Summary\n\n`; output += `- Total Files: ${report.summary.totalFiles}\n`; output += `- Total Issues: ${report.summary.totalIssues}\n`; output += `- Errors: ${report.summary.errors}\n`; output += `- Warnings: ${report.summary.warnings}\n`; if (report.summary.score !== undefined) { output += `- Score: ${report.summary.score}/100\n`; } output += '\n'; // Format details based on type if (report.type === 'code_quality') { output += this.formatCodeQualityMetrics(report.details as CodeQualityMetrics); } else if (report.type === 'dependency') { output += this.formatDependencyReport(report.details as DependencyReport); } return output; } /** * Format bytes to human readable format */ static formatBytes(bytes: number): string { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; } /** * Format JSON with indentation */ static formatJSON(data: unknown): string { return JSON.stringify(data, null, 2); } }

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/code-alchemist01/development-tools-mcp-Server'

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