Skip to content

1broseidon/cymbal

Repository files navigation

cymbal

GitHub Stars Go Reference Go Report Card Latest Release

Fast, language-agnostic code indexer and symbol navigator built on tree-sitter.

cymbal parses your codebase into a local SQLite index, then gives you instant symbol search, cross-references, impact analysis, and scoped diffs — all from the command line. Designed to be called by AI agents, editor plugins, or directly from your terminal.

Install

Homebrew (macOS / Linux):

brew install 1broseidon/tap/cymbal

Windows (PowerShell):

irm https://raw.githubusercontent.com/1broseidon/cymbal/main/install.ps1 | iex

To uninstall (keeps index data by default):

# Remove binary and PATH entry, keep SQLite indexes
irm https://raw.githubusercontent.com/1broseidon/cymbal/main/uninstall.ps1 | iex

# Also remove all SQLite indexes
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/1broseidon/cymbal/main/uninstall.ps1))) -Purge

Note: -Purge removes all per-repo SQLite indexes stored under %LOCALAPPDATA%\cymbal\repos\. Omit it to keep your indexes intact in case you reinstall.

Go (requires CGO for tree-sitter + SQLite):

CGO_CFLAGS="-DSQLITE_ENABLE_FTS5" go install github.com/1broseidon/cymbal@latest

Or grab a binary from releases.

Docker

No local Go toolchain or CGO setup needed — run cymbal from a pre-built container (linux/amd64 and arm64):

docker pull ghcr.io/1broseidon/cymbal:latest

Mount any repo and the SQLite index lands at /workspace/.cymbal/index.db inside the container by default (via CYMBAL_DB):

# Index a repo
docker run --rm -v /path/to/your/repo:/workspace ghcr.io/1broseidon/cymbal index .

# Query it (index persists at /path/to/your/repo/.cymbal/index.db)
docker run --rm -v /path/to/your/repo:/workspace ghcr.io/1broseidon/cymbal investigate handleAuth

# Override the DB location if needed
docker run --rm -v /path/to/your/repo:/workspace -e CYMBAL_DB=/some/other/path.db ghcr.io/1broseidon/cymbal index .

Pin a specific version if needed:

docker pull ghcr.io/1broseidon/cymbal:v0.8.4

Or build the image yourself:

docker build -t cymbal .
# or with docker compose (mounts the current directory by default):
docker compose run --rm cymbal index .

Add .cymbal/ to your .gitignore to keep the index out of version control.

Quick start

Define a shell alias once so every command looks like the native binary:

alias cymbal='docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal'

Then:

# Index the current project
cymbal index .

# Investigate any symbol — one call, right answer
cymbal investigate handleAuth    # function → source + callers + impact
cymbal investigate UserModel     # type → definition + members + references
cymbal trace handleAuth          # downward call chain — what does it call?

# Or use specific commands when you need control
cymbal search handleAuth         # find a symbol
cymbal search "TODO" --text      # full-text grep
cymbal show handleAuth           # read source
cymbal outline internal/auth/handler.go  # file structure
cymbal refs handleAuth           # who calls this?
cymbal importers internal/auth   # who imports this package?
cymbal impact handleAuth         # what breaks if I change this?
cymbal diff handleAuth main      # git diff scoped to a function
cymbal context handleAuth        # bundled: source + types + callers + imports
cymbal ls                        # file tree

The index auto-builds on first use — no manual cymbal index . required. Subsequent queries auto-refresh incrementally (~2ms when nothing changed).

Commands

Command What it does
investigate Start here. Kind-adaptive exploration — one call, right shape
structure Structural overview — entry points, hotspots, central packages
trace Downward call graph — what does this symbol call?
index Parse and index a directory
ls File tree, repo list, or --stats overview
search Symbol search (or --text for grep)
show Display a symbol's source code
outline List all symbols in a file
refs Find references / call sites
importers Reverse import lookup — who imports this?
impact Transitive callers — what's affected by a change?
diff Git diff scoped to a symbol's line range
context Bundled view: source + types + callers + imports

Commands that accept symbols support batch: cymbal investigate Foo Bar Baz runs all three in one invocation.

All commands support --json for structured output.

Agent integration

cymbal is designed as the code navigation layer for AI agents. One command handles most investigations — specific commands exist as escape hatches when you need more control.

Add this to your agent's system prompt (e.g., CLAUDE.md, agent.md, or MCP tool descriptions).

Native install:

## Code Exploration Policy
Use `cymbal` CLI for code navigation — prefer it over Read, Grep, Glob, or Bash for code exploration.
- **New to a repo?**: `cymbal structure` — entry points, hotspots, central packages. Start here.
- **To understand a symbol**: `cymbal investigate <symbol>` — returns source, callers, impact, or members based on what the symbol is.
- **To understand multiple symbols**: `cymbal investigate Foo Bar Baz` — batch mode, one invocation.
- **To trace an execution path**: `cymbal trace <symbol>` — follows the call graph downward (what does X call, what do those call).
- **To assess change risk**: `cymbal impact <symbol>` — follows the call graph upward (what breaks if X changes).
- Before reading a file: `cymbal outline <file>` or `cymbal show <file:L1-L2>`
- Before searching: `cymbal search <query>` (symbols) or `cymbal search <query> --text` (grep)
- Before exploring structure: `cymbal ls` (tree) or `cymbal ls --stats` (overview)
- To disambiguate: `cymbal show path/to/file.go:SymbolName` or `cymbal investigate file.go:Symbol`
- The index auto-builds on first use — no manual indexing step needed. Queries auto-refresh incrementally.
- All commands support `--json` for structured output.

Docker (no local install required):

