@carllee1983/dbcli 1.49.0 → 1.51.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.51.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.51.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`. |
@@ -375,6 +375,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
375
375
  | `upgrade` | n/a | Self-update from npm; 24h-cached version hints on every command. |
376
376
  | `shell` | (same as query+) | Interactive REPL. SQL engines, MongoDB, and Redis (single-line; `.no-limit on/off`). **(v1.22)** Elasticsearch opens a Kibana Dev Tools-style REPL (`<METHOD> /<path>` + optional JSON body, blank line submits). |
377
377
  | `skill` | n/a | Generate / install AI skill docs (`--install <claude\|gemini\|antigravity\|copilot\|cursor\|codex\|windsurf>`); `skill tasks list/show/plan` for Agent Task Packs; `skill context` for an LLM prompt-context payload (for injecting into another LLM, not needed for normal operation). |
378
+ | `semantic` | n/a | Validate, search, inspect drift, migrate to v2, or print the optional project-root `dbcli.semantic.json`. Give its reviewed context to an external agent, but keep provider credentials, prompts, and agent context outside dbcli. `semantic draft validate --input <file|-> [--format text\|json]` validates only the explicit untrusted `QueryDraft` offline against local semantic/schema/saved-query metadata; it returns safe hashes/references/violation codes, never executes or echoes candidate SQL. Review the original draft, then invoke `explain` or `query` separately if intended. |
378
379
  | `migrate` | admin | SQL only. **DDL; dry-run by default** — needs `--execute`. |
379
380
 
380
381
  Use root-level `dbcli --use <name> <command>` for any command; `query`, `schema`, `list`,
@@ -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>"`.
@@ -2189,6 +2190,124 @@ dbcli migrate drop-enum status --execute --force
2189
2190
 
2190
2191
  **AI agent note:** Always use dry-run first (no `--execute`) to preview generated SQL. Only add `--execute` after confirming the SQL is correct. For DROP operations, both `--execute` and `--force` are required.
2191
2192
 
