From 08cc6f328711cd93ce9fa53351266c29a0b0a341 Mon Sep 17 00:00:00 2001 From: Jeevan Mohan Pawar <35501212+jeevan6996@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:00:33 +0100 Subject: [PATCH] fix(read/edit): make compact line prefix unambiguous for tab-indented files (#613) --- src/tools/FileEditTool/prompt.ts | 2 +- src/utils/file.test.ts | 51 ++++++++++++++++++++++++++++++++ src/utils/file.ts | 4 +-- 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/utils/file.test.ts diff --git a/src/tools/FileEditTool/prompt.ts b/src/tools/FileEditTool/prompt.ts index 5a4d93ad..e922f5ce 100644 --- a/src/tools/FileEditTool/prompt.ts +++ b/src/tools/FileEditTool/prompt.ts @@ -11,7 +11,7 @@ export function getEditToolDescription(): string { function getDefaultEditDescription(): string { const prefixFormat = isCompactLinePrefixEnabled() - ? 'line number + tab' + ? 'line number + arrow' : 'spaces + line number + arrow' const minimalUniquenessHint = process.env.USER_TYPE === 'ant' diff --git a/src/utils/file.test.ts b/src/utils/file.test.ts new file mode 100644 index 00000000..d748bd27 --- /dev/null +++ b/src/utils/file.test.ts @@ -0,0 +1,51 @@ +import { afterEach, describe, expect, mock, test } from 'bun:test' + +async function importFileModuleWithKillswitchEnabled( + killswitchEnabled: boolean, +) { + mock.module('../services/analytics/growthbook.js', () => ({ + getFeatureValue_CACHED_MAY_BE_STALE: () => killswitchEnabled, + })) + + return import(`./file.js?ts=${Date.now()}-${Math.random()}`) +} + +afterEach(() => { + mock.restore() +}) + +describe('addLineNumbers', () => { + test('uses unambiguous arrow compact prefix and preserves leading tabs', async () => { + const { addLineNumbers } = await importFileModuleWithKillswitchEnabled(false) + + const result = addLineNumbers({ + content: '\tfirst\n\t\tsecond', + startLine: 41, + }) + + expect(result).toBe('41→\tfirst\n42→\t\tsecond') + }) + + test('keeps padded arrow format when compact mode is disabled', async () => { + const { addLineNumbers } = await importFileModuleWithKillswitchEnabled(true) + + const result = addLineNumbers({ + content: 'alpha\nbeta', + startLine: 1, + }) + + expect(result).toBe(' 1→alpha\n 2→beta') + }) +}) + +describe('stripLineNumberPrefix', () => { + test('strips compact arrow, padded arrow, and legacy tab prefixes', async () => { + const { stripLineNumberPrefix } = await importFileModuleWithKillswitchEnabled( + false, + ) + + expect(stripLineNumberPrefix('41→\tfirst')).toBe('\tfirst') + expect(stripLineNumberPrefix(' 2→beta')).toBe('beta') + expect(stripLineNumberPrefix('7\t\tlegacy-tab')).toBe('\tlegacy-tab') + }) +}) diff --git a/src/utils/file.ts b/src/utils/file.ts index 773c3f41..72bba7c4 100644 --- a/src/utils/file.ts +++ b/src/utils/file.ts @@ -267,7 +267,7 @@ export async function suggestPathUnderCwd( } /** - * Whether to use the compact line-number prefix format (`N\t` instead of + * Whether to use the compact line-number prefix format (`N→` instead of * ` N→`). The padded-arrow format costs 9 bytes/line overhead; at * 1.35B Read calls × 132 lines avg this is 2.18% of fleet uncached input * (bq-queries/read_line_prefix_overhead_verify.sql). @@ -303,7 +303,7 @@ export function addLineNumbers({ if (isCompactLinePrefixEnabled()) { return lines - .map((line, index) => `${index + startLine}\t${line}`) + .map((line, index) => `${index + startLine}→${line}`) .join('\n') }