UNPKG

@andrebuzeli/advanced-memory-markdown-mcp

Version:

Advanced Memory Bank MCP v3.1.5 - Sistema avançado de gerenciamento de memória com isolamento de projetos por IDE, sincronização sob demanda, backup a cada 30min, apenas arquivos .md principais sincronizados, pasta reasoning temporária com limpeza automát

297 lines 10.9 kB
/** * Remember Tool - Busca em todos os arquivos .md dos projetos * * IMPORTANTE - NOME DO PROJETO: * - O projectName DEVE ser exatamente o nome da pasta RAIZ do projeto aberto no IDE * - NÃO é uma subpasta, NÃO é um subprojeto - é a pasta raiz que foi aberta no IDE * - O nome deve ser uma cópia EXATA sem adicionar ou remover nada * * Funcionalidades: * - Busca em memory.md, task.md, plan.md * - Busca semântica com score de relevância * - Filtros por tipo de arquivo e projeto * - Suporte a busca case-sensitive e palavra completa */ import * as fs from 'fs/promises'; import { SyncManager } from './sync-manager.js'; export class RememberTool { syncManager; constructor() { this.syncManager = new SyncManager(); } /** * Lists all available memories, topics, tasks, and plans */ async list(projectName, fileType = 'all') { try { const projects = projectName ? [projectName] : await this.syncManager.listProjects(); const content = []; for (const project of projects) { await this.syncManager.ensureProjectStructure(project); const projectContent = { projectName: project, memories: [], tasks: [], plans: [] }; if (fileType === 'memory' || fileType === 'all') { projectContent.memories = await this.extractMemoryTopics(this.syncManager.getProjectFilePath(project, 'memory.md')); } if (fileType === 'task' || fileType === 'all') { projectContent.tasks = await this.extractTaskTitles(this.syncManager.getProjectFilePath(project, 'task.md')); } if (fileType === 'planning' || fileType === 'all') { projectContent.plans = await this.extractPlanTitles(this.syncManager.getProjectFilePath(project, 'plan.md')); } content.push(projectContent); } return { projects, content }; } catch (error) { console.error('Error listing content:', error); return { projects: [], content: [] }; } } /** * Reads specific content from memory files */ async read(projectName, fileName, itemName) { try { await this.syncManager.ensureProjectStructure(projectName); let filePath; switch (fileName) { case 'memory': filePath = this.syncManager.getProjectFilePath(projectName, 'memory.md'); break; case 'task': filePath = this.syncManager.getProjectFilePath(projectName, 'task.md'); break; case 'planning': filePath = this.syncManager.getProjectFilePath(projectName, 'plan.md'); break; default: return null; } const content = await fs.readFile(filePath, 'utf8'); if (itemName) { return this.extractSpecificItem(content, itemName, fileName); } return content; } catch (error) { console.error('Error reading content:', error); return null; } } /** * Searches across all memory files */ async search(options) { try { const projects = options.projectName ? [options.projectName] : await this.syncManager.listProjects(); const results = []; for (const project of projects) { await this.syncManager.ensureProjectStructure(project); const filesToSearch = this.getFilesToSearch(project, options.fileType || 'all'); for (const { fileName, filePath } of filesToSearch) { const fileResults = await this.searchInFile(project, fileName, filePath, options); results.push(...fileResults); } } // Sort by score (descending) and limit results results.sort((a, b) => b.score - a.score); if (options.maxResults) { return results.slice(0, options.maxResults); } return results; } catch (error) { console.error('Error searching:', error); return []; } } /** * Extracts memory topics from memory.md */ async extractMemoryTopics(filePath) { try { const content = await fs.readFile(filePath, 'utf8'); const topics = []; const lines = content.split('\n'); for (const line of lines) { const topicMatch = line.match(/^### (.+) - (.+)/); if (topicMatch) { topics.push(`${topicMatch[1]} - ${topicMatch[2]}`); } } return topics; } catch { return []; } } /** * Extracts task titles from task.md */ async extractTaskTitles(filePath) { try { const content = await fs.readFile(filePath, 'utf8'); const tasks = []; const lines = content.split('\n'); for (const line of lines) { const taskMatch = line.match(/^\s*- \[.\] \[\w+\] (.+) \(ID: (.+)\)/); if (taskMatch) { tasks.push(`${taskMatch[1]} (${taskMatch[2]})`); } } return tasks; } catch { return []; } } /** * Extracts plan titles from planning.md */ async extractPlanTitles(filePath) { try { const content = await fs.readFile(filePath, 'utf8'); const plans = []; const lines = content.split('\n'); for (const line of lines) { const planMatch = line.match(/^### (.+) \(ID: (.+)\)/); if (planMatch) { plans.push(`${planMatch[1]} (${planMatch[2]})`); } } return plans; } catch { return []; } } /** * Extracts specific item content */ extractSpecificItem(content, itemName, fileType) { const lines = content.split('\n'); let inItem = false; let itemContent = []; let itemPattern; switch (fileType) { case 'memory': itemPattern = new RegExp(`^### ${itemName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`); break; case 'task': itemPattern = new RegExp(`\\(ID: ${itemName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)`); break; case 'planning': itemPattern = new RegExp(`\\(ID: ${itemName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)`); break; default: return null; } for (const line of lines) { if (itemPattern.test(line)) { inItem = true; itemContent.push(line); continue; } if (inItem) { if (line.startsWith('###') || line.startsWith('##')) { break; } itemContent.push(line); } } return itemContent.length > 0 ? itemContent.join('\n') : null; } /** * Gets files to search based on file type */ getFilesToSearch(projectName, fileType) { const files = []; if (fileType === 'all' || fileType === 'memory') { files.push({ fileName: 'memory.md', filePath: this.syncManager.getProjectFilePath(projectName, 'memory.md') }); } if (fileType === 'all' || fileType === 'task') { files.push({ fileName: 'task.md', filePath: this.syncManager.getProjectFilePath(projectName, 'task.md') }); } if (fileType === 'all' || fileType === 'planning') { files.push({ fileName: 'plan.md', filePath: this.syncManager.getProjectFilePath(projectName, 'plan.md') }); } return files; } /** * Searches in a specific file */ async searchInFile(projectName, fileName, filePath, options) { try { const content = await fs.readFile(filePath, 'utf8'); const lines = content.split('\n'); const matches = []; let score = 0; const searchQuery = options.caseSensitive ? options.query : options.query.toLowerCase(); const searchPattern = options.wholeWord ? new RegExp(`\\b${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, options.caseSensitive ? 'g' : 'gi') : null; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (!line) continue; const searchLine = options.caseSensitive ? line : line.toLowerCase(); let found = false; if (options.wholeWord && searchPattern) { found = searchPattern.test(line); } else { found = searchLine.includes(searchQuery); } if (found) { const contextStart = Math.max(0, i - 2); const contextEnd = Math.min(lines.length - 1, i + 2); const context = lines.slice(contextStart, contextEnd + 1).join('\n'); matches.push({ line: i + 1, content: line.trim(), context }); // Calculate score based on match quality if (line.toLowerCase().includes(searchQuery)) { score += 10; } if (line.startsWith('#')) { score += 5; // Headers are more important } if (line.includes('**')) { score += 3; // Bold text is more important } } } if (matches.length > 0) { return [{ projectName, fileName, filePath, matches, score }]; } return []; } catch { return []; } } } //# sourceMappingURL=remember-tool.js.map