UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

703 lines (702 loc) 29.9 kB
import ParserFromPackage from 'web-tree-sitter'; import path from 'path'; import { fileURLToPath } from 'url'; import crypto from 'crypto'; import os from 'os'; import logger from '../../logger.js'; import { FileCache } from './cache/fileCache.js'; import { readFileSecure } from './fsUtils.js'; import { ensureDirectoryExists, validateDirectoryIsWritable, getCacheDirectory } from './directoryUtils.js'; import { GrammarManager } from './cache/grammarManager.js'; import { TieredCache } from './cache/tieredCache.js'; import { MemoryManager } from './cache/memoryManager.js'; import { MemoryLeakDetector } from './cache/memoryLeakDetector.js'; import { ProcessLifecycleManager } from './cache/processLifecycleManager.js'; import { ResourceTracker } from './cache/resourceTracker.js'; import { getProjectRoot, resolveProjectPath } from './utils/pathUtils.enhanced.js'; import { FileContentManager } from './cache/fileContentManager.js'; import { MetadataCache } from './cache/metadataCache.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export let grammarManager = null; export let memoryManager = null; export let astMemoryCache = null; export let sourceCodeMemoryCache = null; let parseCache = null; let sourceCodeCache = null; let parseTreeTieredCache = null; let sourceCodeTieredCache = null; let sourceCodeMetadataCache = null; let astMetadataCache = null; export let memoryLeakDetector = null; export let processLifecycleManager = null; let fileContentManager = null; const GRAMMARS_BASE_DIR = resolveProjectPath('src/tools/code-map-generator/grammars'); logger.info(`Grammar files directory: ${GRAMMARS_BASE_DIR}`); logger.info(`Project root directory: ${getProjectRoot()}`); const currentWorkingDirectory = process.cwd(); logger.info(`Current working directory: ${currentWorkingDirectory}`); logger.info(`Module directory (__dirname): ${__dirname}`); if (currentWorkingDirectory === '/' || currentWorkingDirectory === '') { logger.error('CRITICAL SECURITY ISSUE: Working directory has been changed to root (/). This could compromise security boundaries.'); logger.error('RECOMMENDATION: All file operations should use absolute paths from getProjectRoot() instead of process.cwd()'); logger.warn('Continuing with absolute path resolution to maintain security...'); } export const languageConfigurations = { '.js': { name: 'JavaScript', wasmPath: 'tree-sitter-javascript.wasm' }, '.jsx': { name: 'JavaScript JSX', wasmPath: 'tree-sitter-javascript.wasm' }, '.ts': { name: 'TypeScript', wasmPath: 'tree-sitter-typescript.wasm' }, '.tsx': { name: 'TSX', wasmPath: 'tree-sitter-tsx.wasm' }, '.html': { name: 'HTML', wasmPath: 'tree-sitter-html.wasm' }, '.css': { name: 'CSS', wasmPath: 'tree-sitter-css.wasm' }, '.vue': { name: 'Vue', wasmPath: 'tree-sitter-vue.wasm' }, '.py': { name: 'Python', wasmPath: 'tree-sitter-python.wasm' }, '.java': { name: 'Java', wasmPath: 'tree-sitter-java.wasm' }, '.cs': { name: 'C#', wasmPath: 'tree-sitter-c_sharp.wasm' }, '.go': { name: 'Go', wasmPath: 'tree-sitter-go.wasm' }, '.rb': { name: 'Ruby', wasmPath: 'tree-sitter-ruby.wasm' }, '.rs': { name: 'Rust', wasmPath: 'tree-sitter-rust.wasm' }, '.php': { name: 'PHP', wasmPath: 'tree-sitter-php.wasm' }, '.kt': { name: 'Kotlin', wasmPath: 'tree-sitter-kotlin.wasm' }, '.swift': { name: 'Swift', wasmPath: 'tree-sitter-swift.wasm' }, '.scala': { name: 'Scala', wasmPath: 'tree-sitter-scala.wasm' }, '.ex': { name: 'Elixir', wasmPath: 'tree-sitter-elixir.wasm' }, '.exs': { name: 'Elixir Script', wasmPath: 'tree-sitter-elixir.wasm' }, '.lua': { name: 'Lua', wasmPath: 'tree-sitter-lua.wasm' }, '.c': { name: 'C', wasmPath: 'tree-sitter-c.wasm' }, '.h': { name: 'C Header', wasmPath: 'tree-sitter-c.wasm' }, '.cpp': { name: 'C++', wasmPath: 'tree-sitter-cpp.wasm' }, '.hpp': { name: 'C++ Header', wasmPath: 'tree-sitter-cpp.wasm' }, '.cc': { name: 'C++', wasmPath: 'tree-sitter-cpp.wasm' }, '.m': { name: 'Objective-C', wasmPath: 'tree-sitter-objc.wasm' }, '.mm': { name: 'Objective-C++', wasmPath: 'tree-sitter-objc.wasm' }, '.zig': { name: 'Zig', wasmPath: 'tree-sitter-zig.wasm' }, '.sh': { name: 'Bash', wasmPath: 'tree-sitter-bash.wasm' }, '.bash': { name: 'Bash', wasmPath: 'tree-sitter-bash.wasm' }, '.ml': { name: 'OCaml', wasmPath: 'tree-sitter-ocaml.wasm' }, '.mli': { name: 'OCaml Interface', wasmPath: 'tree-sitter-ocaml.wasm' }, '.elm': { name: 'Elm', wasmPath: 'tree-sitter-elm.wasm' }, '.re': { name: 'ReScript', wasmPath: 'tree-sitter-rescript.wasm' }, '.res': { name: 'ReScript', wasmPath: 'tree-sitter-rescript.wasm' }, '.el': { name: 'Emacs Lisp', wasmPath: 'tree-sitter-elisp.wasm' }, '.json': { name: 'JSON', wasmPath: 'tree-sitter-json.wasm' }, '.yaml': { name: 'YAML', wasmPath: 'tree-sitter-yaml.wasm' }, '.yml': { name: 'YAML', wasmPath: 'tree-sitter-yaml.wasm' }, '.toml': { name: 'TOML', wasmPath: 'tree-sitter-toml.wasm' }, '.sol': { name: 'Solidity', wasmPath: 'tree-sitter-solidity.wasm' }, '.ql': { name: 'CodeQL', wasmPath: 'tree-sitter-ql.wasm' }, '.tla': { name: 'TLA+', wasmPath: 'tree-sitter-tlaplus.wasm' }, '.rdl': { name: 'SystemRDL', wasmPath: 'tree-sitter-systemrdl.wasm' }, '.erb': { name: 'Embedded Ruby', wasmPath: 'tree-sitter-embedded_template.wasm' }, '.ejs': { name: 'EJS', wasmPath: 'tree-sitter-embedded_template.wasm' }, }; export async function initializeParser() { if (grammarManager && grammarManager.isInitialized()) { logger.debug('Tree-sitter parser already initialized.'); return; } try { memoryManager = new MemoryManager({ maxMemoryPercentage: 0.4, monitorInterval: 30000, autoManage: true }); astMemoryCache = memoryManager.createASTCache(); sourceCodeMemoryCache = memoryManager.createSourceCodeCache(); const tempGrammarManager = new GrammarManager(languageConfigurations, { maxGrammars: 20, preloadCommonGrammars: true, preloadExtensions: ['.js', '.ts', '.py', '.html', '.css'], grammarsBaseDir: GRAMMARS_BASE_DIR }); await tempGrammarManager.initialize(); grammarManager = tempGrammarManager; memoryManager.registerGrammarManager(grammarManager); if (grammarManager.isInitialized() && grammarManager.getOptions().preloadCommonGrammars) { await grammarManager.preloadGrammars(); } const resourceTracker = new ResourceTracker(); memoryLeakDetector = new MemoryLeakDetector({ autoDetect: true, checkInterval: 60 * 1000, snapshotInterval: 10 * 60 * 1000, maxSnapshots: 5 }); await memoryLeakDetector.init(); processLifecycleManager = new ProcessLifecycleManager({ autoMonitor: true, healthCheckInterval: 30 * 1000, gcInterval: 5 * 60 * 1000 }); await processLifecycleManager.init(memoryManager, resourceTracker); logger.info('Tree-sitter parser, memory management, and process lifecycle management initialized successfully.'); } catch (error) { logger.error({ err: error }, 'Failed to initialize Tree-sitter parser and memory management.'); throw error; } } export async function initializeCaches(config) { if (config.cache?.enabled === false) { logger.info('File-based caching is disabled in configuration.'); return; } try { const cacheDir = getCacheDirectory(config); logger.debug(`Using cache directory: ${cacheDir}`); await ensureDirectoryExists(cacheDir); const parseTreeCacheDir = path.join(cacheDir, 'parse-trees'); const sourceCodeCacheDir = path.join(cacheDir, 'source-code'); await ensureDirectoryExists(parseTreeCacheDir); await ensureDirectoryExists(sourceCodeCacheDir); await validateDirectoryIsWritable(parseTreeCacheDir); await validateDirectoryIsWritable(sourceCodeCacheDir); parseCache = new FileCache({ name: 'parse-trees', cacheDir: parseTreeCacheDir, maxEntries: config.cache?.maxEntries, maxAge: config.cache?.maxAge, serialize: ((tree) => { return JSON.stringify({ rootNodeType: tree.rootNode?.type || 'unknown' }); }), deserialize: ((serialized) => { return JSON.parse(serialized); }) }); sourceCodeCache = new FileCache({ name: 'source-code', cacheDir: sourceCodeCacheDir, maxEntries: config.cache?.maxEntries, maxAge: config.cache?.maxAge }); if (config.cache?.useMemoryCache) { parseTreeTieredCache = new TieredCache({ name: 'parse-trees-tiered', cacheDir: parseTreeCacheDir, maxEntries: config.cache?.maxEntries, maxAge: config.cache?.maxAge, useMemoryCache: true, memoryMaxEntries: config.cache?.memoryMaxEntries, memoryMaxAge: config.cache?.memoryMaxAge, memoryThreshold: config.cache?.memoryThreshold, serialize: ((tree) => { return JSON.stringify({ rootNodeType: tree.rootNode?.type || 'unknown' }); }), deserialize: ((serialized) => { return JSON.parse(serialized); }) }); sourceCodeTieredCache = new TieredCache({ name: 'source-code-tiered', cacheDir: sourceCodeCacheDir, maxEntries: config.cache?.maxEntries, maxAge: config.cache?.maxAge, useMemoryCache: true, memoryMaxEntries: config.cache?.memoryMaxEntries, memoryMaxAge: config.cache?.memoryMaxAge, memoryThreshold: config.cache?.memoryThreshold }); await parseTreeTieredCache.init(); await sourceCodeTieredCache.init(); logger.info('Tiered caches initialized successfully.'); } await parseCache.init(); await sourceCodeCache.init(); sourceCodeMetadataCache = new MetadataCache({ name: 'source-code-metadata', cacheDir: sourceCodeCacheDir, maxEntries: config.cache?.maxEntries, maxAge: config.cache?.maxAge, useMemoryCache: true, memoryMaxEntries: 5000, memoryThreshold: 0.5 }); astMetadataCache = new MetadataCache({ name: 'ast-metadata', cacheDir: parseTreeCacheDir, maxEntries: config.cache?.maxEntries, maxAge: config.cache?.maxAge, useMemoryCache: true, memoryMaxEntries: 3000, memoryThreshold: 0.5 }); await sourceCodeMetadataCache.init(); await astMetadataCache.init(); fileContentManager = new FileContentManager({ maxCachedFiles: config.cache?.maxCachedFiles !== undefined ? config.cache.maxCachedFiles : 0, maxAge: 5 * 60 * 1000, cacheDir: cacheDir, useFileCache: config.cache?.enabled === undefined || config.cache?.enabled === true }); await fileContentManager.init(); logger.info('File-based caches, metadata caches, and file content manager initialized successfully.'); } catch (error) { logger.error({ err: error }, 'Failed to initialize caches.'); parseCache = null; sourceCodeCache = null; fileContentManager = null; sourceCodeMetadataCache = null; astMetadataCache = null; } } export async function clearCaches() { try { if (parseCache) { await parseCache.clear(); } if (sourceCodeCache) { await sourceCodeCache.clear(); } if (parseTreeTieredCache) { await parseTreeTieredCache.clear(); } if (sourceCodeTieredCache) { await sourceCodeTieredCache.clear(); } if (fileContentManager) { fileContentManager.clearCache(); } if (astMemoryCache) { astMemoryCache.clear(); } if (sourceCodeMemoryCache) { sourceCodeMemoryCache.clear(); } if (sourceCodeMetadataCache) { await sourceCodeMetadataCache.clear(); } if (astMetadataCache) { await astMetadataCache.clear(); } if (memoryManager) { memoryManager.runGarbageCollection(); } logger.info('All caches cleared successfully.'); } catch (error) { logger.error({ err: error }, 'Failed to clear caches.'); } } export async function loadLanguageGrammar(extension, langConfig) { if (!grammarManager) { throw new Error('Parser not initialized. Call initializeParser() first.'); } try { await grammarManager.loadGrammar(extension); return true; } catch (error) { logger.error({ err: error, grammarName: langConfig.name, extension }, `Failed to load Tree-sitter grammar for ${langConfig.name}. Check if the grammar file exists.`); return false; } } export async function getParserForFileExtension(fileExtension) { if (!grammarManager) { logger.warn('Attempted to get parser before Tree-sitter initialization. Call initializeParser() first.'); await initializeParser(); if (!grammarManager) return null; } const langConfig = languageConfigurations[fileExtension]; if (!langConfig) { logger.warn(`No language configuration found for extension: ${fileExtension}. Cannot parse.`); return null; } try { return await grammarManager.getParserForExtensionWithMemoryAwareness(fileExtension); } catch (error) { logger.error({ err: error, fileExtension }, `Error getting parser for extension ${fileExtension}.`); return null; } } export async function getCachedValue(key, fileCache, memoryCache) { if (fileCache) { try { const value = await fileCache.get(key); if (value !== undefined) { logger.debug(`File cache hit for key: ${key}`); if (memoryCache) { memoryCache.set(key, value); } return value; } } catch (error) { logger.warn({ err: error, key }, `Error getting value from file cache, falling back to memory cache`); } } if (memoryCache) { const value = memoryCache.get(key); if (value !== undefined) { logger.debug(`Memory cache hit for key: ${key}`); return value; } } return undefined; } export async function setCachedValue(key, value, fileCache, memoryCache) { if (memoryCache) { memoryCache.set(key, value); } if (fileCache) { try { await fileCache.set(key, value); } catch (error) { logger.warn({ err: error, key }, `Error setting value in file cache`); } } } export async function parseCode(sourceCode, fileExtension, filePath, config) { let cacheKey = ''; if (filePath) { let fileHash; if (fileContentManager) { try { const metadata = await fileContentManager.getMetadata(filePath); if (metadata) { fileHash = metadata.hash; cacheKey = crypto.createHash('md5').update(`${filePath}:${fileExtension}:${fileHash}`).digest('hex'); } } catch (error) { logger.debug(`Error getting file metadata: ${error}`); } } if (!cacheKey) { cacheKey = crypto.createHash('md5').update(`${filePath}:${fileExtension}`).digest('hex'); } const useFileCaching = config?.cache?.enabled !== false && parseCache !== null; const useTieredCaching = config?.cache?.enabled !== false && config?.cache?.useMemoryCache === true && parseTreeTieredCache !== null; const useMemoryCaching = shouldUseMemoryCache(config); if (astMetadataCache && filePath) { try { const astMetadata = await astMetadataCache.get(cacheKey); if (astMetadata) { logger.debug(`Found AST metadata for ${filePath} in metadata cache`); let sourceHash = ''; if (sourceCodeMetadataCache) { const sourceMetadata = await sourceCodeMetadataCache.get(cacheKey); if (sourceMetadata) { sourceHash = sourceMetadata.hash; } } if (!sourceHash) { sourceHash = crypto.createHash('md5').update(sourceCode).digest('hex'); } if (sourceHash === astMetadata.sourceHash) { logger.debug(`Source code unchanged for ${filePath}, using cached AST metadata`); logger.debug(`Using AST metadata to optimize parsing for ${filePath}`); } } } catch (error) { logger.warn({ err: error, filePath }, 'Error checking AST metadata cache'); } } if (useTieredCaching) { const cachedTree = await parseTreeTieredCache.get(cacheKey); if (cachedTree) { logger.debug(`Using tiered cached parse tree for ${filePath}`); return cachedTree; } } else if ((useMemoryCaching && astMemoryCache) || (useFileCaching && parseCache)) { const cachedTree = await getCachedValue(cacheKey, useFileCaching ? parseCache : null, useMemoryCaching ? astMemoryCache : null); if (cachedTree) { logger.debug(`Using cached parse tree for ${filePath}`); return cachedTree; } } } const parser = await getParserForFileExtension(fileExtension); if (!parser) { return null; } try { const useIncrementalParsing = grammarManager?.getOptions().enableIncrementalParsing === true; const incrementalThreshold = grammarManager?.getOptions().incrementalParsingThreshold || 1024 * 1024; let tree; if (useIncrementalParsing && sourceCode.length > incrementalThreshold) { tree = await parseCodeIncrementally(parser, sourceCode); logger.debug({ fileExtension, fileSize: formatBytes(sourceCode.length), threshold: formatBytes(incrementalThreshold) }, `Used incremental parsing for large file with extension ${fileExtension}`); } else { if (!parser || typeof parser.parse !== 'function') { logger.error({ fileExtension }, `Parser is in invalid state for extension ${fileExtension}`); return null; } if (!parser.getLanguage || !parser.getLanguage()) { logger.error({ fileExtension }, `Parser language not set for extension ${fileExtension}`); return null; } tree = parser.parse(sourceCode); logger.debug(`Successfully parsed code for extension ${fileExtension}. Root node: ${tree.rootNode.type}`); } if (cacheKey) { const useFileCaching = config?.cache?.enabled !== false && parseCache !== null; const useTieredCaching = config?.cache?.enabled !== false && config?.cache?.useMemoryCache === true && parseTreeTieredCache !== null; const useMemoryCaching = shouldUseMemoryCache(config); if (astMetadataCache && filePath) { try { let sourceHash = ''; if (sourceCodeMetadataCache) { const sourceMetadata = await sourceCodeMetadataCache.get(cacheKey); if (sourceMetadata) { sourceHash = sourceMetadata.hash; } } if (!sourceHash) { sourceHash = crypto.createHash('md5').update(sourceCode).digest('hex'); } const astMetadata = MetadataCache.createASTMetadata(filePath, sourceHash, tree.rootNode); await astMetadataCache.set(cacheKey, astMetadata); logger.debug(`Cached AST metadata for ${filePath}`); } catch (error) { logger.warn({ err: error, filePath }, 'Error caching AST metadata'); } } if (useTieredCaching) { await parseTreeTieredCache.set(cacheKey, tree); logger.debug(`Cached parse tree in tiered cache for ${filePath}`); } else if ((useMemoryCaching && astMemoryCache) || (useFileCaching && parseCache)) { await setCachedValue(cacheKey, tree, useFileCaching ? parseCache : null, useMemoryCaching ? astMemoryCache : null); } } return tree; } catch (error) { const errorInfo = { err: error, fileExtension, parserState: 'null' }; if (parser) { const language = parser.getLanguage && parser.getLanguage(); errorInfo.parserState = { hasParseMethod: typeof parser.parse === 'function', hasLanguage: !!language, languageName: language ? language?.name || 'unknown' : 'unknown' }; } logger.error(errorInfo, `Error parsing code for extension ${fileExtension}.`); return null; } } export async function readAndParseFile(filePath, fileExtension, config) { try { let sourceCode; let metadata; if (sourceCodeMetadataCache) { const cacheKey = crypto.createHash('md5').update(filePath).digest('hex'); metadata = await sourceCodeMetadataCache.get(cacheKey); if (metadata) { logger.debug(`Found metadata for ${filePath} in metadata cache`); if (metadata.content) { logger.debug(`Using content from metadata cache for ${filePath}`); sourceCode = metadata.content; } else { if (fileContentManager) { sourceCode = await fileContentManager.getContent(filePath, config.allowedMappingDirectory); } else { sourceCode = await readFileSecure(filePath, config.allowedMappingDirectory); } metadata.content = sourceCode; await sourceCodeMetadataCache.set(cacheKey, metadata); } } else { if (fileContentManager) { sourceCode = await fileContentManager.getContent(filePath, config.allowedMappingDirectory); } else { sourceCode = await readFileSecure(filePath, config.allowedMappingDirectory); } metadata = await MetadataCache.createSourceCodeMetadata(filePath, sourceCode); await sourceCodeMetadataCache.set(cacheKey, metadata); } } else { if (fileContentManager) { sourceCode = await fileContentManager.getContent(filePath, config.allowedMappingDirectory); } else { sourceCode = await readFileSecure(filePath, config.allowedMappingDirectory); } } const tree = await parseCode(sourceCode, fileExtension, filePath, config); return { tree, sourceCode }; } catch (error) { logger.error({ err: error, filePath }, `Error reading or parsing file: ${filePath}`); return { tree: null, sourceCode: '' }; } } export function shouldUseMemoryCache(config) { if (config?.cache?.maxCachedFiles === 0) { logger.debug('In-memory caching is disabled via configuration (maxCachedFiles=0).'); return false; } if (!memoryManager) { return true; } const stats = memoryManager.getMemoryStats(); if (stats.formatted.memoryStatus === 'critical') { logger.warn('Memory usage is critical. Disabling memory caching.'); return false; } if (stats.formatted.memoryStatus === 'high') { if (config?.cache?.enabled !== false && parseCache !== null && sourceCodeCache !== null) { logger.warn('Memory usage is high. Using file-based caching only.'); return false; } logger.warn('Memory usage is high, but file-based caching is not available. Using memory caching with caution.'); return true; } return true; } export async function getSourceCodeFromCache(filePath, allowedDir) { if (fileContentManager) { try { if (allowedDir) { return await fileContentManager.getContent(filePath, allowedDir); } logger.warn({ filePath }, 'getSourceCodeFromCache called without allowedDir - security boundary cannot be validated'); return null; } catch (error) { logger.debug(`Error getting source code from FileContentManager: ${error}`); } } if (!sourceCodeMemoryCache) { return null; } const cacheKey = crypto.createHash('md5').update(filePath).digest('hex'); const cachedSourceCode = sourceCodeMemoryCache.get(cacheKey); if (cachedSourceCode) { return cachedSourceCode; } return null; } export function getAllSourceCodeFromCache() { const result = new Map(); if (fileContentManager) { logger.debug('FileContentManager does not currently support retrieving all entries'); } if (!sourceCodeMemoryCache) { return result; } const cachedEntries = sourceCodeMemoryCache.getAll(); logger.info(`Retrieved ${cachedEntries.size} source code entries from cache`); return cachedEntries; } async function parseCodeIncrementally(parser, sourceCode) { const initialChunkSize = 100 * 1024; const initialChunk = sourceCode.slice(0, Math.min(initialChunkSize, sourceCode.length)); let tree = parser.parse(initialChunk); if (sourceCode.length <= initialChunkSize) { return tree; } const chunkSize = 500 * 1024; const totalChunks = Math.ceil((sourceCode.length - initialChunkSize) / chunkSize); for (let i = 0; i < totalChunks; i++) { tree = parser.parse(sourceCode, tree); if (i % 5 === 0) { const memStats = getMemoryStats(); logger.debug({ chunk: i + 1, totalChunks, progress: `${Math.round(((i + 1) / totalChunks) * 100)}%`, memoryUsage: memStats.formatted.heapUsed }, `Incremental parsing progress`); if (global.gc && memStats.memoryUsagePercentage > 0.7) { global.gc(); } } } return tree; } export async function parseSourceCode(sourceCode, fileExtension) { if (!grammarManager) { await initializeParser(); } const tree = await parseCode(sourceCode, fileExtension); if (!tree) { throw new Error(`Failed to parse source code for extension ${fileExtension}`); } return { ast: tree.rootNode, language: fileExtension.replace('.', '') }; } export function cleanupParser() { if (astMemoryCache) { astMemoryCache.clear(); } if (sourceCodeMemoryCache) { sourceCodeMemoryCache.clear(); } if (fileContentManager) { fileContentManager.clearCache(); } if (memoryManager) { memoryManager.runGarbageCollection(); } if (processLifecycleManager) { processLifecycleManager.cleanup(); processLifecycleManager = null; } if (memoryLeakDetector) { memoryLeakDetector.cleanup(); memoryLeakDetector = null; } grammarManager = null; memoryManager = null; astMemoryCache = null; sourceCodeMemoryCache = null; parseCache = null; sourceCodeCache = null; parseTreeTieredCache = null; sourceCodeTieredCache = null; fileContentManager = null; logger.info('Parser cleaned up and resources released.'); } export function getMemoryManager() { return memoryManager; } export function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } export function getMemoryStats() { const memUsage = process.memoryUsage(); const systemTotal = os.totalmem(); return { heapUsed: memUsage.heapUsed, heapTotal: memUsage.heapTotal, rss: memUsage.rss, systemTotal, memoryUsagePercentage: memUsage.rss / systemTotal, formatted: { heapUsed: formatBytes(memUsage.heapUsed), heapTotal: formatBytes(memUsage.heapTotal), rss: formatBytes(memUsage.rss), systemTotal: formatBytes(systemTotal) } }; } export { ParserFromPackage as Parser };