agentgrep grep

The agentgrep grep command searches normalized prompt and history records with the flag grammar and output behavior of ripgrep and the_silver_searcher. If you already reach for rg -i or ag -F without thinking, the same flags work here against your AI history.

Defaults follow rg: smart-case (case-insensitive unless the pattern contains uppercase), regex pattern interpretation, color on TTY, and line-aware output. Each matching record emits one row per matching line. By default each row is just path:text (rg’s default pipe shape); pass -n / --line-number to add line numbers, --column to add column numbers (implies -n), and --vimgrep for the path:line:col:text shape with one row per match span. On TTY a per-record heading line opens with agent · timestamp · path. The one deliberate divergence is session deduplication — see Session deduplication below.

Examples

A literal single-pattern search across every agent:

$ agentgrep grep bliss

Force case-insensitive matching:

$ agentgrep grep -i 'serene bliss'

Treat the pattern as a literal substring (not a regex):

$ agentgrep grep -F --type history 'v1.2.3'

Stream an rg-style event stream as JSON:

$ agentgrep grep --json design

Drop session dedup for the raw rg-faithful view:

$ agentgrep grep --no-dedupe foo

Open the Textual explorer pre-filled with the grep query:

$ agentgrep grep -i foo --ui

Silence the stderr spinner:

$ agentgrep grep --no-progress bliss

Output format

By default grep emits one stdout line per matching line within a record, with the matched substring highlighted. The shape mirrors rg:

  • On TTY (heading mode, default): a per-record heading line carries agent · timestamp · path, then each matching line follows as text (or line:text with -n, or line:col:text with --column). Records are separated by a blank line. Toggle off with --no-heading.

  • On pipe (flat mode, default when stdout isn’t a TTY): every match emits as path:text (rg’s default), so agentgrep grep foo | jq or ... | awk see one line per match. -n adds :line: after the path; --column adds :col: (implies -n). Toggle the heading on with --heading.

The --vimgrep flag forces flat mode and emits path:line:col:text with one row per match span (rather than one per match line), so a line with two hits produces two rows — useful for vim :cfile and other editors that consume the file:line:col:message format.

--only-matching / -o collapses output to just the matched substrings, one per line — the per-record heading separator is suppressed under -o, so the stream is exactly bare matches back-to-back (rg -o parity). -l / --files-with-matches emits only the deduplicated paths. -c emits path:N per matching record with the count of matching lines (or just N when exactly one record matched), matching rg -c.

Live streaming

grep consumes the Event-stream engine directly — text and NDJSON output emit each match as the engine finds it, then flush so your terminal sees rows live. On a slow store the first matches appear within milliseconds, not after the whole scan finishes.

The eager output modes (--json, -c, -l, -L, -v) buffer because their output shape needs the final tally or cross-record deduplication.

Progress

The stderr progress spinner (when stderr is a TTY) lets you know a search is still running on slow stores. Silence it with --no-progress or the equivalent --progress=never:

$ agentgrep grep --no-progress bliss

Progress always writes to stderr, so it never collides with stdout output — agentgrep grep foo | less won’t see the spinner in the piped buffer.

Command

Usage

usage: agentgrep grep [-h] [--agent {codex,claude,cursor,gemini,all}] [-F |
                      -E | -w] [-i | -s | -S] [-c] [-l] [-L] [-o] [-v]
                      [--no-dedupe] [-n | -N] [--heading | --no-heading]
                      [-m N] [--vimgrep] [--column]
                      [--type {prompts,history,all}]
                      [--progress {auto,always,never}] [--no-progress]
                      [--style {default,pretty}] [--json | --ndjson | --ui]
                      PATTERN [PATTERN ...]

Positional Arguments

patterns PATTERN

One or more patterns (regex by default; combined as AND)

Default
None
Required

Options

--agent

Limit results to a specific agent; repeatable

Default
[]
Choices
codex, claude, cursor, gemini, all
-c, --count

Print only the number of matches per (agent, store)

Default
False
-l, --files-with-matches

List source paths with at least one match

Default
False
-L, --files-without-match

List source paths with no matches

Default
False
-o, --only-matching

Print only the matched portion of each record

Default
False
-v, --invert-match

Print records that do NOT match

Default
False
--no-dedupe

Disable per-session dedup (raw rg-style view; default dedupes)

Default
False
-m, --max-count N

Stop after N matches

Default
None
Type
int
--vimgrep

Emit one match per line as path:line:col:text

Default
False
--column

Show column numbers in output (implies -n)

Default
False
--type

Record type to search (default: prompts)

Default
None
Choices
prompts, history, all
--progress

Show search progress on stderr

Default
auto
Choices
auto, always, never
--no-progress

Silence the stderr progress spinner (alias for --progress=never)

Default
None
--style

