ast-sgrep is built for navigation, intent queries, and machine-readable context, terminal workflows, editors, CI, and AI agents.
Agents need ranked, structured hits with enough context to choose the next tool call, not 500 raw grep lines. ast-sgrep returns symbol names, excerpts, caller/callee hints, immutable signal provenance, and within-signal score margins in one JSON payload.
asgrep index .
asgrep --json --format agent "where is auth refreshed"
asgrep semantic "credential renewal" --json{
"provider": "ast-sgrep",
"version": "2.0.0",
"query": "credential renewal",
"hit_count": 3,
"has_semantic_hits": true,
"stack_hint": "Use asgrep for hybrid search; defs:/callers:/literal: prefixes for graph and exact text; asgrep semantic for embedding-only.",
"suggested_next": [
"asgrep \"defs:auth_refresh\"",
"asgrep \"callers:auth_refresh\"",
"asgrep --json --format agent \"credential renewal\""
],
"hits": [{
"kind": "embed",
"signal": "semantic",
"margin": 0.18,
"semantic": true,
"score": 3.42,
"file": "src/main.rs",
"lines": { "start": 19, "end": 22 },
"symbol": "auth_refresh",
"excerpt": "fn auth_refresh() { ... }",
"follow_up_queries": ["defs:auth_refresh", "callers:auth_refresh"]
}]
}Each hit includes follow_up_queries so agents can drill into defs/callers without guessing prefix syntax. signal distinguishes exact, structural, and semantic evidence; margin measures separation from the next lower candidate in that same signal only. See signal provenance and margins.
asgrep index ., then keep long-running CLI work current withasgrep watch .(Pi and Code Mode refresh automatically)asgrep --json --format agent "<user intent>", ranked hits with follow-ups- For each symbol:
asgrep "defs:…"andasgrep "callers:…" - Structural shapes:
asgrep "pattern:…"(native tree-sitter) - Do not spawn ripgrep for indexed source; reserve it for logs and unindexed or unsupported files
| Task | Tool | Example |
|---|---|---|
| Natural language / synonyms | asgrep | asgrep semantic "persist access token" |
| Symbol definitions | asgrep | asgrep "defs:process_request" |
| Caller graph | asgrep | asgrep "callers:main" |
| Structural patterns | asgrep | asgrep "pattern:fn $NAME($$$)" |
| Raw text / logs | ripgrep | rg "ERROR" logs/ |
Default offline semantic path is fully functional (hashed vectors). Optional neural is in-process ONNX, see semantic-search.md.
cargo install --path crates/ast-sgrep-lsp # from a cloned checkout{
"asgrep-lsp": {
"command": "asgrep-lsp",
"transport": "stdio",
"initializationOptions": {
"asgrep": {
"neuralEmbed": false,
"semanticOnly": false,
"annThreshold": 2000,
"embedBackend": "auto"
}
}
}
}Settings may be nested under "asgrep" or at the top level of initializationOptions.
| Key | Type | Description |
|---|---|---|
noEmbed |
bool | Disable semantic indexing and search |
neuralEmbed |
bool | Prefer in-process neural embeddings (feature-gated) |
semanticOnly |
bool | Offline hashed semantic only |
annThreshold |
number | Symbol count before IVF-ANN (default 2000) |
embedBackend |
string | auto, neural, semantic |
indexPath |
string | Trusted custom index.db path. Disabled unless the operator sets ASGREP_ALLOW_EXTERNAL_INDEX=1; otherwise LSP indexes use the private user cache. |
| LSP method | Feature |
|---|---|
workspace/symbol |
Hybrid search across workspace |
textDocument/documentSymbol |
AST symbols per file |
textDocument/definition |
Go-to-definition at cursor |
textDocument/references |
References + callers |
callHierarchy/prepareCallHierarchy |
Symbol at cursor |
callHierarchy/incomingCalls |
Who calls this |
callHierarchy/outgoingCalls |
What this calls |
workspace/executeCommand |
Custom asgrep commands |
textDocument/didSave |
Incremental reindex on save |
textDocument/didChange |
Index unsaved buffer (full-sync) |
{ "command": "asgrep.search", "arguments": ["auth refresh"] }
{ "command": "asgrep.search.semantic", "arguments": ["credential renewal"] }
{ "command": "asgrep.callers", "arguments": ["process_request"] }
{ "command": "asgrep.defs", "arguments": ["main"] }
{ "command": "asgrep.reindex", "arguments": [] }{
"name": "auth_refresh",
"kind": 15,
"detail": "semantic · score 3.42 · margin 0.18",
"containerName": "src/main.rs",
"data": {
"asgrepKind": "embed",
"signal": "semantic",
"margin": 0.18,
"score": 3.42,
"excerpt": "fn auth_refresh() { ... }",
"semantic": true
}
}Kind 15 (String) marks semantic hits.
- JSON-RPC 2.0 over stdio, Content-Length framing (50 MB max)
- Non-blocking
initialize: workspace index on background thread - Index updates on save and full-buffer
didChange
ast-sgrep-plugins adapts search results for CI, platforms, and agents.
asgrep --json "auth refresh" # native
asgrep --json --format agent "credential renewal"
asgrep --json --format github "process_request"
asgrep --json --format gitlab "auth refresh"
asgrep semantic "credential renewal" --json # semantic-only, agent default| Format | Flag aliases | Top-level keys |
|---|---|---|
| Native | native (default) |
query, limit, hits |
| Agent | agent, llm, ai |
hits, suggested_next, has_semantic_hits, stack_hint |
| GitHub | github, gh |
total_count, items, provider |
| GitLab | gitlab, gl |
data, query, provider |
use ast_sgrep_core::Searcher;
use ast_sgrep_plugins::{format_response, OutputFormat};
let response = searcher.search("auth refresh")?;
let agent = format_response(&response, OutputFormat::Agent);For repos where you run code intelligence in CI:
asgrep index . --no-embed # faster, lexical + graph only
asgrep --json "security audit" > results.jsonFull semantic in CI works offline, no API key, but adds index time.
Emit GitHub- or GitLab-compatible JSON for tools that expect those schemas:
asgrep --json --format github "TODO" > gh-shaped.jsonasgrep bench . --iterations 100
# Assert avg search < 20ms in your environment| Goal | Command |
|---|---|
| Onboard to unfamiliar repo | asgrep index . then asgrep "how does routing work" |
| Trace a bug | asgrep "callers:handle_error" |
| Find all defs of a symbol | asgrep "defs:UserService" |
| Check imports | asgrep "imports:tokio" |
| Compare semantic vs lexical | asgrep "credential renewal" vs asgrep --no-embed "credential renewal" |