Skip to content

Commit 856b554

Browse files
committed
fix(copilot): bound grep context expansion
1 parent 869f57d commit 856b554

2 files changed

Lines changed: 30 additions & 7 deletions

File tree

‎apps/sim/lib/mothership/agent-cli/engines/universal-grep.test.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,26 @@ describe('universal grep', () => {
9898
expect(result).toMatchObject({ exitCode: 0, stdout: '1 (files=1)' })
9999
})
100100

101+
it('bounds overlapping context windows while counting matches beyond the output limit', async () => {
102+
const result = await runEngine(
103+
'grep',
104+
['needle'],
105+
runtimeWith({
106+
'/api/v2/files': { data: [{ id: 'input' }], nextCursor: null },
107+
'/api/v2/files/input/text': {
108+
data: { text: ['header', ...Array.from({ length: 30_000 }, () => 'needle')].join('\n') },
109+
},
110+
}),
111+
{ scope: 'files', C: '30000', limit: '3' }
112+
)
113+
expect(result).toMatchObject({
114+
exitCode: 0,
115+
stdout:
116+
'files/input:1: header\nfiles/input:2: needle\nfiles/input:3: needle\n' +
117+
'[2 of 30000 matching lines shown — narrow with --scope, --in, or a tighter pattern]',
118+
})
119+
}, 15_000)
120+
101121
it('fails an exhausted scan budget instead of returning a complete count', async () => {
102122
vi.spyOn(performance, 'now').mockReturnValueOnce(0).mockReturnValue(6_000)
103123
const result = await runEngine('grep', ['id'], runtimeWith(CATALOG), {

‎apps/sim/lib/mothership/agent-cli/engines/universal-grep.ts‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,9 @@ export const universalGrepCommand: AgentCliEngine = {
589589
for (const resource of candidates) {
590590
if (resource.text === null) continue
591591
const lines = resource.text.split('\n')
592-
const selected = new Set<number>()
592+
const selected = new Map<number, boolean>()
593+
const remainingLines = limit - out.length
594+
let nextContextLine = 0
593595
for (let i = 0; i < lines.length; i++) {
594596
if (checkScan() - lastYieldAt >= SCAN_YIELD_INTERVAL_MS) {
595597
await sleep(0)
@@ -600,19 +602,20 @@ export const universalGrepCommand: AgentCliEngine = {
600602
perScope.set(resource.scope, (perScope.get(resource.scope) ?? 0) + 1)
601603
if (countOnly) continue
602604
for (
603-
let j = Math.max(0, i - context.before);
604-
j <= Math.min(lines.length - 1, i + context.after);
605+
let j = Math.max(nextContextLine, i - context.before);
606+
j <= Math.min(lines.length - 1, i + context.after) && selected.size < remainingLines;
605607
j++
606608
) {
607-
selected.add(j)
609+
selected.set(j, false)
610+
nextContextLine = j + 1
608611
}
612+
if (selected.has(i)) selected.set(i, true)
609613
}
610614
if (countOnly || selected.size === 0 || out.length >= limit) continue
611615
const header = `${resource.scope}/${resource.label}${resource.label === resource.id ? '' : ` (${resource.id})`}`
612-
for (const i of [...selected].sort((a, b) => a - b)) {
613-
if (out.length >= limit) break
616+
for (const [i, isMatch] of selected) {
614617
out.push(`${header}:${i + 1}: ${clip(lines[i])}`)
615-
if (matches(lines[i])) shownMatches++
618+
if (isMatch) shownMatches++
616619
}
617620
}
618621
checkScan()

0 commit comments

Comments
 (0)