Description
On Windows, pattern_search (and any tool that calls runRg with line-by-line parsing) silently drops every match from files that use CRLF line endings (\r\n).
The root cause is in execPatternSearch (server.ts:1293):
const m = line.match(/^(.+?):(\d+):(.*)$/);
When ripgrep outputs matches from a CRLF file, it preserves the \r\n endings. After stdout.split("\n"), the \n is consumed but \r remains at the end of each line. Since . does not match \r and $ does not anchor before \r, the entire regex fails to match, and the line is silently skipped.
Minimal reproduction:
// CRLF line → match fails
"abc:1:hello\r".match(/^(.+?):(\d+):(.*)$/); // null
// LF line → match succeeds
"abc:1:hello".match(/^(.+?):(\d+):(.*)$/); // ["abc:1:hello", "abc", "1", "hello"]
Impact: Any file committed with CRLF endings (common on Windows, especially files edited in certain IDEs) is completely invisible to pattern_search. The bug is silent — no error, no warning, just missing results.
Affected Code
src/mcp/server.ts line 1293 and any other runRg consumer that parses output line-by-line with the same regex pattern.
Proposed Fix
Option A — Make the regex CRLF-tolerant:
- const m = line.match(/^(.+?):(\d+):(.*)$/);
+ const m = line.match(/^(.+?):(\d+):(.*)\r?$/);
Option B — Strip \r before parsing:
for (const line of stdout.split("\n")) {
+ const clean = line.replace(/\r$/, "");
- const m = line.match(/^(.+?):(\d+):(.*)$/);
+ const m = clean.match(/^(.+?):(\d+):(.*)$/);
Option B is more defensive and benefits any future regex changes that might forget \r?.
Reproduction Steps
-
Create a CRLF test file on Windows:
mkdir -p /tmp/test-project
printf "export class Hello {\r\n world() {}\r\n}\r\n" > /tmp/test-project/hello.js
-
Create an LF test file in the same directory:
printf "export class Hello {\n world() {}\n}\n" > /tmp/test-project/hello-lf.js
-
Run lexis MCP against this project and call pattern_search:
{
"tool": "pattern_search",
"arguments": { "pattern": "Hello" }
}
-
Expected: Both hello.js and hello-lf.js appear in results.
-
Actual: Only hello-lf.js appears. hello.js is silently missing.
Environment
- OS: Windows 10/11 (CRLF is the default line ending)
- Node.js: v22.x
- ripgrep: 15.0.0 (bundled via
@vscode/ripgrep)
- lexis-mcp: latest
Additional Notes
- The same regex pattern may also affect other
runRg consumers in server.ts. A grep for (.+?):(\d+):(.*) would reveal all occurrences that need patching.
search_code also calls runRg internally and likely has the same issue if it parses stdout line-by-line.
- This bug is particularly insidious because it is silent — no error, no warning, just missing results. On a mixed-line-ending project (common in teams with both Windows and macOS/Linux devs), some files will appear in search results and others won't, with no obvious pattern to the user.
Description
On Windows,
pattern_search(and any tool that callsrunRgwith line-by-line parsing) silently drops every match from files that use CRLF line endings (\r\n).The root cause is in
execPatternSearch(server.ts:1293):When ripgrep outputs matches from a CRLF file, it preserves the
\r\nendings. Afterstdout.split("\n"), the\nis consumed but\rremains at the end of each line. Since.does not match\rand$does not anchor before\r, the entire regex fails to match, and the line is silently skipped.Minimal reproduction:
Impact: Any file committed with CRLF endings (common on Windows, especially files edited in certain IDEs) is completely invisible to
pattern_search. The bug is silent — no error, no warning, just missing results.Affected Code
src/mcp/server.tsline 1293 and any otherrunRgconsumer that parses output line-by-line with the same regex pattern.Proposed Fix
Option A — Make the regex CRLF-tolerant:
Option B — Strip
\rbefore parsing:for (const line of stdout.split("\n")) { + const clean = line.replace(/\r$/, ""); - const m = line.match(/^(.+?):(\d+):(.*)$/); + const m = clean.match(/^(.+?):(\d+):(.*)$/);Option B is more defensive and benefits any future regex changes that might forget
\r?.Reproduction Steps
Create a CRLF test file on Windows:
Create an LF test file in the same directory:
Run lexis MCP against this project and call
pattern_search:{ "tool": "pattern_search", "arguments": { "pattern": "Hello" } }Expected: Both
hello.jsandhello-lf.jsappear in results.Actual: Only
hello-lf.jsappears.hello.jsis silently missing.Environment
@vscode/ripgrep)Additional Notes
runRgconsumers inserver.ts. A grep for(.+?):(\d+):(.*)would reveal all occurrences that need patching.search_codealso callsrunRginternally and likely has the same issue if it parses stdout line-by-line.