Enola
OfficialProvides structural analysis and modeling of Django projects, enabling AI agents to understand routes, models, views, and dependencies.
Provides structural analysis and modeling of FastAPI applications, enabling AI agents to understand routes, dependencies, and types.
Provides structural analysis and modeling of Jetpack Compose UI code, enabling AI agents to understand composables and dependencies.
Provides structural analysis and modeling of Next.js projects, enabling AI agents to understand routes, components, and dependencies.
Provides structural analysis and modeling of Nuxt projects, enabling AI agents to understand routes, components, and dependencies.
Provides structural analysis and modeling of Spring Boot applications, enabling AI agents to understand controllers, services, and dependencies.
Provides structural analysis and modeling of Svelte applications, enabling AI agents to understand components, dependencies, and types.
enola
enola builds one graph of your software system: every repository, language, framework and technology in it, and how they connect.
It runs locally as a single binary, with no model, external language server, or separate indexing infrastructure required.
It reads your source code and records what is there: modules, functions, API routes, database access, message topics, infrastructure. Then it links those pieces, inside each repository and across them. A frontend's call to /api/orders is linked to the Go handler that serves it; one service's Kafka producer is linked to the service that consumes the topic. What exactly is in the graph.
You can ask that graph questions yourself, give it to your coding agent, or build your own tools on it. The graph comes from parsing your code; no AI model takes part in producing it. The same code always produces the same graph, and it never leaves your machine.
Try it
curl -fsSL https://raw.githubusercontent.com/enola-labs/enola/main/install.sh | shThen point it at any repository you have checked out:
enola --explain /path/to/your/repoNo config file, no account, nothing written to disk. It prints what it found. If you have nothing at hand, try it on enola itself:
git clone https://github.com/enola-labs/enola
enola --explain enolaPart of what that prints (the numbers move as the code does):
Overview
Languages: go, typescript, c, ruby, python
Total facts: 10814
Architecture
cyclic dependencies 0
layer violations 0
Impact analysis (hotspots)
Top hotspots (by coupling):
module fan-in fan-out crit blast radius
internal/facts 253 1 high 96
pkg/command 1 94 high 1
pkg/bootstrap 15 69 high 4Read the first hotspot as: internal/facts is used from 253 places, and a change to it can reach 96 modules.
The binary is also on PyPI (pip install enola-cli) and RubyGems. Every install route is in docs/CLI.md.
Related MCP server: amdb
Across repositories
A system rarely lives in one repository, so enola doesn't stop at one. Give it a backend and the things that call it (a web app, a mobile app, another service) and it links them into one graph. Then it can answer the question that usually costs a morning and two colleagues: if I change this endpoint, what breaks?
The hard part is that two sides rarely spell an endpoint the same way. In examples/cross-repo/ the web service calls /api/v2/orders/{id}, but the API service never writes that string anywhere:
v2 := r.PathPrefix("/api/v2").Subrouter()
registerOrders(v2) // in main()
r.HandleFunc("/orders/{id}", getOrder) // in registerOrders(), a different functionenola follows the prefix into the function it was passed to and files the route under the address it actually answers on, so the call links. It does the same for Express, FastAPI, Axum, Rails and Swift, where the prefix often sits in another file entirely.
When it can't link something, it says so instead of guessing:
$ enola coverage cluster.yaml
service classification detected resolved unresolved
api isolated 0 0 0
web connected 3 2 1The unresolved call builds its URL at runtime, so there is nothing to match. That distinction matters: a service with no connections and a service whose connections enola failed to follow should never look the same. The example runs in one command: ./run.sh.
Four ways to use it
On your own
No agent and no AI needed. enola --explain . analyses a repository and prints a report without writing anything. To keep the graph, build it with --generate. It is saved under .enola/, and that saved graph is what the local dashboard shows:
enola --generate .
enola dashboard --openThe simplest way to see what changed is to check what came in with a pull. Record the current state, pull, and compare:
enola baseline pin
git pull
enola checkenola check builds the graph again and reports only what changed since the pin: new dependencies, new calls, new findings. Problems the repository already had stay out of the report. Your own edits work the same way: pin, change, check. Here a new helper in storage imports the delivery layer. It compiles, every test passes, and it breaks the layer order the repository declared:
FAIL — 1 structural regression introduced.
Regressions (fail):
- [layers] 1.00 — Layer violation: storage -> delivery
import of notifyenola check runs every check enola has, and docs/EXPLAINERS.md describes each one. Some of them grade against how you say your system should look, such as a layer order or which service may call which. You write that down in docs/INTENT.md and docs/CONSTRAINTS.md.
Nothing fails by default. You choose what should, for example enola check --fail-on=layers. docs/GATING.md explains what can fail a build and why; docs/HISTORY.md covers how the architecture changed over time.
In CI
The same check runs on every pull request with enola-action. It resolves the exact base commit, grades both sides on the runner, annotates the lines that introduced a finding, and writes the architecture delta to the job summary:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: enola-labs/enola-action@v2 # reports only; add fail-on to gateSame explainers and same exit codes as enola check in your shell, with no baseline to publish or restore.
With your coding agent
First tell your agents enola exists, and let it grade each session:
enola install --hooksThen give the agent the graph over MCP:
Client | Do this |
Claude Code |
|
Codex |
|
Copilot (VS Code) |
|
Cursor | add the block below to |
opencode | nothing, |
Pi | nothing, |
Any other MCP client | add the block below to its MCP config |
{ "mcpServers": { "enola": { "command": "enola" } } }Before an edit, the agent asks the graph what depends on the code it is about to touch, instead of piecing it together from searches. After the edit, a hook runs the same check as above, so the agent sees what it actually changed and fixes a regression before telling you it's done.
enola install previews every file it changes and asks first; enola uninstall reverses all of it. enola doctor tells you whether the hooks are really firing. Per-client details, including Copilot's different config key: docs/CLI.md.
As a foundation for your own tools
A snapshot is a set of plain files with a documented format: the facts, the relationships between them, the findings, and a receipt recording exactly how it was built. Run enola as a subprocess and load them wherever you need them.
Cognee builds its code-graph search this way: it pins an enola-cli release and loads the snapshot files, and that search needs no LLM key. How it finds the binary and what it writes: docs/INTEGRATING.md.
docs/GRAPH.md explains what the graph contains and which files are a stable contract; docs/INTEGRATING.md shows how to load it.
What it reads
23 languages and formats, detected automatically. A repository with two languages is indexed as two languages without being told.
Application code: Go, Java, Kotlin, Scala, JavaScript, TypeScript, Vue, Svelte, Ember, Angular, Python, Ruby, PHP, Swift, Dart/Flutter, Rust, C/C++, .NET (C#, VB.NET, F#)
APIs and messaging: OpenAPI, gRPC, GraphQL, AsyncAPI
Infrastructure: Terraform/HCL, Ansible
On top of the language it understands the frameworks that shape routes, storage and wiring, among them Rails, Django, FastAPI, Spring, Express, Next.js, ASP.NET Core, Laravel, Axum, SwiftUI and Jetpack Compose. The full table is in docs/LANGUAGES.md. A language you don't see there is a gap worth reporting.
How it works
enola parses each file, turns what it finds into typed facts ("this function calls that one", "this route is served here"), links the facts into a graph, and runs checks over it: dependency cycles, layer violations, unused routes, hotspots and more.
Deterministic. 91 open-source repositories indexed three times each (once cold, twice warm) gave byte-identical results, over 8.1 million facts. Every snapshot carries a receipt of how it was built, and enola refuses to compare two snapshots that weren't built the same way.
Fast enough for every commit. Re-indexing an unchanged tree took 4.8s for grafana and 41.5s for the Linux kernel.
Local. One binary reading local files. No model, no embeddings, no upload.
What the graph contains, how it is built and what it writes to disk: docs/GRAPH.md. Numbers and the scripts that produce them: docs/BENCHMARKS.md. Internals: ARCHITECTURE.md.
Limitations
enola models structure, not runtime behaviour. It knows a service calls another; it knows nothing about timeouts, retries or whether a message can be lost.
Calls it cannot resolve, such as URLs built at runtime, are reported as unresolved rather than guessed. Per-language limits are in docs/extraction/ and the gaps found so far in docs/BLIND-SPOTS.md.
Most findings are advisory. Across the benchmark corpus, 89.2% of them could not fail a build even with every check named.
A clean
enola checkmeans the change introduced nothing new, not that the repository is clean.
With a coding agent, most of these stop being dead ends. enola says exactly where its knowledge stops: which call it couldn't resolve, which finding is only advisory. The agent can open that file, read the retry settings or the URL being built, and judge whether a finding matters for this change. The graph shows the agent where to look; the agent reads what the graph can't hold. What the agent concludes is still the agent's judgement, not a measurement, and enola keeps the two apart.
Documentation
Your first graded change: the loop end to end, on a module small enough to read
The graph: what is in it, how it is built, what it writes to disk
CLI reference: install, agent setup, commands, flags and exit codes
Gating a change: what a verdict contains and what can fail a build
Architecture: the fact model, pipeline, graph and MCP tools
Found it useful?
If enola --explain told you something about your codebase you didn't already know, a star helps other people find it.
If it missed something it should have caught (an unresolved edge, a route it didn't match, a construct it walked past), please open an issue. Those are the most useful bug reports this project gets.
License
Apache License 2.0, see LICENSE.
This repository is the full engine, not a trial edition. Nothing is gated, metered or degraded without a key, and no snapshot, fact or usage counter ever leaves your machine. The only outbound request enola makes is to GitHub's release API: a background release check you can turn off (see Staying current), and enola upgrade when you run it.
Acknowledgements
Muhamed Isabegović is the author of a large part of
what this repository does. The constraints program — declared architectural law over the
fact graph — is his, along with the vocabulary it verdicts, plan and constraints mine,
the fact-provider seam and the providers that ride it, the shareable history store behind
blame and diff, declared intent compiling into the graph, Ember support, the Rails
extraction work with the dead-methods and query-loops explainers, the Ruby surface for
writing laws as sentences, and the verdict writers that put a finding where CI reads it.
He also maintains the Ruby and Rails integration gems that drive enola from Bundler.
enola bundles third-party components under their own licenses; see NOTICE. Swift parsing uses the tree-sitter-swift grammar by Alex Pinkus (MIT), vendored under internal/extractors/swiftextractor/grammar/; Dart parsing uses tree-sitter-dart by UserNobody14 and others (MIT), vendored under internal/extractors/dartextractor/grammar/; Scala parsing uses tree-sitter-scala (MIT), vendored under internal/extractors/scalaextractor/grammar/. Every other grammar is a normal Go module dependency and is not vendored.
This server cannot be deployed
Maintenance
Related MCP Connectors
Give your AI agent a persistent map of your project's structure, dependencies, and bugs.
AI Agent with Architectural Memory. Impact analysis (free), tests and code from the graph (pro).
Code intelligence platform for AI agents. 20 tools for architecture, security & impact analysis.
Shared memory for coding agents. Stop re-explaining your codebase every session.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA local-first codebase intelligence layer for AI coding agents, providing a persistent, queryable model of a repository via an MCP server and CLI to enable structure queries instead of reading many files.Apache 2.0
- AlicenseNot gradedqualityAmaintenanceTurn your codebase into AI context — entirely on your machine. Single-binary MCP server with AST parsing, call graph, and local embeddings.26MIT
- FlicenseNot gradedqualityDmaintenanceGive your AI coding agents superpowers — a local MCP server for fast, token-efficient code navigation, search & analysis.-
- FlicenseBqualityCmaintenanceA fully local, privacy-first MCP server that gives AI coding assistants deep repository intelligence with file-and-line-cited answers, persistent semantic memory, and agentic abilities like task planning and code review—all without any cloud API calls.23-