Skip to main content
Glama

AutoDev Codebase MCP Server

by anrgct
file-search.ts5.07 kB
// NOTICE: This file contains optional external dependencies // It's marked as optional integration - see plan.md Day 7 Task 1 // // External dependencies: vscode API, ripgrep binary, Node.js modules // For standalone usage, implement alternative search mechanisms using the abstract interfaces import * as path from "path" import * as fs from "fs" import * as childProcess from "child_process" import * as readline from "readline" import { byLengthAsc, Fzf } from "fzf" import { getBinPath as getRipgrepBinPath } from "../ripgrep" // For VSCode environments, vscode should be available as peer dependency let vscode: any try { vscode = require('vscode') } catch (e) { // VSCode not available - this is expected in standalone environments // console.warn('VSCode not available - search functionality will be limited') } // Use the proper ripgrep getBinPath implementation async function getBinPath(appRoot?: string): Promise<string | null> { const rgPath = await getRipgrepBinPath(undefined, appRoot) return rgPath || null } export type FileResult = { path: string; type: "file" | "folder"; label?: string } export async function executeRipgrep({ args, workspacePath, limit = 500, }: { args: string[] workspacePath: string limit?: number }): Promise<FileResult[]> { const rgPath = await getBinPath(vscode?.env?.appRoot) if (!rgPath) { throw new Error(`ripgrep not found: ${rgPath}`) } return new Promise((resolve, reject) => { const rgProcess = childProcess.spawn(rgPath, args) const rl = readline.createInterface({ input: rgProcess.stdout, crlfDelay: Infinity }) const fileResults: FileResult[] = [] const dirSet = new Set<string>() // Track unique directory paths. let count = 0 rl.on("line", (line) => { if (count < limit) { try { const relativePath = path.relative(workspacePath, line) // Add the file itself. fileResults.push({ path: relativePath, type: "file", label: path.basename(relativePath) }) // Extract and store all parent directory paths. let dirPath = path.dirname(relativePath) while (dirPath && dirPath !== "." && dirPath !== "/") { dirSet.add(dirPath) dirPath = path.dirname(dirPath) } count++ } catch (error) { // Silently ignore errors processing individual paths. } } else { rl.close() rgProcess.kill() } }) let errorOutput = "" rgProcess.stderr.on("data", (data) => { errorOutput += data.toString() }) rl.on("close", () => { if (errorOutput && fileResults.length === 0) { reject(new Error(`ripgrep process error: ${errorOutput}`)) } else { // Convert directory set to array of directory objects. const dirResults = Array.from(dirSet).map((dirPath) => ({ path: dirPath, type: "folder" as const, label: path.basename(dirPath), })) // Combine files and directories and resolve. resolve([...fileResults, ...dirResults]) } }) rgProcess.on("error", (error) => { reject(new Error(`ripgrep process error: ${error.message}`)) }) }) } export async function executeRipgrepForFiles( workspacePath: string, limit: number = 5000, ): Promise<{ path: string; type: "file" | "folder"; label?: string }[]> { const args = [ "--files", "--follow", "--hidden", "-g", "!**/node_modules/**", "-g", "!**/.git/**", "-g", "!**/out/**", "-g", "!**/dist/**", workspacePath, ] return executeRipgrep({ args, workspacePath, limit }) } export async function searchWorkspaceFiles( query: string, workspacePath: string, limit: number = 20, ): Promise<{ path: string; type: "file" | "folder"; label?: string }[]> { try { // Get all files and directories (from our modified function) const allItems = await executeRipgrepForFiles(workspacePath, 5000) // If no query, just return the top items if (!query.trim()) { return allItems.slice(0, limit) } // Create search items for all files AND directories const searchItems = allItems.map((item) => ({ original: item, searchStr: `${item.path} ${item.label || ""}`, })) // Run fzf search on all items const fzf = new Fzf(searchItems, { selector: (item) => item.searchStr, tiebreakers: [byLengthAsc], limit: limit, }) // Get all matching results from fzf const fzfResults = fzf.find(query).map((result) => result.item.original) // Verify types of the shortest results const verifiedResults = await Promise.all( fzfResults.map(async (result) => { const fullPath = path.join(workspacePath, result.path) // Verify if the path exists and is actually a directory if (fs.existsSync(fullPath)) { const isDirectory = fs.lstatSync(fullPath).isDirectory() return { ...result, path: result.path.replace(/\\/g, '/'), // Convert to POSIX path type: isDirectory ? ("folder" as const) : ("file" as const), } } // If path doesn't exist, keep original type return result }), ) return verifiedResults } catch (error) { console.error("Error in searchWorkspaceFiles:", error) return [] } }

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