What happened?
When typing @ inside the interactive CLI to open the file picker, the main thread is frozen for the entire duration of the initial file crawl. On a medium-sized workspace (~3k files) the stall is around 100–150 ms; on a home directory with ~100k files the CLI becomes unresponsive for several seconds (keystrokes drop, cursor stops blinking, Ink stops rendering).
Root cause traces to two synchronous pieces of work on the main thread:
fdir recursive crawl in packages/core/src/utils/filesearch/crawler.ts.
new AsyncFzf(allFiles, ...) in packages/core/src/utils/filesearch/fileSearch.ts:199 — the Async prefix is misleading; construction is synchronous and dominates for large file counts.
Also, the 200 ms "Loading suggestions…" timer in useAtCompletion.ts only covers search() and not initialize(), so during the freeze there's no visual feedback at all — the CLI just appears stuck.
What did you expect to happen?
The @ picker should never block the render loop. On a cold start against a large tree it's OK to show a loading indicator for a moment, but the prompt, cursor, and other UI should remain responsive the whole time. On subsequent presses results should appear instantly.
Client information
Client Information
Qwen Code v0.14.5
macOS 15.1.1 (24B2091, arm64)
Node.js v22.17.0
Bug is reproducible from git main at 30c5eeaf2.
Login information
N/A — bug is in the client UI, independent of auth.
Anything else we need to know?
Repro: QWEN_WORKING_DIR=~ npm start (or just run qwen from ~), then type @.
I've pushed a WIP fix to my fork that moves the crawl + fzf index into a worker_threads worker: https://github.com/callmeYe/qwen-code/tree/feat/filesearch-worker-p1. Happy to open a PR once the maintainers confirm the direction.
Key observations from that work:
- Empirically on macOS:
fdir is faster than ripgrep via child_process for small/medium trees (<50k files) because spawn+IPC overhead dominates, but ripgrep wins 3-4× past ~100k files.
- A persistent file-index cache isn't currently necessary — moving the work off the main thread is enough to restore responsiveness on typical workspaces.
What happened?
When typing
@inside the interactive CLI to open the file picker, the main thread is frozen for the entire duration of the initial file crawl. On a medium-sized workspace (~3k files) the stall is around 100–150 ms; on a home directory with ~100k files the CLI becomes unresponsive for several seconds (keystrokes drop, cursor stops blinking, Ink stops rendering).Root cause traces to two synchronous pieces of work on the main thread:
fdirrecursive crawl inpackages/core/src/utils/filesearch/crawler.ts.new AsyncFzf(allFiles, ...)inpackages/core/src/utils/filesearch/fileSearch.ts:199— theAsyncprefix is misleading; construction is synchronous and dominates for large file counts.Also, the 200 ms "Loading suggestions…" timer in
useAtCompletion.tsonly coverssearch()and notinitialize(), so during the freeze there's no visual feedback at all — the CLI just appears stuck.What did you expect to happen?
The
@picker should never block the render loop. On a cold start against a large tree it's OK to show a loading indicator for a moment, but the prompt, cursor, and other UI should remain responsive the whole time. On subsequent presses results should appear instantly.Client information
Client Information
Bug is reproducible from git
mainat30c5eeaf2.Login information
N/A — bug is in the client UI, independent of auth.
Anything else we need to know?
Repro:
QWEN_WORKING_DIR=~ npm start(or just runqwenfrom~), then type@.I've pushed a WIP fix to my fork that moves the crawl + fzf index into a
worker_threadsworker: https://github.com/callmeYe/qwen-code/tree/feat/filesearch-worker-p1. Happy to open a PR once the maintainers confirm the direction.Key observations from that work:
fdiris faster thanripgrepviachild_processfor small/medium trees (<50k files) because spawn+IPC overhead dominates, butripgrepwins 3-4× past ~100k files.