The find verb

rf find <pattern> [path] --name <ext> [--structural <pat> --lang <id>] [--json]

find answers “where does pattern appear across files named *.<ext>?” — the job a fd -e <ext> | xargs rg <pattern> pipe does, but recording which stage found each match — which a pipe throws away. --name is required and takes an extension with no leading dot.

Four independent sources

find combines four discovery sources. Every match is attributed to exactly one stage:

1. fd name filters (in-process)

The walker reproduces fd’s default / -H / -I behavior and attributes a content match the default walk dropped to the fd stage that hid it: fd_hidden (a hidden dotfile that matches the name filter, recover with fd -H), fd_ignore (a gitignored file that matches the name filter, fd -I), fd_filter (a file some other default fd filter dropped, fd -u), or fd_name (the content match is in a file outside the *.<ext> name filter — widen the filter). A file the default walk surfaced normally is found.

2. ripgrep binary skip (in-process)

rg_binary marks a file that ripgrep’s default binary detection would skip but that contains the term when binary-as-text is enabled — diffed against the default read.

3. git history (subprocess)

git_deleted recovers a match that is gone from the working tree but present in history — the classic “a secret was committed, then scrubbed.” No tree-only search, however aggressive, can find it. Powered by git; when git is absent the stage contributes nothing.

4. ast-grep structural (subprocess)

With --structural PAT --lang LANG, ast_structural finds matches that have no fixed literal form — a call like db.connect(dsn, ...) that a literal string search cannot express. A file is ast_structural when ast-grep matches it and a literal search for the pattern text does not. Powered by ast-grep; absent it, a STRUCTURAL_UNAVAILABLE warning is emitted and the other stages still run. --structural without --lang is a user-input error (exit 1).

Stage reference

Every find row carries a stage. The complete enum, in the order the binary attributes them:

stage Source Meaning Recovery
found fd + rg Surfaced by the default walk and matched by ripgrep. —
rg_binary rg binary skip fd passed the file, but ripgrep suppressed it as binary. rg -a
fd_hidden fd name filters A hidden dotfile that matches the name filter. fd -H
fd_ignore fd name filters A gitignored file that matches the name filter. fd -I
fd_filter fd name filters A file some other default fd filter dropped. fd -u
fd_name fd name filters The content match is in a file outside the *.<ext> name filter. widen --name
git_deleted git history Gone from the working tree but present in history. recover from Git
ast_structural ast-grep Matched only by structural search, with no fixed literal form. —

The set is authoritative in rf capabilities --json under the find verb’s output_schema.data[].stage.

Why a pipe cannot do this

A shell pipe erases fd’s exit code and conflates ripgrep’s “no files” with “no match,” so downstream there is no way to say which stage dropped a file. Reading every stage in one process keeps each match tied to its origin.

Example

rf find DB_DSN . --name config --json
{
  "ok": true,
  "data": [
    { "file": "app.config",         "stage": "found" },
    { "file": ".secret.config",     "stage": "fd_hidden" },
    { "file": "build/gen.config",   "stage": "fd_ignore" },
    { "file": "blob.config",        "stage": "rg_binary" },
    { "file": "history.config",     "stage": "git_deleted" }
  ]
}

A true negative — a *.config with no term in the tree or in history — is correctly not flagged. The stage reports no false positive.