explore

A read-only analysis tool for understanding a database, data warehouse, or DuckDB file. It inventories tables and columns, profiles data quality, detects possible personal information, infers joins, and maps relationships.

In plain words
What is it for?
Use it to inspect a warehouse or local DuckDB database, identify important tables, check column contents and data quality, infer relationships, and create a draft data map.
Why use it?
It helps you learn what data actually exists before writing queries or transformations. This reduces mistakes caused by unknown table structure, unreliable values, or incorrect joins.

Skill for Claude CodeCodex

Install

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.

agentmods
npx agentmods add skills/exmergo/dex/explore
Any agent
npx skills add exmergo/dex --skill explore
Clone the repo
git clone --depth 1 https://github.com/exmergo/dex

Made for: Claude Code, Codex.

Per session 309 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 5,685 The whole file, excluding the scripts and references it only reads on demand.
Security scan C 2 findings. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What 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.

ModelPer sessionOnce 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

Measured 2d ago against content hash a8ea3967da88, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

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.

The scan reads SKILL.md. This mod also ships 1 executable file (scripts/run.py), listed below but not scanned — reading those needs a real analyzer, not pattern matching.

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
skills/explore/SKILL.md · 335 lines

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:

  1. connect test --path <file.duckdb> confirms a read-only connection and reports capabilities.
  2. explore inventory --rank returns a ranked object summary (counts and sizes, never rows).
  3. 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 *_name flag'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, within profile_freshness_hours, default 24) is served from the cache (cache_hit_count) instead of re-scanned, so profiling a table map just wrote costs nothing to spend; pass --refresh to force a re-scan when the source changed in a way the free metadata check cannot see.
  4. explore relationships returns inferred and declared joins with confidences, plus notes explaining what the inference examined (so an empty list is meaningful). Add --verify to measure each inferred join with an aggregate overlap probe (orphan fraction, confidence adjusted). A declared join has two sources: a relationships test, and (with --use-project) an entity two semantic models share, which the layer states outright with the key named per model. declared_by on an edge names that entity, semantic_join_count says 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.
  5. explore map writes or updates the .dex/ cache and returns the map (--verify works here too). Alongside the counts, data.objects gives 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, and data.edges gives the join edges in the same shape explore relationships returns. With --use-project each object also carries semantic_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 chaining profile and relationships to re-derive it; go to those two when you need one object in full, or a value domain, which map never 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 in notes and in an elided_* field, so an empty notes means nothing was cut. --detail widens 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 in notes (with skipped_count); pass --full to profile everything. On a re-map, objects skipped this run keep their prior profiles (carried_forward_count), each stamped with its own profiled_at so staleness is visible instead of column detail silently vanishing. A selected object whose cached profile is still fresh (same connector, schema unchanged, profiled within profile_freshness_hours, default 24) is reused without a re-scan (cache_hit_count), so re-runs cost nothing to spend; pass --refresh to 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 relationships and the standalone explore profile reuse fresh profiles the same way.
  6. explore diagram [--full] renders the cached map as a Mermaid ER diagram in data.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 .mmd or 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 with explore semantic list. Read notes before presenting it, since it states any object or column that was left out; --full widens from the default (profiled, joined objects and their grain, key, join, and PII columns) to everything eligible.
  7. 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, and data.results carries 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.md before writing a probe: it maps common questions to effective probe shapes.
  8. 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 -k is omitted, the k it picked plus the silhouette sweep it chose from. Requires the .dex/ cache (run map/profile first) so features can be auto-selected from profiled numeric, non-PII, non-key columns; pass --features to 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 joins map inferred), and the columns named like one; prefer map over a bare profile here, 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 -k to 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's sample_repeatable says 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.
  9. explore semantic list|values|query reach 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-level semantic group, which authors the layer where this queries it.

Read the full file on GitHub · 335 lines

Files

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.

Changes

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.

  1. 2d ago First seen · 335 lines · 309 tokens per session scan C a8ea3967da88

Subscribe to this mod's changes

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.

Related

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.

AltimateAI/altimate-code · 48 tokens

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…

AltimateAI/altimate-code · 294 tokens

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…

AltimateAI/altimate-code · 189 tokens

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.

AltimateAI/altimate-code · 50 tokens

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.

AltimateAI/altimate-code · 79 tokens

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…

AltimateAI/altimate-code · 216 tokens