### Install C library with make Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Installs the C library using make with different PREFIX and DESTDIR options for system-wide, user-local, or staged installs. On Unix, installs libfff_c.{so,dylib} and libfff_c.a into $(PREFIX)/lib; on Windows, installs fff_c.dll into $(BINDIR) and fff_c.lib into $(PREFIX)/lib. Also installs the header into $(PREFIX)/include/fff.h and fff_c.pc into $(PKGCONFIGDIR). Remove with make uninstall, which honours the same PREFIX and DESTDIR. ```bash # System-wide (needs sudo): sudo make install # User-local, no sudo: make install PREFIX=$HOME/.local # Staged install for packagers: make install DESTDIR=/tmp/pkgroot PREFIX=/usr ``` -------------------------------- ### fff Plugin Usage Examples Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-nvim.md Usage examples for the fff plugin's programmatic API, including setup, file search, and content search with pagination. ```APIDOC ## `require('fff').setup({...})` ### Description Configures the fff plugin with options for layout, preview, keymaps, frecency, history, git, grep, suggestions, debug, logging, and highlighting. ### Method setup ### Parameters - **opts** (table) - Optional - Configuration table with fields for layout, preview, keymaps, frecency, history, git, grep, suggestions, debug, logging, and hl. ### Example ```lua require('fff').setup({ debug = { enabled = true, show_scores = true } }) ``` ``` ```APIDOC ## `require('fff').file_search(query, opts)` ### Description Performs a programmatic file search. Returns a result object with items. Never throws; invalid options raise a Lua error. ### Method file_search ### Parameters - **query** (string) - Required - The search query. - **opts** (table) - Optional - Options table. Supported fields: `mode` (string: 'files', 'directories', or 'mixed'), `max_results` (number). ### Returns - **result** (table) - Contains an `items` array. Each item has `type` and `relative_path` fields. ### Example ```lua local r = require('fff').file_search('button', { mode = 'mixed', max_results = 50 }) for _, item in ipairs(r.items) do print(item.type, item.relative_path) end ``` ### Error Handling - Invalid `opts.mode` raises a Lua error: `"fff.file_search: opts.mode must be 'files', 'directories', or 'mixed', got bogus"`. ``` ```APIDOC ## `require('fff').content_search(query, opts)` ### Description Performs a programmatic content (grep) search with pagination support. Returns a page object with a `next_file_offset` field for pagination. ### Method content_search ### Parameters - **query** (string) - Required - The search query. - **opts** (table) - Optional - Options table. Supported fields: `mode` (string: 'plain'), `smart_case` (boolean), `file_offset` (number) for pagination. ### Returns - **page** (table) - Contains search results and a `next_file_offset` field. If `next_file_offset` is greater than 0, more results are available. ### Example ```lua local page = require('fff').content_search('TODO', { mode = 'plain', smart_case = true }) while page.next_file_offset and page.next_file_offset > 0 do page = require('fff').content_search('TODO', { mode = 'plain', file_offset = page.next_file_offset }) end ``` ``` -------------------------------- ### Install fff with bun add Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/fff-bun/README.md Install the @ff-labs/fff-bun package using Bun. The correct native binary is installed automatically via platform-specific packages. ```bash bun add @ff-labs/fff-bun ``` -------------------------------- ### Install @ff-labs/fff-node Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Install the Node & Bun SDK package using npm or bun. ```bash npm install @ff-labs/fff-node # or bun add @ff-labs/fff-node ``` -------------------------------- ### Run standalone example Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/fff-python/README.md Runs the standalone example script against the current directory. ```bash cd packages/fff-python uv run python examples/basic.py . ``` -------------------------------- ### Plugin setup and configuration Source: https://github.com/dmtrkovalenko/fff/blob/main/doc/fff.nvim.txt Initialize the plugin using the setup function to override default settings for paths, layout, and behavior. ```lua require('fff').setup({ base_path = vim.fn.getcwd(), prompt = '🪿 ', title = 'FFFiles', max_results = 100, max_threads = 4, lazy_sync = true, prompt_vim_mode = false, wrap_around = false, -- true to wrap the cursor around when moving past the first/last item follow_symlinks = false, -- Allow indexing the user's $HOME directory. Enabled by default. -- Disable if you strictly sure you don't want this, as it makes whole fff error hard enable_home_dir_scanning = true, -- Allow indexing a filesystem root (e.g. `/`, `C:\`). Disabled by default enable_fs_root_scanning = false, layout = { height = 0.8, width = 0.8, prompt_position = 'bottom', -- or 'top' preview_position = 'right', -- 'left' | 'right' | 'top' | 'bottom' preview_size = 0.5, -- Border style for the picker windows. Leave unset (nil) to follow the -- global `vim.o.winborder`; set it to override fff's borders independently. border = nil, -- 'single' | 'double' | 'rounded' | 'solid' | 'shadow' | 'none' -- border = { -- { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, -- { ' ', ' ', ' ', ' ', ' ' }, -- }, flex = { size = 130, wrap = 'top' }, min_list_height = 10, -- do not display anything except the list below this threshold show_scrollbar = true, path_shorten_strategy = 'middle', -- 'middle' | 'middle_number' | 'end' | 'start' -- 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right' anchor = 'center', show_path_first = false, -- true renders results as `path/to/file` instead of `file path/to` }, -- find_files specific rendering file_picker = { current_file_label = '(current)', -- virtual text marking the buffer the picker was opened from fuzzy_query_highlighting = false, -- true to highlight fuzzy query matches, not just the literal query }, preview = { enabled = true, max_size = 10 * 1024 * 1024, chunk_size = 8192, binary_file_threshold = 1024, imagemagick_info_format_str = '%m: %wx%h, %[colorspace], %q-bit', ``` -------------------------------- ### Install Python bindings with pip Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Installs the Python bindings for fff from PyPI. ```bash pip install fff-search ``` -------------------------------- ### setup() Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Configures the fff.nvim plugin with options for suggestions, debug, logging, and select window behavior. ```APIDOC ## setup() ### Description Configures the fff.nvim plugin with options for suggestions, debug, logging, and select window behavior. ### Method setup ### Parameters #### Table Options - **suggestions.enabled** (boolean) - Optional - When a query has no results, look them up in the other mode (files <-> grep) and show as a hint. - **suggestions.grep_time_budget_ms** (number) - Optional - Hard cap for the grep hint in file mode; it is skipped until content indexing finishes. - **debug.enabled** (boolean) - Optional - Show the file info panel next to the preview. - **debug.show_scores** (boolean) - Optional - Inline scores in the file list. - **debug.show_file_info** (table or boolean) - Optional - Per-section toggles for the file info panel. Accepts a boolean shorthand to flip everything at once. The panel adapts to width: narrow renders sections vertically, wide renders them as a two-column grid. Disable a section to also shrink the panel. - **file_info** (boolean) - Optional - Size, type, git status, frecency. - **score_breakdown** (boolean) - Optional - Total + match type, bonuses, modifiers, penalty. - **timings** (boolean or table) - Optional - Modified + accessed timestamps; pass a table to hide individual rows: timings = { modified = false, accessed = true }. - **full_path** (boolean) - Optional - Relative path at the bottom (wraps if too long). - **logging.enabled** (boolean) - Optional - Enable logging. - **logging.log_file** (string) - Optional - Log file path. Logs will be written in a parent directory of this file path in files like ++.. Run :FFFOpenLog to open current one. - **logging.log_level** (string) - Optional - Log level. - **logging.retain_runs** (number) - Optional - Number of log runs to retain. - **select.select_window** (function) - Optional - Customize or disable the window selection behavior. The function receives (_current_buf, _action) and should return a window ID or nil. ### Request Example ```lua require('fff').setup({ suggestions = { enabled = true, grep_time_budget_ms = 50, }, debug = { enabled = false, show_scores = false, show_file_info = { file_info = true, score_breakdown = true, timings = true, full_path = true, }, }, logging = { enabled = true, log_file = vim.fn.stdpath('log') .. '/fff.log', log_level = 'info', retain_runs = 20, }, }) ``` ### Response No return value. ``` -------------------------------- ### Install pi-fff via git Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/pi-fff/README.md Installs the pi-fff extension directly from the GitHub repository. ```bash pi install git:github.com/dmtrKovalenko/fff ``` -------------------------------- ### Install cargo-c Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Installs the cargo-c tool, required to build and install the C library. ```bash cargo install cargo-c ``` -------------------------------- ### Install fff MCP server on Windows Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md One-line installer for the fff MCP server on Windows using PowerShell. Downloads and runs the install script from the fff repository. The script prints the exact wiring instructions for your client. ```powershell irm https://raw.githubusercontent.com/dmtrKovalenko/fff/main/install-mcp.ps1 | iex ``` -------------------------------- ### Install pi-fff via npm (recommended) Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/pi-fff/README.md Installs the pi-fff extension as a pi package using the npm registry. Recommended method. ```bash pi install npm:@ff-labs/pi-fff ``` -------------------------------- ### Install fff MCP server on Linux/macOS Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md One-line installer for the fff MCP server on Linux and macOS. Downloads and runs the install script from dmtrkovalenko.dev. The script prints the exact wiring instructions for your client. ```bash curl -L https://dmtrkovalenko.dev/install-fff-mcp.sh | bash ``` -------------------------------- ### Clone and install pi-fff for local development Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/pi-fff/README.md Clones the repository, navigates to the pi-fff package, and installs dependencies for local development. ```bash git clone https://github.com/dmtrKovalenko/fff.git cd fff/packages/pi-fff npm install ``` -------------------------------- ### Install pi-fff locally via npm Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/pi-fff/README.md Installs the pi-fff extension as a project-local pi package. ```bash pi install -l npm:@ff-labs/pi-fff ``` -------------------------------- ### Install fff with vim.pack Source: https://github.com/dmtrkovalenko/fff/blob/main/doc/fff.nvim.txt Vim-pack installation block for the fff plugin. Adds the repository, sets up a PackChanged autocmd to build the binary on install/update, configures vim.g.fff with lazy_sync and debug options, and binds 'ff' to find_files. ```lua -- Package name changed from `fff.nvim` to `fff`. If you installed fff.nvim before, clean with `:packdel fff.nvim` vim.pack.add({ 'https://github.com/dmtrKovalenko/fff' }) vim.api.nvim_create_autocmd('PackChanged', { callback = function(ev) local name, kind = ev.data.spec.name, ev.data.kind if name == 'fff' and (kind == 'install' or kind == 'update') then if not ev.data.active then vim.cmd.packadd('fff') end require('fff.download').download_or_build_binary() end end, }) vim.g.fff = { lazy_sync = true, debug = { enabled = true, show_scores = true }, } vim.keymap.set('n', 'ff', function() require('fff').find_files() end, { desc = 'FFFind files' }) ``` -------------------------------- ### Install fff with lazy.nvim Source: https://github.com/dmtrkovalenko/fff/blob/main/doc/fff.nvim.txt Lazy.nvim installation block for the fff plugin. Sets up the build step (downloads a prebuilt binary or falls back to cargo build), enables debug options, and defines keymaps for find_files, live_grep, fuzzy/plain grep, and grep under cursor. The plugin lazy-initialises itself, so lazy is set to false. ```lua -- Package name changed from `fff.nvim` to `fff`. If you installed fff.nvim before, clean with `:Lazy clean` { 'dmtrKovalenko/fff', build = function() -- downloads a prebuilt binary or falls back to cargo build require("fff.download").download_or_build_binary() end, -- for nixos: -- build = "nix run .#release", opts = { debug = { enabled = true, show_scores = true, }, }, lazy = false, -- the plugin lazy-initialises itself keys = { { "ff", function() require('fff').find_files() end, desc = 'FFFind files' }, { "fg", function() require('fff').live_grep() end, desc = 'LiFFFe grep' }, { "fz", function() require('fff').live_grep({ grep = { modes = { 'fuzzy', 'plain' } } }) end, desc = 'Live fffuzy grep', }, { "fw", function() require('fff').live_grep_under_cursor() end, mode = { 'n', 'x' }, desc = 'Search current word / selection', }, }, } ``` -------------------------------- ### Minimal C example using fff_create_instance and fff_search Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Minimal C example showing how to create an fff instance, handle errors, perform a search, and clean up. Every function returning FffResult* allocates with Rust's Box; free with fff_free_result, not malloc's free. Payloads have their own dedicated free functions, and C strings returned in the handle field are freed with fff_free_string. ```c #include #include int main(void) { FffResult *res = fff_create_instance( ".", // base_path "", // frecency_db_path (empty = default) "", // history_db_path false, // use_unsafe_no_lock true, // enable_mmap_cache true, // enable_content_indexing true, // watch false // ai_mode ); if (!res->success) { fprintf(stderr, "init failed: %s\n", res->error); fff_free_result(res); return 1; } void *handle = res->handle; fff_free_result(res); // Search FffResult *search = fff_search(handle, "main.rs", "", 0, 0, 20, 100, 3); // ... read FffSearchResult from search->handle, then fff_free_search_result() fff_destroy(handle); return 0; } ``` -------------------------------- ### Codex MCP server configuration for fff Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Example entry in ~/.codex/config.toml created by the codex mcp add command. Use the actual installed path for your system, then restart Codex or start a new task so it loads the server. ```toml [mcp_servers.fff] command = "/opt/homebrew/bin/fff-mcp" ``` -------------------------------- ### fff.nvim setup() configuration with keymaps, frecency, history, git, select, grep, suggestions, debug, and logging Source: https://github.com/dmtrkovalenko/fff/blob/main/doc/fff.nvim.txt Complete configuration for fff.nvim passed to require('fff').setup(). Includes keymaps for navigation and selection, frecency and history database paths, git recency scoring, grep options like smart_case and time_budget_ms, suggestions for cross-mode hints, debug panel toggles, and logging settings. Note that select_window returns a winid or nil, and grep.casing overrides smart_case when set. ```lua line_numbers = false, cursorlineopt = 'both', wrap_lines = false, filetypes = { svg = { wrap_lines = true }, markdown = { wrap_lines = true }, text = { wrap_lines = true }, }, }, keymaps = { close = '', select = '', select_split = '', select_vsplit = '', select_tab = '', move_up = { '', '' }, move_down = { '', '' }, preview_scroll_up = '', preview_scroll_down = '', toggle_debug = '', cycle_grep_modes = '', insert_newline_escape = '', -- grep mode only: jump cursor to first match of next/prev file group grep_jump_to_next_file = { '', '' }, grep_jump_to_prev_file = { '', '' }, cycle_previous_query = '', cycle_forward_query = '', -- unbound by default, wipes the whole input line -- clear_query = '', -- overrides preview_scroll_up in insert mode toggle_select = '', send_to_quickfix = '', focus_list = 'l', focus_preview = 'p', }, -- extra keymaps for the picker input, keyed by mode, applied over the built-ins mappings = { -- i = { [''] = function() vim.api.nvim_input('') end }, }, frecency = { enabled = true, db_path = vim.fn.stdpath('cache') .. '/fff_nvim', }, history = { enabled = true, db_path = vim.fn.stdpath('data') .. '/fff_queries', min_combo_count = 3, combo_boost_score_multiplier = 100, }, git = { status_text_color = false, -- true to color filenames by git status -- files that participated in the last N configured commits will get scoring bonus recency = { enabled = true, -- boost files from recent commits of the current branch max_commits = 10, -- analyze the last N branch-specific commits max_files_per_commit = 50, -- skip bulk commits touching more files than this }, }, select = { -- Return winid to open the chosen file in, or nil to open in the original window select_window = function(current_buf, action) --[[ default impl ]] end, }, grep = { max_file_size = 10 * 1024 * 1024, max_matches_per_file = 100, smart_case = true, casing = nil, -- 'smart' | 'sensitive' | 'insensitive'; overrides smart_case when set time_budget_ms = 150, enforce_time_budget = false, -- apply time_budget_ms even before anything matched (off = zero-match queries scan everything) modes = { 'plain', 'regex', 'fuzzy' }, trim_whitespace = false, enable_filename_constraint = false, -- treat filename-like tokens (e.g. `score.rs`) in a grep query as a file-path filter scoping the search; off = searched as literal text location_format = ':%d:%d', -- printf format for line:col prefix in grep results, e.g. ':%d' for line-only }, suggestions = { enabled = true, -- when a query has no results, look them up in the other mode (files <-> grep) and show as a hint grep_time_budget_ms = 50, -- hard cap for the grep hint in file mode; it is skipped until content indexing finishes }, debug = { enabled = false, -- show the file info panel next to the preview show_scores = false, -- inline scores in the file list -- Per-section toggles for the file info panel. Accepts a boolean shorthand -- (`show_file_info = true|false`) to flip everything at once. The panel -- adapts to width: narrow renders sections vertically, wide renders them -- as a two-column grid. Disable a section to also shrink the panel. show_file_info = { file_info = true, -- size, type, git status, frecency score_breakdown = true, -- total + match type, bonuses, modifiers, penalty -- modified + accessed timestamps; pass a table to hide individual rows: -- timings = { modified = false, accessed = true } timings = true, full_path = true, -- relative path at the bottom (wraps if too long) }, }, logging = { enabled = true, -- logs will be written in a parent directory of this file path in files like -- `++.`. Run :FFFOpenLog to open current one log_file = vim.fn.stdpath('log') .. '/fff.log', log_level = 'info', retain_runs = 20, }, }) ``` -------------------------------- ### Configure fff.nvim with require('fff').setup Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Full configuration example for the fff.nvim plugin. Shows all available options for layout, preview, keymaps, frecency, history, git, select, and grep. Defaults are shown; many options have comments explaining behavior and alternatives. ```lua require('fff').setup({ base_path = vim.fn.getcwd(), prompt = '🪿 ', title = 'FFFiles', max_results = 100, max_threads = 4, lazy_sync = true, prompt_vim_mode = false, wrap_around = false, -- true to wrap the cursor around when moving past the first/last item follow_symlinks = false, -- Allow indexing the user's $HOME directory. Enabled by default. -- Disable if you strictly sure you don't want this, as it makes whole fff error hard enable_home_dir_scanning = true, -- Allow indexing a filesystem root (e.g. `/`, `C:\`). Disabled by default enable_fs_root_scanning = false, layout = { height = 0.8, width = 0.8, prompt_position = 'bottom', -- or 'top' preview_position = 'right', -- 'left' | 'right' | 'top' | 'bottom' preview_size = 0.5, -- Border style for the picker windows. Leave unset (nil) to follow the -- global `vim.o.winborder`; set it to override fff's borders independently. border = nil, -- 'single' | 'double' | 'rounded' | 'solid' | 'shadow' | 'none' -- border = { -- { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, -- { ' ', ' ', ' ', ' ', ' ' }, -- }, flex = { size = 130, wrap = 'top' }, min_list_height = 10, -- do not display anything except the list below this threshold show_scrollbar = true, path_shorten_strategy = 'middle', -- 'middle' | 'middle_number' | 'end' | 'start' -- 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right' anchor = 'center', show_path_first = false, -- true renders results as `path/to/file` instead of `file path/to` }, -- find_files specific rendering file_picker = { current_file_label = '(current)', -- virtual text marking the buffer the picker was opened from fuzzy_query_highlighting = false, -- true to highlight fuzzy query matches, not just the literal query }, preview = { enabled = true, max_size = 10 * 1024 * 1024, chunk_size = 8192, binary_file_threshold = 1024, imagemagick_info_format_str = '%m: %wx%h, %[colorspace], %q-bit', line_numbers = false, cursorlineopt = 'both', wrap_lines = false, filetypes = { svg = { wrap_lines = true }, markdown = { wrap_lines = true }, text = { wrap_lines = true }, }, }, keymaps = { close = '', select = '', select_split = '', select_vsplit = '', select_tab = '', move_up = { '', '' }, move_down = { '', '' }, preview_scroll_up = '', preview_scroll_down = '', toggle_debug = '', cycle_grep_modes = '', insert_newline_escape = '', -- grep mode only: jump cursor to first match of next/prev file group grep_jump_to_next_file = { '', '' }, grep_jump_to_prev_file = { '', '' }, cycle_previous_query = '', cycle_forward_query = '', -- unbound by default, wipes the whole input line -- clear_query = '', -- overrides preview_scroll_up in insert mode toggle_select = '', send_to_quickfix = '', focus_list = 'l', focus_preview = 'p', }, -- extra keymaps for the picker input, keyed by mode, applied over the built-ins mappings = { -- i = { [''] = function() vim.api.nvim_input('') end }, }, frecency = { enabled = true, db_path = vim.fn.stdpath('cache') .. '/fff_nvim', }, history = { enabled = true, db_path = vim.fn.stdpath('data') .. '/fff_queries', min_combo_count = 3, combo_boost_score_multiplier = 100, }, git = { status_text_color = false, -- true to color filenames by git status -- files that participated in the last N configured commits will get scoring bonus recency = { enabled = true, -- boost files from recent commits of the current branch max_commits = 10, -- analyze the last N branch-specific commits max_files_per_commit = 50, -- skip bulk commits touching more files than this }, }, select = { -- Return winid to open the chosen file in, or nil to open in the original window select_window = function(current_buf, action) --[[ default impl ]] end, }, grep = { max_file_size = 10 * 1024 * 1024, max_matches_per_file = 100, smart_case = true, casing = nil, -- 'smart' | 'sensitive' | 'insensitive'; overrides smart_case when set time_budget_ms = 150, enforce_time_budget = false, -- apply time_budget_ms even before anything matched (off = zero-match queries scan everything) modes = { 'plain', 'regex', 'fuzzy' }, trim_whitespace = false, enable_filename_constraint = false, -- treat filename-like tokens (e.g. `score.rs`) in a grep query as a file-path filter scoping the search; off = searched as literal text location_format = ':%d:%d', -- printf format for line:col prefix in grep results, e.g. ':%d' for line-only }, }) ``` -------------------------------- ### Preferred entry points: fff_create_instance_with, fff_create_instance_with_value, fff_destroy Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-c.md Preferred entry points. fff_create_instance_with requires opts.version in 1..=FFF_CREATE_OPTIONS_VERSION (else error), non-empty base_path (else error). Initializes tracing when log_file_path is set, opens DBs when paths given, builds cache_budget, and calls FilePicker::new_with_shared_state in FFFMode::Ai when ai_mode is true. fff_destroy frees everything; null handle is a no-op. ```moonbit pub unsafe extern "C" fn fff_create_instance_with(opts: *const FffCreateOptions) -> *mut FffResult pub unsafe extern "C" fn fff_create_instance_with_value(opts: FffCreateOptions) -> *mut FffResult pub unsafe extern "C" fn fff_destroy(fff_handle: *mut c_void) ``` -------------------------------- ### Install fff via npm Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/fff-node/README.md Install the @ff-labs/fff-node package using npm. The correct native binary for your platform is installed automatically via platform-specific packages. ```bash npm install @ff-labs/fff-node ``` -------------------------------- ### Development setup with uv and Maturin Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/fff-python/README.md Sets up the Python package for development: syncs dependencies with uv and builds the native extension in development mode using Maturin. ```bash cd packages/fff-python uv sync --all-extras uv run maturin develop --release ``` -------------------------------- ### C example: fff_create_instance_with, fff_search, fff_watch_args, fff_destroy Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-c.md Shows the full lifecycle of a fff-c instance: create with FffCreateOptions, check success and free on error, search with fff_search, free search results, watch with fff_watch_args, and destroy. The search result handle is an FffSearchResult* and must be freed with fff_free_search_result(). ```c #include FffResult *res = fff_create_instance_with(&(FffCreateOptions){ .version = FFF_CREATE_OPTIONS_VERSION, .base_path = "/path/to/repo", .ai_mode = true, .watch = true, .enable_fs_root_scanning = false, .enable_home_dir_scanning = false, }); if (!res->success) { fprintf(stderr, "%s\n", res->error); fff_free_result(res); return 1; } void *handle = res->handle; fff_free_result(res); FffResult *search = fff_search(handle, "main.rs", "", 0, 0, 20, 100, 3); // search->handle is an FffSearchResult*; free with fff_free_search_result() fff_free_search_result((FffSearchResult *)search->handle); fff_free_result(search); fff_watch_args(handle, "", NULL, 0); fff_destroy(handle); ``` -------------------------------- ### Install fff MCP server with Homebrew Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Install or upgrade the fff MCP server via Homebrew on macOS or Linux. The formula is auto-bumped on every stable release and installs the prebuilt fff-mcp binary from GitHub releases. ```bash brew install dmtrKovalenko/fff/fff-mcp brew upgrade fff-mcp # after new stable releases ``` -------------------------------- ### Build from Source Commands Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/fff-bun/README.md Commands to clone the repository, build the C library, and locate the resulting binary. Use when prebuilt binaries are not available for your platform. ```bash # Clone the repository git clone https://github.com/dmtrKovalenko/fff cd fff # Build the C library cargo build --release -p fff-c # The binary will be at target/release/libfff_c.{so,dylib,dll} ``` -------------------------------- ### FilePicker::new_with_shared_state Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-core.md Creates the picker, installs it into shared_picker, and spawns background indexing + filesystem watcher threads. Entry point used by every binding (fff_create_instance_with, FileFinder::create, Python FileFinder(...)). ```moonbit pub fn new_with_shared_state( shared_picker: SharedFilePicker, shared_frecency: SharedFrecency, options: FilePickerOptions, ) -> Result<(), Error> { ``` -------------------------------- ### Register fff MCP server with Codex (one-line installer) Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Register the fff MCP server with Codex when installed via the one-line installer. Uses the absolute path because Codex desktop sessions may not inherit your interactive shell's PATH. ```bash codex mcp add fff -- "$HOME/.local/bin/fff-mcp" ``` -------------------------------- ### CLI Download and Info Commands Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/fff-bun/README.md Commands to manually download the binary as a fallback and to show platform info and binary location. ```bash # Download binary manually (fallback if npm package unavailable) bunx fff download [tag] # Show platform info and binary location bunx fff info ``` -------------------------------- ### Install C library with cargo-c Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Installs the C library directly with cargo-c, using the zlob feature and a custom prefix. ```bash cargo cinstall -p fff-c --release --no-default-features --features zlob --prefix=/usr/local ``` -------------------------------- ### Install pi-fff via git pinned to a release Source: https://github.com/dmtrkovalenko/fff/blob/main/packages/pi-fff/README.md Installs a specific release of pi-fff from GitHub by pinning to a tag (v0.3.0). ```bash pi install git:github.com/dmtrKovalenko/fff@v0.3.0 ``` -------------------------------- ### FileFinder Usage Examples Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-python.md Usage examples for the FileFinder class, including synchronous and asynchronous usage, error handling, and pagination. ```APIDOC ## FileFinder ### Description FileFinder is the main entry point for searching and grepping files in a project. It supports both synchronous and asynchronous usage, with optional file watching. ### Constructor `FileFinder(path, watch=False)` ### Parameters - **path** (str) - Required - Path to the project directory. - **watch** (bool) - Optional - Whether to watch for file changes. Defaults to `False`. ### Methods #### wait_for_scan_blocking ##### Description Blocks until the initial scan completes or the timeout expires. Returns `False` on timeout. ##### Signature `wait_for_scan_blocking(timeout_ms=5000)` ##### Parameters - **timeout_ms** (int) - Optional - Timeout in milliseconds. Defaults to `5000`. ##### Returns - **bool** - `True` if scan completed, `False` on timeout. #### wait_for_scan ##### Description Asynchronous version of `wait_for_scan_blocking`. Returns `False` on timeout. ##### Signature `async wait_for_scan(timeout_ms=5000)` ##### Parameters - **timeout_ms** (int) - Optional - Timeout in milliseconds. Defaults to `5000`. ##### Returns - **bool** - `True` if scan completed, `False` on timeout. #### search ##### Description Searches for files matching the given query. ##### Signature `search(query)` ##### Parameters - **query** (str) - Required - The search query. ##### Returns - **result** - An object with `items` and `scores` attributes. `items` contains the matched items, `scores` contains the corresponding scores. #### grep ##### Description Searches file contents for a pattern with optional context lines and cursor-based pagination. ##### Signature `grep(pattern, mode="plain", before_context=0, after_context=0, page_limit=None, cursor=None)` ##### Parameters - **pattern** (str) - Required - The pattern to search for. - **mode** (str) - Optional - Search mode, e.g., `"plain"`. Defaults to `"plain"`. - **before_context** (int) - Optional - Number of lines before the match. Defaults to `0`. - **after_context** (int) - Optional - Number of lines after the match. Defaults to `0`. - **page_limit** (int) - Optional - Maximum number of results per page. - **cursor** (str) - Optional - Cursor for pagination. ##### Returns - **page** - An object with `next_cursor` attribute for pagination. If `next_cursor` is `None`, there are no more pages. ### Exceptions All native errors raise `FFFException` (subclass of `Exception` via `PyException`) with the formatted core message. `wait_for_scan`/polling methods never raise for timeouts (return `False`/empty results). ``` -------------------------------- ### fff_watch_events_get_kind Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-c.md Gets the kind of a watch event. ```APIDOC ## fff_watch_events_get_kind ### Description Returns the kind of the event at the given index. The kind matches the `WatchEventKind` enum values: `Created = 0`, `Modified = 1`, `Removed = 2`, `Rescan = 3`, `Renamed = 4`. ### Method C function ### Endpoint `fff_watch_events_get_kind` ### Parameters - **batch** (`*const FffWatchEventBatch`) - Required - Pointer to the event batch. - **idx** (index) - Required - Index of the event. ### Returns - **u8** - Event kind (see enum values). ``` -------------------------------- ### FileFinder constructor with all parameters Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-python.md Initializes a FileFinder instance. All parameters are keyword-only except base_path. Native init runs in a background thread, releasing the GIL. Raises FFFException on errors like InvalidPath or FilesystemRoot. ```python FileFinder( base_path, *, frecency_db_path=None, history_db_path=None, enable_mmap_cache=True, enable_content_indexing=True, watch=True, ai_mode=False, log_file_path=None, log_level=None, cache_budget_max_files=0, cache_budget_max_bytes=0, cache_budget_max_file_size=0, enable_fs_root_scanning=False, enable_home_dir_scanning=False, follow_symlinks=False, ) ``` -------------------------------- ### Configure suggestions, debug, and logging options Source: https://github.com/dmtrkovalenko/fff/blob/main/README.md Configuration options for suggestions, debug, and logging. Suggestions enable cross-mode hints when a query has no results, with a grep time budget. Debug toggles the file info panel and inline scores, with per-section toggles for the panel. Logging writes to a file in the parent directory of the given path; run :FFFOpenLog to open the current log. ```lua suggestions = { enabled = true, -- when a query has no results, look them up in the other mode (files <-> grep) and show as a hint grep_time_budget_ms = 50, -- hard cap for the grep hint in file mode; it is skipped until content indexing finishes }, debug = { enabled = false, -- show the file info panel next to the preview show_scores = false, -- inline scores in the file list -- Per-section toggles for the file info panel. Accepts a boolean shorthand -- (`show_file_info = true|false`) to flip everything at once. The panel -- adapts to width: narrow renders sections vertically, wide renders them -- as a two-column grid. Disable a section to also shrink the panel. show_file_info = { file_info = true, -- size, type, git status, frecency score_breakdown = true, -- total + match type, bonuses, modifiers, penalty -- modified + accessed timestamps; pass a table to hide individual rows: -- timings = { modified = false, accessed = true } timings = true, full_path = true, -- relative path at the bottom (wraps if too long) }, }, logging = { enabled = true, -- logs will be written in a parent directory of this file path in files like -- `++.`. Run :FFFOpenLog to open current one log_file = vim.fn.stdpath('log') .. '/fff.log', log_level = 'info', retain_runs = 20, } ``` -------------------------------- ### fff_watch_events_get_from_path Source: https://github.com/dmtrkovalenko/fff/blob/main/_autodocs/api-reference/fff-c.md Gets the from-path of a watch event. ```APIDOC ## fff_watch_events_get_from_path ### Description Returns the from-path of the event at the given index (used for renames). ### Method C function ### Endpoint `fff_watch_events_get_from_path` ### Parameters - **batch** (`*const FffWatchEventBatch`) - Required - Pointer to the event batch. - **idx** (index) - Required - Index of the event. ### Returns - **`*const c_char`** - From-path of the event. ```