Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ The extension display name is **ERD Studio** (package name `erd-studio`).

### Directory Structure

ERD domain files live at `{project_root}/erd-studio/{layer}/{domain}.json`. Each file is a unified domain containing the logical stage data. The custom editor activates for files matching:
- `**/erd-studio/*/*.json`
ERD domain files live at `{project_root}/.erd-studio/{layer}/{domain}.json`. Each file is a unified domain containing the logical stage data. The custom editor activates for files matching:
- `**/.erd-studio/*/*.json`

The base directory is configurable via the `dbtSemantic.semanticDir` setting (default: `erd-studio`).
The base directory is configurable via the `dbtSemantic.semanticDir` setting (default: `.erd-studio`).

```
erd-studio/
.erd-studio/
├── layers.json
├── templates/
├── silver/
Expand Down Expand Up @@ -80,7 +80,7 @@ manifest.json ─→ ManifestService ─→ buildPhysicalDomain() ─→ Display
```

1. **ManifestService** stream-parses `target/manifest.json` (handles 40MB+ files via `stream-json`). Extracts model nodes, relationship test nodes (`relationships`, `relationships_where`, custom), `unique` tests, and `unique_combination_of_columns` tests.
2. **DomainService** reads unified domain JSON from `erd-studio/{layer}/*.json` → `UnifiedDomain`, then extracts a stage section via `getDomainStage()` → `DisplayDomain`
2. **DomainService** reads unified domain JSON from `.erd-studio/{layer}/*.json` → `UnifiedDomain`, then extracts a stage section via `getDomainStage()` → `DisplayDomain`
3. For physical stage: `DomainService.buildPhysicalDomain()` derives models from logical filtered by manifest. **Relationships are derived entirely from manifest relationship tests** (not copied from logical). Cardinality is inferred from manifest `unique`/`unique_combination_of_columns` tests (no unique test = "many" side). Relationships are scoped to models within the domain to prevent conformed dimensions from pulling in external edges. See `derivePhysicalRelationships()` in `domainService.ts`.
4. **DiscrepancyService** compares two `DisplayDomain` objects to produce a `DiscrepancyReport`
5. Extension sends `domainLoaded` / `stageData` message to webview
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ When design and warehouse disagree, ERD Studio generates a JSON plan mapping eve

1. **Install** ERD Studio from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=liamwynne.erd-studio).
2. **Open your dbt project** in VS Code and click the ERD Studio icon in the Activity Bar.
3. **Initialize** — follow the prompt to create the `erd-studio/` folder.
3. **Initialize** — follow the prompt to create the `.erd-studio/` folder.
4. **Install the AI harness** — <kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> &rarr; `dbt: Install AI Coding Harness`.
5. **Create a domain** — <kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> &rarr; `dbt: Create Semantic Domain`.
6. **Tell your AI what to build** — describe the scope, point it at bronze, let it draft the ERD. Review on the canvas. Prompt it to generate the dbt code.
Expand All @@ -120,7 +120,7 @@ The harness installs the right file for your assistant:
| <img src="https://img.shields.io/badge/Google_Gemini-4285F4?style=for-the-badge&logo=googlegemini&logoColor=white" alt="Google Gemini" /> | `.gemini/styleguide.md` |
| <img src="https://img.shields.io/badge/OpenAI_Codex-412991?style=for-the-badge&logo=openai&logoColor=white" alt="OpenAI Codex" /> | `AGENTS.md` |

The baseline harness teaches your assistant the domain format and sync workflow, with a guard that blocks AI edits to `erd-studio/` until the spec is loaded. Layer your own skills, prompts, and style guides on top so the generated dbt reflects your team's conventions.
The baseline harness teaches your assistant the domain format and sync workflow, with a guard that blocks AI edits to `.erd-studio/` until the spec is loaded. Layer your own skills, prompts, and style guides on top so the generated dbt reflects your team's conventions.

