fix(cursor): grep content results keep their matched lines#25037

open7
main
fix/issue-24919
opened4d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 4h ago

Problem

@cursor/sdk 1.0.38 (and 1.0.35 through 1.2.0) converts native grep content results as if they were flat matches. The wire shape is grouped (file + matches[{lineNumber, content, isContextLine}]), so every match loses its text, line number and context, and totalMatches becomes 0. T3's Cursor adapter uses that text as the file-search preview, so a content-mode grep shows bare file names. Reproduced on main 97e35bcf23 with a real Cursor agent grep (transcript below).

Change

A pnpm patch of @cursor/sdk@1.0.38 rewrites only the converter's content case, in the cjs, esm and bundled builds. Grouped results become one match per non-context line, with adjacent context lines attached as beforeContext/afterContext. totalMatches takes totalMatchedLines, then totalMatches, then the match count. Entries that are already flat pass through unchanged. The patch file is large because the SDK ships single-line minified chunks; the logical change is one expression per file. The test reads the installed converter, finds the chunk that holds it instead of hardcoding a chunk number, and fails loudly if a future SDK rewrites it.

Scope and approval

Fixes pingdotgg/t3code#24919

Triaged via the automated issue flow; GitCafe issue #24919. No settings, contracts or client changes.

Verification

  • Red first: keeps grouped grep content match text, lines, and totals in the %s Cursor SDK build (esm and cjs) fails on unpatched 1.0.38 with line: undefined, no lineNumber/context, and totalMatches: 0 instead of 7.
  • vp test run packages/provider-cursor/src/server/adapter.test.ts: 23 passed. vp run check:changed: all checks passed (unused patches, format, lint, knip, typecheck of 39 packages, tests).
  • Bundled build checked by hand with the same input: output identical to cjs and esm.
  • Runtime: a real Cursor agent (Agent.create, model default, local cwd = a synthetic two-file fixture) asked for one content-mode grep with one line of context. Same driver, same prompt, before and after:
Before: main 97e35bcf23, unpatched SDK
1sdk: node_modules/.pnpm/@cursor+sdk@1.0.38_zod@4.6.5/node_modules/@cursor/sdk/dist/cjs/index.js
2grep tool-call-completed updates: 1
3{
4 "status": "success",
5 "value": {
6 "workspaceResults": {
7 "/tmp/issue-lead-24919-r2/runtime/fixture": {
8 "type": "content",
9 "output": {
10 "matches": [
11 {
12 "file": "./src/one.ts"
13 },
14 {
15 "file": "./src/two.ts"
16 }
17 ],
18 "totalMatches": 0
19 }
20 }
21 }
22 }
23}
After: fix/issue-24919 cc081f946f, patched SDK
1sdk: node_modules/.pnpm/@cursor+sdk@1.0.38_patch_hash=c50f9c6f1eea79f6e41ca9c80dfd8db777206627f77adc8c9607160410f44c56_zod@4.6.5/node_modules/@cursor/sdk/dist/cjs/index.js
2grep tool-call-completed updates: 1
3{
4 "status": "success",
5 "value": {
6 "workspaceResults": {
7 "/tmp/issue-lead-24919-r2/runtime/fixture": {
8 "type": "content",
9 "output": {
10 "matches": [
11 {
12 "file": "./src/one.ts",
13 "lineNumber": 3,
14 "line": "export const marker = \"zebra-needle one\";",
15 "beforeContext": [
16 "// before one"
17 ],
18 "afterContext": [
19 "// after one"
20 ]
21 },
22 {
23 "file": "./src/two.ts",
24 "lineNumber": 2,
25 "line": "export const second = \"zebra-needle two\";",
26 "beforeContext": [
27 "export const first = \"x\";"
28 ]
29 }
30 ],
31 "totalMatches": 2
32 }
33 }
34 }
35 }
36}
Driver script (scratch, not committed)
js
1// Usage: node driver.mjs <provider-cursor dir> <fixture cwd> <out dir>
2import { createRequire } from "node:module";
3import { writeFileSync, mkdirSync } from "node:fs";
4const [pkgDir, cwd, out] = process.argv.slice(2);
5mkdirSync(out, { recursive: true });
6const require = createRequire(pkgDir + "/package.json");
7const sdkPath = require.resolve("@cursor/sdk");
8const mod = await import(sdkPath);
9const { Agent, Cursor } = mod.Agent ? mod : mod.default;
10const apiKey = process.env.CURSOR_API_KEY;
11if (process.argv.includes("--models")) { const m = await Cursor.models.list({ apiKey }); console.log(JSON.stringify(m).slice(0, 2000)); process.exit(0); }
12const model = process.env.MODEL ?? "composer-2";
13const agent = await Agent.create({ apiKey, model: { id: model }, local: { cwd } });
14const updates = [];
15const run = await agent.send(
16 "Use your grep tool exactly once, in content output mode, with 1 line of context before and after, to search for the regex 'zebra-needle' in this directory. Do not use any other tool. Then reply with just the word done.",
17 { onDelta: async ({ update }) => { updates.push(update); } },
18);
19const result = await run.wait?.();
20writeFileSync(out + "/updates.json", JSON.stringify(updates, null, 2));
21const grep = updates.filter((u) => JSON.stringify(u).includes('"grep"') && u.type?.includes("completed"));
22console.log("sdk:", sdkPath);
23console.log("grep tool-call-completed updates:", grep.length);
24for (const u of grep) console.log(JSON.stringify(u.toolCall?.result ?? u, null, 1).slice(0, 2500));
25process.exit(0);

