Contributing to dbterd-vscode
April 25, 2026 · View on GitHub
Thanks for your interest in making dbt ERDs less painful to look at. This repo is built for agentic development — Claude Code is a first-class contributor alongside humans. You can work either way; the tooling is the same.
Code of Conduct
Be kind, be specific, assume good faith. We follow the spirit of the Contributor Covenant. If something feels off, open an issue or reach out to the maintainer privately.
Ways to Contribute
- Bug reports — with a minimal reproducing dbt project if possible
- Feature requests — describe the ERD workflow you want, not the implementation
- Pull requests — see below; start small if it's your first time in the repo
- Docs — humanized prose with the occasional dry joke is encouraged
Development Setup
You need:
git clone https://github.com/datnguye/dbterd-vscode.git
cd dbterd-vscode
task install # installs all three workspaces
task dev # runs server + webview + extension watchers
# Hit F5 in VS Code to launch the Extension Development Host.
task --list shows everything else.
Repo Layout
dbterd-vscode/
├── extension/ # VS Code extension host (TypeScript, esbuild-bundled)
├── webview/ # React + @xyflow/react UI, bundled into extension/media/
├── server/ # FastAPI shim around the dbterd Python API (uv-managed)
└── .claude/ # Agents, skills, commands, hooks — see "Agentic Development"
Each workspace is independently buildable. The extension bundles webview/dist/
into extension/media/ at package time.
The /erd Contract
All three layers share one JSON shape. The Pydantic models in
server/src/dbterd_server/schemas.py are the source of truth — TypeScript
types in webview/src/types/erd.ts are generated, never hand-edited.
Before modifying the contract in any layer, read
.claude/skills/erd-contract/SKILL.md and update the other two layers in the
same PR. Drift between layers is the single most likely source of bugs.
After changing schemas.py, regenerate the TS types:
task sync-contract
Daily Workflows
| Goal | Task | Slash command |
|---|---|---|
| Install all three workspaces | task install | — |
| Run all watchers (server/webview/ext) | task dev | /dev |
| Run every test suite | task test | /test |
| Lint + typecheck | task lint | — |
Build the .vsix locally | task package | /package |
| Regenerate TS types from schemas | task sync-contract | /sync-contract |
| Scaffold a new endpoint | — | /new-endpoint |
| Cut a release | — | /release |
Coding Conventions
Python (server/)
uv run ruff format && uv run ruff checkmust pass.- 100% test coverage — enforced in CI.
- No relative imports. All imports at module top.
- One class per file (exception: related exception classes may share a file).
- Specific exception types in
try/except, never bareexcept:orexcept Exception:. - Pydantic v2. Use
model_dump(), notdict(). - Routes are
async defeven when the body is synchronous — consistent surface. - No nested functions or classes; define at module level.
TypeScript (extension/ and webview/)
npm run typecheckmust pass in both workspaces.- No
any. Model webview↔extension messages with a discriminated union. - The strict
tsconfig.jsonis the lint budget — there's no ESLint layer (yet). - For React Flow nodes: module-scoped
nodeTypesregistry,React.memocomponents, typing viaNode<Data, 'typeKey'>+NodeProps<NodeType>. See.claude/skills/reactflow-nodes/SKILL.md. - Theme with VS Code CSS variables (
--vscode-*), not hardcoded colors.
General
- No backwards-compatibility shims unless explicitly requested — this is a fresh codebase.
- Don't add features, refactor, or introduce abstractions beyond what the task requires.
- Default to no code comments. Only add a comment when the why is non-obvious (a hidden constraint, a workaround for a known bug). Well-named identifiers already explain the what.
Tests
Run everything at once:
task test
Or individually:
task test:server # pytest, 100% coverage gate
task test:webview # vitest
task test:extension # typecheck (no runtime tests yet)
CI runs all three on every PR.
Commit & PR Guidelines
- Keep commits focused and small. Prefer a new commit over amending a pushed one.
- Commit message style: imperative mood, present tense ("Add X", not "Added X" / "Adds X"). A short subject plus a body explaining why if the change isn't obvious.
- PRs should explain the why in the description and reference any related issue. GitHub's auto-generated release notes pick up PR titles — make them readable.
- If your change touches the
/erdcontract, the PR must include updates to all three layers. - CI must be green before merge.
Releases
Release-driven — creating a GitHub Release creates the tag and triggers CD:
gh release create v0.1.0 --generate-notes --title "v0.1.0"
# pre-release (skips marketplace publish):
gh release create v0.1.0-rc.1 --generate-notes --prerelease --title "v0.1.0-rc.1"
CD (.github/workflows/release.yml) builds the webview, packages the .vsix,
uploads it to the Release, and publishes to the VS Code Marketplace (skipped
for pre-releases). Release notes come from --generate-notes — we don't keep
a CHANGELOG.md.
Maintainers can also invoke the release-manager agent or /release slash
command from Claude Code, which handles pre-flight checks and version bumping.
Agentic Development
This repo is wired up for Claude Code. The mental model is three tools:
- Agents (
.claude/agents/) — delegated workers with their own context window. One per layer (extension-dev,webview-dev,server-dev) plus arelease-manager. Cross-layer refactors: delegate to the relevant per-layer agent. - Skills (
.claude/skills/) — knowledge Claude pulls in automatically when relevant.erd-contractis the important one: it keeps all three layers honest when the JSON shape changes. - Commands (
.claude/commands/) — user-invoked workflows:/dev,/test,/package,/release,/new-endpoint,/sync-contract. - Hooks (
.claude/hooks/) — shell scripts the harness runs on tool events.block-secrets.shis a PreToolUse guard that prevents reads/writes/greps against.env,*.pem,*.key,id_rsa, etc.post-edit-check.shruns a fast linter on the file just edited.
Agent memory
Per-layer agents use memory: project — their scratchpads are committed to
this repo and shared with teammates. The release-manager uses memory: local
— personal, gitignored. Because project memory lands in git:
- Never write secrets, tokens, or customer data into agent memory.
- Never write information that's only true for your local setup (ports you picked, paths to personal dbt projects).
- Do write durable knowledge: contract versions, architectural decisions, dbterd quirks you discovered, recurring pitfalls.
Agents curate their own MEMORY.md index — don't hand-edit it.
External docs via context7
context7 is wired in .mcp.json. Prefer it over guessing from training data
when library behavior matters (reactflow, VS Code API, FastAPI, dbterd).
Reporting Issues
- Bug: include VS Code version, OS, dbt adapter, and a minimal repro if you can. The smaller the manifest.json, the faster the fix.
- Security: don't file a public issue. Email the maintainer directly
(see
package.json) or open a GitHub Security Advisory.
Thanks for contributing.