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

open9
main
fix/issue-24919
opened18d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 2d 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

23 successful · 9 skipped · 1 neutral
All checks passed
b071e61

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 has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies.

Synced from GitHub

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — The change patches Cursor grep-result conversion and adds regression coverage. The new test file disables the nodeBuiltinImport diagnostic at file scope, requiring human review of that suppression.

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.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded21.5 KiB21.5 KiB0 B (0.0%)24.4 KiB✅
CodexLive turn messages110 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.9 KiB21.9 KiB0 B (0.0%)24.4 KiB✅
ClaudeLive turn messages110 (0.0%)8✅

Baseline: 3a8c250 · PR result: b071e61 · 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.

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.

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

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
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.

Added 6 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
  • Merge remote-tracking branch 'origin/main' into fix/issue-249195bb0534
  • test(cursor): keep the SDK grep converter check out of the adapter testsb071e61

The automated pipeline stopped on this pull request and will not merge it:

someone else pushed to fix/issue-24919 (head is now 3e5f87b7cf); the pipeline does not build on commits it did not review

It stays open for a person to finish or close.