Metadata-Version: 2.4
Name: graphora-kg
Version: 0.3.0
Summary: Deterministic code knowledge graph: index with tree-sitter, find what could break when you change code, review diffs grounded in structure. No LLM required to build.
Author: Naseem77
License: MIT
Project-URL: Homepage, https://github.com/Naseem77/Graphora
Project-URL: Repository, https://github.com/Naseem77/Graphora
Project-URL: Issues, https://github.com/Naseem77/Graphora/issues
Keywords: knowledge-graph,code-review,falkordb,tree-sitter,blast-radius,mcp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: falkordb>=1.0
Requires-Dist: tree-sitter!=0.26.0,>=0.23
Requires-Dist: tree-sitter-python>=0.23
Requires-Dist: tree-sitter-javascript>=0.23
Requires-Dist: tree-sitter-typescript>=0.23
Requires-Dist: tree-sitter-go>=0.23
Requires-Dist: tree-sitter-java>=0.23
Requires-Dist: tree-sitter-rust>=0.23
Requires-Dist: tree-sitter-c>=0.23
Requires-Dist: tree-sitter-cpp>=0.23
Requires-Dist: tree-sitter-ruby>=0.23
Requires-Dist: tree-sitter-php>=0.23
Provides-Extra: llm
Requires-Dist: litellm>=1.40; extra == "llm"
Provides-Extra: mcp
Requires-Dist: mcp<3,>=1.0; extra == "mcp"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Dynamic: license-file

<div align="center">

# Graphora

### Know what breaks before you change it.

**Every function, method, and class in one code graph · What could break, split into certain and uncertain · Zero Docker · $0 LLM cost to build**