2193
+ ### semantic
2194
+
2195
+ Validate or print the optional, version-controlled `dbcli.semantic.json` in the
2196
+ project root. It supplies business names and descriptions to an agent, but is
2197
+ not a query language: these commands are offline, read-only, and never execute
2198
+ SQL or contact an LLM.
2199
+
2200
+ ```bash
2201
+ dbcli semantic validate
2202
+ dbcli semantic validate --format json
2203
+ dbcli semantic context
2204
+ dbcli semantic context --format markdown
2205
+ dbcli semantic context --file ./analytics.semantic.json
2206
+ dbcli semantic drift --format json
2207
+ dbcli semantic migrate --to 2 --format json
2208
+ dbcli semantic search purchases --kind model --format json
2209
+ dbcli semantic draft validate --input ./draft.json --format json
2210
+ external-agent | dbcli semantic draft validate --input - --format json
2211
+ ```
2212
+
2213
+ The default file has this compact contract:
2214
+
2215
+ ```json
2216
+ {
2217
+ "version": 2,
2218
+ "models": [
2219
+ {
2220
+ "name": "orders",
2221
+ "table": "orders",
2222
+ "description": "Completed purchases.",
2223
+ "aliases": ["purchases"],
2224
+ "fields": [
2225
+ { "column": "created_at", "aliases": ["order date"] },
2226
+ { "column": "customer_id" }
2227
+ ]
2228
+ },
2229
+ {
2230
+ "name": "customers",
2231
+ "table": "customers",
2232
+ "fields": [{ "column": "id" }]
2233
+ }
2234
+ ],
2235
+ "relationships": [
2236
+ {
2237
+ "name": "order-customer",
2238
+ "from": { "model": "orders", "field": "customer_id" },
2239
+ "to": { "model": "customers", "field": "id" },
2240
+ "cardinality": "many-to-one",
2241
+ "description": "Each order belongs to one customer."
2242
+ }
2243
+ ],
2244
+ "metrics": [
2245
+ { "name": "daily-revenue", "query": "@analytics/revenue" }
2246
+ ]
2247
+ }
2248
+ ```
2249
+
2250
+ Version 1 remains supported and is normalized with `relationships: []`.
2251
+ Version 2 relationships must reference a declared model and a declared field on
2252
+ that model; the field must also be visible in cached schema. Their `cardinality`
2253
+ is one of `one-to-one`, `one-to-many`, `many-to-one`, or `many-to-many`.
2254
+ `models[].table` and `models[].fields[].column` must name a visible cached
2255
+ schema object. Blacklisted tables and columns are not visible and are rejected.
2256
+ Each metric `query` must name an available saved query. Validation parses its
2257
+ local file through the normal saved-query safety checks, but never executes or
2258
+ emits SQL. The semantic file cannot contain SQL, connection data, or
2259
+ credentials. `semantic validate` reports a
2260
+ deterministic success summary (`--format text|json`); `semantic context` prints
2261
+ the validated context (`--format json|markdown`). `semantic drift` returns a
2262
+ stable `valid`, `stale`, `invalid`, or `unavailable` report and exits non-zero
2263
+ for every status except `valid`; it never connects to a database. `semantic
2264
+ migrate --to 2` prints a deterministic v2 JSON document to stdout and never
2265
+ writes the input file. An absent default file is allowed for `skill context`
2266
+ and simply omits the semantic section; a stale or invalid present file fails
2267
+ closed rather than being silently ignored.
2268
+
2269
+ `semantic search <terms...>` performs deterministic, case-insensitive matching
2270
+ over canonical names, aliases, descriptions, and governed model paths. Results
2271
+ are ranked by exact canonical name, exact alias, prefix, then description token;
2272
+ kind/name breaks ties. Use `--kind model|field|relationship|metric` and
2273
+ `--limit <1-100>` (default `20`). It returns only canonical references, matched
2274
+ terms, aliases, descriptions, and necessary model paths—never SQL bodies,
2275
+ connection data, or blacklist names. No result is an empty array / text notice
2276
+ with exit 0.
2277
+
2278
+ `semantic draft validate --input <file|-> [--format text|json]` accepts only an
2279
+ explicit untrusted `QueryDraft` JSON document from a file or stdin. First give
2280
+ the external agent reviewed `dbcli semantic context --format json` output; its
2281
+ provider credentials, prompt, and other agent context remain outside dbcli. The
2282
+ agent returns a draft using only the declared models and fields, for example:
2283
+
2284
+ ```json
2285
+ {
2286
+ "version": 1,
2287
+ "questionHash": "<sha256-of-the-original-question>",
2288
+ "candidate": { "kind": "sql", "sql": "<reviewed-read-only-sql>" },
2289
+ "semanticReferences": ["model:<model>", "field:<model>.<field>"]
2290
+ }
2291
+ ```
2292
+
2293
+ The command validates the contract, read-only single-statement SQL, canonical
2294
+ semantic references, saved-query names, and local filtered schema/blacklist
2295
+ compatibility without connecting to a database, reading query results, storing
2296
+ the draft, or calling a provider. JSON reports contain only status, hashes,
2297
+ canonical references, and safe violation codes; they never echo candidate SQL
2298
+ or protected names. Exit `0` means valid, `1` means rejected, and `2` means
2299
+ required local semantic evidence is unavailable. A valid report is not execution
2300
+ authorization: review the original draft, then separately and explicitly invoke
2301
+ `dbcli explain "<reviewed-read-only-sql>"` or `dbcli query
2302
+ "<reviewed-read-only-sql>"` when appropriate.
2303
+
2304
+ When valid, `dbcli skill context` includes the same bounded data in its JSON,
2305
+ XML, and Markdown output. To run a metric, an agent must still invoke the named
2306
+ saved query through `dbcli q`; all ordinary permissions, blacklist masking,
2307
+ limits, audit, and recovery safeguards remain in force.
2308
+
2309
+ **Permission:** n/a (local files only; no database connection).
2310
+
2192
2311
  ### skill
