diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d0974597..dcb4a1038 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Fixed `filterByRepos` and `filterByFilepaths` returning no results for repository names containing dots, dashes, or slashes by replacing `escape-string-regexp` (which produces `\xNN` hex escapes incompatible with Zoekt's RE2 engine) with a RE2-compatible escaper. [#1004](https://github.com/sourcebot-dev/sourcebot/pull/1004) + ### Added - Added AGENTS.md with Cursor Cloud development environment instructions. [#1001](https://github.com/sourcebot-dev/sourcebot/pull/1001) - Added support for configuring SMTP via individual environment variables (SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD) as an alternative to SMTP_CONNECTION_URL. [#1002](https://github.com/sourcebot-dev/sourcebot/pull/1002) diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index aa8b27513..355817da4 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -4,7 +4,9 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import _dedent from "dedent"; -import escapeStringRegexp from 'escape-string-regexp'; +// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2). +// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support. +const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&'); import { z } from 'zod'; import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, listTree, search } from './client.js'; import { env, numberSchema } from './env.js'; @@ -81,7 +83,7 @@ server.tool( useRegex = false, }) => { if (repos.length > 0) { - query += ` (repo:${repos.map(id => escapeStringRegexp(id)).join(' or repo:')})`; + query += ` (repo:${repos.map(id => escapeRE2(id)).join(' or repo:')})`; } if (languages.length > 0) { @@ -89,7 +91,7 @@ server.tool( } if (filepaths.length > 0) { - query += ` (file:${filepaths.map(filepath => escapeStringRegexp(filepath)).join(' or file:')})`; + query += ` (file:${filepaths.map(filepath => escapeRE2(filepath)).join(' or file:')})`; } if (ref) { diff --git a/packages/web/src/app/[domain]/browse/layoutClient.tsx b/packages/web/src/app/[domain]/browse/layoutClient.tsx index 8128b5717..07de400c9 100644 --- a/packages/web/src/app/[domain]/browse/layoutClient.tsx +++ b/packages/web/src/app/[domain]/browse/layoutClient.tsx @@ -10,7 +10,9 @@ import { useBrowseParams } from "./hooks/useBrowseParams"; import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog"; import { useDomain } from "@/hooks/useDomain"; import { SearchBar } from "../components/searchBar"; -import escapeStringRegexp from "escape-string-regexp"; +// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2). +// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support. +const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&'); import { Session } from "next-auth"; interface LayoutProps { @@ -37,7 +39,7 @@ export function LayoutClient({ s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&'); // @NOTE: When adding a new tool, follow these steps: // 1. Add the tool to the `toolNames` constant in `constants.ts`. @@ -198,7 +200,7 @@ export const createCodeSearchTool = (selectedRepos: string[]) => tool({ } if (repos.length > 0) { - query += ` (repo:${repos.map(id => escapeStringRegexp(id)).join(' or repo:')})`; + query += ` (repo:${repos.map(id => escapeRE2(id)).join(' or repo:')})`; } if (languages.length > 0) { @@ -206,7 +208,7 @@ export const createCodeSearchTool = (selectedRepos: string[]) => tool({ } if (filepaths.length > 0) { - query += ` (file:${filepaths.map(filepath => escapeStringRegexp(filepath)).join(' or file:')})`; + query += ` (file:${filepaths.map(filepath => escapeRE2(filepath)).join(' or file:')})`; } if (ref) { diff --git a/packages/web/src/features/mcp/server.ts b/packages/web/src/features/mcp/server.ts index e65aa070a..6c4fe4b4e 100644 --- a/packages/web/src/features/mcp/server.ts +++ b/packages/web/src/features/mcp/server.ts @@ -11,7 +11,9 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { ChatVisibility } from '@sourcebot/db'; import { SOURCEBOT_VERSION } from '@sourcebot/shared'; import _dedent from 'dedent'; -import escapeStringRegexp from 'escape-string-regexp'; +// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2). +// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support. +const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&'); import { z } from 'zod'; import { ListTreeEntry, @@ -141,13 +143,13 @@ export function createMcpServer(): McpServer { maxTokens?: number; }) => { if (repos.length > 0) { - query += ` (repo:${repos.map(id => escapeStringRegexp(id)).join(' or repo:')})`; + query += ` (repo:${repos.map(id => escapeRE2(id)).join(' or repo:')})`; } if (languages.length > 0) { query += ` (lang:${languages.join(' or lang:')})`; } if (filepaths.length > 0) { - query += ` (file:${filepaths.map(fp => escapeStringRegexp(fp)).join(' or file:')})`; + query += ` (file:${filepaths.map(fp => escapeRE2(fp)).join(' or file:')})`; } if (ref) { query += ` ( rev:${ref} )`;