Skip to main content
Lettuce runs fully local-first. You index a repo into a single SQLite file on disk, then point your coding agent at a stdio MCP server that reads that file. Nothing is uploaded — source, graph, and embeddings all stay on your machine, and there’s no API key or network call at query time.
Prefer a hosted endpoint with no local setup? See the Quickstart for lettuce cloud instead. The tools your agent calls (understand, read_snippet, …) are the same either way.

1. Install

Lettuce is a Python package managed with uv.
git clone https://github.com/<you>/lettuce.git
cd lettuce
uv sync
This exposes two commands you’ll use: codewaze (the CLI) and codewaze-mcp (the stdio MCP server).

2. Index a codebase

Point index at a repo. Lettuce walks the tree, parses each source file with tree-sitter into a graph of symbols (files, classes, functions, methods, variables) and relationships (contains, imports, calls, inherits, references), and builds semantic embeddings so understand and search can match by meaning.
uv run codewaze index ./path/to/repo
The graph is written to <repo>/.lettuce/graph.db by default. Re-running index refreshes it.
{
  "db": "/path/to/repo/.lettuce/graph.db",
  "repos": [
    { "repo": "/path/to/repo", "branch": "main",
      "files_indexed": 141, "nodes": 203, "edges": 75 }
  ],
  "embeddings": { "nodes": 203, "vectors": 267 }
}
The first index downloads a small embedding model (BAAI/bge-small-en-v1.5, ~130 MB, runs on CPU via ONNX) and loads it once — budget ~30–60s of one-time startup. Subsequent indexes are fast.

Options

--db PATH
Where to write the graph DB. Defaults to <first-repo>/.lettuce/graph.db. Pass an explicit path to put it elsewhere or to share one map across repos.
--embed / --no-embed
Build semantic embeddings (on by default). --no-embed is faster but leaves understand/search lexical-only — they’ll work, but match on names and text rather than meaning.
--branch TEXT
Scope the graph to a branch. Defaults to the repo’s current git branch. Re-indexing a different branch never clobbers a previously indexed one, so you can keep several branches mapped in the same DB.

Multiple repos in one map

Pass several paths plus a shared --db to build a single cross-repo graph — calls and imports resolve across repo boundaries, so an agent can follow a reference from a service into the library it depends on:
uv run codewaze index ./api ./shared-lib ./worker --db ./workspace.db

3. Query from the CLI (optional)

You can drive the graph directly to sanity-check an index:
uv run codewaze query search "rate limit" --repo ./path/to/repo
uv run codewaze query symbol UserService --repo ./path/to/repo
uv run codewaze query entry-points --repo ./path/to/repo
Each --repo resolves to that repo’s .lettuce/graph.db; pass --db to point at a shared map instead.

4. Connect your agent

The local server speaks MCP over stdio — your agent launches it as a subprocess, no port or auth.

Claude Code

claude mcp add lettuce -- uv run --directory /abs/path/to/lettuce codewaze-mcp

Cursor / Windsurf / Cline

Add a stdio server to the agent’s MCP config (e.g. .cursor/mcp.json):
{
  "mcpServers": {
    "lettuce": {
      "command": "uv",
      "args": ["run", "--directory", "/abs/path/to/lettuce", "codewaze-mcp"]
    }
  }
}
By default the local server exposes a minimal two-tool surfaceunderstand and read_snippet — which is all most agents need. Set CODEWAZE_MCP_FULL=1 in the server’s environment to expose the full navigation surface (search, find_symbol, callers, callees, find_path, transitive, subgraph, …); see MCP tools.
Each tool takes an optional repo (a path) or db argument so the server knows which graph to read. You can also set LETTUCE_DB in the server’s environment to pin it to a single graph and drop the argument entirely.

5. Tell the agent to use it

Agents won’t reach for a new tool unprompted. Drop this into the repo’s CLAUDE.md (or your agent’s equivalent):
For navigation, prefer the lettuce MCP tools over grep/Read:
call `understand` first, then `read_snippet` only for the lines you need.
Pass the `repo` path so lettuce knows which graph to read.
Now ask “Who calls process_payment, and what would break if I rename it?” — the agent should answer from a single understand call instead of grepping and reading whole files.

Languages

Python, TypeScript, JavaScript, Go, Rust, Java, Ruby, C, C++, .NET (C#), PHP, Kotlin, Scala, Swift, Bash, and Lua. Python and TS/JS use hand-written tree-sitter walkers; the rest go through a shared generic extractor.

Where things live

PathWhat
<repo>/.lettuce/graph.dbThe graph + embeddings (one SQLite file).
~/.lettuce/credentials.jsonCloud login, only if you use codewaze login.
The graph DB is portable — copy it to another machine and the MCP server reads it as-is. No daemon, no server, no external service.