Download Code-agent-main/ycode/tools/search.py from Yazhiniyan/YCode: direct link, hf CLI and curl.
- Browser
- Download file 5.42 kB
-
https://huggingface.co/Yazhiniyan/YCode/resolve/main/Code-agent-main/ycode/tools/search.py
- Command line
-
hf download hf://Yazhiniyan/YCode/Code-agent-main/ycode/tools/search.py
-
curl -L -o search.py https://huggingface.co/Yazhiniyan/YCode/resolve/main/Code-agent-main/ycode/tools/search.py
5.42 kB
| """Search tools: search_files (content search) and find_files (glob).""" | |
| from __future__ import annotations | |
| import fnmatch | |
| import re | |
| from pathlib import Path | |
| from typing import Any | |
| from ycode.errors import ToolError | |
| from ycode.tools.base import Tool, ToolContext, ToolResult | |
| from ycode.tools.filesystem import iter_files | |
| MAX_FILE_BYTES = 1_000_000 | |
| def _matches_glob(rel: str, pattern: str | None) -> bool: | |
| if not pattern: | |
| return True | |
| name = rel.rsplit("/", 1)[-1] | |
| patterns = [p.strip() for p in pattern.split(",") if p.strip()] | |
| return any( | |
| fnmatch.fnmatch(rel, p) or fnmatch.fnmatch(name, p) or fnmatch.fnmatch(rel, f"**/{p}") for p in patterns | |
| ) | |
| class SearchFilesTool(Tool): | |
| name = "search_files" | |
| description = ( | |
| "Search file contents with a regular expression (Python syntax). Returns matching " | |
| "lines as path:line: text. Use `glob` (e.g. '*.py' or 'src/**/*.ts') to narrow files." | |
| ) | |
| parameters = { | |
| "type": "object", | |
| "properties": { | |
| "pattern": {"type": "string", "description": "Regular expression to search for."}, | |
| "path": {"type": "string", "description": "Directory to search (default project root)."}, | |
| "glob": {"type": "string", "description": "Filename filter, comma-separated globs."}, | |
| "case_sensitive": {"type": "boolean", "description": "Default false."}, | |
| "max_results": {"type": "integer", "description": "Default 100."}, | |
| }, | |
| "required": ["pattern"], | |
| } | |
| def describe(self, args: dict[str, Any]) -> str: | |
| where = f" in {args['glob']}" if args.get("glob") else "" | |
| return f"Searching for /{args.get('pattern', '')}/{where}" | |
| def run(self, args: dict[str, Any], ctx: ToolContext) -> ToolResult: | |
| root = ctx.permissions.paths.resolve(args.get("path") or ".") | |
| if not root.exists(): | |
| raise ToolError(f"Path not found: {args.get('path')}") | |
| flags = 0 if args.get("case_sensitive") else re.IGNORECASE | |
| try: | |
| regex = re.compile(args["pattern"], flags) | |
| except re.error as exc: | |
| raise ToolError(f"invalid regular expression: {exc}") from None | |
| limit = min(max(int(args.get("max_results") or 100), 1), 1000) | |
| files = [root] if root.is_file() else iter_files(root, ctx.ignore_dirs) | |
| results: list[str] = [] | |
| files_matched = 0 | |
| for file in files: | |
| rel = ctx.permissions.paths.relative(file) | |
| if not _matches_glob(rel, args.get("glob")): | |
| continue | |
| try: | |
| if file.stat().st_size > MAX_FILE_BYTES: | |
| continue | |
| data = file.read_bytes() | |
| except OSError: | |
| continue | |
| if b"\x00" in data[:8192]: | |
| continue | |
| hit = False | |
| for lineno, line in enumerate(data.decode("utf-8", errors="replace").splitlines(), 1): | |
| if regex.search(line): | |
| hit = True | |
| results.append(f"{rel}:{lineno}: {line.strip()[:300]}") | |
| if len(results) >= limit: | |
| break | |
| files_matched += hit | |
| if len(results) >= limit: | |
| break | |
| if not results: | |
| return ToolResult(content="No matches found.", summary="No matches") | |
| suffix = f"\n[Stopped after {limit} results; refine the pattern.]" if len(results) >= limit else "" | |
| return ToolResult( | |
| content="\n".join(results) + suffix, | |
| summary=f"{len(results)} matches in {files_matched} files", | |
| ) | |
| class FindFilesTool(Tool): | |
| name = "find_files" | |
| description = "Find files by name/glob pattern (e.g. '*.test.ts', 'package.json', 'src/**/auth*')." | |
| parameters = { | |
| "type": "object", | |
| "properties": { | |
| "pattern": {"type": "string", "description": "Glob pattern (comma-separated for several)."}, | |
| "path": {"type": "string", "description": "Directory to search (default project root)."}, | |
| "max_results": {"type": "integer", "description": "Default 200."}, | |
| }, | |
| "required": ["pattern"], | |
| } | |
| def describe(self, args: dict[str, Any]) -> str: | |
| return f"Finding files matching {args.get('pattern', '')}" | |
| def run(self, args: dict[str, Any], ctx: ToolContext) -> ToolResult: | |
| root = ctx.permissions.paths.resolve(args.get("path") or ".") | |
| if not root.is_dir(): | |
| raise ToolError(f"Directory not found: {args.get('path')}") | |
| limit = min(max(int(args.get("max_results") or 200), 1), 2000) | |
| found: list[str] = [] | |
| for file in iter_files(root, ctx.ignore_dirs): | |
| rel = ctx.permissions.paths.relative(file) | |
| if _matches_glob(rel, args["pattern"]): | |
| found.append(rel) | |
| if len(found) >= limit: | |
| break | |
| if not found: | |
| return ToolResult(content="No files found.", summary="No files found") | |
| return ToolResult(content="\n".join(found), summary=f"Found {len(found)} files") | |
| def list_project_files(root: Path, ignore_dirs: tuple[str, ...], limit: int = 2000) -> list[str]: | |
| out: list[str] = [] | |
| for file in iter_files(root, ignore_dirs): | |
| out.append(file.relative_to(root).as_posix()) | |
| if len(out) >= limit: | |
| break | |
| return out | |