CLDK is a Python SDK that runs static program analysis on Java, Python, and TypeScript code. AI agents and LLM applications query code in all three languages through one API. Support for other languages is in development. Krishna et al. (2025) describe the design and how it supplies program-analysis context to LLMs that work on code.
CLDK unifies language-specific static-analysis tools, formats, and program models so agents need not reconcile them. Each analyzer emits one shared schema. The SDK maps that schema onto typed Pydantic models. The agent queries those models through the API. The API answers the questions an agent asks while it reads, tests, or changes code:
- Which callable contains this line?
- What calls it, and what does it call?
- How does a value move through the program?
- Which callables are entry points?
- Which packages does the project depend on?
- Which code reads a configuration key?
Two backends answer every query. The local backend runs the analyzer. The Neo4j backend reads a graph the analyzer wrote earlier and never runs the analyzer. Both give the same answer. Where a backend cannot answer, it refuses and says why. It never returns an empty value that reads like a real answer.
CLDK is developed at IBM Software Innovation Labs. Issues and contributions are welcome.
CLDK requires Python 3.11 or newer.
pip install cldk # Python and TypeScript analysis
pip install "cldk[java]" # adds the Java analyzer: the jar and a bundled JVM
pip install "cldk[neo4j]" # adds the read-only Neo4j backend
pip install "cldk[all]" # everything aboveJava call graphs need a JDK. The bundled JVM is enough for the symbol table. From the call-graph level up, the analyzer compiles the project with its Maven or Gradle wrapper. JAVA_HOME must then point to a JDK with javac (Java 11 or newer). Without one, the analyzer still exits 0 but emits a call graph with declared edges only. CLDK logs the degradation at WARNING.
export JAVA_HOME=/path/to/jdk # must contain bin/javacUpgrading from 1.x. The old entry point CLDK(language="java").analysis(...) still works. It emits a DeprecationWarning. Use the factory methods.
The three steps below take a Python project from disk to a question about its code. Java and TypeScript facades answer the same calls, so the same steps apply with a different factory.
-
Create a facade. Each factory returns a typed analysis facade for its language. The call-graph level covers all three steps.
from cldk import CLDK from cldk.analysis import AnalysisLevel analysis = CLDK.python( project_path="/path/to/python/project", analysis_level=AnalysisLevel.call_graph, ) # CLDK.java(...) and CLDK.typescript(...) take the same arguments.
-
Find the callable at a line, and read its source.
hit = analysis.locate("src/app/handlers.py", line=42) print(hit.callable.signature if hit.callable else "module scope") print(hit.source) # the enclosing callable's text
-
If the line was inside a callable, ask who calls it.
for caller in analysis.callers_of(hit.callable.signature): print(caller.callable, caller.file, caller.line)
Query Surface lists what else a facade answers. Backends shows how to read a graph from Neo4j instead of running the analyzer.
Every family below is on all three facades, except where the table says otherwise. The agent API reference names each accessor with its return type, its cost, and what will mislead you.
| Family | Question it answers |
|---|---|
| Addressing | Which callable owns this line? What does this name resolve to? |
| Symbol table | What does the project declare? |
| Declarations by kind | Which interfaces, enums, records, type aliases, exports, or functions exist? (Java and TypeScript, with a different set on each) |
| Call graph | What calls this, and what does it call? |
| Per-callable graphs | How do control and data flow inside one callable? |
| Slices and paths | What does this value depend on, and where does it flow? |
| Taint | Do these sources reach these sinks? You supply the names. |
| Entry points | Where does execution enter the application? |
| Bulk projections | What are the callables, their bodies, decorators, and call sites, in one pass? |
| Repository artifacts | What does the project depend on, and which code reads which configuration key? |
| View dispatches | Which code renders which template? (Java only) |
| Comments | What do the comments and docstrings say? (Java only) |
Deeper questions need a higher analysis level. The default level is the symbol table. Pass analysis_level= to raise it, as in Quick Start step 1. Where a backend or a language cannot answer, the accessor refuses rather than returning an empty result. The reference lists every such case.
The type of the backend= configuration selects the backend. CodeAnalyzerConfig and its subclasses run the analyzer locally. This is the default. Neo4jConnectionConfig reads a graph the analyzer emitted earlier with --emit neo4j, so no source checkout is needed:
from cldk import CLDK
from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
analysis = CLDK.python(
backend=Neo4jConnectionConfig(
uri="bolt://localhost:7687",
username="neo4j",
password="...",
application_name="my-app", # the --app-name the graph was loaded with
),
)| Language | Analyzer | Built on | Runs as |
|---|---|---|---|
| Java | codeanalyzer-java |
JavaParser and WALA | a subprocess on the bundled JVM |
| Python | codeanalyzer-python |
Jedi, the standard-library ast, and a vendored Scalpel alias oracle |
in-process |
| TypeScript / JavaScript | codeanalyzer-typescript |
the TypeScript compiler via ts-morph, Rapid Type Analysis, and a def-use linker | a self-contained binary |
The reference's Attach section and the per-language sections after it list the graph versions each backend accepts. They also show how to migrate an older graph.
- docs/agent-api-reference.md is the full reference. It opens with the four moves most questions decompose into, then lists every accessor with its cost.
- docs/skills/using-cldk/SKILL.md is a Claude Code skill. It states the invariants an agent must hold. Ids are opaque, levels gate accessors, bounds are never silent, and a refusal is not an empty answer. To install it, copy the directory into your skills folder.
- docs/architecture.md shows how the SDK is laid out, for contributors.
- codellm-devkit.info holds the full documentation.
We welcome contributors of all experience levels. See the CONTRIBUTING guide to get started.
If you use CLDK in your research, please cite:
@inproceedings{krishna2025codellm,
title = {Codellm-Devkit: A Framework for Contextualizing Code LLMs with Program Analysis Insights},
author = {Krishna, Rahul and Pan, Rangeet and Pavuluri, Raju and Tamilselvam, Srikanth and Vukovic, Maja and Sinha, Saurabh},
booktitle = {Proceedings of the 33rd ACM International Conference on the Foundations of Software Engineering (FSE Companion '25)},
pages = {308--318},
year = {2025},
publisher = {ACM},
doi = {10.1145/3696630.3728555}
}Research that uses or cites CLDK is listed in docs/citations.md.
| Name | |
|---|---|
| Rahul Krishna | i.m.ralk@gmail.com |
| Rangeet Pan | rangeet.pan@ibm.com |
| Saurabh Sinha | sinhas@us.ibm.com |
Licensed under the Apache License 2.0.