dmtrKovalenko / dmtrKovalenko/fff

pi-fff blocks Pi /new and /resume while waiting for FileFinder scan on session_start

Open
#477 4 comments 3 reactions 0 assignees View on GitHub
triaged
Dominant language
Rust
Stars
10.7k
Forks
446
Avg merge
1d 21h
Merged PRs (30d)
39

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`:

```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:

```ts
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-fff` makes `/new` and `/resume` fast 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:

```ts
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:

```bash
cd packages/pi-fff
npx tsc --noEmit
```

and 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:

- #403: parallel `ffgrep` calls and `ensureFinder` singleton synchronization
- #439: `aiMode: true` watcher file descriptor usage on macOS
- #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

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.