Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,40 @@ 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:

```bash
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
ducklocal check dashboard.dash # validate; JSON diagnostics, exit 2 on mistakes
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:
Expand Down
22 changes: 20 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,40 @@ 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 的人:

```bash
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 标签页打开
ducklocal check dashboard.dash # 校验;JSON 诊断,有错误时退出码 2
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 目录即可,例如在本仓库执行:
Expand Down
54 changes: 49 additions & 5 deletions examples/analysis_app/dashboard.dash
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
// 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
//
// or open it as a dashboard tab in the GUI:
//
// 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 = <<SQL
SELECT sum(revenue) AS revenue, sum(orders) AS orders,
count(DISTINCT channel) AS channels
FROM orders
WHERE $channel
SQL
}

query "revenue_by_channel" {
sql = <<SQL
SELECT channel, sum(revenue) AS revenue
FROM 'examples/analysis_app/orders.csv'
FROM orders
GROUP BY channel
ORDER BY revenue DESC
SQL
Expand All @@ -22,18 +35,41 @@ query "revenue_by_channel" {
query "daily_orders" {
sql = <<SQL
SELECT date, sum(orders) AS orders
FROM 'examples/analysis_app/orders.csv'
FROM orders
WHERE $channel
GROUP BY date
ORDER BY date
SQL
}

plot "revenue_total" {
type = "card"
query = query.totals
value = revenue
title = "Revenue"
}

plot "orders_total" {
type = "card"
query = query.totals
value = orders
title = "Orders"
}

plot "channels" {
type = "card"
query = query.totals
value = channels
title = "Channels"
}

plot "revenue" {
type = "bar"
query = query.revenue_by_channel
x = channel
y = revenue
title = "Revenue by channel"
width = 6
}

plot "orders" {
Expand All @@ -42,4 +78,12 @@ plot "orders" {
x = date
y = orders
title = "Orders per day"
width = 6
}

// A click on a revenue bar narrows the cards and the daily orders to that
// channel; the bar chart itself stays whole, so another bar can be picked.
// Nothing picked, `$channel` is TRUE.
filter "channel" {
plot = plot.revenue
}
Loading
Loading