<br />

Expand Down Expand Up @@ -148,7 +148,7 @@ After setup, your dbt project gets:

```
your-dbt-project/
├── erd-studio/
├── .erd-studio/
│ ├── layers.json # Layer definitions (silver, gold, etc.)
│ ├── logical-models/ # Model definitions — one YAML per table
│ │ ├── dim_customer.yml
Expand All @@ -172,7 +172,7 @@ Layer folders match what's in `layers.json`. You're not stuck with `silver`/`gol

### Anatomy of a model file

`erd-studio/logical-models/dim_customer.yml`:
`.erd-studio/logical-models/dim_customer.yml`:

```yaml
name: dim_customer
Expand Down Expand Up @@ -208,7 +208,7 @@ columns:

### Anatomy of a domain file

`erd-studio/silver/orders.json`:
`.erd-studio/silver/orders.json`:

```json
{
Expand Down Expand Up @@ -282,7 +282,7 @@ The spec teaches the AI:
- The naming conventions (`dim_`, `fct_`, `ref_`, `brg_` prefixes for dimensions, facts, references, bridges)
- The full field reference (every key documented above)

For Claude Code, the install also adds a **PreToolUse hook** at `.claude/settings.local.json` that blocks the first edit to any `erd-studio/` file in a session until the assistant has loaded the skill. No half-read spec, no drift.
For Claude Code, the install also adds a **PreToolUse hook** at `.claude/settings.local.json` that blocks the first edit to any `.erd-studio/` file in a session until the assistant has loaded the skill. No half-read spec, no drift.

The harness embeds a version marker. When you upgrade ERD Studio, the extension detects out-of-date harness files and prompts to update.

Expand Down Expand Up @@ -317,7 +317,7 @@ So `dbt run --selector domain_silver_orders` refreshes every model in your "orde

Type comparison normalises common aliases (`varchar`/`string`, `int`/`integer`, `timestamp_ntz`/`timestamp`, etc.) so equivalent types don't show as mismatches.

For unrecoverable drift, the AI can generate a **sync plan** at `erd-studio/.sync-plan.json` — every discrepancy mapped to a concrete action (`add-to-logical`, `update-type-in-physical`, etc.). You pick the source of truth per item; the AI executes it.
For unrecoverable drift, the AI can generate a **sync plan** at `.erd-studio/.sync-plan.json` — every discrepancy mapped to a concrete action (`add-to-logical`, `update-type-in-physical`, etc.). You pick the source of truth per item; the AI executes it.

### Editing by hand vs by AI vs on the canvas

Expand Down
18 changes: 9 additions & 9 deletions docs/semantic-domain-json-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

## File Layout

Domain files are organized by stage and layer under the `erd-studio/` directory:
Domain files are organized by stage and layer under the `.erd-studio/` directory:

```
erd-studio/
.erd-studio/
conceptual/
{layer}/
{domain}.json
Expand All @@ -22,7 +22,7 @@ erd-studio/

There are three stages: **conceptual**, **logical**, and **physical**. Conceptual and logical have persisted JSON files. Physical has no files -- it is derived at runtime by merging the logical domain with the dbt manifest.

## Domain File (`erd-studio/{stage}/{layer}/{domain}.json`)
## Domain File (`.erd-studio/{stage}/{layer}/{domain}.json`)

### Top-Level Schema

Expand Down Expand Up @@ -187,7 +187,7 @@ When generating new domains, omit `positions` -- the extension auto-layouts on f

### Conceptual Stage

Conceptual domains capture high-level entity design. Files live at `erd-studio/conceptual/{layer}/{domain}.json`.
Conceptual domains capture high-level entity design. Files live at `.erd-studio/conceptual/{layer}/{domain}.json`.

- Models can omit `columns` entirely (entity-level modelling only).
- When columns are present, `dataType` can be `""` if the type is not yet decided.
Expand All @@ -196,7 +196,7 @@ Conceptual domains capture high-level entity design. Files live at `erd-studio/c

### Logical Stage

Logical domains capture detailed column-level design. Files live at `erd-studio/logical/{layer}/{domain}.json`.
Logical domains capture detailed column-level design. Files live at `.erd-studio/logical/{layer}/{domain}.json`.

- Models should have full `columns` arrays with `dataType` and `description` populated.
- FK relationships should specify concrete column references.
Expand All @@ -207,7 +207,7 @@ Logical domains capture detailed column-level design. Files live at `erd-studio/

Physical has no files. It is derived at runtime by merging the logical domain with the dbt manifest. Do not create files for the physical stage.

## Layers File (`erd-studio/layers.json`)
## Layers File (`.erd-studio/layers.json`)

Defines the medallion architecture layers available in the project.

Expand Down Expand Up @@ -248,7 +248,7 @@ Defines the medallion architecture layers available in the project.

**Known layer defaults:** `bronze` (`#cd7f32`, creatable: false), `silver` (`#a0a0a0`, creatable: true), `gold` (`#d4a800`, creatable: true).

## Model Templates (`erd-studio/templates/{id}.json`)
## Model Templates (`.erd-studio/templates/{id}.json`)

Templates provide preset columns when creating new models.

Expand Down Expand Up @@ -303,7 +303,7 @@ Templates provide preset columns when creating new models.

A conceptual domain focuses on entities, relationships, and design intent. Columns are optional or minimal.

File path: `erd-studio/conceptual/silver/sales.json`
File path: `.erd-studio/conceptual/silver/sales.json`

```json
{
Expand Down Expand Up @@ -403,7 +403,7 @@ File path: `erd-studio/conceptual/silver/sales.json`

The same sales domain at the logical stage with full column definitions, data types, and metadata flags.

File path: `erd-studio/logical/silver/sales.json`
File path: `.erd-studio/logical/silver/sales.json`

```json
{
Expand Down
2 changes: 1 addition & 1 deletion docs/vision.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Claude generates a complete semantic domain:
- Relationships with cardinalities
- Column definitions with data types

The design is written to a `.json` file in `erd-studio/logical/`.
The design is written to a `.json` file in `.erd-studio/logical/`.

### 3. Data Engineer Reviews in VS Code
**Actor:** Human
Expand Down
2 changes: 1 addition & 1 deletion forge-app/static/config/build/assets/index-Crna0F04.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion forge-app/static/config/src/ConfigPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export default function ConfigPanel() {
<input
style={styles.input}
type="text"
placeholder="https://github.com/owner/repo/blob/main/erd-studio/silver/domain.json"
placeholder="https://github.com/owner/repo/blob/main/.erd-studio/silver/domain.json"
value={config.githubUrl}
onChange={(e) => onUrlChange(e.target.value)}
/>
Expand Down
2 changes: 1 addition & 1 deletion forge-app/static/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function InlineConfig({ onSaved }: { onSaved: () => void }) {
<input
style={inputStyle}
type="text"
placeholder="https://github.com/owner/repo/blob/main/erd-studio/silver/domain.json"
placeholder="https://github.com/owner/repo/blob/main/.erd-studio/silver/domain.json"
value={url}
onChange={(e) => { setUrl(e.target.value); setStatus(null); }}
/>
Expand Down
8 changes: 4 additions & 4 deletions mcp-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ These editors support MCP via their respective config files. Use the same `comma

## Tools

All tools take a `project_path` argument: the **absolute path** to the dbt project root (the directory containing `dbt_project.yml`). The project should also contain an `erd-studio/` directory created by the [ERD Studio VS Code extension](https://marketplace.visualstudio.com/items?itemName=liamwynne.erd-studio).
All tools take a `project_path` argument: the **absolute path** to the dbt project root (the directory containing `dbt_project.yml`). The project should also contain a `.erd-studio/` directory created by the [ERD Studio VS Code extension](https://marketplace.visualstudio.com/items?itemName=liamwynne.erd-studio).

| Tool | Returns |
|---|---|
| `list_domains` | All ERDs grouped by layer. Filter optional by `layer`. |
| `read_domain` | Full domain: models + columns + relationships + cardinality + rationale. |
| `list_models` | All logical model definitions from `erd-studio/logical-models/*.yml`. |
| `list_models` | All logical model definitions from `.erd-studio/logical-models/*.yml`. |
| `read_model` | Single logical model with column-level metadata, grain, SCD types, rationale. |
| `list_manifest_models` | Models from `target/manifest.json` (what dbt actually built), with unique/relationship test coverage. Filter optional by `name_contains`. |
| `get_editor_setup` | Returns install instructions for the ERD Studio VS Code extension. Use this when the user wants to edit, design, or build (this MCP server is read-only). |

All tools are read-only. If the project hasn't been initialized with an `erd-studio/` directory yet, list-tools return empty results with a `tip` field pointing to the install path; read-tools throw a friendly error doing the same. Either way the AI naturally surfaces the extension install path to the user.
All tools are read-only. If the project hasn't been initialized with a `.erd-studio/` directory yet, list-tools return empty results with a `tip` field pointing to the install path; read-tools throw a friendly error doing the same. Either way the AI naturally surfaces the extension install path to the user.

## What the AI gets

Expand All @@ -91,7 +91,7 @@ So when you ask the AI *"propose a column to add to `dim_customer`"*, it sees no

## Without ERD Studio yet

If your dbt project doesn't have an `erd-studio/` directory yet:
If your dbt project doesn't have a `.erd-studio/` directory yet:

1. Install the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=liamwynne.erd-studio)
2. In VS Code: Command Palette → `dbt: Set Up Semantic Domains Directory`
Expand Down
4 changes: 2 additions & 2 deletions mcp-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ Install: https://marketplace.visualstudio.com/items?itemName=liamwynne.erd-studi
Or call the \`get_editor_setup\` tool for canonical install instructions.

Every tool takes \`project_path\`: the absolute path to the dbt project root (the
directory containing dbt_project.yml). If the project hasn't been initialized with an
erd-studio/ directory yet, list-tools return empty results with a \`tip\` field pointing
directory containing dbt_project.yml). If the project hasn't been initialized with a
.erd-studio/ directory yet, list-tools return empty results with a \`tip\` field pointing
to the install path; read-tools throw a friendly error doing the same.

Typical inspection workflow:
Expand Down
4 changes: 2 additions & 2 deletions mcp-server/src/lib/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export const EXTENSION_REPO_URL = 'https://github.com/liam-machine/erd-studio';
* surfaces the install path to the user.
*/
export const NOT_INITIALIZED_TIP =
"This project doesn't have an erd-studio/ directory yet. To start designing ERDs, " +
"This project doesn't have a .erd-studio/ directory yet. To start designing ERDs, " +
`install the ERD Studio VS Code extension: ${EXTENSION_MARKETPLACE_URL} ` +
"Then run Command Palette → 'dbt: Set Up Semantic Domains Directory'. " +
"The extension also installs an AI coding skill (.claude/skills/erd-studio/SKILL.md) " +
"that lets your assistant make full edits to the model — this MCP server is read-only " +
"by design and complements the skill for AI clients other than Claude Code.";

/**
* Returns true if the project has an erd-studio/ directory.
* Returns true if the project has a .erd-studio/ directory.
*/
export function isInitialized(projectPath: string, semanticDir: string): boolean {
return fs.existsSync(path.join(projectPath, semanticDir));
Expand Down
2 changes: 1 addition & 1 deletion mcp-server/src/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface Services {
manifestService: ManifestService;
}

const SEMANTIC_DIR = 'erd-studio';
const SEMANTIC_DIR = '.erd-studio';

export function resolveProjectPath(input: string): string {
const resolved = path.resolve(input);
Expand Down
2 changes: 1 addition & 1 deletion mcp-server/src/tools/get_editor_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const get_editor_setup = {
'dbt: Set Up Semantic Domains Directory',
'```',
'',
'This creates `erd-studio/` with `layers.json`, `logical-models/`, and `templates/`.',
'This creates `.erd-studio/` with `layers.json`, `logical-models/`, and `templates/`.',
'',
'## 3. Install the AI coding harness (writes the skill file)',
'',
Expand Down
2 changes: 1 addition & 1 deletion mcp-server/src/tools/list_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const list_models = {
config: {
title: 'List logical models',
description:
'List all logical model definitions in erd-studio/logical-models/. ' +
'List all logical model definitions in .erd-studio/logical-models/. ' +
'Each model is one table (dimension, fact, bridge, etc.) reusable across domains. ' +
'Returns model names + light metadata. Call read_model for full column-level detail.',
inputSchema: {
Expand Down
2 changes: 1 addition & 1 deletion mcp-server/test-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PROJECT_PATH = path.resolve(__dirname, '../test/fixtures/dbt-project');
const SERVER = path.resolve(__dirname, 'dist/index.js');

// Build a temporary "uninitialized" project — has dbt_project.yml but no erd-studio/
// Build a temporary "uninitialized" project — has dbt_project.yml but no .erd-studio/
const UNINIT_PATH = fs.mkdtempSync(path.join(os.tmpdir(), 'erd-mcp-uninit-'));
fs.writeFileSync(path.join(UNINIT_PATH, 'dbt_project.yml'), "name: 'test_uninit'\nversion: '1.0.0'\n");

Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,21 @@
"viewsWelcome": [
{
"view": "dbtSemantic.domainTree",
"contents": "No ERD domains found.\n\nERD Studio lets you design your data warehouse visually across two stages:\n\n**Logical** — columns, data types, FK keys, and design rationale\n**Physical** — read-only view of what's built in dbt\n\n[Set Up ERD Studio](command:dbtSemantic.setupSemanticDirectory)\n\nCreates the erd-studio directory structure and your first domain.\n\n[Install AI Coding Harness](command:dbtSemantic.installCodingHarness)\n\nAdd ERD Studio schema reference to Claude, Copilot, Gemini, or Codex."
"contents": "No ERD domains found.\n\nERD Studio lets you design your data warehouse visually across two stages:\n\n**Logical** — columns, data types, FK keys, and design rationale\n**Physical** — read-only view of what's built in dbt\n\n[Set Up ERD Studio](command:dbtSemantic.setupSemanticDirectory)\n\nCreates the .erd-studio directory structure and your first domain.\n\n[Install AI Coding Harness](command:dbtSemantic.installCodingHarness)\n\nAdd ERD Studio schema reference to Claude, Copilot, Gemini, or Codex."
},
{
"view": "dbtSemantic.modelLibrary",
"contents": "No logical models found.\n\nModels are stored as YAML files in erd-studio/logical-models/.\n\nAdd models to a domain using the ERD editor to create them here."
"contents": "No logical models found.\n\nModels are stored as YAML files in .erd-studio/logical-models/.\n\nAdd models to a domain using the ERD editor to create them here."
}
],
"customEditors": [
{
"viewType": "dbtSemantic.domainEditor",
"displayName": "Semantic Domain Editor",
"selector": [
{
"filenamePattern": "**/.erd-studio/*/*.json"
},
{
"filenamePattern": "**/erd-studio/*/*.json"
}
Expand Down Expand Up @@ -260,7 +263,7 @@
},
"dbtSemantic.semanticDir": {
"type": "string",
"default": "erd-studio",
"default": ".erd-studio",
"description": "Relative path to ERD domain files within the project"
}
}
Expand Down
Loading
Loading