[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
[![PyPI](https://img.shields.io/pypi/v/graphora-kg?color=3775A9&logo=pypi&logoColor=white)](https://pypi.org/project/graphora-kg/)
[![FalkorDB](https://img.shields.io/badge/FalkorDB-graph-FF4438?logo=redis&logoColor=white)](https://www.falkordb.com/)
[![tree--sitter](https://img.shields.io/badge/tree--sitter-parsing-2C2C2C)](https://tree-sitter.github.io/tree-sitter/)
[![MCP](https://img.shields.io/badge/MCP-server-6E56CF)](https://modelcontextprotocol.io/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

`pip install` it as a library · run it as a CLI · plug it into any AI agent as an MCP server · or install the skill into 22 agents with one command

</div>

---

> **TL;DR**: Graphora parses a repository with tree-sitter (no LLM) into a graph of every
> function, method, and class, and the calls between them. Ask it what could break if you
> change something, and it answers in three parts: callers that **will** break, callers that
> **might** break (with the alternatives spelled out: "this call is either X or Y"), and the
> tests that reach the change. It never guesses silently.

## Why Graphora

AI coding agents and code review share the same blind spot: they see text, not structure. A
one-line change to a shared function looks harmless in a diff; its callers live in other files.
And when a tool *does* follow calls, it usually picks one target by name and presents the guess
as fact.

| Problem | What Graphora does |
| --- | --- |
| Callers live outside the diff | Walks the call graph backwards (callers of callers, `--depth N`) from every changed function, method, or class. Edits inside a body count. |
| Name matching guesses wrong | Resolves each call the way the language does: `self`/`this`, same file, imports (incl. re-exports and `src/` layouts), same Go package, declared types and return types. External library calls (`os.path.join`, `fmt.Println`) never link to your code. |
| Guesses look like facts | When a call could mean several definitions, it says so: **"this `save(...)` call is either `Db.save` or `FileStore.save`, or a method outside this repository"**. |
| Missing definitions | Every function, method, and class: 100% of the definitions Python's own `ast` finds, on every repo we measured (see [Benchmarks](#benchmarks)). |
| Repo dumps cost tokens | The answer is a few hundred tokens of facts, not the files. |

## Quick start

```bash
pip install graphora-kg          # or: pip install -e . inside this repo

graphora index /path/to/repo     # build the graph (offline, $0)
cd /path/to/repo

graphora breaks charge_card      # what could break if I change charge_card?
graphora breaks Db.save --depth 3
git diff | graphora breaks       # ... for everything my diff touches
```

Without a FalkorDB server, Graphora uses its embedded backend (pure Python, JSON under
`~/.graphora`). For a shared, queryable graph, start FalkorDB and Graphora picks it up:

```bash
docker run -d --name graphora-falkordb -p 6379:6379 falkordb/falkordb:latest
# force a backend anytime with --backend embedded|falkordb|auto
```

## What the answer looks like

Real output, from Graphora indexing its own repository:

```text
$ graphora breaks EmbeddedGraphStore.find_symbols --depth 1

What could break if you change `EmbeddedGraphStore.find_symbols` (graphora/embedded.py:233)

Will break (1):
  - `EmbeddedGraphStore.find_symbol` (graphora/embedded.py:277) calls `EmbeddedGraphStore.find_symbols` at line 280

Might break (3): the call could mean something else
  - `blast_radius` (graphora/blast.py:82) may call `EmbeddedGraphStore.find_symbols` at line 87
      this `find_symbols(...)` call is either `EmbeddedGraphStore.find_symbols` (graphora/embedded.py:233)  <- the one you are changing
      or `GraphStore.find_symbols` (graphora/store.py:267)
      or a method outside this repository
  ... (2 more: what_could_break, changed_symbols)

Tests that reach it (0):
  none found: consider adding one
```

With the default `--depth 2` the same question also reaches the CLI commands, the MCP tools,
and 25 tests through those callers.

`blast_radius` receives a store typed as either backend, so the graph cannot know which
`find_symbols` runs; it shows both instead of picking one. Use `--json` for machine-readable
output.

### How certain is each link?

Every `CALLS` edge carries a confidence tag and the rule that produced it:

| Tag | Meaning | Examples |
| --- | --- | --- |
| `EXTRACTED` | The source names the target. | same-file call, `self.method()`, `from pkg.mod import f; f()`, `db = FalkorDB(); db.query()`, `def f(g: Graph): g.query()` |
| `INFERRED` | A bare call whose name matches exactly one definition. | `compute()` with one `compute` in the repo and no import |
| `AMBIGUOUS` | Several definitions match, or the receiver's type is unknown. All candidates are linked, and the answer lists them. | `store.save()` with `Db.save` and `FileStore.save` |

"Will break" uses only `EXTRACTED`/`INFERRED` paths. A single `AMBIGUOUS` call on the path
moves the caller to "might break".

## Four ways to use it

### 1. CLI

```text
graphora index PATH [--include-generated]   Build the graph (no LLM; honours .gitignore, skips generated code)
graphora breaks NAME... [--depth N]         What could break: will / might (X or Y) / tests
graphora breaks --diff F | --git-range R    ... for every symbol a diff touches
graphora review [--diff F|--git-range R]    Same facts as a review, plus missing-test warnings (LLM optional via --llm)
graphora blast NAME...                      Raw callers / callees / tests / importers
graphora stats                              Node and edge counts
graphora benchmark PATH                     Accuracy + context-size benchmark
graphora serve-mcp                          Serve the graph to AI agents over MCP
graphora install-skill [all|AGENT]          Install the Graphora skill for 22 AI coding agents
graphora sessions ingest|connected          Agent session-history graph (see below)
```

Every graph command accepts `--project`, `--backend auto|falkordb|embedded`, `--host`, `--port`.

### 2. Python library

```python
from graphora import index_repository, open_store
from graphora.impact import what_could_break, what_could_break_for_diff

store = open_store("myrepo", backend="embedded")
index_repository("/path/to/repo", project="myrepo", store=store)

result = what_could_break(store, ["Db.save"], depth=2)
print(result.to_text())            # or result.to_dict()

# CI gate: fail when a PR changes code that certainly breaks callers and no test reaches it.
impact = what_could_break_for_diff(store, pr_diff)
assert impact.tests or not impact.will_break, impact.to_text()
```

### 3. MCP server (for AI agents)

```bash
graphora serve-mcp --project myrepo
```

Tools: `what_could_break`, `what_could_break_for_diff`, `blast_radius`, `review_diff`,
`find_symbol`, `graph_stats`. The server tells agents to call `what_could_break` before
editing a symbol and to report uncertain callers as uncertain. Works with mcp 1.x and 2.x.

### 4. One-command skill install (22 agents)

```bash
graphora install-skill all          # every supported agent, into the current repo
graphora install-skill claude cursor copilot
graphora install-skill --list       # see all supported agents
```

Writes the Graphora workflow (ask what could break before editing, check the diff before
committing) into each agent's project config: `.claude/skills/`, `.cursor/rules/`,
`.github/instructions/`, `AGENTS.md`, `GEMINI.md`, Windsurf, Cline, Roo, Continue, Amazon Q,
Zed, Goose, Aider, and more. Idempotent; existing files are preserved, shared files get a
marked block.

## What gets indexed

Python, JavaScript/JSX, TypeScript/TSX, Go, Java, Rust, C, C++, Ruby, PHP (tree-sitter,
regex fallback). Per language this includes, for example: Python nested functions, lambdas,
decorated and async defs; JS/TS arrow functions, class fields, object-literal methods,
interfaces, enums; Go methods attached to their receiver type; Java constructors, records,
enums, interfaces; Rust `impl` methods; C++ out-of-class `A::f` definitions and destructors;
Ruby singleton methods; PHP traits and enums. Each symbol has a qualified name
(`geo.Square.area`), a parent, and a line range.

Indexing honours `.gitignore` inside a git work tree and skips generated files (ANTLR,
protobuf, `@generated`/`DO NOT EDIT` headers, minified JS) unless `--include-generated`.

## Agent session memory: graph your work, not just your code

AI coding agents already record every session locally: GitHub Copilot CLI
(`~/.copilot/session-store.db`), Claude Code (`~/.claude/projects/`), Codex CLI
(`~/.codex/sessions/`). Graphora ingests them all into **one graph**, so sessions from
different terminals and different agents become connected the moment they touch the
same file, repo, or PR.

```bash
graphora sessions ingest --days 7             # all agents found (or --source copilot|claude|codex)
graphora sessions connected file build.yml    # which windows/agents touched this file?
graphora sessions connected ref 275           # which sessions relate to PR 275?
graphora sessions connected repo org/proj     # everything that happened in one repo
graphora install-skill all --skill sessions   # let agents run these themselves
```

Read-only on the sources, no LLM, idempotent. See [use case 6](USECASES.md).

## Benchmarks

Measured on local checkouts with `graphora benchmark` (embedded backend, deterministic, no
network). "Definitions found" compares the graph with Python's own `ast` parser; "certain"
is the share of call edges tagged `EXTRACTED` or `INFERRED`.

| Repository | Commit | Files | Definitions found (vs `ast`) | Call edges | Certain | Context: repo → Graphora |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| [Shiftory](https://github.com/Naseem77/Shiftory) | `f4e1a03630` | 61 | 498/498 (100%) | 1,479 | 97% | 134,155 → 1,243 tokens |
| [falkordb-py](https://github.com/FalkorDB/falkordb-py) | `e4230e1ce7` | 42 | 390/390 (100%) | 1,143 | 57% | 61,931 → 1,708 tokens |
| [GraphRAG-SDK](https://github.com/FalkorDB/GraphRAG-SDK) | `93b760edd4` | 190 | 5,043/5,043 (100%) | 14,656 | 66% | 888,919 → 36,312 tokens |

The remaining ambiguous edges are mostly real polymorphism (several classes implement the
same method) and values whose type the source never states (e.g. pytest fixtures); those are
exactly the calls the answer lists as "X or Y". Indexing GraphRAG-SDK (190 files) takes 2.3 s
embedded and 2.5 s on FalkorDB. Methodology and the pinned public benchmark (`scripts/public_benchmark.py`)
are in [BENCHMARKS.md](./BENCHMARKS.md).

## Architecture

```text
   source files ──▶ tree-sitter parse: symbols (qualified names, ranges, return types),
                    call sites (callee + receiver), import bindings, local type facts
                                            │
                                            ▼
                    resolver (graphora/resolve.py): self → same file → imports →
                    package → declared types → name; external imports dropped
                                            │
                                            ▼
                       code graph (FalkorDB or embedded JSON)
              File · Function · Class · Module  /  CALLS · DEFINED_IN · IMPORTS · IMPORTS_FILE
                       (every CALLS edge: confidence, candidates, reason)
                                            │
              ┌──────────────┬──────────────┼───────────────┬─────────────┐
              ▼              ▼              ▼               ▼             ▼
       what could break   library API     MCP server     benchmark    skill installer
       (CLI, review)      (CI gates)      (AI agents)    (accuracy)   (22 agents)
```

Both stores keep each file's raw call sites and re-resolve the whole graph after every
write, so an incremental update (`update_files`) never drops callers in unchanged files.

## Enterprise notes

- **Privacy**: indexing and queries are fully local. Nothing leaves the machine unless the
  optional `review --llm` is used, and then only the diff plus the graph facts go to *your*
  configured provider.
- **Determinism**: identical input produces identical graphs and answers. No embeddings, no
  model drift in the core.
- **Multi-project**: one FalkorDB instance serves many project graphs (`graphora:{project}`).
  Solo developers need no server at all (embedded backend).
- **Auditability**: every claim in an answer traces to a tagged edge with the rule that
  produced it (`same-file`, `import`, `self`, `returns`, `name`, `receiver-unknown`, ...).

## Testing

```bash
pip install -e ".[dev,mcp]"
python -m pytest tests -q           # 200 tests
ruff check graphora tests scripts && mypy graphora
```

The suite covers every language's definitions (exact sets, including no false definitions),
each resolution rule, what-could-break (certain vs uncertain, depth, diffs, X-or-Y text), both
backends with a parity test, incremental updates, the CLI, the MCP server on mcp 1.x and 2.x,
the skill installer, and the benchmarks. CI runs it against a live FalkorDB and fails on any
skipped test.

## Requirements

| Requirement | Version | Notes |
| --- | --- | --- |
| Python | 3.10+ | |
| FalkorDB | optional | for the client-server backend; embedded backend needs nothing |
| git | any | `.gitignore` handling and `--git-range` |
| litellm | optional | only for `review --llm` |
| mcp | optional | only for `serve-mcp` (1.x or 2.x) |

## License

MIT
