Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add skills/exmergo/dex/explorenpx skills add exmergo/dex --skill exploregit clone --depth 1 https://github.com/exmergo/dexWhat it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.00309 | $0.05685 |
| Opus 5 | $0.00154 | $0.02842 |
| Sonnet 5 | $0.00062 | $0.01137 |
| Haiku 4.5 | $0.00031 | $0.00568 |
Grade C, and why
explore scanned grade C with 2 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Downloads and executes remote codehighSupply chain
curl | sh runs whatever the server returns today, which is not necessarily what it returned when this was reviewed.
to install it (`curl -LsSf https://astral.sh/uv/install.sh | sh`, or Makes network callslowCapability
Not a fault in itself. Listed so you know the mod talks to something, and to what.
to install it (`curl -LsSf https://astral.sh/uv/install.sh | sh`, or How it starts
The opening of the file, as written. The whole thing — 335 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Explore
Make sense of a warehouse or a local DuckDB database the way an analytics engineer does: rank what matters, drill selectively, and persist a draft map. This is the flagship, fully read-only skill. It absorbs profiling and relationship inference as capabilities; they are not separate skills.
How to drive it
Run the engine through the wrapper. It prints one sanitized JSON envelope and nothing else; read the envelope and decide the next step.
uv run "${CLAUDE_SKILL_DIR}/scripts/run.py" <subcommand> [flags]
dex runs its engine through uv, which is a prerequisite and is not installed by
Claude Code. If the shell reports uv: command not found, stop and tell the user
to install it (curl -LsSf https://astral.sh/uv/install.sh | sh, or
brew install uv, or pipx install uv), then re-run. Never fall back to raw
Python, pip, or a database CLI to do the work another way: the guardrails live in
the engine, so any other path is unguarded.
If the user has no warehouse to point at and wants to see what dex does, demo
generates one: a seeded local DuckDB warehouse plus the .dex/config.yml for it,
with no credentials and no network, so every subcommand below then runs with no
flags. It only ever creates, so it refuses rather than touch a file that already
exists. Offer it rather than assuming it: a user who does have a warehouse wants
that one read, not a fixture built beside it.
Subcommands, in the usual order:
connect test --path <file.duckdb>confirms a read-only connection and reports capabilities.explore inventory --rankreturns a ranked object summary (counts and sizes, never rows).explore profile <objects>(space- or comma-separated) returns column profiles, PII flags recorded as (column, category, confidence) and never example values, plus candidate keys, the likely grain, and data-quality warnings (e.g. a non-unique id that will fan out on joins). A generic*_nameflag's confidence is refined by value-shape evidence from the same scan, in both directions: person-shaped values corroborate it, a closed reference vocabulary or long labels de-rate it below the firewall's blocking threshold, and missing evidence changes nothing (the flag itself is never removed). Distinct counts are approximate for scale, but any column that looks unique within approximation noise is escalated to an exact COUNT(DISTINCT) (distinct_count_exact: true), so uniqueness and grain verdicts rest on proof; a~prefix in a warning marks a count that is still approximate. A requested object whose cached profile is still fresh (same connector, schema unchanged, withinprofile_freshness_hours, default 24) is served from the cache (cache_hit_count) instead of re-scanned, so profiling a tablemapjust wrote costs nothing to spend; pass--refreshto force a re-scan when the source changed in a way the free metadata check cannot see.explore relationshipsreturns inferred and declared joins with confidences, plus notes explaining what the inference examined (so an empty list is meaningful). Add--verifyto measure each inferred join with an aggregate overlap probe (orphan fraction, confidence adjusted). A declared join has two sources: arelationshipstest, and (with--use-project) an entity two semantic models share, which the layer states outright with the key named per model.declared_byon an edge names that entity,semantic_join_countsays how many came that way, and the notes call out the ones name-based inference did not find, which is the interesting set: a semantic layer routinely joins columns that share no name at all.explore mapwrites or updates the.dex/cache and returns the map (--verifyworks here too). Alongside the counts,data.objectsgives each top-ranked object its row count, detected grain, candidate key, notable columns (each carrying the role that earned it a place:grain,key,join, or a PII flag) and data-quality findings, anddata.edgesgives the join edges in the same shapeexplore relationshipsreturns. With--use-projecteach object also carriessemantic_models, the semantic models that sit on that relation, which is what separates a load-bearing table from a merely large one: empty means nothing in the layer reads it. Read that payload instead of chainingprofileandrelationshipsto re-derive it; go to those two when you need one object in full, or a value domain, whichmapnever carries. It is budgeted: 25 objects by rank, 12 columns per object, 40 edges, 5 findings per object. Every cap binds in every mode and every elision is counted innotesand in anelided_*field, so an emptynotesmeans nothing was cut.--detailwidens the selection to every column and to objects that were inventoried but never profiled, and lifts no cap; it spends nothing, unlike--full. Past 50 objects it profiles only the top 25 by rank and says so innotes(withskipped_count); pass--fullto profile everything. On a re-map, objects skipped this run keep their prior profiles (carried_forward_count), each stamped with its ownprofiled_atso staleness is visible instead of column detail silently vanishing. A selected object whose cached profile is still fresh (same connector, schema unchanged, profiled withinprofile_freshness_hours, default 24) is reused without a re-scan (cache_hit_count), so re-runs cost nothing to spend; pass--refreshto force a full re-profile when the source changed in a way the free metadata check cannot see (e.g. rows changed but the schema did not).explore relationshipsand the standaloneexplore profilereuse fresh profiles the same way.explore diagram [--full]renders the cached map as a Mermaid ER diagram indata.mermaid. Free and connectionless (it reads the cache, never the warehouse), so it is safe to re-run while shaping the picture. Reproduce the string verbatim in a fenced ```mermaid block so the human can see it, and write it to a.mmdor a markdown file when they want one on disk: the engine deliberately writes no file. Never redraw or "tidy up" the diagram by hand. The glyphs are claims the engine derived from evidence, and a plausible-looking cardinality you supplied is exactly the overclaim this command exists to prevent: declared joins are solid, inferred dotted, and an unverified inference never says "exactly one". A solid line labelled with a semantic entity is a join the semantic layer declares; look the entity up withexplore semantic list. Readnotesbefore presenting it, since it states any object or column that was left out;--fullwidens from the default (profiled, joined objects and their grain, key, join, and PII columns) to everything eligible.explore query "<SELECT ...>" ["<SELECT ...>" ...]answers ad-hoc questions the fixed commands don't cover: you write the SQL, the engine's query firewall refuses or bounds it. Pass a statement per argument, or--sql-file <path>for a longer list, and ask a whole chain of questions in one call rather than one call each; each statement is judged and answered on its own, so a refusal on one does not cost you the others, anddata.resultscarries one entry per statement. A table you have not profiled, including a model you just built, is profiled for you and the statement then runs, so probing something new is one call rather than three; the envelope says what it profiled, and on a metered connector that profile is priced into the same confirmation as the statements. Results come back row-major and capped; a refusal names the offending column and the fix, so one rewrite is enough. Read${CLAUDE_SKILL_DIR}/references/probe-playbook.mdbefore writing a probe: it maps common questions to effective probe shapes.explore cluster <object> [--features a,b,c] [-k N]runs k-means over a bounded sample of the object's numeric columns and returns the segment structure: per-cluster sizes and fractions, centroids (each coordinate is a cluster's mean of that feature, an aggregate), the silhouette score, and, when-kis omitted, the k it picked plus the silhouette sweep it chose from. Requires the.dex/cache (runmap/profilefirst) so features can be auto-selected from profiled numeric, non-PII, non-key columns; pass--featuresto choose them yourself (naming a PII column, or a key, opts it in deliberately, and only its mean is ever reported). A key is never a feature: its mean is meaningless, and a fact table is mostly keys plus a handful of measures, so clustering on them just partitions surrogate ranges. Keys are the unique columns, the columns that join out (from the joinsmapinferred), and the columns named like one; prefermapover a bareprofilehere, because without inferred joins a foreign key is caught only if its name gives it away. The notes name every excluded column, so check them before trusting a result. Two things the silhouette alone will not tell you, both of which the notes will. A cluster holding under 1% of the sample is an outlier pocket, not a segment, and it pushes the score up precisely because it sits so far out: report that as outlier detection, or re-run with-kto split the bulk. And on connectors that cannot seed a sample the draw changes per run, so two runs can disagree on k; the envelope'ssample_repeatablesays which case you are in, and comparing runs across different draws is meaningless. Only aggregates cross the boundary: the sample rows are clustered in-process and never enter context. On a metered connector it takes the same cost handshake as the scanning commands below (only the feature columns are scanned, and a dialect-aware sample clause reads a fraction), so surface the estimate and get a budget first. Needs the[cluster]extra (scikit-learn); the wrapper installs it automatically for this subcommand.explore semantic list|values|queryreach the semantic layer: the metrics an author defined, and the semantic models, measures, dimensions and entities they are built out of. Distinct from the warehouse commands above, and from the top-levelsemanticgroup, which authors the layer where this queries it.
What ships with it
4 files beside SKILL.md in the same directory: the scripts, references and assets a skill reads on demand. Not counted in the per-session cost; read them before you install if any of them is executable.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 2d ago First seen · 335 lines · 309 tokens per session scan C a8ea3967da88
explore is a skill published in the GitHub repository exmergo/dex (24 stars, last pushed 2d ago), licensed Apache-2.0. It adds 309 tokens to every session and 5,685 once invoked, about $0.0015 per session on Opus 5. A static security scan graded it C with 2 findings (downloads and executes remote code, makes network calls). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other skills, from other repositories
data-parity
Validate that two tables or query results are identical — or diagnose exactly how they differ. Discover schema, identify keys, profile cheaply, then diff. Use for migration validation, ETL regression, and query refactor verification.
dbt-develop
REQUIRED before writing or modifying ANY dbt model. Invoke this skill FIRST whenever a task says "create", "build", "add", "modify", "update", "fix", or "refactor" a dbt model, staging file, mart, incremental, or snapshot. Skipping this skill is the leading cause of silent-correctness bugs — models that compile and…
data-viz
Build modern, interactive data visualizations and dashboards using code-based component libraries (shadcn/ui, Recharts, Tremor, Nivo, D3, Victory, visx). Use this skill whenever the user asks to visualize data, build dashboards, create analytics views, chart metrics, tell a data story, build a reporting interface…
dbt-troubleshoot
Debug dbt errors — compilation failures, runtime database errors, test failures, wrong data, and performance issues. Use when something is broken, producing wrong results, or failing to build. Powered by altimate-dbt.
dbt-unit-tests
Generate dbt unit tests automatically for any model. Analyzes SQL logic (CASE/WHEN, JOINs, window functions, NULLs), creates type-correct mock inputs from manifest schema, and assembles complete YAML. Use when a user says "generate tests", "add unit tests", "test this model", or "test coverage" for dbt models.
dbt-schema-verify
REQUIRED after building or modifying ANY dbt model that has columns declared in schema.yml / models.yml. Run altimate-dbt schema-verify --model to diff actual columns against the spec, and treat any mismatch verdict as "not done." The most common reason "the build is green but the tests still fail" is that the model…