UNPKG

code-auditor-mcp

Version:

Multi-language code quality auditor with MCP server - Analyze TypeScript, JavaScript, and Go code for SOLID principles, DRY violations, security patterns, and more

488 lines 17.7 kB
/** * Enhanced Code Index Database for Cross-Language Support * Extends the existing CodeIndexDB with multi-language capabilities */ import * as FlexSearch from 'flexsearch'; import { CodeIndexDB } from './codeIndexDB.js'; /** * Enhanced CodeIndexDB with cross-language support * Maintains backward compatibility while adding new capabilities */ export class EnhancedCodeIndexDB extends CodeIndexDB { // New collections for cross-language support crossLanguageEntitiesCollection = null; crossReferencesCollection = null; apiContractsCollection = null; // Enhanced search index for cross-language entities crossLanguageSearchIndex; migrationCompleted = false; constructor(dbPath = ':memory:') { super(dbPath); // Initialize enhanced FlexSearch for cross-language entities const { Document } = FlexSearch; this.crossLanguageSearchIndex = new Document({ document: { id: '$loki', index: [ { field: 'name', tokenize: 'full', optimize: true, resolution: 9, bidirectional: true, weight: 10 }, { field: 'signature', tokenize: 'full', optimize: true, resolution: 9, weight: 9 }, { field: 'purpose', tokenize: 'full', optimize: true, resolution: 9, weight: 8 }, { field: 'context', tokenize: 'full', optimize: true, resolution: 9, weight: 7 }, { field: 'language', tokenize: 'full', optimize: true, resolution: 9, weight: 6 }, { field: 'type', tokenize: 'full', optimize: true, resolution: 9, weight: 6 }, { field: 'searchTokens', tokenize: 'full', optimize: true, resolution: 9, weight: 5 }, { field: 'file', tokenize: 'full', optimize: true, resolution: 9, weight: 3 } ] } }); } /** * Initialize enhanced collections */ async initializeEnhanced() { await this.initialize(); // Initialize base collections first // Initialize cross-language collections this.crossLanguageEntitiesCollection = this.getDatabase().addCollection('crossLanguageEntities', { indices: ['id', 'name', 'language', 'type', 'file'], unique: ['id'] }); this.crossReferencesCollection = this.getDatabase().addCollection('crossReferences', { indices: ['sourceId', 'targetId', 'sourceLanguage', 'targetLanguage', 'type'] }); this.apiContractsCollection = this.getDatabase().addCollection('apiContracts', { indices: ['entityId', 'version'] }); console.log('[EnhancedCodeIndexDB] Cross-language collections initialized'); } /** * Migrate existing data to cross-language format */ async migrateToEnhanced(options = { preserveExisting: true, enhanceMetadata: true, buildReferences: false, validateContracts: false }) { if (this.migrationCompleted) { console.log('[EnhancedCodeIndexDB] Migration already completed'); return; } console.log('[EnhancedCodeIndexDB] Starting migration to enhanced schema...'); if (!this.crossLanguageEntitiesCollection) { await this.initializeEnhanced(); } const adapter = new DefaultCompatibilityAdapter(); // Migrate existing functions const existingFunctions = await this.getAllFunctions(); for (const func of existingFunctions) { const entity = adapter.convertFunction(func); await this.addCrossLanguageEntity(entity); } // Migrate existing components (if any) // Note: This assumes components are stored somehow in the base class // Implementation would depend on how components are currently stored console.log(`[EnhancedCodeIndexDB] Migrated ${existingFunctions.length} functions to cross-language format`); this.migrationCompleted = true; } /** * Add a cross-language entity to the index */ async addCrossLanguageEntity(entity) { if (!this.crossLanguageEntitiesCollection) { await this.initializeEnhanced(); } // Ensure required fields const enhancedEntity = { ...entity, id: entity.id || this.generateEntityId(entity), searchTokens: entity.searchTokens || this.generateSearchTokens(entity), createdAt: new Date(), updatedAt: new Date() }; // Check for existing entity const existing = this.crossLanguageEntitiesCollection.findOne({ id: enhancedEntity.id }); if (existing) { // Update existing Object.assign(existing, enhancedEntity); existing.updatedAt = new Date(); this.crossLanguageEntitiesCollection.update(existing); } else { // Add new this.crossLanguageEntitiesCollection.insert(enhancedEntity); } // Update search index this.crossLanguageSearchIndex.add(enhancedEntity); } /** * Add cross-reference between entities */ async addCrossReference(reference) { if (!this.crossReferencesCollection) { await this.initializeEnhanced(); } const refDoc = { ...reference, createdAt: new Date(), updatedAt: new Date() }; this.crossReferencesCollection.insert(refDoc); } /** * Add API contract for an entity */ async addAPIContract(entityId, contract) { if (!this.apiContractsCollection) { await this.initializeEnhanced(); } const contractDoc = { ...contract, entityId, createdAt: new Date(), updatedAt: new Date() }; // Remove existing contract for this entity this.apiContractsCollection.removeWhere({ entityId }); // Add new contract this.apiContractsCollection.insert(contractDoc); } /** * Search cross-language entities */ async searchCrossLanguage(query, options = {}) { if (!this.crossLanguageEntitiesCollection) { await this.initializeEnhanced(); } // Perform FlexSearch const searchResults = this.crossLanguageSearchIndex.search(query, { limit: 50, enrich: true }); const results = []; for (const result of searchResults) { for (const doc of result.result) { const entity = this.crossLanguageEntitiesCollection.get(doc.id); if (!entity) continue; // Apply filters if (options.languages && !options.languages.includes(entity.language)) continue; if (options.types && !options.types.includes(entity.type)) continue; const searchResult = { entity, score: 1.0, // FlexSearch doesn't provide scores directly matches: [result.field] // Field that matched }; // Include references if requested if (options.includeReferences) { searchResult.references = this.crossReferencesCollection.find({ $or: [ { sourceId: entity.id }, { targetId: entity.id } ] }); } // Include contracts if requested if (options.includeContracts) { searchResult.contracts = this.apiContractsCollection.find({ entityId: entity.id }); } results.push(searchResult); } } return results; } /** * Find entity by ID */ async findEntityById(id) { if (!this.crossLanguageEntitiesCollection) { await this.initializeEnhanced(); } return this.crossLanguageEntitiesCollection.findOne({ id }) || null; } /** * Find entities by language */ async findEntitiesByLanguage(language) { if (!this.crossLanguageEntitiesCollection) { await this.initializeEnhanced(); } return this.crossLanguageEntitiesCollection.find({ language }); } /** * Find entities by type */ async findEntitiesByType(type) { if (!this.crossLanguageEntitiesCollection) { await this.initializeEnhanced(); } return this.crossLanguageEntitiesCollection.find({ type }); } /** * Get cross-references for an entity */ async getCrossReferences(entityId) { if (!this.crossReferencesCollection) { await this.initializeEnhanced(); } return this.crossReferencesCollection.find({ $or: [ { sourceId: entityId }, { targetId: entityId } ] }); } /** * Generate dependency graph */ async generateDependencyGraph(languages) { if (!this.crossLanguageEntitiesCollection || !this.crossReferencesCollection) { await this.initializeEnhanced(); } const entities = languages ? this.crossLanguageEntitiesCollection.find({ language: { $in: languages } }) : this.crossLanguageEntitiesCollection.find(); const references = this.crossReferencesCollection.find(); const nodes = entities.map(entity => ({ id: entity.id, name: entity.name, language: entity.language, type: entity.type, file: entity.file, weight: entity.complexity || 1 })); const edges = references.map(ref => ({ from: ref.sourceId, to: ref.targetId, type: ref.type, weight: ref.confidence, protocol: ref.protocol })); // TODO: Implement cycle detection const cycles = []; return { nodes, edges, cycles, metrics: { totalNodes: nodes.length, totalEdges: edges.length, cycleCount: cycles.length, averageDepth: 0, // TODO: Calculate maxDepth: 0, // TODO: Calculate stronglyConnectedComponents: 0 // TODO: Calculate } }; } /** * Get analysis summary */ async getAnalysisSummary() { if (!this.crossLanguageEntitiesCollection || !this.crossReferencesCollection) { await this.initializeEnhanced(); } const entities = this.crossLanguageEntitiesCollection.find(); const references = this.crossReferencesCollection.find(); const contracts = this.apiContractsCollection.find(); const entitiesByLanguage = {}; const entitiesByType = {}; const complexities = {}; for (const entity of entities) { entitiesByLanguage[entity.language] = (entitiesByLanguage[entity.language] || 0) + 1; entitiesByType[entity.type] = (entitiesByType[entity.type] || 0) + 1; if (entity.complexity) { if (!complexities[entity.language]) complexities[entity.language] = []; complexities[entity.language].push(entity.complexity); } } // Calculate average complexities const avgComplexities = {}; for (const [lang, values] of Object.entries(complexities)) { avgComplexities[lang] = values.reduce((a, b) => a + b, 0) / values.length; } // Find orphaned entities (no references) const referencedIds = new Set([ ...references.map(r => r.sourceId), ...references.map(r => r.targetId) ]); const orphanedEntities = entities .filter(e => !referencedIds.has(e.id)) .map(e => e.id); return { totalEntities: entities.length, entitiesByLanguage, entitiesByType, crossReferences: references.length, apiContracts: contracts.length, orphanedEntities, complexities: avgComplexities, coverage: {} // TODO: Calculate test coverage }; } /** * Generate entity ID */ generateEntityId(entity) { return `${entity.language}:${entity.type}:${entity.file}:${entity.name}:${entity.startLine || 0}`; } /** * Generate search tokens for an entity */ generateSearchTokens(entity) { const tokens = new Set(); // Add name variations tokens.add(entity.name); tokens.add(entity.name.toLowerCase()); // Add camelCase breakdown const camelCaseTokens = entity.name.replace(/([A-Z])/g, ' $1').trim().split(' '); camelCaseTokens.forEach(token => tokens.add(token.toLowerCase())); // Add snake_case breakdown const snakeCaseTokens = entity.name.split('_'); snakeCaseTokens.forEach(token => tokens.add(token.toLowerCase())); // Add type and language tokens.add(entity.type); tokens.add(entity.language); // Add purpose keywords const purposeWords = entity.purpose.split(/\s+/); purposeWords.forEach(word => { if (word.length > 2) tokens.add(word.toLowerCase()); }); return Array.from(tokens); } /** * Get access to the underlying database for advanced operations */ getDatabase() { // Protected method to access the underlying database return this.db; } } /** * Default compatibility adapter for migrating existing data */ class DefaultCompatibilityAdapter { convertFunction(func) { return { id: this.generateId('function', func.filePath, func.name, func.lineNumber), name: func.name, language: func.language || 'typescript', file: func.filePath, type: 'function', startLine: func.startLine, endLine: func.endLine, lineNumber: func.lineNumber, signature: func.name, // Basic signature, could be enhanced parameters: [], // Would need to be extracted from enhanced metadata purpose: func.purpose, context: func.context, searchTokens: [], calls: [], calledBy: [], implementedBy: [], extendedBy: [], metadata: func.metadata }; } convertComponent(comp) { return { id: this.generateId('component', comp.filePath, comp.name, comp.lineNumber), name: comp.name, language: comp.language || 'typescript', file: comp.filePath, type: 'component', startLine: comp.startLine, endLine: comp.endLine, lineNumber: comp.lineNumber, signature: comp.name, parameters: comp.props?.map(prop => ({ name: prop.name, type: prop.type, optional: !prop.required, language: 'typescript' })) || [], purpose: comp.purpose, context: comp.context, searchTokens: [], calls: [], calledBy: [], implementedBy: [], extendedBy: [], complexity: comp.complexity, metadata: comp.metadata }; } toFunctionMetadata(entity) { return { name: entity.name, filePath: entity.file, lineNumber: entity.lineNumber, startLine: entity.startLine, endLine: entity.endLine, language: entity.language, dependencies: [], // Would need to be extracted from references purpose: entity.purpose, context: entity.context, metadata: entity.metadata }; } toComponentMetadata(entity) { // This would need more implementation based on the ComponentMetadata interface throw new Error('Component metadata conversion not implemented'); } generateId(type, filePath, name, line) { return `${type}:${filePath}:${name}:${line || 0}`; } } //# sourceMappingURL=codeIndexDB-enhanced.js.map