You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tui: @ file search drops filename text after # even without a line suffix #60304
TUI @ file autocomplete treats the last # as the start of a line-number suffix even when what follows is not a line number. For a valid filename such as report#2026.md, it searches only for report; for #notes.md, it searches for an empty base. The suffix of the filename cannot narrow the results, and with the 20-result cap the intended file may be absent.
Reproduction
On dev commit 82666b6612cdb2ba13a7a26e150b21e897a80373, run from packages/tui:
bun -e 'import {parseFileLineRange} from "./src/prompt/parse.ts"; for (const q of ["report#2026.md", "#notes.md", "report#2026.md#3"]) console.log(q,JSON.stringify(parseFileLineRange(q)))'
Expected: the first two inputs keep the full filename as the search base; a valid #3 line suffix is still recognized. The autocomplete implementation passes this base to file search. To confirm the user-visible consequence, I used the actual ripgrep-backed FileSystemSearch with 40 reportNN.txt files plus report#2026.md and a limit of 20: searching report returned 20 other files, while searching report#2026.md returned the target. Ranking may vary by filesystem. This verifies the parser and real search separately, not a full interactive TUI run. The parser blob matches current upstream dev.
Thanks for the clear report. I reproduced this on v2.2.1 and on the latest dev (ccd406b).
Steps: a project with 40 reportNN.txt files plus report#2026.md, then type @report#2026.md in the prompt. The autocomplete shows report00.txt, report01.txt and so on, and report#2026.md is not among the visible entries. As you found, parseFileLineRange in packages/tui/src/prompt/parse.ts returns the text before the last # even when what follows isn't a line range, so the search runs on report only.
The recording and screenshots below show it, and the script below reproduces it.
Related: #58033 is a different bug in the same #N line-range handling.
recording.mp4
Reproduction script (drive.ts)
// Repro for #60304: `@` file autocomplete drops the text after '#' even when it is not a line suffix.import{Effect}from"effect"import{OpenCodeDriver}from"opencode-drive"constfiles: Record<string,string>={"report#2026.md": "# target\n"}for(leti=0;i<40;i++)files[`report${String(i).padStart(2,"0")}.txt`]=`filler ${i}\n`exportdefaultOpenCodeDriver.use({
...(process.env.OC_DEV ? {opencode: {dev: process.env.OC_DEV}} : {}),project: {git: true, files },tui: {recording: true,keypressOverlay: true},},({ ui })=>Effect.gen(function*(){yield*Effect.sleep("2 seconds")// Type the full, valid filename after '@'yield*ui.type("@report#2026.md")yield*Effect.sleep("2 seconds")yield*ui.screenshot("full-filename-query")// Bug: the list shows report00.txt, report01.txt, ... (search base "report") instead of report#2026.mdyield*Effect.sleep("1 second")}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
opencodejr commentedon Oct 29, 2026
Thanks for the clear report. I reproduced this on v2.2.1 and on the latest
dev(ccd406b).Steps: a project with 40
reportNN.txtfiles plusreport#2026.md, then type@report#2026.mdin the prompt. The autocomplete showsreport00.txt,report01.txtand so on, andreport#2026.mdis not among the visible entries. As you found,parseFileLineRangeinpackages/tui/src/prompt/parse.tsreturns the text before the last#even when what follows isn't a line range, so the search runs onreportonly.The recording and screenshots below show it, and the script below reproduces it.
Related: #58033 is a different bug in the same
#Nline-range handling.recording.mp4
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.