How to Add a Custom Language to code-review-graph Using languages.toml
You can add a custom language to code-review-graph by creating a languages.toml configuration file inside the .code-review-graph/ directory at your repository root, defining the grammar from the bundled tree-sitter language pack, and mapping AST node types to graph entities.
The code-review-graph tool provides extensibility for unsupported languages through a declarative TOML interface. By placing a languages.toml file in the hidden .code-review-graph/ folder at your project root, you instruct the parser to load grammars from the bundled tree-sitter-language-pack and extract functions, classes, imports, and calls using the generic AST walker. This integration allows custom languages to participate fully in dependency analysis, impact-radius queries, and MCP tool interactions.
Configuration File Location and Structure
The custom language loader expects the configuration file at a specific path relative to your repository root.
Create the file at:
<repo_root>/.code-review-graph/languages.toml
Each language is defined as a TOML table under the [languages.<name>] namespace. The schema requires you to specify file extensions, the tree-sitter grammar name, and the node-type mappings that tell the walker which AST nodes correspond to functions, classes, imports, and calls.
Required Configuration Schema
In code_review_graph/custom_languages.py (lines 48-78), the loader validates each language definition against a strict schema. You must provide the following keys inside each language table:
extensions– Array of file extensions (e.g.,[".erl"]) that identify source files for this language.grammar– String name of the tree-sitter grammar available intree_sitter_language_pack(e.g.,"erlang").function_node_types– Array of AST node types representing function definitions.class_node_types– Array of AST node types representing class or type definitions.import_node_types– Array of AST node types representing import statements.call_node_types– Array of AST node types representing function calls.comment(optional) – Description for logging purposes.name_field(optional) – Specifies non-standard field names within AST nodes that contain the definition's identifier.
How the Custom Language Loader Works
When you run a build, parser.py constructs a CustomLanguages instance that initiates a six-stage pipeline defined in custom_languages.py (line 116) and validated at line 77.
1. File Discovery
The loader checks for .code-review-graph/languages.toml and parses it using the TOML library.
2. Schema Validation
Each [languages.<name>] table is converted to a CustomLanguage object. The validator ensures extensions are unique, grammars are non-empty, and node-type lists are arrays.
3. Conflict Handling Built-in languages always take precedence over custom definitions. Duplicate extensions are ignored, and the system enforces a maximum of 20 custom languages per repository.
4. Grammar Loading
The loader attempts to obtain a parser via tslp.get_parser(grammar). If the specified grammar is not available in the bundled language pack, the entry is skipped with a warning.
5. Node-Type Mapping
The supplied node-type lists configure the generic walker to generate Function, Class, IMPORTS_FROM, and CALLS edges from the AST.
6. Name Extraction
If your grammar stores identifiers in non-standard fields (not the default name child), use name_field entries to direct the walker to the correct location.
Step-by-Step Implementation
Follow these steps to register a custom language using the TOML configuration method.
1. Create the Configuration Directory and File
First, create the hidden directory and TOML file:
mkdir -p .code-review-graph
touch .code-review-graph/languages.toml
2. Define the Language Configuration
Edit languages.toml to declare your language. The following example registers Erlang using the bundled erlang grammar:
# .code-review-graph/languages.toml
[languages.erlang]
extensions = [".erl"]
grammar = "erlang"
function_node_types = ["function_clause"]
class_node_types = ["record_decl"]
import_node_types = ["import_attribute"]
call_node_types = ["call"]
comment = "Erlang via the bundled tree-sitter-erlang grammar"
Replace the values with the appropriate grammar and node-type names for your target language. The complete schema reference is documented in docs/CUSTOM_LANGUAGES.md (lines 37-56).
3. Build the Graph
Run the build command to compile the graph with your custom language:
uv run code-review-graph build
During initialization, you will see log output confirming the registration:
INFO Loading custom language 'erlang' from .code-review-graph/languages.toml
4. Verify Integration
Confirm that the custom language is indexed correctly using the Python API:
from code_review_graph.parser import GraphBuilder
builder = GraphBuilder(repo_root=".")
builder.build()
count = builder.graph.nodes.filter(lambda n: n.language == "erlang").count()
print(f"Found {count} Erlang entities")
Alternatively, query the graph using MCP tools:
uv run code-review-graph query "language:erlang and type:Function"
Successful output confirms that Function, Class, and other entities are extracted and integrated into the graph.
Summary
- Create a
languages.tomlfile inside the.code-review-graph/directory at your repository root. - Define the
grammarstring and node-type mappings (function_node_types,class_node_types, etc.) for the tree-sitter parser. - Validate that the grammar exists in the bundled
tree_sitter_language_packor the loader will skip the entry with a warning. - Build using
uv run code-review-graph buildto register the custom language and generate graph nodes. - Respect limits: Maximum 20 custom languages per repository, and built-in languages override custom definitions with duplicate extensions.
Frequently Asked Questions
Where must I place the languages.toml file?
The file must reside at .code-review-graph/languages.toml relative to your repository root. The loader specifically checks this path during parser initialization in code_review_graph/parser.py. Placing it elsewhere will prevent the CustomLanguages class from discovering your configuration.
What grammars are available for custom languages?
You must use grammars available in the bundled tree-sitter-language-pack. The loader calls tslp.get_parser(grammar) to instantiate the parser. If you specify a grammar not included in the pack (such as a custom tree-sitter grammar not bundled with the tool), the entry is skipped with a warning. Check the tree-sitter-language-pack documentation for the full list of supported language identifiers.
How do I handle languages that store identifiers in non-standard AST fields?
Use the optional name_field configuration key within your language table. This tells the generic AST walker which field contains the definition's name when it differs from the standard name child node. This is implemented in the name extraction logic of the walker to ensure Function and Class nodes receive correct labels.
Why is my custom language not appearing in the graph after building?
First, check the build logs for validation warnings—common issues include unknown grammars (not in tree_sitter_language_pack), duplicate file extensions that conflict with built-in languages, or exceeding the 20-custom-language limit. Second, verify that your extensions array includes the leading dot (e.g., [".erl"] not ["erl"]). Finally, ensure the TOML syntax is valid; malformed files are silently skipped by the loader in custom_languages.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →