UNPKG

@opichi/smartcode

Version:

Universal code intelligence MCP server - analyze any codebase with TypeScript excellence and multi-language support

309 lines (298 loc) 13.9 kB
import { z } from "zod"; import { CodebaseIndexer } from '../indexer/index.js'; import { CodeKnowledgeGraph } from '../knowledge/graph.js'; import { CodeAnalyzer } from '../knowledge/analyzer.js'; import { CodeVectorStore } from '../indexer/qdrant.js'; import { CodeEmbedder } from '../indexer/embedder.js'; export class SemanticTools { indexer; graph; analyzer; vectorStore; embedder; isInitialized = false; constructor() { this.indexer = new CodebaseIndexer(); this.graph = new CodeKnowledgeGraph(); this.analyzer = new CodeAnalyzer(this.graph); this.vectorStore = new CodeVectorStore(); this.embedder = new CodeEmbedder(); } async initialize() { if (this.isInitialized) return; console.log('Initializing semantic tools...'); await Promise.all([ this.indexer.initialize(), this.embedder.initialize() ]); this.isInitialized = true; console.log('Semantic tools initialized'); } registerTools(server) { // Semantic search tool server.tool("find_similar_code", "Find code similar to a description using semantic search", { description: z.string().describe("Description of the code you're looking for"), limit: z.number().optional().default(10).describe("Maximum number of results"), node_type: z.enum(['function', 'class', 'variable', 'any']).optional().default('any').describe("Type of code nodes to search") }, async ({ description, limit, node_type }) => { try { await this.ensureIndexed(); // Generate embedding for the query const queryEmbedding = await this.embedder.embedQuery(description); // Search in vector store const filter = node_type !== 'any' ? { must: [{ key: 'type', match: { value: node_type } }] } : undefined; const results = await this.vectorStore.searchSimilar(queryEmbedding, limit, filter); if (results.length === 0) { return { content: [{ type: "text", text: `No similar code found for: "${description}"` }] }; } const output = results.map(result => `**${result.node.name}** (${result.node.type}) - Score: ${result.score.toFixed(3)} File: ${result.node.filePath}:${result.node.startLine} Context: ${result.context} \`\`\` ${result.node.content.split('\n').slice(0, 5).join('\n')}${result.node.content.split('\n').length > 5 ? '\n...' : ''} \`\`\``).join('\n\n---\n\n'); return { content: [{ type: "text", text: `Found ${results.length} similar code snippets for "${description}":\n\n${output}` }] }; } catch (error) { return { content: [{ type: "text", text: `Error searching for similar code: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } }); // Impact analysis tool server.tool("analyze_impact", "Analyze the impact of changing a specific code component", { file_path: z.string().describe("Path to the file containing the code"), node_name: z.string().describe("Name of the function, class, or variable to analyze"), change_description: z.string().optional().describe("Description of the planned change") }, async ({ file_path, node_name, change_description }) => { try { await this.ensureIndexed(); // Find the node const nodes = await this.vectorStore.searchByFile(file_path); const targetNode = nodes.find(n => n.name === node_name); if (!targetNode) { return { content: [{ type: "text", text: `Node "${node_name}" not found in ${file_path}` }] }; } // Perform impact analysis const impact = this.analyzer.analyzeImpact(targetNode.id); const output = `# Impact Analysis for ${node_name} **Risk Level:** ${impact.riskLevel.toUpperCase()} ## Directly Affected (${impact.directlyAffected.length} components): ${impact.directlyAffected.map(node => `- ${node.name} (${node.type}) in ${node.filePath}:${node.startLine}`).join('\n')} ## Indirectly Affected (${impact.indirectlyAffected.length} components): ${impact.indirectlyAffected.slice(0, 10).map(node => `- ${node.name} (${node.type}) in ${node.filePath}:${node.startLine}`).join('\n')}${impact.indirectlyAffected.length > 10 ? `\n... and ${impact.indirectlyAffected.length - 10} more` : ''} ## Recommendations: ${impact.suggestions.map(suggestion => `- ${suggestion}`).join('\n')} ${change_description ? `\n## Planned Change: ${change_description}` : ''}`; return { content: [{ type: "text", text: output }] }; } catch (error) { return { content: [{ type: "text", text: `Error analyzing impact: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } }); // Implementation suggestion tool server.tool("suggest_implementation", "Get implementation suggestions based on existing code patterns", { feature_description: z.string().describe("Description of the feature to implement"), context_files: z.array(z.string()).optional().describe("Related files for context") }, async ({ feature_description, context_files }) => { try { await this.ensureIndexed(); // Get context nodes if files provided let contextNodes = []; if (context_files) { for (const filePath of context_files) { const fileNodes = await this.vectorStore.searchByFile(filePath); contextNodes.push(...fileNodes); } } // Get implementation suggestions const suggestions = this.analyzer.suggestImplementation(feature_description, contextNodes); if (suggestions.length === 0) { return { content: [{ type: "text", text: `No implementation suggestions found for: "${feature_description}"` }] }; } const output = `# Implementation Suggestions for "${feature_description}" ${suggestions.map((suggestion, index) => ` ## ${index + 1}. ${suggestion.description} **Type:** ${suggestion.type} **Confidence:** ${(suggestion.confidence * 100).toFixed(1)}% **Reasoning:** ${suggestion.reasoning} ${suggestion.code ? `\`\`\` ${suggestion.code} \`\`\`` : ''} ${suggestion.filePath ? `**Reference:** ${suggestion.filePath}` : ''} `).join('\n---\n')}`; return { content: [{ type: "text", text: output }] }; } catch (error) { return { content: [{ type: "text", text: `Error generating suggestions: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } }); // Architecture overview tool server.tool("get_component_architecture", "Get a comprehensive overview of the codebase architecture and patterns", { focus: z.enum(['overview', 'patterns', 'components', 'statistics']).optional().default('overview').describe("What aspect to focus on") }, async ({ focus }) => { try { await this.ensureIndexed(); const overview = this.analyzer.getArchitecturalOverview(); let output = ''; if (focus === 'overview' || focus === 'components') { output += '# Architectural Components\n\n'; for (const [componentType, nodes] of overview.components.entries()) { if (nodes.length > 0) { output += `## ${componentType.charAt(0).toUpperCase() + componentType.slice(1)} (${nodes.length})\n`; nodes.slice(0, 10).forEach(node => { output += `- ${node.name} in ${node.filePath}:${node.startLine}\n`; }); if (nodes.length > 10) { output += `... and ${nodes.length - 10} more\n`; } output += '\n'; } } } if (focus === 'overview' || focus === 'patterns') { output += '# Detected Patterns\n\n'; if (overview.patterns.length === 0) { output += 'No architectural patterns detected.\n\n'; } else { overview.patterns.forEach(pattern => { output += `## ${pattern.name} **Type:** ${pattern.type} **Confidence:** ${(pattern.confidence * 100).toFixed(1)}% **Description:** ${pattern.description} **Components:** ${pattern.components.length} `; }); } } if (focus === 'overview' || focus === 'statistics') { output += '# Statistics\n\n'; const stats = overview.statistics; output += `- **Total Nodes:** ${stats.totalNodes} - **Functions:** ${stats.nodesByType.functions} - **Classes:** ${stats.nodesByType.classes} - **Variables:** ${stats.nodesByType.variables} - **Patterns Detected:** ${stats.patternsDetected} - **Average Complexity:** ${stats.averageComplexity.toFixed(1)} `; } return { content: [{ type: "text", text: output }] }; } catch (error) { return { content: [{ type: "text", text: `Error getting architecture overview: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } }); // Index project tool server.tool("index_project", "Index the current project for semantic analysis (run this first)", { project_path: z.string().optional().default('.').describe("Path to the project to index"), watch: z.boolean().optional().default(false).describe("Start watching for file changes") }, async ({ project_path, watch }) => { try { await this.initialize(); console.log(`Starting to index project: ${project_path}`); await this.indexer.indexProject(project_path); // Build knowledge graph const nodes = await this.vectorStore.searchByType('function'); const classes = await this.vectorStore.searchByType('class'); const variables = await this.vectorStore.searchByType('variable'); this.graph.addNodes([...nodes, ...classes, ...variables]); this.graph.buildRelationships(); // Analyze patterns await this.analyzer.analyzeCodebase(); if (watch) { await this.indexer.startWatching(project_path); } return { content: [{ type: "text", text: `✅ Successfully indexed project at ${project_path} - Parsed ${nodes.length + classes.length + variables.length} code nodes - Built knowledge graph with relationships - Detected architectural patterns ${watch ? '- Started file watching for real-time updates' : ''} The semantic brain is now ready! Try: - find_similar_code: Search for code by description - analyze_impact: See what breaks when you change something - suggest_implementation: Get implementation ideas - get_component_architecture: Understand your system` }] }; } catch (error) { return { content: [{ type: "text", text: `Error indexing project: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } }); } async ensureIndexed() { if (!this.isInitialized) { throw new Error('Semantic tools not initialized. Run index_project first.'); } // Check if we have any data const testNodes = await this.vectorStore.searchByType('function'); if (testNodes.length === 0) { throw new Error('No indexed data found. Run index_project first.'); } } } //# sourceMappingURL=semantic.js.map