Semantic code tools

May 29, 2026 · View on GitHub

When built with --features "semantic,semantic-ts,semantic-python" (and the other semantic-<lang> features), dirge gains AST-powered code analysis via tree-sitter:

ToolDescription
list_symbolsList functions, classes, methods, interfaces, and type aliases in a file or project. Filter by kind.
get_symbol_bodyFull source of a named symbol via precise byte-range extraction.
find_definitionLocate where a symbol is defined across the project.
find_callersFind all call sites of a function/method via the tree-sitter symbol index (word-boundary semantics, excludes the definition site).
find_calleesExtract all function/method calls made within a symbol's body (tree-sitter query).

Supports TypeScript/TSX, Python, Clojure (.clj/.cljs/.cljc/.edn/.bb), Go, Ruby (.rb/.rake/.gemspec), Rust, Java, C (.c/.h), and C++ (.cpp/.cc/.cxx/.hpp/.hh/.hxx). Index is built lazily on first use and cached by file mtime.

Export detection per language

LanguageExports detected fromMaps to dirge SymbolKinds
TypeScript/TSXexport keyword + index re-exportsfunction/class/interface/method/type alias
Pythonleading underscore convention; __dunder__ treated as publicfunction/class/method
Clojuredefn- is private; everything else exportedfunction/variable/class (defrecord/deftype) /interface (defprotocol) /method (defmethod, defprotocol body)
Gouppercase-first-letter conventionfunction/method (receiver type as parent_class) /class (struct) /interface (with methods) /type alias
Rubynot detected (visibility is keyword-scoped)class/interface (module) /method (instance + def self.) /function (top-level)
Rustpub / pub(crate) / pub(super) visibility modifierfunction/class (struct/enum) /interface (trait + methods) /method (impl block, attached to receiving type) /type alias /variable (const/static)
Javapublic modifier; package-private + private / protected stay non-exportedclass/interface/method (incl. constructors) /variable (fields) — nested classes recursed
Cstatic storage class = non-exported; extern by defaultfunction/class (struct/enum) /type alias (typedef; suppressed when wrapping a named struct to avoid duplicates)
C++public: / private: / protected: access labels tracked through class bodiesclass (class/struct) /method (incl. through templates + namespaces) /function (top-level) — namespaces recursed

C and C++ both claim .h. When extracting a .h file, dirge sniffs the first 32 KiB for C++-only markers (class , namespace , template<, ::) and routes the file to the C++ adapter if any match — so a Qt / libstdc++ header with classes is parsed correctly without the user having to rename it. Pure-C headers fall through to the C adapter as before.

Adding a language

Adding a new language requires writing a Rust LanguageAdapter impl (see src/semantic/adapters/clojure.rs for a 60-line reference covering the full lifecycle) and gating it behind a new semantic-<lang> cargo feature. Tree-sitter Rust bindings don't load grammars dynamically today, so the per-language adapters need to ship in the binary — but users who want their own language can add an adapter in a fork without touching anything outside src/semantic/. For runtime-pluggable language intelligence, register an LSP server in config.json instead (see lsp.md) — that's the supported path for languages dirge doesn't bake in.