# Fast File Search

> Ultra-fast file search toolkit for AI agents, Neovim, and code editors. MCP server for Claude Code/Cursor/Codex, frecency-ranked search, symbol indexing via tree-sitter, and a Rust core that outperforms ripgrep and fzf in long-running processes.

- **Type:** MCP server
- **Install:** `agentstack add mcp-quangdang46-fast-file-search`
- **Verified:** Pending review
- **Seller:** [quangdang46](https://agentstack.voostack.com/s/quangdang46)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [quangdang46](https://github.com/quangdang46)
- **Source:** https://github.com/quangdang46/fast_file_search

## Install

```sh
agentstack add mcp-quangdang46-fast-file-search
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

A code-aware file search CLI for humans and AI agents. Really fast.

ffs replaces `find`, `fd`, `grep`, `rg`, `glob`, and `cat` with a single
typo-resistant, frecency-ranked binary, and adds tree-sitter powered
code-navigation (`symbol`, `callers`, `callees`, `refs`, `flow`, `impact`)
and a token-budget aware file reader for AI agents.

---

## Install

```bash
curl -fsSL "https://raw.githubusercontent.com/quangdang46/fast_file_search/main/install.sh?$(date +%s)" | bash
```

**Windows (PowerShell):**

```powershell
irm https://raw.githubusercontent.com/quangdang46/fast_file_search/main/install.ps1 | iex
```

The scripts detect your platform, fetch the matching binary from
[GitHub Releases](https://github.com/quangdang46/fast_file_search/releases/latest),
verify the SHA-256 sidecar, and atomically install to `~/.local/bin/ffs`
(Unix) or `%LOCALAPPDATA%\ffs\bin` (Windows).

### Supported platforms

- Linux x86_64 / aarch64 (musl-linked, portable across glibc versions)
- macOS x86_64 / aarch64
- Windows x86_64 / aarch64

---

## Quick start

```bash
ffs --help
ffs index                                  # one-time warm-up (~200ms on a 10k-file repo)
ffs find UnifiedScanner
ffs find grep --scored                     # role-aware file search (+20 impl, -15 test)
ffs grep '\bTODO\b' --root crates/
ffs grep 'fn main' --group                # symbol-grouped grep output
ffs symbol FilePicker
ffs callers UnifiedScanner
ffs read crates/ffs-engine/src/lib.rs --budget 5000 --filter minimal
ffs map --depth 3
ffs mcp                                    # run as MCP server over stdio
```

`ffs index` writes a tree-sitter symbol-index cache to `/.ffs/`. Subsequent
`ffs symbol`/`callers`/`refs`/`flow`/`siblings`/`impact` invocations skip the
re-parse and load the cache directly — sub-200 ms on a Linux-kernel-sized repo.
The cache is invalidated automatically on schema bumps, git HEAD changes, or
significant file-count drift. Add `.ffs/` to your `.gitignore` (the
repository's own `.gitignore` already does this).

## Subcommands

```
ffs find        Find files by name. --scored for role-aware ranking (impl +20, test -15)
ffs glob        Match files by glob (replaces glob, shell **)
ffs grep        Search file contents. --group for symbol-grouped output
ffs read        Read a file with token-budget aware truncation (replaces cat)
ffs outline     Render a file's structural outline
ffs symbol      Look up symbol definitions (tree-sitter powered)
ffs callers     List call sites of a symbol
ffs callees     List symbols referenced inside a symbol body
ffs refs        Definitions + single-hop usages of a symbol in one shot
ffs flow        Drill-down envelope per definition (def + body + callees + callers)
ffs siblings    Sibling symbols (peers in the same parent scope)
ffs deps        File imports + the workspace files that depend on it
ffs impact      Rank files by how much they'd be affected if a symbol changed
ffs index       Build / refresh the on-disk indexes (Bigram, Bloom, Symbol, Outline)
ffs map         Render the workspace as a tree annotated with file count and tokens
ffs overview    High-signal summary of the workspace (languages, top symbols, ...)
ffs mcp         Run as an MCP server over stdio
ffs guide       Print the embedded agent guide
```

Pass `--format json` for machine-readable output. Use `--root ` to override
the working directory globally.

---

## MCP server

`ffs mcp` (or the standalone `ffs-mcp` binary) speaks JSON-RPC over stdio
and registers 15 tools that any MCP-capable agent (Claude Code, Codex,
OpenCode, Cursor, Cline, …) can call:

### Tools registered

| Tool            | What it answers                                                                                  |
| --------------- | ------------------------------------------------------------------------------------------------ |
| `ffs_find`    | Fuzzy file-name search. Smart-case, frecency-ranked, glob constraints, git-aware.                |
| `ffs_grep`      | Content search. Plain / regex / fuzzy auto-detect, pagination cursor, definition-first hinting.  |
| `ffs_multi_grep`    | OR-logic multi-pattern content search via SIMD Aho-Corasick.                                     |
| `ffs_symbol`    | Exact + prefix lookup over the tree-sitter symbol index (16 languages).                          |
| `ffs_callers`   | Find call sites of a symbol. Bloom-filter narrowed candidates → literal-text confirm pass.       |
| `ffs_callees`   | Symbols referenced inside the body of a definition.                                              |
| `ffs_refs`      | Definitions + single-hop usages of a symbol in one shot. Mirrors `ffs refs` from the CLI.        |
| `ffs_flow`      | Drill-down envelope per definition: def metadata + body excerpt + top-N callees + top-N callers. |
| `ffs_impact`    | Rank workspace files by how much they'd be affected if `name` changed.                           |
| `ffs_outline`   | Structural outline of a file (functions, classes, top-level decls). Agent-friendly default view. |
| `ffs_siblings`  | Peers of a symbol in its parent scope — surfaces the rest of the impl/class around a method.     |
| `ffs_deps`      | A file's imports plus the workspace files that depend on it. Blast-radius estimate for changes.  |
| `ffs_map`       | Workspace tree annotated with file count and per-directory token estimate.                       |
| `ffs_overview`  | High-signal repo summary: languages, top-defined symbols, entry-point candidates.                |
| `ffs_read`      | Token-budget aware file read. Maps `maxTokens` to `~85% body × 4 bytes/token`, applies filters.  |

Recommended agent prompt — drop into `CLAUDE.md` (or equivalent):

```markdown
For any file search, grep, or symbol lookup in the current git-indexed
directory, use ffs tools.
```

---

## Why ffs

- **Typo-resistant fuzzy matching** for both paths and content. `*.rs !test/ shcema`
  is a valid query; even with a typo in `shcema` it still finds matches.
- **Frecency-ranked.** Files you open often rank higher next time. Warm-up uses
  git touch history.
- **Tree-sitter symbol index** across 16 languages — answer code questions, not
  just file-name questions.
- **Bigram + Bloom pre-filter stack.** `ffs callers SomeSymbol` typically inspects
  fewer than 30 files on a 10k-file repo.
- **Token-budget aware reader** for AI agents. `ffs read path --budget 5000` clips
  the body but always preserves the file header and a `[truncated to budget]`
  footer so the agent knows the output was clipped.
- **One long-lived process when used via library/MCP.** No per-call subprocess
  spawn, no re-reading `.gitignore`, no rebuilding state. After the first call
  every subsequent search hits warm memory.

On a 500k-file Chromium checkout, that is the difference between 3-9 **seconds**
per ripgrep spawn and sub-10 ms per ffs query.

---

## Architecture

ffs is a single Rust workspace organised as a **layered core** with multiple
thin frontends. Every surface (CLI, MCP, Neovim, Node, Bun, C ABI) calls
into the same engine — there is no duplicated search logic.

### Layered design

```
┌──────────────────────────────────────────────────────────────────────┐
│  Frontends                                                           │
│  ─────────                                                           │
│  ffs-cli    ffs-mcp    ffs-c                                         │
│  (binary)   (stdio     (C ABI                                        │
│             JSON-RPC)  .so/.dylib/.dll)                              │
└────────────────────────────────┬─────────────────────────────────────┘
                                 │ all surfaces share one core
                                 ▼
┌──────────────────────────────────────────────────────────────────────┐
│  Engine layer                                                        │
│  ────────────                                                        │
│  ffs-engine          unified scanner · dispatch · ranking · memory   │
│  ffs-query-parser    `*.rs !test/ shcema` → constraints + modes      │
└────────────────────────────────┬─────────────────────────────────────┘
                                 │
                                 ▼
┌──────────────────────────────────────────────────────────────────────┐
│  Capability layer                                                    │
│  ────────────────                                                    │
│  ffs-symbol     tree-sitter index · bloom · bigram pre-filter        │
│  ffs-grep       SIMD literal / regex grep                            │
│  ffs-budget     token-aware reader · comment + whitespace filters    │
└────────────────────────────────┬─────────────────────────────────────┘
                                 │
                                 ▼
┌──────────────────────────────────────────────────────────────────────┐
│  Core layer (ffs-core)                                               │
│  ─────────────────────                                               │
│  scan · file_picker · score · bigram_filter · git · frecency         │
│  background_watcher · ignore · simd_path · constraints               │
└──────────────────────────────────────────────────────────────────────┘
```

Each layer only depends on the ones below it. Adding a new frontend
(e.g. a Python binding) means wrapping `ffs-c`; it never reaches into
`ffs-core` directly.

### Crate responsibilities

| Crate              | Role                                                                                |
| ------------------ | ----------------------------------------------------------------------------------- |
| `ffs-core`         | Filesystem scan, frecency, fuzzy scoring, bigram filter, git integration, watcher. |
| `ffs-query-parser` | Parses the query DSL (globs, negations, regex, fuzzy fallback).                    |
| `ffs-symbol`       | Tree-sitter symbol index, bloom filter, outline cache, on-disk artifact format.    |
| `ffs-grep`         | SIMD literal & regex content search backend.                                       |
| `ffs-budget`       | Token-budget aware file reader and content filters for AI agents.                  |
| `ffs-engine`       | Glue layer: dispatch, ranking, prefilter, in-memory state coordination.            |
| `ffs-cli`          | The `ffs` binary, subcommand routing, on-disk cache (`.ffs/`).                     |
| `ffs-mcp`          | JSON-RPC MCP server exposing 16 tools over stdio.                                  |
| `ffs-c`            | Stable C ABI (`libffs_c`, header in `crates/ffs-c/include/ffs.h`).                 |

### Query path (e.g. `ffs callers UnifiedScanner`)

```
   user input                        on-disk artifacts in /.ffs/
   ───────────                       ─────────────────────────────────
        │                            ┌────────────────────────────┐
        ▼                            │ symbol_index.postcard.zst  │
   ┌─────────────┐                   │ bigram.postcard.zst        │
   │ ffs-cli     │                   │ meta.json (HEAD, schema)   │
   │ subcommand  │                   └─────────────┬──────────────┘
   │ dispatch    │                                 │ mmap + decode
   └──────┬──────┘                                 ▼
          │                                  ┌──────────────┐
          ▼                                  │ ffs-symbol   │
   ┌─────────────┐    parse query DSL        │ index + bloom│
   │ ffs-query-  │ ────────────────────►     └──────┬───────┘
   │ parser      │                                  │
   └──────┬──────┘                                  │ candidate
          │ Mode + Constraints                      │ file set
          ▼                                         │
   ┌─────────────────────────────────────────┐     │
   │ ffs-engine                              │ ◄───┘
   │  classify ▸ prefilter ▸ dispatch        │
   │     │          │           │            │
   │     ▼          ▼           ▼            │
   │  symbol     bigram      grep / scan     │
   │  lookup     filter      backends        │
   └────────────────────┬────────────────────┘
                        │ ranked hits
                        ▼
   ┌─────────────────────────────────────────┐
   │ ffs-engine::ranking                     │
   │   frecency · fuzzy score · git-touch    │
   └────────────────────┬────────────────────┘
                        ▼
                ┌───────────────┐
                │ formatter     │  text │ json │ MCP tool result
                └───────────────┘
```

### Indexing path (`ffs index`)

```
  walk repo (gitignore-aware, parallel)
  ────────────────────────────────────────►   ffs-core::scan
                                                    │
                                                    ▼
                                           ┌──────────────────┐
                                           │ ffs-symbol       │
                                           │  tree-sitter     │
                                           │  parse · extract │
                                           │  decls + scopes  │
                                           └────────┬─────────┘
                                                    ▼
                                           ┌──────────────────┐
                                           │ build artifacts  │
                                           │  • bigram        │
                                           │  • bloom         │
                                           │  • symbol index  │
                                           │  • outline cache │
                                           └────────┬─────────┘
                                                    ▼
                                           write `/.ffs/*.postcard.zst`
                                           + meta.json (schema · HEAD · count)
```

The cache invalidates automatically on schema bumps, git HEAD changes,
or significant file-count drift. Subsequent `ffs symbol` / `callers` /
`refs` / `flow` / `siblings` / `impact` invocations skip the re-parse
and load the cache directly — sub-200 ms on a Linux-kernel-sized repo.

### Background watcher (long-lived processes)

When ffs is embedded as a library (`ffs-c`, `ffs-mcp`) it
keeps a single process alive and runs a notify-based background thread
that incrementally updates the in-memory index on filesystem events.
That is why MCP hits stay sub-10 ms after the first call —
no `.gitignore` re-read, no cold scan, no subprocess spawn.

```
┌─────────────────┐       fs events        ┌──────────────────────┐
│ host process    │ ◄──────────────────────│ background_watcher   │
│ (mcp / cli /    │                        │  (notify crate)      │
│  external C)    │                        └──────────┬───────────┘
│                 │                                   │ patch
│ ┌─────────────┐ │                                   ▼
│ │ in-memory   │ ├──── query ────────────►   ffs-engine ──► result
│ │ index +     │ │
│ │ frecency DB │ │
│ └─────────────┘ │
└─────────────────┘
```

---

## Performance & Benchmarks

All numbers below are **single-threaded medians** measured with
[Criterion.rs](https://github.com/bheisler/criterion.rs) on a Linux x86-64
machine. Benchmarks run weekly in CI (`bench-track.yml`); the raw Criterion
HTML reports are published as GitHub Actions artifacts.

### Engine dispatch (ffs-engine)

End-to-end latency from raw query string to ranked results over a 256-file
fixture repo (32 dirs × 8 files).

| Query type | Example | Median |
|---|---|---|
| Symbol lookup | `worker_05_3` | **202 ns** |
| Concept / NL | `how does the worker handle payloads` | **205 ns** |
| File path | `mod_03/file_2.rs` | **2.2 µs** |
| Glob | `**/*.rs` | **2.8 µs** |

Query classification alone: 107–1,640 ns depending on pattern complexity.
Scoring a single result (`score_one`): **~3 ns**.

### Bigram index (ffs-core)

Query la

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [quangdang46](https://github.com/quangdang46)
- **Source:** [quangdang46/fast_file_search](https://github.com/quangdang46/fast_file_search)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-quangdang46-fast-file-search
- Seller: https://agentstack.voostack.com/s/quangdang46
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
