UNPKG

@wildcard-ai/deepcodex

Version:

Advanced codebase indexing and semantic search MCP server

228 lines 7.07 kB
/** * FileUtils - File system operations and discovery */ import * as fs from 'fs/promises'; import * as path from 'path'; export class FileUtils { SUPPORTED_EXTENSIONS = new Map([ ['typescript', ['.ts', '.tsx']], ['javascript', ['.js', '.jsx', '.mjs', '.cjs']], ['python', ['.py', '.pyx', '.pyi']], ['java', ['.java']], ['cpp', ['.cpp', '.cxx', '.cc', '.c', '.h', '.hpp', '.hxx']], ['go', ['.go']], ['rust', ['.rs']], ['csharp', ['.cs']], ['php', ['.php']], ['ruby', ['.rb']], ['swift', ['.swift']], ['kotlin', ['.kt', '.kts']] ]); IGNORE_DIRECTORIES = new Set([ 'node_modules', '.git', '.svn', '.hg', 'dist', 'build', 'out', 'target', 'bin', 'obj', '__pycache__', '.pytest_cache', '.mypy_cache', '.tox', 'venv', 'env', '.env', '.next', '.nuxt', 'coverage', '.nyc_output', 'logs', 'tmp', 'temp', '.DS_Store', 'Thumbs.db' ]); IGNORE_FILES = new Set([ '.gitignore', '.dockerignore', '.eslintignore', '.prettierignore', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'Cargo.lock', 'Pipfile.lock', 'poetry.lock' ]); /** * Discover all supported files in a codebase directory */ async discoverFiles(codebasePath, supportedLanguages) { const files = []; const allowedExtensions = this.getExtensionsForLanguages(supportedLanguages); await this.traverseDirectory(codebasePath, codebasePath, files, allowedExtensions); return files.sort(); // Consistent ordering } /** * Recursively traverse directory structure */ async traverseDirectory(currentPath, basePath, files, allowedExtensions, depth = 0) { // Prevent infinite recursion and very deep structures if (depth > 20) { console.warn(`[FileUtils] Maximum depth reached for: ${currentPath}`); return; } try { const entries = await fs.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); if (entry.isDirectory()) { // Skip ignored directories if (!this.IGNORE_DIRECTORIES.has(entry.name) && !entry.name.startsWith('.')) { await this.traverseDirectory(fullPath, basePath, files, allowedExtensions, depth + 1); } } else if (entry.isFile()) { // Check if file should be included if (this.shouldIncludeFile(entry.name, allowedExtensions)) { files.push(fullPath); } } } } catch (error) { console.warn(`[FileUtils] Error reading directory ${currentPath}: ${error}`); } } /** * Check if a file should be included based on extension and name */ shouldIncludeFile(fileName, allowedExtensions) { // Skip ignored files if (this.IGNORE_FILES.has(fileName)) { return false; } // Skip hidden files and common patterns if (fileName.startsWith('.') || fileName.endsWith('.min.js') || fileName.endsWith('.map') || fileName.includes('.generated.') || fileName.includes('.gen.')) { return false; } // Check extension const ext = path.extname(fileName).toLowerCase(); return allowedExtensions.has(ext); } /** * Get file extensions for specified languages */ getExtensionsForLanguages(languages) { if (!languages || languages.length === 0) { // Return all supported extensions const allExtensions = new Set(); for (const exts of this.SUPPORTED_EXTENSIONS.values()) { exts.forEach(ext => allExtensions.add(ext)); } return allExtensions; } const extensions = new Set(); for (const language of languages) { const langExtensions = this.SUPPORTED_EXTENSIONS.get(language.toLowerCase()); if (langExtensions) { langExtensions.forEach(ext => extensions.add(ext)); } } return extensions; } /** * Get file statistics */ async getFileStats(filePath) { try { const stats = await fs.stat(filePath); return { size: stats.size, modified: stats.mtime, created: stats.birthtime }; } catch (error) { return null; } } /** * Check if file has been modified since a given timestamp */ async isFileModifiedSince(filePath, timestamp) { const stats = await this.getFileStats(filePath); return stats ? stats.modified > timestamp : true; } /** * Get relative path with normalized separators */ getRelativePath(filePath, basePath) { return path.relative(basePath, filePath).replace(/\\/g, '/'); } /** * Ensure directory exists */ async ensureDirectory(dirPath) { try { await fs.mkdir(dirPath, { recursive: true }); } catch (error) { console.warn(`[FileUtils] Failed to create directory ${dirPath}: ${error}`); } } /** * Read file with error handling */ async readFileContent(filePath) { try { return await fs.readFile(filePath, 'utf-8'); } catch (error) { console.warn(`[FileUtils] Failed to read file ${filePath}: ${error}`); return null; } } /** * Write file with directory creation */ async writeFileContent(filePath, content) { try { const dir = path.dirname(filePath); await this.ensureDirectory(dir); await fs.writeFile(filePath, content, 'utf-8'); return true; } catch (error) { console.warn(`[FileUtils] Failed to write file ${filePath}: ${error}`); return false; } } /** * Get supported languages for a file extension */ getLanguageForExtension(extension) { const normalizedExt = extension.toLowerCase(); for (const [language, extensions] of this.SUPPORTED_EXTENSIONS.entries()) { if (extensions.includes(normalizedExt)) { return language; } } return null; } /** * Get all supported languages */ getSupportedLanguages() { return Array.from(this.SUPPORTED_EXTENSIONS.keys()); } } //# sourceMappingURL=FileUtils.js.map