Skip to main content
Glama

AutoDev Codebase MCP Server

by anrgct
RooIgnoreController.ts6.41 kB
import ignore, { Ignore } from "ignore" import { IFileWatcher, FileWatchEvent, IFileSystem } from "../abstractions/core" import { IWorkspace, IPathUtils } from "../abstractions/workspace" export const LOCK_TEXT_SYMBOL = "\u{1F512}" /** * Controls LLM access to files by enforcing ignore patterns. * Designed to be instantiated once in Cline.ts and passed to file manipulation services. * Uses the 'ignore' library to support standard .gitignore syntax in .rooignore files. */ export class RooIgnoreController { private ignoreInstance: Ignore private cleanupFunctions: (() => void)[] = [] private fileWatcher?: IFileWatcher private fileSystem: IFileSystem private workspace: IWorkspace private pathUtils: IPathUtils rooIgnoreContent: string | undefined constructor( fileSystem: IFileSystem, workspace: IWorkspace, pathUtils: IPathUtils, fileWatcher?: IFileWatcher ) { this.fileSystem = fileSystem this.workspace = workspace this.pathUtils = pathUtils this.ignoreInstance = ignore() this.rooIgnoreContent = undefined this.fileWatcher = fileWatcher // Set up file watcher for .rooignore if available if (this.fileWatcher) { this.setupFileWatcher() } } /** * Initialize the controller by loading custom patterns * Must be called after construction and before using the controller */ async initialize(): Promise<void> { await this.loadRooIgnore() } /** * Set up the file watcher for .rooignore changes */ private setupFileWatcher(): void { if (!this.fileWatcher) { return } const rootPath = this.workspace.getRootPath() if (!rootPath) { return } const rooignorePath = this.pathUtils.join(rootPath, ".rooignore") // Watch for changes to the .rooignore file const cleanup = this.fileWatcher.watchFile(rooignorePath, (event: FileWatchEvent) => { // Reload .rooignore on any file system event this.loadRooIgnore() }) this.cleanupFunctions.push(cleanup) } /** * Load custom patterns from .rooignore if it exists */ private async loadRooIgnore(): Promise<void> { try { // Reset ignore instance to prevent duplicate patterns this.ignoreInstance = ignore() const rootPath = this.workspace.getRootPath() if (!rootPath) { this.rooIgnoreContent = undefined return } const ignorePath = this.pathUtils.join(rootPath, ".rooignore") try { const buffer = await this.fileSystem.readFile(ignorePath) const content = new TextDecoder().decode(buffer) this.rooIgnoreContent = content this.ignoreInstance.add(content) this.ignoreInstance.add(".rooignore") } catch (fileError) { // File doesn't exist or can't be read this.rooIgnoreContent = undefined } } catch (error) { // Should never happen: reading file failed even though it exists console.error("Unexpected error loading .rooignore:", error) } } /** * Check if a file should be accessible to the LLM * @param filePath - Path to check (can be absolute or relative) * @returns true if file is accessible, false if ignored */ validateAccess(filePath: string): boolean { // Always allow access if .rooignore does not exist if (!this.rooIgnoreContent) { return true } try { // Get relative path using workspace abstraction const relativePath = this.workspace.getRelativePath(filePath) // Ignore expects paths to be relative and use forward slashes return !this.ignoreInstance.ignores(relativePath) } catch (error) { // console.error(`Error validating access for ${filePath}:`, error) // Ignore is designed to work with relative file paths, so will throw error for paths outside workspace. We are allowing access to all files outside workspace. return true } } /** * Check if a terminal command should be allowed to execute based on file access patterns * @param command - Terminal command to validate * @returns path of file that is being accessed if it is being accessed, undefined if command is allowed */ validateCommand(command: string): string | undefined { // Always allow if no .rooignore exists if (!this.rooIgnoreContent) { return undefined } // Split command into parts and get the base command const parts = command.trim().split(/\s+/) const baseCommand = parts[0].toLowerCase() // Commands that read file contents const fileReadingCommands = [ // Unix commands "cat", "less", "more", "head", "tail", "grep", "awk", "sed", // PowerShell commands and aliases "get-content", "gc", "type", "select-string", "sls", ] if (fileReadingCommands.includes(baseCommand)) { // Check each argument that could be a file path for (let i = 1; i < parts.length; i++) { const arg = parts[i] // Skip command flags/options (both Unix and PowerShell style) if (arg.startsWith("-") || arg.startsWith("/")) { continue } // Ignore PowerShell parameter names if (arg.includes(":")) { continue } // Validate file access if (!this.validateAccess(arg)) { return arg } } } return undefined } /** * Filter an array of paths, removing those that should be ignored * @param paths - Array of paths to filter (relative to cwd) * @returns Array of allowed paths */ filterPaths(paths: string[]): string[] { try { return paths .map((p) => ({ path: p, allowed: this.validateAccess(p), })) .filter((x) => x.allowed) .map((x) => x.path) } catch (error) { console.error("Error filtering paths:", error) return [] // Fail closed for security } } /** * Clean up resources when the controller is no longer needed */ dispose(): void { this.cleanupFunctions.forEach((cleanup) => cleanup()) this.cleanupFunctions = [] } /** * Get formatted instructions about the .rooignore file for the LLM * @returns Formatted instructions or undefined if .rooignore doesn't exist */ getInstructions(): string | undefined { if (!this.rooIgnoreContent) { return undefined } return `# .rooignore\n\n(The following is provided by a root-level .rooignore file where the user has specified files and directories that should not be accessed. When using list_files, you'll notice a ${LOCK_TEXT_SYMBOL} next to files that are blocked. Attempting to access the file's contents e.g. through read_file will result in an error.)\n\n${this.rooIgnoreContent}\n.rooignore` } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anrgct/autodev-codebase'

If you have feedback or need assistance with the MCP directory API, please join our Discord server