Built by Exmergo · The AI Stack for Your Data Stack.


Install (Any Agent)
Run this command in your terminal
npx skills add exmergo/dex
Install (Claude Code)
Run these commands inside Claude Code one at a time
/plugin marketplace add exmergo/exmergo-agent-plugins
/plugin install dex@exmergo
Update later with /plugin marketplace update exmergo. The skills appear as
/dex:explore, /dex:transform, and /dex:maintain and auto-trigger on matching
intent. Ask it to warm dex once after installing, so the first real command does
not wait for the engine to install (see Prerequisite: uv).
dex: the agent-native analytics engineering toolkit
dex is analytics engineering for Claude Code and any agent: data warehouse
exploration, dbt transformation and semantic modeling, and schema-drift
maintenance. Point it at your warehouse (or a local DuckDB file, or the one
dex demo generates for you) and at your repository; it learns the landscape,
writes and refactors your dbt transformations and your semantic layer, and tells
you what to fix when anything drifts. Your repository is the source of truth, on
two independent axes: the transformation project, which is dbt, and the semantic
layer, which is dbt's own, a hosted dbt Cloud deployment, or native Apache Ossie
documents that need no dbt project at all. Every change is a reviewable diff.
Read-only against your data.
It closes the gap a general coding agent still has: agents re-learn the schema
each session, have no strategy for thousands of tables, are blind to warehouse
cost, will pull sensitive data into context, do not treat a dbt project as a
first-class object, and have no concept of a semantic model to keep coherent over
time. dex owns exactly that loop.
The loop
Explore. Transform. Maintain. (ETM)
- Explore an unfamiliar warehouse: rank what matters, profile selectively,
infer and verify joins, answer ad-hoc questions with guarded SQL probes behind
a PII-aware query firewall, read the semantic layer as the object graph it is
(semantic models, metrics with their composition, measures, dimensions, and the
declared join graph, each resolved to the relation and column behind it, and the
whole of it searchable and budgeted), read a
dimension's value domain before filtering on it,
and query its metrics (locally via
MetricFlow or against a hosted dbt Cloud deployment; a native Apache Ossie
layer is catalog-first and refuses a metric query by name, because the format
specifies interchange metadata and no query runtime), and render the map as a
Mermaid ER diagram that draws the joins the semantic layer declares and never
claims a cardinality the data has not proven.
Persist a draft map. Fully read-only.
- Transform the project: author dbt models (staging to marts) with tests
and docs, and the semantic layer on top, either as dbt semantic models
(MetricFlow YAML: entities, dimensions, measures, metrics) or as native Apache
Ossie documents written back byte for byte, with a free Viz preview.
Validated against a dev target, cost-guarded.
- Maintain the repository as it drifts: diff the warehouse, the project, and
the semantic layer against the last snapshot, surface schema, volume, grain,
and definition drift ranked by blast radius, and propose edits. The two project
axes are fingerprinted independently, so a repository with a semantic layer and
no dbt project still gets a baseline.
Try it in three commands, on your laptop
No warehouse, no credentials, no cloud account, no network. dex demo generates a
small e-commerce DuckDB warehouse locally and points the following commands at it.
pip install "exmergo-dex-core[duckdb]"
dex demo
dex explore map
dex demo writes two files in the directory you are standing in, and refuses rather
than overwrite anything: dex_demo.duckdb (7 tables, 29,512 rows) and a
.dex/config.yml so everything after it runs with no flags. The data is generated
from a pinned seed, so what you see is what is written here.
It is seeded to be realistically broken, because a first run that reports a clean
bill of health teaches you nothing. explore map flags 6 columns as personal data,
infers 5 joins, and reports 6 data-quality findings. Then:
dex explore profile order_items products
dex explore relationships --verify
dex explore query "select email from customers"
- A broken grain, reported as duplicates rather than as a missing key.
order_item_id is not unique: 13000 distinct over 14000 rows (1000 rows would have to be removed for it to be unique, so it is unique for 92.9% of rows), because a
batch was loaded twice. Any join on it silently fans out. The grain comes back
unknown rather than as one of the several column pairs that are technically unique
here only because order_item_id almost is; those are in key_evidence with the
reason each was suppressed.
- A key that mixes id schemes.
sku is 90% numeric, 10% 32-character hexadecimal (md5-shaped), from a merged catalogue. Cast it to a number and you
drop 10% of your rows without an error.
- A join that looks right and is not.
web_events.customer_id shares the CRM's
column name and type, so it is inferred; verification finds 100% of values have
no match, so the inference collapses instead of shipping a join that returns all
NULLs and looks like it worked.
- A refusal. The query firewall declines to project
customers.email into
context. select count(distinct email) from customers runs, because a statistic
is not a value.
- Plus a table an interrupted load left empty, two columns whose declared type
contradicts their content, and two PII false positives on a distribution
centre's city and coordinates, which are a designed behaviour and worth meeting
early.
DuckDB is free and local, so nothing here asks you to confirm a spend. On BigQuery
or Snowflake the same commands return an estimate first and run only once you agree
to it.
Prerequisite: uv
dex installs and runs its engine through uv, so you
need it on your PATH before either install below. Neither Claude Code nor the
plugin installs it for you.
curl -LsSf https://astral.sh/uv/install.sh | sh
brew install uv and pipx install uv work too. Nothing else is required: uv
supplies the Python and the engine, with the connector extra chosen for you at
runtime.
The first command in a fresh environment pays for that install, which is tens of
seconds on a cold uv cache. --warm pays it up front instead: it materializes
the environment, prints what it installed, and exits without running anything.
uv run --no-project --script skills/<skill>/scripts/run.py --warm
Run it as a container build step or a CI setup step, or ask your agent to warm dex
once after installing. Add --connector snowflake (or any other connector) to warm
a warehouse before there is a project to read the choice from.
Benchmarks
We run dex on two public analytics-engineering benchmarks. Every run's raw
per-task results are committed, including the ones that flatter us least.
data-eng-bench (Snowflake, 103 tasks)
Each task hands the agent a 2,356-model dbt project on a 489 MB DuckDB
warehouse and a prescriptive ticket, then runs a hidden pytest suite after a
cold dbt run. Scoring is binary and total: one failed assertion is a zero.
dex + Claude Sonnet 5 resolves 59 of 103 tasks (57.3%), with dex firing
on 98% of trials. That is nominally the highest published Sonnet 5 figure and
statistically indistinguishable from the 56.6% Snowflake published for both Claude
Code and their own CoCo harness, since 0.7 points on 103 tasks is less than one
task. Read it as parity, not as a win.
Because the reward is all-or-nothing, we also publish assertion-level results:
89.7% of assertions pass (task-weighted), and 19 of the 44 unresolved tasks
missed by exactly one assertion. Full methodology and the per-check record are in
the data-eng-bench README.
ADE-bench (dbt Labs, 75 tasks)
Fix, build, and extend dbt projects on DuckDB. dex + Claude Sonnet 5 reaches
76% task resolution, at 2.5x lower cost than Claude Fable 5.
With dex, accuracy clusters tightly across models (72-76%) while cost does not,
so you can run an inexpensive model and still get top-tier results. One honest
caveat on this one: dex was actually invoked in only 15 of the 75 Sonnet 5 trials,
and on those 15 it netted a single extra task, so the 76% is mostly a statement
about the model rather than about dex. Per-model cost and the raw results.json
for every run are in the ADE-bench README.
On benchmarks
We publish these to be transparent, not to overclaim. A task-resolution score
measures whether tests pass; it does not measure what matters most in practice:
the experience of the human engineer working with the agent. Trust in a diff,
clarity of the proposed change, cost surfaced before spend, and sensitive data
kept out of context never show up in a pass rate. We optimize for that
experience first and treat these scores as guide posts, not as the goal.
Two habits follow from that. We report how often dex actually ran next to
the accuracy, because a score with the tool firing on 98% of trials and the same
score with it firing on 20% are different claims and only the first says anything
about dex. And we do not turn a gap smaller than the noise into a headline: a
single run, one attempt per task, tells you roughly where a setup stands, not that
it is better than the one a point below it.
Connectors
| Connector | Type | Self-hostable | Extra / --connector | Cost surfaced as | Credentials discovered from |
|---|
| Snowflake | Cloud warehouse | ❌ | snowflake | Warehouse-seconds, credits alongside | connections.toml, SNOWFLAKE_* env, or a dbt profile |
| BigQuery | Cloud warehouse | ❌ | bigquery | Bytes scanned | Application Default Credentials (gcloud auth application-default login) |