Lattice builds a graph of your codebase so agents (LLMs) can get real context about it — what symbols exist, who calls what, and how files depend on each other. It parses Go source with tree-sitter and turns it into a queryable call graph.
The pipeline is: walk → parse → resolve → persist.
- Walk —
internal/repositorywalks the directory and collects every.gofile, skipping.git. - Parse —
internal/parserparses each file with tree-sitter, using the SCM queries embedded ininternal/queries(viago:embed). For every file it extracts:- Symbols — functions, methods, types, constants, and package-level variables, each with a stable
ID(path::kind::receiver::name::line) and source line range. - Imports — every imported module.
- Calls — call sites covering simple calls, method calls, package-qualified calls, chained calls, and functions passed as first-class values. Each call is tagged with its enclosing symbol's
ParentSymbolID, so caller → callee edges are map-able. - Type references — parameter/receiver types, struct field types, return types, and composite-literal types, which drive file-level
type_useedges.
- Symbols — functions, methods, types, constants, and package-level variables, each with a stable
- Resolve —
internal/resolverbuilds a symbol table (lookup byIDand byName) and maps every call target to its definition, producing the resolved call edges. - Persist —
internal/storagewrites everything into a SQLite database. Files are hashed (sha256), and a file is skipped entirely if its stored hash already matches (i.e. it's unchanged since the last run). Each parsed file is stored in a single transaction: the file row, its symbols (withdefinesedges from a synthetic per-file symbol), plusimports,type_use, andcallsedges — calls resolve to the symbol they hit, or fall back to an external target. - Graph —
internal/graphis where the final graph representation will live (currently a stub).
cmd/lattice/ CLI entry point — reads a repo path, builds the index, resolves and persists it
internal/
config/ app configuration (stub)
graph/ graph representation (stub)
logger/ structured slog logger (JSON or text, level-configurable)
models/ shared types — symbols, imports, calls, type refs, parsed files
parser/ tree-sitter parsing — per-file reader, repository indexer
queries/ embedded tree-sitter SCM query source (go.scm)
repository/ filesystem walker
resolver/ symbol table + call resolution
storage/ SQLite store — migrations, persistence, change detection
go build -o lattice ./cmd/lattice
./lattice
# type a path to a Go repository and press EnterThe CLI reads the root path from stdin, indexes the repo, and stores everything in ./Lattice.db. Files whose content hasn't changed since the last run are skipped, so repeat runs are fast.
Done so far:
- Directory walker (Go files only,
.gitskipped) - Tree-sitter parsing for Go: symbols, imports, calls, type refs
- Call sites tagged with their enclosing symbol (
ParentSymbolID) for caller → callee mapping - Symbol table + call-to-definition resolution
- Structured logging via
slog - SQLite persistence with WAL, embedded migrations, and hash-based change detection
- Graph representation (
internal/graph) - Additional languages beyond Go
- Proper CLI flags / subcommands
why did I write this in go? cuz I wanted to