From 8a9bc284a03c6da58c9a2e88f42ba30d03d815b9 Mon Sep 17 00:00:00 2001 From: afjcjsbx Date: Sat, 28 Mar 2026 09:47:31 +0100 Subject: [PATCH] enhanced infos --- pkg/tools/filesystem.go | 27 ++++++++++++++----- pkg/tools/filesystem_test.go | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/pkg/tools/filesystem.go b/pkg/tools/filesystem.go index fc8ab7784..c206e8ad2 100644 --- a/pkg/tools/filesystem.go +++ b/pkg/tools/filesystem.go @@ -534,6 +534,12 @@ func (t *ReadFileLinesTool) Execute(ctx context.Context, args map[string]any) *T if startLine < 1 { return ErrorResult("start_line must be >= 1") } + if _, exists := args["offset"]; exists { + return ErrorResult("offset is not supported in line mode; use start_line") + } + if _, exists := args["length"]; exists { + return ErrorResult("length is not supported in line mode; use max_lines") + } limit := int64(-1) if raw, exists := args["max_lines"]; exists && raw != nil { @@ -644,19 +650,28 @@ func (t *ReadFileLinesTool) Execute(ctx context.Context, args map[string]any) *T switch { case lineTruncated: header += fmt.Sprintf( - "\n[TRUNCATED - line %d exceeded the %d byte read budget and was cut mid-line.", + "\n[TRUNCATED - line %d exceeded the %d byte read budget and was cut mid-line.]", endLine, t.maxSize, ) case byteBudgetTruncated: - header += fmt.Sprintf( - "\n[TRUNCATED - byte budget reached. Call read_file again with start_line=%d to continue at the next line.]", - startLine+linesRead, - ) + if limit > 0 { + header += fmt.Sprintf( + "\n[TRUNCATED - byte budget reached. Call read_file again with start_line=%d and max_lines=%d to continue at the next line.]", + startLine+linesRead, + limit, + ) + } else { + header += fmt.Sprintf( + "\n[TRUNCATED - byte budget reached. Call read_file again with start_line=%d to continue at the next line.]", + startLine+linesRead, + ) + } case !reachedEOF && limit > 0 && linesRead >= limit: header += fmt.Sprintf( - "\n[PARTIAL - more content remains. Call read_file again with start_line=%d to continue.]", + "\n[PARTIAL - more content remains. Call read_file again with start_line=%d and max_lines=%d to continue.]", startLine+linesRead, + limit, ) default: header += "\n[END OF FILE - no further content.]" diff --git a/pkg/tools/filesystem_test.go b/pkg/tools/filesystem_test.go index 0bc6b9edc..0249bdd86 100644 --- a/pkg/tools/filesystem_test.go +++ b/pkg/tools/filesystem_test.go @@ -897,6 +897,9 @@ func TestReadFileLinesTool_ChunkedReading(t *testing.T) { if !strings.Contains(result1.ForLLM, "start_line=3") { t.Fatalf("expected continuation start_line=3, got: %s", result1.ForLLM) } + if !strings.Contains(result1.ForLLM, "max_lines=2") { + t.Fatalf("expected continuation max_lines=2, got: %s", result1.ForLLM) + } result2 := tool.Execute(context.Background(), map[string]any{ "path": testFile, @@ -912,6 +915,9 @@ func TestReadFileLinesTool_ChunkedReading(t *testing.T) { if !strings.Contains(result2.ForLLM, "start_line=5") { t.Fatalf("expected continuation start_line=5, got: %s", result2.ForLLM) } + if !strings.Contains(result2.ForLLM, "max_lines=2") { + t.Fatalf("expected continuation max_lines=2, got: %s", result2.ForLLM) + } result3 := tool.Execute(context.Background(), map[string]any{ "path": testFile, @@ -1042,6 +1048,52 @@ func TestReadFileLinesTool_RegistryValidationSupportsMaxLinesAndRejectsLimit(t * } } +func TestReadFileLinesTool_RejectsOffset(t *testing.T) { + tmpDir := t.TempDir() + testFile := filepath.Join(tmpDir, "legacy_offset.txt") + + err := os.WriteFile(testFile, []byte("line 1\nline 2\n"), 0o644) + if err != nil { + t.Fatalf("Failed to write test file: %v", err) + } + + tool := NewReadFileLinesTool(tmpDir, false, MaxReadFileSize) + result := tool.Execute(context.Background(), map[string]any{ + "path": testFile, + "start_line": 1, + "offset": 1, + }) + if !result.IsError { + t.Fatalf("expected offset to be rejected, got success: %s", result.ForLLM) + } + if !strings.Contains(result.ForLLM, "offset is not supported in line mode; use start_line") { + t.Fatalf("unexpected error for offset in line mode: %s", result.ForLLM) + } +} + +func TestReadFileLinesTool_RejectsLength(t *testing.T) { + tmpDir := t.TempDir() + testFile := filepath.Join(tmpDir, "legacy_length.txt") + + err := os.WriteFile(testFile, []byte("line 1\nline 2\n"), 0o644) + if err != nil { + t.Fatalf("Failed to write test file: %v", err) + } + + tool := NewReadFileLinesTool(tmpDir, false, MaxReadFileSize) + result := tool.Execute(context.Background(), map[string]any{ + "path": testFile, + "start_line": 1, + "length": 1, + }) + if !result.IsError { + t.Fatalf("expected length to be rejected, got success: %s", result.ForLLM) + } + if !strings.Contains(result.ForLLM, "length is not supported in line mode; use max_lines") { + t.Fatalf("unexpected error for length in line mode: %s", result.ForLLM) + } +} + func TestReadFileLinesTool_BinaryFileRejected(t *testing.T) { tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "binary.dat")