dbt-doctor
v0.3.4
Published
Static analysis and health checks for dbt projects. Find missing docs, missing tests, schema drift risks, stale models, DAG maintainability issues, and CI quality gate failures.
Maintainers
Readme
See CHANGELOG.md for release history.
[!NOTE] dbt-doctor is a clone of React Doctor by Million. React Doctor is published under the MIT License; this project clones and adapts its ideas (CLI, scoring, agent tooling) for dbt and remains MIT as well - see the monorepo LICENSE. Thank you to the React Doctor maintainers for the original work.
Static analysis and health checks for dbt projects.
Find missing docs, missing tests, schema drift risks, stale models, DAG maintainability issues, and CI quality gate failures.
Site & demo →
dbt-doctor
Static analysis and health checks for dbt projects.
dbt-doctor scans your dbt project and finds maintainability issues before they become production debt:
- missing model documentation
- missing dbt tests
- weak docs/test coverage
- schema drift risks
- stale models
- source freshness gaps
- naming convention issues
- oversized DAG areas
- governance and ownership gaps
Use it locally, in pre-commit, or as a CI quality gate for analytics engineering teams.
Requirements
- Node.js 22+
- dbt layout: a
dbt_project.ymlat the project root (or userootDirin config for monorepos). - SQLFluff (recommended):
pip install sqlfluff sqlfluff-templater-dbt— omit if you setskipSqlfluff: trueor usecustomRulesOnly(custom rules only).
Quick start
From your dbt project root:
npx dbt-doctor@latestYou get a score (75+ Great, 50–74 Needs work, under 50 Critical) and grouped findings. The score counts unique rules that fired (each plugin/rule once), not every occurrence.
Install for coding agents
Install the dbt-doctor skill into detected agents (Cursor, Claude Code, Codex, and others):
npx dbt-doctor@latest installUse --yes to skip prompts. Same behavior as the website’s curl installer, which delegates to this command.
GitHub Actions
A composite action lives in this repo. Example workflow:
name: dbt Doctor
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
pull-requests: write # for sticky PR comments
jobs:
dbt-doctor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # required when using diff
- uses: northgraindata/dbt-doctor@main
with:
diff: main
github-token: ${{ secrets.GITHUB_TOKEN }}Notable inputs: directory, verbose, project, diff, github-token, fail-on (error / warning / none), offline, annotations, setup-sqlfluff (default installs SQLFluff + dbt templater), node-version. See action.yml for full descriptions.
With github-token on pull_request, findings are posted as a sticky PR comment. The action exposes a score output (0–100) for follow-up steps (e.g. score floor checks).
Bare CLI in CI (no marketplace action):
- run: npx dbt-doctor@latest --fail-on warningRemember to install SQLFluff in that job unless you rely on skipSqlfluff / customRulesOnly.
PR blocking and exit codes
--fail-on <level>—error(default),warning, ornone. Applies to diagnostics that pass theciFailuresurface (see configuration).--diff <base>— only files changed vsmain/masteror your branch name; good for regression-only gates.--staged— only staged files (pre-commit). Do not combine with--diff.
Annotations (--annotations) and PR comments (github-token) are display-only for exit code purposes unless paired with --fail-on.
Configuration
Add a .dbt-doctor props file at the project root (KEY=value, # comments — like .env). CLI flags override config.
Presets (default | strict | enterprise) control which rule tags run and default CI strictness. Omit preset to run all 122 rules. See presets.
Example .dbt-doctor:
# Full catalog + governance (all 122 rules including native SQL style)
preset=enterprise
score_mode=files
fail_on=warning
# Silence noisy rules on legacy paths
ignore.rules=dbt-doctor/no-select-star
ignore.files=models/legacy/**
skip_sqlfluff=false
offline=falseCLI overrides: --preset enterprise, --score-mode files, --fail-on warning.
ignore.rules— silence those rules everywhere.ignore.files— silence all rules on matching paths (use sparingly).ignore.overrides— silence listed rules only on matched files.ignore.tags— silence every rule with a given tag (e.g. design-style hints if you use them).rootDir— if config lives above the dbt project, point here (path relative to the config file).skipSqlfluff/customRulesOnly— run only built-in dbt-doctor rules (no Python).adoptExistingSqlfluffConfig— pick up.sqlfluff/pyproject.tomlwhen present (defaulttrue).offline— skip the remote score API and share link; score still uses the local formula.surfaces— tunecli,prComment,score, andciFailureindependently (include/exclude by tag, category, orplugin/ruleid).rules/categories— severity overrides (error/warn/off) for rule ids and rule categories.
Rule catalog: Rules reference (generated from packages/dbt-doctor-rules/src/rules/).
Inline suppressions (SQL / YAML)
Use dbt-doctor-disable-next-line on the line above the finding. SQL:
-- dbt-doctor-disable-next-line dbt-doctor/no-select-star
select * from {{ ref('stg_orders') }}YAML / Jinja can use # or -- style comments depending on context. Use dbt-doctor --explain path/to/file.sql:42 (or --why) to see why a rule fired or why a suppression did not apply.
Respect for other tools: .gitignore is honored. Optional --no-respect-inline-disables neutralizes eslint-disable / oxlint-disable markers for audit-style runs (dbt-doctor’s own inline directives are controlled separately).
CLI reference
Usage: dbt-doctor [directory] [options]
Options:
-v, --version display the version number
--lint / --no-lint enable or skip linting
--verbose show every rule and per-file details
--score output only the score
--json structured JSON report on stdout
--json-compact with --json, minimal whitespace
-y, --yes skip prompts; scan all workspace packages
--full full scan (overrides diff in config / CLI)
--project <name> workspace package(s), comma-separated
--diff [base] only files changed vs base branch
--staged only git-staged files
--offline skip score API and share URL (local score)
--fail-on <level> error | warning | none
--annotations GitHub Actions annotation format
--pr-comment tune output for sticky PR comments
--explain <file:line> why a rule fired or suppression missed
--why <file:line> alias for --explain
-h, --help display help
Commands:
install|setup install agent skills (--yes, --dry-run, -c cwd)Scoring
Formula (same locally and on the default API):
score = round(100 − (unique error rules × 1.5) − (unique warning rules × 0.75))
Labels: Great ≥ 75, Needs work ≥ 50, Critical < 50.
Scores can shift when new rules ship — pin dbt-doctor in CI if you need stable numbers.
Diff and staged modes
--diff [base]— changed files vs branch (or"diff": true/"diff": "develop"in config).--staged— index-only; for pre-commit hooks.--full— forces a full scan.
Interactive runs may prompt to scan only changes; CI and --json skip that prompt.
Node.js API
import { diagnose, toJsonReport, summarizeDiagnostics } from "dbt-doctor/api";
const result = await diagnose("./path/to/dbt-project", { offline: false });
console.log(result.score); // { score, label } | null in edge cases
console.log(result.diagnostics);
console.log(result.project); // ProjectInfo: adapter, model paths, etc.diagnose accepts options such as lint, offline, includePaths, respectInlineDisables. See packages/dbt-doctor/src/api.ts.
Monorepo & contributing
This package is part of the dbt-doctor monorepo. To hack on the CLI locally:
git clone https://github.com/northgraindata/dbt-doctor.git
cd dbt-doctor
pnpm install
pnpm build
node packages/dbt-doctor/bin/dbt-doctor.js /path/to/your/dbt-projectIssues: github.com/northgraindata/dbt-doctor/issues
Who made dbt Doctor?
dbt Doctor is built and maintained by Northgrain Data, a data engineering agency for modern dbt and Snowflake teams. We design, build, test, and improve reliable data systems — and publish open-source tools based on what works in production.
License
This package is MIT - LICENSE. Upstream credit: dbt-doctor is a clone of Million / React Doctor (see the note at the top of this README).


