Metadata-Version: 2.4
Name: design-graph
Version: 0.34.0
Summary: Parse prototype HTML into a knowledge graph — surgical MCP context for LLM agents at a fraction of the token cost
License: MIT
Keywords: mcp,llm,design-system,agent,context,kuzu
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: kuzu>=0.6
Requires-Dist: mcp>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Provides-Extra: interactive
Requires-Dist: questionary>=2.0; extra == "interactive"

# design-graph

[![Tests](https://github.com/manorfm/design-graph/actions/workflows/tests.yml/badge.svg)](https://github.com/manorfm/design-graph/actions/workflows/tests.yml)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Version](https://img.shields.io/badge/version-v0.33.0-green.svg)](https://github.com/manorfm/design-graph/tags)

`design-graph` parses standalone HTML prototypes—including bundled React generated by tools such as Claude Artifacts and Cursor Composer—into a typed Kuzu knowledge graph. It exposes screens, sections, components, props, styles, design tokens, interactions, text and layout through a CLI and an MCP server.

Instead of sending a 50–200k-token HTML bundle to an agent, the agent can request only the component or screen context it needs.

## Requirements

- Python 3.10 or later
- `pip` or `pipx`
- An MCP-compatible client only if you intend to use `design-mcp`

Runtime dependencies (`beautifulsoup4`, `kuzu` and `mcp`) are installed automatically.

## Install

Install directly from GitHub:

```bash
python3 -m pip install git+https://github.com/manorfm/design-graph.git
```

For an isolated command-line installation:

```bash
pipx install git+https://github.com/manorfm/design-graph.git
```

This installs three commands:

- `design-graph` — build, validate, inspect and export graphs
- `design-query` — query graphs from the terminal
- `design-mcp` — expose graphs to MCP clients over stdio

Verify the installation:

```bash
design-graph --version
design-graph --help
design-query --help
design-mcp --help
```

### Upgrade

```bash
python3 -m pip install --upgrade git+https://github.com/manorfm/design-graph.git
```

With `pipx`:

```bash
pipx upgrade design-graph
```

This package now requires Python 3.10+ (the `mcp` SDK dependency doesn't support 3.9). If `python3`/`pipx` resolve to an older interpreter, the upgrade fails to resolve dependencies — install or point `pipx` at a 3.10+ interpreter first (e.g. `pipx install --python python3.11 ...`).

### Uninstall

```bash
python3 -m pip uninstall design-graph
```

Or, if installed with `pipx`:

```bash
pipx uninstall design-graph
```

Uninstalling the package does not delete generated databases. Remove those separately from the configured graph directory if they are no longer needed.

### Local clone, installed globally (no venv)

```bash
git clone https://github.com/manorfm/design-graph.git
cd design-graph
python3 -m pip install -e .
```

This installs `design-graph`, `design-query` and `design-mcp` as global commands backed directly by this clone (an editable install just points at `src/`, it doesn't copy files).

To update, pull and reinstall:

```bash
cd design-graph
git pull
python3 -m pip install -e . --no-deps
```

`--no-deps` skips reinstalling `beautifulsoup4`/`kuzu` and is safe unless `pyproject.toml` added a new dependency. Reinstalling replaces the old version metadata in place — `pip` uninstalls the previous editable version automatically, so nothing stale is left behind. `git pull` alone is not enough to fix `--version` output, since the version string is baked into package metadata at install time, not read from git at runtime.

### Development installation

```bash
git clone https://github.com/manorfm/design-graph.git
cd design-graph
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -e '.[dev]'
pytest
```

## Build a graph

```bash
design-graph prototype.html
```

By default the graph is written to:

```text
~/.local/share/design-graph/prototype.db
```

Each source file gets its own database based on the HTML filename.

```bash
design-graph app-v1.html
design-graph admin.html
```

Build options:

| Option | Description |
|---|---|
| `--db PATH` | Write to a custom database path |
| `--name NAME` | Write to `<name>.db` under the graph directory |
| `--diff` | Show changes since the previous build |
| `--force` | Rebuild even when the HTML hash is unchanged |
| `--verbose` | Show debug-level pipeline logs |
| `--quiet` | Suppress output except errors |
| `--json` | Emit machine-readable build output for CI |
| `--version` | Print the installed version |

Use `--db` when you want to choose the exact file path. Use `--name` when you want the CLI to keep the standard graph directory but change the database filename.

Builds are incremental. An unchanged HTML file is skipped unless `--force` is supplied.

Each database owns an independent state file named `<database>.state.json`. Builds, diffs and `--force` therefore affect only their target prototype. When an old shared `.graph-state.json` exists, it is migrated only if the directory contains a single database.

If two HTML files have the same filename stem, they resolve to the same default database. The CLI warns when that database was previously built from a different source; use `--db` to keep both.

Use `--name` when you want the generated database to be named explicitly, for example:

```bash
design-graph prototype.html --name "Admin Panel"
```

This produces `Admin Panel.db` inside the resolved graph directory.

A build takes an exclusive lock on its target database. A second build started against the same database while one is already running (e.g. a watch script overlapping a manual build) fails fast with a clear error instead of corrupting the database. If a build reports non-zero `write_errors` in its summary or `--json` output, the graph may be missing nodes or edges — rerun with `--verbose` for details.

## Additional `design-graph` commands

### Export AI-ready chunks

```bash
design-graph chunk prototype.html
design-graph chunk prototype.html --output context.jsonl --max-chars 8000
```

The default output is `<prototype>.jsonl` beside the input file. The default maximum chunk size is 12,000 characters.

### Inspect graph status

```bash
design-graph status
design-graph status --doc "prototype"
design-graph status --db /path/to/prototype.db --verbose
```

With multiple databases, select one using `--doc`, `--db`, `DESIGN_GRAPH_DOC`, or `design-graph db use`.

Status separates fully extracted components from unresolved references. `Components` is the total,
while `Extracted` and `Unresolved` explain how that total was formed.

### Validate graph integrity

```bash
design-graph validate
design-graph validate --doc "prototype"
design-graph validate --db /path/to/prototype.db
design-graph validate --json
```

Validation checks database readability and graph integrity conditions such as orphan components and unused tokens. It exits with status 1 when errors are found.

### Generate a Markdown report

```bash
design-graph report
design-graph report --doc "prototype"
design-graph report --db /path/to/prototype.db --output report.md
design-graph report --name "Admin" --no-tokens --jsx
```

Without `--output`, the report is written to stdout.

### List and select databases

```bash
design-graph db list
design-graph db list --json
design-graph db current
design-graph db use "app-v1"
design-graph db info "app-v1"
design-graph db remove "old-prototype"
design-graph db remove "old-prototype" --force
design-graph db prune --dry-run
design-graph db prune
```

`db use` persists `default_doc` in the user configuration. Database names are filenames without the `.db` suffix.

`db remove` deletes the selected database, its `<database>.state.json`, and any interrupted build directory owned by it. It asks for confirmation unless `--force` is supplied. If the removed database was the configured default, only `default_doc` is cleared; other settings are preserved.

`db prune` removes per-database state files whose database no longer exists and interrupted build directories older than one hour. Use `--dry-run` to inspect the plan without deleting anything. Stop MCP or other processes holding the database before removal.

### Configure the UI-context skill in a consuming project

`design-graph` ships a project-agnostic agent skill — a set of instructions that makes an agent working on a frontend project check design-graph for screen/component context before writing UI code, instead of reading the whole prototype HTML. Configure it for one or more AI coding tools with:

```bash
cd /path/to/your-frontend-project
design-graph init                          # interactive: pick tools from a menu
design-graph init --tool claude,cursor     # non-interactive: install for specific tools
design-graph init --tool all               # install for every supported tool
design-graph init --force                  # overwrite a copy you edited by hand
```

Without `--tool`, the interactive prompt uses a real arrow-key/checkbox menu when [`questionary`](https://pypi.org/project/questionary/) is installed (`pip install design-graph[interactive]` or `pipx inject design-graph questionary`) and stdin/stdout are a real terminal; otherwise it falls back to a plain numbered text prompt — both work the same from a script or CI as long as `--tool` is passed, since neither path runs then.

Each tool gets its own native format and location, adapted from a single canonical source so the five copies can't drift out of sync with each other:

| Tool | File | Format |
|---|---|---|
| Claude Code | `.claude/skills/design-graph-ui-context/SKILL.md` | `name`/`description` frontmatter, loaded when relevant to the task |
| Cursor | `.cursor/rules/design-graph-ui-context.mdc` | `description`/`globs`/`alwaysApply` frontmatter, loaded when relevant |
| Codex CLI | `AGENTS.md` (project root) | Plain Markdown, always loaded — appended inside a marked section, never overwrites the rest of the file |
| Google Antigravity | `.agents/rules/design-graph-ui-context.md` | Plain Markdown, always loaded |
| Kiro | `.kiro/steering/design-graph-ui-context.md` | `inclusion: always` frontmatter, always loaded |

`design-graph init` never silently overwrites a copy you've customized — it refuses with a clear message unless `--force` is passed. Re-running it with no changes is a no-op.

Run `design-graph <command> --help` for the complete options of a command.

## Graph directory configuration

The graph directory is resolved in this order:

1. `GRAPH_DIR` environment variable
2. `graph_dir` in the user configuration file
3. `$XDG_DATA_HOME/design-graph`, defaulting to `~/.local/share/design-graph`

The user configuration file is `$XDG_CONFIG_HOME/design-graph/config.json`, defaulting to `~/.config/design-graph/config.json`:

```json
{
  "graph_dir": "/path/to/graphs",
  "default_doc": "app-v1"
}
```

`GRAPH_DIR` affects all three commands and overrides the configuration file.

```bash
GRAPH_DIR=/path/to/graphs design-graph prototype.html
GRAPH_DIR=/path/to/graphs design-query screens
```

## Query from the terminal

```bash
design-query screens
design-query --doc "app-v1" screens
design-query --db /path/to/app-v1.db screens
design-query tokens
design-query tokens color
design-query search "primary button"
design-query inspect SectionCard
design-query impact SectionCard
design-query screen RestaurantsPage
design-query interactions BtnPrimary
design-query children CardProduct
design-query metrics
```

`--verbose`, `--doc` and `--db` are accepted before or after the command.

Token filters accepted by `design-query tokens` are `color`, `spacing`, `typography`, `shadow` and `radius`. Without a filter, all categories are returned, including extracted CSS variables.

Every terminal query operates on one selected database. Selection follows this order:

1. `--db PATH`
2. `--doc NAME`
3. `DESIGN_GRAPH_DOC`
4. `default_doc` in the user configuration
5. Automatic selection when exactly one database exists

When multiple databases exist without a selection, the command exits with guidance instead of choosing one silently.

`design-query metrics` is the one exception: it never touches a `.db` file, so it works even when no prototype has been built yet — see [Call metrics](#call-metrics).

## Configure the MCP server

The server scans the configured graph directory for every `*.db` file when it starts. It remains available in degraded mode when no graph exists and returns setup guidance from tool calls.

### Cursor

Add the server to the Cursor MCP configuration (user-level `~/.cursor/mcp.json` or project-level `.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "design-graph": {
      "command": "design-mcp"
    }
  }
}
```

### Claude Code

Register the stdio server with the Claude Code CLI:

```bash
claude mcp add --scope user design-graph -- design-mcp
claude mcp get design-graph
```

For project-shared configuration, use `--scope project`; Claude Code writes a `.mcp.json` file in the project.

### Custom graph directory and default prototype

Environment variables can be passed by the MCP client:

```json
{
  "mcpServers": {
    "design-graph": {
      "command": "design-mcp",
      "env": {
        "GRAPH_DIR": "/path/to/graphs",
        "DESIGN_GRAPH_DOC": "app-v1"
      }
    }
  }
}
```

- `GRAPH_DIR` selects the directory scanned for databases.
- `DESIGN_GRAPH_DOC` sets the default active prototype when multiple databases are loaded.

The server detects a rebuilt `*.db` file on its own (it compares file mtimes before each tool call) and reloads without a restart. Restarting or reconnecting the MCP client is only needed after changing `GRAPH_DIR`, `DESIGN_GRAPH_DOC`, or upgrading the package itself — those are read once, at startup.

## MCP tools

| Tool | Purpose | Parameters |
|---|---|---|
| `list_screens` | List screens across all loaded prototypes | — |
| `get_screen` | Return a structural screen overview | `name`, `doc?` |
| `get_screen_full` | Return everything needed to reconstruct a screen | `name`, `doc?` |
| `get_screen_layout` | Return layout profiles for a screen's components and, for any section styled via CSS classes, one profile per section selector | `name`, `doc?` |
| `get_section` | Return visual details for a section | `screen`, `section`, `doc?` |
| `list_components` | List components, optionally filtered by semantic type (capped at 100 by default) | `comp_type?`, `limit?`, `doc?` |
| `get_component` | Return JSX, styles, tokens, text, interactions and children | `name`, `doc?` |
| `get_component_spec` | Return a reconstruction-oriented component specification. Falls back to a CSS-class spec (styles + which screens/components use it) when `name` matches no component — for a shared class like `.page-title` or `.chip` that was never factored into a named React component | `name`, `doc?` |
| `get_component_full` | Return a component plus every descendant (up to 3 levels via CONTAINS), each with its own styles, tokens, texts, interactions and props | `name`, `doc?` |
| `get_component_props` | Return declared component props and defaults | `name`, `doc?` |
| `get_component_children` | Return direct child components, in render order | `name`, `doc?` |
| `get_component_interactions` | Return hover/focus effects and transitions | `name`, `doc?` |
| `get_full_jsx` | Return unsanitized JSX | `name`, `doc?` |
| `get_full_styles` | Return a component's or a screen section's complete style list, with no display cap — the `get_full_jsx` equivalent for styles. For `name=`, also includes `@media`-scoped styles in their own section | `name?` (component) or `screen?` + `section?`, `doc?` |
| `get_full_texts` | Return a component's or a screen section's complete text list, with no display cap — the `get_full_styles` equivalent for texts | `name?` (component) or `screen?` + `section?`, `doc?` |
| `get_component_data` | Return the complete, uncapped content of every module-level constant a component's own body references by name (e.g. an icon-name -> SVG-path table indexed as `ICONS[name]`) — reuse these exact values instead of substituting an equivalent icon/asset | `name`, `doc?` |
| `get_tokens` | Return color, spacing, typography, shadow, radius or CSS-variable tokens, or all categories when omitted | `category?`, `screen?`, `doc?` |
| `find_token_usage` | Find components and screens using a token | `value`, `doc?` |
| `search` | Search screens, components, tokens, text and shared CSS classes across prototypes | `query` |
| `impact` | Find screens and sections affected by a component or token | `name`, `doc?` |
| `get_build_diff` | Return screens/components added or removed since the previous build, plus a warning when any bundle entry failed to decode and was dropped from that build | `doc?` |
| `validate_component_implementation` | Compare JSX you wrote against a component's stored spec (children, default-state styles, texts) and report discrepancies | `name`, `jsx_source`, `doc?` |
| `set_prototype` | Set or inspect the active prototype for this MCP connection — resets on a connection restart (e.g. a client `/mcp` reconnect), even mid-task | `name?` |
| `get_metrics` | Return usage metrics for this server's own tool calls — see [Call metrics](#call-metrics) | `doc?`, `tool?`, `outcome?`, `since?`, `until?`, `limit?`, `raw?` |

`get_tokens.category` accepts `color`, `spacing`, `typography`, `shadow`, `radius` and `css_var`. Omit `category` to retrieve every extracted category.

`validate_component_implementation` is best-effort, not a full re-extraction: it reliably catches missing/extra child components and missing inline styles/texts, but cannot verify styles that came from the prototype's own CSS classes or Tailwind color utilities (e.g. `bg-blue-500`) — those require the original stylesheet, unavailable for a standalone snippet. Treat a clean report as "no red flags found", not proof of a pixel-perfect match. `jsx_source` is capped at 20,000 characters.

A response that includes a `⚠ Extração truncada` notice means an extraction cap was hit for that component — the spec shown is incomplete for the listed fields. Call `get_full_jsx` for the raw, uncapped JSX before treating it as the full picture. A style, text or "Dados referenciados" table that ends in a `+N mais` notice is a display-time cut, not missing data, and always names the call that recovers the rest — `get_full_styles` for styles, `get_full_texts` for texts, `get_component_data` for referenced module-level data. Pass `screen` to scope the list to tokens that screen's own components actually use.

A component's spec can include a "Dados referenciados" ("Referenced data") section: the literal content of any module-level constant its own body references by name — e.g. an icon-name -> SVG-path table indexed as `ICONS[name]`, or a role-key -> badge metadata table. Reuse those exact values when reimplementing a component instead of substituting a different icon or asset — see `get_component_data` above.

### Prototype selection

When multiple databases are loaded, the server selects a prototype in this order:

1. `doc` passed to the current tool call
2. Prototype selected by `set_prototype`
3. `DESIGN_GRAPH_DOC`
4. `default_doc` from the user configuration
5. Automatic selection when only one prototype is loaded

```text
set_prototype(name="app-v1")
get_component(name="SectionCard")
get_screen_full(name="HomePage", doc="admin")
```

The `doc` value is the database filename without `.db`.

## Call metrics

Every call made through the MCP server (`dispatch_tool_call`) is logged as one JSON line to `metrics.jsonl` in the XDG data directory — always `$XDG_DATA_HOME/design-graph` (`~/.local/share/design-graph` by default), regardless of any `GRAPH_DIR`/`graph_dir` override used for `.db` files (see [Graph directory configuration](#graph-directory-configuration)): the log's location doesn't move with the graph directory. Each line records the tool name, the active prototype, an outcome, duration and the call's own arguments. `design-query`'s other commands talk to the graph directly and are not logged — only calls that went through the MCP server are.

Outcome is classified from the tool's own rendered response: `error` for a raised exception, `not_found` / `ambiguous` / `no_results` from the same phrasing every tool already uses for those cases, `ok` otherwise. This is a heuristic read on the response text, not a separate contract — a tool that changes its wording could fall through to `ok`.

Read the log back with the `get_metrics` MCP tool or `design-query metrics` — both take the same filters and read the same file:

```bash
design-query metrics
design-query metrics --tool search --outcome no_results
design-query metrics --doc "app-v1" --since 24h
design-query metrics --raw --limit 20
```

| Parameter | Meaning |
|---|---|
| `doc` | Filter to calls tagged with this prototype |
| `tool` | Filter to calls of one tool (e.g. `search`) |
| `outcome` | `ok`, `not_found`, `ambiguous`, `no_results` or `error` |
| `since` / `until` | ISO-8601 timestamp or relative shorthand (`24h`, `7d`, `30m`) |
| `limit` | Max rows shown with `raw=true` — never affects the aggregate below |
| `raw` | Return the raw call list instead of the aggregate summary |

The default output is an aggregate summary: counts per tool and outcome, an overall not-ok rate, a per-prototype breakdown, and the search queries that most often returned nothing — the fastest way to see where search coverage is actually failing in real usage, not just in a hand-picked example:

```text
## Métricas de uso
(342 chamadas)

| Ferramenta | Total | ok | not_found | ambiguous | no_results | error |
|---|---|---|---|---|---|---|
| search | 128 | 96 | 0 | 6 | 26 | 0 |
| get_component_spec | 74 | 68 | 6 | 0 | 0 | 0 |
| list_components | 30 | 30 | 0 | 0 | 0 | 0 |

**Taxa não-ok:** 14.6%

### Por prototype
| Prototype | Total | Taxa não-ok |
|---|---|---|
| app-v1 | 210 | 12.4% |
| admin | 132 | 18.2% |

### Buscas sem resultado (top)
| Query | Ocorrências |
|---|---|
| botao cinza | 6 |
| modal confirmação | 4 |
```

Set `DESIGN_GRAPH_METRICS_DISABLED=1` to turn logging off entirely.

## Extracted capabilities

- Bundled React/JSX and plain HTML routing
- Visual-function filtering that excludes non-rendering React/Babel runtime internals
- Lexical function scanning that handles destructured parameters, strings, templates and comments
- Semantic screen roles that keep forms, tabs, sections and modals as components unless they are true navigation surfaces
- Screens, semantic sections and reusable components — a section is detected from a padding/margin-heavy container whether that padding comes from an inline `style={{}}` or only from a CSS class the container's own stylesheet resolves
- Typed screen-to-screen and section-to-screen references without synthetic component shells
- Consolidation of same-named source variants without dropping props, JSX, styles or child references
- Component hierarchy in sibling render order, occurrence counts, declared props and defaults
- Default, hover, focus and transition styles, including hover/focus resolved from Tailwind state-variant classes (`hover:`, `focus:`)
- Component and property-level token linkage
- Color, spacing, typography, shadow, radius and CSS-variable tokens
- UI text and semantic text types
- Layout profiles for flex/grid and dimensions, per component and per section selector
- Tailwind utility and custom CSS class resolution, attributed per selector — a section styled by several classes never has their properties flattened into one bag
- CSS classes reused across screens without ever becoming a named component (e.g. `.page-title`) stay discoverable via search and `get_component_spec`, not just classes belonging to a real component
- Every top-level `return` in a component's body is captured (guard clauses / early returns included), not just the first one a naive scan would match
- Fuzzy component matching and Portuguese/English search aliases
- Sanitized JSX markers for lists, conditionals and alternatives
- Extraction-cap truncation surfaced as data on the affected component, not just a build log
- External/library component references (e.g. icon imports) kept visible instead of silently dropped
- Whole-subtree reconstruction of a single complex component (itself plus every descendant) in one call
- Best-effort round-trip validation of agent-written JSX against a component's stored spec
- Incremental builds, exclusive per-database write locks, graph diffs (persisted for MCP, not just CLI), validation and JSON output for CI
- AI-ready JSONL chunks and Markdown reports

## Graph schema

![Schema](./schema.svg)

Editable source: [`diagram.excalidraw`](./diagram.excalidraw).

See [`schema.py`](./src/design_graph/graph/schema.py) for the authoritative node and relationship definitions.

## Makefile shortcuts

After installing the package or activating the development environment:

```bash
make build PROTO=prototype.html
make diff PROTO=prototype.html
make rebuild PROTO=prototype.html

make start
make stop
make restart
make status
make logs

make screens
make tokens
make search Q='button'
make inspect C='SectionCard'
make impact C='SectionCard'
make screen S='RestaurantsPage'

make list-graphs
make remove-db DOC='old-prototype'
make prune-dbs
make clean-graph DB=/path/to/prototype.db
make clean-all
```

Developer-oriented targets include `install-hooks`, `version`, `push` and `release`. Run `make help` for the complete list.

## Repository structure

```text
src/design_graph/
├── cli/          # build, query, status, validation, reports and init
├── core/         # shared models, constants and patterns
├── extraction/   # components, props, screens, sections and chunks
├── graph/        # Kuzu schema, writer, reader and diff
├── mcp/          # stdio server, tool schemas and search
├── parsing/      # source, HTML/JS, CSS classes and token extraction
├── pipeline/     # orchestration, progress and incremental state
├── resources/    # packaged agent skill (see `design-graph init`)
└── paths.py      # GRAPH_DIR, user config and XDG resolution

tests/            # unit and integration tests
docs/             # architecture specs, plans and change records
pyproject.toml    # package metadata and CLI entry points
Makefile          # local workflow shortcuts
schema.svg        # graph schema diagram
```

## License

MIT
