-
Notifications
You must be signed in to change notification settings - Fork 325
pi-fff blocks Pi /new and /resume while waiting for FileFinder scan on session_start #477
Description
Summary
@ff-labs/pi-fff currently blocks Pi session replacement (/new and /resume) because the session_start handler eagerly initializes FileFinder and awaits the initial scan.
In packages/pi-fff/src/index.ts:
pi.on("session_start", async (_event, ctx) => { try { activeCwd = ctx.cwd; if (shouldEnableMentions()) applyEditorMode(ctx); await ensureFinder(activeCwd); } catch (e: unknown) { ctx.ui.notify(...); } });
ensureFinder() calls:
const result = FileFinder.create({ basePath: cwd, ... }); ... await finder.waitForScan(15000);
Because Pi recreates/reloads the session runtime when switching sessions, this means /new and selecting a session from /resume can hang until FileFinder.create() / waitForScan(15000) finishes.
Impact
/resume: after selecting a session, Pi appears stuck before the resumed session is usable./new: after pressing Enter, Pi appears stuck before the new session is usable.- This is especially noticeable in larger workspaces where scanning/watching takes non-trivial time.
- Disabling
@ff-labs/pi-fffmakes/newand/resumefast again.
Expected behavior
Pi session startup should not be blocked by a file index warmup. The finder can still be warmed in the background, and actual fff tool calls / @-mention autocomplete can await the same in-flight initialization if needed.
Suggested fix
Make the session_start warmup non-blocking, e.g. schedule it after the session start event returns:
let lifecycleId = 0; pi.on("session_start", async (_event, ctx) => { try { activeCwd = ctx.cwd; const sessionLifecycleId = ++lifecycleId; if (shouldEnableMentions()) applyEditorMode(ctx); setTimeout(() => { if (sessionLifecycleId !== lifecycleId) return; ensureFinder(activeCwd).catch((e: unknown) => { if (sessionLifecycleId !== lifecycleId) return; ctx.ui.notify( `FFF init failed: ${e instanceof Error ? e.message : String(e)}`, "error", ); }); }, 0); } catch (e: unknown) { ctx.ui.notify( `FFF init failed: ${e instanceof Error ? e.message : String(e)}`, "error", ); } }); pi.on("session_shutdown", async () => { lifecycleId++; destroyFinder(); });
I also found it safer for ensureFinder() to return the local createdFinder after waitForScan() rather than the mutable global finder, so a shutdown during warmup cannot accidentally return a destroyed/replaced finder.
Local validation
I tested a local patch with:
cd packages/pi-fff
npx tsc --noEmitand verified that importing the extension still returns a valid factory. With the patch, /new and /resume are no longer blocked by the scan warmup; the first actual fff operation may still await initialization, which seems like the expected tradeoff.
Related issues I found
I did not find an exact duplicate for pi-fff blocking Pi /new or /resume. Some related pi-fff/watcher issues:
- pi-fff: parallel ffgrep calls can hang indefinitely due to unsynchronized ensureFinder singleton #403 : parallel
ffgrepcalls andensureFindersingleton synchronization - pi-fff / FileFinder({ aiMode: true }) on macOS holds ~1 fd per watched directory, breaks posix_spawn in large trees #439 :
aiMode: truewatcher file descriptor usage on macOS - pi segfaults when @ff-labs/pi-fff watches a directory and files are cloned into it on osx #476 : macOS segfault while watching directories during clone operations
This issue seems specifically about lifecycle behavior in the Pi extension: doing blocking index warmup inside session_start.
Environment
@ff-labs/pi-fff: 0.8.1- Pi: 0.75.1
- Node.js: v24.13.1
- OS: macOS / Darwin arm64