Problem Description
FileTools.search_content today has several limitations that make it unsuitable for anything beyond trivial lookups:
- Substring-only matching — no regex. An agent can't search for
r"def \w+", r"class [A-Z]", or r"TODO.*priority".
- Pure Python — walks the tree, opens each file, scans line by line. Slow on monorepos.
- Hardcoded file types — only files with an extension in
TEXT_EXTENSIONS are searched. .proto, .tf, .nix, .go, and everything else is invisible.
- Snippet-based output — returns file path + size + a character snippet around the first match. Useful for a human reader, not for an agent that wants structured per-line hits it can reason about.
Claude Code, Cursor, and every modern coding assistant reach for ripgrep as the search primitive. Agno should too, with a pure-Python fallback for users who don't have rg installed so the tool remains zero-dependency.
Proposed Solution
Add grep to FileTools with the following shape:
def grep(
self,
pattern: str,
path: Optional[str] = None,
output_mode: Literal["files_with_matches", "content", "count"] = "files_with_matches",
include: Optional[str] = None,
ignore_case: bool = False,
context: int = 0,
limit: int = 250,
multiline: bool = False,
) -> str
Key properties:
- ripgrep when available, Python
re fallback otherwise. shutil.which("rg") resolved once at __init__; the JSON result shape is identical across backends so callers never special-case.
- Three output modes.
files_with_matches (default, paths only), content (per-line hits with line numbers), count (per-file totals). Gives the agent an explicit verbosity knob.
include glob — filename filter like "*.py" or "**/*.tsx".
multiline — pattern can cross line boundaries when needed (ripgrep -U --multiline-dotall, Python re.DOTALL).
- Hard cap at
GREP_MAX_LIMIT=1000 — protects the agent's context from accidental blow-up; docstring instructs the agent to narrow the search when truncated: true rather than raise limit.
search_content stays untouched — grep is additive, not a replacement.
Parameter names (ignore_case, include, context, limit) are aligned with the existing CodingTools.grep for consistent vocabulary.
Alternatives Considered
- Extend
search_content with use_regex=True flag plus output mode flags. Rejected: clutter of flags on a method whose name doesn't promise regex. Cleaner to add grep as a sibling.
- Hard ripgrep dependency. Rejected: adds a non-Python dependency to what is today a zero-dep toolkit. The fallback path keeps Agno installable without any binary.
- Use
fff.nvim or similar. Rejected: no Python binding, no standalone CLI, requires Neovim.
Additional Context
Implementation and test coverage in #7642.
Would you like to work on this?
Problem Description
FileTools.search_contenttoday has several limitations that make it unsuitable for anything beyond trivial lookups:r"def \w+",r"class [A-Z]", orr"TODO.*priority".TEXT_EXTENSIONSare searched..proto,.tf,.nix,.go, and everything else is invisible.Claude Code, Cursor, and every modern coding assistant reach for ripgrep as the search primitive. Agno should too, with a pure-Python fallback for users who don't have
rginstalled so the tool remains zero-dependency.Proposed Solution
Add
greptoFileToolswith the following shape:Key properties:
refallback otherwise.shutil.which("rg")resolved once at__init__; the JSON result shape is identical across backends so callers never special-case.files_with_matches(default, paths only),content(per-line hits with line numbers),count(per-file totals). Gives the agent an explicit verbosity knob.includeglob — filename filter like"*.py"or"**/*.tsx".multiline— pattern can cross line boundaries when needed (ripgrep-U --multiline-dotall, Pythonre.DOTALL).GREP_MAX_LIMIT=1000— protects the agent's context from accidental blow-up; docstring instructs the agent to narrow the search whentruncated: truerather than raiselimit.search_contentstays untouched —grepis additive, not a replacement.Parameter names (
ignore_case,include,context,limit) are aligned with the existingCodingTools.grepfor consistent vocabulary.Alternatives Considered
search_contentwithuse_regex=Trueflag plus output mode flags. Rejected: clutter of flags on a method whose name doesn't promise regex. Cleaner to addgrepas a sibling.fff.nvimor similar. Rejected: no Python binding, no standalone CLI, requires Neovim.Additional Context
Implementation and test coverage in #7642.
Would you like to work on this?