Help us improve
Share bugs, ideas, or general feedback.
From agentops
Teaches precise, fast `rg` (ripgrep) search habits over slow `grep -r` or `find | xargs grep` patterns. Covers type filtering, quoting, `--json` output, and avoiding multiline reflex.
npx claudepluginhub boshu2/agentops --plugin agentopsHow this skill is triggered — by the user, by Claude, or both
Slash command
/agentops:ripgrep-search-disciplineThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- TOC: Critical Constraints | Why This Exists | Quick Start | Flag Reference | Strategy Decision Tree | Robot Mode | Exit Codes | Slow Patterns | Quality Rubric | Examples | Troubleshooting | See Also -->
Searches codebases using ripgrep (rg) with regex patterns, file type/extension filtering, multi-line support, and .gitignore respect. Use for text patterns, code snippets, or multi-file analysis.
Fast recursive grep with gitignore awareness for regex content searches across source trees. Invoke `rg` with `-c` or `--max-count` to bound results.
Enhances codebase searches via semantic query parsing, regex patterns, AST analysis, symbol lookups, and optimized grep/ripgrep with relevance ranking and context.
Share bugs, ideas, or general feedback.
Core insight:
rgis gitignore-aware, parallel, and Unicode-correct by default. Reaching forgrep -rorfind | xargs grepthrows all three away. Default torg.
Default to rg, never grep -r or find … -exec grep.
Why: rg walks the tree in parallel, skips .git/, node_modules/, and
everything in .gitignore automatically, and is typically 5–50× faster.
# WRONG — slow, scans ignored dirs, no parallelism
grep -rn "parseConfig" .
find . -name '*.go' -exec grep -l "parseConfig" {} +
# CORRECT
rg "parseConfig"
rg -t go "parseConfig"
Filter by type with -t/--type, not by piping find into rg.
Why: -t go is resolved internally against rg's built-in type map; spawning
find first serializes the walk and defeats rg's own ignore logic.
# WRONG
find . -name '*.py' | xargs rg "TODO"
# CORRECT
rg -t py "TODO"
Quote the pattern; escape regex metacharacters or use -F.
Why: an unquoted pattern is glob-expanded by the shell, and . ( [ *
$ are regex operators. Literal searches must use -F/--fixed-strings.
# WRONG — shell expands *, regex eats the dots and parens
rg config.get(*)
# CORRECT — literal search for the exact text
rg -F 'config.get(*)'
Never use -U/--multiline as a reflex. It buffers whole files and is far
slower. Why: most "find the function" searches are single-line; only reach
for -U when the match genuinely spans newlines.
For agent/tool consumption, emit --json, not human output.
Why: human output is heading-grouped and color-coded; --json is one
stable NDJSON object per event, parseable without regex-scraping.
Agents repeatedly default to grep -rn, find -exec, or unfiltered rg over the
whole repo, then wait on (or drown in) results. The cost is real: scanning .git/
and vendored trees, missing the -t filter that would have narrowed 10k files to
40, or dumping thousands of lines into context when -l / -c would have answered
the question in one line. This contract encodes the small set of flags and the
decision tree that turn search from a bottleneck into a reflex. It is a reference
you grep in place, not a workflow you execute top-to-bottom.
rg "needle" # recursive, gitignore-aware, line-numbered, grouped
rg -t go "func NewServer" # only Go files
rg -i "deadline" # case-insensitive
rg -S "Deadline" # smart-case: literal-lower → insensitive, has-upper → sensitive
rg -l "import requests" # files-with-matches only (cheap, great for "where is X used")
rg -c "TODO" # per-file match counts
rg -A3 -B1 "panic(" # 3 lines after, 1 before each hit
rg -o '\bv\d+\.\d+\.\d+\b' # print only the matched substring (extraction)
rg -F 'a[b].c(' src/ # literal search, no regex interpretation
rg --json "needle" | jq -c . # machine-readable NDJSON for tooling
| Need | Flag | Note |
|---|---|---|
| Limit by language/type | -t TYPE / --type-not TYPE | rg --type-list to see types; --type-add for custom |
| Limit by path pattern | -g GLOB / --iglob | -g '!**/test/**' to exclude; repeatable |
| Context | -A N / -B N / -C N | after / before / both |
| Files only (cheap) | -l / --files-with-matches | answer "where" without dumping bodies |
| Counts | -c / --count-matches | -c = lines/file; --count-matches = total hits |
| Literal (no regex) | -F / --fixed-strings | always use for strings with .([*$ |
| Whole word | -w / --word-regexp | avoids id matching valid |
| Case | -i / -s / -S | insensitive / sensitive / smart |
| Extract substring | -o / --only-matching | print match, not whole line |
| Cross-line match | -U / --multiline (+--multiline-dotall) | slow; only when match spans \n |
| Search ignored files | --no-ignore / --hidden / -uu | opt back in to .gitignored / dotfiles |
| Bound the search | --max-count N / --max-depth N | cap hits per file / dir depth |
| Replace in output | -r REPLACEMENT | preview substitutions (does NOT write files) |
| Perl regex | -P / --pcre2 | lookaround/backrefs; slower, use only when needed |
| Stable order | --sort path | deterministic output (disables parallelism) |
| Machine output | --json | NDJSON event stream — parse this, not text |
What do you need?
├── "Which files contain X?" → rg -l "X" (cheapest)
├── "How many / how often?" → rg -c "X" or --count-matches
├── "Where is X, with context?" → rg -C3 "X" (then narrow with -t/-g)
├── "Only in Go/Py/etc.?" → rg -t go "X"
├── "Exclude tests/vendor?" → rg -g '!**/{test,vendor}/**' "X"
├── "Literal string with . ( [ ?" → rg -F 'X'
├── "Just the matched token?" → rg -o 'PATTERN'
├── "Match spans multiple lines?" → rg -U 'A[\s\S]*B' (slow; last resort)
├── "Need to parse results?" → rg --json "X" | jq
└── "Too many hits?" → add -t, -g, -w, or --max-count to NARROW first
Default reflex: start with rg -l or rg -c to scope the problem, then re-run
narrowed (-t, -g, -w) before pulling full match bodies into context.
For tool/agent consumption, use NDJSON — one JSON object per event:
rg --json "pattern" path/
Event type values: begin (per file), match (one per matching line, carries
data.path, data.line_number, data.lines.text, data.submatches[]), end
(per-file stats), summary (totals). Parse with jq -c 'select(.type=="match")'.
scripts/rg-json.sh PATTERN [PATH] wraps this and emits path:line:text tuples —
Execute it (bash {baseDir}/scripts/rg-json.sh "TODO" src/) when you want
structured hits without writing jq inline.
| Code | Meaning | Agent action |
|---|---|---|
0 | One or more matches found | Proceed; results are on stdout |
1 | No matches (clean, not an error) | Treat as "not found", not failure |
2 | Real error (bad regex, unreadable path, bad flag) | Fix the command; read stderr |
Why: code 1 is the normal "absent" signal — never treat a no-match as a crash.
Only 2 is an actual failure. Use rg -q "X" && … to branch on presence cheaply.
| Slow / wrong | Fast / right | Why |
|---|---|---|
grep -rn "x" . | rg "x" | parallel + gitignore-aware |
find . -name '*.js' | xargs grep x | rg -t js "x" | no serial find, uses type map |
rg "x" then visually filter | rg -t go -g '!**/vendor/**' "x" | narrow at the source |
rg -U "x" for single-line | rg "x" | multiline buffers whole files |
rg "a.b.c" expecting literal | rg -F "a.b.c" | dots are regex wildcards |
| dumping all matches into context | rg -l / -c first | scope before you read bodies |
rg "x" $(find ...) | rg "x" (let rg walk) | rg's walker is the fast path |
-l/-c before full bodies; -t/-g before whole-repo)?-F and words with -w so the regex engine isn't fighting you?1 as "no match" (not an error) and 2 as the only real failure?--json instead of scraping human text?# Find a function definition in Go, with surrounding context
rg -t go -A5 'func \(s \*Server\) Start'
# Locate every file importing a package, no bodies (fast survey)
rg -l 'github.com/boshu2/agentops'
# Count TODO/FIXME density per file, excluding tests
rg -c -g '!**/*_test.go' '\b(TODO|FIXME)\b'
# Extract all semver tags mentioned anywhere
rg -o --no-filename '\bv\d+\.\d+\.\d+\b' | sort -u
# Cross-line: a struct field block (multiline, last resort)
rg -U 'type Config struct \{[\s\S]*?\}'
# Machine-readable hits for a downstream tool
rg --json 'panic\(' cli/ | jq -c 'select(.type=="match") | {path:.data.path.text, line:.data.line_number}'
| Symptom | Cause | Fix |
|---|---|---|
| "No matches" but you know it's there | file is gitignored / hidden | add --no-ignore and/or --hidden (-uu for both) |
| Pattern matches too much | regex metachar in your literal | use -F, or escape . ( [ * $ |
id matches inside valid | substring match | add -w / --word-regexp |
| Search is slow | -U on large files, or huge unfiltered tree | drop -U; add -t/-g/--max-depth |
| Output unparseable in a script | scraping human format | switch to --json |
| Different runs reorder results | parallel walk | add --sort path for determinism |
-P complains | ripgrep built without PCRE2 | rephrase without lookaround/backrefs |
| For… | Reference |
|---|---|
| Codex/cross-vendor parity (dual-file install) | references/codex-parity.md |
| NDJSON wrapper to run | scripts/rg-json.sh (Execute) |
| Self-check this skill's structure | scripts/validate.sh (Execute) |