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
JavaScript
/**
* 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