## Code Exploration Policy
Use `cymbal` via Docker for code navigation — prefer it over Read, Grep, Glob, or Bash for code exploration.
Run all cymbal commands as: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal <command>`
- **New to a repo?**: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal structure` — entry points, hotspots, central packages. Start here.
- **To understand a symbol**: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal investigate <symbol>` — returns source, callers, impact, or members based on what the symbol is.
- **To understand multiple symbols**: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal investigate Foo Bar Baz` — batch mode, one invocation.
- **To trace an execution path**: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal trace <symbol>` — follows the call graph downward (what does X call, what do those call).
- **To assess change risk**: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal impact <symbol>` — follows the call graph upward (what breaks if X changes).
- Before reading a file: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal outline <file>` or `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal show <file:L1-L2>`
- Before searching: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal search <query>` (symbols) or add `--text` for grep
- Before exploring structure: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal ls` or `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal ls --stats`
- To disambiguate: `docker run --rm -v "$(pwd)":/workspace ghcr.io/1broseidon/cymbal investigate path/to/file.go:Symbol`
- The index auto-builds on first use — no manual indexing step needed. Queries auto-refresh incrementally.
- The SQLite index is stored at `.cymbal/index.db` inside the mounted repo (via `CYMBAL_DB`).
- All commands support `--json` for structured output.

Why this works

An agent tracing an auth flow typically makes 15-20 sequential tool calls: show function → read the code → guess the next function → show that → repeat. Each call costs a reasoning step (~500 tokens). Three commands eliminate this:

Command Question it answers Direction
investigate X "Tell me about X" Adaptive (source + callers + impact or members)
trace X "What does X depend on?" Downward (callees, depth 3)
impact X "What depends on X?" Upward (callers, depth 2)

investigate replaces search → show → refs. trace replaces 10+ sequential show calls to follow a call chain. Together they reduce a 22-call investigation to 4 calls.

Supported languages

cymbal uses tree-sitter grammars. Currently supported:

Go, Python, JavaScript, TypeScript, TSX, Rust, C, C++, C#, Java, Ruby, Swift, Kotlin, Scala, PHP, Lua, Bash, YAML, Elixir, HCL/Terraform, Protobuf, Dart

Adding a language requires a tree-sitter grammar and a symbol extraction query — see internal/parser/ for examples.

How it works

  1. Index — tree-sitter parses each file into an AST. cymbal extracts symbols (functions, types, variables, imports) and references (calls, type usage) and stores them in SQLite with FTS5 full-text search. Each repo gets its own database under the OS cache directory (~/.cache/cymbal/repos/<hash>/index.db on Linux, ~/Library/Caches/cymbal/repos/ on macOS, %LOCALAPPDATA%\cymbal\repos\ on Windows). Override with --db <path> or the CYMBAL_DB environment variable. The index auto-builds on first query — no manual cymbal index . required.

  2. Query — all commands read from the current repo's SQLite index. Symbol lookups, cross-references, and import graphs are SQL queries. No re-parsing needed. No cross-repo bleed.

  3. Always fresh — every query automatically checks for changed files and reindexes them before returning results. No manual reindexing, no watch daemons, no hooks. Edit a file, run a query, get the right answer. The mtime+size fast path adds ~10-24ms when nothing changed; only dirty files are re-parsed.

Benchmarks

Measured against ripgrep on three real-world repos (gin, kubectl, fastapi) across Go and Python. Full harness in bench/.

go run ./bench setup   # clone pinned corpus repos
go run ./bench run     # run all benchmarks → bench/RESULTS.md

Speed — cymbal queries complete in 9-27ms. Reindex with nothing changed: 8-20ms.

Accuracy — 100% automated ground-truth verification across 37 checks (search returns correct file+kind, show returns correct source, refs finds known callers, investigate includes expected signature).

Token efficiency — for targeted lookups, cymbal uses 17-100% fewer tokens than ripgrep. Refs queries show the biggest wins (3-100% savings) because cymbal returns semantic call sites, not every line mentioning the string.

JIT freshness — queries auto-detect and reparse changed files. Overhead: ~10-23ms when nothing changed, ~22-27ms after touching 1 file, ~33-43ms after touching 5 files. Deleted files are automatically pruned.

Agent workflowcymbal investigate replaces 3 separate ripgrep calls (search + show + refs) with 1 call. Typical savings: 41-100% fewer tokens for focused symbols.

Use as a library

CGO_CFLAGS="-DSQLITE_ENABLE_FTS5" go get github.com/1broseidon/cymbal@latest

Four packages are exported:

Package What it does
index Indexing engine, SQLite store, and all query APIs
parser Tree-sitter parsing for 22 languages
symbols Core data types (Symbol, Import, Ref)
walker Concurrent file discovery with language detection
import (
    "fmt"
    "github.com/1broseidon/cymbal/index"
)

// Index a repo
stats, _ := index.Index("/path/to/repo", "", index.Options{})
fmt.Printf("%d files, %d symbols\n", stats.FilesIndexed, stats.SymbolsFound)

// Query — all functions take a dbPath and return typed results
dbPath, _ := index.RepoDBPath("/path/to/repo")

results, _ := index.SearchSymbols(dbPath, index.SearchQuery{Text: "handleAuth"})
inv, _ := index.Investigate(dbPath, "handleAuth")
trace, _ := index.FindTrace(dbPath, "handleAuth", 3, 50)
impact, _ := index.FindImpact(dbPath, "handleAuth", 2, 100)
refs, _ := index.FindReferences(dbPath, "handleAuth", 50)

For the full API reference, streaming patterns, and lower-level store access, see the library guide.

Docs

License

MIT

About

Language-agnostic code navigation CLI powered by tree-sitter

Resources

License

Stars

Watchers

Forks

Contributors

Languages