Grep Can't Read Code

Giving AI agents IDE-grade code intelligence

An agent can grep for text — but text can't understand code

Agents already have grep. The Amplifier LSP bundles hand them the same language-server intelligence an IDE gives a human, so they navigate by meaning instead of string match.

Meaning is not a slogan here — it's a shipped engine. Here's the evidence.

tool-lsp ships 14 real LSP operations, backed by tests

Exactly 14 operations: 7 always-on base ops plus 7 language-gated extended ops, no overlap. Grouped as Navigation, Call hierarchy, Verification, Refactoring, Inspection, Extensions.

A substantial, shipped engine. So why isn't grep enough on its own?

14
LSP operations exposed by tool-lsp
7 + 7
Base ops + extended ops, zero overlap
17
Test files in the tool-lsp module
519
Lines in tool.py

Grep returns text matches — so an agent navigating by string is navigating blind

Grep gives text matches that may include comments, strings, and similar names. LSP gives semantic results — actual code relationships. For code navigation, semantic wins.

The fix is to match by meaning. Start with finding a function's real callers.

findReferences returns the actual calls, not text that looks like a call

To find all calls to a function, findReferences is semantic — it finds the actual calls. Grep may match comments, strings, or similar names that merely resemble the name.

Finding one function's callers is the start. Now trace the whole graph.

incomingCalls and outgoingCalls trace the complete call graph

These call-hierarchy operations map the complete call graph — something grep cannot do reliably. Text search has no way to follow who calls whom across a codebase.

Navigation by meaning is powerful. But the real payoff is verifying the agent's own edits.

After an edit, the agent reads compiler errors and fixes them itself

The diagnostics → codeAction → apply cycle is the AI equivalent of a developer watching compiler output and applying suggested fixes. Checking for errors after editing is simply not possible with grep.

This verify loop is production-tuned per language. For Rust, that means killing cold-start delay.

  1. 1diagnosticsCheck for errors introduced by the edit
  2. 2codeActionGet suggested fixes for those errors
  3. 3applyThe AI equivalent of watching compiler output

For Rust, rust-analyzer runs as a persistent background service

A 5-minute (300-second) idle timeout keeps rust-analyzer warm to eliminate cold-start indexing, then auto-shuts down after 5 minutes with no connected clients. customRequest unlocks rust-analyzer extensions like macro expansion.

Real intelligence, production-tuned. So when do you still reach for grep?

300s
Idle timeout — 5 minutes, keeps rust-analyzer warm
30–90s
First-session cold start on large projects, without persistence

Use LSP for meaning; use grep for text — and be honest about the limits

The durable rule: LSP for semantic understanding, diagnostics, and refactoring; grep for text searching. The bundle earns trust by documenting rust-analyzer's real limits, with fallbacks.

Credibility comes from honest limits, not just capabilities.

Sources

Research Methodology

SHIPPED & CONSOLIDATED

Data as of: repositories last pushed 2026-05-18 (release tag v0.2.0 on both engines).

Repositories (all microsoft/*): the general engine microsoft/amplifier-bundle-lsp provides tool-lsp (14 operations, 17 test files) and the code-navigator agent; Rust intelligence lives in microsoft/amplifier-bundle-rust-dev (code-intel agent, rust-analyzer config, rust-lsp.md), which absorbed the deprecated microsoft/amplifier-bundle-lsp-rust forwarding stub on 2026-02-15.

Research performed:

Primary contributors: Brian Krabach (8 of 9 lsp-rust commits; drove absorption into rust-dev); Salil Das (1 of 9 lsp-rust commits plus MIT LICENSE / OSS governance PRs).

Gaps: v0.2.0 is a git tag on both engines; the in-repo bundle.yaml manifests still read version 0.1.0. The lsp-rust stub bundle.yaml reads version 2.0.0.

More Amplifier Stories