mongodb-memory-bank-mcp
Version:
FIXED: MongoDB Memory Bank MCP with bulletproof error handling, smart operations, and session state management. Eliminates [object Object] errors and user confusion.
922 lines (921 loc) ⢠47.7 kB
JavaScript
import { ClineMemoryStructure, CLINE_CORE_FILES } from "../../../shared/services/cline-memory-structure.js";
import { validateProjectContext } from "../../../shared/services/project-context-manager.js";
import { MCPErrorHandler } from "../../../shared/errors/mcp-error.js";
import { MemoryBankSession } from "../../../shared/state/session-state.js";
export class MemoryStore {
memoryRepository;
projectRepository;
constructor(memoryRepository, projectRepository) {
this.memoryRepository = memoryRepository;
this.projectRepository = projectRepository;
}
async store(params) {
const session = MemoryBankSession.getInstance();
const startTime = new Date();
try {
const { projectName, fileName, content, tags = [] } = params;
console.log(`[MEMORY-STORE] š HYBRID SEARCH FIRST: Starting with search for project: ${projectName}`);
// š PROJECT VALIDATION: Ensure we're working on the correct project
const isValidProject = await validateProjectContext(projectName);
if (!isValidProject) {
throw new Error(`Project context validation failed for: ${projectName}`);
}
// Update session state
session.setActiveProject(projectName);
// Ensure project exists
await this.projectRepository.ensureProject(projectName);
// šØ WRITE-ONLY VALIDATION: Fail if file already exists (following their pattern)
const existingMemory = await this.memoryRepository.findByFileName(projectName, fileName);
if (existingMemory) {
// For ALL files, fail if exists (use update instead) - this is WRITE-ONLY behavior
throw new Error(`File ${fileName} already exists in project ${projectName}. Use memory_bank_update to modify existing files.`);
}
// š STEP 1: HYBRID SEARCH FIRST - Find ALL related memories
console.log(`[MEMORY-STORE] š Hybrid search for existing memories related to: ${fileName}`);
const searchResults = await this.memoryRepository.search({
query: `${fileName} ${content.substring(0, 200)}`,
projectName,
limit: 10,
useSemanticSearch: true
});
console.log(`[MEMORY-STORE] š Found ${searchResults.length} related memories via hybrid search`);
// šÆ CONTENT DISTRIBUTION BREAKTHROUGH: ALWAYS route to core files - never create separate files
const routingAnalysis = ClineMemoryStructure.analyzeContentRouting(fileName, content);
// š FORCED DISTRIBUTION: Check if we should route to a different core file or enhance existing core file
if (Object.values(CLINE_CORE_FILES).includes(fileName)) {
// Direct core file write - enhance existing content
console.log(`[CLINE-ROUTING] Direct core file write: ${fileName}`);
return this.updateCoreFile(projectName, fileName, content, fileName, routingAnalysis.mergeStrategy, tags);
}
else {
// šÆ BREAKTHROUGH: ALWAYS route to core files - never create separate files
console.log(`[CONTENT-DISTRIBUTION] š Routing ${fileName} ā ${routingAnalysis.coreFile} (${routingAnalysis.reasoning})`);
console.log(`[CONTENT-DISTRIBUTION] š” Breakthrough: All content distributed to 6 core files for optimal AI context`);
return this.updateCoreFile(projectName, routingAnalysis.coreFile, content, fileName, routingAnalysis.mergeStrategy, tags);
}
// š SEARCH-FIRST LOGIC: For core files, we already checked above, so this is for edge cases
if (existingMemory && Object.values(CLINE_CORE_FILES).includes(fileName)) {
console.log(`[SEARCH-FIRST] Found existing core file: ${fileName}, using intelligent merge`);
return this.intelligentMemoryMerge(existingMemory, content, tags);
}
// š HYBRID SEARCH: Find similar memories to potentially consolidate
const similarMemories = await this.findSimilarMemoriesForConsolidation(projectName, fileName, content);
if (similarMemories.length > 0) {
console.log(`[SEARCH-FIRST] Found ${similarMemories.length} similar memories, checking for consolidation opportunities`);
const consolidationResult = await this.considerMemoryConsolidation(similarMemories, fileName, content, tags);
if (consolidationResult) {
return consolidationResult;
}
}
// š§ ENHANCED: COMPREHENSIVE AI CONTEXT ANALYSIS
const analysis = this.analyzeContentForAI(fileName, content);
// šÆ INTELLIGENT TAG GENERATION: Optimized for AI code generation
const enhancedTags = this.generateAIOptimizedTags(content, fileName, analysis);
const finalTags = tags.length > 0 ? [...tags, ...enhancedTags] : enhancedTags;
// š RELATED MEMORY DISCOVERY: Find contextually relevant memories
const relatedMemories = await this.findRelatedMemoriesForAI(projectName, content, analysis);
const memory = {
projectName,
fileName,
content,
tags: finalTags,
lastModified: new Date(),
wordCount: this.countWords(content),
// š ENHANCED: AI-optimized metadata
memoryType: analysis.contentType,
summary: this.generateAISummary(content, analysis),
// Store AI context analysis for enhanced search
metadata: {
aiContextType: analysis.aiContextType,
codeRelevance: analysis.codeRelevance,
technicalDepth: analysis.technicalDepth,
implementationDetails: analysis.implementationDetails,
errorPatterns: analysis.errorPatterns,
architecturalInsights: analysis.architecturalInsights,
relatedMemories: relatedMemories.map(m => m.fileName)
}
};
console.log(`[SEARCH-FIRST] Creating new memory: ${fileName}`);
const storedMemory = await this.memoryRepository.store(memory);
// š¤ AI GUIDANCE: Check if all core files have real content
await this.provideAIGuidanceForCompleteness(projectName, storedMemory);
// Record successful operation
session.updateAfterOperation({
name: 'memory_store',
parameters: params,
timestamp: startTime,
success: true,
result: { fileName: storedMemory.fileName, projectName: storedMemory.projectName }
});
return storedMemory;
}
catch (error) {
// Record failed operation
session.updateAfterOperation({
name: 'memory_store',
parameters: params,
timestamp: startTime,
success: false,
error: MCPErrorHandler.formatError(error, { operation: 'memory_store', params })
});
// Re-throw with better error formatting
throw MCPErrorHandler.formatError(error, {
operation: 'memory_store',
params,
suggestions: [
'Check if the project exists',
'Use memory_bank_update for existing files',
'Verify your MongoDB connection'
]
});
}
}
// š§ ENHANCED: INTELLIGENT CONTENT ANALYSIS FOR OPTIMAL AI CONTEXT
analyzeContentForAI(fileName, content) {
const lowerFileName = fileName.toLowerCase();
const lowerContent = content.toLowerCase();
// Analyze content for AI code generation needs
const analysis = {
contentType: this.detectContentType(lowerFileName, lowerContent),
aiContextType: this.detectAIContextType(content),
codeRelevance: this.calculateCodeRelevance(content),
technicalDepth: this.calculateTechnicalDepth(content),
implementationDetails: this.extractImplementationDetails(content),
errorPatterns: this.extractErrorPatterns(content),
architecturalInsights: this.extractArchitecturalInsights(content)
};
return analysis;
}
detectContentType(fileName, content) {
// Architecture & System Design
if (content.includes('architecture') || content.includes('system design') ||
content.includes('component') || content.includes('module') ||
fileName.includes('architecture') || fileName.includes('system')) {
return 'architecture';
}
// Implementation & Code Patterns
if (content.includes('implementation') || content.includes('function') ||
content.includes('class') || content.includes('method') ||
content.includes('algorithm') || content.includes('pattern')) {
return 'implementation';
}
// Error Solutions & Debugging
if (content.includes('error') || content.includes('bug') ||
content.includes('issue') || content.includes('fix') ||
content.includes('debug') || content.includes('solution')) {
return 'error-solution';
}
// Performance & Optimization
if (content.includes('performance') || content.includes('optimization') ||
content.includes('speed') || content.includes('memory') ||
content.includes('benchmark') || content.includes('profiling')) {
return 'performance';
}
// Configuration & Setup
if (content.includes('config') || content.includes('setup') ||
content.includes('installation') || content.includes('deployment') ||
fileName.includes('config') || fileName.includes('setup')) {
return 'configuration';
}
// Testing & Quality
if (content.includes('test') || content.includes('testing') ||
content.includes('quality') || content.includes('validation') ||
fileName.includes('test')) {
return 'testing';
}
// API & Integration
if (content.includes('api') || content.includes('endpoint') ||
content.includes('integration') || content.includes('service') ||
content.includes('request') || content.includes('response')) {
return 'api-integration';
}
// Database & Data
if (content.includes('database') || content.includes('query') ||
content.includes('schema') || content.includes('migration') ||
content.includes('data model') || content.includes('sql')) {
return 'database';
}
// Security & Authentication
if (content.includes('security') || content.includes('auth') ||
content.includes('permission') || content.includes('encryption') ||
content.includes('token') || content.includes('credential')) {
return 'security';
}
// Progress & Planning
if (content.includes('progress') || content.includes('milestone') ||
content.includes('completed') || content.includes('todo') ||
fileName.includes('progress') || fileName.includes('plan')) {
return 'progress';
}
return 'general';
}
// šÆ AI CONTEXT ANALYSIS: What type of context does AI need from this content?
detectAIContextType(content) {
const lowerContent = content.toLowerCase();
// Code Generation Context
if (lowerContent.includes('function') || lowerContent.includes('class') ||
lowerContent.includes('method') || lowerContent.includes('algorithm') ||
lowerContent.includes('implementation') || lowerContent.includes('code example')) {
return 'code-generation';
}
// Architectural Decision Context
if (lowerContent.includes('decision') || lowerContent.includes('choice') ||
lowerContent.includes('alternative') || lowerContent.includes('trade-off') ||
lowerContent.includes('why we chose') || lowerContent.includes('rationale')) {
return 'architectural-decision';
}
// Problem-Solution Context
if (lowerContent.includes('problem') || lowerContent.includes('challenge') ||
lowerContent.includes('solution') || lowerContent.includes('approach') ||
lowerContent.includes('resolved') || lowerContent.includes('fixed')) {
return 'problem-solution';
}
// Pattern & Best Practice Context
if (lowerContent.includes('pattern') || lowerContent.includes('best practice') ||
lowerContent.includes('convention') || lowerContent.includes('standard') ||
lowerContent.includes('guideline') || lowerContent.includes('principle')) {
return 'pattern-practice';
}
// Learning & Insight Context
if (lowerContent.includes('learned') || lowerContent.includes('insight') ||
lowerContent.includes('discovery') || lowerContent.includes('gotcha') ||
lowerContent.includes('important') || lowerContent.includes('key finding')) {
return 'learning-insight';
}
return 'general-context';
}
// š CODE RELEVANCE: How relevant is this content for code generation?
calculateCodeRelevance(content) {
let score = 0;
const lowerContent = content.toLowerCase();
// High relevance indicators
const highRelevanceTerms = [
'function', 'class', 'method', 'algorithm', 'implementation', 'code',
'api', 'endpoint', 'database', 'query', 'schema', 'model',
'component', 'module', 'service', 'library', 'framework'
];
// Medium relevance indicators
const mediumRelevanceTerms = [
'architecture', 'design', 'pattern', 'structure', 'organization',
'configuration', 'setup', 'deployment', 'environment',
'testing', 'validation', 'error handling', 'security'
];
// Low relevance indicators
const lowRelevanceTerms = [
'meeting', 'discussion', 'planning', 'timeline', 'milestone',
'progress', 'status', 'update', 'review'
];
// Calculate score based on term frequency
highRelevanceTerms.forEach(term => {
const matches = (lowerContent.match(new RegExp(term, 'g')) || []).length;
score += matches * 3;
});
mediumRelevanceTerms.forEach(term => {
const matches = (lowerContent.match(new RegExp(term, 'g')) || []).length;
score += matches * 2;
});
lowRelevanceTerms.forEach(term => {
const matches = (lowerContent.match(new RegExp(term, 'g')) || []).length;
score += matches * 0.5;
});
// Normalize to 0-100 scale
return Math.min(100, Math.max(0, score * 2));
}
// š¬ TECHNICAL DEPTH: How technically detailed is this content?
calculateTechnicalDepth(content) {
let score = 0;
const lowerContent = content.toLowerCase();
// Technical depth indicators
const technicalTerms = [
'typescript', 'javascript', 'python', 'rust', 'go', 'java',
'react', 'node', 'express', 'mongodb', 'postgresql', 'redis',
'docker', 'kubernetes', 'aws', 'azure', 'gcp',
'jwt', 'oauth', 'graphql', 'rest', 'websocket',
'async', 'await', 'promise', 'callback', 'event',
'middleware', 'decorator', 'interface', 'generic',
'optimization', 'performance', 'memory', 'cpu',
'index', 'query', 'transaction', 'migration'
];
// Code-specific indicators
const codeIndicators = [
'function(', 'class ', 'interface ', 'type ', 'const ',
'async ', 'await ', 'return ', 'throw ', 'try {', 'catch',
'import ', 'export ', 'require(', 'module.exports',
'=> {', '() => ', 'new ', 'this.', '.then(', '.catch('
];
// Calculate technical term frequency
technicalTerms.forEach(term => {
const matches = (lowerContent.match(new RegExp(term, 'g')) || []).length;
score += matches * 2;
});
// Calculate code indicator frequency
codeIndicators.forEach(indicator => {
const matches = (content.match(new RegExp(indicator.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
score += matches * 3;
});
// Bonus for code blocks
const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length;
score += codeBlocks * 10;
// Normalize to 0-100 scale
return Math.min(100, Math.max(0, score));
}
// š§ IMPLEMENTATION DETAILS: Extract specific implementation patterns for AI
extractImplementationDetails(content) {
const details = [];
const lines = content.split('\n');
// Extract code patterns
const codeBlockRegex = /```[\s\S]*?```/g;
const codeBlocks = content.match(codeBlockRegex) || [];
codeBlocks.forEach(block => {
// Extract key patterns from code blocks
if (block.includes('function') || block.includes('const') || block.includes('class')) {
details.push(`Code pattern: ${block.substring(0, 100)}...`);
}
});
// Extract configuration patterns
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.includes('config') || trimmed.includes('setting') || trimmed.includes('option')) {
details.push(`Configuration: ${trimmed}`);
}
});
// Extract API patterns
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.includes('endpoint') || trimmed.includes('route') || trimmed.includes('api')) {
details.push(`API pattern: ${trimmed}`);
}
});
// Extract database patterns
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.includes('query') || trimmed.includes('schema') || trimmed.includes('model')) {
details.push(`Database pattern: ${trimmed}`);
}
});
return details.slice(0, 10); // Limit to most relevant
}
// šØ ERROR PATTERNS: Extract error patterns and solutions for AI learning
extractErrorPatterns(content) {
const patterns = [];
const lines = content.split('\n');
// Extract error descriptions
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('error:') || trimmed.includes('failed:') || trimmed.includes('issue:')) {
patterns.push(`Error: ${line.trim()}`);
}
});
// Extract solution patterns
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('solution:') || trimmed.includes('fix:') || trimmed.includes('resolved:')) {
patterns.push(`Solution: ${line.trim()}`);
}
});
// Extract debugging insights
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('debug') || trimmed.includes('trace') || trimmed.includes('root cause')) {
patterns.push(`Debug insight: ${line.trim()}`);
}
});
return patterns.slice(0, 8); // Limit to most relevant
}
// šļø ARCHITECTURAL INSIGHTS: Extract architectural decisions and rationale
extractArchitecturalInsights(content) {
const insights = [];
const lines = content.split('\n');
// Extract decision rationale
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('decided') || trimmed.includes('chose') || trimmed.includes('because')) {
insights.push(`Decision: ${line.trim()}`);
}
});
// Extract trade-offs
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('trade-off') || trimmed.includes('pros and cons') || trimmed.includes('alternative')) {
insights.push(`Trade-off: ${line.trim()}`);
}
});
// Extract performance considerations
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('performance') || trimmed.includes('scalability') || trimmed.includes('optimization')) {
insights.push(`Performance: ${line.trim()}`);
}
});
// Extract security considerations
lines.forEach(line => {
const trimmed = line.trim().toLowerCase();
if (trimmed.includes('security') || trimmed.includes('vulnerability') || trimmed.includes('auth')) {
insights.push(`Security: ${line.trim()}`);
}
});
return insights.slice(0, 8); // Limit to most relevant
}
// šÆ AI-OPTIMIZED TAG GENERATION: Tags that help AI understand context better
generateAIOptimizedTags(content, fileName, analysis) {
const tags = [];
// Add content type tags
tags.push(analysis.contentType);
tags.push(analysis.aiContextType);
// Add technical depth indicator
if (analysis.technicalDepth > 70) {
tags.push('high-technical-depth');
}
else if (analysis.technicalDepth > 40) {
tags.push('medium-technical-depth');
}
// Add code relevance indicator
if (analysis.codeRelevance > 70) {
tags.push('high-code-relevance');
}
else if (analysis.codeRelevance > 40) {
tags.push('medium-code-relevance');
}
// Extract technology tags
const techTags = this.extractTechnologyTags(content);
tags.push(...techTags);
// Extract pattern tags
const patternTags = this.extractPatternTags(content);
tags.push(...patternTags);
// Add file-based tags
const fileBaseName = fileName.replace(/\.[^/.]+$/, "");
const fileWords = fileBaseName.split(/[-_\s]+/).filter(word => word.length > 2);
tags.push(...fileWords);
// Remove duplicates and limit
return [...new Set(tags)].slice(0, 15);
}
// š§ TECHNOLOGY TAG EXTRACTION: Identify specific technologies for better AI context
extractTechnologyTags(content) {
const lowerContent = content.toLowerCase();
const techTags = [];
// Programming languages
const languages = ['typescript', 'javascript', 'python', 'rust', 'go', 'java', 'c++', 'c#'];
languages.forEach(lang => {
if (lowerContent.includes(lang))
techTags.push(lang);
});
// Frameworks and libraries
const frameworks = ['react', 'vue', 'angular', 'express', 'fastapi', 'django', 'spring', 'nest'];
frameworks.forEach(fw => {
if (lowerContent.includes(fw))
techTags.push(fw);
});
// Databases
const databases = ['mongodb', 'postgresql', 'mysql', 'redis', 'elasticsearch', 'sqlite'];
databases.forEach(db => {
if (lowerContent.includes(db))
techTags.push(db);
});
// Cloud and infrastructure
const infrastructure = ['aws', 'azure', 'gcp', 'docker', 'kubernetes', 'terraform'];
infrastructure.forEach(infra => {
if (lowerContent.includes(infra))
techTags.push(infra);
});
return techTags;
}
// šØ PATTERN TAG EXTRACTION: Identify design patterns and practices
extractPatternTags(content) {
const lowerContent = content.toLowerCase();
const patternTags = [];
// Design patterns
const patterns = ['singleton', 'factory', 'observer', 'strategy', 'decorator', 'adapter', 'facade'];
patterns.forEach(pattern => {
if (lowerContent.includes(pattern))
patternTags.push(`pattern-${pattern}`);
});
// Architectural patterns
const archPatterns = ['microservices', 'monolith', 'mvc', 'mvp', 'mvvm', 'clean-architecture'];
archPatterns.forEach(pattern => {
if (lowerContent.includes(pattern))
patternTags.push(`arch-${pattern}`);
});
// Development practices
const practices = ['tdd', 'bdd', 'ddd', 'solid', 'dry', 'kiss', 'yagni'];
practices.forEach(practice => {
if (lowerContent.includes(practice))
patternTags.push(`practice-${practice}`);
});
return patternTags;
}
// š RELATED MEMORY DISCOVERY: Find memories that provide relevant context for AI
async findRelatedMemoriesForAI(projectName, content, analysis) {
try {
// Find memories with similar content type
const similarTypeMemories = await this.memoryRepository.searchByType(analysis.contentType, projectName, 5);
// Find memories with overlapping technologies
const techTags = this.extractTechnologyTags(content);
const techRelatedMemories = await this.memoryRepository.searchByTags(techTags, projectName, 3);
// Combine and deduplicate
const allRelated = [...similarTypeMemories, ...techRelatedMemories];
const uniqueRelated = allRelated.filter((memory, index, self) => index === self.findIndex(m => m.fileName === memory.fileName));
return uniqueRelated.slice(0, 5);
}
catch (error) {
// Graceful fallback if advanced search fails
return [];
}
}
// š AI SUMMARY GENERATION: Create concise summary optimized for AI context
generateAISummary(content, analysis) {
const lines = content.split('\n').filter(line => line.trim().length > 0);
// Extract key sentences based on AI context needs
const keySentences = [];
// Add first meaningful line as context
const firstMeaningful = lines.find(line => line.trim().length > 20 &&
!line.startsWith('#') &&
!line.startsWith('*') &&
!line.startsWith('-'));
if (firstMeaningful) {
keySentences.push(firstMeaningful.trim());
}
// Add implementation details
if (analysis.implementationDetails.length > 0) {
keySentences.push(`Implementation: ${analysis.implementationDetails[0]}`);
}
// Add error patterns if present
if (analysis.errorPatterns.length > 0) {
keySentences.push(`Error pattern: ${analysis.errorPatterns[0]}`);
}
// Add architectural insights if present
if (analysis.architecturalInsights.length > 0) {
keySentences.push(`Architecture: ${analysis.architecturalInsights[0]}`);
}
// Create summary
const summary = keySentences.join(' | ').substring(0, 300);
return summary || content.substring(0, 200);
}
// š§ UTILITY: Count words in content
countWords(content) {
return content.trim().split(/\s+/).filter(word => word.length > 0).length;
}
// šÆ CLINE CORE FILE UPDATE: Update core files using Cline's structure
async updateCoreFile(projectName, coreFileName, content, originalFileName, mergeStrategy, tags = []) {
console.log(`[CLINE-UPDATE] Updating core file: ${coreFileName} with content from ${originalFileName}`);
// šØ CONTENT LENGTH VALIDATION: Prevent context window overflow
const wordCount = this.countWords(content);
if (wordCount > 2000) {
console.warn(`[CLINE-UPDATE] ā ļø Large content detected (${wordCount} words) for ${coreFileName}`);
console.warn(`[CLINE-UPDATE] Consider breaking into smaller, focused sections`);
}
// Check if core file exists
let existingCoreFile = await this.memoryRepository.findByFileName(projectName, coreFileName);
if (!existingCoreFile) {
// Create core file with template
const template = ClineMemoryStructure.getCoreFileTemplate(coreFileName, projectName);
existingCoreFile = await this.memoryRepository.store({
projectName,
fileName: coreFileName,
content: template,
tags: ['core', 'cline-structure'],
lastModified: new Date(),
wordCount: this.countWords(template),
memoryType: 'documentation'
});
console.log(`[CLINE-UPDATE] Created new core file: ${coreFileName}`);
}
// Merge content based on strategy
let updatedContent;
switch (mergeStrategy) {
case 'replace':
updatedContent = content;
break;
case 'append':
updatedContent = `${existingCoreFile.content}\n\n## ${originalFileName}\n${content}`;
break;
case 'section-update':
updatedContent = this.updateCoreFileSection(existingCoreFile.content, content, originalFileName);
break;
case 'merge':
default:
updatedContent = this.mergeComplementaryContent(existingCoreFile.content, content);
break;
}
// Update core file
const updatedMemory = {
...existingCoreFile,
content: updatedContent,
tags: this.mergeTags(existingCoreFile.tags, [...tags, 'updated-from:' + originalFileName]),
lastModified: new Date(),
wordCount: this.countWords(updatedContent)
};
return await this.memoryRepository.store(updatedMemory);
}
// š INTELLIGENT MEMORY MERGE: Merge new content with existing memory
async intelligentMemoryMerge(existingMemory, newContent, newTags = []) {
console.log(`[MEMORY-MERGE] Merging content for: ${existingMemory.fileName}`);
// Analyze both contents
const existingAnalysis = this.analyzeContentForAI(existingMemory.fileName, existingMemory.content);
const newAnalysis = this.analyzeContentForAI(existingMemory.fileName, newContent);
// Intelligent content merging strategy
let mergedContent;
if (this.shouldReplaceContent(existingMemory.content, newContent, existingAnalysis, newAnalysis)) {
// Replace if new content is significantly better
mergedContent = newContent;
console.log(`[MEMORY-MERGE] Replacing content (new content is better)`);
}
else if (this.shouldAppendContent(existingMemory.content, newContent)) {
// Append if content is complementary
mergedContent = this.mergeComplementaryContent(existingMemory.content, newContent);
console.log(`[MEMORY-MERGE] Appending complementary content`);
}
else {
// Update with enhanced content
mergedContent = this.enhanceExistingContent(existingMemory.content, newContent);
console.log(`[MEMORY-MERGE] Enhancing existing content`);
}
// Merge tags intelligently
const mergedTags = this.mergeTags(existingMemory.tags, newTags);
// Update the memory
const updatedMemory = {
...existingMemory,
content: mergedContent,
tags: mergedTags,
lastModified: new Date(),
wordCount: this.countWords(mergedContent),
summary: this.generateAISummary(mergedContent, newAnalysis)
};
return await this.memoryRepository.store(updatedMemory);
}
// š FIND SIMILAR MEMORIES: Use hybrid search to find memories for potential consolidation
async findSimilarMemoriesForConsolidation(projectName, fileName, content) {
try {
// Use hybrid search to find similar content
const searchResults = await this.memoryRepository.search({
query: content.substring(0, 200), // Use first 200 chars as search query
projectName,
limit: 5,
useSemanticSearch: true
});
// Filter out exact filename matches and low-relevance results
return searchResults
.filter(result => result.fileName !== fileName && (result.score || 0) > 0.7)
.map(result => ({
id: result.id,
projectName: result.projectName,
fileName: result.fileName,
content: result.content,
tags: result.tags,
lastModified: result.lastModified,
wordCount: result.wordCount,
contentVector: result.contentVector,
summary: result.summary,
memoryType: result.memoryType,
metadata: result.metadata
}));
}
catch (error) {
console.warn(`[SEARCH-FIRST] Error finding similar memories: ${error}`);
return [];
}
}
// š¤ CONSIDER CONSOLIDATION: Decide whether to consolidate similar memories
async considerMemoryConsolidation(similarMemories, fileName, content, tags) {
// Only consolidate if we find very similar memories with related filenames
const consolidationCandidates = similarMemories.filter(memory => this.areMemoriesConsolidatable(memory, fileName, content));
if (consolidationCandidates.length === 0) {
return null;
}
console.log(`[MEMORY-CONSOLIDATION] Found ${consolidationCandidates.length} consolidation candidates`);
// For now, just merge with the most similar one
const bestCandidate = consolidationCandidates[0];
return this.intelligentMemoryMerge(bestCandidate, content, tags);
}
// š§ CONTENT ANALYSIS: Determine if content should be replaced
shouldReplaceContent(existingContent, newContent, existingAnalysis, newAnalysis) {
// Replace if new content is significantly longer and more detailed
if (newContent.length > existingContent.length * 1.5 && newAnalysis.technicalDepth > existingAnalysis.technicalDepth) {
return true;
}
// Replace if new content has more implementation details
if (newAnalysis.implementationDetails.length > existingAnalysis.implementationDetails.length * 1.5) {
return true;
}
// Replace if new content has higher code relevance
if (newAnalysis.codeRelevance > existingAnalysis.codeRelevance + 0.3) {
return true;
}
return false;
}
// š CONTENT MERGING: Check if content should be appended
shouldAppendContent(existingContent, newContent) {
// Append if content is different and complementary
const similarity = this.calculateContentSimilarity(existingContent, newContent);
return similarity < 0.7 && similarity > 0.3; // Somewhat related but different
}
// š MERGE COMPLEMENTARY CONTENT: Intelligently combine different but related content
mergeComplementaryContent(existingContent, newContent) {
// Add a separator and append new content
const separator = '\n\n---\n\n';
return `${existingContent}${separator}${newContent}`;
}
// ⨠ENHANCE EXISTING CONTENT: Update existing content with new insights
enhanceExistingContent(existingContent, newContent) {
// For now, prefer the longer, more detailed content
return newContent.length > existingContent.length ? newContent : existingContent;
}
// š·ļø MERGE TAGS: Intelligently combine tag sets
mergeTags(existingTags, newTags) {
const mergedTags = [...new Set([...existingTags, ...newTags])];
return mergedTags.slice(0, 20); // Limit to 20 tags to avoid bloat
}
// š CONSOLIDATION CHECK: Determine if memories can be consolidated
areMemoriesConsolidatable(memory, fileName, content) {
// Check if filenames are related (similar patterns)
const filenameSimilarity = this.calculateFilenameSimilarity(memory.fileName, fileName);
if (filenameSimilarity < 0.5) {
return false;
}
// Check if content is similar enough to consolidate
const contentSimilarity = this.calculateContentSimilarity(memory.content, content);
return contentSimilarity > 0.8; // High similarity threshold for consolidation
}
// š FILENAME SIMILARITY: Calculate similarity between filenames
calculateFilenameSimilarity(filename1, filename2) {
// Simple similarity based on common words and patterns
const words1 = filename1.toLowerCase().split(/[^a-z0-9]+/).filter(w => w.length > 2);
const words2 = filename2.toLowerCase().split(/[^a-z0-9]+/).filter(w => w.length > 2);
if (words1.length === 0 || words2.length === 0)
return 0;
const commonWords = words1.filter(word => words2.includes(word));
return commonWords.length / Math.max(words1.length, words2.length);
}
// š CONTENT SIMILARITY: Calculate similarity between content strings
calculateContentSimilarity(content1, content2) {
// Simple word-based similarity
const words1 = content1.toLowerCase().split(/\s+/).filter(w => w.length > 3);
const words2 = content2.toLowerCase().split(/\s+/).filter(w => w.length > 3);
if (words1.length === 0 || words2.length === 0)
return 0;
const set1 = new Set(words1);
const set2 = new Set(words2);
const intersection = new Set([...set1].filter(word => set2.has(word)));
const union = new Set([...set1, ...set2]);
return intersection.size / union.size; // Jaccard similarity
}
// š CORE FILE SECTION UPDATE: Intelligently update sections in core files
updateCoreFileSection(existingContent, newContent, sourceFileName) {
// Look for existing section with same source
const sectionPattern = new RegExp(`## ${sourceFileName}[\\s\\S]*?(?=##|$)`, 'g');
if (sectionPattern.test(existingContent)) {
// Update existing section
return existingContent.replace(sectionPattern, `## ${sourceFileName}\n${newContent}\n`);
}
else {
// Add new section
return `${existingContent}\n\n## ${sourceFileName}\n${newContent}`;
}
}
// š¤ AI GUIDANCE: Provide intelligent feedback to ensure all core files get content
async provideAIGuidanceForCompleteness(projectName, storedMemory) {
try {
const { ClineMemoryStructure, CLINE_CORE_FILES } = await import('../../../shared/services/cline-memory-structure.js');
const coreFiles = Object.values(CLINE_CORE_FILES);
// Check which core files still have only template content
const templateFiles = [];
const contentFiles = [];
const fileWordCounts = {};
for (const coreFile of coreFiles) {
const memory = await this.memoryRepository.findByFileName(projectName, coreFile);
if (memory) {
const isTemplate = this.isTemplateContent(memory.content);
const wordCount = this.countWords(memory.content);
fileWordCounts[coreFile] = wordCount;
if (isTemplate) {
templateFiles.push(coreFile);
}
else {
contentFiles.push(coreFile);
}
}
}
// šÆ SMART DISTRIBUTION SUGGESTION: If content is large and other files are empty
const currentFileWordCount = this.countWords(storedMemory.content);
if (currentFileWordCount > 1000 && templateFiles.length > 2) {
console.log(`\nš§ [SMART-DISTRIBUTION] Large content detected (${currentFileWordCount} words) in ${storedMemory.fileName}`);
console.log(`š” [SUGGESTION] Consider breaking this content into focused sections:`);
// Analyze content and suggest specific distributions
const distributionSuggestions = this.analyzeContentForDistribution(storedMemory.content, templateFiles);
distributionSuggestions.forEach(suggestion => {
console.log(` ⢠${suggestion.targetFile}: ${suggestion.contentType} (${suggestion.reason})`);
});
}
// Provide AI guidance based on completeness
if (templateFiles.length > 0) {
console.log(`\nš¤ [AI-GUIDANCE] Memory Bank Completeness Check:`);
console.log(`ā
Files with real content (${contentFiles.length}): ${contentFiles.join(', ')}`);
console.log(`ā ļø Files still needing content (${templateFiles.length}): ${templateFiles.join(', ')}`);
console.log(`\nš” [AI-SUGGESTION] To maximize AI context effectiveness:`);
templateFiles.forEach(file => {
const guidance = this.getFileSpecificGuidance(file);
console.log(` ⢠${file}: ${guidance}`);
});
console.log(`\nšÆ [NEXT-STEPS] Consider adding content to remaining ${templateFiles.length} files for complete project context.`);
}
else {
console.log(`\nš [AI-GUIDANCE] All 6 core files have real content! Memory bank is optimally structured.`);
}
}
catch (error) {
console.warn(`[AI-GUIDANCE] Could not provide completeness guidance: ${error}`);
}
}
// š§ CONTENT DISTRIBUTION ANALYSIS: Suggest how to break up large content
analyzeContentForDistribution(content, emptyFiles) {
const suggestions = [];
const lowerContent = content.toLowerCase();
// Analyze content sections and suggest distribution
if (emptyFiles.includes('techContext.md') && (lowerContent.includes('javascript') || lowerContent.includes('html') ||
lowerContent.includes('css') || lowerContent.includes('framework') ||
lowerContent.includes('technology') || lowerContent.includes('dependencies'))) {
suggestions.push({
targetFile: 'techContext.md',
contentType: 'Technical implementation details',
reason: 'Contains technology stack and implementation specifics'
});
}
if (emptyFiles.includes('systemPatterns.md') && (lowerContent.includes('architecture') || lowerContent.includes('component') ||
lowerContent.includes('module') || lowerContent.includes('class') ||
lowerContent.includes('design') || lowerContent.includes('pattern'))) {
suggestions.push({
targetFile: 'systemPatterns.md',
contentType: 'Architecture and design patterns',
reason: 'Contains system architecture and component design'
});
}
if (emptyFiles.includes('productContext.md') && (lowerContent.includes('user') || lowerContent.includes('ux') ||
lowerContent.includes('experience') || lowerContent.includes('workflow') ||
lowerContent.includes('persona') || lowerContent.includes('problem'))) {
suggestions.push({
targetFile: 'productContext.md',
contentType: 'User experience and product context',
reason: 'Contains user-focused and product information'
});
}
if (emptyFiles.includes('progress.md') && (lowerContent.includes('status') || lowerContent.includes('progress') ||
lowerContent.includes('milestone') || lowerContent.includes('timeline') ||
lowerContent.includes('done') || lowerContent.includes('remaining'))) {
suggestions.push({
targetFile: 'progress.md',
contentType: 'Project status and progress',
reason: 'Contains progress tracking and status information'
});
}
return suggestions;
}
// š TEMPLATE DETECTION: Identify if content is still just a template
isTemplateContent(content) {
const oldTemplateIndicators = [
'[Define', '[List', '[Describe', '[Document',
'Define the problems', 'List user problems', 'Describe how',
'Document important', 'Define core requirements'
];
const newTemplateIndicators = [
'[Brief description of the project]', '[Business/user value proposition]',
'[specific functionality]', '[specific capability]', '[specific outcome]',
'[measurable outcome]', '[specific achievement]', '[business impact]',
'[what we\'re building]', '[who we\'re serving]', '[where it will run]',
'[specific problem]', '[market area]', '[specific value]',
'[React/Vue/Angular or Vanilla JS]', '[Webpack/Vite/Parcel]',
'[npm/yarn/pnpm]', '[CI/CD pipeline details]'
];
// Check for both old and new template patterns
const hasOldTemplate = oldTemplateIndicators.some(indicator => content.includes(indicator));
const hasNewTemplate = newTemplateIndicators.some(indicator => content.includes(indicator));
// Also check for template structure patterns
const templateStructurePatterns = [
'**What We\'re Building**: [',
'**Primary Goals**:',
'**Must-Have Features**:',
'**Problem Statement**: Users struggle with [',
'**Frontend**: JavaScript ES6, HTML5, CSS3',
'**Pattern**: [MVC/Component-based'
];
const hasTemplateStructure = templateStructurePatterns.some(pattern => content.includes(pattern));
return hasOldTemplate || hasNewTemplate || hasTemplateStructure;
}
// š” FILE-SPECIFIC GUIDANCE: Provide targeted suggestions for each core file
getFileSpecificGuidance(fileName) {
switch (fileName) {
case 'projectbrief.md':
return 'Add project goals, requirements, scope, and success criteria';
case 'productContext.md':
return 'Add user problems, solution approach, UX goals, and target users';
case 'systemPatterns.md':
return 'Add architecture decisions, design patterns, and component relationships';
case 'techContext.md':
return 'Add technology stack, development setup, dependencies, and constraints';
case 'activeContext.md':
return 'Add current work focus, recent changes, next steps, and active decisions';
case 'progress.md':
return 'Add what works, what\'s left to build, current status, and known issues';
default:
return 'Add relevant project content';
}
}
}