# fastfind 2.5.2 Fast file search with glob, regex, fuzzy and natural-language queries, content filters, Git awareness, compact indexes and an interactive terminal browser. ## Measured performance Version 2.5.1 fixes full-root load balancing: workers share subtrees discovered during traversal instead of assigning a large subtree permanently to one worker. Local depth-first scanning avoids queue contention while the other workers are busy; idle workers sleep until work arrives. Output remains bounded to 64 KiB per worker. See [benchmark details](benchmarks/RESULTS.md) for the full-root comparison and measurement limits. The following table records the earlier 2.5.0 measurements. Warm-cache Linux measurements on an Intel i7-9700 (8 cores), Nim 2.2.0, ORC, release optimizations and LTO; fd 10.5.0. The synthetic tree contains 100,000 files in 2,200 directories. The mixed `/usr` tree contains 314,814 visible entries. Listing writes to `/dev/null`; no indexes are used. The harness verifies identical filename multisets using NUL-delimited absolute paths before measuring, with hidden files and ignore rules disabled equally. | Tree / workers | 2.4.0 | 2.5.0 | fd 10.5.0 | | --- | ---: | ---: | ---: | | Synthetic / 1 | 20.03 ms | 19.39 ms | 88.80 ms | | Synthetic / 8 | 19.88 ms | 3.77 ms | 18.73 ms | | `/usr` / 1 | 118.75 ms | 117.71 ms | 342.47 ms | | `/usr` / 8 | 118.62 ms | 26.39 ms | 53.90 ms | Values are medians of 30 synthetic-tree runs and 20 `/usr` runs, with three warmups and randomized command order. On these workloads, eight-thread listing is approximately **5× faster than fd on the synthetic tree** and **2× faster on `/usr`**. Single-thread listing also beats fd at the same thread count. These are measured workloads, not a guarantee for every tree, filesystem, cache state or combination of filters. Peak resident memory for eight-thread listing was **3.54 MiB** on the synthetic tree and **4.70 MiB** on `/usr`, versus fd's 8.36 MiB and 18.58 MiB. Memory is measured separately with GNU time, avoiding inherited Python verification buffers in child RSS readings. Count-only traversal does not collect results; eight-thread counts took 3.49 ms and 24.48 ms respectively. Reproduce the comparison (Python standard library and fd required): ```bash nimble release_full benchmarks/compare_fd.sh ./bin/fastfind /path/to/tree 20 python3 benchmarks/compare_fd.py ./bin/fastfind /usr 20 --threads 1 8 --json results.json ``` See [benchmark details](benchmarks/RESULTS.md) for dataset construction, exact build commands, measurements and limitations. ## Parallel search and memory Simple POSIX filename listing and counting adapt to tree size. Small searches finish without starting workers; larger searches use up to eight. `-j 1` forces serial traversal and `-j N` selects up to 32 workers. Explicit parallel regex, exclusion, metadata and content searches stream plain results too. Each streaming worker buffers 64 KiB of output. A slow pipe applies backpressure without collecting the entire result set. Sorting, ranking, selection and JSON arrays collect the results they need. `--count` avoids retaining match objects, including parallel content searches. Following symbolic links and hierarchical Git ignore rules currently use the serial walker to preserve their semantics. ## Natural-language search Queries run locally, without a model download or network request: ```bash ff 'find python files modified in the past three weeks' ff --query 'ten largest PDF files in my downloads' ff --query 'files between one and five megabytes' ff --query 'python files named main or config excluding vendor' ff --query 'files containing TODO and FIXME' ff --query "files that don't contain 'secret key'" ff --query 'files modified yesterday' --explain ``` The parser understands language/format alternatives, filename phrases, prefixes, suffixes and exact names; content phrases, AND/OR and regex; negative content, paths and exclusions; written numbers, size ranges, calendar windows, relative times, ordering and limits. `newest` and `largest` sort results without imposing an arbitrary age or size cutoff. `yesterday` excludes today. Specific formats such as PDF remain specific formats. Use `--query` to explicitly interpret a sentence and `--explain` to inspect the resulting filters. Explicit `--glob`, `--regex` and `--fixed` patterns bypass automatic NLP detection, allowing literal multiword filenames. Unrecognized words become a name constraint or produce a warning. This is an offline grammar with documented clauses; it cannot infer arbitrary intent, understand image contents, or express every cross-field Boolean sentence. ## Interactive browser ```bash ff --interactive '*' /path/to/project ``` The browser opens the requested root, uses Unicode cell widths, adapts its preview to terminal size and buffers screen writes. Metadata and bounded 64 KiB previews are cached; FIFOs and device files cannot block previews. Recursive searches run in a cancellable background worker in threaded builds, and accept natural-language queries. The alternate screen and terminal state are restored on exit, Ctrl+C, SIGINT and SIGTERM. UI output stays on the terminal when the selected path is redirected into a pipe. | Key | Action | | --- | --- | | Arrows or j/k/h/l | Navigate; Enter enters a directory or returns a file | | / | Fuzzy or glob filter in the current results | | g | Recursive search below the current directory; Esc cancels | | Tab | Go to path, with Tab completion | | Space, v, V | Toggle selection, select visible, clear selection | | p, i, . | Preview, details, hidden files | | s, r | Sort field, reverse order | | : | Commands; :rm asks before deleting files | | ?, q | Help, quit | Bracketed paste is treated as text rather than a sequence of commands. Shell placeholders pass filenames as arguments, including when `{}` is surrounded by quotes. On Windows, shell actions reject paths containing shell metacharacters; use structured `--exec-cmd` arguments without `--shell` for those names. JSON output replaces invalid UTF-8 for display and includes Base64 `pathBytes`, `absPathBytes`, `relPathBytes` or `nameBytes` fields where needed to preserve the original bytes. `--print0` always preserves raw paths. ***[Changelog for 2.5.2](https://github.com/RobertFlexx/fastfind/blob/main/CHANGELOG.md)*** --- # Natural Language Queries is now out of BETA! ## Below is more information on fastfind. Read below to learn how to install, use, and/or compile this software. Cross-platform native binary written in Nim. Standard builds use PCRE1; some release packages may bundle or statically link it. ### (SOFTWARE IS NOT MATURE, EXPECT BUGS!) ⚠ fastfind is still early in development. Expect occasional bugs or behavioral changes. > Below is software supported, and software used in this project. click on any to be redirected to respected website. [![License: MIT](https://img.shields.io/badge/License-MIT-1f6feb?style=for-the-badge)](LICENSE) [![Language: Nim](https://img.shields.io/badge/Language-Nim-FFC200?style=for-the-badge&logo=nim&logoColor=black)](https://nim-lang.org/) [![Linux](https://img.shields.io/badge/Linux-Supported-2ea44f?style=for-the-badge&logo=linux&logoColor=white)](https://www.kernel.org/) [![macOS](https://img.shields.io/badge/macOS-Supported-2ea44f?style=for-the-badge&logo=apple&logoColor=white)](https://www.apple.com/macos/) [![FreeBSD](https://img.shields.io/badge/FreeBSD-Supported-2ea44f?style=for-the-badge&logo=freebsd&logoColor=white)](https://www.freebsd.org/) [![OpenBSD](https://img.shields.io/badge/OpenBSD-Supported-2ea44f?style=for-the-badge&logo=openbsd&logoColor=white)](https://www.openbsd.org/) [![NetBSD](https://img.shields.io/badge/NetBSD-Supported-2ea44f?style=for-the-badge&logo=netbsd&logoColor=white)](https://www.netbsd.org/) [![DragonFly BSD](.github/assets/platforms/dragonflybsd.svg)](https://www.dragonflybsd.org/) [![Solaris](.github/assets/platforms/solaris.svg)](https://www.oracle.com/solaris/) [![illumos](.github/assets/platforms/illumos.svg)](https://illumos.org/) [![OmniOS](.github/assets/platforms/omnios.svg)](https://omnios.org/) [![OpenIndiana](.github/assets/platforms/openindiana.svg)](https://www.openindiana.org/) [![Tribblix](.github/assets/platforms/tribblix.svg)](https://tribblix.org/) Native VM builds cover Solaris 11.4, OmniOS r151058, OpenIndiana 2026.04, Tribblix m41, and DragonFly BSD 6.4.2. Each VM compiles its own amd64 executables, runs parser/matcher/input tests, compares single- and multithreaded traversal, and checks both manual names. The illumos targets have separate binaries so each distribution's native runtime is tested. `fastfind`/`ff` is a fast file finder with: * Glob/regex/fixed/fuzzy matching * Natural-language query planning with `--explain` * Size/time/content filters * Git-aware filtering * Interactive mode * Optional index-based search * Semantic symbol search Installed command names: * `ff` * `fastfind` ## Installation ### Quick install Install the latest published binary with the installer below. The installer requires a release-metadata SHA-256 digest and refuses unverified assets. As with any pipe-to-shell command, inspect or download the script first in a sensitive environment. ```bash curl -fsSL https://raw.githubusercontent.com/RobertFlexx/fastfind/main/install.sh | bash ``` The installer requires Bash, Python 3, and curl or wget, including on BSD, Solaris, and illumos. Use the Bash command above rather than the system sh. ### Or if youre on an Arch distro ```bash paru -S fastfind-bin ``` ```bash yay -S fastfind-bin ``` ### Update Update to the latest version: ```bash curl -fsSL https://raw.githubusercontent.com/RobertFlexx/fastfind/main/update.sh | bash ``` * The update script checks your current version and only downloads if there's a new release. * Release builds provide Linux glibc and macOS binaries for amd64/arm64, Windows amd64, and native FreeBSD, OpenBSD, NetBSD, DragonFly BSD, Solaris, OmniOS, OpenIndiana, and Tribblix amd64. PCRE is linked statically; the operating system C runtime is still required. Musl Linux builds are not part of this release matrix. ### Or build from source (recommended for best performance) ```bash git clone https://github.com/RobertFlexx/fastfind cd fastfind nim c -d:release --mm:orc --threads:on -d:lto --opt:speed \ --passC:-O3 --passC:-march=native --passC:-flto --passL:-flto --passL:-s \ -o:bin/fastfind src/ff.nim ``` Nushell: ```nu git clone https://github.com/RobertFlexx/fastfind cd fastfind nim c '-d:release' '--mm:orc' '--threads:on' '-d:lto' '--opt:speed' '--passC:-O3' '--passC:-march=native' '--passC:-flto' '--passL:-flto' '--passL:-s' '-o:bin/fastfind' src/ff.nim ``` #### Build Flags Explained | Flag | Purpose | Recommended | |------|---------|--------------| | `-d:danger` | Remove runtime safety checks | Not recommended | | `-d:release` | Enable compiler optimizations | ✅ Required | | `--mm:orc` | Memory manager (orc = best balance, arc = low memory) | Use `orc` | | `--threads:on` | Enable multi-threading support | ✅ Required | | `-d:lto` | Enable link-time optimization | ✅ Required | | `--opt:speed` | Optimize for speed not size | ✅ Required | | `--passC:-O3` | C compiler optimization level 3 | ✅ Required | | `--passC:-march=native` | Optimize for this CPU | ✅ Required | | `--passC:-flto` | C compiler LTO | ✅ Required | | `--passL:-flto` | Linker LTO | ✅ Required | | `--passL:-s` | Strip symbols (smaller binary) | Optional | #### Build for Different Systems (COMPILE WITH THREADS ON FOR FULL PERFORMANCE) **For distribution, very optimized build (generic binary):** ```bash nim c -d:release --mm:orc --threads:on -d:lto --opt:speed \ --passC:-O3 --passC:-march=x86-64 --passC:-flto --passL:-flto --passL:-s \ -o:bin/fastfind src/ff.nim ``` Nushell: ```nu nim c '-d:release' '--mm:orc' '--threads:on' '-d:lto' '--opt:speed' '--passC:-O3' '--passC:-march=x86-64' '--passC:-flto' '--passL:-flto' '--passL:-s' '-o:bin/fastfind' src/ff.nim ``` **Debug build (for testing):** ```bash nim c --threads:on -o:bin/fastfind_debug src/ff.nim ``` Nushell: ```nu nim c '--threads:on' '-o:bin/fastfind_debug' src/ff.nim ``` **Production build (balanced):** ```bash nim c -d:release --mm:arc --threads:on --opt:speed \ -o:bin/fastfind src/ff.nim ``` Nushell: ```nu nim c '-d:release' '--mm:arc' '--threads:on' '--opt:speed' '-o:bin/fastfind' src/ff.nim ``` ### Install to PATH ```bash sudo cp bin/fastfind /usr/local/bin/ff ``` ## Troubleshooting ### libpcre.so error If you see this error when running `ff`: ``` could not load: libpcre.so ``` First, check which library your binary actually needs: ```bash ldd "$(command -v ff)" | rg -i pcre ``` Then install the matching runtime package for your distro: | OS/Distro | Package Name | Install Command | |-----------|--------------|------------------| | **Ubuntu/Debian** | libpcre3 | `sudo apt install libpcre3` | | **Fedora/RHEL/CentOS (current repos)** | pcre2 | `sudo dnf install pcre2` | | **Arch Linux** | pcre (legacy `libpcre.so.*`) | `sudo pacman -S pcre` | | **openSUSE** | libpcre1 | `sudo zypper install libpcre1` | | **Alpine Linux** | pcre | `doas apk add pcre` | | **macOS** | pcre (via brew) | `brew install pcre` | | **FreeBSD** | pcre | `sudo pkg install pcre` | | **OpenBSD** | pcre | `doas pkg_add pcre` | If you still see an error for a specific SONAME (for example `libpcre.so.3`), query which package provides it: ```bash sudo dnf provides '*/libpcre.so*' ``` On Fedora/RHEL 10+, legacy PCRE1 (`libpcre.so.*`) is no longer in default repos. If your binary was built against PCRE1, use a static release binary or rebuild against currently available libraries. On Arch, `pcre` is available but deprecated upstream; new builds should prefer PCRE2. > **Tip**: Using static builds from the release page avoids this dependency issue entirely. ### RHEL 10 / EL10 PCRE Issue If you encounter this error on RHEL 10, EL10, or modern Fedora-like systems: ``` could not load: libpcre.so(.3|.1|) ``` **Root Cause:** The binary depends on legacy PCRE1 (`libpcre.so.1` / `libpcre.so.3`). RHEL 10+ and similar distributions no longer provide PCRE1 in default repositories. Installing `pcre2` does NOT fix the issue because it is ABI-incompatible. **Workaround with Homebrew (Linuxbrew):** 1. Install PCRE: ```bash brew install pcre ``` 2. Run with the library path: ```bash LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib ff ``` **Permanent Fix:** - **Option A:** Add to your shell config (`~/.bashrc`, `~/.zshrc`, etc.): ```bash export LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib:$LD_LIBRARY_PATH ``` - **Option B (recommended):** Create a wrapper script: ```bash sudo mv /usr/local/bin/ff /usr/local/bin/ff.real sudo tee /usr/local/bin/ff >/dev/null <<'EOF' #!/usr/bin/env bash export LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib:${LD_LIBRARY_PATH:-} exec /usr/local/bin/ff.real "$@" EOF sudo chmod +x /usr/local/bin/ff ``` **Verification:** ```bash LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib ff ``` If `ff` runs successfully, the issue is resolved. > **Note for developers:** Rebuild without PCRE1 dependency or use static builds. Consider migrating away from runtime `dlopen` of PCRE1. ### Scanning Root Directory Both `ff` and `fd` handle full system traversal well. Here's what to expect: ``` $ time ff "*" / | wc # ~1.09M files (0.58s) $ time fd . / | wc # ~1.09M files (0.26s) ``` Key points: - **fd** is ~2x faster for pure traversal (highly optimized Rust implementation) - **ff** finds comparable file counts (uses similar directory traversal) - **ff with `-H`** finds more files (~1.39M) due to slightly different hidden file handling For best performance, use explicit glob patterns: ```bash ff "*" / # Explicit glob (fastest for ff) ff --glob "*" / # Same, more explicit ``` Simple filename searches adapt automatically. Use `-j 1` to force serial traversal or `-j N` to select a worker count. ## Quick start ```bash # Find Nim files ff "*.nim" src/ # Fuzzy match ff --fuzzy config src/ # Content search ff "*.py" --contains TODO # Natural language query ff "python files containing TODO" # Interactive mode ff --interactive ``` ## Natural language queries `fastfind` can interpret natural language queries and translate them into filters automatically. ### Basic Usage ```bash ff "python files" ff "rust code" ff "config files" ff "log files" ``` ### Time-based Queries ```bash ff "files modified this week" ff "files modified 2 days ago" ff "files modified in the last hour" ff "files older than 30 days" ff "recent files" ``` ### Size-based Queries ```bash ff "large files" ff "small files" ff "empty files" ff "files larger than 10mb" ff "files between 1mb and 10mb" ``` ### Compound Expressions ```bash ff "python code files modified this week" ff "rust files larger than 100kb" ff "config files modified today" ff "large log files modified yesterday" ff "image files modified this month" ``` ### Language-specific Queries ```bash ff "python files" ff "javascript files" ff "rust code" ff "go files" ff "shell scripts" ff "docker files" ff "terraform files" ``` ### Content Search ```bash ff "python files containing \"TODO and FIXME\" under ./src" ff "config files containing password" ff "log files containing error" ``` ### Inspect the query plan Natural-language queries are parsed into independent, intersecting filters (name, language/category, size, time, content, location, exclusions, sorting, depth and limit). Use `--explain` to inspect the plan without searching: ```bash ff --explain 'find Python files larger than 5 MB modified in the last 2 weeks containing "TODO" under ./src excluding tests sort newest limit 20' ``` Compact clauses such as `ext:rs`, `name:parser`, `exclude:target`, `depth:4` and `limit:25` can be mixed with conversational text. > for more documentation, use `man ff` (or whatever alias you're using) in the terminal. ## Why fastfind `find` is extremely powerful, but syntax is heavy and platform behavior differs. `fd` is excellent for speed and simplicity, but intentionally smaller in scope. `fzf` is a fuzzy selector, not a filesystem crawler by itself. `fastfind` sits between these: one command with richer features, while keeping command shape compact. fastfind aims to combine the strengths of several tools: * `find`: extremely powerful but complex * `fd`: simple and fast but limited scope * `fzf`: great interactive filtering but requires input pipelines fastfind provides: * a single consistent command * built-in fuzzy and content search * interactive mode * semantic code queries * optional indexed search ## Comparison: `ff` vs `find` vs `fd` vs `fzf` ### Ability and feature depth | Capability | `ff` | `find` | `fd` | `fzf` | | ------------------------ | ---------------------------------------------- | ---------------------------------- | --------------------------------------- | ------------------------------- | | Recursive file discovery | Built-in | Built-in | Built-in | Input-driven (needs producer) | | Glob/regex/literal modes | Built-in switches | Primarily `-name`/`-regex` forms | Built-in regex/glob modes | Fuzzy/text filtering over input | | Fuzzy matching | Built-in (`--fuzzy`) | No | No | Core strength | | Natural language queries | Built-in query planner | No | No | No | | Size/time filters | Built-in (`--size`, `--changed`, etc.) | Built-in (`-size`, `-mtime`, etc.) | Built-in (`--size`, `--changed-within`) | Via upstream command only | | Content filtering | Built-in (`--contains`) | Via `-exec grep`/pipeline | Via `-X grep`/pipeline | Via upstream command only | | Git-aware file filters | Built-in (`--git-*`) | No | Partial (`--no-ignore-vcs`, etc.) | No | | Interactive picker | Built-in (`--interactive`, `--select`) | No | No | Core strength | | Index mode | Built-in (`--use-index`) | No | No | No | | Semantic symbol search | Built-in (`--function`, `--class`, `--symbol`) | No | No | No | ### Ease of use (common tasks) | Task | `ff` | `find` | `fd` | `fzf` | | -------------------------- | --------------------------- | ---------------------------------------- | ----------------------------------------- | ------------------------------------------- | | Find all `*.nim` files | `ff "*.nim"` | `find . -type f -name '*.nim'` | `fd --glob '*.nim'` | `find . -type f \| fzf --filter '.nim'` | | Files changed in 1 day | `ff "*.nim" --changed 1d` | `find . -type f -name '*.nim' -mtime -1` | `fd --changed-within 1day --glob '*.nim'` | `find ... -mtime -1 \| fzf --filter '.nim'` | | Find files containing TODO | `ff "*.py" --contains TODO` | `find ... -exec grep -l TODO {} +` | `fd --glob '*.py' -X grep -l TODO` | `find ... \| xargs grep -l TODO \| fzf` | ## Performance and speed (historical v2.1 baseline) The tables below are retained as a historical v2.1 baseline. The current engine has since replaced the path hot loop, parallel accounting, content buffers, Git status parsing, and JSON index. Do not treat these old numbers as current release measurements. A later v2.1-era regression snapshot (15,100 files, warm cache, 200 complete scans) measured 1.13 s with fastfind and 1.10 s with fd 10.1.0. For metadata sorting, 200 indexed queries took 3.08 s versus 6.26 s for live traversal. These figures remain here only as historical context for the tables below. Benchmark dataset: * 20,000 files across 100 directories * Tests run with `--warmup 3 --runs 20` using hyperfine System specs: * OS: Linux `6.19.12-1.el10.elrepo.x86_64` (Red Hat Enterprise Linux 10.1) * CPU: Intel Core i7-9700 (8 cores @ 3.00GHz) * RAM: 16 GB DDR4 * Tool versions: `ff 2.1.0`, `fd 10.4.2`, `find 4.9.0` --- ### Glob Patterns | Pattern | ff (ms) | fd (ms) | find (ms) | Winner | |---------|--------:|--------:|----------:|--------| | `*.txt` | 8.1 | 7.5 | 10.4 | fd (+8% faster) | | All files (`*`) | 14.6 | 7.1 | 7.3 | fd (2x faster) | ### With Type Filter | Pattern | ff (ms) | fd (ms) | find (ms) | Winner | |---------|--------:|--------:|----------:|--------| | `*.txt -t f` | 12.4 | 7.2 | 10.7 | fd (+72% faster) | ### Content Search | Command | ff (ms) | fd+grep (ms) | find+grep (ms) | Winner | |---------|--------:|-------------:|---------------:|--------| | `*.py --contains TODO` | 11.5 | 7.4 | 11.6 | fd (+56% faster) | ### Regex Patterns | Pattern | ff (ms) | fd (ms) | find (ms) | Winner | |---------|--------:|--------:|----------:|--------| | `.*\.txt$` | 15.2 | 7.3 | 10.6 | fd (2x faster) | ### Single-thread vs Parallel | Command | Time (ms) | Notes | |---------|----------:|-------| | `ff "*"` (default) | 14.6 | **Fastest** | | `ff "*" -j 4` | 22.7 | 55% slower | | `ff "*" -j 8` | 22.9 | 56% slower | --- ### Historical summary - **In the v2.1 baseline, fd was faster** - particularly for broad traversal - In that historical version, serial traversal beat parallel traversal. Version 2.5.0 uses the adaptive engine described above. - **ff built-in content search** - simpler than fd+grep pipeline (but slower) - **find** - surprisingly competitive for simple patterns --- > *Fun fact, ff uses less cpu in ALMOST the speed of fd!* --- | Operation | Recommendation | |-----------|----------------| | Simple glob (`*.txt`) | `fd --glob` or `find -name` | | All files | `fd .` | | Content search | `fd --glob \| xargs grep` (faster) or `ff --contains` (simpler) | | Parallel work | `fd` (better implementation) | ### What ff does better than fd: * Built-in content search (no external grep needed) * Natural language queries (`"python files modified this week"`) * Fuzzy matching * Semantic symbol search * More flexible filtering options * Compact binaries; PCRE1 runtime requirements are documented below ## Semantic code search fastfind also supports semantic-style symbol discovery directly from the CLI. Run these inside a project directory: Find function definitions: ``` ff --function parse . ``` Find classes: ``` ff --class Parser . ``` Find symbols: ``` ff --symbol Config . ``` This allows basic code discovery without launching a language server. ## Index-based search fastfind supports optional index-based search for faster repeated queries: ```bash # Build initial index ff --rebuild-index ~ # Atomically refresh the index (bounded-memory streaming rebuild) ff --update-index ~ # Validate records and report stale entries ff --verify-index # Check index status ff --index-status # Use index for searches ff "*.py" --use-index # Refuse to fall back to live traversal ff "*.py" --index-only ``` The v4 index is a home-grown, versioned binary format. Roots are stored once; each entry stores compact varints and a relative path. Queries decode records as a stream, so memory scales with returned matches instead of database size. Updates write a sibling temporary file and atomically replace the previous index only after a complete successful build. Legacy JSON indexes require one `--rebuild-index` migration. ## Interactive mode commands In interactive mode, use these commands: ```bash :cd # Change directory :exec # Run command on selected files (use {} for path) :rm # Confirm, then delete selected files (never recursive) :sort # Sort by name/size/time :filter # Filter by type (file/dir/link) :h # Toggle hidden files :q # Quit ``` Example: ``` # Select files with Space, then: :exec rm {} # Delete selected files :exec echo {} # Echo selected paths ``` ## Use cases Use `ff` when: * You want one command for name, size/time, content, and git filters. * You need fuzzy file lookup without wiring pipelines. * You want interactive selection and direct output from the same tool. * You want optional index-based search for repeated lookups. * You need semantic symbol discovery (`--function`, `--class`, `--symbol`) without launching a language server. Use something else when: * Choose `find` for highly specialized POSIX expression logic or legacy scripts that already rely on it. * Choose `fd` for minimal syntax and very fast everyday filename/path lookup. * Choose `fzf` when you already have a candidate list and want the best interactive narrowing. ## Usage ```text ff [OPTIONS] [path ...] ff "" [path ...] ``` ### High-value flags | Area | Flags | | ----------------- | ------------------------------------------------------------------------- | | Match mode | `--glob`, `--regex`, `--fixed`, `--fuzzy` | | Path mode | `--name`, `--full-path`, `--full-match` | | Traversal | `-H`, `-L`, `-x`, `--gitignore`, `--max-depth`, `-j` | | Filters | `--type`, `--extension`, `--size`, `--changed`, `--contains`, `--exclude` | | Git | `--git-modified`, `--git-untracked`, `--git-tracked`, `--git-changed` | | Output | `--long`, `--json`, `--ndjson`, `--table`, `--sort`, `--limit`, `--stats` | | Interactive/index | `--interactive`, `--select`, `--use-index`, `--index-only`, `--rebuild-index`, `--update-index`, `--verify-index` | ### More examples ```bash # Recent logs, newest first ff "*.log" --sort time --reverse # Changed Python files in the current directory tree ff "*.py" --changed 7d . # Symbol search inside a project ff --function parse . # Interactive select, then open with editor ff "*.nim" --select --exec "vim {}" ``` ## Important repository files If you are reading the codebase, start here: * Entry point: [`src/ff.nim`](src/ff.nim) * CLI parsing/config: [`src/ff/cli.nim`](src/ff/cli.nim) * Search engine: [`src/ff/search.nim`](src/ff/search.nim) * Core types: [`src/ff/core.nim`](src/ff/core.nim) * Output formatting: [`src/ff/output.nim`](src/ff/output.nim) * Fuzzy matcher: [`src/ff/fuzzy.nim`](src/ff/fuzzy.nim) * Index support: [`src/ff/index.nim`](src/ff/index.nim) * Interactive UI: [`src/ff/interactive.nim`](src/ff/interactive.nim) * Semantic search: [`src/ff/semantic.nim`](src/ff/semantic.nim) * Package metadata: [`fastfind.nimble`](fastfind.nimble) * Binary output dir: [`bin/`](bin/) Project docs: * Contributing guide: [`CONTRIBUTING.md`](CONTRIBUTING.md) * Security policy: [`SECURITY.md`](SECURITY.md) * Code of conduct: [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) ## Platform support * Linux: full support * macOS: full support * FreeBSD, OpenBSD, NetBSD, DragonFly BSD: native CLI builds and tests * Solaris 11.4: native CLI builds and tests * illumos: native CLI builds and tests on OmniOS, OpenIndiana, and Tribblix * Windows: partial support (interactive/index behavior may differ) The manual uses classic `man` macros and ASCII roff source for BSD/macOS mandoc, Linux groff, and Solaris/illumos man/nroff. Both `ff.1` and `fastfind.1` contain the complete source. Run `nimble test_man` to check available formatters, or `sh tests/manpage_portability.sh` for a native smoke test without Python. See the [manual maintenance notes](mandoc/README.md). ## Contributing Contributions are welcome. Please read [`CONTRIBUTING.md`](CONTRIBUTING.md) first. ## Security Wanna talk privately? email me at: [robertflexxgh@gmail.com](mailto:robertflexxgh@gmail.com) Please report vulnerabilities using [`SECURITY.md`](SECURITY.md). ## License MIT License. See [`LICENSE`](LICENSE). ## Regards, As a solo developer, i love and hate developing. I love developing a lot especially for the open source community, and i enjoy making free software for EVERYONE to enjoy; 0 fee. But development gets hard especially on a medium scale project as 1 guy, and if updates dont happen often, or updates are buggy, i am very sorry. I take hours out of my day strictly programming on projects i love, and care about. Especially this one, i focus a lot of my time on this and im happy its getting attention. Programming has been a wild ride for me, and a very passionate one too, as i love doing the things i do for the open source community. And the cons. Developing a lot has taken a toll on my mental health, as i barely get sleep at night due to me constantly debugging stuff lol, but i will always try to push new updates on a lot of my software. Thank you for the support! > - RobertFlexx