UNPKG

bc-code-intelligence-mcp

Version:

BC Code Intelligence MCP Server - Complete Specialist Bundle with AI-driven expert consultation, seamless handoffs, and context-preserving workflows

290 lines 8.94 kB
/** * Base Knowledge Layer * * Abstract base class providing common functionality for all knowledge layer implementations. * ALL layers support three content types: topics, specialists, and methodologies. */ export class BaseKnowledgeLayer { name; priority; enabled; // ALL layers support all three content types supported_content_types = ['topics', 'specialists', 'methodologies']; topics = new Map(); specialists = new Map(); methodologies = new Map(); indexes = new Map(); loadResult = null; initialized = false; constructor(name, priority, enabled = true) { this.name = name; this.priority = priority; this.enabled = enabled; } /** * Check if the layer has a specific topic */ hasTopic(topicId) { return this.topics.has(topicId); } /** * Get a topic from this layer */ async getTopic(topicId) { if (!this.initialized) { await this.initialize(); } return this.topics.get(topicId) || null; } /** * Get a topic from this layer synchronously (for already loaded topics) */ getTopicSync(topicId) { return this.topics.get(topicId) || null; } /** * Get all topic IDs available in this layer */ getTopicIds() { return Array.from(this.topics.keys()); } /** * Search for topics within this layer using simple text matching */ searchTopics(query, limit = 50) { const normalizedQuery = query.toLowerCase(); const results = []; for (const topic of this.topics.values()) { const searchableText = [ topic.frontmatter.title, topic.frontmatter.domain, topic.frontmatter.tags?.join(' ') || '', topic.content ].join(' ').toLowerCase(); if (searchableText.includes(normalizedQuery)) { results.push(topic); if (results.length >= limit) break; } } return results; } /** * Generic content access methods (for MultiContentLayerService compatibility) */ /** * Check if layer has specific content by type and ID */ hasContent(type, id) { switch (type) { case 'topics': return this.topics.has(id); case 'specialists': return this.specialists.has(id); case 'methodologies': return this.methodologies.has(id); default: return false; } } /** * Get content by type and ID */ async getContent(type, id) { if (!this.initialized) { await this.initialize(); } switch (type) { case 'topics': return this.topics.get(id) || null; case 'specialists': return this.specialists.get(id) || null; case 'methodologies': return this.methodologies.get(id) || null; default: return null; } } /** * Get all content IDs for a specific type */ getContentIds(type) { switch (type) { case 'topics': return Array.from(this.topics.keys()); case 'specialists': return Array.from(this.specialists.keys()); case 'methodologies': return Array.from(this.methodologies.keys()); default: return []; } } /** * Search content by type */ searchContent(type, query, limit = 10) { switch (type) { case 'topics': return this.searchTopics(query, limit); case 'specialists': return this.searchSpecialists(query, limit); case 'methodologies': return this.searchMethodologies(query, limit); default: return []; } } /** * Search specialists within this layer */ searchSpecialists(query, limit = 10) { const normalizedQuery = query.toLowerCase(); const results = []; for (const specialist of this.specialists.values()) { const searchableText = [ specialist.title, specialist.role, specialist.team, specialist.expertise.primary.join(' '), specialist.expertise.secondary.join(' '), specialist.domains.join(' ') ].join(' ').toLowerCase(); if (searchableText.includes(normalizedQuery)) { results.push(specialist); if (results.length >= limit) break; } } return results; } /** * Search methodologies within this layer */ searchMethodologies(query, limit = 10) { // TODO: Implement when methodology structure is defined return []; } /** * Get layer statistics */ getStatistics() { const topicsMemory = this.estimateTopicsMemoryUsage(); const indexesMemory = this.estimateIndexesMemoryUsage(); return { name: this.name, priority: this.priority, enabled: this.enabled, topicCount: this.topics.size, indexCount: this.indexes.size, lastLoaded: this.loadResult?.success ? new Date() : undefined, loadTimeMs: this.loadResult?.loadTimeMs, memoryUsage: { topics: topicsMemory, indexes: indexesMemory, total: topicsMemory + indexesMemory } }; } /** * Cleanup resources */ async dispose() { this.topics.clear(); this.indexes.clear(); this.initialized = false; this.loadResult = null; } /** * Helper to create successful load result */ createLoadResult(topicsLoaded, indexesLoaded, loadTimeMs) { return { layerName: this.name, success: true, topicsLoaded, indexesLoaded, loadTimeMs }; } /** * Helper to create error load result */ createErrorResult(error, loadTimeMs) { return { layerName: this.name, success: false, topicsLoaded: 0, indexesLoaded: 0, error, loadTimeMs }; } /** * Estimate memory usage of loaded topics */ estimateTopicsMemoryUsage() { let totalBytes = 0; for (const topic of this.topics.values()) { // Rough estimation of memory usage totalBytes += JSON.stringify(topic).length * 2; // UTF-16 character size } return totalBytes; } /** * Estimate memory usage of loaded indexes */ estimateIndexesMemoryUsage() { let totalBytes = 0; for (const index of this.indexes.values()) { // Rough estimation of memory usage totalBytes += JSON.stringify(index).length * 2; // UTF-16 character size } return totalBytes; } /** * Validate that a topic has required structure */ validateTopic(topic) { return !!(topic.id && topic.frontmatter?.title && topic.frontmatter?.domain && topic.content); } /** * Normalize topic ID for consistent lookup */ normalizeTopicId(filePath, basePath) { // Normalize both paths to handle different formats const normalizedFilePath = filePath.replace(/\\/g, '/'); const normalizedBasePath = basePath.replace(/\\/g, '/'); // Remove base path to get relative path let relativePath = normalizedFilePath; if (normalizedFilePath.startsWith(normalizedBasePath)) { relativePath = normalizedFilePath.substring(normalizedBasePath.length); } // Clean up the relative path to create a clean ID return relativePath .replace(/^[/\\]+/, '') // Remove leading slashes .replace(/\.md$/, '') // Remove .md extension .replace(/^domains\//, '') // Remove domains/ prefix if present .replace(/[\\]/g, '/'); // Normalize to forward slashes } /** * Get layer statistics with content type breakdown */ getEnhancedStatistics() { return { name: this.name, priority: this.priority, content_counts: { topics: this.topics.size, specialists: this.specialists.size, methodologies: this.methodologies.size }, load_time_ms: this.loadResult?.loadTimeMs, initialized: this.initialized }; } } //# sourceMappingURL=base-layer.js.map