| name | codanna-explore |
| description | Explore an indexed codebase on a specific topic using codanna's semantic search, symbol lookup, and call/dependency graphs. Use when the user asks to investigate, map, or understand a feature, subsystem, concept, or symbol. |
Codanna Explore
Topic-driven codebase exploration via codanna's local code intelligence
(semantic search + tree-sitter symbol graph + RAG over docs).
Docs: https://docs.codanna.sh/ · Repo: https://github.com/bartolli/codanna
Workflow diagram & rationale: README.md
When to use
- User gives a topic: "explore the auth flow", "map the parser", "how does X work".
- Need call graph, callers, or impact analysis for a function / type.
- Need semantic ("find code that does Y") search, not just grep.
Don't use for: regex / text-pattern searches (use rg, fall back to
grep), nix lookups (search-nix-manix), or trivial single-file questions.
Tool surface (prefer MCP, fall back to CLI)
| Purpose | MCP tool | CLI |
|---|
| Status check | get_index_info | codanna mcp get_index_info --json | toon |
| Anchor in code | semantic_search_with_context | codanna mcp semantic_search_with_context query:"…" |
| Semantic over docstrings | semantic_search_docs | codanna mcp semantic_search_docs query:"…" limit:5 |
| Fuzzy symbol search | search_symbols | codanna retrieve search "…" --kind function --limit 10 |
| Exact symbol lookup | find_symbol | codanna retrieve symbol <name> |
| Symbol details | — | codanna retrieve describe <name> |
| Outgoing calls | get_calls | codanna retrieve calls <name|symbol_id:N> |
| Incoming callers | find_callers | codanna retrieve callers <name|symbol_id:N> |
| Trait implementors | — | codanna retrieve implementations <Trait> |
| Full dependency graph | analyze_impact | codanna mcp analyze_impact <name> |
| Docs RAG search | search_documents | codanna documents search "…" --collection <name> |
| List doc collections | — | codanna documents list |
Output flags: --json, --fields=<top-level-keys>, -k/--kind,
-l/--limit, -m/--module. Add --watch to codanna mcp … to reindex stale
files before the call.
--fields= only matches top-level JSON keys, not nested paths. For
retrieve search-style results the top level is symbol, file_path,
relationships — use --fields=symbol,file_path (not
--fields=name,symbol_id,file_path, which silently drops everything but
file_path). The symbol's ID lives at symbol.id, but the CLI/MCP input
parameter is symbol_id:N (name mismatch is intentional in codanna).
Default text output is often better than --json: codanna's default
formatter is compact, shows similarity scores, inlines relationships, and most
tools end with a 💡 hint pointing to the next useful call (e.g. "Explore
'get_calls' to see what it calls"). Read those hints — they save round-trips.
search_documents is the one exception that doesn't emit a 💡 line. Reach for
--json | toon only when you need to extract symbol.ids programmatically or
paginate large result sets.
Always suppress stderr when piping JSON through toon:
codanna ... --json 2>/dev/null | toon. Tantivy emits lock-busy warnings on
stderr that break toon's JSON parser.
Workflow
-
Status check — get_index_info and codanna documents list. Branch
into bootstrap if symbol_count: 0 or no docs collections exist.
Note: semantic_search.enabled: false in get_index_info is misleading
— the embedding model loads on demand. Try semantic_search_* anyway; only
fall back to fuzzy search_symbols if it actually errors.
-
Bootstrap if missing (skip questions the user has already answered):
-
Harvest concepts from docs (only when a docs collection exists) —
search_documents query:"<topic>". Read the previews and freeform-extract
concepts and likely symbol names related to the query — no fixed schema,
whatever looks useful. Feed into step 4. Note: enriching the anchor query
often the result set toward the harvested terms rather than
simply boosting recall; compare with the bare query if you suspect drift.
Companion tools (prefer if installed)
Run command -v <tool> once per session, then route accordingly.
| Tool | Use for | Example |
|---|
toon | Codanna --json output you actually need to parse (extracting symbol.id, large result sets). Default text is usually better — see note above. | codanna retrieve search "parse" --json --fields=symbol,file_path 2>/dev/null | toon |
zat | Signature-first file reading (vs full cat / Read). Supports C, C++, C#, Go, Haskell, Java, JS, Kotlin, MD, Python, Ruby, Rust, Swift, TS/TSX. Exit 1 on unsupported. | zat src/parser.rs then Read(offset, limit) on interesting line numbers |
rg (ripgrep) | Default for any regex / text-pattern search — sanity pass for what codanna missed (macros, dynamic dispatch, FFI, generated code) | rg -nP --type rust 'macro_rules!\s*\w+' src/ |
grep | Regex fallback when rg not on PATH | grep -RnE 'pattern' src/ |
Routing rule: codanna first (structural understanding) → rg (or grep if
rg is unavailable) for any regex or text-pattern search.
Output shape
All MCP / retrieve JSON wraps results in
{type, status, code, exit_code, message, hint, data, meta}. The shape of
data varies by command — don't assume a single path:
| Command | data shape | How to extract |
|---|
get_index_info | object {symbol_count, file_count, ..., semantic_search:{}} | data.symbol_count |
find_symbol <name> (found) | object {symbol:{}, file_path, relationships:{}} | data.symbol.id, data.file_path |
find_symbol <name> (missing) | null (status: not_found) | read hint field |
retrieve search … / search_symbols | array [{symbol:{}, file_path, relationships:{}}, …] | data[N].symbol.id |
mcp get_calls / find_callers | array [{id, name, kind, file_path, …}, …] (flat — no symbol wrapper) | data[N].id |
search_documents | array [{chunk_id, collection, content_preview, source_path, …}, …] | data[N].source_path |
status: "not_found" → data: null; read the hint field — it tells
you the next useful move (e.g. "try semantic_search_docs"). Don't retry the
identical query.
- Most default-text outputs end with a
💡 line carrying the same nudge.
search_documents is the exception — it has no trailing 💡.
Common pitfalls
| Pitfall | Fix |
|---|
| Semantic search returns nothing | Threshold is 0.6; rephrase, drop jargon, or fall back to search_symbols / rg. |
get_index_info says semantic_search.enabled: false | Misleading — the model loads on demand. Just call the semantic tool; only fall back if it errors. |
search_documents previews contain [1;36m...[0m garbage | ANSI escape codes (search-term highlighting) appear in both default text and --json content_preview fields. For machine reads, strip with sed 's/\x1b\[[0-9;]*m//g'. For human reads, accept the ANSI (terminals render it). Tantivy lock-busy warnings on stderr separately need 2>/dev/null. |
--fields=name,symbol_id,file_path returns only file_path | --fields matches top-level JSON keys only, not nested paths. Use --fields=symbol,file_path and dig into data[].symbol.id afterward. |
analyze_impact output worry | Often small (~20 lines). Just call it — check data.items length if it actually returns a lot, then narrow with find_callers. |
| Wrong symbol picked by name | Use symbol_id:N from a --json --fields=name,symbol_id,file_path search. |
| Macro / dynamic call missed | Codanna is static — verify with rg on suspicious gaps. |
Re-running same query after not_found | Read the hint field (or 💡 line) instead. |
| Stale index after edits | codanna index <dir> or codanna mcp … --watch. |
openspec/ folder ignored | Add it as an openspec documents collection (per global CLAUDE.md). |