Skip to main content
Glama

Hi-AI

by su-record
analyzeComplexity.ts9.18 kB
// Convention management tool - completely independent interface ToolResult { content: Array<{ type: 'text'; text: string; }>; } interface ToolDefinition { name: string; description: string; inputSchema: { type: 'object'; properties: Record<string, any>; required: string[]; }; } import { Project, ScriptKind } from "ts-morph"; // Reusable in-memory project to avoid re-parsing standard lib every call const AST_PROJECT = new Project({ useInMemoryFileSystem: true, compilerOptions: { allowJs: true, skipLibCheck: true } }); // Enhanced Software Engineering Metrics const CODE_QUALITY_METRICS = { COMPLEXITY: { maxCyclomaticComplexity: 10, maxCognitiveComplexity: 15, maxFunctionLines: 20, maxNestingDepth: 3, maxParameters: 5 }, COUPLING: { maxDependencies: 7, maxFanOut: 5, preventCircularDeps: true }, COHESION: { singleResponsibility: true, relatedFunctionsOnly: true }, MAINTAINABILITY: { noMagicNumbers: true, consistentNaming: true, properErrorHandling: true, typesSafety: true }, PERFORMANCE: { memoizeExpensiveCalc: true, lazyLoading: true, batchOperations: true } }; export const analyzeComplexityDefinition: ToolDefinition = { name: 'analyze_complexity', description: 'IMPORTANT: This tool should be automatically called when users mention "복잡도", "복잡한지", "complexity", "how complex", "is it complex", "difficulty level" or similar keywords. Analyze code complexity', inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'Code to analyze' }, metrics: { type: 'string', description: 'Metrics to calculate', enum: ['cyclomatic', 'cognitive', 'halstead', 'all'] } }, required: ['code'] } }; export async function analyzeComplexity(args: { code: string; metrics?: string }): Promise<ToolResult> { const { code: complexityCode, metrics: complexityMetrics = 'all' } = args; const complexityAnalysis = { action: 'analyze_complexity', metrics: complexityMetrics, results: {} as any, overallScore: 0, issues: [] as string[], recommendations: [] as string[], status: 'pending' as string }; // AST 기반 cyclomatic complexity 분석 let astCyclomatic = 1; try { const sourceFile = AST_PROJECT.createSourceFile('temp.ts', complexityCode, { overwrite: true, scriptKind: ScriptKind.TS }); sourceFile.forEachDescendant((node) => { const kind = node.getKindName(); if ( kind === 'IfStatement' || kind === 'ForStatement' || kind === 'ForOfStatement' || kind === 'ForInStatement' || kind === 'WhileStatement' || kind === 'CaseClause' || kind === 'ConditionalExpression' || kind === 'DoStatement' || kind === 'CatchClause' || kind === 'BinaryExpression' // &&, || ) { astCyclomatic++; } }); complexityAnalysis.results.astCyclomaticComplexity = { value: astCyclomatic, threshold: CODE_QUALITY_METRICS.COMPLEXITY.maxCyclomaticComplexity, status: astCyclomatic <= CODE_QUALITY_METRICS.COMPLEXITY.maxCyclomaticComplexity ? 'pass' : 'fail', description: 'AST 기반 분기/조건문 수를 통한 cyclomatic complexity' }; } catch (e) { complexityAnalysis.results.astCyclomaticComplexity = { value: null, status: 'error', description: 'AST 분석 실패: ' + (e instanceof Error ? e.message : String(e)) }; } if (complexityMetrics === 'cyclomatic' || complexityMetrics === 'all') { const cyclomaticComplexityScore = (complexityCode.match(/\bif\b|\bfor\b|\bwhile\b|\bcase\b|\b&&\b|\b\|\|\b/g) || []).length + 1; complexityAnalysis.results.cyclomaticComplexity = { value: cyclomaticComplexityScore, threshold: CODE_QUALITY_METRICS.COMPLEXITY.maxCyclomaticComplexity, status: cyclomaticComplexityScore <= CODE_QUALITY_METRICS.COMPLEXITY.maxCyclomaticComplexity ? 'pass' : 'fail', description: 'Number of linearly independent paths through the code' }; } if (complexityMetrics === 'cognitive' || complexityMetrics === 'all') { // Cognitive complexity calculation (simplified version) let cognitiveComplexityScore = 0; const lines = complexityCode.split('\n'); let nestingLevel = 0; for (const line of lines) { const trimmedLine = line.trim(); // Increment nesting for control structures if (trimmedLine.includes('if') || trimmedLine.includes('for') || trimmedLine.includes('while')) { cognitiveComplexityScore += 1 + nestingLevel; } // Increment for catch blocks if (trimmedLine.includes('catch')) { cognitiveComplexityScore += 1 + nestingLevel; } // Increment for switch statements if (trimmedLine.includes('switch')) { cognitiveComplexityScore += 1 + nestingLevel; } // Update nesting level const braceCount = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length; nestingLevel = Math.max(0, nestingLevel + braceCount); } complexityAnalysis.results.cognitiveComplexity = { value: cognitiveComplexityScore, threshold: CODE_QUALITY_METRICS.COMPLEXITY.maxCognitiveComplexity, status: cognitiveComplexityScore <= CODE_QUALITY_METRICS.COMPLEXITY.maxCognitiveComplexity ? 'pass' : 'fail', description: 'How difficult the code is to understand' }; } if (complexityMetrics === 'halstead' || complexityMetrics === 'all') { // Halstead metrics calculation (simplified version) const operators = (complexityCode.match(/[+\-*/=<>!&|%^~?:]/g) || []).length; const operands = (complexityCode.match(/\b[a-zA-Z_]\w*\b/g) || []).length; const uniqueOperators = new Set(complexityCode.match(/[+\-*/=<>!&|%^~?:]/g) || []).size; const uniqueOperands = new Set(complexityCode.match(/\b[a-zA-Z_]\w*\b/g) || []).size; const vocabulary = uniqueOperators + uniqueOperands; const length = operators + operands; const calculatedLength = vocabulary > 0 ? uniqueOperators * Math.log2(uniqueOperators) + uniqueOperands * Math.log2(uniqueOperands) : 0; const volume = length * Math.log2(vocabulary); const difficulty = vocabulary > 0 ? (uniqueOperators / 2) * (operands / uniqueOperands) : 0; const effort = difficulty * volume; complexityAnalysis.results.halsteadMetrics = { vocabulary: vocabulary, length: length, calculatedLength: Math.round(calculatedLength), volume: Math.round(volume), difficulty: Math.round(difficulty * 100) / 100, effort: Math.round(effort), timeToProgram: Math.round(effort / 18), // Halstead's formula: effort / 18 seconds bugsDelivered: Math.round(volume / 3000 * 100) / 100, // Halstead's formula: volume / 3000 description: 'Software science metrics measuring program complexity' }; } // Additional complexity metrics if (complexityMetrics === 'all') { const lines = complexityCode.split('\n'); const nonEmptyLines = lines.filter(line => line.trim().length > 0).length; const comments = (complexityCode.match(/\/\*[\s\S]*?\*\/|\/\/.*$/gm) || []).length; const functions = (complexityCode.match(/function\s+\w+|\w+\s*=\s*\(/g) || []).length; const classes = (complexityCode.match(/class\s+\w+/g) || []).length; complexityAnalysis.results.additionalMetrics = { linesOfCode: nonEmptyLines, comments: comments, commentRatio: nonEmptyLines > 0 ? Math.round((comments / nonEmptyLines) * 100) / 100 : 0, functions: functions, classes: classes, averageFunctionLength: functions > 0 ? Math.round(nonEmptyLines / functions) : 0 }; } // Overall assessment const issues = []; let overallScore = 100; if (complexityAnalysis.results.cyclomaticComplexity && complexityAnalysis.results.cyclomaticComplexity.status === 'fail') { issues.push('High cyclomatic complexity detected'); overallScore -= 20; } if (complexityAnalysis.results.cognitiveComplexity && complexityAnalysis.results.cognitiveComplexity.status === 'fail') { issues.push('High cognitive complexity detected'); overallScore -= 25; } if (complexityAnalysis.results.halsteadMetrics && complexityAnalysis.results.halsteadMetrics.difficulty > 10) { issues.push('High Halstead difficulty detected'); overallScore -= 15; } complexityAnalysis.overallScore = Math.max(0, overallScore); complexityAnalysis.issues = issues; complexityAnalysis.recommendations = issues.length > 0 ? [ 'Consider breaking down complex functions into smaller ones', 'Reduce nesting depth using early returns or guard clauses', 'Extract complex logic into separate functions', 'Use more descriptive variable names', 'Add comments for complex logic' ] : ['Code complexity is within acceptable ranges']; complexityAnalysis.status = 'success'; return { content: [{ type: 'text', text: `Complexity Analysis:\n${JSON.stringify(complexityAnalysis, null, 2)}` }] }; }

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/su-record/hi-ai'

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