UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

141 lines (112 loc) 4.17 kB
import { promises as fs } from 'fs'; import path from 'path'; export interface FileSystemAdapter { readdir(dirPath: string): Promise<any[]>; stat(filePath: string): Promise<{ isDirectory(): boolean }>; access(filePath: string): Promise<void>; readFile(filePath: string, encoding: string): Promise<string>; writeFile(filePath: string, content: string): Promise<void>; } export class NodeFileSystemAdapter implements FileSystemAdapter { async readdir(dirPath: string): Promise<any[]> { return await fs.readdir(dirPath, { withFileTypes: true }); } async stat(filePath: string): Promise<{ isDirectory(): boolean }> { return await fs.stat(filePath); } async access(filePath: string): Promise<void> { await fs.access(filePath); } async readFile(filePath: string, encoding: string): Promise<string> { return await fs.readFile(filePath, { encoding: encoding as BufferEncoding }); } async writeFile(filePath: string, content: string): Promise<void> { await fs.writeFile(filePath, content); } } export class InMemoryFileSystemAdapter implements FileSystemAdapter { private directories: Map<string, any[]> = new Map(); private files: Map<string, string> = new Map(); private dirStats: Set<string> = new Set(); constructor() { // Initialize with root directory this.directories.set('/', []); this.dirStats.add('/'); } async readdir(dirPath: string): Promise<any[]> { const normalizedPath = path.normalize(dirPath); const entries = this.directories.get(normalizedPath); if (!entries) { throw new Error(`ENOENT: no such file or directory, scandir '${dirPath}'`); } return entries; } async stat(filePath: string): Promise<{ isDirectory(): boolean }> { const normalizedPath = path.normalize(filePath); return { isDirectory: () => this.dirStats.has(normalizedPath) }; } async access(filePath: string): Promise<void> { const normalizedPath = path.normalize(filePath); if (!this.files.has(normalizedPath) && !this.dirStats.has(normalizedPath)) { throw new Error(`ENOENT: no such file or directory, access '${filePath}'`); } } async readFile(filePath: string, encoding: string): Promise<string> { const normalizedPath = path.normalize(filePath); const content = this.files.get(normalizedPath); if (content === undefined) { throw new Error(`ENOENT: no such file or directory, open '${filePath}'`); } return content; } async writeFile(filePath: string, content: string): Promise<void> { const normalizedPath = path.normalize(filePath); this.files.set(normalizedPath, content); } // Test helper methods addDirectory(dirPath: string, entries: Array<{ name: string; isDirectory: boolean }>): void { const normalizedPath = path.normalize(dirPath); // Add the directory itself this.dirStats.add(normalizedPath); // Create mock directory entries const mockEntries = entries.map(entry => ({ name: entry.name, isDirectory: () => entry.isDirectory })); this.directories.set(normalizedPath, mockEntries); // Add subdirectories to dirStats entries.forEach(entry => { if (entry.isDirectory) { const subPath = path.join(normalizedPath, entry.name); this.dirStats.add(subPath); } }); } addGitDirectory(dirPath: string): void { const normalizedPath = path.normalize(dirPath); const gitPath = path.join(normalizedPath, '.git'); // Mark the .git path as accessible this.dirStats.add(gitPath); // Also update parent directory entries if it exists const parentEntries = this.directories.get(normalizedPath); if (parentEntries) { const hasGit = parentEntries.some(e => e.name === '.git'); if (!hasGit) { parentEntries.push({ name: '.git', isDirectory: () => true }); } } } clear(): void { this.directories.clear(); this.files.clear(); this.dirStats.clear(); // Re-initialize with root this.directories.set('/', []); this.dirStats.add('/'); } }