fff: a file search library for agents, editors and Neovim
The fastest and the most accurate file search SDK for AI agents, Neovim, Rust, C, Python, Bun and NodeJS
At a glance
- What is it?
- fff is an MIT-licensed Rust file search toolkit with bindings for Python, Node, Bun and C, plus an MCP server and a Neovim plugin. It is built for long-running processes that search repeatedly, and the documentation is honest that this is where its speed comes from.
- Who is it for?
- Adopt fff if you are building an agent harness, an editor integration or any long-running process that issues file search queries more than once, and you are willing to run a background watcher over the repository. Do not adopt it for one-shot command-line searches, where ripgrep or fzf already do the job without keeping an index in memory.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem fff targets: repeated search inside a live process
The README makes a specific claim about scope: fff is faster than CLIs like ripgrep and fzf in any long-running process that searches more than once. That qualifier is the whole argument. A command-line tool pays process startup and directory traversal on every invocation, which is fine when a human runs it a few times. An agent harness or an editor pays that cost on every tool call, and the cost compounds. fff moves the traversal into a persistent process and keeps a lightweight in-memory content index plus a background watcher, so later queries hit structures that are already built. The README states the project began as a Neovim plugin and then found that AI harnesses and code editors wanted the same thing as a library. The audience follows from that: people embedding search into a host process, not people typing grep into a terminal. The repository description lists Rust, C, Python, Bun and NodeJS as targets, so the intended consumer is a program, and the Neovim plugin and MCP server are two shipped front ends over the same core.
What the index actually holds: frecency, definitions and git state
Three mechanisms are described in the MCP section and they are the parts that distinguish fff from a plain path matcher. First, frecency memory: files that get opened rank higher on subsequent queries, and the README says warm-up from git touch history runs automatically. That means the ranking is seeded from repository history before the user has done anything, which is a reasonable default and also a bias worth knowing about. Second, definition-first hinting: lines that look like code definitions are classified on the Rust side, and the stated reason is that this avoids regex overhead in the prompt. The classification happens in the library, not in a pattern the caller supplies. Third, git-aware annotations: modified, untracked and staged files are tagged so an agent can prioritise what is currently being changed. Search behaviour includes smart-case with an automatic fuzzy fallback. The README gives the example that a query for IsOffTheRecord finds snake_case variants, and that a zero-match query is retried as fuzzy to surface approximate hits. The Pi extension description adds a detail the MCP section does not: fffind matches the whole repo-relative path, not just the filename, and a weak-match detector flags scattered fuzzy noise before it reaches the agent's context. That detector is the most interesting design choice here, because fuzzy fallback and context pollution pull in opposite directions.
Installing the MCP server and pointing a client at it
The README gives two install routes. The one-line installer for Linux and macOS is curl -L https://dmtrkovalenko.dev/install-fff-mcp.sh | bash, and the Windows equivalent is irm https://raw.githubusercontent.com/dmtrKovalenko/fff/main/install-mcp.ps1 | iex. The scripts are in the repository as install-mcp.sh and install-mcp.ps1, and the README says they print the exact wiring instructions for your client. Homebrew is the second route: brew install dmtrKovalenko/fff/fff-mcp, with brew upgrade fff-mcp after new stable releases. The formula lives at Formula/fff-mcp.rb and the README states it is auto-bumped on every stable release by the bump-homebrew-formula job in .github/workflows/release.yaml, installing a prebuilt binary from GitHub releases. For Codex, registration uses an absolute path because desktop sessions may not inherit the interactive shell PATH. With Homebrew that is codex mcp add fff -- "$(brew --prefix)/bin/fff-mcp", and with the installer it is codex mcp add fff -- "$HOME/.local/bin/fff-mcp". The resulting entry in ~/.codex/config.toml looks like [mcp_servers.fff] with a command key set to the installed path. The README notes you should restart Codex or start a new task so it loads the server, and that once connected the agent picks up ffgrep, fffind and fff-multi-grep. It also suggests a line for CLAUDE.md: "For any file search or grep in the current git-indexed directory, use fff tools."
The Pi extension and the Neovim plugin share one frecency database
The Pi extension installs with pi install npm:@ff-labs/pi-fff and runs in one of three modes, switchable at runtime with /fff-mode. tools-and-ui is the default and adds ffgrep and fffind while replacing @-mention autocomplete with fff. tools-only injects the tools and keeps pi's native editor autocomplete. override replaces pi's built-in grep, find and multi_grep with the fff implementations. Configuration is available through the environment variables PI_FFF_MODE, FFF_FRECENCY_DB and FFF_HISTORY_DB, or the flags --fff-mode, --fff-frecency-db and --fff-history-db. The README states that the databases default to your existing fff.nvim ones when present, and otherwise to ~/.pi/agent/fff/. That shared default is the most consequential detail in the section: a Neovim user who installs the Pi extension inherits the ranking built up from their editor usage. It is convenient when the two tools are used on the same repositories and surprising when they are not. The extension also exposes /fff-health for picker, frecency and git integration status, and /fff-rescan to force a rescan. The tool parameters are documented: ffgrep accepts path, exclude (comma, space or array, with an optional leading !), caseSensitive, context and cursor pagination, auto-detects regex, falls back to fuzzy on zero exact matches, and rejects .*-style wildcard-only patterns up front. That rejection is a deliberate guard against a pattern that would match everything.
Where fff is the wrong tool, and how it differs from ripgrep and fzf
The README's own framing is the clearest limitation. fff is faster in any long-running process that searches more than once. Run it once from a shell and the advantage is gone, because you paid for index construction and a watcher you will not use again. For a single grep in a CI step, ripgrep remains the simpler answer: it starts, searches and exits, with no background process, no database file and no state to invalidate. fzf is a different kind of alternative. It is a generic fuzzy filter that reads lines from stdin, so it composes with anything that produces text and does not need to know about your repository. fff is repository-aware by design, which buys the frecency ranking, the git annotations and the definition classification, and costs you the ability to point it at arbitrary input. The second limitation is the watcher itself. A background watcher over a large or rapidly changing tree has to keep up, and the README does not describe what happens when it falls behind, nor how the in-memory content index is bounded. The presence of /fff-rescan in the Pi extension suggests a manual rescan is the documented remedy when the index is stale, which is a reasonable escape hatch but also an admission that staleness is possible. Third, the release history is dominated by nightly builds. The recent releases listed are 0.10.7-nightly.7f8537e, nightly (0.9.2-nightly.4e8f447) and 0.10.7-nightly.d84c0a1, with the stable Homebrew upgrade path explicitly described as following new stable releases. Anyone pinning a version should check which tags are stable rather than assuming the newest tag is.
Maintenance cost, release cadence and the MIT licence
The maintenance story is mixed in a way worth stating plainly. On one side, the Homebrew formula is auto-bumped on every stable release through the bump-homebrew-formula job, so the packaged binary tracks releases without manual work. On the other side, the visible release list is nightly-heavy, and the README instructs Homebrew users to run brew upgrade fff-mcp after new stable releases, which implies stable releases are less frequent than nightly ones. If you depend on fff as a library rather than as a binary, you are tracking a project whose public cadence is nightly. The frecency and history databases are persistent state. The Pi extension documents FFF_FRECENCY_DB and FFF_HISTORY_DB and a default location of ~/.pi/agent/fff/, and it will reuse fff.nvim databases when they exist. That state is machine-local and needs a decision about whether it belongs in a backup, a container image or neither. The licence is MIT, which permits commercial and closed-source use and requires preserving the copyright notice and licence text. That is a statement about the licence identifier in the repository, not legal advice; if you are redistributing a modified binary or embedding the library, have your own counsel read the LICENSE file.
Editorial conclusion
Adopt fff if you are building an agent harness, an editor integration or any long-running process that issues file search queries more than once, and you are willing to run a background watcher over the repository. Do not adopt it for one-shot command-line searches, where ripgrep or fzf already do the job without keeping an index in memory. Before wiring it into anything, verify the frecency database path on your platform, confirm that the MCP client can reach the absolute binary path, and read install-mcp.sh and install-mcp.ps1 rather than piping them into a shell.
Community notes