2193
2312
 
2194
2313
  Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or an
@@ -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.51.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`. |
@@ -375,6 +375,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
375
375
  | `upgrade` | n/a | Self-update from npm; 24h-cached version hints on every command. |
376
376
  | `shell` | (same as query+) | Interactive REPL. SQL engines, MongoDB, and Redis (single-line; `.no-limit on/off`). **(v1.22)** Elasticsearch opens a Kibana Dev Tools-style REPL (`<METHOD> /<path>` + optional JSON body, blank line submits). |
377
377
  | `skill` | n/a | Generate / install AI skill docs (`--install <claude\|gemini\|antigravity\|copilot\|cursor\|codex\|windsurf>`); `skill tasks list/show/plan` for Agent Task Packs; `skill context` for an LLM prompt-context payload (for injecting into another LLM, not needed for normal operation). |
378
+ | `semantic` | n/a | Validate, search, inspect drift, migrate to v2, or print the optional project-root `dbcli.semantic.json`. Give its reviewed context to an external agent, but keep provider credentials, prompts, and agent context outside dbcli. `semantic draft validate --input <file|-> [--format text\|json]` validates only the explicit untrusted `QueryDraft` offline against local semantic/schema/saved-query metadata; it returns safe hashes/references/violation codes, never executes or echoes candidate SQL. Review the original draft, then invoke `explain` or `query` separately if intended. |
378
379
  | `migrate` | admin | SQL only. **DDL; dry-run by default** — needs `--execute`. |
379
380
 
380
381
  Use root-level `dbcli --use <name> <command>` for any command; `query`, `schema`, `list`,
@@ -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>"`.
@@ -2189,6 +2190,124 @@ dbcli migrate drop-enum status --execute --force
2189
2190
 
2190
2191
  **AI agent note:** Always use dry-run first (no `--execute`) to preview generated SQL. Only add `--execute` after confirming the SQL is correct. For DROP operations, both `--execute` and `--force` are required.
2191
2192
 
