Skip to content

Commit db8d3c6

Browse files
committed
feat(read): surface lines-read/total in Read transcript label
Cursor ReadArgs = { path } only — no offset/limit (cursor-sdk-shared ReadArgsSchema is a strip object). Two same-path reads are re-reads of identical content, never different sections. Derive per-call detail from result instead: count content lines and compare to totalLines. Title becomes e.g. "handlers.go (2000/5000 lines)" so a reader can spot redundant re-reads and see truncation at a glance. Also stores linesReturned + fileSize in block metadata, and documents the no-range constraint in a comment above the adapter so the question does not recur.
1 parent fbba872 commit db8d3c6

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

src/provider/stream-map.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,15 @@ const NATIVE_ADAPTERS: Record<string, NativeToolAdapter> = {
313313
},
314314
},
315315
// Cursor `read` → opencode `read`.
316+
//
317+
// Cursor's read tool takes ONLY `{ path }` — there is no `offset`/`limit`/
318+
// line-range arg (see `@cursor/sdk` `ReadArgsSchema`, a `{ path: string }`
319+
// "strip" object). The model therefore cannot request a sub-range, so two
320+
// same-path reads in a transcript are RE-READS of the same content, never
321+
// "different sections". The only per-call detail available is in the result
322+
// `value` (`content`, `totalLines`, `fileSize`); we derive the lines-read
323+
// count from `content` and surface `linesReturned/totalLines` in the title
324+
// so a reader can see how much was read and spot redundant re-reads.
316325
read: {
317326
tool: "read",
318327
input: (args) => ({ filePath: strField(args, "path") ?? "" }),
@@ -321,12 +330,20 @@ const NATIVE_ADAPTERS: Record<string, NativeToolAdapter> = {
321330
if (content === undefined) return null;
322331
const filePath = strField(args, "path") ?? "";
323332
const totalLines = numField(value, "totalLines");
333+
const fileSize = numField(value, "fileSize");
334+
const linesReturned = content.split("\n").length;
335+
const lineLabel =
336+
totalLines !== undefined
337+
? `${linesReturned}/${totalLines} lines`
338+
: `${linesReturned} lines`;
324339
return {
325-
title: filePath,
340+
title: `${filePath} (${lineLabel})`,
326341
metadata: {
327342
preview: content.split("\n").slice(0, 20).join("\n"),
328343
loaded: [] as string[],
344+
linesReturned,
329345
...(totalLines !== undefined ? { totalLines } : {}),
346+
...(fileSize !== undefined ? { fileSize } : {}),
330347
},
331348
output: content,
332349
};

test/stream-map.test.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -894,9 +894,30 @@ describe("native tool mapping (blocks)", () => {
894894
input: JSON.stringify({ filePath: "/a.ts" }),
895895
});
896896
expect(foldedResult(result)).toMatchObject({
897-
title: "/a.ts",
897+
title: "/a.ts (2/2 lines)",
898898
output: "l1\nl2",
899-
metadata: { preview: "l1\nl2", totalLines: 2 },
899+
metadata: {
900+
preview: "l1\nl2",
901+
totalLines: 2,
902+
linesReturned: 2,
903+
fileSize: 6,
904+
},
905+
});
906+
});
907+
908+
it("read title shows lines-read/total when Cursor truncates the file", async () => {
909+
const { result } = await mapTool(
910+
"read",
911+
{ path: "/a.ts" },
912+
{
913+
status: "success",
914+
value: { content: "l1", totalLines: 10, fileSize: 99 },
915+
},
916+
);
917+
expect(foldedResult(result)).toMatchObject({
918+
title: "/a.ts (1/10 lines)",
919+
output: "l1",
920+
metadata: { linesReturned: 1, totalLines: 10, fileSize: 99 },
900921
});
901922
});
902923

0 commit comments

Comments
 (0)