@carllee1983/dbcli 1.49.0 → 1.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbcli-agent",
3
- "version": "1.49.0",
3
+ "version": "1.50.0",
4
4
  "description": "Database CLI skill and command reference for AI agents.",
5
5
  "author": {
6
6
  "name": "Carl Lee",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbcli-agent",
3
- "version": "1.49.0",
3
+ "version": "1.50.0",
4
4
  "description": "Database CLI skill and command reference for AI agents",
5
5
  "author": {
6
6
  "name": "Carl Lee",
@@ -363,7 +363,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
363
363
  | `assert` | query-only+ | **(v1.25)** SQL only. Verify an invariant; exit 1 on failure unless `--no-fail`. `--expect "rows>0\|value==X\|col:c not null\|unique\|between a and b\|>= n"`, `--vs <query> --compare rows\|value` (reconcile), `--against <snapshot> --tolerance <pct>`. |
364
364
  | `verification` | n/a | Inspect and manage local verification artifacts. `list` / `show <id-or-path>` / `summary` are read-only; `prune` is dry-run by default and deletes only with `--execute --force`. Reads `<cwd>/.dbcli/verification/`; no DB connection, no audit writes. |
365
365
  | `backfill artifact` | n/a | Build a bounded, reviewable source-to-SQL backfill artifact from JSON. Includes source/target identity, blacklist/schema preflight, read-back verification, and rollback hints; dry-run only and never executes writes. |
366
- | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
366
+ | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. **(v1.50)** `proxy analyze --format markdown` produces the QueryLens report and redacts SQL/error literals in-memory before analysis; use `proxy <engine> --redact literals` to protect the log itself. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
367
367
  | `status` | query-only+ | Safe JSON/text summary (no credentials). |
368
368
  | `inspect` | query-only+ | Read-only context snapshot (connection, permission, blacklist, objects, snippets, context-aware `suggestedCommands`, and **(v1.23)** human-readable `hints`). `--for-agent` / `--brief` / `--no-connect` / `--require-schema-cache`. Supports `--recovery`. |
369
369
  | `report` | query-only+ | Diagnostic report built from `@diag/*` snippets. `--section <health\|capacity\|perf>` (comma-separated to combine), `--brief`, `--for-agent`, `--no-connect`. |
@@ -1180,6 +1180,7 @@ dbcli --use prod proxy postgresql # infer target from na
1180
1180
 
1181
1181
  dbcli proxy analyze # analyze .dbcli/proxy/events.jsonl (JSON)
1182
1182
  dbcli proxy analyze --format text --top 10 # human-readable top-10 view
1183
+ dbcli proxy analyze --format markdown # QueryLens shareable, redacted Markdown report
1183
1184
  dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1184
1185
  ```
1185
1186
 
@@ -1200,7 +1201,7 @@ dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1200
1201
 
1201
1202
  **Log rotation:** all writes are serialized through one in-process chain (concurrent sessions never interleave partial lines). The event log auto-rotates to keep one rolling segment — when the next line would reach ~50 MiB or 200,000 entries, the current file is renamed to `<events>.1` (overwriting any prior segment) and a fresh file starts. Worst-case on-disk footprint is ~2× the byte cap.
1202
1203
 
1203
- **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default.
1204
+ **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text|markdown` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default. `markdown` is the QueryLens report: it redacts SQL and error-message literals in an in-memory copy before analysis, while leaving the source log untouched.
1204
1205
 
1205
1206
  Every actionable block carries machine-readable next steps so an agent can move from "what is wrong" to "what to run":
1206
1207
  - `byFingerprint[]` — top-N SELECT entries get `suggestedCommands`: `dbcli explain "<sql>"` and `dbcli guide missing-index-for "<sql>"`.
@@ -2,7 +2,7 @@
2
2
  "name": "dbcli-agent",
3
3
  "displayName": "dbcli Agent",
4
4
  "description": "Database CLI skill and command reference for AI agents.",
5
- "version": "1.49.0",
5
+ "version": "1.50.0",
6
6
  "author": {
7
7
  "name": "Carl Lee",
8
8
  "url": "https://github.com/CarlLee1983"
@@ -363,7 +363,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
363
363
  | `assert` | query-only+ | **(v1.25)** SQL only. Verify an invariant; exit 1 on failure unless `--no-fail`. `--expect "rows>0\|value==X\|col:c not null\|unique\|between a and b\|>= n"`, `--vs <query> --compare rows\|value` (reconcile), `--against <snapshot> --tolerance <pct>`. |
364
364
  | `verification` | n/a | Inspect and manage local verification artifacts. `list` / `show <id-or-path>` / `summary` are read-only; `prune` is dry-run by default and deletes only with `--execute --force`. Reads `<cwd>/.dbcli/verification/`; no DB connection, no audit writes. |
365
365
  | `backfill artifact` | n/a | Build a bounded, reviewable source-to-SQL backfill artifact from JSON. Includes source/target identity, blacklist/schema preflight, read-back verification, and rollback hints; dry-run only and never executes writes. |
366
- | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
366
+ | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. **(v1.50)** `proxy analyze --format markdown` produces the QueryLens report and redacts SQL/error literals in-memory before analysis; use `proxy <engine> --redact literals` to protect the log itself. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
367
367
  | `status` | query-only+ | Safe JSON/text summary (no credentials). |
368
368
  | `inspect` | query-only+ | Read-only context snapshot (connection, permission, blacklist, objects, snippets, context-aware `suggestedCommands`, and **(v1.23)** human-readable `hints`). `--for-agent` / `--brief` / `--no-connect` / `--require-schema-cache`. Supports `--recovery`. |
369
369
  | `report` | query-only+ | Diagnostic report built from `@diag/*` snippets. `--section <health\|capacity\|perf>` (comma-separated to combine), `--brief`, `--for-agent`, `--no-connect`. |
@@ -1180,6 +1180,7 @@ dbcli --use prod proxy postgresql # infer target from na
1180
1180
 
1181
1181
  dbcli proxy analyze # analyze .dbcli/proxy/events.jsonl (JSON)
1182
1182
  dbcli proxy analyze --format text --top 10 # human-readable top-10 view
1183
+ dbcli proxy analyze --format markdown # QueryLens shareable, redacted Markdown report
1183
1184
  dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1184
1185
  ```
1185
1186
 
@@ -1200,7 +1201,7 @@ dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1200
1201
 
1201
1202
  **Log rotation:** all writes are serialized through one in-process chain (concurrent sessions never interleave partial lines). The event log auto-rotates to keep one rolling segment — when the next line would reach ~50 MiB or 200,000 entries, the current file is renamed to `<events>.1` (overwriting any prior segment) and a fresh file starts. Worst-case on-disk footprint is ~2× the byte cap.
1202
1203
 
1203
- **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default.
1204
+ **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text|markdown` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default. `markdown` is the QueryLens report: it redacts SQL and error-message literals in an in-memory copy before analysis, while leaving the source log untouched.
1204
1205
 
1205
1206
  Every actionable block carries machine-readable next steps so an agent can move from "what is wrong" to "what to run":
1206
1207
  - `byFingerprint[]` — top-N SELECT entries get `suggestedCommands`: `dbcli explain "<sql>"` and `dbcli guide missing-index-for "<sql>"`.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to dbcli are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.50.0] - 2026-08-06 - QueryLens proxy query analysis
9
+
10
+ ### Added
11
+
12
+ - **QueryLens Markdown report for `dbcli proxy analyze`.** `dbcli proxy analyze --format markdown` now produces a shareable report covering query volume, latency percentiles, expensive fingerprints, slowest queries, errors, and N+1 suspects. It reads the proxy JSONL log offline and includes its rotated segment unless `--no-include-rotated` is supplied.
13
+
14
+ ### Security
15
+
16
+ - **QueryLens redacts report literals independently of capture configuration.** The Markdown path analyzes an in-memory redacted copy of SQL-bearing events, including error messages, so a legacy log captured without `--redact literals` does not expose those values in the report. Use `dbcli proxy <engine> --redact literals` as well to protect the event log itself.
17
+
8
18
  ## [1.49.0] - 2026-08-06 - 欄位遮罩涵蓋攤平與陣列列,且不再為每條規則掃過整個結果集
9
19
 
10
20
  延續 1.48.0 的 blacklist 主題:那一版修的是「哪些物件受保護」,這一版修的是「受保護的欄位到底有沒有真的被遮掉」,以及遮罩本身的成本。兩條安全性修復都屬於 fail-open —— 資料原樣回傳,其中一條連安全通知都不會發。
package/README.md CHANGED
@@ -880,6 +880,7 @@ dbcli proxy mysql --listen 127.0.0.1:3307 --target 127.0.0.1:3306
880
880
  dbcli proxy postgresql --listen 127.0.0.1:5434 --target 127.0.0.1:5432
881
881
  dbcli proxy mysql --slow-ms 500 --redact literals
882
882
  dbcli proxy mariadb --events ./logs/proxy.jsonl
883
+ dbcli proxy analyze --events ./logs/proxy.jsonl --format markdown # QueryLens report
883
884
  ```
884
885
 
885
886
  **Options:**
@@ -890,6 +891,8 @@ dbcli proxy mariadb --events ./logs/proxy.jsonl
890
891
  - `--redact none|literals` — Strip SQL literal values from event records (default: `none`)
891
892
  - `--format text|json` — Startup output format (default: `text`)
892
893
 
894
+ **QueryLens:** `dbcli proxy analyze --format markdown` reads the event log offline and produces a shareable Markdown report. It redacts SQL and error-message literals in memory before analysis; use `--redact literals` while capturing as well to protect the log on disk.
895
+
893
896
  ---
894
897
 
895
898
  #### `dbcli status`
package/assets/SKILL.md CHANGED
@@ -363,7 +363,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
363
363
  | `assert` | query-only+ | **(v1.25)** SQL only. Verify an invariant; exit 1 on failure unless `--no-fail`. `--expect "rows>0\|value==X\|col:c not null\|unique\|between a and b\|>= n"`, `--vs <query> --compare rows\|value` (reconcile), `--against <snapshot> --tolerance <pct>`. |
364
364
  | `verification` | n/a | Inspect and manage local verification artifacts. `list` / `show <id-or-path>` / `summary` are read-only; `prune` is dry-run by default and deletes only with `--execute --force`. Reads `<cwd>/.dbcli/verification/`; no DB connection, no audit writes. |
365
365
  | `backfill artifact` | n/a | Build a bounded, reviewable source-to-SQL backfill artifact from JSON. Includes source/target identity, blacklist/schema preflight, read-back verification, and rollback hints; dry-run only and never executes writes. |
366
- | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
366
+ | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. **(v1.50)** `proxy analyze --format markdown` produces the QueryLens report and redacts SQL/error literals in-memory before analysis; use `proxy <engine> --redact literals` to protect the log itself. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
367
367
  | `status` | query-only+ | Safe JSON/text summary (no credentials). |
368
368
  | `inspect` | query-only+ | Read-only context snapshot (connection, permission, blacklist, objects, snippets, context-aware `suggestedCommands`, and **(v1.23)** human-readable `hints`). `--for-agent` / `--brief` / `--no-connect` / `--require-schema-cache`. Supports `--recovery`. |
369
369
  | `report` | query-only+ | Diagnostic report built from `@diag/*` snippets. `--section <health\|capacity\|perf>` (comma-separated to combine), `--brief`, `--for-agent`, `--no-connect`. |
@@ -276,7 +276,7 @@ dbcli init --conn-name prod --env-file .env.production --use-env-refs --skip-tes
276
276
  | `assert` | query-only+ | **(v1.25)** 僅 SQL。驗證不變量;失敗時 exit 1,除非 `--no-fail`。`--expect "rows>0\|value==X\|col:c not null\|unique\|between a and b\|>= n"`、`--vs <query> --compare rows\|value`(對帳)、`--against <snapshot> --tolerance <pct>`。 |
277
277
  | `verification` | n/a | 檢視與管理本機驗證 artifact。`list` / `show <id-or-path>` / `summary` 為唯讀;`prune` 預設 dry-run,僅在 `--execute --force` 時刪除。讀取 `<cwd>/.dbcli/verification/`;不需 DB 連線,不寫入 audit log。 |
278
278
  | `backfill artifact` | n/a | 將受限 JSON source catalog 產生可檢閱的 source-to-SQL 回填 artifact,包含 source/target identity、blacklist/schema preflight、read-back 驗證與 rollback hint;只產生 dry-run,絕不執行寫入。 |
279
- | `proxy` | n/a | **(v1.26)** 僅 MySQL/MariaDB/PostgreSQL。本地端開發觀測代理 — 中繼應用程式流量至真實資料庫,並將查詢 / 延遲 / 位元組 / 錯誤事件附加到 `.dbcli/proxy/events.jsonl`。子指令:`mysql` \| `mariadb` \| `postgresql`。`--listen`、`--target`、`--events`、`--slow-ms`(預設 `1000`)、`--redact none\|literals`。僅作觀測。**(v1.27)** `proxy analyze` 離線彙整事件 log 為 JSON / 文字報表(summary、byFingerprint 含 suggestedCommands、slowest、errors、hotTables、N+1)— 若無事件則報錯。 |
279
+ | `proxy` | n/a | **(v1.26)** 僅 MySQL/MariaDB/PostgreSQL。本地端開發觀測代理 — 中繼應用程式流量至真實資料庫,並將查詢 / 延遲 / 位元組 / 錯誤事件附加到 `.dbcli/proxy/events.jsonl`。子指令:`mysql` \| `mariadb` \| `postgresql`。`--listen`、`--target`、`--events`、`--slow-ms`(預設 `1000`)、`--redact none\|literals`。僅作觀測。**(v1.27)** `proxy analyze` 離線彙整事件 log 為 JSON / 文字報表(summary、byFingerprint 含 suggestedCommands、slowest、errors、hotTables、N+1)— 若無事件則報錯。**(v1.50)** `proxy analyze --format markdown` 產生 QueryLens 報告,分析前會在記憶體中遮罩 SQL/錯誤 literal;仍應以 `proxy <engine> --redact literals` 保護事件日誌本身。 |
280
280
  | `status` | query-only+ | 安全 JSON / 文字摘要(不含憑證)。 |
281
281
  | `inspect` | query-only+ | 唯讀脈絡快照(連線、權限、blacklist、物件、snippets、建議指令,以及 **(v1.23)** 人類可讀 `hints`)。`--for-agent` / `--brief` / `--no-connect` / `--require-schema-cache`。支援 `--recovery`。 |
282
282
  | `report` | query-only+ | 以 `@diag/*` snippet 組成的診斷報告。`--section <health\|capacity\|perf>`(可用逗號組合)、`--brief`、`--for-agent`、`--no-connect`。 |
@@ -1180,6 +1180,7 @@ dbcli --use prod proxy postgresql # infer target from na
1180
1180
 
1181
1181
  dbcli proxy analyze # analyze .dbcli/proxy/events.jsonl (JSON)
1182
1182
  dbcli proxy analyze --format text --top 10 # human-readable top-10 view
1183
+ dbcli proxy analyze --format markdown # QueryLens shareable, redacted Markdown report
1183
1184
  dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1184
1185
  ```
1185
1186
 
@@ -1200,7 +1201,7 @@ dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1200
1201
 
1201
1202
  **Log rotation:** all writes are serialized through one in-process chain (concurrent sessions never interleave partial lines). The event log auto-rotates to keep one rolling segment — when the next line would reach ~50 MiB or 200,000 entries, the current file is renamed to `<events>.1` (overwriting any prior segment) and a fresh file starts. Worst-case on-disk footprint is ~2× the byte cap.
1202
1203
 
1203
- **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default.
1204
+ **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text|markdown` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default. `markdown` is the QueryLens report: it redacts SQL and error-message literals in an in-memory copy before analysis, while leaving the source log untouched.
1204
1205
 
1205
1206
  Every actionable block carries machine-readable next steps so an agent can move from "what is wrong" to "what to run":
1206
1207
  - `byFingerprint[]` — top-N SELECT entries get `suggestedCommands`: `dbcli explain "<sql>"` and `dbcli guide missing-index-for "<sql>"`.
package/dist/cli.mjs CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@carllee1983/dbcli",
55
- version: "1.49.0",
55
+ version: "1.50.0",
56
56
  description: "Database CLI for AI agents",
57
57
  type: "module",
58
58
  publishConfig: {
@@ -106615,6 +106615,86 @@ function renderAnalysisText(report, top) {
106615
106615
  `);
106616
106616
  }
106617
106617
 
106618
+ // src/querylens/analyze.ts
106619
+ function hasSql2(event) {
106620
+ return event.type === "query_observed" || event.type === "query_completed" || event.type === "query_errored";
106621
+ }
106622
+ function redactEventsForAnalysis(events) {
106623
+ return events.map((event) => {
106624
+ if (event.type === "query_errored") {
106625
+ return {
106626
+ ...event,
106627
+ sql: redactLiterals(event.sql),
106628
+ error: { ...event.error, message: redactLiterals(event.error.message) }
106629
+ };
106630
+ }
106631
+ if (hasSql2(event))
106632
+ return { ...event, sql: redactLiterals(event.sql) };
106633
+ return event;
106634
+ });
106635
+ }
106636
+ function analyzeQuerylensEvents(events, opts) {
106637
+ return {
106638
+ ...analyzeEvents(redactEventsForAnalysis(events), opts),
106639
+ querylens: { name: "querylens", version: QUERYLENS_VERSION }
106640
+ };
106641
+ }
106642
+ var QUERYLENS_VERSION = "0.1.0";
106643
+ var init_analyze2 = __esm(() => {
106644
+ init_analyze();
106645
+ init_sql_metadata();
106646
+ });
106647
+
106648
+ // src/querylens/render.ts
106649
+ function sqlInline(sql) {
106650
+ return sql.replace(/\s+/g, " ").trim().replace(/`/g, "\\`");
106651
+ }
106652
+ function none(lines, values) {
106653
+ if (values.length === 0)
106654
+ lines.push("_None._");
106655
+ }
106656
+ function renderQuerylensMarkdown(report, top) {
106657
+ const lines = [
106658
+ "# QueryLens report",
106659
+ "",
106660
+ "## Summary",
106661
+ "",
106662
+ `- Sessions: ${report.summary.sessions}`,
106663
+ `- Queries: ${report.summary.queries}`,
106664
+ `- Errors: ${report.summary.errors} (${(report.summary.errorRate * 100).toFixed(2)}%)`,
106665
+ `- Slow queries: ${report.summary.slowCount}`,
106666
+ `- Latency: p50 ${report.summary.latencyMs.p50}ms, p95 ${report.summary.latencyMs.p95}ms, p99 ${report.summary.latencyMs.p99}ms, max ${report.summary.latencyMs.max}ms`,
106667
+ "",
106668
+ "## Top expensive fingerprints",
106669
+ ""
106670
+ ];
106671
+ const fingerprints = report.byFingerprint.slice(0, top);
106672
+ none(lines, fingerprints);
106673
+ for (const item of fingerprints) {
106674
+ lines.push(`- ${item.count}\xD7 \xB7 ${item.durationMs.total}ms total \xB7 ${item.durationMs.avg}ms avg \xB7 \`${sqlInline(item.fingerprint)}\``);
106675
+ }
106676
+ lines.push("", "## Slowest queries", "");
106677
+ const slowest = report.slowest.slice(0, top);
106678
+ none(lines, slowest);
106679
+ for (const query of slowest)
106680
+ lines.push(`- ${query.durationMs}ms \xB7 \`${sqlInline(query.sql)}\``);
106681
+ lines.push("", "## Errors", "");
106682
+ const errors3 = report.errors.slice(0, top);
106683
+ none(lines, errors3);
106684
+ for (const error of errors3) {
106685
+ lines.push(`- ${error.count}\xD7 \xB7 [${error.code ?? "?"}] ${error.message} \xB7 \`${sqlInline(error.fingerprint)}\``);
106686
+ }
106687
+ lines.push("", "## N+1 suspects", "");
106688
+ const repetition = report.repetition.slice(0, top);
106689
+ none(lines, repetition);
106690
+ for (const item of repetition) {
106691
+ lines.push(`- ${item.count}\xD7 in session ${item.sessionId} over ${item.spanMs}ms \xB7 \`${sqlInline(item.fingerprint)}\``);
106692
+ }
106693
+ return `${lines.join(`
106694
+ `)}
106695
+ `;
106696
+ }
106697
+
106618
106698
  // src/commands/proxy.ts
106619
106699
  import { join as join38 } from "path";
106620
106700
  function parseHostPort(value) {
@@ -106753,19 +106833,24 @@ async function runAnalyze(options) {
106753
106833
  if (files.length === 0) {
106754
106834
  throw new Error(`no events found at ${eventsPath}; run 'dbcli proxy <engine>' first`);
106755
106835
  }
106756
- const report = analyzeEvents(events, {
106836
+ const analyzeOptions = {
106757
106837
  slowMs,
106758
106838
  top,
106759
106839
  nPlusOne,
106760
106840
  sourceFiles: files,
106761
106841
  malformedLines
106762
- });
106763
- if (format === "text") {
106764
- process.stdout.write(renderAnalysisText(report, top) + `
106765
- `);
106842
+ };
106843
+ if (format === "markdown") {
106844
+ process.stdout.write(renderQuerylensMarkdown(analyzeQuerylensEvents(events, analyzeOptions), top));
106766
106845
  } else {
106767
- process.stdout.write(JSON.stringify(report, null, 2) + `
106846
+ const report = analyzeEvents(events, analyzeOptions);
106847
+ if (format === "text") {
106848
+ process.stdout.write(renderAnalysisText(report, top) + `
106849
+ `);
106850
+ } else {
106851
+ process.stdout.write(JSON.stringify(report, null, 2) + `
106768
106852
  `);
106853
+ }
106769
106854
  }
106770
106855
  } catch (error) {
106771
106856
  if (error instanceof Error)
@@ -106782,6 +106867,7 @@ var init_proxy = __esm(() => {
106782
106867
  init_server();
106783
106868
  init_event_reader();
106784
106869
  init_analyze();
106870
+ init_analyze2();
106785
106871
  SUPPORTED = ["mysql", "mariadb", "postgresql"];
106786
106872
  ALLOWED_FORMATS21 = ["text", "json"];
106787
106873
  ALLOWED_REDACT = ["none", "literals"];
@@ -106792,8 +106878,8 @@ var init_proxy = __esm(() => {
106792
106878
  await runProxy(engine, options, command);
106793
106879
  });
106794
106880
  }
106795
- ANALYZE_FORMATS = ["json", "text"];
106796
- proxyCommand.command("analyze").description("Analyze a proxy event log offline (no DB connection)").option("--events <path>", "Event JSONL path", join38(".dbcli", "proxy", "events.jsonl")).option("--format <format>", "Output format: json | text", "json").option("--top <number>", "Rows shown in text + suggestedCommands depth", "20").option("--slow-ms <number>", "Slow-query threshold (ms) for slowCount", "1000").option("--n-plus-one <number>", "Min repeats per (session,fingerprint) to flag N+1", "10").option("--no-include-rotated", "Do not merge the rotated <events>.1 segment").action(async (options) => {
106881
+ ANALYZE_FORMATS = ["json", "text", "markdown"];
106882
+ proxyCommand.command("analyze").description("Analyze a proxy event log offline (no DB connection)").option("--events <path>", "Event JSONL path", join38(".dbcli", "proxy", "events.jsonl")).option("--format <format>", "Output format: json | text | markdown (QueryLens)", "json").option("--top <number>", "Rows shown in text + suggestedCommands depth", "20").option("--slow-ms <number>", "Slow-query threshold (ms) for slowCount", "1000").option("--n-plus-one <number>", "Min repeats per (session,fingerprint) to flag N+1", "10").option("--no-include-rotated", "Do not merge the rotated <events>.1 segment").action(async (options) => {
106797
106883
  await runAnalyze(options);
106798
106884
  });
106799
106885
  addCommonOptions(proxyCommand).action(async (options, command) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carllee1983/dbcli",
3
- "version": "1.49.0",
3
+ "version": "1.50.0",
4
4
  "description": "Database CLI for AI agents",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbcli-agent",
3
- "version": "1.49.0",
3
+ "version": "1.50.0",
4
4
  "description": "Database CLI skill and command reference for AI agents",
5
5
  "author": {
6
6
  "name": "Carl Lee",
@@ -363,7 +363,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
363
363
  | `assert` | query-only+ | **(v1.25)** SQL only. Verify an invariant; exit 1 on failure unless `--no-fail`. `--expect "rows>0\|value==X\|col:c not null\|unique\|between a and b\|>= n"`, `--vs <query> --compare rows\|value` (reconcile), `--against <snapshot> --tolerance <pct>`. |
364
364
  | `verification` | n/a | Inspect and manage local verification artifacts. `list` / `show <id-or-path>` / `summary` are read-only; `prune` is dry-run by default and deletes only with `--execute --force`. Reads `<cwd>/.dbcli/verification/`; no DB connection, no audit writes. |
365
365
  | `backfill artifact` | n/a | Build a bounded, reviewable source-to-SQL backfill artifact from JSON. Includes source/target identity, blacklist/schema preflight, read-back verification, and rollback hints; dry-run only and never executes writes. |
366
- | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
366
+ | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. **(v1.50)** `proxy analyze --format markdown` produces the QueryLens report and redacts SQL/error literals in-memory before analysis; use `proxy <engine> --redact literals` to protect the log itself. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
367
367
  | `status` | query-only+ | Safe JSON/text summary (no credentials). |
368
368
  | `inspect` | query-only+ | Read-only context snapshot (connection, permission, blacklist, objects, snippets, context-aware `suggestedCommands`, and **(v1.23)** human-readable `hints`). `--for-agent` / `--brief` / `--no-connect` / `--require-schema-cache`. Supports `--recovery`. |
369
369
  | `report` | query-only+ | Diagnostic report built from `@diag/*` snippets. `--section <health\|capacity\|perf>` (comma-separated to combine), `--brief`, `--for-agent`, `--no-connect`. |
@@ -1180,6 +1180,7 @@ dbcli --use prod proxy postgresql # infer target from na
1180
1180
 
1181
1181
  dbcli proxy analyze # analyze .dbcli/proxy/events.jsonl (JSON)
1182
1182
  dbcli proxy analyze --format text --top 10 # human-readable top-10 view
1183
+ dbcli proxy analyze --format markdown # QueryLens shareable, redacted Markdown report
1183
1184
  dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1184
1185
  ```
1185
1186
 
@@ -1200,7 +1201,7 @@ dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1200
1201
 
1201
1202
  **Log rotation:** all writes are serialized through one in-process chain (concurrent sessions never interleave partial lines). The event log auto-rotates to keep one rolling segment — when the next line would reach ~50 MiB or 200,000 entries, the current file is renamed to `<events>.1` (overwriting any prior segment) and a fresh file starts. Worst-case on-disk footprint is ~2× the byte cap.
1202
1203
 
1203
- **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default.
1204
+ **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text|markdown` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default. `markdown` is the QueryLens report: it redacts SQL and error-message literals in an in-memory copy before analysis, while leaving the source log untouched.
1204
1205
 
1205
1206
  Every actionable block carries machine-readable next steps so an agent can move from "what is wrong" to "what to run":
1206
1207
  - `byFingerprint[]` — top-N SELECT entries get `suggestedCommands`: `dbcli explain "<sql>"` and `dbcli guide missing-index-for "<sql>"`.
@@ -363,7 +363,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
363
363
  | `assert` | query-only+ | **(v1.25)** SQL only. Verify an invariant; exit 1 on failure unless `--no-fail`. `--expect "rows>0\|value==X\|col:c not null\|unique\|between a and b\|>= n"`, `--vs <query> --compare rows\|value` (reconcile), `--against <snapshot> --tolerance <pct>`. |
364
364
  | `verification` | n/a | Inspect and manage local verification artifacts. `list` / `show <id-or-path>` / `summary` are read-only; `prune` is dry-run by default and deletes only with `--execute --force`. Reads `<cwd>/.dbcli/verification/`; no DB connection, no audit writes. |
365
365
  | `backfill artifact` | n/a | Build a bounded, reviewable source-to-SQL backfill artifact from JSON. Includes source/target identity, blacklist/schema preflight, read-back verification, and rollback hints; dry-run only and never executes writes. |
366
- | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
366
+ | `proxy` | n/a | **(v1.26)** MySQL/MariaDB/PostgreSQL only. Local-dev observability proxy — relays app traffic to the real DB and appends query/latency/byte/error events to `.dbcli/proxy/events.jsonl`. Subcommands: `mysql` \| `mariadb` \| `postgresql`. `--listen`, `--target`, `--events` (default `.dbcli/proxy/events.jsonl`), `--slow-ms` (default `1000`), `--redact none\|literals` (default `none`). Observe-only. **(v1.27)** `proxy analyze` aggregates the event log offline into a JSON/text report (summary, byFingerprint, slowest, errors, hotTables, N+1) — `--format`, `--top`, `--slow-ms`, `--n-plus-one`; errors out if no events exist yet. **(v1.50)** `proxy analyze --format markdown` produces the QueryLens report and redacts SQL/error literals in-memory before analysis; use `proxy <engine> --redact literals` to protect the log itself. Actionable blocks carry `suggestedCommands` + `hints` so an agent can act: SELECT hotspots/N+1 → `explain` / `guide missing-index-for`, errors → `schema <table>` (verify names, never guess), N+1 → batch (JOIN / `IN (...)`). After analyzing, run each finding's `suggestedCommands`, read its `hints`, then propose the fix. |
367
367
  | `status` | query-only+ | Safe JSON/text summary (no credentials). |
368
368
  | `inspect` | query-only+ | Read-only context snapshot (connection, permission, blacklist, objects, snippets, context-aware `suggestedCommands`, and **(v1.23)** human-readable `hints`). `--for-agent` / `--brief` / `--no-connect` / `--require-schema-cache`. Supports `--recovery`. |
369
369
  | `report` | query-only+ | Diagnostic report built from `@diag/*` snippets. `--section <health\|capacity\|perf>` (comma-separated to combine), `--brief`, `--for-agent`, `--no-connect`. |
@@ -1180,6 +1180,7 @@ dbcli --use prod proxy postgresql # infer target from na
1180
1180
 
1181
1181
  dbcli proxy analyze # analyze .dbcli/proxy/events.jsonl (JSON)
1182
1182
  dbcli proxy analyze --format text --top 10 # human-readable top-10 view
1183
+ dbcli proxy analyze --format markdown # QueryLens shareable, redacted Markdown report
1183
1184
  dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1184
1185
  ```
1185
1186
 
@@ -1200,7 +1201,7 @@ dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
1200
1201
 
1201
1202
  **Log rotation:** all writes are serialized through one in-process chain (concurrent sessions never interleave partial lines). The event log auto-rotates to keep one rolling segment — when the next line would reach ~50 MiB or 200,000 entries, the current file is renamed to `<events>.1` (overwriting any prior segment) and a fresh file starts. Worst-case on-disk footprint is ~2× the byte cap.
1202
1203
 
1203
- **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default.
1204
+ **`proxy analyze`** — offline aggregation of the event log (no DB). Flags: `--events <path>` (default `.dbcli/proxy/events.jsonl`), `--format json|text|markdown` (default `json`), `--top <n>` (default 20; text rows + suggestedCommands depth), `--slow-ms <ms>` (default 1000; recomputes slowCount), `--n-plus-one <n>` (default 10), `--no-include-rotated`. JSON report blocks: `summary`, `byFingerprint` (sorted by total time; SELECT entries in the top-N carry `suggestedCommands` for `explain` / `guide missing-index-for`), `slowest`, `errors`, `hotTables`, `repetition` (N+1 suspects). Reads the current log plus the rotated `.1` segment by default. `markdown` is the QueryLens report: it redacts SQL and error-message literals in an in-memory copy before analysis, while leaving the source log untouched.
1204
1205
 
1205
1206
  Every actionable block carries machine-readable next steps so an agent can move from "what is wrong" to "what to run":
1206
1207
  - `byFingerprint[]` — top-N SELECT entries get `suggestedCommands`: `dbcli explain "<sql>"` and `dbcli guide missing-index-for "<sql>"`.