2193
+ ### semantic
2194
+
2195
+ Validate or print the optional, version-controlled `dbcli.semantic.json` in the
2196
+ project root. It supplies business names and descriptions to an agent, but is
2197
+ not a query language: these commands are offline, read-only, and never execute
2198
+ SQL or contact an LLM.
2199
+
2200
+ ```bash
2201
+ dbcli semantic validate
2202
+ dbcli semantic validate --format json
2203
+ dbcli semantic context
2204
+ dbcli semantic context --format markdown
2205
+ dbcli semantic context --file ./analytics.semantic.json
2206
+ dbcli semantic drift --format json
2207
+ dbcli semantic migrate --to 2 --format json
2208
+ dbcli semantic search purchases --kind model --format json
2209
+ dbcli semantic draft validate --input ./draft.json --format json
2210
+ external-agent | dbcli semantic draft validate --input - --format json
2211
+ ```
2212
+
2213
+ The default file has this compact contract:
2214
+
2215
+ ```json
2216
+ {
2217
+ "version": 2,
2218
+ "models": [
2219
+ {
2220
+ "name": "orders",
2221
+ "table": "orders",
2222
+ "description": "Completed purchases.",
2223
+ "aliases": ["purchases"],
2224
+ "fields": [
2225
+ { "column": "created_at", "aliases": ["order date"] },
2226
+ { "column": "customer_id" }
2227
+ ]
2228
+ },
2229
+ {
2230
+ "name": "customers",
2231
+ "table": "customers",
2232
+ "fields": [{ "column": "id" }]
2233
+ }
2234
+ ],
2235
+ "relationships": [
2236
+ {
2237
+ "name": "order-customer",
2238
+ "from": { "model": "orders", "field": "customer_id" },
2239
+ "to": { "model": "customers", "field": "id" },
2240
+ "cardinality": "many-to-one",
2241
+ "description": "Each order belongs to one customer."
2242
+ }
2243
+ ],
2244
+ "metrics": [
2245
+ { "name": "daily-revenue", "query": "@analytics/revenue" }
2246
+ ]
2247
+ }
2248
+ ```
2249
+
2250
+ Version 1 remains supported and is normalized with `relationships: []`.
2251
+ Version 2 relationships must reference a declared model and a declared field on
2252
+ that model; the field must also be visible in cached schema. Their `cardinality`
2253
+ is one of `one-to-one`, `one-to-many`, `many-to-one`, or `many-to-many`.
2254
+ `models[].table` and `models[].fields[].column` must name a visible cached
2255
+ schema object. Blacklisted tables and columns are not visible and are rejected.
2256
+ Each metric `query` must name an available saved query. Validation parses its
2257
+ local file through the normal saved-query safety checks, but never executes or
2258
+ emits SQL. The semantic file cannot contain SQL, connection data, or
2259
+ credentials. `semantic validate` reports a
2260
+ deterministic success summary (`--format text|json`); `semantic context` prints
2261
+ the validated context (`--format json|markdown`). `semantic drift` returns a
2262
+ stable `valid`, `stale`, `invalid`, or `unavailable` report and exits non-zero
2263
+ for every status except `valid`; it never connects to a database. `semantic
2264
+ migrate --to 2` prints a deterministic v2 JSON document to stdout and never
2265
+ writes the input file. An absent default file is allowed for `skill context`
2266
+ and simply omits the semantic section; a stale or invalid present file fails
2267
+ closed rather than being silently ignored.
2268
+
2269
+ `semantic search <terms...>` performs deterministic, case-insensitive matching
2270
+ over canonical names, aliases, descriptions, and governed model paths. Results
2271
+ are ranked by exact canonical name, exact alias, prefix, then description token;
2272
+ kind/name breaks ties. Use `--kind model|field|relationship|metric` and
2273
+ `--limit <1-100>` (default `20`). It returns only canonical references, matched
2274
+ terms, aliases, descriptions, and necessary model paths—never SQL bodies,
2275
+ connection data, or blacklist names. No result is an empty array / text notice
2276
+ with exit 0.
2277
+
2278
+ `semantic draft validate --input <file|-> [--format text|json]` accepts only an
2279
+ explicit untrusted `QueryDraft` JSON document from a file or stdin. First give
2280
+ the external agent reviewed `dbcli semantic context --format json` output; its
2281
+ provider credentials, prompt, and other agent context remain outside dbcli. The
2282
+ agent returns a draft using only the declared models and fields, for example:
2283
+
2284
+ ```json
2285
+ {
2286
+ "version": 1,
2287
+ "questionHash": "<sha256-of-the-original-question>",
2288
+ "candidate": { "kind": "sql", "sql": "<reviewed-read-only-sql>" },
2289
+ "semanticReferences": ["model:<model>", "field:<model>.<field>"]
2290
+ }
2291
+ ```
2292
+
2293
+ The command validates the contract, read-only single-statement SQL, canonical
2294
+ semantic references, saved-query names, and local filtered schema/blacklist
2295
+ compatibility without connecting to a database, reading query results, storing
2296
+ the draft, or calling a provider. JSON reports contain only status, hashes,
2297
+ canonical references, and safe violation codes; they never echo candidate SQL
2298
+ or protected names. Exit `0` means valid, `1` means rejected, and `2` means
2299
+ required local semantic evidence is unavailable. A valid report is not execution
2300
+ authorization: review the original draft, then separately and explicitly invoke
2301
+ `dbcli explain "<reviewed-read-only-sql>"` or `dbcli query
2302
+ "<reviewed-read-only-sql>"` when appropriate.
2303
+
2304
+ When valid, `dbcli skill context` includes the same bounded data in its JSON,
2305
+ XML, and Markdown output. To run a metric, an agent must still invoke the named
2306
+ saved query through `dbcli q`; all ordinary permissions, blacklist masking,
2307
+ limits, audit, and recovery safeguards remain in force.
2308
+
2309
+ **Permission:** n/a (local files only; no database connection).
2310
+
2192
2311
  ### skill
