code-review-graph
Local code graph that gives a coding assistant review context, blast radius and change risk.
Local code graph that gives coding agents task context and checks changes before they land
By Clay Good (individual) TypeScript Local · stdio Developer toolsKnowledge and memory
67 popularity score ★ 358 ↓ 3.0k/wk
Opens the app, which asks before it adds the server.
npm install -g openlore
cd <PROJECT_DIR> && openlore analyzeclaude mcp add openlore -- openlore mcp{
"mcpServers": {
"openlore": {
"command": "openlore",
"args": [
"mcp"
]
}
}
}{
"mcpServers": {
"openlore": {
"command": "openlore",
"args": [
"mcp"
]
}
}
}{
"servers": {
"openlore": {
"type": "stdio",
"command": "openlore",
"args": [
"mcp"
]
}
}
}[mcp_servers.openlore]
command = "openlore"
args = ["mcp"]gemini mcp add --scope user openlore openlore -- mcpOpens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.
git clone https://github.com/clay-good/openlore
cd openlore && npm install && npm run buildclaude mcp add openlore -- node '<ABSOLUTE_PATH_TO_CLONE>/dist/cli/index.js' mcp{
"mcpServers": {
"openlore": {
"command": "node",
"args": [
"<ABSOLUTE_PATH_TO_CLONE>/dist/cli/index.js",
"mcp"
]
}
}
}{
"mcpServers": {
"openlore": {
"command": "node",
"args": [
"<ABSOLUTE_PATH_TO_CLONE>/dist/cli/index.js",
"mcp"
]
}
}
}{
"servers": {
"openlore": {
"type": "stdio",
"command": "node",
"args": [
"<ABSOLUTE_PATH_TO_CLONE>/dist/cli/index.js",
"mcp"
]
}
}
}[mcp_servers.openlore]
command = "node"
args = ["<ABSOLUTE_PATH_TO_CLONE>/dist/cli/index.js", "mcp"]gemini mcp add --scope user openlore node -- '<ABSOLUTE_PATH_TO_CLONE>/dist/cli/index.js' mcp| Set together: remote embeddings | |
|---|---|
EMBED_BASE_URL |
Optional: Base URL of an OpenAI-compatible embeddings API. Does nothing without EMBED_MODEL. |
EMBED_MODEL |
Optional: Model name for the remote embeddings API, needed together with EMBED_BASE_URL. |
EMBED_API_KEY secret |
Optional: Key for the remote embeddings API, when it asks for one. |
ANTHROPIC_API_KEY secret |
Optional: Picked first for the optional LLM features when no provider is configured. GEMINI_API_KEY, OPENAI_COMPAT_API_KEY or OPENAI_API_KEY also work. |
OPENLORE_TELEMETRY |
Optional: Set to 1 to log tool calls locally in .openlore/telemetry/. Nothing is uploaded. |
OPENLORE_LLM_LOGS |
Optional: Set to 1 to keep redacted LLM prompts and responses in .openlore/logs/. |
OpenLore indexes a repository with static analysis (tree-sitter parsing into a local SQLite graph) and serves that graph to a coding agent over MCP. Asked about a task, it answers with the functions involved, who calls them, the matching specs and tests, and where new code would fit, so the agent can skip much of the file-by-file exploring. The same graph holds Infrastructure-as-Code resources and recorded architectural decisions. It covers 18 programming languages and 12 IaC formats, Terraform and Kubernetes among them.
The second half is change checking. Tools report the blast radius of the current diff and which tests reach it, classify each changed export as breaking or not, flag a diff that opens a new route into a boundary you have declared, and settle claims like "this function is dead" with a cited verdict. Notes and decisions are tied to symbols, so they follow renames and expire when the code under them changes. No language model is called to answer queries, and the core features need no API key.
The README talks about 80 tools, and the v3.5.0 registry in src/cli/commands/mcp.ts does hold 80. A plain `openlore mcp` shows only 15 of them, though: the `substrate` preset, where every tool is annotated read-only. You get `remember`, `record_decision` and the other writing tools by starting it with `--preset memory`, `--preset minimal` or `--preset full` (`--all-tools`). Other presets exist for navigation only, claim checking, multi-repo federation and parallel-work coordination. Any single tool result is truncated at 256 KB (`MCP_TOOL_MAX_BYTES` in src/constants.ts). Beside the server the package ships a CLI (`openlore install`, `analyze`, `review`, `prove`, `doctor`) and a GitHub Action.
| Runtime | Node.js 22.19+ |
|---|---|
| Runs on | macOS, Windows, Linux |
Install with `npm install -g openlore`, then run `openlore install` or `openlore analyze` in the repository to build the index under `.openlore/` (the `analyze_codebase` tool can also build it). Analysis, navigation and the checking tools need no account or key. Spec generation, LLM-backed verification and decision consolidation need ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_COMPAT_API_KEY or OPENAI_API_KEY.
Static analysis only. Reflective calls with non-literal targets, computed dispatch, eval, DI or plugin registries with no visible binding, and cross-language bridges are missing from the graph. Search defaults to BM25 keywords with no stemming, so abbreviated names can be missed. Semantic search needs `openlore embed --local` (about 23 MB, CPU) or a remote EMBED_BASE_URL. The first index of a large monorepo takes minutes. When one save touches more than 40 dependent files (the default budget), the watcher marks the rest stale until it re-indexes. The maker's own benchmark shows it adds cost on small, familiar repos with shallow questions. It never edits source code.
It suits developers using Claude Code, Cursor, Cline or similar agents on large, private or polyglot codebases (including the IaC that deploys them) who want structural context and pre-commit checks without sending code to a hosted service. Everything runs on your machine, and the default tool set only reads. On a small repo the agent already handles well, the maker's measurements show the agent's built-in search is often cheaper. If you need an agent to perform renames or moves across files, look elsewhere. OpenLore locates and checks code but does not edit it, and its README points to LSP toolkits for that work.
50 only read, 8 change things, 2 can delete or overwrite. A ? marks an optional parameter.
orientdirectory?: stringtask: stringlimit?: numbertokenBudget?: numberlean?: booleanrankBy?: enumanalyze_codebase changesdirectory?: stringforce?: booleanget_architecture_overviewdirectory?: stringprepare_spec_generationdirectory?: stringdomain: stringcursor?: stringmaxItems?: numbermaxResponseBytes?: numberprepare_spec_repairdirectory?: stringdomain: stringbaseRef?: stringcursor?: stringmaxItems?: numbermaxResponseBytes?: numberget_refactor_reportdirectory?: stringget_call_graphdirectory?: stringget_duplicate_reportdirectory?: stringresponseFormat?: enumget_signaturesdirectory?: stringfilePattern?: stringget_subgraphdirectory?: stringfunctionName: stringdirection?: enummaxDepth?: numberformat?: enumdirectResolvedOnly?: booleantrace_execution_pathdirectory?: stringentryFunction: stringtargetFunction: stringmaxDepth?: numbermaxPaths?: numberdirectResolvedOnly?: booleanvalueLevel?: booleanvalueParam?: stringget_mappingdirectory?: stringdomain?: stringorphansOnly?: booleancheck_spec_driftdirectory?: stringbase?: stringfiles?: arraydomains?: arrayfailOn?: enummaxFiles?: integeranalyze_impactdirectory?: stringsymbol: stringdepth?: numberdirectResolvedOnly?: booleanvalueLevel?: booleanvalueParam?: stringselect_testsdirectory?: stringchangedSymbols?: arraydiffRef?: stringmaxDepth?: numberdirectResolvedOnly?: booleanblast_radiusdirectory?: stringbaseRef?: stringdepth?: numbermaxSymbols?: numberfind_dead_codedirectory?: stringifDeleted?: stringmaxResults?: numberfilePattern?: stringdirectResolvedOnly?: booleanverify_claimdirectory?: stringkind: enumsubject: stringobject?: stringstructural_diffdirectory?: stringbaseRef?: stringheadRef?: stringmaxResults?: numberfiles?: arraydeclaredFootprint?: objectpeerFootprints?: arrayget_change_couplingdirectory?: stringfile?: stringlimit?: numbercheck_architecturedirectory?: stringfrom?: stringto?: stringget_critical_hubsdirectory?: stringlimit?: numberminFanIn?: numberget_function_skeletondirectory?: stringfilePath: stringget_god_functionsdirectory?: stringfilePath?: stringfanOutThreshold?: numbersuggest_insertion_pointsdirectory?: stringdescription: stringlimit?: numberlanguage?: stringsearch_codedirectory?: stringquery: stringlimit?: numberlanguage?: stringminFanIn?: numbertokenBudget?: numbermode?: enumquestionKind?: enumsearch_unifieddirectory?: stringquery: stringlimit?: numberlanguage?: stringdomain?: stringsection?: stringget_function_bodydirectory?: stringfilePath: stringfunctionName: stringfocus?: stringfocusKind?: enumget_file_dependenciesdirectory?: stringfilePath: stringdirection?: enumgenerate_change_proposal deletesdirectory?: stringdescription: stringslug: stringstoryContent?: stringannotate_story deletesdirectory?: stringstoryFilePath: stringdescription: stringget_route_inventorydirectory?: stringget_middleware_inventorydirectory?: stringget_schema_inventorydirectory?: stringget_ui_component_inventorydirectory?: stringget_env_varsdirectory?: stringget_external_packagesdirectory?: stringgenerate_tests changesdirectory?: stringdomains?: arrayframework?: enumuseLlm?: booleandryRun?: booleanget_minimal_contextdirectory?: stringfunctionName: stringfilePath?: stringrankBy?: enumtokenBudget?: numberget_landmarksdirectory?: stringlimit?: numberlabel?: stringget_mapdirectory?: stringcommunityId?: stringfind_pathdirectory?: stringfrom: stringto: stringuseCallDistance?: booleandirectResolvedOnly?: booleanfederation_status changesdirectory?: stringchange_impact_certificate changesdirectory?: stringbaseRef?: stringchange?: stringpersist?: booleanplan_parallel_workdirectory?: stringtasks: arraymap_in_flight_conflictsdirectory?: stringbaseRef?: stringincludeBranches?: booleanbranches?: arrayincludePullRequests?: booleantasks?: arraymaxChanges?: numberreport_coverage_gapsdirectory?: stringmaxResults?: numberfilePattern?: stringchangedSymbols?: arraydiffRef?: stringdirectResolvedOnly?: booleancertify_public_surfacedirectory?: stringbaseRef?: stringmaxResults?: numberbriefing_sincedirectory?: stringbaseRef?: stringfilePattern?: stringmaxResults?: numberfind_clonesdirectory?: stringsymbol?: stringsnippet?: stringminSimilarity?: numbermaxResults?: numberlocate_symbol_spandirectory?: stringsymbol: stringanalyze_error_propagationdirectory?: stringsymbol: stringmaxDepth?: numberanalyze_env_impactdirectory?: stringname: stringmaxDepth?: numberdetect_changesdirectory?: stringbase?: stringget_health_mapdirectory?: stringlimit?: numberrecord_decision changesdirectory?: stringtitle: stringrationale: stringconsequences?: stringaffectedFiles?: arraysupersedes?: stringscope?: enumconstraints?: objectapprove_decision changesdirectory?: stringid: stringnote?: stringsync_decisions changesdirectory?: stringdryRun?: booleanid?: stringremember changesdirectory?: stringcontent: stringanchors?: arraytags?: arraytype?: enumsupersedes?: stringtrigger?: objectpinned?: booleanclaims?: arrayrecalldirectory?: stringtask?: stringlimit?: numbertokenBudget?: numberasOf?: stringchangedSince?: stringtype?: string