feat: Add file editing tools with anchor validation and improve context compaction by redacting stale ReadFile results based on deduplication and time-to-live.
This commit is contained in:
@@ -14,14 +14,14 @@ internal static class FileTools
|
||||
{
|
||||
public static Action<string> Log { get; set; } = Console.WriteLine;
|
||||
|
||||
[Description("Read a file and return its lines tagged with Hashline anchors in the format lineNumber:hash|content. Optionally restrict to a line window.")]
|
||||
[Description("Read a file. Max 200 lines per call. Returns lines with line:hash| anchors.")]
|
||||
public static string ReadFile(
|
||||
[Description("Path to the file to read. Can be relative to the working directory or absolute.")] string path,
|
||||
[Description("First line to return, 1-indexed inclusive. Defaults to 1.")] int startLine = 1,
|
||||
[Description("Last line to return, 1-indexed inclusive. Use 0 for end of file. Defaults to 0.")] int endLine = 0)
|
||||
[Description("Path to the file.")] string path,
|
||||
[Description("First line to return (inclusive). Defaults to 1.")] int startLine = 1,
|
||||
[Description("Last line to return (inclusive). Use 0 for EOF. Defaults to 0.")] int endLine = 0)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
Log($"Reading file: {path}");
|
||||
Log($"Reading file: {path} {startLine}:{endLine}L");
|
||||
|
||||
if (!File.Exists(path))
|
||||
return $"ERROR: File not found: {path}";
|
||||
@@ -33,6 +33,13 @@ internal static class FileTools
|
||||
if (lines.Length == 0)
|
||||
return $"(empty file: {path})";
|
||||
|
||||
int actualEnd = endLine <= 0 ? lines.Length : Math.Min(endLine, lines.Length);
|
||||
int start = Math.Max(1, startLine);
|
||||
if (actualEnd - start + 1 > 200)
|
||||
{
|
||||
return $"ERROR: File too large to read at once ({lines.Length} lines). Provide startLine and endLine to read a chunk of max 200 lines. Use GrepFile to get an outline (grep 'public') and find the line numbers.";
|
||||
}
|
||||
|
||||
return HashlineEncoder.Encode(lines, startLine, endLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -41,10 +48,10 @@ internal static class FileTools
|
||||
}
|
||||
}
|
||||
|
||||
[Description("Search a file for lines matching a regex pattern. Returns only matching lines, already tagged with Hashline anchors so you can reference them in edit operations immediately.")]
|
||||
[Description("Search a file for a regex pattern. Returns matches with line:hash| anchors.")]
|
||||
public static string GrepFile(
|
||||
[Description("Path to the file to search.")] string path,
|
||||
[Description("Regular expression pattern to search for.")] string pattern)
|
||||
[Description("Regex pattern.")] string pattern)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
Log($"Searching file: {path}");
|
||||
@@ -90,9 +97,9 @@ internal static class FileTools
|
||||
}
|
||||
}
|
||||
|
||||
[Description("List the files and subdirectories in a directory.")]
|
||||
[Description("List files and subdirectories.")]
|
||||
public static string ListDir(
|
||||
[Description("Path to the directory to list. Defaults to the current working directory.")] string path = ".")
|
||||
[Description("Path to the directory.")] string path = ".")
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
Log($"Listing directory: {path}");
|
||||
@@ -122,10 +129,10 @@ internal static class FileTools
|
||||
}
|
||||
}
|
||||
|
||||
[Description("Search for files matching a glob/wildcard pattern (e.g., '*.cs', 'src/**/*.js'). Returns full paths of matching files.")]
|
||||
[Description("Find files matching a glob pattern (e.g. '*.cs', '**/*.json').")]
|
||||
public static string FindFiles(
|
||||
[Description("Path to start the search (directory).")] string path,
|
||||
[Description("Glob pattern to match files (e.g., '*.cs', '**/*.json'). Supports * and ** wildcards.")] string pattern)
|
||||
[Description("Directory to start search.")] string path,
|
||||
[Description("Glob pattern (supports * and **).")] string pattern)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
Log($"Finding files: {pattern} in {path}");
|
||||
@@ -159,11 +166,11 @@ internal static class FileTools
|
||||
}
|
||||
}
|
||||
|
||||
[Description("Search for a regex pattern across all files in a directory tree. Returns matches with file:line:hash|content format.")]
|
||||
[Description("Recursive regex search across all files. Returns matches with file:line:hash| format.")]
|
||||
public static string GrepRecursive(
|
||||
[Description("Path to the directory to search recursively.")] string path,
|
||||
[Description("Regular expression pattern to search for.")] string pattern,
|
||||
[Description("Optional glob pattern to filter which files to search (e.g., '*.cs'). Defaults to all files.")] string? filePattern = null)
|
||||
[Description("Directory to search.")] string path,
|
||||
[Description("Regex pattern.")] string pattern,
|
||||
[Description("Optional glob to filter files (e.g. '*.cs').")] string? filePattern = null)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
Log($"Recursive grep: {pattern} in {path}" + (filePattern != null ? $" (files: {filePattern})" : ""));
|
||||
@@ -264,9 +271,9 @@ internal static class FileTools
|
||||
}
|
||||
}
|
||||
|
||||
[Description("Get detailed information about a file (size, permissions, last modified, type, etc.).")]
|
||||
[Description("Get detailed file info (size, last modified, etc).")]
|
||||
public static string GetFileInfo(
|
||||
[Description("Path to the file to get information about.")] string path)
|
||||
[Description("Path to the file.")] string path)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
Log($"Getting file info: {path}");
|
||||
|
||||
Reference in New Issue
Block a user