lets find
Searches files or directories and prints hits as path:line, grouped by file. locate is an
alias for the same command.
lets find [OPTIONS] <PATTERN> [PATHS]...
What it does
Regex search by default (Rust regex syntax), walked with the same rules as ripgrep:
.gitignore, .ignore, the global gitignore and .git/info/exclude are honored, and hidden
files are skipped unless asked for. Capped at 50 hits by default — over the cap, no hit lines are
printed; instead a bounded map of the files holding the most hits, so the next call can narrow to
one of them. Replaces grep -rn and rg run directly in the shell.
Flags
| Flag | Meaning | Default |
|---|---|---|
-F, --fixed-string |
match the pattern literally, not as a regex | off |
-i, --ignore-case |
case-insensitive | off |
-w, --word |
match whole words only | off |
--cap <N> |
raise the hit cap (prints hits, not the over-cap map) | 50 |
-l, --files |
list matching files only, one bare path per line, no cap | off |
-c, --count |
print the footer first, then one <count> <path> row per file |
off |
-g, --glob <GLOB> (alias --include) |
narrow the walk to paths the glob matches; footer names it | — |
--hidden |
include hidden files and directories | off |
-A <N> |
lines of context after a hit | — |
-B <N> |
lines of context before | — |
-C <N> |
lines of context on both sides | — |
--no-ignore |
do not honor .gitignore/.ignore/global excludes |
off |
--allow-outside |
permit a path outside the working tree | off |
--json / --jsonl |
structured output | off |
--budget <N> |
shape the answer to ~N tokens | unset |
--max-bytes <N> |
content budget | 65536 |
--max-file-bytes <N> |
files larger than this are skipped as unreadable | 8388608 |
-q, --quiet |
shared flag | off |
Grep-compatible no-ops, accepted so a command copied from grep runs unchanged: -n,
--line-number, -r, -R, -E, -H (find already behaves as if each were set). -v, --invert-match has no equivalent — find only ever prints matching lines — so it is refused at
parse time, exit 64 ERROR_CODE=usage.
Output shape
Under the cap:
── <path>
<n>: <line with the match wrapped in «»>
<n>- <context line>
── <H> hits in <F> files · searched <S> files[ · ignored …][ · skipped …][ · glob …]
Over the cap, no hit lines print. Instead:
<count> <path>
...
── <H> hits in <F> files · over the 50-hit cap · narrow the pattern or the paths, or --files · top 10 files shown
- Matches are wrapped in
«»so they survive a pipe. --filesprints one bare path per line, no header, no line numbers, then the ordinary footer.--countprints the footer first, then<count> <path>rows, count right-aligned to the widest.- A path that does not exist is named in the footer alongside what the other paths produced:
· nope failed (not_found). - A file with a NUL byte, or a UTF-16 file, is not text and is not searched; it is counted in
skipped N (binary a · too large b · unreadable c). - A regex holding one of grep’s BRE escapes (
\|,\(,\),\{,\},\+,\?) that matches nothing is retried read grep-style, and the footer names the reading used:· «A\|B» had no hits, read grep-style as «A|B».
Exit codes
| Exit | Slug | Means |
|---|---|---|
| 0 | — | hits found |
| 1 | not_found |
no hits, or a searched path does not exist |
| 1 | over_cap |
hit count exceeded the cap; the top-files map was printed instead |
| 1 | invalid_pattern |
the regex does not parse |
| 4 | over_budget |
content exceeded --max-bytes and no --budget was given |
| 6 | outside_tree |
a path is outside the working tree; pass --allow-outside |
| 64 | usage |
malformed flag, e.g. -v |
Examples
A basic search, hits wrapped so they survive a pipe:
$ lets find usageCap
── src/config.ts
1: export const «usageCap» = 10
── src/usage.ts
1: import { «usageCap» } from './config'
── 2 hits in 2 files · searched 3 files · ~17 tokens
Two patterns at once, with context:
$ lets find 'Bottom line|Next' small.md
── small.md
5: ## «Bottom line»
13: ## «Next»
── 2 hits in 1 file · searched 1 file
Over the cap: no hits printed, a map of where they are instead, exit 1:
$ lets find needle many-hits.txt
? 1
64 many-hits.txt
── 64 hits in 1 file · searched 1 file · over the 50-hit cap · narrow the pattern or the paths, or --files · top 1 file shown
ERROR_CODE=over_cap
Narrowing the walk with a glob, and the control with no glob:
$ lets find -g '*.ts' needle
── a.ts
1: const «needle» = 1;
── 1 hit in 1 file · searched 1 file · glob *.ts
$ lets find needle
── a.ts
1: const «needle» = 1;
── b.py
1: «needle» = 1
── 2 hits in 2 files · searched 2 files
--files and --count shapes:
$ lets find filler . --files
big.ts
store.go
── 2 files · searched 5 files · ignored 3 (gitignore 1 · hidden 2) · skipped 1 (binary 1)
$ lets find filler . --count
── 393 hits in 2 files · ignored 3 (gitignore 1 · hidden 2) · skipped 1 (binary 1)
185 big.ts
208 store.go
No hits still prints a footer and exits 1, rather than staying silent:
$ lets find zzz_never_appears_zzz small.md
? 1
── 0 hits in 0 files · searched 1 file
no hits for «zzz_never_appears_zzz»
ERROR_CODE=not_found
Back to the top of lets find