Skip to content

fix(mcp): Follow grep separator and prefix semantics in grep_repomix_output output - #1869

Open
sxh313 wants to merge 2 commits into
yamadashy:mainfrom
sxh313:fix/grep-output-context-blocks
Open

sxh313 wants to merge 2 commits into
yamadashy:mainfrom
sxh313:fix/grep-output-context-blocks

Conversation

@sxh313

@sxh313 sxh313 commented Sep 20, 2026

Copy link
Copy Markdown

Summary

grep_repomix_output's formattedOutput is documented as grep-style output, but two of its formatting rules are not grep -C rules, and the code implementing them fails outright on repository-sized outputs. Fixes #1868.

The gap check compared the wrong pair of blocks.

if (resultLines.length > 0 && start > Math.min(...addedLines) + 1) {

addedLines accumulates every line emitted so far, so Math.min(...addedLines) is the start of the first block, not the end of the previous one. The condition reads "does this block start after the first line?" instead of "are lines missing between these two blocks?", so -- was pushed between blocks that touch or overlap. Tracking lastBlockEnd as a running maximum asks the intended question.

A match could be labelled as context. The prefix came from i === match.lineNumber - 1, evaluated only for the match currently being rendered, and lines already in addedLines were skipped. When an earlier match's context emitted a line that is itself a match, it kept the - prefix. Deciding from the set of matching line numbers makes the mark independent of emission order.

The same expression was also the failure mode on large outputs. It re-spreads every accumulated line per match: quadratic, and past a few hundred thousand entries the argument list exhausts the stack. lastBlockEnd is a scalar comparison, so the loop is linear and has no argument limit to hit.

Measured before / after

Fixture from #1868 (six lines, four matches two apart), called through the stdio MCP server with contextLines: 1. Lines 50–57 are consecutive, so the correct output has no separators; totalMatches reported 4 while only 3 lines were marked.

Before:

50-<file path="src/greetings.js">
51:export const hello = 'hello';
52-const padHello = (s) => s.trim();
--
53:export const helloWorld = 'hello world';
54-export const helloThere = 'hello there';
--
55-const goodbye = 'goodbye';
--
56:export const helloAgain = 'hello again';
57-</file>

After:

50-<file path="src/greetings.js">
51:export const hello = 'hello';
52-const padHello = (s) => s.trim();
53:export const helloWorld = 'hello world';
54:export const helloThere = 'hello there';
55-const goodbye = 'goodbye';
56:export const helloAgain = 'hello again';
57-</file>

formatSearchResults directly, disjoint matches 8 lines apart, context 1/1, one core:

matches file lines main this PR
10,000 80,010 7.3 s 16 ms
30,000 240,010 79 s 27 ms
60,000 480,010 RangeError 91 ms
100,000 800,010 RangeError 223 ms

The RangeError is caught by the tool and returned as reason: "SEARCH_ERROR" with the message Error: Maximum call stack size exceeded, so the client gets no result rather than a partial one.

Tests

Checklist

  • Run npm run test
  • Run npm run lint

Also npx tsc --noEmit clean, npm run build clean, and the change was reverted source-side only to confirm 7 tests fail without it.

…output output

The gap test used Math.min(...addedLines), which is the start of the first
block ever emitted rather than the end of the previous one, so `--` appeared
between context blocks that touch or overlap. A match line first printed as
another match's context kept the `-` prefix and stopped being identifiable as
a match. The same spread is re-evaluated per match and exhausts the call stack
past a few hundred thousand accumulated context lines, which surfaced to MCP
clients as SEARCH_ERROR on repository-sized outputs.

Track the previous block's end as a running maximum and decide the prefix from
the set of matching line numbers instead of the current loop iteration.

Measured on yamadashy#1868's fixture: 3 spurious `--` inside a contiguous 8-line run and
one match downgraded to context; 30k disjoint matches took 79s and 60k threw.
After: zero separators, all four matches marked, 27ms and 91ms.

Context: yamadashy#1868
Constraint: Existing formatSearchResults expectations encoded the old output and
            were corrected in the same commit; keep `--` semantics identical to
            grep -C rather than inventing a denser format.
Validation: npm run test (1822 passed, 20 skipped); npx tsc --noEmit; npm run
            lint exit 0; npm run build; with only the source fix reverted, the
            two new tests plus five corrected ones fail.
@sxh313
sxh313 requested a review from yamadashy as a code owner September 20, 2026 23:21
@coderabbitai

coderabbitai Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: yamadashy/repomix/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 1531528a-ab55-443c-b55a-356f96a5462b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: yamadashy/repomix/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: dcc7c439-de98-47f1-bc70-33b3f845970d

📥 Commits

Reviewing files that changed from the base of the PR and between 9f01703 and 7e80afa.

📒 Files selected for processing (2)
  • src/mcp/tools/grepRepomixOutputTool.ts
  • tests/mcp/tools/grepRepomixOutputTool.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

formatSearchResults now places -- only between separated output blocks and preserves : for matching lines emitted earlier as context. Tests cover contiguous, separated, overlapping, asymmetric, and multilingual results.

Changes

Grep output formatting

Layer / File(s) Summary
Format context blocks and match prefixes
src/mcp/tools/grepRepomixOutputTool.ts
The formatter tracks the end of the previous emitted block instead of scanning all emitted lines. It precomputes matching line numbers so matching lines retain the : prefix.
Validate formatted output
tests/mcp/tools/grepRepomixOutputTool.test.ts
Tests cover contiguous and separated context groups, overlapping blocks without duplicate lines, match prefixes, asymmetric context, and multilingual output.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the MCP formatting fix and its alignment with grep separator and prefix semantics.
Description check ✅ Passed The description includes a detailed summary, implementation rationale, test coverage, validation results, and the required checklist with both items completed.
Linked Issues check ✅ Passed The change satisfies the coding requirements in issue #1868. formatSearchResults tracks lastBlockEnd, so it adds -- only when a line is missing between context blocks. matchedLineNumbers ensur…
Out of Scope Changes check ✅ Passed The changes stay within issue #1868. The source change is limited to formatSearchResults, and the test changes verify separator, prefix, overlap, gap, and multilingual behavior required by the issue…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

grep_repomix_output: -- separators inside contiguous context blocks, matches printed with the context prefix, and RangeError on large outputs

1 participant