Metadata-Version: 2.4
Name: graph-build
Version: 0.4.0
Summary: Interactive code+docs dependency graph with doc-sync tooling and an AI-agent-friendly JSON export
Project-URL: Homepage, https://github.com/Mr-Freewan/build-graph
Project-URL: Repository, https://github.com/Mr-Freewan/build-graph
Project-URL: Documentation, https://github.com/Mr-Freewan/build-graph/blob/main/docs/guide.md
Project-URL: Live demo, https://mr-freewan.github.io/build-graph/
Project-URL: Changelog, https://github.com/Mr-Freewan/build-graph/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/Mr-Freewan/build-graph/issues
Author: Yuriy Totyshev
License-Expression: MIT
License-File: LICENSE
Keywords: ai-agents,ast,d3,dependency-graph,doc-sync,documentation,imports,llm,visualization
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Documentation
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.11
Description-Content-Type: text/markdown

<div align="center">

<img src="https://raw.githubusercontent.com/Mr-Freewan/build-graph/main/docs/media/banner.jpg" alt="build-graph" width="840">

[Deutsch](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.de.md) | **English** | [Español](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.es.md) | [Français](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.fr.md) | [Italiano](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.it.md) | [日本語](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.ja.md) | [한국어](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.ko.md) | [Português](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.pt.md) | [Русский](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.ru.md) | [中文](https://github.com/Mr-Freewan/build-graph/blob/main/i18n/README.zh.md)

[![CI](https://github.com/Mr-Freewan/build-graph/actions/workflows/ci.yml/badge.svg)](https://github.com/Mr-Freewan/build-graph/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/graph-build)](https://pypi.org/project/graph-build/)
![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)
![Zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/Mr-Freewan/build-graph/blob/main/LICENSE)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![LLM-Agent friendly](https://img.shields.io/badge/LLM--Agent-friendly-blueviolet)](#designed-for-ai-agents)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](https://github.com/Mr-Freewan/build-graph/blob/main/CONTRIBUTING.md)

[![Live demo](https://img.shields.io/badge/demo-online-blueviolet?style=for-the-badge&logo=googlechrome&logoColor=white)](https://mr-freewan.github.io/build-graph/)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/Mr-Freewan/build-graph)

</div>

> **Architectural memory for your refactors.** See the blast radius of your
> changes across code, docs, and git — on one interactive map that both you
> and your AI agent can read. A set of lightweight utilities and a simple but
> highly functional UI, delivered as a self-contained HTML file you can share
> as-is. Lightweight and private.

`build-graph` renders a **single-file interactive HTML graph** connecting
five layers no other tool combines:

- **code → code** — Python imports (AST-based, `TYPE_CHECKING`-aware)
- **code ↔ docs** — which markdown files mention which source files
- **git drift** — added / modified / renamed / deleted overlay with ghost
  nodes for files that no longer exist
- **file-change heat map** — surface and tame the hottest spots of churn, and
  the likely sources of bugs
- **test-coverage map** — reads the project's `coverage.xml` and shows test
  coverage at a glance, highlighting the least-covered files

…and exports the same map as a **compact, token-efficient JSON** designed to
drop into an LLM agent's context.

All of that with **zero dependencies** — pure Python stdlib, `pip install`
brings in nothing else. The only third-party code is D3.js in the browser,
SRI-pinned from CDN or fully embedded with `--no-cdn`.

![Force layout settling on a real project — 1070 nodes / 6279 edges, dark theme](https://raw.githubusercontent.com/Mr-Freewan/build-graph/main/docs/media/hero-dark.jpg)

**[▶ Live demo](https://mr-freewan.github.io/build-graph/)** — the graph of
this very repository (dogfood), with a synthetic `--mock-git` overlay so the
Git and coverage modes are clickable too.
**[📖 UI guide](https://github.com/Mr-Freewan/build-graph/blob/main/docs/guide.md)** — a concise step-by-step walkthrough of the
core features.

## Install

```bash
pip install graph-build        # or: uv tool install graph-build
```

Install straight from GitHub:

```bash
pip install git+https://github.com/Mr-Freewan/build-graph.git

# or from a clone:
git clone https://github.com/Mr-Freewan/build-graph.git
pip install ./build-graph
```

> The PyPI distribution is named `graph-build` (the straight name is taken);
> the installed commands keep their names: `build-graph`, `find-related-docs`,
> `verify-doc-links`.

## Quick start

```bash
cd your-project
build-graph                    # autodiscovery, no config needed → docs/graph.html
build-graph --ultra-compact    # + docs/graph-ultra.json for AI agents
build-graph --init             # optional: pin discovered structure to graph.toml
```

Two companion CLIs — `find-related-docs` (reverse lookup: code → docs) and
`verify-doc-links` (broken-reference gate for CI) — ship in the same package;
see [Companion tools](#companion-tools).

## Why not X?

- **pydeps / Import Linter** — imports only; no docs layer, no git drift.
- **lychee & co.** — dead-URL checkers; no map, no code layer.
- **Obsidian graph view** — notes only; doesn't see your code.
- **Repomix / Gitingest** — pack the repo *text* for LLMs; build-graph gives
  the *structure*: ~2 % of the tokens the raw text would cost (see
  [the numbers](#what-it-costs-in-context)).
- **Graphify / Understand-Anything** — knowledge-graph tools that drag in
  heavier dependency stacks and lean on a non-deterministic LLM for the
  analysis; build-graph is deterministic and dependency-free, and adds the git
  and doc-sync layers neither of them has.

## Designed for AI agents

`--ultra-compact` (or the older `--compact`) writes a self-documenting JSON
snapshot — embedded legend, no external schema needed — that agents use for:

1. **Blast radius** — incoming imports of the file you're about to change,
   without grep.
2. **Docs routing** — which ADR / reference doc to read *before* editing a
   file.
3. **Three-way doc-sync** — the graph reveals (1) what's documented, (2) what
   should be documented but isn't, and (3) what's documented but no longer
   exists (ghost nodes = staleness detector).

Add `build-graph --ultra-compact` to a pre-commit hook or CI step to keep the
map fresh for every agent session.

### The ultra-compact format

`--ultra-compact` writes `graph-ultra.json` (schema v3) — the format to reach
for when the snapshot is going into a context window. It carries exactly what
`--compact` carries, in roughly 40% of the bytes, by writing each fact once:

```jsonc
{
  "v": "3.0",
  "legend": { "...": "what every section and code below means" },
  "stats": { "nodes": 1070, "ghosts": 0, "edges": 6279 },
  "cols": ["id", "file", "cat", "heat", "cov"],
  "n": {
    "app/core/security": [[412, "access.py", "cor", 23, 87]],
    "docs/adr":          [[7, "0009-parser-framework.md", "adr", 4, -1]]
  },
  "git": [[412, "mod"]],
  "e": {
    "imported_by":  [[412, [[518, 31], [604, [12, 88]]]]],
    "doc_mentions": [[7, [[412, 142]]]]
  }
}
```

Files are grouped under their directory, so a path is written once and a node
row is just `id, filename, category` — plus a `heat` column (commits touching
the file) and a `cov` column (line coverage %, `-1` when unmeasured) whenever
those layers were collected. `cols` declares that layout, so a row is never
ambiguous.

Edges are grouped into sections named after the group key, so a row reads
key-first and direction never has to be inferred from argument order:
`imported_by` is keyed by the module being imported, `doc_mentions` by the doc
doing the mentioning. An item is a bare id, `[id, line]`, or `[id, [lines]]`.
Optional layers — `git` statuses, `ghosts` (deleted files still referenced by
docs), `ge` (edges touching them) — appear only when there is something to
report. `degree` isn't stored: it is the number of incident edges.

### The compact format

`--compact` writes `graph-compact.json` (schema v2), the flatter predecessor
of the format above — unchanged, and still the right pick if you already
parse it: nodes as an indexed array, edges as
`[source_idx, target_idx, type, [line_numbers]]` rows, 3-letter codes for
every category and edge type. The `legend` key embeds the full decoding
table, so this file explains itself too:

```jsonc
{
  "v": "2.0",
  "legend": { "...": "what every field and code below means" },
  "stats": { "nodes": 1070, "ghosts": 0, "edges": 6279 },
  "n": [
    { "p": "smm_bot_async/core/security/access.py", "t": "cor", "d": 56 },
    { "p": "docs/explanation/adr/0009-parser-framework.md", "t": "adr",
      "d": 11, "s": "mod" }
  ],
  "e": [
    [ 1, 75, "d2d", [186] ]
  ]
}
```

`p` — path, `t` — category, `d` — degree, `s` — git status (omitted when
clean). Edge types: `c2c` imports, `c2d` doc mentions, `d2d` doc links,
`dcs` docstring refs, `typ` `TYPE_CHECKING`-only, `ren` git renames.
Deleted-but-still-referenced files ride along as ghost nodes (`"G": 1`).

### What it costs in context

Real numbers from a production repo — a thousand-odd mapped files and several
thousand edges (tokens ≈ bytes / 4, the usual rough estimate):

| What you put in context             |   Size | ≈ Tokens   |
|-------------------------------------|-------:|-----------:|
| The mapped files themselves         |  14 MB | ~3,650,000 |
| `--json` (verbose snapshot)         | 1.2 MB |   ~320,000 |
| `--compact`                         | 0.27 MB |   ~69,000 |
| **`--ultra-compact`**               | **0.10 MB** | **~27,000** |

The whole architecture — every import, every doc mention, every stale
reference — lands in well under 1 % of what the raw text would cost, and
leaves a 200 k-context session almost entirely free to work in. Without the
map an agent rediscovers this structure every session: dozens of speculative
greps and file reads that burn comparable tokens *per question*, not once. On
small projects the map is almost free — the ultra-compact snapshot of this
very repo is under 8 KB ≈ ~2,000 tokens.

<details>
<summary>Don't take these numbers on faith — measure your own repo</summary>

```bash
$ build-graph --root . --bench

Context cost on this repo (tokens ~= bytes / 4):

  What you put in context            Size      ~Tokens  vs corpus
  raw corpus (1060 files)         13.9 MB    3,651,892     100.0%
  --json export (schema v1)        1.2 MB      320,439       8.8%
  --compact export (schema v2)   270.9 KB       69,339       1.9%
  --ultra-compact export (v3)    103.6 KB       26,517       0.7%
```

`--bench` only measures — it writes no files.

</details>

### A prompt to start from

```text
graph-ultra.json is a dependency map of this repository: nodes are
files, edges are imports and documentation mentions. Read the embedded
"legend" key first — it explains every section and code.

Using the map (before any grep):
1. Lay of the land: the 10 highest-degree hubs, grouped by category,
   with one line each on why they're central.
2. I'm about to modify <path/to/file.py>. List the blast radius:
   direct and 2-hop incoming importers, plus every doc that mentions
   the file — and tell me which of those docs to read first.
3. Anything suspicious: ghost nodes (docs pointing at deleted files),
   zero-degree modules, docs nothing links to.

Verify any surprising claim against the actual source before acting.
```

**More recipes:** [Prompts for AI agents](https://github.com/Mr-Freewan/build-graph/blob/main/docs/agent-prompts.md) — ready-made
prompts for blast radius, three-way doc-sync, ghost detection and dead-code hunts.

## The interactive graph

- **Canvas renderer** — smooth at 1000+ nodes / 6000+ edges (pre-warmed
  layout, viewport culling, label LOD).
- **6 edge types** — doc→doc, code→doc, code→code, type-only
  (`TYPE_CHECKING`), docstring mentions, git renames.
- **Git overlay** — status colours + ghost nodes + rename edges; `--mock-git`
  for a synthetic demo.
- **Graph diff** — `--diff-base REF` compares the working tree against a
  git ref: file statuses feed the Git overlay, new dependency edges show
  green and removed ones red (dashed), with counters in the legend. Add
  `--diff-head REF` to compare two specific refs instead.
- **Heat overlay** — node colour by git-commit frequency (blue→red
  gradient), whole history by default or the last N days with
  `--heat-days N`. A min-edits slider in the legend hides anything colder
  than the chosen threshold — edges follow. Mutually exclusive with the Git
  overlay — both recolour nodes, so only one is on at a time; unlike Git
  mode, it's additive: Node types stays visible and usable underneath.
- **Coverage overlay** — node colour by test-line coverage (green→red
  gradient — this one's about finding badly-covered files, so it reads
  the opposite way from Heat) from a Cobertura `coverage.xml`
  (`--coverage PATH`, e.g. `pytest --cov=your_pkg --cov-report=xml`). A max-coverage
  slider hides anything covered *more* than the chosen ceiling, isolating
  the worst-covered files as you lower it; turning it on also auto-hides
  every Node type but code in the legend (shown again with one click).
  Mutually exclusive with Git and Heat too. Off — and its button hidden —
  when no report is given.
- **Node tooltip** — hover any node to see its name and path; in Heat or
  Coverage mode, also the edit count or coverage percentage behind the
  colour. Edge tooltips turn off while either of those modes is active.
- **Analysis aids** — dead-code candidates, import-cycle detector (Tarjan
  SCC over runtime imports; `TYPE_CHECKING` edges don't count), orphan ring,
  shortest path between two nodes (Shift+click), isolate-a-type,
  exclude-by-name. A doc's bare mention of a same-named file (`config.py`
  with dozens of matches, no path) is credited to one `ambiguous`-category
  node instead of fanning out to every candidate.
- **Sharing** — URL-encoded views (Copy link), Mermaid export of the focused
  subgraph, full/compact JSON export.
- **Comfort** — 10 UI languages, dark/light themes, hue-aligned
  pastel/saturated palettes, draggable glass panels, IDE deep links
  (VS Code / Cursor / PyCharm), FAQ built in (`?`).

Everything lands in **one self-contained HTML file** — attach it to a PR,
send it in chat, open it offline.

## Configuration (optional)

Autodiscovery classifies every tracked file by kind (code / doc / config /
locale) × location, detects your package and docs layout, and generates
deterministic colours. A `graph.toml` is only an override:

```bash
build-graph --init           # generate graph.toml pinning current structure
build-graph --init --diff    # report drift (new folders, stale pins), change nothing
build-graph --init --merge   # append coverage for new folders, keep your edits
```

See [`graph.example.toml`](https://github.com/Mr-Freewan/build-graph/blob/main/graph.example.toml) for the annotated format
(`[docs]` categories, `[[code]]` dirs, `[[rules]]`, `[scan]` excludes,
`[dead_code]` exemptions, colour pins).

Two optional plain-text companions, both looked up in the project root:

- `known-brokens.txt` — whitelist for `verify-doc-links` false positives
  (one exact path per line).
- `exclude-dirs.txt` — directory-name skip list used only when git is
  unavailable (with git, `.gitignore` is the source of truth).

## CLI reference (build-graph)

| Flag | Effect |
|---|---|
| `--root PATH` | project root to scan (default: cwd) |
| `--config PATH` | graph.toml location (default: `<root>/graph.toml`) |
| `--output PATH` | HTML output (default: `docs/graph.html` or `[output].path`) |
| `--scope full\|package` | whole repo (default) or package+tests+docs only |
| `--json` / `--compact` / `--ultra-compact` | JSON snapshots next to the HTML: verbose (v1), compact (v2), ultra-compact (v3) |
| `--docs-only` / `--no-tests` | trim the node set |
| `--no-cdn` | fully offline output: embed D3.js inline (SHA-256 verified) and drop the external font link |
| `--mock-git` | synthetic git overlay for demos/testing |
| `--diff-base REF` | ref-diff build: statuses + edge changes vs a git ref (head = working tree unless `--diff-head` is set) |
| `--diff-head REF` | with `--diff-base`: diff against this ref instead of the working tree |
| `--heat-days N` | restrict the Heat overlay to the last N days (default: whole history) |
| `--coverage PATH` | enable the Coverage overlay from a Cobertura `coverage.xml` |
| `--init [--diff\|--merge\|--force]` | config lifecycle (see above) |

## Companion tools

`find-related-docs` and `verify-doc-links` run the same reference scanner the
graph is built from — what the map draws as a code↔docs edge is exactly what
they look up and verify. `graph-query` answers questions over an
already-built snapshot.

### find-related-docs

Reverse lookup: which docs mention a given code file. Run it before editing a
file to know which pages need updating afterwards, or wire `--git-added` into
a pre-commit hook so undocumented changes get flagged before they land.

<details>
<summary>Flags &amp; examples</summary>

```bash
find-related-docs src/mypkg/core/access.py   # single file (bare filename works too)
find-related-docs --git-added -v             # pre-commit: staged files, with doc line numbers
find-related-docs --git-modified             # working tree: staged + unstaged modifications
```

| Flag | Effect |
|---|---|
| `path` | file or directory to look up (a bare filename is searched project-wide) |
| `--docs-dir PATH` | documentation directory (default: `docs`) |
| `--exclude DIRNAME` | skip a folder name anywhere under the docs dir (repeatable) |
| `--git-added` | check all staged files; also warns about deleted files still mentioned in docs |
| `--git-modified` | check all modified files (staged + unstaged) |
| `-v` / `--verbose` | print `docs/<file>.md:<line>` for every mention |

</details>

### verify-doc-links

Check that every file reference in your `.md` files points to a real file.
Exit codes make it a drop-in CI gate:

<details>
<summary>Flags &amp; examples</summary>

| Exit | Meaning |
|---|---|
| `0` | all references valid |
| `1` | broken references found |
| `2` | target path invalid (not found, or not a `.md` file) |

```bash
verify-doc-links                     # whole docs/ against the project root
verify-doc-links docs/reference -v   # one subtree, with the offending lines
```

```yaml
# CI step (GitHub Actions)
- run: pip install graph-build
- run: verify-doc-links --root .
```

| Flag | Effect |
|---|---|
| `path` | `.md` file or directory to check (default: `docs`) |
| `--root PATH` | project root the references resolve against (default: cwd) |
| `--known-brokens PATH` | whitelist file (default: `<root>/known-brokens.txt`) |
| `-v` / `--verbose` | show the offending lines |

Besides `known-brokens.txt`, false positives can be silenced inline with HTML
comments (invisible in rendered Markdown): `<!-- broken-link-ok -->` on the
same line, `<!-- broken-links-ok-start -->` / `<!-- broken-links-ok-end -->`
around a block, or `<!-- ignore-ref: path/to/file.py -->` anywhere in the file.

</details>

### graph-query

Ask the graph questions without opening a browser. Works on the JSON written
by `--json`, `--compact` or `--ultra-compact` (auto-detected; defaults to
`docs/graph-ultra.json`, then the older snapshots):

<details>
<summary>Flags &amp; examples</summary>

```bash
graph-query blast-radius app/core.py   # transitive importers + every doc mentioning them
graph-query hubs --top 15              # most-connected files, in/out breakdown
graph-query stale-docs --check         # docs older than the code they describe (CI gate: exit 1)
graph-query orphans --type code        # files with no edges at all
```

| Command | Answers |
|---|---|
| `blast-radius <path>` | "what breaks if I touch this file" — transitive incoming imports (`--depth`, `--edges` to tune), plus affected docs |
| `hubs` | "where is the center of gravity" — top nodes by in+out edges (`--top N`) |
| `stale-docs` | "which docs lag behind the code" — compares last-commit times (one `git log` pass; mtime fallback), `--check` for CI |
| `orphans` | "what is connected to nothing" — degree-0 nodes, filterable by category |

Every command takes `--json` for machine-readable output — pipe it to `jq`
or feed it to an agent.

</details>

## Known limitations

Static analysis has natural borders — the graph is a referential map, not a
semantic one:

- Dynamic imports resolve only literal / top-level-constant module names
  (f-strings, dict lookups, conditional rebinding are skipped).
- `eval` / `exec` and DI-by-string are invisible. `[project.scripts]` /
  `[project.gui-scripts]` entry points in `pyproject.toml` are read, but only
  to exempt those modules from dead-code flagging — they don't create edges.
- Markdown templating (`{{ ref }}`, Jekyll/Hugo shortcodes) isn't parsed.
- Links resolve to whole files — section anchors (`file.md#part`) map to the
  file node.
- code→code edges are Python-only for now (the markdown/doc layers are
  language-agnostic).
- One repo per graph; symlinks are treated as physical paths.

## License

[MIT](https://github.com/Mr-Freewan/build-graph/blob/main/LICENSE) © Yuriy Totyshev
