* Content search using ripgrep * Searches file contents for a pattern, returns matching lines with context
(params: ContentSearchParams)
| 722 | * Searches file contents for a pattern, returns matching lines with context |
| 723 | */ |
| 724 | async function handleContentSearch(params: ContentSearchParams): Promise<ContentSearchResponse> { |
| 725 | const startTime = Date.now() |
| 726 | const { dir, query, limit = 100, caseSensitive = false, regex = false, rankByHotFiles = false } = params |
| 727 | |
| 728 | logger.info("[Files:contentSearch] Starting search", JSON.stringify({ dir, query, limit, caseSensitive, regex, rankByHotFiles })) |
| 729 | |
| 730 | if (!query || !query.trim()) { |
| 731 | return { matches: [], truncated: false } |
| 732 | } |
| 733 | |
| 734 | // Validate directory exists |
| 735 | if (!fs.existsSync(dir)) { |
| 736 | throw new Error(`Directory does not exist: ${dir}`) |
| 737 | } |
| 738 | |
| 739 | if (!fs.statSync(dir).isDirectory()) { |
| 740 | throw new Error(`Path is not a directory: ${dir}`) |
| 741 | } |
| 742 | |
| 743 | // Get ripgrep path |
| 744 | const rgPath = (await getRipgrepPath()) || getManagedRipgrepPath() |
| 745 | if (!rgPath) { |
| 746 | throw new Error("Ripgrep not available") |
| 747 | } |
| 748 | |
| 749 | // Build ripgrep arguments |
| 750 | const args: string[] = [ |
| 751 | "--json", // JSON output for structured parsing |
| 752 | "--line-number", // Include line numbers |
| 753 | "--hidden", // Search hidden files |
| 754 | "--max-count", String(limit + 1), // Get one extra to detect truncation |
| 755 | // Exclude common non-code files and directories |
| 756 | "--glob", "!.git", |
| 757 | "--glob", "!*.lock", |
| 758 | "--glob", "!package-lock.json", |
| 759 | "--glob", "!yarn.lock", |
| 760 | "--glob", "!pnpm-lock.yaml", |
| 761 | "--glob", "!node_modules", |
| 762 | "--glob", "!dist", |
| 763 | "--glob", "!build", |
| 764 | "--glob", "!.next", |
| 765 | "--glob", "!.nuxt", |
| 766 | "--glob", "!coverage", |
| 767 | "--glob", "!*.min.js", |
| 768 | "--glob", "!*.min.css", |
| 769 | "--glob", "!*.map", |
| 770 | "--glob", "!*.chunk.js", |
| 771 | "--glob", "!vendor", |
| 772 | "--glob", "!__pycache__", |
| 773 | "--glob", "!*.pyc", |
| 774 | "--glob", "!.venv", |
| 775 | "--glob", "!venv", |
| 776 | "--glob", "!*.egg-info", |
| 777 | ] |
| 778 | |
| 779 | if (!caseSensitive) { |
| 780 | args.push("--ignore-case") |
| 781 | } |
no test coverage detected