2193
2312
 
2194
2313
  Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or an
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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.51.0] - 2026-08-07 - Local semantic context and offline query-draft validation
9
+
10
+ ### Added
11
+
12
+ - **Business semantic context commands.** Add the offline, read-only `dbcli semantic validate`, `context`, `search`, `drift`, and `migrate` commands for a reviewable `dbcli.semantic.json`. Semantic models, metrics, aliases, and v2 relationships are checked against the cached visible schema and saved-query names; v1 files remain supported and `migrate --to 2` writes only to stdout.
13
+ - **Deterministic governed semantic search and relationship drift checks.** `semantic search` returns only reviewed metadata, removes blacklist names from free-text results, and supports bounded result counts. `semantic drift` identifies stale, invalid, or unavailable local semantic evidence, including relationship references that no longer match declared visible fields.
14
+ - **Offline validation boundary for agent query drafts.** `dbcli semantic draft validate --input <file|->` accepts an explicit untrusted JSON draft and validates its references and read-only SQL without executing it, persisting it, or calling a provider. Reports contain hashes, canonical references, and safe violation codes rather than candidate SQL; a successful validation is explicitly not permission to execute.
15
+ - **Semantic context in agent-facing skill context.** `dbcli skill context` includes the validated semantic context when present, after blacklist filtering, so agents receive only governed schema and semantic metadata.
16
+
17
+ ## [1.50.0] - 2026-08-06 - QueryLens proxy query analysis
18
+
19
+ ### Added
20
+
21
+ - **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.
22
+
23
+ ### Security
24
+
25
+ - **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.
26
+
8
27
  ## [1.49.0] - 2026-08-06 - 欄位遮罩涵蓋攤平與陣列列,且不再為每條規則掃過整個結果集
9
28
 
10
29
  延續 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`. |
@@ -375,6 +375,7 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
375
375
  | `upgrade` | n/a | Self-update from npm; 24h-cached version hints on every command. |
376
376
  | `shell` | (same as query+) | Interactive REPL. SQL engines, MongoDB, and Redis (single-line; `.no-limit on/off`). **(v1.22)** Elasticsearch opens a Kibana Dev Tools-style REPL (`<METHOD> /<path>` + optional JSON body, blank line submits). |
377
377
  | `skill` | n/a | Generate / install AI skill docs (`--install <claude\|gemini\|antigravity\|copilot\|cursor\|codex\|windsurf>`); `skill tasks list/show/plan` for Agent Task Packs; `skill context` for an LLM prompt-context payload (for injecting into another LLM, not needed for normal operation). |
378
+ | `semantic` | n/a | Validate, search, inspect drift, migrate to v2, or print the optional project-root `dbcli.semantic.json`. Give its reviewed context to an external agent, but keep provider credentials, prompts, and agent context outside dbcli. `semantic draft validate --input <file|-> [--format text\|json]` validates only the explicit untrusted `QueryDraft` offline against local semantic/schema/saved-query metadata; it returns safe hashes/references/violation codes, never executes or echoes candidate SQL. Review the original draft, then invoke `explain` or `query` separately if intended. |
378
379
  | `migrate` | admin | SQL only. **DDL; dry-run by default** — needs `--execute`. |
379
380
 
380
381
  Use root-level `dbcli --use <name> <command>` for any command; `query`, `schema`, `list`,
@@ -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`。 |
@@ -288,6 +288,7 @@ dbcli init --conn-name prod --env-file .env.production --use-env-refs --skip-tes
288
288
  | `upgrade` | n/a | 從 npm 自我更新;每個指令都帶 24h 快取的版本提示。 |
289
289
  | `shell` | (與 query 同) | 互動式 REPL。SQL 引擎、MongoDB 與 Redis(單行;`.no-limit on/off`)。**(v1.22)** Elasticsearch 開啟 Kibana Dev Tools 風格的 REPL(`<METHOD> /<path>` + 可選 JSON body,空白行送出)。 |