Output style: default (rg-faithful) or pretty (snippet-first, amber highlights)

Default
default
Choices
default, pretty
-F, --fixed-strings

Treat patterns as literal strings, not regex

Default
False
-E, --extended-regexp

Treat patterns as regex (default)

Default
False
-w, --word-regexp

Match the pattern only as a whole word

Default
False
-i, --ignore-case

Force case-insensitive matching

Default
False
-s, --case-sensitive

Force case-sensitive matching

Default
False
-S, --smart-case

Smart-case (default): case-sensitive when pattern has uppercase

Default
False
-n, --line-number

Force line numbers in output

Default
False
-N, --no-line-number

Suppress line numbers

Default
False
--heading

Force file-grouped headings (default on TTY)

Default
False
--no-heading

Suppress file-grouped headings (default on pipe)

Default
False
--json

Emit one JSON document

Default
False
--ndjson

Emit one JSON object per line

Default
False
--ui

Launch a read-only UI

Default
False

Exit codes

agentgrep grep follows grep’s conventions:

  • 0 — at least one matching record was found

  • 1 — no matches

  • 2 — error during search (invalid regex, unreadable store, …)

Use these in shell scripts the same way you’d use rg’s exit codes.

Error handling

Invalid regex patterns are caught at the argparse layer and surfaced with the standard argparse error shape, then exit 2:

$ agentgrep grep '['
usage: agentgrep grep [...]
agentgrep grep: error: invalid regex '[': unterminated character set at position 0

The check runs before the engine starts so a malformed pattern never emits partial output and never escapes as a Python traceback. -F (fixed-strings) skips the check — its patterns are literal substrings, not regex.

Empty patterns are also rejected at parse time (git-grep parity):

$ agentgrep grep ''
usage: agentgrep grep [...]
agentgrep grep: error: pattern cannot be empty

The check applies to every term — a valid pattern followed by an empty one (agentgrep grep foo '') still fails.

-v / --invert-match for plain text output is not yet implemented and is refused at parse time:

$ agentgrep grep -v bliss
usage: agentgrep grep [...]
agentgrep grep: error: --invert-match for text output is not yet
implemented (see https://github.com/tony/agentgrep/issues/8); use
-c or -L

The flag is still honored under -c (returns 0 if any record matched, 1 if none) and -L (lists sources with no matches), since both reduce to a “did anything match?” question that the engine’s current output supports. Tracking issue: tony/agentgrep#8.

Files without matches

-L / --files-without-match lists every planned source whose records produced no matches — the rg-style “what did I miss?” view:

$ agentgrep grep -L bliss
~/.codex/sessions/2026/05/b.jsonl

Exit code follows rg: 0 when at least one path is printed (the listed paths are themselves the “match” for -L), 1 when every planned source contains a match.

Session deduplication

By default grep deduplicates matches by session so a single conversation that repeats near-identical text doesn’t drown the output. This is the one place where agentgrep grep deliberately diverges from rg’s raw behavior — AI history stores often replay the same message text many times across one session, which makes the raw rg view noisier than a filesystem grep.

Pass --no-dedupe to disable the per-session dedup and get every matching record back, exactly matching rg’s “every line is its own match” convention:

$ agentgrep grep --no-dedupe foo

JSON output

Pass --json to emit an rg-shaped per-line event stream:

$ agentgrep grep --json deploy

The output is a JSON document whose events array carries one begin event opening each matching record, one match event per matching line within that record, an end event closing the record, and a final summary event with the total match count.

Each match event mirrors rg’s per-line shape:

{"type":"match","data":{
  "path":{"text":"~/.codex/.../sample.jsonl"},
  "line_number":1,
  "lines":{"text":"The bliss primitive ships with serene defaults"},
  "submatches":[{"match":{"text":"bliss"},"start":4,"end":9}]}}

submatches carries byte offsets within the line so consumers can slice the matched substring directly. Tools written against rg’s JSON contract can consume agentgrep’s stream with the same parser.

NDJSON output

Pass --ndjson for one event per line:

$ agentgrep grep --ndjson foo | jq 'select(.type == "match") | .data.lines.text'

This mode is the right pick when piping into another CLI, into jq, or into a non-MCP agent that consumes results incrementally.

Interactive UI

Pass --ui to open the Textual explorer pre-filled with the grep query.

Query language

grep accepts the same Lucene-style field syntax search does — mix field predicates with text patterns inline:

$ agentgrep grep agent:codex bliss
$ agentgrep grep '(agent:codex OR agent:cursor) AND deploy'

The text portion (bliss, deploy) feeds grep’s existing line- aware matching; the field predicates (agent:, path:, timestamp:) prune sources and filter records around it. A query with only field predicates and no text errors out — grep needs text to match lines against, so steer to agentgrep find for source-level enumeration. See Query language for the full grammar.