Fix MCP file discovery errors (#9)

* fix tool errors

*QA
This commit is contained in:
Shawn Morreau
2025-12-17 11:29:27 -05:00
committed by GitHub
parent a88b3d18ce
commit 6716183068
7 changed files with 6909 additions and 7559 deletions
+65 -15
View File
@@ -1,6 +1,7 @@
import { relative, resolve } from "node:path";
import { type } from "arktype";
import { $ } from "../utils/shell.ts";
import { handleToolError, handleToolSuccess, tool, type ToolResult } from "./shared.ts";
import { handleToolError, handleToolSuccess, type ToolResult, tool } from "./shared.ts";
export const ListFiles = type({
path: type.string
@@ -12,32 +13,81 @@ export const ListFiles = type({
export const ListFilesTool = tool({
name: "list_files",
description:
"List files in the repository using git ls-files. Useful for discovering the file structure and locating files.",
"List files in the repository, including both git-tracked and untracked files. Useful for discovering the file structure and locating files, including newly created files that haven't been committed yet.",
parameters: ListFiles,
execute: async ({ path }: { path?: string }): Promise<ToolResult> => {
try {
// Use git ls-files to list tracked files
// This respects .gitignore and gives a clean list of source files
const pathStr = path ?? ".";
const output = $("git", pathStr === "." ? ["ls-files"] : ["ls-files", pathStr], {
log: false,
});
const files = output.split("\n").filter((f) => f.trim() !== "");
const cwd = process.cwd();
if (files.length === 0) {
// Fallback for non-git environments or untracked files
// Get git-tracked files
let gitFiles: string[] = [];
let gitFailed = false;
try {
const gitArgs = pathStr === "." ? ["ls-files"] : ["ls-files", pathStr];
const gitOutput = $("git", gitArgs, { log: false });
gitFiles = gitOutput
.split("\n")
.filter((f) => f.trim() !== "")
.map((f) => f.trim());
} catch {
// git might fail, that's ok - we'll use find instead
gitFailed = true;
}
// Always also check filesystem for untracked files
// This is important because newly created files won't be in git yet
let filesystemFiles: string[] = [];
let findFailed = false;
try {
const findOutput = $(
"find",
[pathStr, "-maxdepth", "3", "-not", "-path", "*/.*", "-type", "f"],
{ log: false }
);
return handleToolSuccess({
files: findOutput.split("\n").filter((f) => f.trim() !== ""),
method: "find",
});
filesystemFiles = findOutput
.split("\n")
.filter((f) => f.trim() !== "")
.map((f) => {
const trimmed = f.trim();
// normalize to relative paths for comparison
try {
return relative(cwd, resolve(cwd, trimmed));
} catch {
return trimmed;
}
});
} catch {
// find might fail, that's ok - we'll just use git files
findFailed = true;
}
return handleToolSuccess({ files, method: "git" });
// if both methods failed, return an error
if (gitFailed && findFailed) {
return handleToolError(
new Error(
`Failed to list files: both git ls-files and find commands failed. ` +
`Path: ${pathStr}, working directory: ${cwd}`
)
);
}
// Create a Set of git files for efficient lookup
const gitFilesSet = new Set(gitFiles);
// Combine both lists, removing duplicates
const allFiles = [...new Set([...gitFiles, ...filesystemFiles])].sort();
// Calculate actual untracked count (files in filesystem but not in git)
const untrackedFiles = filesystemFiles.filter((f) => !gitFilesSet.has(f));
const untrackedCount = untrackedFiles.length;
return handleToolSuccess({
files: allFiles,
method: "combined",
trackedCount: gitFiles.length,
untrackedCount,
});
} catch (error) {
return handleToolError(error);
}