290
290
  | `skill` | n/a | 產出 / 安裝 AI skill 文件(`--install <claude\|gemini\|antigravity\|copilot\|cursor\|codex\|windsurf>`);`skill tasks list/show/plan` 提供 Agent Task Packs;`skill context` 提供 LLM 提示詞脈絡載荷(用於注入其他 LLM,正常操作不需要)。 |
291
+ | `semantic` | n/a | 驗證、搜尋、檢查漂移、遷移至 v2 或輸出可選的專案根目錄 `dbcli.semantic.json`。把已檢閱的 context 交給外部 agent,但 provider 憑證、prompt 與 agent context 都留在 dbcli 外。`semantic draft validate --input <file|-> [--format text\|json]` 只會以本機 semantic/schema/saved-query metadata 離線驗證明確提交、不受信任的 `QueryDraft`;只回傳安全的 hash/reference/violation code,絕不執行或回顯 candidate SQL。先檢閱原始 draft,若要執行再另行呼叫 `explain` 或 `query`。 |
291
292
  | `migrate` | admin | 僅 SQL。**DDL;預設 dry-run** — 需 `--execute`。 |
292
293
 
293
294
  任何指令都可使用 root 層級的 `dbcli --use <name> <command>`;`query`、`schema`、`list`、`export`、`check` 也接受指令層級的 `--use`。兩種寫法都只把本次目標切到 v2 連線,不改變預設值。`--recovery` 被 `query`、`q`、`insert`、`update`、`delete`、`export`、`schema`、`inspect`、`lint` 與 `diff --against-orm` 支援(見上方**失敗時**)。
@@ -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>"`.
@@ -2189,6 +2190,124 @@ dbcli migrate drop-enum status --execute --force
2189
2190
 
2190
2191
  **AI agent note:** Always use dry-run first (no `--execute`) to preview generated SQL. Only add `--execute` after confirming the SQL is correct. For DROP operations, both `--execute` and `--force` are required.
2191
2192
 
