@carllee1983/dbcli 1.51.2 → 1.52.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.
@@ -381,11 +381,11 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
381
381
  | `use` | n/a | Show/switch default named connection (v2 only). |
382
382
  | `list` | query-only+ | Tables (SQL), collections (MongoDB), keys (Redis), or indices (Elasticsearch). |
383
383
  | `schema` | query-only+ | SQL: per-table or full scan into `.dbcli/schemas/`. MongoDB: sampled. ES: flattened mapping. Redis: per-key only (type/TTL/size). Supports `--recovery`. |
384
- | `query` | query-only+ | SQL, Mongo JSON (`--collection`), Redis command, or ES DSL/Lucene (`--collection`). `--format table\|json\|csv\|html`, `--ui` to open the interactive dashboard in a browser. `--fields` (projection), `--truncate` (cell width), `-f/--query-file` (read query from file or stdin), `--use a,b` (read-only fan-out). Supports `--recovery`. See **Query workflow flags**. |
384
+ | `query` | query-only+ | SQL, Mongo JSON (`--collection`), Redis command, or ES DSL/Lucene (`--collection`). `--format table\|json\|csv\|html`, `--ui` to open the interactive dashboard in a browser. `--fields` (projection), `--truncate` (cell width), `-f/--query-file` (read query from file or stdin), `--use a,b` (read-only fan-out). Supports `--recovery`. `--slow-ms <n>` sets the passive slow-query hint threshold (default 1000, `0` off): at or above it, table output gains a `Performance hint` footer and JSON gains `metadata.performanceAdvisory`; it runs no extra diagnostics and is suppressed under `--recovery`. Distinct from the `proxy` flag of the same name. See **Query workflow flags**. |
385
385
  | `explain` | query-only+ | **(v1.23)** Read-only query plan with annotations. SQL only. Single query, `@saved-query`, `@file.sql`, or `--bulk @glob/*`. `--analyze` (EXPLAIN ANALYZE / MariaDB ANALYZE SELECT), `--format markdown\|json\|table`. |
386
386
  | `lint` | n/a | Static SQL anti-pattern advisor (no DB connection). 9 rules incl. schema-aware implicit-cast / NOT IN-nullable checks via the layered `.dbcli/schemas/` cache; global `--use <conn>` selects a named cache. Findings carry rewrite drafts + guarded `explain` verify commands (`--analyze` only for proven read-only SQL) — report-only, never executes. `--format text\|json\|markdown`, `--min-severity`, `--no-schema`, `--bulk`. Supports `--recovery`. |
387
387
  | `plan` | n/a | Static SQL risk analyzer (`--format text\|json`); classifies a statement without connecting to the database. |
388
- | `q` | query-only+ | Run a saved snippet by `@name` with `--param k=v`. Supports `--verify` to run assertions. |
388
+ | `q` | query-only+ | Run a saved snippet by `@name` with `--param k=v`. Supports `--verify` to run assertions and `--slow-ms <n>` (same passive slow-query hint as `query`). |
389
389
  | `queries` | n/a | Manage saved snippets: `list` / `show` / `search` / `suggest` / `new` / `edit` / `check` / `delete` / `rename` / `copy` / `import` / `export`. |
390
390
  | `insert` / `update` | read-write+ | SQL or MongoDB only. JSON `--data` / `--set`; `--where` required on `update`; `--dry-run` first. Redis writes go through `query`. Supports `--recovery`. |
391
391
  | `delete` | data-admin+ | SQL or MongoDB; Redis has a basic implementation (see Redis section). `--where` required; `--dry-run` first. Supports `--recovery`. |
@@ -220,9 +220,44 @@ dbcli query "SELECT day, dau FROM dau_daily" --ui # open in brows
220
220
  dbcli query "SELECT * FROM orders" --format html > orders.html # pipe to stdout