Not checked: no before/after UI capture at this head. The renderer is unchanged and the adapter maps line straight to the search preview (adapter.ts:486); captures from the earlier 1.0.35 revision were dropped as stale. The bundled build was not run under Bun. Electron and mobile were not run; both consume the same server-side search result.

Implemented with Claude Opus 5.5 in Claude Code; reviewed with GPT-6.1-Sol in Codex and Claude Opus 5.5 in Claude Code.

🤖 Generated with Claude Code

Checks

22 successful · 8 skipped · 1 neutral
All checks passed
3e5f87b

Added 3 commits

  • test(cursor): grep content matches survive the installed SDK converteraf43770
  • Merge remote-tracking branch 'origin/main' into fix/issue-24919c3ab326
  • fix(cursor): grep content results keep their matched linesd6bebe6
Synced from GitHub

Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting).

This review would cost an estimated $192.00, which exceeds your per-review limit of $15.00.

The top 2 files driving up this estimate:

FileDiff SizeEstimate
patches/@cursor__sdk@1.0.38.patch4.97MB$191.98
pnpm-workspace.yaml406B$0.02

Tip

To get this pull request reviewed, you can:

  1. Comment @macroscope-app on this PR to request a manual review (monthly spend limits still apply).
  2. Exclude the file(s) above from review by adding a pattern to your .macroscope/ignore.md — note that creating this file replaces Macroscope's built-in default ignores rather than extending them.
  3. Raise your cost limit in your workspace billing settings.

Turn off this reminder going forward

Synced from GitHub

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — The change patches Cursor SDK grep conversion to retain matched lines and adds regression coverage. It also introduces a file-level nodeBuiltinImport diagnostic suppression in the adapter tests, which requires human review.

Needs human approval because:

  • Per-review cost limit exceeded (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings, or comment @macroscope-app review this PR to bypass the limit and review now. You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.3 KiB1.3 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded21.6 KiB21.6 KiB0 B (0.0%)24.4 KiB✅
CodexLive turn messages220 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB−29 B (−0.6%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.3 KiB1.2 KiB−29 B (−2.3%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.9 KiB21.9 KiB−41 B (−0.2%)24.4 KiB✅
ClaudeLive turn messages21−1 (−50.0%)8✅

Baseline: 5ef5685 · PR result: 3e5f87b · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

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.

External image Recent review info
External image Run configuration
External image Commits

Reviewing files that changed from the base of the PR and between 2ed92618e60f5ee6ecc694eb834c28105a502d09 and c3ab326d51d25702ed9d7291249f71560c361521.

External image Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
External image Files selected for processing: 3
  • packages/provider-cursor/src/server/adapter.test.ts
  • patches/@cursor__sdk@1.0.35.patch
  • pnpm-workspace.yaml

Limit details: You’ve used all 10 included reviews currently available.

Walkthrough

The workspace pins @cursor/sdk to version 1.0.35 and registers a patch. Adapter tests load the installed ESM or CJS grep converter and check its output for grouped and legacy results, malformed entries, and context lines around gapped matches.

Changes: Cursor grep conversion

Layer / File(s)Summary
SDK version and patch registration
pnpm-workspace.yaml
Pins @cursor/sdk to version 1.0.35 and registers its patch.
Installed converter regression tests
packages/provider-cursor/src/server/adapter.test.ts
Adds a helper that extracts the installed converter from the ESM or CJS chunk. Tests check grouped matches, context lines, reported totals, legacy-shaped results, malformed entries, and context around gapped matches.

Priority: External image Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related PRs

  • pingdotgg/t3code#24920: Changes the same Cursor SDK grep conversion behavior and supports grouped and legacy results.

Suggested reviewers: t3dotgg, macroscopeapp

Merge Risk: External image Minimal · previous review c3ab3

No actionable merge-blocking issue remains in the reviewed grep change. The converter preserves the reported totals and marked context under the established contract.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Linked Issues checkPassedIssue #24919 is the active direct linked issue. The PR patches the Cursor grep converter in the CJS, ESM, and bundled builds. The converter flattens grouped content matches and preserves file, line nu…
Out of Scope Changes checkPassedThe changes stay within the grep-output scope of issue #24919. The SDK patch changes the grep conversion expression. The workspace override pins that patch. The adapter test loader and regression test…
Title checkPassedThe title is concise, uses the required conventional format, and clearly identifies the main change: preserving matched lines in Cursor grep content results.
Description checkPassedThe description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the bug, implementation, linked issue, test results, runtime validation, and untested …
Full details: Linked Issues check

Explanation

Issue #24919 is the active direct linked issue. The PR patches the Cursor grep converter in the CJS, ESM, and bundled builds. The converter flattens grouped content matches and preserves file, line number, match text, context, and the reported total. The PR retains flat-result handling and rejects malformed entries. The adapter tests cover grouped matches, context, totals, flat compatibility, malformed entries, and both module builds. The SDK override pins the patched dependency.

Full details: Out of Scope Changes check

Explanation

The changes stay within the grep-output scope of issue #24919. The SDK patch changes the grep conversion expression. The workspace override pins that patch. The adapter test loader and regression tests verify the conversion boundary. No client, renderer, settings, or unrelated product behavior changes are reported.

Full details: Description check

Explanation

The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the bug, implementation, linked issue, test results, runtime validation, and untested environments.

Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Added 4 commits

  • Merge remote-tracking branch 'origin/main' into fix/issue-249193c2c60a
  • test(cursor): reproduce lost grouped grep match text in the SDK converter (#24919)cf6bfbc
  • fix(cursor): grep content results keep their matched linescc081f9
  • Merge remote-tracking branch 'origin/main' into fix/issue-249193e5f87b