Skip to main content

Quick Start

Analyze a real project and explore its graph in a few minutes — first from the command line, then from Python.

1. Install

pip install "graphlens-cli[python]"

2. Analyze a project

Point graphlens analyze at any Python project root:

graphlens analyze ./my-project

You get a breakdown of the graph — node counts by kind, relation counts by kind, and the busiest callers:

graphlens · my-project
nodes: 1240
relations: 3981
resolver: ok

nodes by kind relations by kind
FUNCTION 410 CONTAINS 980
METHOD 265 DECLARES 870
CLASS 98 CALLS 640
MODULE 54 REFERENCES 410
... ...

The resolver: ok line is important — it tells you the type-aware layer ran to completion. See Concepts for what the other values mean.

3. Save the graph

Serialize the graph to JSON so you can query it later without re-parsing:

graphlens analyze ./my-project --output graph.json

4. Query it

# Who calls this function?
graphlens query my_function --graph graph.json --op callers

# What does it call?
graphlens query my_function --graph graph.json --op callees

# 2-hop neighborhood around a method
graphlens query MyClass.method --graph graph.json --op neighbors --depth 2

5. Visualize it

Open an interactive graph in your browser:

graphlens visualize ./my-project

Click any FUNCTION or METHOD node and press Show callers to focus on just that node and everything that reaches it. See the visualization guide.

The same thing from Python

Everything the CLI does is available as a library. Load an adapter from the registry, analyze a project, and query the returned GraphLens:

from pathlib import Path
from graphlens import adapter_registry, NodeKind, RESOLVER_STATUS_KEY

# Load and instantiate the Python adapter
adapter = adapter_registry.load("python")()

# Analyze — returns a GraphLens
graph = adapter.analyze(Path("./my-project"))

print(f"Nodes: {len(graph.nodes)}")
print(f"Relations: {len(graph.relations)}")

# Make sure the resolver actually ran (don't trust a silently degraded graph)
assert graph.metadata[RESOLVER_STATUS_KEY] == "ok"

# Find a function and walk its call graph
fn = graph.nodes_by_name("my_function")[0]
callers = graph.callers(fn.id) # who calls it
callees = graph.callees(fn.id) # what it calls
near = graph.neighbors(fn.id, depth=2) # 2-hop neighbourhood

# Inspect nodes by kind
classes = graph.nodes_by_kind(NodeKind.CLASS)

# Serialize for pipelines / agents, then reload
text = graph.to_json(indent=2)
graph2 = type(graph).from_json(text)

Comparing two scans

GraphLens.diff gives you a structural diff between two graphs — useful for "what changed in this PR" reports:

diff = old_graph.diff(new_graph)
print(diff.added_nodes) # list[Node]
print(diff.removed_relations) # list[Relation]
print(diff.is_empty) # True when the graphs are structurally identical

Next steps

  • Core Concepts — the vocabulary behind adapters, resolvers, nodes, and relations.
  • Library API — the full GraphLens query surface.
  • CLI — every command and flag.
  • CI Integration — index your repo on every push.