mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-30 14:52:36 +00:00
move to crewai-tools
This commit is contained in:
@@ -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.glob_tool.glob_tool import GlobTool
|
||||
from crewai_tools.tools.grep_tool.grep_tool import GrepTool
|
||||
from crewai_tools.tools.hyperbrowser_load_tool.hyperbrowser_load_tool import (
|
||||
HyperbrowserLoadTool,
|
||||
@@ -231,6 +232,7 @@ __all__ = [
|
||||
"FirecrawlSearchTool",
|
||||
"GenerateCrewaiAutomationTool",
|
||||
"GithubSearchTool",
|
||||
"GlobTool",
|
||||
"GrepTool",
|
||||
"HyperbrowserLoadTool",
|
||||
"InvokeCrewAIAutomationTool",
|
||||
|
||||
@@ -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.glob_tool.glob_tool import GlobTool
|
||||
from crewai_tools.tools.grep_tool.grep_tool import GrepTool
|
||||
from crewai_tools.tools.hyperbrowser_load_tool.hyperbrowser_load_tool import (
|
||||
HyperbrowserLoadTool,
|
||||
@@ -215,6 +216,7 @@ __all__ = [
|
||||
"FirecrawlSearchTool",
|
||||
"GenerateCrewaiAutomationTool",
|
||||
"GithubSearchTool",
|
||||
"GlobTool",
|
||||
"GrepTool",
|
||||
"HyperbrowserLoadTool",
|
||||
"InvokeCrewAIAutomationTool",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
from crewai_tools.tools.glob_tool.glob_tool import GlobTool
|
||||
|
||||
|
||||
__all__ = ["GlobTool"]
|
||||
@@ -7,10 +7,9 @@ import os
|
||||
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_FILES = 1000
|
||||
MAX_OUTPUT_CHARS = 30_000
|
||||
@@ -87,6 +86,11 @@ class GlobTool(BaseTool):
|
||||
Recursively searches for files matching a glob pattern within a directory.
|
||||
Useful for discovering files by name, extension, or path pattern.
|
||||
Complements GrepTool which searches by file content.
|
||||
|
||||
Example:
|
||||
>>> tool = GlobTool()
|
||||
>>> result = tool.run(pattern="*.py", path="/path/to/project")
|
||||
>>> result = tool.run(pattern="**/*.yaml", output_mode="detailed")
|
||||
"""
|
||||
|
||||
name: str = "glob"
|
||||
182
lib/crewai-tools/tests/tools/glob_tool_test.py
Normal file
182
lib/crewai-tools/tests/tools/glob_tool_test.py
Normal file
@@ -0,0 +1,182 @@
|
||||
"""Unit tests for GlobTool."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from crewai_tools import GlobTool
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_dir(tmp_path: Path) -> Path:
|
||||
"""Create a temp directory with sample files for testing."""
|
||||
# src/main.py
|
||||
src = tmp_path / "src"
|
||||
src.mkdir()
|
||||
(src / "main.py").write_text("print('hello')")
|
||||
(src / "utils.py").write_text("def helper(): pass")
|
||||
|
||||
# src/components/button.tsx
|
||||
components = src / "components"
|
||||
components.mkdir()
|
||||
(components / "button.tsx").write_text("export const Button = () => {}")
|
||||
(components / "input.tsx").write_text("export const Input = () => {}")
|
||||
|
||||
# tests/test_main.py
|
||||
tests = tmp_path / "tests"
|
||||
tests.mkdir()
|
||||
(tests / "test_main.py").write_text("def test_hello(): pass")
|
||||
(tests / "test_utils.py").write_text("def test_helper(): pass")
|
||||
|
||||
# config files
|
||||
(tmp_path / "config.yaml").write_text("key: value")
|
||||
(tmp_path / "settings.json").write_text("{}")
|
||||
|
||||
# hidden file
|
||||
(tmp_path / ".hidden").write_text("secret")
|
||||
|
||||
# empty directory
|
||||
(tmp_path / "empty_dir").mkdir()
|
||||
|
||||
return tmp_path
|
||||
|
||||
|
||||
class TestGlobTool:
|
||||
"""Tests for GlobTool."""
|
||||
|
||||
def setup_method(self) -> None:
|
||||
"""Set up test fixtures."""
|
||||
self.tool = GlobTool()
|
||||
|
||||
def test_tool_metadata(self) -> None:
|
||||
"""Test tool has correct name and description."""
|
||||
assert self.tool.name == "glob"
|
||||
assert "pattern" in self.tool.description.lower()
|
||||
|
||||
def test_args_schema(self) -> None:
|
||||
"""Test that args_schema has correct fields."""
|
||||
schema = self.tool.args_schema
|
||||
fields = schema.model_fields
|
||||
|
||||
assert "pattern" in fields
|
||||
assert fields["pattern"].is_required()
|
||||
|
||||
assert "path" in fields
|
||||
assert not fields["path"].is_required()
|
||||
|
||||
assert "output_mode" in fields
|
||||
assert not fields["output_mode"].is_required()
|
||||
|
||||
assert "include_hidden" in fields
|
||||
assert not fields["include_hidden"].is_required()
|
||||
|
||||
def test_find_python_files(self, sample_dir: Path) -> None:
|
||||
"""Test finding Python files with *.py pattern."""
|
||||
result = self.tool._run(pattern="*.py", path=str(sample_dir))
|
||||
assert "main.py" in result
|
||||
assert "utils.py" in result
|
||||
assert "test_main.py" in result
|
||||
assert "test_utils.py" in result
|
||||
|
||||
def test_find_specific_extension(self, sample_dir: Path) -> None:
|
||||
"""Test finding files with specific extension."""
|
||||
result = self.tool._run(pattern="*.tsx", path=str(sample_dir))
|
||||
assert "button.tsx" in result
|
||||
assert "input.tsx" in result
|
||||
assert "main.py" not in result
|
||||
|
||||
def test_find_test_files(self, sample_dir: Path) -> None:
|
||||
"""Test finding test files with test_*.py pattern."""
|
||||
result = self.tool._run(pattern="test_*.py", path=str(sample_dir))
|
||||
assert "test_main.py" in result
|
||||
assert "test_utils.py" in result
|
||||
# Verify non-test files are not included (check for exact filename, not substring)
|
||||
lines = result.split("\n")
|
||||
file_lines = [l for l in lines if l.endswith(".py")]
|
||||
assert not any(l.endswith("/main.py") or l == "main.py" for l in file_lines)
|
||||
assert not any(l.endswith("/utils.py") or l == "utils.py" for l in file_lines)
|
||||
|
||||
def test_recursive_pattern(self, sample_dir: Path) -> None:
|
||||
"""Test explicit recursive pattern **/*.py."""
|
||||
result = self.tool._run(pattern="**/*.py", path=str(sample_dir))
|
||||
assert "main.py" in result
|
||||
assert "test_main.py" in result
|
||||
|
||||
def test_hidden_files_excluded_by_default(self, sample_dir: Path) -> None:
|
||||
"""Test hidden files are excluded by default."""
|
||||
result = self.tool._run(pattern="*", path=str(sample_dir))
|
||||
assert ".hidden" not in result
|
||||
|
||||
def test_hidden_files_included(self, sample_dir: Path) -> None:
|
||||
"""Test hidden files included when include_hidden=True."""
|
||||
result = self.tool._run(
|
||||
pattern=".*", path=str(sample_dir), include_hidden=True
|
||||
)
|
||||
assert ".hidden" in result
|
||||
|
||||
def test_output_mode_paths(self, sample_dir: Path) -> None:
|
||||
"""Test paths output mode shows file paths."""
|
||||
result = self.tool._run(
|
||||
pattern="*.py", path=str(sample_dir), output_mode="paths"
|
||||
)
|
||||
# Should contain full paths
|
||||
assert str(sample_dir) in result or "main.py" in result
|
||||
|
||||
def test_output_mode_detailed(self, sample_dir: Path) -> None:
|
||||
"""Test detailed output mode shows sizes."""
|
||||
result = self.tool._run(
|
||||
pattern="*.py", path=str(sample_dir), output_mode="detailed"
|
||||
)
|
||||
# Should contain size indicators
|
||||
assert "B" in result # Bytes indicator
|
||||
|
||||
def test_output_mode_tree(self, sample_dir: Path) -> None:
|
||||
"""Test tree output mode shows directory structure."""
|
||||
result = self.tool._run(
|
||||
pattern="*.py", path=str(sample_dir), output_mode="tree"
|
||||
)
|
||||
assert str(sample_dir) in result
|
||||
|
||||
def test_dirs_only(self, sample_dir: Path) -> None:
|
||||
"""Test dirs_only=True only returns directories."""
|
||||
result = self.tool._run(
|
||||
pattern="*",
|
||||
path=str(sample_dir),
|
||||
dirs_only=True,
|
||||
files_only=False,
|
||||
)
|
||||
assert "src" in result or "tests" in result or "empty_dir" in result
|
||||
# Should not contain file extensions
|
||||
assert ".py" not in result
|
||||
assert ".yaml" not in result
|
||||
|
||||
def test_path_not_found(self) -> None:
|
||||
"""Test error message when path doesn't exist."""
|
||||
result = self.tool._run(pattern="*.py", path="/nonexistent/path")
|
||||
assert "Error" in result
|
||||
assert "does not exist" in result
|
||||
|
||||
def test_path_is_file(self, sample_dir: Path) -> None:
|
||||
"""Test error message when path is a file, not directory."""
|
||||
file_path = sample_dir / "config.yaml"
|
||||
result = self.tool._run(pattern="*.py", path=str(file_path))
|
||||
assert "Error" in result
|
||||
assert "not a directory" in result
|
||||
|
||||
def test_no_matches(self, sample_dir: Path) -> None:
|
||||
"""Test message when no files match pattern."""
|
||||
result = self.tool._run(pattern="*.xyz", path=str(sample_dir))
|
||||
assert "No files found" in result
|
||||
|
||||
def test_found_count_in_output(self, sample_dir: Path) -> None:
|
||||
"""Test that result includes count of found files."""
|
||||
result = self.tool._run(pattern="*.py", path=str(sample_dir))
|
||||
assert "Found" in result
|
||||
assert "file(s)" in result
|
||||
|
||||
def test_run_with_kwargs(self, sample_dir: Path) -> None:
|
||||
"""Test _run ignores extra kwargs."""
|
||||
result = self.tool._run(
|
||||
pattern="*.py", path=str(sample_dir), extra_arg="ignored"
|
||||
)
|
||||
assert "main.py" in result
|
||||
@@ -1,10 +1 @@
|
||||
"""Agent tools for crewAI."""
|
||||
|
||||
from crewai.tools.agent_tools.glob_tool import GlobTool
|
||||
from crewai.tools.agent_tools.grep_tool import GrepTool
|
||||
|
||||
|
||||
__all__ = [
|
||||
"GlobTool",
|
||||
"GrepTool",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user