{"id":47664156,"url":"https://github.com/1broseidon/cymbal","last_synced_at":"2026-04-20T05:07:39.796Z","repository":{"id":346464520,"uuid":"1190098869","full_name":"1broseidon/cymbal","owner":"1broseidon","description":"Language-agnostic code navigation CLI powered by tree-sitter","archived":false,"fork":false,"pushed_at":"2026-04-15T04:34:51.000Z","size":1040,"stargazers_count":157,"open_issues_count":3,"forks_count":16,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T06:28:41.987Z","etag":null,"topics":["ai-agents","cli","code-analysis","code-navigation","developer-tools","golang","sqlite","static-analysis","tree-sitter"],"latest_commit_sha":null,"homepage":"http://chain.sh/cymbal/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1broseidon.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-24T00:59:01.000Z","updated_at":"2026-04-15T04:34:40.000Z","dependencies_parsed_at":"2026-04-08T05:00:43.084Z","dependency_job_id":null,"html_url":"https://github.com/1broseidon/cymbal","commit_stats":null,"previous_names":["1broseidon/cymbal"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/1broseidon/cymbal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1broseidon%2Fcymbal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1broseidon%2Fcymbal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1broseidon%2Fcymbal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1broseidon%2Fcymbal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1broseidon","download_url":"https://codeload.github.com/1broseidon/cymbal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1broseidon%2Fcymbal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32033735,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","cli","code-analysis","code-navigation","developer-tools","golang","sqlite","static-analysis","tree-sitter"],"created_at":"2026-04-02T11:49:14.574Z","updated_at":"2026-04-20T05:07:39.791Z","avatar_url":"https://github.com/1broseidon.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cymbal\n\n[![GitHub Stars](https://img.shields.io/github/stars/1broseidon/cymbal?style=social)](https://github.com/1broseidon/cymbal/stargazers)\n[![Go Reference](https://pkg.go.dev/badge/github.com/1broseidon/cymbal.svg)](https://pkg.go.dev/github.com/1broseidon/cymbal)\n[![Go Report Card](https://goreportcard.com/badge/github.com/1broseidon/cymbal)](https://goreportcard.com/report/github.com/1broseidon/cymbal)\n[![Latest Release](https://img.shields.io/github/v/release/1broseidon/cymbal)](https://github.com/1broseidon/cymbal/releases/latest)\n\nFast, language-agnostic code indexer and symbol navigator built on [tree-sitter](https://tree-sitter.github.io/).\n\ncymbal 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.\n\n## Install\n\nHomebrew (macOS / Linux):\n\n```sh\nbrew install 1broseidon/tap/cymbal\n```\n\nWindows (PowerShell):\n\n```powershell\nirm https://raw.githubusercontent.com/1broseidon/cymbal/main/install.ps1 | iex\n```\n\nTo uninstall (keeps index data by default):\n\n```powershell\n# Remove binary and PATH entry, keep SQLite indexes\nirm https://raw.githubusercontent.com/1broseidon/cymbal/main/uninstall.ps1 | iex\n\n# Also remove all SQLite indexes\n\u0026 ([scriptblock]::Create((irm https://raw.githubusercontent.com/1broseidon/cymbal/main/uninstall.ps1))) -Purge\n```\n\n\u003e **Note:** `-Purge` removes all per-repo SQLite indexes stored under `%LOCALAPPDATA%\\cymbal\\repos\\`. Omit it to keep your indexes intact in case you reinstall.\n\nGo (requires CGO for tree-sitter + SQLite):\n\n```sh\nCGO_CFLAGS=\"-DSQLITE_ENABLE_FTS5\" go install github.com/1broseidon/cymbal@latest\n```\n\nOr grab a binary from [releases](https://github.com/1broseidon/cymbal/releases).\n\n### Docker\n\nNo local Go toolchain or CGO setup needed — run cymbal from a pre-built container (linux/amd64 and arm64):\n\n```sh\ndocker pull ghcr.io/1broseidon/cymbal:latest\n```\n\nMount any repo and the SQLite index lands at `/workspace/.cymbal/index.db` inside the container by default (via `CYMBAL_DB`):\n\n```sh\n# Index a repo\ndocker run --rm -v /path/to/your/repo:/workspace ghcr.io/1broseidon/cymbal index .\n\n# Query it (index persists at /path/to/your/repo/.cymbal/index.db)\ndocker run --rm -v /path/to/your/repo:/workspace ghcr.io/1broseidon/cymbal investigate handleAuth\n\n# Override the DB location if needed\ndocker run --rm -v /path/to/your/repo:/workspace -e CYMBAL_DB=/some/other/path.db ghcr.io/1broseidon/cymbal index .\n```\n\nPin a specific version if needed:\n\n```sh\ndocker pull ghcr.io/1broseidon/cymbal:v0.8.4\n```\n\nOr build the image yourself:\n\n```sh\ndocker build -t cymbal .\n# or with docker compose (mounts the current directory by default):\ndocker compose run --rm cymbal index .\n```\n\nAdd `.cymbal/` to your `.gitignore` to keep the index out of version control.\n\n## Quick start\n\nDefine a shell alias once so every command looks like the native binary:\n\n```sh\nalias cymbal='docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal'\n```\n\nThen:\n\n```sh\n# Index the current project\ncymbal index .\n\n# Investigate any symbol — one call, right answer\ncymbal investigate handleAuth    # function → source + callers + impact\ncymbal investigate UserModel     # type → definition + members + references\ncymbal trace handleAuth          # downward call chain — what does it call?\n\n# Or use specific commands when you need control\ncymbal search handleAuth         # find a symbol\ncymbal search \"TODO\" --text      # full-text grep\ncymbal show handleAuth           # read source\ncymbal outline internal/auth/handler.go  # file structure\ncymbal refs handleAuth           # who calls this?\ncymbal importers internal/auth   # who imports this package?\ncymbal impact handleAuth         # what breaks if I change this?\ncymbal diff handleAuth main      # git diff scoped to a function\ncymbal context handleAuth        # bundled: source + types + callers + imports\ncymbal ls                        # file tree\n```\n\nThe index auto-builds on first use — no manual `cymbal index .` required. Subsequent queries auto-refresh incrementally (~2ms when nothing changed).\n\n## Commands\n\n| Command | What it does |\n|---------|-------------|\n| `investigate` | **Start here.** Kind-adaptive exploration — one call, right shape |\n| `structure` | Structural overview — entry points, hotspots, central packages |\n| `trace` | Downward call graph — what does this symbol call? |\n| `index` | Parse and index a directory |\n| `ls` | File tree, repo list, or `--stats` overview |\n| `search` | Symbol search (or `--text` for grep). Supports `--path`, `--exclude` |\n| `show` | Display a symbol's source code. `--all` for every match |\n| `outline` | List all symbols in a file |\n| `refs` | Find references / call sites. `--file` to scope by path |\n| `importers` | Reverse import lookup — who imports this? |\n| `impls` | Types that implement / conform to / extend this symbol. `--of \u003ctype\u003e` for inverse |\n| `impact` | Transitive callers — what's affected by a change? |\n| `diff` | Git diff scoped to a symbol's line range |\n| `context` | Bundled view: source + types + callers + imports |\n| `hook` | Agent-integration hooks — `nudge`, `remind`, `install \u003cagent\u003e` |\n\nCommands that accept symbols support **batch**: `cymbal investigate Foo Bar Baz` runs all three in one invocation.\n\nAll commands support `--json` for structured output.\n\n## Agent integration\n\ncymbal 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.\n\nAdd this to your agent's system prompt (e.g., `CLAUDE.md`, `AGENTS.md`, or MCP tool descriptions).\n\n**Native install:**\n\n```markdown\n## Code Exploration Policy\nUse `cymbal` CLI for code navigation — prefer it over Read, Grep, Glob, or Bash for code exploration.\n- **New to a repo?**: `cymbal structure` — entry points, hotspots, central packages. Start here.\n- **To understand a symbol**: `cymbal investigate \u003csymbol\u003e` — returns source, callers, impact, or members based on what the symbol is.\n- **To understand multiple symbols**: `cymbal investigate Foo Bar Baz` — batch mode, one invocation.\n- **To trace an execution path**: `cymbal trace \u003csymbol\u003e` — follows the call graph downward (what does X call, what do those call).\n- **To assess change risk**: `cymbal impact \u003csymbol\u003e` — follows the call graph upward (what breaks if X changes).\n- Before reading a file: `cymbal outline \u003cfile\u003e` or `cymbal show \u003cfile:L1-L2\u003e`\n- Before searching: `cymbal search \u003cquery\u003e` (symbols) or `cymbal search \u003cquery\u003e --text` (grep, delegates to rg when available)\n- To filter results: `cymbal search --path 'src/*' --exclude '*_test.go' \u003cquery\u003e`\n- To see all definitions: `cymbal show --all \u003csymbol\u003e` or `cymbal refs --file context.go \u003csymbol\u003e`\n- Before exploring structure: `cymbal ls` (tree) or `cymbal ls --stats` (overview)\n- To disambiguate: `cymbal show path/to/file.go:SymbolName` or `cymbal investigate file.go:Symbol`\n- The index auto-builds on first use — no manual indexing step needed. Queries auto-refresh incrementally.\n- All commands support `--json` for structured output.\n```\n\n**Docker (no local install required):**\n\n```markdown\n## Code Exploration Policy\nUse `cymbal` via Docker for code navigation — prefer it over Read, Grep, Glob, or Bash for code exploration.\nRun all cymbal commands as: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal \u003ccommand\u003e`\n- **New to a repo?**: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal structure` — entry points, hotspots, central packages. Start here.\n- **To understand a symbol**: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal investigate \u003csymbol\u003e` — returns source, callers, impact, or members based on what the symbol is.\n- **To understand multiple symbols**: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal investigate Foo Bar Baz` — batch mode, one invocation.\n- **To trace an execution path**: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal trace \u003csymbol\u003e` — follows the call graph downward (what does X call, what do those call).\n- **To assess change risk**: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal impact \u003csymbol\u003e` — follows the call graph upward (what breaks if X changes).\n- Before reading a file: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal outline \u003cfile\u003e` or `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal show \u003cfile:L1-L2\u003e`\n- Before searching: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal search \u003cquery\u003e` (symbols) or add `--text` for grep\n- 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`\n- To disambiguate: `docker run --rm -v \"$(pwd)\":/workspace ghcr.io/1broseidon/cymbal investigate path/to/file.go:Symbol`\n- The index auto-builds on first use — no manual indexing step needed. Queries auto-refresh incrementally.\n- The SQLite index is stored at `.cymbal/index.db` inside the mounted repo (via `CYMBAL_DB`).\n- All commands support `--json` for structured output.\n```\n\n### Agent hooks\n\nPrompting works, but agents drift back to `grep`/`find` as context grows (see [issue #23](https://github.com/1broseidon/cymbal/issues/23)). Cymbal ships two small, agent-agnostic hook commands:\n\n| Command | What it does |\n|---|---|\n| `cymbal hook nudge` | Inspect a would-be shell command and, if it looks like a code search, emit a short system-message suggesting the cymbal equivalent. Never blocks. |\n| `cymbal hook remind` | Print a tone-calibrated reminder block agents can inject at session start or on demand. |\n\n**Claude Code — one-liner install:**\n\n```bash\ncymbal hook install claude-code                   # ~/.claude/settings.json\ncymbal hook install claude-code --scope project   # .claude/settings.json\ncymbal hook uninstall claude-code                 # clean removal\n```\n\nThe installer is idempotent, preserves unrelated settings, and marks its own entries so `uninstall` is surgical.\n\n**Other agents** (Cursor, Windsurf, aider, Cline, Continue, Zed, Codex/OpenAI Agents SDK, or anything that can shell out on a pre-tool event) — see [`docs/AGENT_HOOKS.md`](docs/AGENT_HOOKS.md) for copy-paste snippets. The same two subcommands work everywhere; `nudge` offers `--format=claude-code|json|text` and `remind` offers the same three formats, so every integration is one or two lines.\n\n### Why this works\n\nAn 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:\n\n| Command | Question it answers | Direction |\n|---|---|---|\n| `investigate X` | \"Tell me about X\" | Adaptive (source + callers + impact or members) |\n| `trace X` | \"What does X depend on?\" | Downward (callees, depth 3) |\n| `impact X` | \"What depends on X?\" | Upward (callers, depth 2) |\n\n`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.\n\n## Supported languages\n\ncymbal currently parses and indexes these languages with tree-sitter:\n\n- Go\n- Python (`.py`, `.pyw`)\n- JavaScript (`.js`, `.jsx`, `.mjs`, `.cjs`)\n- TypeScript (`.ts`, `.tsx`, `.mts`, `.cts`)\n- Rust\n- C / C++ (`.c`, `.h`, `.cpp`, `.cc`, `.hpp`, `.cxx`, `.hxx`, `.hh`)\n- C#\n- Java\n- Apex\n- Ruby (`.rb`, `.rake`, `.gemspec`)\n- Swift\n- Kotlin (`.kt`, `.kts`)\n- Scala (`.scala`, `.sc`)\n- PHP\n- Lua\n- Bash / shell\n- YAML\n- Elixir\n- HCL / Terraform (`.tf`, `.hcl`, `.tfvars`)\n- Protobuf\n- Dart\n\ncymbal also recognizes additional file types for classification and CLI path heuristics, even when they are not parseable/indexable: `Dockerfile`, `Makefile`, `Jenkinsfile`, `CMakeLists.txt`, JSON, TOML, Markdown, SQL, Vue, Svelte, Zig, Erlang, Haskell, OCaml, R, and Perl.\n\nAdding a language requires a tree-sitter grammar and a symbol extraction query.\n\n## How it works\n\n1. **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/\u003chash\u003e/index.db` on Linux, `~/Library/Caches/cymbal/repos/` on macOS, `%LOCALAPPDATA%\\cymbal\\repos\\` on Windows). Override with `--db \u003cpath\u003e` or the `CYMBAL_DB` environment variable. The index auto-builds on first query — no manual `cymbal index .` required.\n\n2. **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.\n\n3. **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.\n\n## Benchmarks\n\nMeasured against ripgrep on three real-world repos (gin, kubectl, fastapi) across Go and Python. Full harness in `bench/`.\n\n```sh\ngo run ./bench setup   # clone pinned corpus repos\ngo run ./bench run     # run all benchmarks → bench/RESULTS.md\n```\n\n**Speed** — cymbal queries complete in 9-27ms. Reindex with nothing changed: 8-20ms.\n\n**Accuracy** — 100% ground-truth precision/recall across 43 checks. 100% canonical @1 ranking across 9 hard disambiguation cases. 7/7 grep-footgun avoidance tests pass.\n\n**Token efficiency** — for targeted lookups, cymbal uses 17-100% fewer tokens than ripgrep (`FastAPI`: 11k grep hits → 8 cymbal results; `Context`: 915 → 5). Refs queries show the biggest wins because cymbal returns semantic call sites, not every line mentioning the string.\n\n**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.\n\n**Agent workflow** — `cymbal investigate` replaces 3 separate ripgrep calls (search + show + refs) with 1 call. Typical savings: 41-100% fewer tokens for focused symbols.\n\n## Use as a library\n\n```sh\nCGO_CFLAGS=\"-DSQLITE_ENABLE_FTS5\" go get github.com/1broseidon/cymbal@latest\n```\n\nFive packages are exported:\n\n| Package | What it does |\n|---------|-------------|\n| `index` | Indexing engine, SQLite store, and all query APIs |\n| `lang` | Unified language registry for names, extensions, special filenames, and parser availability |\n| `parser` | Tree-sitter parsing for 22 languages |\n| `symbols` | Core data types (Symbol, Import, Ref) |\n| `walker` | Concurrent file discovery with language detection |\n\n```go\nimport (\n    \"fmt\"\n    \"github.com/1broseidon/cymbal/index\"\n)\n\n// Index a repo\nstats, _ := index.Index(\"/path/to/repo\", \"\", index.Options{})\nfmt.Printf(\"%d files, %d symbols\\n\", stats.FilesIndexed, stats.SymbolsFound)\n\n// Query — all functions take a dbPath and return typed results\ndbPath, _ := index.RepoDBPath(\"/path/to/repo\")\n\nresults, _ := index.SearchSymbols(dbPath, index.SearchQuery{Text: \"handleAuth\"})\ninv, _ := index.Investigate(dbPath, \"handleAuth\")\ntrace, _ := index.FindTrace(dbPath, \"handleAuth\", 3, 50)\nimpact, _ := index.FindImpact(dbPath, \"handleAuth\", 2, 100)\nrefs, _ := index.FindReferences(dbPath, \"handleAuth\", 50)\n```\n\n```go\nimport \"github.com/1broseidon/cymbal/lang\"\n\nfmt.Println(lang.Default.Supported(\"typescript\"))     // true\nfmt.Println(lang.Default.Known(\"dockerfile\"))        // true\nfmt.Println(lang.Default.LangForFile(\"Dockerfile\"))  // \"dockerfile\"\nfmt.Println(lang.Default.LangForFile(\"notes.toml\"))  // \"toml\"\n```\n\nFor the full API reference, streaming patterns, and lower-level store access, see the [library guide](./docs/guide/library.md).\n\n## Docs\n\n- [Library guide](./docs/guide/library.md)\n- [Changelog](./CHANGELOG.md)\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1broseidon%2Fcymbal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1broseidon%2Fcymbal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1broseidon%2Fcymbal/lists"}