Your code is a database. Why query it with full-text search?
Agents using grep have to regex their way to understanding — pattern-matching raw text, parsing noise, running multiple commands to answer one question. smartgrep parses source files via tree-sitter, extracts symbols into a structured index, and lets agents query the code the same way you'd query a database.
The queries are designed to be readable by humans and generatable by LLMs, unlike grep regexes.
# grep — terse, brittle, requires regex expertise to read
grep -rn "^pub\s\+fn\|pub async fn" src/commands/ | grep -v "//\|mod.rs"
# smartgrep — reads like a sentence
smartgrep query "functions where visibility = public and file contains 'commands/'"When Claude uses smartgrep, you can see exactly what it's looking for at a glance.
curl -fsSL https://raw.githubusercontent.com/rohitkg98/smartgrep/main/install.sh | shInstalls the latest release binary for your platform (macOS arm64/x86_64, Linux x86_64/arm64). No Rust required.
smartgrep updateFetches and runs the install script to replace the binary in-place.
git clone git@github.com:rohitkg98/smartgrep.git
cd smartgrep
cargo install --path .Requires Rust 1.70+.
Install as a Claude Code skill so Claude automatically uses smartgrep for structural questions — no prompting needed:
smartgrep install-skill --global # all projects (~/.claude/skills/)
smartgrep install-skill # this repo only (.claude/skills/)Or add to your project's CLAUDE.md manually:
## Code Navigation
Use `smartgrep` for structural code queries instead of grep/find.
- `smartgrep query "<dsl>"` for composable one-shot structural questions
- `smartgrep context <file>` for a structural overview of a file
- `smartgrep query "symbol <Name> | with deps, refs"` to understand a symbol's role
- Use batch queries (semicolon-separated) to answer multi-part questions in one callEvery smartgrep call is logged. Run this to see Claude's recent queries:
smartgrep logExample output:
ts command args results ms
2026-03-14 10:42:11 query classes where attributes contains '@RestController' 4 12
2026-03-14 10:42:18 query methods where parent = UserController | with signature 7 9
2026-03-14 10:42:31 query symbol OrderService | with deps, refs 1 8
2026-03-14 10:43:02 query functions where file contains 'commands/' | with params 6 11
Each line is one tool call Claude made. Compare these to what you'd have had to decipher with grep:
# What Claude asked for (smartgrep) # What you'd have with grep
classes where attributes contains '@RestController' grep -rn "@RestController" src/ | grep "^class\|^public class"
methods where parent = UserController grep -rn "UserController" src/ | grep "^\s\+public\|void\|String"
symbol OrderService | with deps, refs grep -rn "OrderService" src/ | grep "import\|extends\|new OrderService"The smartgrep query column tells you exactly what Claude was looking for, in plain language.
The query command is the main feature. Queries compose a source, optional filters, and pipeline stages:
source [where conditions] [| stage] [| stage] ...
Side by side with grep, for the same questions:
| What you want to know | grep | smartgrep |
|---|---|---|
| All public functions | grep -rn "^pub fn|pub async fn" src/ |
functions where visibility = public |
| Spring REST controllers | grep -rn "@RestController" src/ | grep "class " |
classes where attributes contains '@RestController' |
| Methods on UserService | grep -rn "UserService" src/ | grep "def |public " |
methods where parent = UserService |
| Structs with 5+ fields | (requires reading each file) | structs | with fields | where field_count > 5 |
| What does Config depend on | grep -rn "Config" src/ | grep "use |import |new " |
symbol Config | with deps |
The grep column requires knowing the language's syntax, writing a correct regex, and filtering noise. The smartgrep column reads like a question.
symbols / structs / classes / interfaces / records / functions / methods
traits / enums / impls / consts / types / modules
symbol <name> -- single symbol lookup
deps [<name>] -- dependencies (all, or for one symbol)
refs [<name>] -- references (all, or to one symbol)
<source> in '<path>' -- restrict to files matching a path substring
where <field> <op> <value> [and|or ...]
| Field | Applies to |
|---|---|
name, file, visibility, kind, parent |
symbols |
attributes |
symbols with annotations/decorators |
field_count, param_count |
after with fields / with params |
from, to, dep_kind |
dependency rows |
| Operator | Meaning |
|---|---|
= / is |
equals (case-insensitive) |
!= |
not equals |
contains |
substring match |
starts_with / ends_with |
prefix / suffix |
> < >= <= |
numeric comparison |
Stages follow | and apply left to right:
with fields— add struct/class/enum field list and countwith params— add function/method parameter list and countwith deps— add outbound dependencieswith refs— add inbound referenceswith signature— add full type signatureshow col1, col2— select specific output columnswhere field_count > 5— filter after enrichmentsort field asc|desc— sort resultslimit N— cap output
Separate queries with ; to run multiple in one call:
smartgrep query "structs | with fields; enums | with fields"
smartgrep query "classes where file contains 'service/'; methods where parent = UserController"Results print under # Query 1, # Query 2 headers.
Long file paths in text output are automatically shortened. src/main/java/com/example/catalog/controller/ProductController.java becomes [P]controller/ProductController.java, with a [paths] legend at the top. JSON output always keeps full paths.
- Rust — full support via tree-sitter-rust
- Java — full support via tree-sitter-java
- Go — full support via tree-sitter-go
Adding a language means writing one parser. The IR layer and query engine are language-agnostic.
Parser (tree-sitter) --> IR --> Index Builder --> Index --> Commands / Query Engine
- Parser (
src/parser/) — language-specific, produces the IR - IR (
src/ir/types.rs) — language-agnostic symbol and dependency maps - Index (
src/index/types.rs) — queryable, with lookup tables for fast symbol resolution
Auto-indexing: queries trigger indexing implicitly. The index rebuilds when source files change.
See AGENTS_README.md for the agent-targeted reference.