CLI REFERENCE
dvt run
Extract sources, transform models, load to targets.
dvt run # Run all models (incremental) dvt run --full-refresh # Rebuild everything from scratch dvt run --select model_name # Run specific model dvt run --select tag:finance # Run models with tag dvt run --target prod # Run against production target
dvt build
Seeds + models + snapshots + tests in DAG order.
dvt build # Full build dvt build --full-refresh # Clean rebuild dvt build --select +model_name+ # Model with upstream/downstream
dvt seed
Bulk-load seed files via Sling (10-100x faster than dbt). Reads every Sling-supported format — CSV, TSV, Parquet, JSON/JSONL, Avro, Arrow, XLSX — and --target lands them on ANY output in profiles.yml: databases get tables, bucket outputs (S3/GCS/Azure) get Parquet objects. Column names are normalized to snake_case by default ('SKU Code' → sku_code) — the one casing every engine accepts — so reference seed columns in snake_case in your models, or override with --column-casing {source,snake,upper,lower}.
dvt seed # Load all seeds (any format) dvt seed --select seed_name # Load specific seed dvt seed --full-refresh # Drop and recreate tables dvt seed --target snowflake_prod # Seed a different engine dvt seed --target data_lake # Seed a bucket (lands as Parquet)
dvt test
Run data tests on target.
dvt test # All tests dvt test --select model_name # Tests for specific model
dvt show
Preview a model or inline query against the default target (dbt passthrough — the query executes on the engine, not locally).
dvt show --select model_name # Preview model output dvt show --inline "SELECT 1" # Ad-hoc query dvt show --limit 100 # Limit rows
dvt sync
Self-healing environment bootstrap. Installs drivers, DuckDB extensions, Sling — and manages adapter environments: the default target's dbt adapter lives in your environment; every other adapter gets its own isolated environment under .dvt/ (its own dbt-core, zero version conflicts). Switch the default target and sync reshuffles everything automatically. Requires uv — and installs it for you if missing, so you never have to think about it.
dvt sync # Bootstrap + reconcile everything
dvt debug
Test connections on both layers: the dbt adapter and Sling.
dvt debug # Test the default target dvt debug --target snowflake_dev # Test a specific target dvt debug --all # Full matrix: every output, both layers
dvt init
Scaffold a DVT project that builds in seconds — no warehouse, no prompts. Bare dvt init turns the CURRENT folder into the project, named after the folder; dvt init <name> creates the folder first. Either way you get a DuckDB default target out of the box: an entry under the project name is merged into dbt's own profiles.yml (~/.dbt, $DBT_PROFILES_DIR honored — never clobbered, timestamped .bak), pointing at a visible <name>.duckdb file at the project root. A starter seed, model, and doctrine-ready sources.yml mean dvt build is green immediately — and because DuckDB is the default, everything you write is one dialect, federated models included. When you outgrow the laptop, dvt flip-target-to migrates you.
dvt init # this folder becomes the project dvt init sales_dwh # create sales_dwh/ and scaffold it cd sales_dwh && dvt build # green in seconds, no warehouse
dvt flip-target-to
Change the default target without abandoning your models. Runs config-checkup FIRST (scope decisions are only as good as the configs), then makes <target> the new default and transpiles every default-target model to the new engine's dialect — deterministically, via the same SQLGlot machinery federation trusts on every run, no AI tokens spent. Jinja survives byte-for-byte; federated models (always DuckDB by doctrine) and target-pinned models are never touched. Anything the deterministic pass refuses gets ONE AI rescue attempt through your gateway (validated before it touches disk), or an interactive prompt: open it in your editor, or skip. The default target only flips when every model made it — the run ends by asking, not by demanding --force. --silent restores the plain ledger (automatic off-TTY). Also Martin's /flip-target-to: pick a target, read the dry-run card, APPLY.
dvt flip-target-to databricks_prod --dry-run # the ledger, nothing written dvt flip-target-to databricks_prod # checkup → flip → aftercare dvt flip-target-to pg_dev -s 'staging/*' # scope with patterns dvt flip-target-to pg_dev --no-ai --silent # deterministic, no prompts
dvt config-checkup
Make model configs tell the truth — zero AI. Applies the engine's own federation classification (foreign sources + the ref cascade) to your FILES: a standard materialization whose dependencies are federated in fact becomes f_table/f_incremental automatically (the engine was already coercing it at runtime — now the config says so), materialization typos snap to their one legal neighbour, and everything ambiguous becomes an interactive CHOICE — target= on a standard model offers federate-or-drop, a source missing meta.connection offers your profile's actual connections, needless federation offers convert-in-place (SQL transpiled to the default's dialect) or keep. Enter always means skip; every write keeps a .bak; --silent (or any non-TTY run) gives the plain report. flip-target-to runs this automatically. Also /config-checkup in Chat with Martin — each suggestion is a card with one button per resolution.
dvt config-checkup --dry-run # the ledger, nothing written dvt config-checkup # fix the provable, ask about the rest dvt config-checkup --silent # no prompts (cron/CI behavior) dvt config-checkup -s 'silver_*' # scope with patterns
dvt clean-bak-files
When you trust the fixes and the flips: remove the timestamped .bak files DVT's fixers left in your model folders (config-checkup, flip-target-to, applied Martin proposals). Only DVT's own <name>.YYYYmmdd-HHMMSS.bak pattern is touched — a hand-made .bak survives. Also /clean-bak-files in Chat with Martin, behind APPLY.
dvt clean-bak-files --dry-run # list what would go dvt clean-bak-files # remove them
dvt docs
Generate and serve the data catalog with cross-engine metadata.
dvt docs generate # Generate catalog dvt docs serve # Serve UI on localhost dvt docs serve --port 9090 # Custom port
dvt martin
Martin in your terminal — the same agent as Chat with Martin (:46104), because it is the same agent: tools, streaming MARTIN'S WORK, plan/execute modes, and every proposal gated behind an [a]pply / [d]ismiss prompt through the same file jail and verb whitelist. /model picks from your gateway's live catalog; /sql runs the guarded no-AI lane; @file mentions are read by Martin's own project-jailed reader.
dvt martin # the agent, in this terminal dvt martin --plan # start in plan mode dvt martin --model claude-sonnet-5 # pin a gateway model
dvt generate-sources
The project kick-start, straight from the terminal — no UI, no AI tokens. Databases are introspected live (engine-correct metadata query per adapter) and every schema and table lands in <model-root>/<connection>_sources.yml, meta.connection on each source. Bucket connections (S3/MinIO, GCS, Azure, SFTP, local files) are first-class too: their objects are listed recursively and every data file — CSV, Parquet, JSON/JSONL, XLSX, Avro, compressed variants — becomes a source table named by its object path, exactly the shape federation streams. -s/--select and -x/--exclude narrow by schema.table patterns on databases and by object path on buckets; --print previews; --all sweeps the whole profile, buckets included. Timestamped .bak when the file exists. The same paths power Martin's /generate-sources.
dvt generate-sources pg_prod # every schema and table dvt generate-sources s3_lake # every data file in the bucket dvt generate-sources --all # the whole profile, one yml each dvt generate-sources sf_dev -s sales '*.orders' -x '*.tmp_*' dvt generate-sources s3_lake -s 'raw/*.parquet' dvt generate-sources # list connections
dvt serve
Start the whole DVT app suite on DVT's own port range: the hub (46100 — license, links, running apps), the data catalog (46101), the api-portal (46102), Chat with Martin (46104), Data Profiling (46105), the scheduler (46106) and Dashboards (46108). Each app runs detached and registers itself; dvt kill is the off switch. All apps bind to localhost unless --host says otherwise.
dvt serve # the whole suite, hub opens dvt serve -s api-portal # just one app (mirror of dvt kill -s) dvt serve --no-browser # start without opening the hub dvt api-portal # just the portal (46102) dvt hub # just the hub (46100) dvt chat # just Chat with Martin (46104) dvt profiling # just Data Profiling (46105) dvt scheduler # just the scheduler (46106) dvt dashboards # just Dashboards (46108)
dvt dashboards
The saved answers, on a wall (:46108). Every asset saved in Chat with Martin appears as a card; RERUN LIVE executes its SQL through the same guarded virtualization path the chat uses and renders the saved chart or a table — never a stale number. Deliberately basic v1: assets are born in the chat, they live here. Rerun and remove answer loopback-only.
dvt dashboards # dashboards on :46108 dvt kill -s dashboards # stop just this app
dvt api-portal
Denodo-style REST exposure over your models. A themed portal lists every model with an exposure switch and per-column access control; enabled models serve GET /api/v1/models/<name> with equality filters and limit/offset pagination — live-executed against the model's sources through the federation engine on every call, so responses reflect the sources right now, not the last materialization. Optional bearer API key (generated in the portal, stored hashed).
dvt api-portal # portal + REST API on :46102 curl localhost:46102/api/v1/models curl 'localhost:46102/api/v1/models/orders?limit=5®ion=EMEA'
dvt kill
The emergency brake. Stops every hosted DVT app (hub, catalog, api-portal, scheduler), reaps SUSPENDED dvt processes (a Ctrl-Z'd run stays frozen holding the federation cache lock — dvt kill frees it), and lists running dvt work; --force terminates that too. -s takes down exactly one app; --exclude takes down everything BUT one. When things feel stuck, this is the one thing to type.
dvt kill # all apps down + suspended runs reaped dvt kill -s api-portal # stop only the api-portal dvt kill --exclude scheduler # everything down EXCEPT the scheduler (jobs keep firing) dvt kill --force # also terminate RUNNING dvt work
dvt retract
The mirror image of dvt build: removes everything the project materialized — models, seeds, snapshots, and stored test failures (store_failures audit tables) — in REVERSE dependency order, across every engine involved. Database relations are dropped (dialect-correct, cascade where the engine needs it); object-store outputs are deleted (the model.parquet/csv the loader wrote, on S3/GCS/Azure/local files); each retracted model's local incremental bookkeeping is cleared too, so its next run behaves like a first run. Sources are never touched. Asks for confirmation unless --yes.
dvt retract # remove the whole DAG (confirm first) dvt retract --select my_model # remove specific models dvt retract --target sf_dev # resolve default-target models like dvt run --target dvt retract --yes # no prompt (CI / teardown scripts)
dvt clean
Remove project assets: dbt's target/ and dbt_packages/, plus tmp staging and all isolated adapter environments (dvt sync brings them back). Never touches data — the incremental cache belongs to dvt run/build --full-refresh.
dvt clean # Clean everything
PASSTHROUGH COMMANDS
These work identically to dbt:
- ▸
dvt compile— Compile models without executing - ▸
dvt parse— Parse project and build manifest - ▸
dvt list / dvt ls— List resources - ▸
dvt snapshot— Run snapshot models - ▸
dvt deps— Install dbt packages - ▸
dvt source freshness— Check source freshness - ▸
dvt run-operation— Run a macro