Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/fastcontext/agent/tool/grep.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class GrepTool(Tool):
"head_limit": {
"type": "number",
"minimum": 0,
"description": 'Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). When unspecified, shows all results from ripgrep.',
"description": 'Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). When unspecified, results are capped at the first 100 lines.',
},
"multiline": {
"type": "boolean",
Expand Down Expand Up @@ -104,9 +104,8 @@ async def call(self, parameters: str, **kwargs) -> str:
return "No matches found"

limit = 100
if head_limit is not None:
if head_limit < limit and head_limit > 0:
limit = head_limit
if head_limit is not None and head_limit > 0:
limit = head_limit

lines = output.splitlines()
if len(lines) > limit:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_tool.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
import asyncio
import json
import tempfile
from pathlib import Path

from fastcontext.agent.tool.glob import GlobTool
from fastcontext.agent.tool.grep import GrepTool
from fastcontext.agent.tool.read import ReadTool


def test_grep_head_limit():
"""head_limit must be honored for values above the default 100-line cap,
and fall back to 100 when unspecified."""
grep = GrepTool()
with tempfile.TemporaryDirectory() as cwd:
# 250 matching lines, single file, so output exceeds any tested limit.
(Path(cwd) / "haystack.txt").write_text("\n".join("MATCH" for _ in range(250)), encoding="utf-8")

def lines_for(params):
out = asyncio.run(grep.call(json.dumps(params), cwd=cwd))
return out.splitlines()

# Above the default cap: previously clamped to 100, must now be honored.
# Truncated output = first `limit` lines + one "Results truncated" note.
out = lines_for({"pattern": "MATCH", "output_mode": "content", "head_limit": 150})
assert len(out) == 151, f"expected 150 lines + note, got {len(out)}"
assert "truncated to first 150" in out[-1]

# Unspecified: 100-line default cap still applies.
out = lines_for({"pattern": "MATCH", "output_mode": "content"})
assert len(out) == 101, f"expected 100 lines + note, got {len(out)}"
assert "truncated to first 100" in out[-1]


def test_grep_tool():
grep = GrepTool()
params = {
Expand Down