Skip to content

Commit c1bea08

Browse files
fix: use RE2-compatible escaper for repo/file filter queries
escape-string-regexp v5 encodes some characters (e.g. '-') as \xNN hex escapes, which Go's RE2 engine (used by Zoekt) does not support. This caused filterByRepos/filterByFilepaths with names containing dots, dashes, or slashes to return no results. Replace the import with a local escapeRE2 helper that only backslash-escapes characters special in RE2. Fixes #990 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 890f6c4 commit c1bea08

File tree

4 files changed

+19
-11
lines changed

4 files changed

+19
-11
lines changed

packages/mcp/src/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
55
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
66
import _dedent from "dedent";
7-
import escapeStringRegexp from 'escape-string-regexp';
7+
// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2).
8+
// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support.
9+
const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&');
810
import { z } from 'zod';
911
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, listTree, search } from './client.js';
1012
import { env, numberSchema } from './env.js';
@@ -81,15 +83,15 @@ server.tool(
8183
useRegex = false,
8284
}) => {
8385
if (repos.length > 0) {
84-
query += ` (repo:${repos.map(id => escapeStringRegexp(id)).join(' or repo:')})`;
86+
query += ` (repo:${repos.map(id => escapeRE2(id)).join(' or repo:')})`;
8587
}
8688

8789
if (languages.length > 0) {
8890
query += ` (lang:${languages.join(' or lang:')})`;
8991
}
9092

9193
if (filepaths.length > 0) {
92-
query += ` (file:${filepaths.map(filepath => escapeStringRegexp(filepath)).join(' or file:')})`;
94+
query += ` (file:${filepaths.map(filepath => escapeRE2(filepath)).join(' or file:')})`;
9395
}
9496

9597
if (ref) {

packages/web/src/app/[domain]/browse/layoutClient.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import { useBrowseParams } from "./hooks/useBrowseParams";
1010
import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog";
1111
import { useDomain } from "@/hooks/useDomain";
1212
import { SearchBar } from "../components/searchBar";
13-
import escapeStringRegexp from "escape-string-regexp";
13+
// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2).
14+
// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support.
15+
const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&');
1416
import { Session } from "next-auth";
1517

1618
interface LayoutProps {
@@ -37,7 +39,7 @@ export function LayoutClient({
3739
<SearchBar
3840
size="sm"
3941
defaults={{
40-
query: `repo:^${escapeStringRegexp(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
42+
query: `repo:^${escapeRE2(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
4143
}}
4244
className="w-full"
4345
isSearchAssistSupported={isSearchAssistSupported}

packages/web/src/features/chat/tools.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import { toolNames } from "./constants";
99
import { listReposQueryParamsSchema } from "@/lib/schemas";
1010
import { ListReposQueryParams } from "@/lib/types";
1111
import { listRepos } from "@/app/api/(server)/repos/listReposApi";
12-
import escapeStringRegexp from "escape-string-regexp";
12+
// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2).
13+
// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support.
14+
const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&');
1315

1416
// @NOTE: When adding a new tool, follow these steps:
1517
// 1. Add the tool to the `toolNames` constant in `constants.ts`.
@@ -198,15 +200,15 @@ export const createCodeSearchTool = (selectedRepos: string[]) => tool({
198200
}
199201

200202
if (repos.length > 0) {
201-
query += ` (repo:${repos.map(id => escapeStringRegexp(id)).join(' or repo:')})`;
203+
query += ` (repo:${repos.map(id => escapeRE2(id)).join(' or repo:')})`;
202204
}
203205

204206
if (languages.length > 0) {
205207
query += ` (lang:${languages.join(' or lang:')})`;
206208
}
207209

208210
if (filepaths.length > 0) {
209-
query += ` (file:${filepaths.map(filepath => escapeStringRegexp(filepath)).join(' or file:')})`;
211+
query += ` (file:${filepaths.map(filepath => escapeRE2(filepath)).join(' or file:')})`;
210212
}
211213

212214
if (ref) {

packages/web/src/features/mcp/server.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1111
import { ChatVisibility } from '@sourcebot/db';
1212
import { SOURCEBOT_VERSION } from '@sourcebot/shared';
1313
import _dedent from 'dedent';
14-
import escapeStringRegexp from 'escape-string-regexp';
14+
// Escapes special RE2 regex characters using backslash (compatible with Zoekt/Go RE2).
15+
// escape-string-regexp v5 uses \xNN hex escapes which RE2 does not support.
16+
const escapeRE2 = (s: string) => s.replace(/[.+*?^${}[\]|(\\]/g, '\\$&');
1517
import { z } from 'zod';
1618
import {
1719
ListTreeEntry,
@@ -141,13 +143,13 @@ export function createMcpServer(): McpServer {
141143
maxTokens?: number;
142144
}) => {
143145
if (repos.length > 0) {
144-
query += ` (repo:${repos.map(id => escapeStringRegexp(id)).join(' or repo:')})`;
146+
query += ` (repo:${repos.map(id => escapeRE2(id)).join(' or repo:')})`;
145147
}
146148
if (languages.length > 0) {
147149
query += ` (lang:${languages.join(' or lang:')})`;
148150
}
149151
if (filepaths.length > 0) {
150-
query += ` (file:${filepaths.map(fp => escapeStringRegexp(fp)).join(' or file:')})`;
152+
query += ` (file:${filepaths.map(fp => escapeRE2(fp)).join(' or file:')})`;
151153
}
152154
if (ref) {
153155
query += ` ( rev:${ref} )`;

0 commit comments

Comments
 (0)