2193
+ ### semantic
2194
+
2195
+ Validate or print the optional, version-controlled `dbcli.semantic.json` in the
2196
+ project root. It supplies business names and descriptions to an agent, but is
2197
+ not a query language: these commands are offline, read-only, and never execute
2198
+ SQL or contact an LLM.
2199
+
2200
+ ```bash
2201
+ dbcli semantic validate
2202
+ dbcli semantic validate --format json
2203
+ dbcli semantic context
2204
+ dbcli semantic context --format markdown
2205
+ dbcli semantic context --file ./analytics.semantic.json
2206
+ dbcli semantic drift --format json
2207
+ dbcli semantic migrate --to 2 --format json
2208
+ dbcli semantic search purchases --kind model --format json
2209
+ dbcli semantic draft validate --input ./draft.json --format json
2210
+ external-agent | dbcli semantic draft validate --input - --format json
2211
+ ```
2212
+
2213
+ The default file has this compact contract:
2214
+
2215
+ ```json
2216
+ {
2217
+ "version": 2,
2218
+ "models": [
2219
+ {
2220
+ "name": "orders",
2221
+ "table": "orders",
2222
+ "description": "Completed purchases.",
2223
+ "aliases": ["purchases"],
2224
+ "fields": [
2225
+ { "column": "created_at", "aliases": ["order date"] },
2226
+ { "column": "customer_id" }
2227
+ ]
2228
+ },
2229
+ {
2230
+ "name": "customers",
2231
+ "table": "customers",
2232
+ "fields": [{ "column": "id" }]
2233
+ }
2234
+ ],
2235
+ "relationships": [
2236
+ {
2237
+ "name": "order-customer",
2238
+ "from": { "model": "orders", "field": "customer_id" },
2239
+ "to": { "model": "customers", "field": "id" },
2240
+ "cardinality": "many-to-one",
2241
+ "description": "Each order belongs to one customer."
2242
+ }
2243
+ ],
2244
+ "metrics": [
2245
+ { "name": "daily-revenue", "query": "@analytics/revenue" }
2246
+ ]
2247
+ }
2248
+ ```
2249
+
2250
+ Version 1 remains supported and is normalized with `relationships: []`.
2251
+ Version 2 relationships must reference a declared model and a declared field on
2252
+ that model; the field must also be visible in cached schema. Their `cardinality`
2253
+ is one of `one-to-one`, `one-to-many`, `many-to-one`, or `many-to-many`.
2254
+ `models[].table` and `models[].fields[].column` must name a visible cached
2255
+ schema object. Blacklisted tables and columns are not visible and are rejected.
2256
+ Each metric `query` must name an available saved query. Validation parses its
2257
+ local file through the normal saved-query safety checks, but never executes or
2258
+ emits SQL. The semantic file cannot contain SQL, connection data, or
2259
+ credentials. `semantic validate` reports a
2260
+ deterministic success summary (`--format text|json`); `semantic context` prints
2261
+ the validated context (`--format json|markdown`). `semantic drift` returns a
2262
+ stable `valid`, `stale`, `invalid`, or `unavailable` report and exits non-zero
2263
+ for every status except `valid`; it never connects to a database. `semantic
2264
+ migrate --to 2` prints a deterministic v2 JSON document to stdout and never
2265
+ writes the input file. An absent default file is allowed for `skill context`
2266
+ and simply omits the semantic section; a stale or invalid present file fails
2267
+ closed rather than being silently ignored.
2268
+
2269
+ `semantic search <terms...>` performs deterministic, case-insensitive matching
2270
+ over canonical names, aliases, descriptions, and governed model paths. Results
2271
+ are ranked by exact canonical name, exact alias, prefix, then description token;
2272
+ kind/name breaks ties. Use `--kind model|field|relationship|metric` and
2273
+ `--limit <1-100>` (default `20`). It returns only canonical references, matched
2274
+ terms, aliases, descriptions, and necessary model paths—never SQL bodies,
2275
+ connection data, or blacklist names. No result is an empty array / text notice
2276
+ with exit 0.
2277
+
2278
+ `semantic draft validate --input <file|-> [--format text|json]` accepts only an
2279
+ explicit untrusted `QueryDraft` JSON document from a file or stdin. First give
2280
+ the external agent reviewed `dbcli semantic context --format json` output; its
2281
+ provider credentials, prompt, and other agent context remain outside dbcli. The
2282
+ agent returns a draft using only the declared models and fields, for example:
2283
+
2284
+ ```json
2285
+ {
2286
+ "version": 1,
2287
+ "questionHash": "<sha256-of-the-original-question>",
2288
+ "candidate": { "kind": "sql", "sql": "<reviewed-read-only-sql>" },
2289
+ "semanticReferences": ["model:<model>", "field:<model>.<field>"]
2290
+ }
2291
+ ```
2292
+
2293
+ The command validates the contract, read-only single-statement SQL, canonical
2294
+ semantic references, saved-query names, and local filtered schema/blacklist
2295
+ compatibility without connecting to a database, reading query results, storing
2296
+ the draft, or calling a provider. JSON reports contain only status, hashes,
2297
+ canonical references, and safe violation codes; they never echo candidate SQL
2298
+ or protected names. Exit `0` means valid, `1` means rejected, and `2` means
2299
+ required local semantic evidence is unavailable. A valid report is not execution
2300
+ authorization: review the original draft, then separately and explicitly invoke
2301
+ `dbcli explain "<reviewed-read-only-sql>"` or `dbcli query
2302
+ "<reviewed-read-only-sql>"` when appropriate.
2303
+
2304
+ When valid, `dbcli skill context` includes the same bounded data in its JSON,
2305
+ XML, and Markdown output. To run a metric, an agent must still invoke the named
2306
+ saved query through `dbcli q`; all ordinary permissions, blacklist masking,
2307
+ limits, audit, and recovery safeguards remain in force.
2308
+
2309
+ **Permission:** n/a (local files only; no database connection).
2310
+
2192
2311
  ### skill
2193
2312
 
2194
2313
  Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or an