README
¶
Mimir
Language-agnostic code indexer. Parse a repository once with tree-sitter, store symbols and call references in SQLite, then explore the codebase via a fast CLI — no daemon, no LSP, no runtime dependencies.
mimir index ./myrepo
mimir search ./myrepo --name "processJob"
mimir callers ./myrepo processJob
mimir dead ./myrepo --unexported
Features
- 9 CLI commands + workspace sub-commands — index, search, symbol lookup, cross-reference tracing, dead-code detection, file tree, report; plus
workspaceto manage named collections of repos and declare cross-repo symbol links - 6 languages — Go, JavaScript, TypeScript, TSX, Python, C#
- Incremental re-index — mtime+size stat-skip; only changed files are re-parsed
- Auto-refresh — query commands transparently re-index stale files; no manual
mimir indexneeded between edits --jsonon every command — pipe tojqor consume programmatically- Single binary — requires Go 1.26+ and a C compiler (CGO, via tree-sitter)
- FTS5 full-text search — fuzzy symbol search with prefix wildcards (
proc*) - Dot-notation —
Class.method,*.method,Class.*in--name/--like
Installation
go install github.com/Utahjezz/mimir/cmd/mimir@latest
Or build from source:
git clone https://github.com/Utahjezz/mimir
cd mimir
go build -o mimir ./cmd/mimir
Requirements: Go 1.26+, and a C compiler (macOS: Xcode CLT — xcode-select --install · Linux: gcc · Windows: TDM-GCC or MSYS2).
Quick Start
# 1. Index a repository (run once; re-run to update)
mimir index ./myrepo
# 2. See what's in it
mimir report ./myrepo
# 3. Find a symbol
mimir search ./myrepo --name "NewMuncher"
# 4. Show its source
mimir symbol pkg/indexer/facade.go NewMuncher
# 5. Who calls it?
mimir callers ./myrepo NewMuncher
# 6. What's never called?
mimir dead ./myrepo --unexported
Commands
| Command | Syntax | Description |
|---|---|---|
index |
mimir index <path> |
Walk and index all supported source files |
symbols |
mimir symbols <file> |
List all symbols in a file (no index needed) |
symbol |
mimir symbol <file> <name> |
Show a symbol's metadata and source body |
search |
mimir search <root> [flags] |
Search indexed symbols with filters |
report |
mimir report <root> |
Summary: files, symbols, language breakdown |
refs |
mimir refs <root> [flags] |
Query call-reference table |
tree |
mimir tree <root> [--files] |
Directory tree with file/symbol counts |
callers |
mimir callers <root> <symbol> |
All call sites that invoke a symbol |
dead |
mimir dead <root> [flags] |
Symbols with no recorded callers |
mimir search flags
--name <str> Exact symbol name (supports dot-notation: Class.method)
--like <str> Prefix match (LIKE)
--fuzzy <str> FTS5 match — use * for prefix: "proc*"
--type <str> Filter by type: function | method | class | interface |
type_alias | enum | namespace | variable
--file <str> Filter by file path substring
--json Output as JSON
--no-refresh Skip automatic re-index before querying
mimir dead flags
--type <str> Restrict to symbol type
--file <str> Filter by file path substring
--unexported Only show unexported symbols (reduces false positives)
--json Output as JSON
--no-refresh Skip automatic re-index before querying
mimir refs flags
--caller <str> Filter by caller symbol name
--callee <str> Filter by callee name
--file <str> Filter by caller file path
--json Output as JSON
--no-refresh Skip automatic re-index before querying
Global flags (all commands)
--refresh-threshold <duration> Minimum index age before a query triggers auto re-index
(default 10s; e.g. 30s, 2m, 0s for always-refresh)
Workspaces
A workspace is a named collection of repositories. Create one workspace per project or team, add multiple repos to it, and index them all with a single command.
# Create a workspace and set it as active
mimir workspace create myproject
# Next, set it as current: mimir workspace use myproject
mimir workspace use myproject
# Add repositories
mimir workspace add ~/code/backend
mimir workspace add ~/code/frontend
# Index all repos in the active workspace (2 concurrent by default)
mimir workspace index
# Show what's in the workspace
mimir workspace show
Workspace commands
| Command | Syntax | Description |
|---|---|---|
workspace create |
mimir workspace create <name> |
Create a new workspace |
workspace use |
mimir workspace use <name> |
Set the active workspace |
workspace add |
mimir workspace add <path> [workspace] |
Add a repository to a workspace |
workspace remove |
mimir workspace remove <path> [workspace] |
Remove a repository from a workspace |
workspace show |
mimir workspace show [workspace] |
List repositories in a workspace |
workspace index |
mimir workspace index [workspace] [flags] |
Index all repos in a workspace |
workspace link |
mimir workspace link <src-repo-id> <src-symbol> <dst-repo-id> <dst-symbol> [workspace] |
Declare a cross-repo symbol link |
workspace links |
mimir workspace links [--from <repo>] [--src-symbol <name>] [--dst-symbol <name>] [workspace] |
List cross-repo symbol links |
workspace unlink |
mimir workspace unlink <id> [workspace] |
Remove a cross-repo symbol link by ID |
mimir workspace index flags
--rebuild Drop and rebuild each repo's index from scratch
--concurrency N Number of repos to index in parallel (default 2)
--json Output results as JSON (one object per repo)
mimir workspace link flags
--src-file <path> Disambiguate src-symbol when multiple files match
--dst-file <path> Disambiguate dst-symbol when multiple files match
--note <text> Free-text note stored with the link
--meta <k=v> Key/value metadata (repeatable: --meta protocol=grpc --meta transport=kafka)
mimir workspace links flags
--from <repo-id> Filter links by source repo ID (defaults to cwd repo; lists all if cwd not in workspace)
--src-symbol <name> Filter links by source symbol name (exact match)
--dst-symbol <name> Filter links by destination symbol name (exact match)
--json Output as JSON
mimir workspace show --json
mimir workspace show --json | jq '.[].ID'
DB location: each workspace is stored at ~/.config/mimir/workspaces/<name>.db
The active workspace name is stored in ~/.config/mimir/config.json.
Cross-repo Symbol Links
A cross-repo link is a manually declared mapping from a symbol in one repository to a symbol in another. Both sides are validated against their repo indexes when the link is created.
Common uses: documenting gRPC/HTTP boundaries (client call → server handler), marking shared interfaces implemented across repos, or capturing any meaningful cross-codebase relationship.
# First, find your repo IDs
mimir workspace show
# Declare that OrderService.PlaceOrder in the backend calls PaymentClient.Charge in payments
mimir workspace link backend-a1b2c3d4 OrderService.PlaceOrder payments-def45678 PaymentClient.Charge
# Attach metadata and a note
mimir workspace link backend-a1b2c3d4 OrderService.PlaceOrder payments-def45678 PaymentClient.Charge \
--meta protocol=grpc --meta transport=kafka \
--note "async via Kafka topic orders.placed"
# If a symbol name is ambiguous, use --src-file or --dst-file to disambiguate
mimir workspace link backend-a1b2c3d4 Shared payments-def45678 Shared \
--src-file pkg/orders/handler.go \
--dst-file pkg/payments/client.go
# List all links in the active workspace (filters to cwd repo by default)
mimir workspace links
# List links from a specific repo (by repo ID)
mimir workspace links --from backend-a1b2c3d4
# List all links across the workspace
mimir workspace links --from ""
# Filter by source symbol name
mimir workspace links --src-symbol OrderService.PlaceOrder
# Filter by destination symbol name
mimir workspace links --dst-symbol PaymentClient.Charge
# Combine filters: links from a specific repo that target a specific symbol
mimir workspace links --from backend-a1b2c3d4 --dst-symbol PaymentClient.Charge
# JSON output for scripting
mimir workspace links --json | jq '.[].SrcSymbol'
# Remove a link by ID
mimir workspace unlink 3
Link output format:
#1 OrderService.PlaceOrder (abc123)
→ PaymentClient.Charge (def456)
note: async via Kafka topic orders.placed
protocol=grpc
transport=kafka
Auto-refresh
Query commands (search, report, refs, tree, callers, dead, symbol) automatically re-index the repository when the index is older than the refresh threshold (default 10 seconds). This means you rarely need to run mimir index manually between edits.
# Edit a file, then query immediately — auto-refresh picks up the change
vim pkg/indexer/walk.go
mimir search . --name "Run" # re-indexes if index is > 10s old, then searches
How it works: before executing a query, mimir checks a single SQLite timestamp (last_indexed_at in the meta table). If the index is younger than the threshold, it proceeds directly. If stale, it runs the same incremental walk as mimir index (only changed files are re-parsed), then queries.
Opt out when you want raw speed or are running many queries in a tight loop:
mimir search . --name "Foo" --no-refresh
mimir dead . --unexported --no-refresh
Tune the threshold globally for the entire command:
mimir --refresh-threshold=0s search . --name "Foo" # always re-index
mimir --refresh-threshold=5m search . --name "Foo" # re-index at most once per 5 min
mimir indexitself is still useful for the very first index build or when you want an explicit, unconditional walk (e.g. in CI).
Supported Languages
| Language | Extensions |
|---|---|
| Go | .go |
| JavaScript | .js .mjs .cjs |
| TypeScript | .ts .mts .cts |
| TSX | .tsx |
| Python | .py |
| C# | .cs |
Files with any other extension are silently skipped.
Symbol Types
function · method · class · interface · type_alias · enum · namespace · variable
JSON Output
Every command supports --json for scripting:
# Count dead unexported functions
mimir dead ./myrepo --unexported --type function --json | jq 'length'
# All method names in a package
mimir search ./myrepo --type method --file pkg/indexer/ --json | jq '.[].Name'
# Language breakdown
mimir report ./myrepo --json | jq '.Languages'
How It Works
- Walk — directory tree skipping dot-dirs (
.git,.env, …) andnode_modules/vendor - Stat-skip — compare mtime+size against stored
FileMeta; skip if unchanged - Parse — tree-sitter extracts symbols and call references per file
- Write — single collector goroutine writes to SQLite (no locking errors)
- Query — cobra commands open the index, run queries, and return results (read-only when the index is up to date)
- Auto-refresh — query commands check
last_indexed_atin meta; if stale they transparently re-run steps 1–4 and update SQLite before returning results
DB location: ~/.config/mimir/indexes/<repo-id>/index.db
(override with $XDG_CONFIG_HOME)
Adding a New Language
Two files and one registry entry:
pkg/indexer/languages/<lang>/
├── language.go ← Language() + Extensions()
└── queries.go ← SymbolQuery, CallQuery, RefQuery constants
Then add one entry to buildLangMap() in pkg/indexer/registry.go.
Development
# Run all tests
go test ./...
# Build binary
go build -o mimir ./cmd/mimir
# Index this repo and explore it
./mimir index .
./mimir report .
./mimir dead . --unexported
Project Structure
cmd/mimir/ ← entry point
internal/commands/ ← one file per subcommand
workspace/ ← workspace subcommands (create, use, add, show, remove, index, link, links, unlink)
pkg/indexer/ ← core: walker, parser, store, queries, facade
languages/ ← per-language tree-sitter grammars + queries
pkg/workspace/ ← workspace library: store, config, repository, index
License
MIT