diff --git a/Cargo.lock b/Cargo.lock index 051ab3a..caf6f48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,6 +2236,7 @@ dependencies = [ "chrono", "directories", "duckdb", + "getrandom 0.3.4", "gpui-component-shell", "gpui-kit", "gpui-shell", diff --git a/Cargo.toml b/Cargo.toml index b5ac9f9..85bc955 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,11 +36,12 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } ureq = { version = "3", default-features = false, features = ["rustls"] } hmac = "0.12" sha2 = "0.10" -quick-xml = { version = "0.36", features = ["serialize"] } -unicode-width = "0.2" +getrandom = "0.3" # Map tiles: the same `image` GPUI decodes with, only the formats tile # servers send. image = { version = "0.25", default-features = false, features = ["png", "jpeg"] } +quick-xml = { version = "0.36", features = ["serialize"] } +unicode-width = "0.2" [dev-dependencies] rust_xlsxwriter = "0.99" diff --git a/README.md b/README.md index ed6133e..64e1037 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,15 @@ ducklocal query --sql "SELECT * FROM 'sales.csv'" --limit 20 ducklocal query --database warehouse.duckdb --sql "SHOW TABLES" ``` -Results are structured JSON, with explicit truncation and precision-preserving value encodings; `--format md` prints the same values as a Markdown table for a document to quote. File databases default to read-only; writes require `--read-write`. Read-only is not a filesystem/network sandbox: `COPY` can still write files. +Results are structured JSON, with explicit truncation and precision-preserving value encodings; `--format md` prints the same values as a Markdown table for a document to quote. File databases default to read-only; writes require `--read-write`. Read-only is not a filesystem/network sandbox: `COPY` can still write files. An error is JSON too, and carries a `hint` when there is an obvious next step — the flag that was meant, `--read-write` for a refused write, `ducklocal schema` for a table that does not exist. + +`ducklocal schema` maps what there is to query in one call — every file under the PATHs, or a database's tables and views, with their columns and the SQL that reads each. A large catalog comes back as a summary sized for a prompt, and `--stats` gives one relation's exact per-column statistics: + +```bash +ducklocal schema ./data/ +ducklocal schema --database warehouse.duckdb --table orders +ducklocal schema sales.csv --stats # nulls, distinct, min/max, median, day coverage +``` An analysis app can be exported as one standalone HTML file — the statements its `query()` calls issued, with their results — for sharing with someone who does not have DuckLocal: @@ -51,7 +59,7 @@ An analysis app can be exported as one standalone HTML file — the statements i ducklocal export --html examples/analysis_app ``` -A dashboard can also be declared as data: a `.dash` file of query and plot blocks, opened as a tab like an app, editable right there in the GUI. `ducklocal check` validates a spec without a window, and `ducklocal lsp` gives any LSP-capable editor the same diagnostics, completion, hover and go-to-definition: +A dashboard can also be declared as data: a `.dash` file of query and plot blocks, opened as a tab like an app, editable right there in the GUI. A `filter` block makes a bar chart or a table clickable: the value picked there narrows every query that reads it as `$name` — cross-filtering, with the picking plot itself left whole. `ducklocal check` validates a spec without a window, and `ducklocal lsp` gives any LSP-capable editor the same diagnostics, completion, hover and go-to-definition: ```bash ducklocal dashboard.dash # open as a dashboard tab @@ -59,6 +67,16 @@ ducklocal check dashboard.dash # validate; JSON diagnostics, exit 2 o ducklocal lsp # language server over stdio, for editors ``` +When the work is for a person to see, `ducklocal open` hands it to the window that is already running — starting one if none is — as a tab in front. It runs in the window's own session, so they can carry on from it: + +```bash +ducklocal open --title "Revenue" --run --sql "SELECT channel, sum(amount) FROM 'orders.csv' GROUP BY 1" +ducklocal open dashboard.dash # a dashboard tab; data files and folders attach +ducklocal open --no-launch --sql-file q.sql # exit 1, kind not_running, if no window is up +``` + +The other direction works too: `ducklocal open --state` reports what the running window shows — its tabs, each query's SQL and result, each dashboard's picked filters — so an agent can carry on from where the person left off. + See the [CLI guide](https://ducklocal.app/docs/cli) for conversion, stdin, output, app export, and safety details. The [official agent skill](skills/ducklocal/SKILL.md) teaches schema-first exploration, SQL analysis, and verified file conversion. Copy it into your target project's supported skills directory; for example, from this checkout: diff --git a/README.zh-CN.md b/README.zh-CN.md index 9565a9a..ff0cb2f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -43,7 +43,15 @@ ducklocal query --sql "SELECT * FROM 'sales.csv'" --limit 20 ducklocal query --database warehouse.duckdb --sql "SHOW TABLES" ``` -结果为结构化 JSON,显式标记截断并保留数值精度;`--format md` 把同样的值输出为 Markdown 表格,便于写进文档引用。文件数据库默认只读,写入需 `--read-write`;只读不是文件系统/网络沙箱,`COPY` 仍可写文件。 +结果为结构化 JSON,显式标记截断并保留数值精度;`--format md` 把同样的值输出为 Markdown 表格,便于写进文档引用。文件数据库默认只读,写入需 `--read-write`;只读不是文件系统/网络沙箱,`COPY` 仍可写文件。出错时同样输出 JSON;有明确下一步时会带 `hint`,比如拼错的参数本来是哪个、写入被拒时加 `--read-write`、表不存在时先跑 `ducklocal schema`。 + +`ducklocal schema` 一次列出所有可查询的东西:PATH 下的每个文件,或数据库的表和视图,连同列和读取它的 SQL。目录很大时返回适合放进提示词的摘要;`--stats` 给出单个关系精确的逐列统计: + +```bash +ducklocal schema ./data/ +ducklocal schema --database warehouse.duckdb --table orders +ducklocal schema sales.csv --stats # 空值、去重数、最值、中位数、日期覆盖 +``` 分析应用可以导出为一个独立 HTML 文件——应用 `query()` 发出的语句及其结果——方便发给没有 DuckLocal 的人: @@ -51,7 +59,7 @@ ducklocal query --database warehouse.duckdb --sql "SHOW TABLES" ducklocal export --html examples/analysis_app ``` -dashboard 也可以声明为数据:一个由 query 和 plot block 组成的 `.dash` 文件,像应用一样以标签页打开,并可直接在 GUI 里编辑。`ducklocal check` 无窗口校验规格文件,`ducklocal lsp` 则把同样的诊断、补全、悬停和跳转定义提供给任何支持 LSP 的编辑器: +dashboard 也可以声明为数据:一个由 query 和 plot block 组成的 `.dash` 文件,像应用一样以标签页打开,并可直接在 GUI 里编辑。`filter` block 让柱状图或表格可以点选:选中的值会筛选所有以 `$name` 引用它的查询,也就是交叉筛选,做选择的那张图本身保持不变。`ducklocal check` 无窗口校验规格文件,`ducklocal lsp` 则把同样的诊断、补全、悬停和跳转定义提供给任何支持 LSP 的编辑器: ```bash ducklocal dashboard.dash # 以 dashboard 标签页打开 @@ -59,6 +67,16 @@ ducklocal check dashboard.dash # 校验;JSON 诊断,有错误时 ducklocal lsp # 面向编辑器的语言服务器(stdio) ``` +如果结果是给人看的,`ducklocal open` 会把它交给已经在运行的窗口(没有就先启动一个),作为前台的新标签页打开。它在窗口自己的会话里运行,人可以直接接着改: + +```bash +ducklocal open --title "Revenue" --run --sql "SELECT channel, sum(amount) FROM 'orders.csv' GROUP BY 1" +ducklocal open dashboard.dash # 打开为 dashboard 标签页;数据文件和文件夹会被挂载 +ducklocal open --no-launch --sql-file q.sql # 没有窗口时退出码 1,kind 为 not_running +``` + +反方向也可以:`ducklocal open --state` 报告正在运行的窗口里显示的内容,包括各个标签页、每个查询的 SQL 和结果、每个 dashboard 当前选中的筛选。这样 agent 可以从人停下的地方接着做。 + 转换、stdin、输出编码、应用导出和安全说明见 [CLI 指南](https://ducklocal.app/docs/zh/cli)。 [官方 agent skill](skills/ducklocal/SKILL.md) 教 AI 先查 schema,再进行 SQL 分析及验证格式转换。复制到目标项目支持的 skill 目录即可,例如在本仓库执行: diff --git a/examples/analysis_app/dashboard.dash b/examples/analysis_app/dashboard.dash index a82ff52..5350889 100644 --- a/examples/analysis_app/dashboard.dash +++ b/examples/analysis_app/dashboard.dash @@ -1,5 +1,5 @@ // The analysis_app panel's data as a dashboard spec: same orders.csv, -// declared rather than scripted. Check it from the repository root: +// declared rather than scripted. Check it from anywhere: // // ducklocal check examples/analysis_app/dashboard.dash // @@ -7,13 +7,26 @@ // // ducklocal examples/analysis_app/dashboard.dash // -// Paths in SQL are relative to the working directory, like every other -// ducklocal command. +// The source's path is relative to this file, so the spec and its data move +// together; the queries read it by name. + +source "orders" { + path = "orders.csv" +} + +query "totals" { + sql = <