221
221
  ```
222
222
 
223
- **Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--recovery`
223
+ **Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--slow-ms <number>`, `--recovery`
224
224
  **Permission:** query-only+ (Redis: per-command; Elasticsearch: per HTTP method/path)
225
225
 
226
+ #### Passive slow-query hint (`--slow-ms`)
227
+
228
+ `query` and `q` read the execution time they already measured for a finished
229
+ query and, at or above the threshold, add a hint. Default `1000`; `--slow-ms 0`
230
+ disables it for that invocation. This is **not** the `proxy` / `proxy analyze`
231
+ flag of the same name — that one flags events in the proxy log; this one only
232
+ annotates a single command's own result.
233
+
234
+ The hint performs no extra work: it never runs `EXPLAIN`, reads a schema, or
235
+ issues a second request. It is suppressed entirely under `--recovery`, so the
236
+ recovery envelope keeps its exact machine contract.
237
+
238
+ - `--format table` appends `Performance hint: <recommendation>` to the footer.
239
+ - `--format json` adds `metadata.performanceAdvisory`:
240
+
241
+ ```json
242
+ {
243
+ "metadata": {
244
+ "statement": "SELECT",
245
+ "performanceAdvisory": {
246
+ "code": "SLOW_QUERY",
247
+ "executionTimeMs": 1250,
248
+ "thresholdMs": 1000,
249
+ "recommendation": "Review safely with: dbcli guide slow-query --format markdown. This hint runs no additional database diagnostics."
250
+ }
251
+ }
252
+ }
253
+ ```
254
+
255
+ The recommendation is engine-aware: PostgreSQL, MySQL, MariaDB, and Redis are
256
+ pointed at `dbcli guide slow-query`, because that goal resolves to real
257
+ diagnostic snippets for them. MongoDB and Elasticsearch ship no snippet for its
258
+ intents, so their hint states the timing and says so instead of naming a command
259
+ that would come back empty. `csv` and `html` output are unchanged.
260
+
226
261
  Below `admin`, SQL holding more than one statement is rejected, because only the
227
262
  first statement would decide the permission check while a driver on the simple
228
263
  query protocol executes them all. Semicolons inside string literals, backtick
@@ -529,6 +564,7 @@ dbcli q @analytics/revenue --param days=30 --format html > report.html
529
564
  - `--no-limit` — skip the `SELECT * FROM (…) AS _dbcli_guard LIMIT 1000` wrap
530
565
  - `--dry-run` — print the bound SQL + values without executing
531
566
  - `--use <name>` — pick a v2 named connection
567
+ - `--slow-ms <number>` — passive slow-query hint threshold (default `1000`; `0` disables). Same contract as `query` — see "Passive slow-query hint" there
532
568
  - `--recovery` — emit a `RecoveryEnvelope` on failure (see `recover`)
533
569
  - `--verify` — run the snippet's verification assertions after execution (only if the snippet defines them)
534
570
 
@@ -1077,6 +1113,139 @@ Both parameters are required. Keep each expansion as one quoted shell argument;
1077
1113
  never use `eval`, and consider `--execute` only after the plan and captured DDL
1078
1114
  have been reviewed.
1079
1115
 
1116
+ ### design
1117
+
1118
+ Author, validate, render, and review a version-controlled SQL database design
1119
+ kept beside the code as `dbcli.design.json`. Every subcommand is offline: none
1120
+ opens a database connection, executes DDL, or calls an LLM. `design init` is the
1121
+ only writer, and it writes only to the explicit `--output` path.
1122
+
1123
+ ```text
1124
+ dbcli design init --output <path> [--dialect <dialect>]
1125
+ dbcli design validate [--file <path>] [--format <format>]
1126
+ dbcli design render [--file <path>] [--format <format>]
1127
+ dbcli design diff (--against-cache | --against-orm <paths>) [options]
1128
+ dbcli design propose (--against-cache | --against-orm <paths>) [options]
1129
+ ```
1130
+
1131
+ ```bash
1132
+ # Writes only to this explicit, missing path; edit the starter before validating.
1133
+ dbcli design init --output ./dbcli.design.json --dialect postgresql
1134
+
1135
+ dbcli design validate --format json
1136
+ dbcli design render --format mermaid
1137
+ dbcli design diff --against-cache --format markdown
1138
+ dbcli design diff --against-orm ./prisma/schema.prisma --format markdown
1139
+ dbcli design propose --against-orm ./prisma/schema.prisma --format markdown
1140
+ ```
1141
+
1142
+ | Option | Applies to | Default | Meaning |
1143
+ |---|---|---|---|
1144
+ | `--output <path>` | `init` | required | Destination for the new artifact; refuses to overwrite an existing file. |
1145
+ | `--dialect <postgresql\|mysql\|mariadb>` | `init` | `postgresql` | Target SQL dialect recorded in the artifact. |
1146
+ | `--file <path>` | all but `init` | `dbcli.design.json` | Design artifact to read. |
1147
+ | `--format <format>` | all but `init` | see below | `validate`/`propose`: `json`, `markdown`. `render`: `json`, `markdown`, `mermaid` (default `markdown`). `diff`: `json` (default), `table`, `markdown`. |
1148
+ | `--against-cache` | `diff`, `propose` | off | Compare with the local schema cache; requires a configured PostgreSQL/MySQL/MariaDB connection whose system matches the artifact dialect, and a non-empty cache (run `dbcli schema` first). |
1149
+ | `--against-orm <paths>` | `diff`, `propose` | none | Compare with local ORM definition(s); repeatable or comma-separated, DDL paths support globs. Needs no config and no connection. |
1150
+ | `--orm-format <format>` | `diff`, `propose` | auto-detect | Force `prisma`, `ddl`, `json`, `drizzle`, `typeorm`, or `sequelize`. |
1151
+ | `--ignore <globs>` | `diff`, `propose` | none | Comma-separated table globs excluded from drift. |
1152
+
1153
+ `diff` and `propose` require **exactly one** comparison target; passing both or
1154
+ neither is an error.
1155
+
1156
+ #### Artifact shape
1157
+
1158
+ ```json
1159
+ {
1160
+ "version": 1,
1161
+ "dialect": "postgresql",
1162
+ "models": [
1163
+ {
1164
+ "name": "orders",
1165
+ "table": "orders",
1166
+ "description": "Completed purchases.",
1167
+ "fields": [
1168
+ { "name": "id", "type": "bigint", "nullable": false, "primaryKey": true, "unique": true },
1169
+ { "name": "customer_id", "type": "bigint", "nullable": false, "primaryKey": false, "unique": false }
1170
+ ],
1171
+ "indexes": [{ "name": "orders_customer_idx", "columns": ["customer_id"], "unique": false }]
1172
+ }
1173
+ ],
1174
+ "relationships": [
1175
+ {
1176
+ "name": "orders_customer",
1177
+ "from": { "model": "orders", "field": "customer_id" },
1178
+ "to": { "model": "customers", "field": "id" },
1179
+ "cardinality": "many-to-one"
1180
+ }
1181
+ ],
1182
+ "accessPatterns": [{ "model": "orders", "filters": ["customer_id"], "sort": ["created_at"] }],
1183
+ "decisions": [{ "name": "single-currency", "rationale": "Amounts are stored in minor units, USD only." }]
1184
+ }
1185
+ ```
1186
+
1187
+ It holds no SQL, credentials, rows, or provider configuration. `design init`
1188
+ emits this envelope with empty `models`, `relationships`, `accessPatterns`, and
1189
+ `decisions`.
1190
+
1191
+ #### Review findings
1192
+
1193
+ `validate` is fail-closed: any `error` finding exits `1`, and `render`, `diff`,
1194
+ and `propose` refuse to do their work while errors remain.
1195
+
1196
+ | Severity | Codes |
1197
+ |---|---|
1198
+ | `error` | `NO_MODELS`, `DUPLICATE_MODEL`, `DUPLICATE_TABLE`, `DUPLICATE_FIELD`, `PRIMARY_KEY_COUNT`, `NULLABLE_PRIMARY_KEY`, `UNKNOWN_INDEX_FIELD`, `DUPLICATE_RELATIONSHIP`, `REVERSE_RELATIONSHIP`, `UNKNOWN_RELATIONSHIP_MODEL`, `UNKNOWN_RELATIONSHIP_FIELD`, `RELATIONSHIP_TYPE_MISMATCH`, `MANY_TO_MANY_REQUIRES_BRIDGE`, `ONE_TO_ONE_REQUIRES_UNIQUE_FK`, `UNKNOWN_ACCESS_MODEL`, `UNKNOWN_ACCESS_FIELD` |
1199
+ | `warn` | `DUPLICATE_INDEX`, `REDUNDANT_PRIMARY_KEY_INDEX`, `PREFIX_REDUNDANT_INDEX`, `ACCESS_PATTERN_INDEX` |
1200
+
1201
+ `REVERSE_RELATIONSHIP` fires when the same endpoints are declared again in the
1202
+ opposite direction; `PREFIX_REDUNDANT_INDEX` fires when a non-unique index is a
1203
+ leading-column prefix of a longer index. `v1` requires exactly one primary-key
1204
+ field per model and an explicit bridge model for `many-to-many`.
1205
+
1206
+ #### `design propose` (review-only)
1207
+
1208
+ `propose` turns drift into a plan a human reviews; it never applies a write. Each
1209
+ entry carries a `safety` of `dry-run` (an existing `migrate` command can represent
1210
+ the change losslessly) or `migration-review` (everything else), plus `preflight`,
1211
+ `rollback`, and `verification` steps:
1212
+
1213
+ ```json
1214
+ {
1215
+ "table": "orders",
1216
+ "object": "total_cents",
1217
+ "safety": "migration-review",
1218
+ "commands": ["..."],
1219
+ "preflight": [
1220
+ "dbcli blacklist list",
1221
+ "Confirm the exact affected table with: dbcli schema <exact-table> --format json"
1222
+ ],
1223
+ "rollback": "Capture the current schema and generated DDL before any approved write; define the inverse migration before execution.",
1224
+ "verification": [
1225
+ "After an approved write, run: dbcli schema <exact-table> --format json",
1226
+ "Re-run this same design diff command and review the remaining drift."
1227
+ ]
1228
+ }
1229
+ ```
1230
+
1231
+ **Workflows:**
1232
+
1233
+ - **New project** — `design init` → edit the artifact → `design validate` →
1234
+ `design render`. If application models already exist, use the offline
1235
+ `design diff --against-orm <path>` to reconcile the artifact and the ORM before
1236
+ any database exists.
1237
+ - **Existing database** — `blacklist list` → refresh the cache with
1238
+ `schema --format json` → `design diff --against-cache` →
1239
+ `design propose --against-cache`. Review the plan, perform any approved
1240
+ migration separately, then refresh the schema and rerun the same diff.
1241
+
1242
+ **Exit codes:** `0` when no errors, `1` when the artifact has review errors, an
1243
+ invalid target selection, an unreadable file, or reported drift errors.
1244
+
1245
+ An external coding agent may draft the artifact, but a human should review it
1246
+ before it is relied upon. Do not create or rewrite `dbcli.design.json` without an
1247
+ explicit human request.
1248
+
1080
1249
  ### snapshot
1081
1250
 
1082
1251
  Capture a **result fingerprint** of a query (not schema): `rowCount` plus per-column
@@ -381,11 +381,11 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
381
381
  | `use` | n/a | Show/switch default named connection (v2 only). |
382
382
  | `list` | query-only+ | Tables (SQL), collections (MongoDB), keys (Redis), or indices (Elasticsearch). |
383
383
  | `schema` | query-only+ | SQL: per-table or full scan into `.dbcli/schemas/`. MongoDB: sampled. ES: flattened mapping. Redis: per-key only (type/TTL/size). Supports `--recovery`. |
384
- | `query` | query-only+ | SQL, Mongo JSON (`--collection`), Redis command, or ES DSL/Lucene (`--collection`). `--format table\|json\|csv\|html`, `--ui` to open the interactive dashboard in a browser. `--fields` (projection), `--truncate` (cell width), `-f/--query-file` (read query from file or stdin), `--use a,b` (read-only fan-out). Supports `--recovery`. See **Query workflow flags**. |
384
+ | `query` | query-only+ | SQL, Mongo JSON (`--collection`), Redis command, or ES DSL/Lucene (`--collection`). `--format table\|json\|csv\|html`, `--ui` to open the interactive dashboard in a browser. `--fields` (projection), `--truncate` (cell width), `-f/--query-file` (read query from file or stdin), `--use a,b` (read-only fan-out). Supports `--recovery`. `--slow-ms <n>` sets the passive slow-query hint threshold (default 1000, `0` off): at or above it, table output gains a `Performance hint` footer and JSON gains `metadata.performanceAdvisory`; it runs no extra diagnostics and is suppressed under `--recovery`. Distinct from the `proxy` flag of the same name. See **Query workflow flags**. |
385
385
  | `explain` | query-only+ | **(v1.23)** Read-only query plan with annotations. SQL only. Single query, `@saved-query`, `@file.sql`, or `--bulk @glob/*`. `--analyze` (EXPLAIN ANALYZE / MariaDB ANALYZE SELECT), `--format markdown\|json\|table`. |
386
386
  | `lint` | n/a | Static SQL anti-pattern advisor (no DB connection). 9 rules incl. schema-aware implicit-cast / NOT IN-nullable checks via the layered `.dbcli/schemas/` cache; global `--use <conn>` selects a named cache. Findings carry rewrite drafts + guarded `explain` verify commands (`--analyze` only for proven read-only SQL) — report-only, never executes. `--format text\|json\|markdown`, `--min-severity`, `--no-schema`, `--bulk`. Supports `--recovery`. |
387
387
  | `plan` | n/a | Static SQL risk analyzer (`--format text\|json`); classifies a statement without connecting to the database. |
388
- | `q` | query-only+ | Run a saved snippet by `@name` with `--param k=v`. Supports `--verify` to run assertions. |
388
+ | `q` | query-only+ | Run a saved snippet by `@name` with `--param k=v`. Supports `--verify` to run assertions and `--slow-ms <n>` (same passive slow-query hint as `query`). |
389
389
  | `queries` | n/a | Manage saved snippets: `list` / `show` / `search` / `suggest` / `new` / `edit` / `check` / `delete` / `rename` / `copy` / `import` / `export`. |
390
390
  | `insert` / `update` | read-write+ | SQL or MongoDB only. JSON `--data` / `--set`; `--where` required on `update`; `--dry-run` first. Redis writes go through `query`. Supports `--recovery`. |
391
391
  | `delete` | data-admin+ | SQL or MongoDB; Redis has a basic implementation (see Redis section). `--where` required; `--dry-run` first. Supports `--recovery`. |
@@ -220,9 +220,44 @@ dbcli query "SELECT day, dau FROM dau_daily" --ui # open in brows
220
220
  dbcli query "SELECT * FROM orders" --format html > orders.html # pipe to stdout
221
221
  ```
222
222
 
223
- **Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--recovery`
223
+ **Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--slow-ms <number>`, `--recovery`
224
224
  **Permission:** query-only+ (Redis: per-command; Elasticsearch: per HTTP method/path)
225
225
 
226
+ #### Passive slow-query hint (`--slow-ms`)
227
+
228
+ `query` and `q` read the execution time they already measured for a finished
229
+ query and, at or above the threshold, add a hint. Default `1000`; `--slow-ms 0`
230
+ disables it for that invocation. This is **not** the `proxy` / `proxy analyze`
231
+ flag of the same name — that one flags events in the proxy log; this one only
232
+ annotates a single command's own result.
233
+
234
+ The hint performs no extra work: it never runs `EXPLAIN`, reads a schema, or
235
+ issues a second request. It is suppressed entirely under `--recovery`, so the
236
+ recovery envelope keeps its exact machine contract.
237
+
238
+ - `--format table` appends `Performance hint: <recommendation>` to the footer.
239
+ - `--format json` adds `metadata.performanceAdvisory`:
240
+
241
+ ```json
242
+ {
243
+ "metadata": {
244
+ "statement": "SELECT",
245
+ "performanceAdvisory": {
246
+ "code": "SLOW_QUERY",
247
+ "executionTimeMs": 1250,
248
+ "thresholdMs": 1000,
249
+ "recommendation": "Review safely with: dbcli guide slow-query --format markdown. This hint runs no additional database diagnostics."
250
+ }
251
+ }
252
+ }
253
+ ```
254
+
255
+ The recommendation is engine-aware: PostgreSQL, MySQL, MariaDB, and Redis are
256
+ pointed at `dbcli guide slow-query`, because that goal resolves to real
257
+ diagnostic snippets for them. MongoDB and Elasticsearch ship no snippet for its
258
+ intents, so their hint states the timing and says so instead of naming a command
259
+ that would come back empty. `csv` and `html` output are unchanged.
260
+
226
261
  Below `admin`, SQL holding more than one statement is rejected, because only the
227
262
  first statement would decide the permission check while a driver on the simple
228
263
  query protocol executes them all. Semicolons inside string literals, backtick
@@ -529,6 +564,7 @@ dbcli q @analytics/revenue --param days=30 --format html > report.html
529
564
  - `--no-limit` — skip the `SELECT * FROM (…) AS _dbcli_guard LIMIT 1000` wrap
530
565
  - `--dry-run` — print the bound SQL + values without executing
531
566
  - `--use <name>` — pick a v2 named connection
567
+ - `--slow-ms <number>` — passive slow-query hint threshold (default `1000`; `0` disables). Same contract as `query` — see "Passive slow-query hint" there
532
568
  - `--recovery` — emit a `RecoveryEnvelope` on failure (see `recover`)
533
569
  - `--verify` — run the snippet's verification assertions after execution (only if the snippet defines them)
534
570
 
@@ -1077,6 +1113,139 @@ Both parameters are required. Keep each expansion as one quoted shell argument;
1077
1113
  never use `eval`, and consider `--execute` only after the plan and captured DDL
1078
1114
  have been reviewed.
1079
1115
 
1116
+ ### design
1117
+
1118
+ Author, validate, render, and review a version-controlled SQL database design
1119
+ kept beside the code as `dbcli.design.json`. Every subcommand is offline: none
1120
+ opens a database connection, executes DDL, or calls an LLM. `design init` is the
1121
+ only writer, and it writes only to the explicit `--output` path.
1122
+
1123
+ ```text
1124
+ dbcli design init --output <path> [--dialect <dialect>]
1125
+ dbcli design validate [--file <path>] [--format <format>]
1126
+ dbcli design render [--file <path>] [--format <format>]
1127
+ dbcli design diff (--against-cache | --against-orm <paths>) [options]
1128
+ dbcli design propose (--against-cache | --against-orm <paths>) [options]
1129
+ ```
1130
+
1131
+ ```bash
1132
+ # Writes only to this explicit, missing path; edit the starter before validating.
1133
+ dbcli design init --output ./dbcli.design.json --dialect postgresql
1134
+
1135
+ dbcli design validate --format json
1136
+ dbcli design render --format mermaid
1137
+ dbcli design diff --against-cache --format markdown
1138
+ dbcli design diff --against-orm ./prisma/schema.prisma --format markdown
1139
+ dbcli design propose --against-orm ./prisma/schema.prisma --format markdown
1140
+ ```
1141
+
1142
+ | Option | Applies to | Default | Meaning |
1143
+ |---|---|---|---|
1144
+ | `--output <path>` | `init` | required | Destination for the new artifact; refuses to overwrite an existing file. |
1145
+ | `--dialect <postgresql\|mysql\|mariadb>` | `init` | `postgresql` | Target SQL dialect recorded in the artifact. |
1146
+ | `--file <path>` | all but `init` | `dbcli.design.json` | Design artifact to read. |
1147
+ | `--format <format>` | all but `init` | see below | `validate`/`propose`: `json`, `markdown`. `render`: `json`, `markdown`, `mermaid` (default `markdown`). `diff`: `json` (default), `table`, `markdown`. |
1148
+ | `--against-cache` | `diff`, `propose` | off | Compare with the local schema cache; requires a configured PostgreSQL/MySQL/MariaDB connection whose system matches the artifact dialect, and a non-empty cache (run `dbcli schema` first). |
1149
+ | `--against-orm <paths>` | `diff`, `propose` | none | Compare with local ORM definition(s); repeatable or comma-separated, DDL paths support globs. Needs no config and no connection. |
1150
+ | `--orm-format <format>` | `diff`, `propose` | auto-detect | Force `prisma`, `ddl`, `json`, `drizzle`, `typeorm`, or `sequelize`. |
1151
+ | `--ignore <globs>` | `diff`, `propose` | none | Comma-separated table globs excluded from drift. |
1152
+
1153
+ `diff` and `propose` require **exactly one** comparison target; passing both or
1154
+ neither is an error.
1155
+
1156
+ #### Artifact shape
1157
+
1158
+ ```json
1159
+ {
1160
+ "version": 1,
1161
+ "dialect": "postgresql",
1162
+ "models": [
1163
+ {
1164
+ "name": "orders",
1165
+ "table": "orders",
1166
+ "description": "Completed purchases.",
1167
+ "fields": [
1168
+ { "name": "id", "type": "bigint", "nullable": false, "primaryKey": true, "unique": true },
1169
+ { "name": "customer_id", "type": "bigint", "nullable": false, "primaryKey": false, "unique": false }
1170
+ ],
1171
+ "indexes": [{ "name": "orders_customer_idx", "columns": ["customer_id"], "unique": false }]
1172
+ }
1173
+ ],
1174
+ "relationships": [
1175
+ {
1176
+ "name": "orders_customer",
1177
+ "from": { "model": "orders", "field": "customer_id" },
1178
+ "to": { "model": "customers", "field": "id" },
1179
+ "cardinality": "many-to-one"
1180
+ }
1181
+ ],
1182
+ "accessPatterns": [{ "model": "orders", "filters": ["customer_id"], "sort": ["created_at"] }],
1183
+ "decisions": [{ "name": "single-currency", "rationale": "Amounts are stored in minor units, USD only." }]
1184
+ }
1185
+ ```
1186
+
1187
+ It holds no SQL, credentials, rows, or provider configuration. `design init`
1188
+ emits this envelope with empty `models`, `relationships`, `accessPatterns`, and
1189
+ `decisions`.
1190
+
1191
+ #### Review findings
1192
+
1193
+ `validate` is fail-closed: any `error` finding exits `1`, and `render`, `diff`,
1194
+ and `propose` refuse to do their work while errors remain.
1195
+
1196
+ | Severity | Codes |
1197
+ |---|---|
1198
+ | `error` | `NO_MODELS`, `DUPLICATE_MODEL`, `DUPLICATE_TABLE`, `DUPLICATE_FIELD`, `PRIMARY_KEY_COUNT`, `NULLABLE_PRIMARY_KEY`, `UNKNOWN_INDEX_FIELD`, `DUPLICATE_RELATIONSHIP`, `REVERSE_RELATIONSHIP`, `UNKNOWN_RELATIONSHIP_MODEL`, `UNKNOWN_RELATIONSHIP_FIELD`, `RELATIONSHIP_TYPE_MISMATCH`, `MANY_TO_MANY_REQUIRES_BRIDGE`, `ONE_TO_ONE_REQUIRES_UNIQUE_FK`, `UNKNOWN_ACCESS_MODEL`, `UNKNOWN_ACCESS_FIELD` |
1199
+ | `warn` | `DUPLICATE_INDEX`, `REDUNDANT_PRIMARY_KEY_INDEX`, `PREFIX_REDUNDANT_INDEX`, `ACCESS_PATTERN_INDEX` |
1200
+
1201
+ `REVERSE_RELATIONSHIP` fires when the same endpoints are declared again in the
1202
+ opposite direction; `PREFIX_REDUNDANT_INDEX` fires when a non-unique index is a
1203
+ leading-column prefix of a longer index. `v1` requires exactly one primary-key
1204
+ field per model and an explicit bridge model for `many-to-many`.
1205
+
1206
+ #### `design propose` (review-only)
1207
+
1208
+ `propose` turns drift into a plan a human reviews; it never applies a write. Each
1209
+ entry carries a `safety` of `dry-run` (an existing `migrate` command can represent
1210
+ the change losslessly) or `migration-review` (everything else), plus `preflight`,
1211
+ `rollback`, and `verification` steps:
1212
+
1213
+ ```json
1214
+ {
1215
+ "table": "orders",
1216
+ "object": "total_cents",
1217
+ "safety": "migration-review",
1218
+ "commands": ["..."],
1219
+ "preflight": [
1220
+ "dbcli blacklist list",
1221
+ "Confirm the exact affected table with: dbcli schema <exact-table> --format json"
1222
+ ],
1223
+ "rollback": "Capture the current schema and generated DDL before any approved write; define the inverse migration before execution.",
1224
+ "verification": [
1225
+ "After an approved write, run: dbcli schema <exact-table> --format json",
1226
+ "Re-run this same design diff command and review the remaining drift."
1227
+ ]
1228
+ }
1229
+ ```
1230
+
1231
+ **Workflows:**
1232
+
1233
+ - **New project** — `design init` → edit the artifact → `design validate` →
1234
+ `design render`. If application models already exist, use the offline
1235
+ `design diff --against-orm <path>` to reconcile the artifact and the ORM before
1236
+ any database exists.
1237
+ - **Existing database** — `blacklist list` → refresh the cache with
1238
+ `schema --format json` → `design diff --against-cache` →
1239
+ `design propose --against-cache`. Review the plan, perform any approved
1240
+ migration separately, then refresh the schema and rerun the same diff.
1241
+
1242
+ **Exit codes:** `0` when no errors, `1` when the artifact has review errors, an
1243
+ invalid target selection, an unreadable file, or reported drift errors.
1244
+
1245
+ An external coding agent may draft the artifact, but a human should review it
1246
+ before it is relied upon. Do not create or rewrite `dbcli.design.json` without an
1247
+ explicit human request.
1248
+
1080
1249
  ### snapshot
1081
1250
 
1082
1251
  Capture a **result fingerprint** of a query (not schema): `rowCount` plus per-column
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ 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.52.0] - 2026-08-07 - Offline database design assistant and slow-query hints
9
+
10
+ ### Added
11
+
12
+ - **Offline database design assistant.** `dbcli design init|validate|render|diff|propose` authors and reviews a version-controlled `dbcli.design.json` beside the code. Every subcommand is offline: none opens a connection, executes DDL, or calls a provider, and `design init` is the only writer — to the explicit `--output` path, refusing to overwrite. `validate` is fail-closed, so `render`, `diff`, and `propose` refuse to work while `error` findings remain; `render` emits `json`, `markdown`, or `mermaid`.
13
+ - **Design drift comparison and review-only proposals.** `design diff` and `design propose` compare the artifact against the local schema cache (`--against-cache`) or local ORM definitions (`--against-orm`, supporting Prisma, DDL, Drizzle, TypeORM, Sequelize, and JSON), with `--orm-format` and `--ignore` for control. `propose` turns drift into a plan a human reviews and never applies a write: each entry carries a `dry-run` or `migration-review` safety level plus `preflight`, `rollback`, and `verification` steps.
14
+ - **Two further design review rules.** `REVERSE_RELATIONSHIP` (error) fires when the same endpoints are declared again in the opposite direction, and `PREFIX_REDUNDANT_INDEX` (warn) fires when a non-unique index is a leading-column prefix of a longer index.
15
+ - **Passive slow-query hint on `query` and `q`.** At or above `--slow-ms` (default 1000, `0` disables), a finished query gains a Performance hint footer and `metadata.performanceAdvisory`. It reuses the execution time already measured — no `EXPLAIN`, no schema read, no second request. The recommendation is engine-aware: PostgreSQL, MySQL, MariaDB, and Redis are pointed at `guide slow-query`; MongoDB and Elasticsearch state the timing instead. `--recovery` suppresses the hint so that envelope keeps its contract.
16
+
17
+ ### Changed
18
+
19
+ - **Skill reference documents the new surface.** `skills/dbcli/reference.md` gains the `design` section (subcommands, artifact shape, finding codes and severities, the review-only `propose` contract, and workflows) plus the `--slow-ms` flag and the `metadata.performanceAdvisory` shape, disambiguated from the proxy flag of the same name.
20
+
21
+ ### Tests
22
+
23
+ - Gherkin CLI workflow coverage for workspace inspection, blacklist review, and the verification prune dry run.
24
+
8
25
  ## [1.51.2] - 2026-08-07 - Intent confirmation for business requests
9
26
 
10
27
  ### Added
package/README.md CHANGED
@@ -1679,6 +1679,7 @@ bun test # full test suite (Bun test runner)
1679
1679
  bun run typecheck # TypeScript compile-time validation
1680
1680
  bun run test:unit # unit + core tests only
1681
1681
  bun run test:integration # integration tests
1682
+ bun run test:gherkin # Gherkin feature tests at the CLI boundary
1682
1683
  bun run test:docker # integration tests with docker-compose.test.yml (MySQL + PostgreSQL)
1683
1684
  bun run build # bundle CLI to dist/ (used before publish)
1684
1685
  ```
package/README.zh-TW.md CHANGED
@@ -1558,6 +1558,7 @@ import {
1558
1558
  bun test # 完整測試(Bun test runner)
1559
1559
  bun run test:unit # 僅單元與 core 測試
1560
1560
  bun run test:integration # 整合測試
1561
+ bun run test:gherkin # CLI 邊界的 Gherkin feature 測試
1561
1562
  bun run test:docker # 搭配 docker-compose.test.yml(MySQL + PostgreSQL)
1562
1563
  bun run build # 建置 CLI 至 dist/(發布前使用)
1563
1564
  ```
package/assets/SKILL.md CHANGED
@@ -381,11 +381,11 @@ Full flags and edge cases: see [reference.md](reference.md) `init` section.
381
381
  | `use` | n/a | Show/switch default named connection (v2 only). |
382
382
  | `list` | query-only+ | Tables (SQL), collections (MongoDB), keys (Redis), or indices (Elasticsearch). |
383
383
  | `schema` | query-only+ | SQL: per-table or full scan into `.dbcli/schemas/`. MongoDB: sampled. ES: flattened mapping. Redis: per-key only (type/TTL/size). Supports `--recovery`. |
384
- | `query` | query-only+ | SQL, Mongo JSON (`--collection`), Redis command, or ES DSL/Lucene (`--collection`). `--format table\|json\|csv\|html`, `--ui` to open the interactive dashboard in a browser. `--fields` (projection), `--truncate` (cell width), `-f/--query-file` (read query from file or stdin), `--use a,b` (read-only fan-out). Supports `--recovery`. See **Query workflow flags**. |
384
+ | `query` | query-only+ | SQL, Mongo JSON (`--collection`), Redis command, or ES DSL/Lucene (`--collection`). `--format table\|json\|csv\|html`, `--ui` to open the interactive dashboard in a browser. `--fields` (projection), `--truncate` (cell width), `-f/--query-file` (read query from file or stdin), `--use a,b` (read-only fan-out). Supports `--recovery`. `--slow-ms <n>` sets the passive slow-query hint threshold (default 1000, `0` off): at or above it, table output gains a `Performance hint` footer and JSON gains `metadata.performanceAdvisory`; it runs no extra diagnostics and is suppressed under `--recovery`. Distinct from the `proxy` flag of the same name. See **Query workflow flags**. |
385
385
  | `explain` | query-only+ | **(v1.23)** Read-only query plan with annotations. SQL only. Single query, `@saved-query`, `@file.sql`, or `--bulk @glob/*`. `--analyze` (EXPLAIN ANALYZE / MariaDB ANALYZE SELECT), `--format markdown\|json\|table`. |
386
386
  | `lint` | n/a | Static SQL anti-pattern advisor (no DB connection). 9 rules incl. schema-aware implicit-cast / NOT IN-nullable checks via the layered `.dbcli/schemas/` cache; global `--use <conn>` selects a named cache. Findings carry rewrite drafts + guarded `explain` verify commands (`--analyze` only for proven read-only SQL) — report-only, never executes. `--format text\|json\|markdown`, `--min-severity`, `--no-schema`, `--bulk`. Supports `--recovery`. |
387
387
  | `plan` | n/a | Static SQL risk analyzer (`--format text\|json`); classifies a statement without connecting to the database. |
388
- | `q` | query-only+ | Run a saved snippet by `@name` with `--param k=v`. Supports `--verify` to run assertions. |
388
+ | `q` | query-only+ | Run a saved snippet by `@name` with `--param k=v`. Supports `--verify` to run assertions and `--slow-ms <n>` (same passive slow-query hint as `query`). |
389
389
  | `queries` | n/a | Manage saved snippets: `list` / `show` / `search` / `suggest` / `new` / `edit` / `check` / `delete` / `rename` / `copy` / `import` / `export`. |
390
390
  | `insert` / `update` | read-write+ | SQL or MongoDB only. JSON `--data` / `--set`; `--where` required on `update`; `--dry-run` first. Redis writes go through `query`. Supports `--recovery`. |
391
391
  | `delete` | data-admin+ | SQL or MongoDB; Redis has a basic implementation (see Redis section). `--where` required; `--dry-run` first. Supports `--recovery`. |