v0.2.38
LATESTSTART IN SECONDS, LEAVE WHEN YOU WANT
The default target stops being a commitment. dvt init scaffolds a project with a DuckDB warehouse out of the box — dvt build is green seconds after pip install, no warehouse account, no prompts — and dvt flip-target-to migrates the whole project to any engine's dialect in one deterministic command when you outgrow the laptop, with an AI rescue pass for the models determinism can't touch. Around that headline: generate-sources learns to read buckets, Martin delivers many charts in one answer and narrates his steps, Data Explorer joins the suite, and Dashboards grow folders, richer formatting, and chart types that know what your columns can express.
ADDED
- ▸dvt init, rebuilt: bare `dvt init` turns the CURRENT folder into a project named after it; `dvt init <name>` creates the folder. Either way, zero prompts and a DuckDB default target out of the box — the profile entry merges into dbt's own ~/.dbt/profiles.yml (never clobbered, timestamped .bak, $DBT_PROFILES_DIR honored), pointing at a visible <name>.duckdb at the project root: that file IS your warehouse. Starter seed + model + doctrine-ready sources.yml mean dvt build succeeds immediately, and dbt-duckdb now ships with dvt-core so out-of-the-box means exactly that. One dialect everywhere: with DuckDB as default, regular and federated models speak the same SQL.
- ▸dvt flip-target-to <target> — the graduation command. Makes <target> the default and transpiles every default-target model to the new engine's dialect via the same SQLGlot machinery federation trusts on every run: Jinja survives byte-for-byte, federated models (DuckDB by doctrine) and target-pinned models are never touched, every changed file keeps a .bak, and --dry-run shows the full ledger first. What determinism refuses gets ONE AI rescue attempt through your gateway — validated (Jinja unchanged, output parseable in the new dialect) before it touches disk — or, with no AI connected, its full path printed so you can jump straight in. The default target flips only when every model made it. Also Martin's /flip-target-to: pick a target, read the dry-run card, APPLY.
- ▸generate-sources reads buckets: S3/MinIO, GCS, Azure, SFTP and local-file connections are no longer skipped — their objects are discovered 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 live. -s/-x patterns match object paths (raw/*.csv, **/*.parquet); --all now sweeps buckets too. Verified against MinIO, Azurite and local files in the same sitting.
- ▸Martin answers with MANY charts: ask for ten charts and you get ten — the reply carries a results array (up to 10 titled query+chart pairs), executed in parallel on the server, each rendered as its own block with chart, table, collapsible SQL and per-result SAVE ASSET / COPY SQL / PNG / CSV. One broken query never sinks the batch. And he narrates: phase lines stream live (thinking with the model → running N queries in parallel → rendering) in the chat bubble and as dim progress lines in dvt martin.
- ▸Data Explorer on :46107 — raw SQL on any connection, read-only, the engine computes. Pick a connection, browse its schemas and tables live, run guarded SELECTs with the results grid capped and honest. Part of dvt serve; dvt kill -s explorer stops it alone.
- ▸Dashboards grow up: create folders from the rail (+📁, empty folders are real drop targets), drag boards and assets into them, delete either; a VS-Code-style explorer rail with separate DASHBOARDS and MARTIN'S ASSETS sections; a collapsible right-hand formatting panel with DATA (X/Y column mapping) and FORMAT (title/size, display type, single color or palette, number format, value labels) tabs; rich hover tooltips; results cached with 'as of' honesty and a per-tile live refresh. DISPLAY AS options are gated by what your result columns can actually express — a two-numeric result offers SCATTER and refuses PIE, and the dimmed button tells you why.
CHANGED
- ▸Martin can reach the web — guarded and gated: web_search and web_fetch for third-party docs, adapter errors and fresh bug fixes, behind a guard that refuses local and private-network addresses outright; page content is information to him, never instructions. And a shell exists as a LAST RESORT: when no dedicated tool covers the job, Martin proposes the exact command with his reasoning, and it runs only after your APPLY — every command behind your click, always.
- ▸The scheduler's verb whitelist (shared by Martin's propose_run) gains generate-sources and flip-target-to — both schedulable, both proposable, both deterministic.
FIXED
- ▸dbt compatibility, three real bugs: $DBT_PROFILES_DIR was ignored by DVT's own profile readers (dbt honors it — dbt and DVT could silently read DIFFERENT profiles files; now they never can), the DuckDB connection mapper only understood a non-standard `database:` key instead of dbt-duckdb's canonical `path:` (both work now, canonical first), and relative DuckDB paths are absolutized so runs work from any directory.
- ▸Chat with Martin: the answer renderer was rebuilt for multi-result answers — and the /flip-target-to and /generate-sources pickers share one connection list from the live profile.
Get it: pip install dvt-core==0.2.38 · wheels on PyPI for macOS (arm64 + x86_64) and Linux, Python 3.10–3.12.