diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index 5aded17a7..1ecf814fd 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -77,6 +77,7 @@ from crewai_tools.tools.generate_crewai_automation_tool.generate_crewai_automati GenerateCrewaiAutomationTool, ) from crewai_tools.tools.github_search_tool.github_search_tool import GithubSearchTool +from crewai_tools.tools.grep_tool.grep_tool import GrepTool from crewai_tools.tools.hyperbrowser_load_tool.hyperbrowser_load_tool import ( HyperbrowserLoadTool, ) @@ -230,6 +231,7 @@ __all__ = [ "FirecrawlSearchTool", "GenerateCrewaiAutomationTool", "GithubSearchTool", + "GrepTool", "HyperbrowserLoadTool", "InvokeCrewAIAutomationTool", "JSONSearchTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/__init__.py index 51d32ddc2..1dfc614c5 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/tools/__init__.py @@ -66,6 +66,7 @@ from crewai_tools.tools.generate_crewai_automation_tool.generate_crewai_automati GenerateCrewaiAutomationTool, ) from crewai_tools.tools.github_search_tool.github_search_tool import GithubSearchTool +from crewai_tools.tools.grep_tool.grep_tool import GrepTool from crewai_tools.tools.hyperbrowser_load_tool.hyperbrowser_load_tool import ( HyperbrowserLoadTool, ) @@ -214,6 +215,7 @@ __all__ = [ "FirecrawlSearchTool", "GenerateCrewaiAutomationTool", "GithubSearchTool", + "GrepTool", "HyperbrowserLoadTool", "InvokeCrewAIAutomationTool", "JSONSearchTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/grep_tool/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/grep_tool/__init__.py new file mode 100644 index 000000000..bd5d132f6 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/grep_tool/__init__.py @@ -0,0 +1,3 @@ +from crewai_tools.tools.grep_tool.grep_tool import GrepTool + +__all__ = ["GrepTool"] diff --git a/lib/crewai/src/crewai/tools/agent_tools/grep_tool.py b/lib/crewai-tools/src/crewai_tools/tools/grep_tool/grep_tool.py similarity index 88% rename from lib/crewai/src/crewai/tools/agent_tools/grep_tool.py rename to lib/crewai-tools/src/crewai_tools/tools/grep_tool/grep_tool.py index 56a45b38e..1b863f69e 100644 --- a/lib/crewai/src/crewai/tools/agent_tools/grep_tool.py +++ b/lib/crewai-tools/src/crewai_tools/tools/grep_tool/grep_tool.py @@ -2,33 +2,33 @@ from __future__ import annotations -from dataclasses import dataclass, field import os -from pathlib import Path import re +from dataclasses import dataclass, field +from pathlib import Path from typing import Literal +from crewai.tools import BaseTool from pydantic import BaseModel, Field -from crewai.tools.base_tool import BaseTool - - MAX_OUTPUT_CHARS = 50_000 MAX_FILES = 10_000 MAX_MATCHES_PER_FILE = 200 MAX_LINE_LENGTH = 500 BINARY_CHECK_SIZE = 8192 -SKIP_DIRS = frozenset({ - ".git", - "__pycache__", - "node_modules", - ".venv", - "venv", - ".tox", - ".mypy_cache", - ".pytest_cache", -}) +SKIP_DIRS = frozenset( + { + ".git", + "__pycache__", + "node_modules", + ".venv", + "venv", + ".tox", + ".mypy_cache", + ".pytest_cache", + } +) @dataclass @@ -52,7 +52,9 @@ class FileSearchResult: class GrepToolSchema(BaseModel): """Schema for grep tool arguments.""" - pattern: str = Field(..., description="Regex pattern to search for in file contents") + pattern: str = Field( + ..., description="Regex pattern to search for in file contents" + ) path: str | None = Field( default=None, description="File or directory to search in. Defaults to current working directory.", @@ -84,13 +86,23 @@ class GrepTool(BaseTool): Recursively searches files in a directory for lines matching a regex pattern. Supports glob filtering, context lines, and multiple output modes. + + Example: + >>> tool = GrepTool() + >>> result = tool.run(pattern="def.*main", path="/path/to/project") + >>> result = tool.run( + ... pattern="TODO", + ... path="/path/to/project", + ... glob_pattern="*.py", + ... context_lines=2, + ... ) """ - name: str = "grep" + name: str = "Search file contents" description: str = ( - "Search file contents on disk using regex patterns. " + "A tool that searches file contents on disk using regex patterns. " "Recursively searches files in a directory for matching lines. " - "Returns matching content, file paths, or match counts." + "Returns matching content with line numbers, file paths only, or match counts." ) args_schema: type[BaseModel] = GrepToolSchema @@ -154,7 +166,10 @@ class GrepTool(BaseTool): # Truncate if needed if len(output) > MAX_OUTPUT_CHARS: - output = output[:MAX_OUTPUT_CHARS] + "\n\n... Output truncated. Try a narrower search pattern or glob filter." + output = ( + output[:MAX_OUTPUT_CHARS] + + "\n\n... Output truncated. Try a narrower search pattern or glob filter." + ) return output @@ -255,11 +270,13 @@ class GrepTool(BaseTool): text = lines[i].rstrip("\n\r") if len(text) > MAX_LINE_LENGTH: text = text[:MAX_LINE_LENGTH] + "..." - current_group.append(MatchLine( - line_number=i + 1, # 1-indexed - text=text, - is_match=(i in match_line_nums), - )) + current_group.append( + MatchLine( + line_number=i + 1, # 1-indexed + text=text, + is_match=(i in match_line_nums), + ) + ) prev_end = end diff --git a/lib/crewai/tests/tools/agent_tools/test_grep_tool.py b/lib/crewai-tools/tests/tools/grep_tool_test.py similarity index 98% rename from lib/crewai/tests/tools/agent_tools/test_grep_tool.py rename to lib/crewai-tools/tests/tools/grep_tool_test.py index 8422ddcbc..410b31fd7 100644 --- a/lib/crewai/tests/tools/agent_tools/test_grep_tool.py +++ b/lib/crewai-tools/tests/tools/grep_tool_test.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest -from crewai.tools.agent_tools.grep_tool import GrepTool +from crewai_tools import GrepTool @pytest.fixture @@ -69,7 +69,7 @@ class TestGrepTool: def test_tool_metadata(self) -> None: """Test tool has correct name and description.""" - assert self.tool.name == "grep" + assert self.tool.name == "Search file contents" assert "search" in self.tool.description.lower() or "Search" in self.tool.description def test_args_schema(self) -> None: