greppy
greppy is a tool for navigating and changing code, built for coding agents. It also accepts ordinary grep invocations: they run through the real system grep and forward stdout, stderr and the exit code byte for byte. Everything else is a structured command over a precomputed symbol graph and a semantic index that runs on your machine.
The commands answer the questions an agent otherwise spends several rounds of searching and reading on: Who calls this function? What breaks if I change it? Where is the code that does X? Deterministic evidence from source and graph is the authority. The locally generated summaries sit next to the exact signature as navigation hints; you decide on the returned code.
In this demo, the same coding agent (MiniMax-M3, driven by Pi) answers a who-calls question on a real repository: on the left with grep, on the right with greppy.

The questions greppy answers
All examples in these docs run against serde version 1.0.229.
| You want to know | Command |
|---|---|
| Who calls this function? | greppy who-calls visit_content_map |
| What breaks if I change this? | greppy impact get_renames |
| Where is the code that does X? | greppy search "check whether a field should be skipped when serializing" |
| What does this function do? | greppy brief visit_content_map |
| How do I change exactly this code? | greppy read visit_content_map --handle, then an edit command |
| Did the build pass, and if not, why? | greppy bash-smart -- cargo test |
Every answer is a compact list: the location as file:line, the resolved name and, after an em dash, a generated hint. who-calls gives the line of the call.
$ greppy who-calls visit_content_map
serde/src/private/de.rs:1152 ContentDeserializer::deserialize_any
serde/src/private/de.rs:1399 ContentDeserializer::deserialize_map
serde/src/private/de.rs:1415 ContentDeserializer::deserialize_struct
impact returns the whole tree of transitive callers in one call. An indented line calls the line above it, and (above) points to an entry that is already in the tree. Here the path leads from a helper function up to the entry points of #[derive(Serialize)] and #[derive(Deserialize)].
$ greppy impact get_renames
serde_derive/src/internals/attr.rs:237 Container::from_ast — builds rename rules from a DeriveInput's attributes
serde_derive/src/internals/ast.rs:62 Container::from_ast — converts a syn::DeriveInput into a Container, handling enums and structs
serde_derive/src/de.rs:25 expand_derive_deserialize — expands a syn::DeriveInput into a TokenStream
serde_derive/src/lib.rs:122 derive_deserialize — derives deserialize code from a TokenStream
serde_derive/src/ser.rs:13 expand_derive_serialize — expands a syn::DeriveInput into a TokenStream of serialize methods
serde_derive/src/lib.rs:114 derive_serialize — derives serialization from a TokenStream
serde_derive/src/internals/attr.rs:748 Variant::from_ast — parses rename and alias attributes from a syn::Variant
serde_derive/src/internals/ast.rs:133 enum_from_ast — converts a list of syn::Variant entries into a flat list
serde_derive/src/internals/ast.rs:62 Container::from_ast (above)
search finds code by its meaning, even when you don't know its name.
$ greppy search "check whether a field should be skipped when serializing"
serde_derive/src/internals/attr.rs:1286 skip_serializing — returns the skip_serializing field value as a boolean
serde_derive/src/ser.rs:160 needs_serialize_bound — decides whether a field should be serialized
serde_derive/src/internals/check.rs:455 allow_transparent — decides whether a field is allowed to be transparent
serde_core/src/ser/mod.rs:1923 SerializeStruct::skip_field — acts as a no-op skip that returns Ok without doing anything
serde_derive/src/internals/check.rs:226 check_variant_skip_attrs — reports errors when variants lack required serialization attributes
serde_derive/src/internals/attr.rs:1290 skip_deserializing — returns the skip_deserializing flag
serde_core/src/ser/mod.rs:1993 SerializeStructVariant::skip_field — acts as a no-op skip that returns Ok without doing anything
serde_derive/src/internals/attr.rs:956 Variant::skip_serializing — returns the skip_serializing field value as a boolean
Ordinary grep keeps running through the same binary, with grep output and grep exit codes.
$ greppy -c deny_unknown_fields serde_derive/src/internals/attr.rs
7
$ cmp <(greppy -rn deny_unknown_fields serde_derive/src) <(grep -rn deny_unknown_fields serde_derive/src) && echo identical
identical
$ greppy -rn TODO serde/src; echo "exit $?"
exit 1
Get started
New to greppy? The Quickstart walks you through installation, the first query and connecting your agent.
- How greppy works: the graph, the embedded models, the index lifecycle and the freshness check.
- Agent setup: one prompt file, no MCP server.
Use greppy
- Navigate the graph:
who-calls,callees,brief,impact,path. - Search: by meaning, by name, by literal text.
- Read source: exact definitions, folded definitions, file pages and edit handles.
- Edit transactionally: hash-pinned handles, atomic patches, verification and undo.
- grep and ripgrep passthrough: what stays the same byte for byte, and how ripgrep syntax is forwarded.
- Run builds and tests:
bash-smartreturns a verdict and the diagnostics instead of the whole log. - Worktrees and shared indexes: linked Git worktrees use one shared, immutable index (0.4.1).
- Built-in agent and browser tool, both in beta.
Reference
- CLI reference: every command, every flag, every exit code and every environment variable.
- Languages and Platforms: what is parsed, fully tested and released.
- Performance and footprint and Benchmarks and paper: measured numbers and how they were measured.
- Limits: what a static graph does not see.
- Local data, privacy and uninstall and Security and verification.