How to Use FFF with AI Agents like Claude Code: Complete Integration Guide
You can control FFF (Fast File Finder) from AI agents like Claude Code by invoking its pure Lua API through Neovim's RPC interface, allowing headless file picking and remote command execution without modifying the plugin.
FFF is a high-performance Neovim file picker developed in the dmtrKovalenko/fff repository that combines a Rust core for indexing and fuzzy matching with a thin Lua UI layer. Because the plugin exposes its functionality through pure Lua functions, AI agents can programmatically trigger file searches and consume results via standard Neovim remote APIs.
Architecture Overview: Rust Core and Lua UI
FFF separates performance-critical operations from editor integration. The Rust core (located in crates/fff-core/ and crates/fff-nvim/) handles indexing, frecency scoring, Git status caching, and fuzzy matching. This compiled layer exposes low-level functions like search, index, and git_status in crates/fff-nvim/src/lib.rs.
The Lua UI layer consumes these capabilities through a tiny FFI bridge defined in lua/fff/rust/init.lua. This bridge loads the compiled shared library (fff_nvim.so) using ffi.load and exposes its symbols to the Lua runtime. The public API surface lives entirely in lua/fff/main.lua, which exports stable functions including:
require'fff'.find_files()- Opens the file pickerrequire'fff'.search()- Generic search interfacerequire'fff'.change_directory()- Directory navigation
Because these entry points are pure Lua, any external process capable of executing Lua within Neovim can drive FFF programmatically.
Integration Methods for AI Agents
AI agents like Claude Code can interact with FFF through multiple RPC-based approaches, depending on whether they need to spawn new Neovim instances or attach to existing editor sessions.
Headless Neovim Execution
For one-shot file selection tasks, agents can spawn a headless Neovim process that executes FFF commands and returns results via stdout:
nvim --headless +"lua require'fff'.find_files({prompt='Pick a file: '})" +qa
This approach uses --headless to suppress the UI and +qa to quit after execution. The Rust core performs the fuzzy search, and the selected path prints to standard output for the agent to parse.
Attaching to Running Sessions with neovim-remote
When integrating with an active editor instance, Claude Code can use nvr (neovim-remote) to send commands through the Unix socket defined in NVIM_LISTEN_ADDRESS:
nvr --remote-send ":lua require'fff'.search({prompt='Search symbols: '})<CR>"
This method preserves the user's existing editor state while allowing the AI to trigger picker windows. The agent can poll for results using nvr --remote-expr to capture the selection without blocking the main workflow.
Custom Lua Scripts for Tool Integration
For complex orchestration, agents can load custom Lua scripts that wrap FFF calls with error handling and result formatting:
-- Save as fff_agent.lua
local fff = require'fff'
local function pick_file()
local ok, result = pcall(fff.find_files, {prompt = 'Select file → '})
if ok and result then
print('USER_SELECTION:' .. result)
return result
else
print('FFF_CANCELLED')
return nil
end
end
-- Expose via keymap or RPC
vim.api.nvim_set_keymap('n', '<Leader>fa',
'<Cmd>lua pick_file()<CR>', { noremap = true, silent = true })
Claude Code can request the user press <Leader>fa or call pick_file() directly via RPC, parsing the USER_SELECTION: prefix to identify valid file paths.
Python Client Integration via pynvim
External Python scripts using the pynvim library can attach to Neovim sockets and execute FFF functions synchronously:
import neovim
def fff_pick(socket_path='/tmp/nvim'):
nvim = neovim.attach('socket', path=socket_path)
result = nvim.exec_lua('return require"fff".find_files({prompt="Pick: "})')
return result
selected = fff_pick()
print(f'AI agent received: {selected}')
This pattern allows Claude Code to embed Python tool scripts that block until the user selects a file, then feed that path into subsequent generation steps.
Key Source Files and API Contracts
Understanding the internal structure helps AI agents predict behavior when calling FFF:
| File | Role | Key Details |
|---|---|---|
lua/fff/main.lua |
Public Lua API | Exports find_files(), search(), change_directory() with optional parameter tables |
lua/fff/rust/init.lua |
FFI loader | Calls ffi.load('fff_nvim') to bind the Rust shared library |
crates/fff-nvim/src/lib.rs |
Rust FFI definitions | Implements search, index, git_status functions compiled to fff_nvim.so |
crates/fff-core/src/score.rs |
Fuzzy algorithm | Core matching logic using frizbee integration |
crates/fff-core/src/git.rs |
Git integration | Status detection and caching for repository-aware file listing |
lua/fff/picker_ui.lua |
UI rendering | Handles buffer management, keymaps, and layout calculations |
plugin/fff.lua |
Bootstrap | Vim-script entry point that initializes the plugin |
The Lua API in lua/fff/main.lua represents the only stable integration surface. Agents should rely exclusively on these documented functions rather than internal Rust or UI implementation details.
Implementation Workflow
To integrate FFF with Claude Code or similar agents:
- Install FFF in Neovim using your plugin manager (Packer, lazy.nvim, etc.)
- Build the Rust core: Run
make buildto producetarget/release/libfff_nvim.so - Expose the RPC socket: Set
export NVIM_LISTEN_ADDRESS=/tmp/nvimbefore starting Neovim - Choose an integration method: Use headless mode for scripts,
nvrfor active sessions, or Python clients for complex orchestration - Parse return values: Handle
nilreturns for cancellations and capture file paths from stdout or RPC responses
Because FFF uses Neovim's standard remote API rather than custom protocols, it works with any LLM-driven tool capable of shell execution or socket communication.
Summary
- FFF exposes a pure Lua API through
lua/fff/main.luathat accepts optional configuration tables for prompts and working directories - The Rust core handles performance-critical operations while the FFI bridge in
lua/fff/rust/init.luaconnects it to Neovim's Lua runtime - AI agents can invoke FFF via headless Neovim commands, neovim-remote (
nvr), or Python clients (pynvim) using standard RPC mechanisms - The plugin requires no modifications for AI integration; it responds to the same Lua functions whether called interactively or programmatically
- Stable integration depends on calling documented functions like
require'fff'.find_files()rather than internal Rust implementation details
Frequently Asked Questions
Can Claude Code use FFF without installing additional plugins?
Yes. FFF works with Claude Code through standard Neovim RPC calls. As long as FFF is installed in the target Neovim instance, Claude Code can trigger it via headless mode or remote sockets without requiring additional wrapper plugins or Python dependencies.
How does FFF handle Git repository information when called by an AI agent?
FFF caches Git status data in the Rust core (crates/fff-core/src/git.rs) and updates it asynchronously. When an AI agent calls find_files() or search(), the Rust layer already has repository state indexed, so file listings include status symbols (modified, staged, untracked) without additional latency in the RPC response.
What happens if the user cancels the FFF picker during an AI-driven workflow?
The Lua functions in lua/fff/main.lua return nil when the picker is cancelled or an error occurs. AI agents should wrap calls in pcall() to catch errors and check for nil returns to distinguish between user cancellation and successful selection.
Is it possible to customize the fuzzy matching behavior when using FFF programmatically?
The fuzzy matching algorithm lives in crates/fff-core/src/score.rs and uses frecency scoring by default. While the Rust core's scoring parameters are fixed at compile time, the Lua API accepts options like cwd (current working directory) and prompt strings. For custom scoring, you would need to modify the Rust source and rebuild fff_nvim.so, as the FFI bridge binds directly to compiled symbols.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →