Search workspace files and return structured matches.
(
pattern: Annotated[str, ParamMeta(description="Plain text or regex pattern")],
*,
globs: Annotated[
Optional[Sequence[str]],
ParamMeta(description="Restrict search to these glob patterns"),
] = None,
exclude_globs: Annotated[
Optional[Sequence[str]],
ParamMeta(description="Glob patterns to exclude"),
] = None,
use_regex: Annotated[bool, ParamMeta(description="Treat pattern as regex")]=True,
case_sensitive: Annotated[bool, ParamMeta(description="Match case when True")]=False,
max_results: Annotated[int, ParamMeta(description="Stop after this many matches")]=200,
before_context: Annotated[int, ParamMeta(description="Lines to include before match")]=2,
after_context: Annotated[int, ParamMeta(description="Lines to include after match")]=2,
include_hidden: Annotated[bool, ParamMeta(description="Search hidden files/folders")]=False,
_context: Dict[str, Any] | None = None,
)
| 708 | |
| 709 | |
| 710 | def search_in_files( |
| 711 | pattern: Annotated[str, ParamMeta(description="Plain text or regex pattern")], |
| 712 | *, |
| 713 | globs: Annotated[ |
| 714 | Optional[Sequence[str]], |
| 715 | ParamMeta(description="Restrict search to these glob patterns"), |
| 716 | ] = None, |
| 717 | exclude_globs: Annotated[ |
| 718 | Optional[Sequence[str]], |
| 719 | ParamMeta(description="Glob patterns to exclude"), |
| 720 | ] = None, |
| 721 | use_regex: Annotated[bool, ParamMeta(description="Treat pattern as regex")]=True, |
| 722 | case_sensitive: Annotated[bool, ParamMeta(description="Match case when True")]=False, |
| 723 | max_results: Annotated[int, ParamMeta(description="Stop after this many matches")]=200, |
| 724 | before_context: Annotated[int, ParamMeta(description="Lines to include before match")]=2, |
| 725 | after_context: Annotated[int, ParamMeta(description="Lines to include after match")]=2, |
| 726 | include_hidden: Annotated[bool, ParamMeta(description="Search hidden files/folders")]=False, |
| 727 | _context: Dict[str, Any] | None = None, |
| 728 | ) -> Dict[str, Any]: |
| 729 | """Search workspace files and return structured matches.""" |
| 730 | |
| 731 | if max_results <= 0: |
| 732 | raise ValueError("max_results must be positive") |
| 733 | |
| 734 | ctx = FileToolContext(_context) |
| 735 | include_patterns = _normalize_globs(globs) or ["**/*"] |
| 736 | exclude_patterns = _normalize_globs(exclude_globs) |
| 737 | |
| 738 | matches: List[Dict[str, Any]] = [] |
| 739 | searched_files = 0 |
| 740 | compiled_regex: Optional[re.Pattern[str]] = None |
| 741 | literal = pattern if case_sensitive else pattern.lower() |
| 742 | if use_regex: |
| 743 | flags = re.MULTILINE |
| 744 | if not case_sensitive: |
| 745 | flags |= re.IGNORECASE |
| 746 | compiled_regex = re.compile(pattern, flags) |
| 747 | |
| 748 | for candidate in _iter_candidate_files( |
| 749 | ctx.workspace_root, |
| 750 | include_patterns, |
| 751 | exclude_patterns, |
| 752 | include_hidden, |
| 753 | ): |
| 754 | searched_files += 1 |
| 755 | lines = _read_file_lines_for_search(candidate) |
| 756 | if not lines: |
| 757 | continue |
| 758 | for match in _iter_line_matches( |
| 759 | lines, |
| 760 | compiled_regex, |
| 761 | literal, |
| 762 | pattern, |
| 763 | case_sensitive, |
| 764 | use_regex, |
| 765 | ): |
| 766 | before = _slice_context(lines, match["line_number"], before_context, before=True) |
| 767 | after = _slice_context(lines, match["line_number"], after_context, before=False) |
nothing calls this directly
no test coverage detected