@carllee1983/dbcli 1.20.2 → 1.25.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.
- package/CHANGELOG.md +122 -0
- package/README.md +47 -1
- package/README.zh-TW.md +54 -5
- package/assets/SKILL.md +26 -9
- package/assets/SKILL.zh-TW.md +11 -4
- package/assets/reference.md +245 -17
- package/assets/tasks/analyze-table-perf.md +35 -0
- package/dist/cli.mjs +54395 -1558
- package/package.json +2 -1
package/assets/reference.md
CHANGED
|
@@ -161,6 +161,40 @@ dbcli query "SELECT * FROM orders" --format html > orders.html # pipe to stdou
|
|
|
161
161
|
> - Hits are flattened: each result row contains `_id` plus dotted-path fields from `_source`. Pass `--format json` to keep nested structures readable.
|
|
162
162
|
> - Query-only mode caps at 1000 hits; `--no-limit` is internally capped at 10 000 (use saved searches / `search_after` for deeper pagination).
|
|
163
163
|
|
|
164
|
+
### explain
|
|
165
|
+
|
|
166
|
+
**(v1.23)** Read-only query-plan inspection across MySQL/MariaDB and PostgreSQL,
|
|
167
|
+
wrapping `EXPLAIN` / `EXPLAIN ANALYZE` / MariaDB `ANALYZE SELECT` behind one
|
|
168
|
+
interface. Output is a unified `ExplainRow` schema plus severity-coded
|
|
169
|
+
annotations. SQL `SELECT` only.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
dbcli explain "SELECT * FROM betting_logs WHERE settled_at >= '2026-03-01'"
|
|
173
|
+
dbcli explain @analytics/live-summary # saved query
|
|
174
|
+
dbcli explain @file.sql # @file reference
|
|
175
|
+
dbcli explain --analyze "SELECT ..." # MariaDB ANALYZE SELECT / PG EXPLAIN ANALYZE
|
|
176
|
+
dbcli explain --format json "..." # markdown (default) | json | table
|
|
177
|
+
dbcli explain --bulk @queries.sql # batch from file
|
|
178
|
+
dbcli explain --bulk @analytics/* # glob over saved queries
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Options:** `--analyze` (run the query for real — EXPLAIN ANALYZE / ANALYZE SELECT), `--format <markdown|json|table>` (default `markdown`), `--bulk <input>` (comma-separated `@file` / `@glob` / `@saved-query`).
|
|
182
|
+
**Permission:** query-only+ (no upgrade required).
|
|
183
|
+
|
|
184
|
+
**Annotations:**
|
|
185
|
+
|
|
186
|
+
| Rule | Severity | Triggered when |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| `full-scan` | red | MySQL `type=ALL` or `key=NULL`; PG `Seq Scan` |
|
|
189
|
+
| `temp-table` | yellow | MySQL `Using temporary` |
|
|
190
|
+
| `filesort` | yellow | MySQL `Using filesort`; PG `Sort Method: external merge` |
|
|
191
|
+
| `cost-estimate-skew` | gray | `--analyze` actual rows / planner rows > 10× |
|
|
192
|
+
| `nested-loop-large` | yellow | PG `Nested Loop` with planner rows > 10,000 |
|
|
193
|
+
|
|
194
|
+
> Notes:
|
|
195
|
+
> - `--analyze` executes the statement — do not use against destructive SQL.
|
|
196
|
+
> - Auto-`LIMIT` is **not** applied to EXPLAIN statements (since v1.23 P1).
|
|
197
|
+
|
|
164
198
|
### plan
|
|
165
199
|
|
|
166
200
|
Static SQL risk analyzer. Classifies a statement into the same permission tiers
|
|
@@ -480,13 +514,20 @@ dbcli export "SELECT * FROM users" --format csv --output users.csv --force # Sk
|
|
|
480
514
|
dbcli export "SELECT * FROM users" --format json | jq '.[]'
|
|
481
515
|
dbcli export "SELECT * FROM users" --format jsonl --output users.ndjson
|
|
482
516
|
dbcli export "SELECT * FROM orders" --format html --output orders.html # standalone dashboard
|
|
517
|
+
|
|
518
|
+
# Elasticsearch (v1.22)
|
|
519
|
+
dbcli export '{"query":{"match":{"status":"active"}}}' --index orders --format jsonl --output orders.ndjson
|
|
520
|
+
dbcli export orders --format csv --output orders.csv # index name as query → match_all + scroll
|
|
521
|
+
dbcli export orders --no-limit --format jsonl # scroll the whole index in batches
|
|
483
522
|
```
|
|
484
523
|
|
|
485
|
-
**Options:** `--format <json|jsonl|csv|html>` (required), `--output <path>`, `--force`, `--recovery`
|
|
486
|
-
**Permission:** query-only+
|
|
524
|
+
**Options:** `--format <json|jsonl|csv|html>` (required), `--output <path>`, `--force`, `--recovery`, `--index <name>` (Elasticsearch), `--no-limit` (Elasticsearch full-index scroll)
|
|
525
|
+
**Permission:** query-only+ — SQL, MongoDB, and **(v1.22)** Elasticsearch.
|
|
487
526
|
|
|
488
527
|
The `html` format emits the same self-contained dashboard as `query --ui` (see [Interactive HTML dashboard](#interactive-html-dashboard)). Because `export` runs raw SQL (no snippet metadata), the HTML report is always rendered as a sortable / filterable table — no KPIs or charts. Use `dbcli q @<name> --format html` (or `--ui`) for the charted view.
|
|
489
528
|
|
|
529
|
+
> **Elasticsearch export (v1.22):** pass a search DSL with `--index <index>` to export the hits, or pass an index name as the query to scroll the whole index via `match_all`. Default cap is 1000 rows; `--no-limit` streams the full index via scroll in batches. Index-level blacklist is checked before export and an audit record is written.
|
|
530
|
+
|
|
490
531
|
### blacklist
|
|
491
532
|
|
|
492
533
|
Manage sensitive data blacklist to prevent AI access to restricted tables/columns.
|
|
@@ -531,6 +572,50 @@ dbcli diff --against before.json --format json
|
|
|
531
572
|
**Options:** `--snapshot <path>`, `--against <path>`, `--format <json|table>`
|
|
532
573
|
**Permission:** query-only+
|
|
533
574
|
|
|
575
|
+
### snapshot
|
|
576
|
+
|
|
577
|
+
Capture a **result fingerprint** of a query (not schema): `rowCount` plus per-column
|
|
578
|
+
aggregates (null/distinct counts, min/max/sum, an order-independent checksum) and a
|
|
579
|
+
top-level `resultChecksum`. Blacklisted columns are masked at the source by QueryExecutor,
|
|
580
|
+
so the fingerprint is safe to store and share. Use it as a baseline for `assert --against`.
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
dbcli snapshot "SELECT * FROM orders WHERE created_at >= '2026-05-01'" # → .dbcli/snapshots/snap-<timestamp>.json
|
|
584
|
+
dbcli snapshot @analytics/daily-revenue --out base.json # saved query → explicit path
|
|
585
|
+
dbcli snapshot "SELECT status, count(*) FROM orders GROUP BY status" --stdout
|
|
586
|
+
dbcli snapshot "SELECT * FROM orders" --rows --out full.json # also store masked rows
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Options:** `--out <path>` (default `.dbcli/snapshots/snap-<timestamp>.json`), `--rows`, `--stdout`, `--format <json|table>`, `--no-limit`
|
|
590
|
+
**Engines:** SQL only (PostgreSQL / MySQL / MariaDB)
|
|
591
|
+
**Permission:** query-only+
|
|
592
|
+
|
|
593
|
+
### assert
|
|
594
|
+
|
|
595
|
+
Assert an **invariant** on a query result. Exits `1` on failure (so it composes in
|
|
596
|
+
scripts / CI) unless `--no-fail` is given. Three modes (combinable):
|
|
597
|
+
|
|
598
|
+
- `--expect <condition>` — inline check against the result:
|
|
599
|
+
- `rows > 0` / `rows == 1` … (row count vs operators `> >= < <= == !=`)
|
|
600
|
+
- `value == 5000` / `value == "done"` (single-cell result; project to one column)
|
|
601
|
+
- `col:email not null` · `col:id unique` · `col:amount between 0 and 100` · `col:age >= 18`
|
|
602
|
+
- `--vs <query> --compare rows|value` — reconcile against a second query (cross-check totals/counts).
|
|
603
|
+
- `--against <snapshot> --tolerance <pct>` — compare the current result fingerprint to a saved snapshot. `tolerance 0` requires an exact (order-independent) checksum match; `tolerance 0.01` allows ±1% drift on rowCount and each numeric column sum.
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
dbcli assert "SELECT count(*) FROM orders" --expect "value > 0"
|
|
607
|
+
dbcli assert "SELECT * FROM orders WHERE total < 0" --expect "rows == 0" # no negative totals
|
|
608
|
+
dbcli assert "SELECT email FROM users" --expect "col:email not null"
|
|
609
|
+
dbcli assert "SELECT sum(amount) FROM ledger_a" --vs "SELECT sum(amount) FROM ledger_b" --compare value
|
|
610
|
+
dbcli assert "SELECT * FROM orders" --against base.json --tolerance 0.01
|
|
611
|
+
dbcli assert "SELECT count(*) FROM orders" --expect "value > 100" --no-fail # report only, exit 0
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Options:** `--expect <condition>`, `--vs <query>`, `--compare <rows|value>` (default `value`), `--against <path>`, `--tolerance <pct>` (default `0`), `--no-fail`, `--format <json|table>`
|
|
615
|
+
**Output:** `AssertVerdict` = `{ pass, checks: [{ name, expected, actual, pass }] }`
|
|
616
|
+
**Engines:** SQL only (PostgreSQL / MySQL / MariaDB)
|
|
617
|
+
**Permission:** query-only+
|
|
618
|
+
|
|
534
619
|
### status
|
|
535
620
|
|
|
536
621
|
Show current configuration status (safe for AI agents, no credentials exposed).
|
|
@@ -554,6 +639,8 @@ Read-only snapshot for AI agents. Never emits credentials or blacklisted values.
|
|
|
554
639
|
| `--for-agent` | Shortcut for `--format json --brief` |
|
|
555
640
|
| `--no-connect` | Skip the cheap version/object probe (no DB traffic) |
|
|
556
641
|
| `--probe-timeout <ms>` | Hard timeout for the version/object probe (default 1500) |
|
|
642
|
+
| `--require-schema-cache` | Throw `SCHEMA_CACHE_MISSING` (recovery code) when the active SQL connection has no usable schema cache |
|
|
643
|
+
| `--recovery` | On failure, emit a structured `RecoveryEnvelope` to stdout |
|
|
557
644
|
|
|
558
645
|
Example:
|
|
559
646
|
|
|
@@ -561,7 +648,14 @@ Example:
|
|
|
561
648
|
dbcli inspect --for-agent
|
|
562
649
|
```
|
|
563
650
|
|
|
564
|
-
Output schema is locked at `schemaVersion: 1`. Sections: `connection`, `permission`, `blacklist`, `objects`, `schemaCache`, `snippets`, `suggestedCommands`, `warnings`.
|
|
651
|
+
Output schema is locked at `schemaVersion: 1`. Sections: `connection`, `permission`, `blacklist`, `objects`, `schemaCache`, `snippets`, `suggestedCommands`, `hints` **(v1.23)**, `warnings`.
|
|
652
|
+
|
|
653
|
+
**`suggestedCommands` (context-aware, v1.23)** — a three-tier weighted list:
|
|
654
|
+
1. *Bootstrap* — always-safe orientation commands (`blacklist list`, `schema <table>`, ...).
|
|
655
|
+
2. *Context-aware* — driven by recent activity. When a hot table is detected in the audit log **and** task packs are available, suggests `dbcli skill tasks plan analyze-table-perf --param table=<table>` plus `dbcli queries suggest <intent>` from your snippet intents.
|
|
656
|
+
3. *Discovery* — broader exploration commands.
|
|
657
|
+
|
|
658
|
+
**`hints` (v1.23)** — a parallel array of human-readable, non-executable notes: the most-queried table from recent audit, the number of available task packs, and the schema-cache size with its last-refresh timestamp. In markdown output they render as a `## Hints` section. Audit reads here are read-only and never throw. Both `suggestedCommands` and `hints` are trimmed under `--for-agent` / `--brief` (≤ 3 hints, single safest command).
|
|
565
659
|
|
|
566
660
|
**Permission:** query-only+
|
|
567
661
|
|
|
@@ -633,6 +727,30 @@ Boundaries:
|
|
|
633
727
|
|
|
634
728
|
**Permission:** query-only+
|
|
635
729
|
|
|
730
|
+
#### guide missing-index-for (v1.23)
|
|
731
|
+
|
|
732
|
+
A single-query composite-index advisor. Parses one `SELECT`, combines a real
|
|
733
|
+
`EXPLAIN` plan with existing indexes, and emits index candidates each carrying a
|
|
734
|
+
`confidence` (`high` / `medium` / `low`) and a `reason`. Read-only (EXPLAIN +
|
|
735
|
+
index introspection only). MySQL/MariaDB + PostgreSQL.
|
|
736
|
+
|
|
737
|
+
```bash
|
|
738
|
+
dbcli guide missing-index-for "SELECT ... FROM betting_logs b JOIN hoster_machines hm ON ..."
|
|
739
|
+
dbcli guide missing-index-for @analytics/live-summary # @saved-query
|
|
740
|
+
dbcli guide missing-index-for "..." --format json # yaml (default) | json | markdown
|
|
741
|
+
dbcli guide missing-index-for "..." --min-confidence medium # drop candidates below low|medium|high
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
**Options:** `--format <yaml|json|markdown>` (default `yaml`), `--min-confidence <low|medium|high>`.
|
|
745
|
+
|
|
746
|
+
Behaviour:
|
|
747
|
+
- Detects existing-index collisions (a single-column index that can be extended into a composite).
|
|
748
|
+
- Functional/expression columns (e.g. `DATE(settled_at)`) and SQL it cannot parse are reported under `warnings`, never as recommendations.
|
|
749
|
+
- Single `SELECT` only — no INSERT/UPDATE/DELETE, stored procedures, or view bodies.
|
|
750
|
+
- Dialects beyond node-sql-parser support fall back to EXPLAIN-only heuristics.
|
|
751
|
+
|
|
752
|
+
**Permission:** query-only+
|
|
753
|
+
|
|
636
754
|
### recovery
|
|
637
755
|
|
|
638
756
|
Machine-readable error envelope. Two surfaces share one `RecoveryEnvelope`
|
|
@@ -1005,6 +1123,11 @@ Inside the shell:
|
|
|
1005
1123
|
- Multi-line SQL: keeps accumulating until `;` is found
|
|
1006
1124
|
- History persists across sessions (~/.dbcli_history)
|
|
1007
1125
|
|
|
1126
|
+
The REPL flavor depends on the active engine: SQL engines and MongoDB use the
|
|
1127
|
+
form above; **Redis** opens a single-line command REPL (see [Redis › Interactive
|
|
1128
|
+
shell](#interactive-shell)); **Elasticsearch** opens a Kibana Dev Tools-style
|
|
1129
|
+
REPL (v1.22, see [Elasticsearch › Interactive shell](#interactive-shell-v122)).
|
|
1130
|
+
|
|
1008
1131
|
### migrate
|
|
1009
1132
|
|
|
1010
1133
|
Schema DDL operations. **All commands default to dry-run** — use `--execute` to actually run the SQL. Destructive operations (DROP) also require `--force`.
|
|
@@ -1059,26 +1182,28 @@ dbcli migrate drop-enum status --execute --force
|
|
|
1059
1182
|
|
|
1060
1183
|
### skill
|
|
1061
1184
|
|
|
1062
|
-
Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or
|
|
1063
|
-
|
|
1064
|
-
Claude Code / Gemini / Copilot / Cursor know how to drive dbcli safely.
|
|
1185
|
+
Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or an
|
|
1186
|
+
AI-agent platform directory. The skill is the source of truth that lets
|
|
1187
|
+
Claude Code / Gemini / Antigravity / Copilot / Cursor know how to drive dbcli safely.
|
|
1065
1188
|
|
|
1066
1189
|
```bash
|
|
1067
1190
|
dbcli skill # print SKILL.md to stdout
|
|
1068
1191
|
dbcli skill --output ./SKILL.md # write to a file (no platform install)
|
|
1069
1192
|
dbcli skill --install claude # install to ~/.claude/skills/dbcli/
|
|
1070
|
-
dbcli skill --install gemini # install to ~/.gemini/skills/dbcli/
|
|
1193
|
+
dbcli skill --install gemini # install to ~/.gemini/skills/dbcli/ (being phased out)
|
|
1194
|
+
dbcli skill --install antigravity # install to ~/.gemini/antigravity-cli/skills/dbcli/
|
|
1071
1195
|
dbcli skill --install copilot # install to .github/skills/dbcli/ (repo-local)
|
|
1072
1196
|
dbcli skill --install cursor # install to .cursor/skills/dbcli/ (repo-local)
|
|
1073
1197
|
```
|
|
1074
1198
|
|
|
1075
1199
|
**Options:**
|
|
1076
|
-
- `--install <platform>` — `claude` | `gemini` | `copilot` | `cursor`. Writes `SKILL.md` plus `reference.md` next to it so the agent gets progressive disclosure.
|
|
1200
|
+
- `--install <platform>` — `claude` | `gemini` | `antigravity` | `copilot` | `cursor` | `codex` | `windsurf`. Writes `SKILL.md` plus `reference.md` next to it so the agent gets progressive disclosure.
|
|
1077
1201
|
- `--output <path>` — write `SKILL.md` to a file instead of stdout. Does not install `reference.md`.
|
|
1078
1202
|
|
|
1079
1203
|
**Notes:**
|
|
1080
1204
|
- Both files come straight from `assets/SKILL.md` + `assets/reference.md` inside the dbcli package — no runtime rendering. Keep these in sync when shipping a release.
|
|
1081
|
-
- `claude` / `gemini` install paths are user-global; `copilot` / `cursor` are repo-local under `.github/` / `.cursor/`.
|
|
1205
|
+
- `claude` / `gemini` / `antigravity` install paths are user-global; `copilot` / `cursor` are repo-local under `.github/` / `.cursor/`.
|
|
1206
|
+
- `gemini` (Gemini CLI) is retained for now but is being phased out in favour of `antigravity` (Antigravity CLI), Google's successor terminal agent.
|
|
1082
1207
|
- Re-running `--install` overwrites the existing skill atomically; no prompt.
|
|
1083
1208
|
|
|
1084
1209
|
**Permission:** n/a.
|
|
@@ -1099,6 +1224,16 @@ dbcli skill tasks plan diagnose-slow-query --param query="..." --format json
|
|
|
1099
1224
|
- **show:** prints the full task definition (frontmatter + Agent Notes). Use `--format json` for an agent-friendly contract.
|
|
1100
1225
|
- **plan:** resolves `{{param}}` placeholders, validates required parameters, and emits a stable plan. Plans are **plan-only** in this version — dbcli will never execute the resulting commands automatically.
|
|
1101
1226
|
|
|
1227
|
+
**Builtin packs:** `diagnose-slow-query` and **(v1.23)** `analyze-table-perf` —
|
|
1228
|
+
a read-only (`plan-only`) pack taking a required `table` parameter that walks
|
|
1229
|
+
`blacklist list` → `schema <table> --format json` → `guide index-usage --format json`.
|
|
1230
|
+
`dbcli inspect` suggests `analyze-table-perf` automatically for the hottest table
|
|
1231
|
+
in recent audit activity.
|
|
1232
|
+
|
|
1233
|
+
```bash
|
|
1234
|
+
dbcli skill tasks plan analyze-table-perf --param table=betting_logs --format json
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1102
1237
|
Task storage layers:
|
|
1103
1238
|
|
|
1104
1239
|
| Source | Path | Notes |
|
|
@@ -1531,19 +1666,19 @@ dbcli delete orders --where '{"status":"cancelled"}' --force
|
|
|
1531
1666
|
|
|
1532
1667
|
## Redis Support
|
|
1533
1668
|
|
|
1534
|
-
Redis connections speak Redis commands rather than SQL. The adapter uses
|
|
1669
|
+
Redis connections speak Redis commands rather than SQL. The adapter uses Bun's native `Bun.RedisClient` and exposes a permission-gated surface with a query size guard and key-glob blacklist enforcement.
|
|
1535
1670
|
|
|
1536
|
-
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `status`, `doctor`, `upgrade`, `completion`
|
|
1671
|
+
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `shell`, `status`, `doctor`, `upgrade`, `completion`
|
|
1537
1672
|
|
|
1538
1673
|
**Saved queries:** `q` is supported for read-only Redis commands (see "Redis snippets" below).
|
|
1539
1674
|
|
|
1540
|
-
**Not supported (exit with error or unsupported error):** `insert`, `update`, `delete`, `export`, `check`, `diff`, `migrate
|
|
1675
|
+
**Not supported (exit with error or unsupported error):** `insert`, `update`, `delete`, `export`, `check`, `diff`, `migrate`. For writes, run the equivalent Redis command via `query` — the same permission gate applies.
|
|
1541
1676
|
|
|
1542
1677
|
### Connection and configuration
|
|
1543
1678
|
|
|
1544
1679
|
- Required fields: `system: redis`, `host`, `port`. `password` and `database` are optional.
|
|
1545
1680
|
- `database` is the **logical DB index** (`"0"` … `"15"`), kept as a string to play nicely with env-ref bindings. `list` and the connection metadata both label it as the active DB.
|
|
1546
|
-
- `connection.timeout` (ms, default 5000) maps to
|
|
1681
|
+
- `connection.timeout` (ms, default 5000) maps to the client's `connectionTimeout`.
|
|
1547
1682
|
|
|
1548
1683
|
### Permission classification
|
|
1549
1684
|
|
|
@@ -1590,22 +1725,83 @@ dbcli query "DEL temp:lock"
|
|
|
1590
1725
|
dbcli query "HDEL user:42 lastLogin"
|
|
1591
1726
|
```
|
|
1592
1727
|
|
|
1728
|
+
### Size guard (`query --no-limit` / shell `.no-limit`)
|
|
1729
|
+
|
|
1730
|
+
The adapter rewrites unbounded reads before dispatch and truncates oversized replies after:
|
|
1731
|
+
|
|
1732
|
+
| Strategy | Commands | Behavior |
|
|
1733
|
+
|----------|----------|----------|
|
|
1734
|
+
| inject/cap `COUNT` | `SCAN`, `HSCAN`, `SSCAN`, `ZSCAN` | adds `COUNT 1000` when absent; caps a larger `COUNT` to 1000 |
|
|
1735
|
+
| clamp `stop` | `LRANGE`, `ZRANGE`, `ZREVRANGE` | rewrites `stop` so the span ≤ 1000 (`-1` becomes `start+999`) |
|
|
1736
|
+
| inject/cap `LIMIT` | `ZRANGEBYSCORE` | appends `LIMIT 0 1000` when absent; caps a larger count |
|
|
1737
|
+
| client truncate | `HGETALL`, `HKEYS`, `HVALS`, `SMEMBERS`, `KEYS` | keeps the first 1000 entries |
|
|
1738
|
+
|
|
1739
|
+
Rewrites emit a `REDIS_SIZE_REWRITE` warning; truncations emit `REDIS_SIZE_TRUNCATE`. Both surface in the result's `warnings[]`. Pass `--no-limit` (CLI) or toggle `.no-limit on` (shell) to disable all guards.
|
|
1740
|
+
|
|
1741
|
+
### Blacklist enforcement
|
|
1742
|
+
|
|
1743
|
+
Blacklist rules are enforced as **Redis-native key globs** (`*`, `?`, `[abc]`, `[a-z]`):
|
|
1744
|
+
|
|
1745
|
+
```bash
|
|
1746
|
+
dbcli blacklist add 'secrets:*' # register a key-glob rule
|
|
1747
|
+
dbcli query "GET secrets:api_key" # → BlacklistRejection (exit non-zero)
|
|
1748
|
+
dbcli query "MGET safe:k secrets:api" # → rejected (any matching key fails the whole command)
|
|
1749
|
+
dbcli query "KEYS secrets:*" # → rejected (pattern overlaps a rule)
|
|
1750
|
+
dbcli query "KEYS *" # → returns only non-blacklisted keys
|
|
1751
|
+
```
|
|
1752
|
+
|
|
1753
|
+
Rejections are written to the audit log with `success: false` and `metadata.rejection_reason: 'blacklist'` + `matched_pattern`.
|
|
1754
|
+
|
|
1755
|
+
### Value / hash-field masking (v1.22)
|
|
1756
|
+
|
|
1757
|
+
Where the key-glob blacklist *rejects*, masking instead *redacts*: a matched read still
|
|
1758
|
+
runs, but the sensitive value comes back as `[REDACTED]` so an agent can use the command
|
|
1759
|
+
without ever seeing it. Add an optional `redis.mask` block to `.dbcli`:
|
|
1760
|
+
|
|
1761
|
+
```yaml
|
|
1762
|
+
redis:
|
|
1763
|
+
mask:
|
|
1764
|
+
- keyPattern: 'session:*' # whole value redacted on read
|
|
1765
|
+
- keyPattern: 'user:*'
|
|
1766
|
+
fields: [password, token] # only these hash fields redacted
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
- Applies on reads: `GET`, `GETRANGE`, `HGETALL`, `HGET`, `HMGET`, `HVALS`.
|
|
1770
|
+
- A rule without `fields` redacts the entire value; with `fields` only the named hash fields are redacted.
|
|
1771
|
+
- Masking and key-glob rejection coexist, and **rejection always wins over masking** — a key that matches a blacklist rule is rejected, never merely masked.
|
|
1772
|
+
|
|
1773
|
+
### Interactive shell
|
|
1774
|
+
|
|
1775
|
+
`dbcli shell` on a Redis connection opens a single-line REPL:
|
|
1776
|
+
|
|
1777
|
+
```text
|
|
1778
|
+
$ dbcli --use local-redis shell
|
|
1779
|
+
Redis shell: single-line commands; SCAN/LRANGE auto-capped at 1000. Type `.no-limit on` to bypass (unsafe).
|
|
1780
|
+
redis> SCAN 0 # wire args become: SCAN 0 COUNT 1000 (REDIS_SIZE_REWRITE)
|
|
1781
|
+
redis> HGETALL bighash # >1000 fields → kept 1000 (REDIS_SIZE_TRUNCATE)
|
|
1782
|
+
redis> .no-limit on # bypass size guard for this session
|
|
1783
|
+
redis> GET secrets:api_key # → REDIS_BLACKLIST / BlacklistRejection if blacklisted
|
|
1784
|
+
redis> .exit
|
|
1785
|
+
```
|
|
1786
|
+
|
|
1787
|
+
Tab completion offers Redis command names and known key prefixes; history persists to `~/.dbcli_history`.
|
|
1788
|
+
|
|
1593
1789
|
### Limitations
|
|
1594
1790
|
|
|
1595
1791
|
- No `--dry-run` for writes — Redis commands execute immediately. Pair writes with a confirming read (`GET`, `HGETALL`, `EXISTS`).
|
|
1596
1792
|
- No transaction wrapping (`MULTI`/`EXEC`). Submit one command at a time.
|
|
1597
1793
|
- `KEYS` requires `admin`. Prefer `SCAN` for routine work.
|
|
1598
|
-
- Blacklist
|
|
1794
|
+
- Blacklist enforcement covers **keys** (Redis-native globs); value / hash-field **masking** is available via the `redis.mask` config block (v1.22).
|
|
1599
1795
|
|
|
1600
1796
|
## Elasticsearch Support
|
|
1601
1797
|
|
|
1602
1798
|
Elasticsearch connections speak the REST API. The adapter is fetch-based (no SDK) and supports HTTPS, custom CA, API key, basic auth, and Cloud ID.
|
|
1603
1799
|
|
|
1604
|
-
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `status`, `doctor`, `upgrade`, `completion`
|
|
1800
|
+
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `export` (v1.22), `shell` (v1.22), `status`, `doctor`, `upgrade`, `completion`
|
|
1605
1801
|
|
|
1606
1802
|
**Saved queries:** `q` is supported for ES JSON DSL bodies (see "Elasticsearch snippets" below).
|
|
1607
1803
|
|
|
1608
|
-
**Not supported (use external tooling):** `insert`, `update`, `delete`, `
|
|
1804
|
+
**Not supported (use external tooling):** `insert`, `update`, `delete`, `check`, `diff`, `migrate`. The permission classifier already understands `_doc` / `_update` / `_bulk` so future write surfaces can be wired in without changing tiers.
|
|
1609
1805
|
|
|
1610
1806
|
### Connection and configuration
|
|
1611
1807
|
|
|
@@ -1658,6 +1854,38 @@ dbcli query '{"size":0,"aggs":{"by_status":{"terms":{"field":"status.keyword"}}}
|
|
|
1658
1854
|
dbcli query 'status:active AND amount:>100' --index orders --limit 100
|
|
1659
1855
|
```
|
|
1660
1856
|
|
|
1857
|
+
### Export (v1.22)
|
|
1858
|
+
|
|
1859
|
+
`dbcli export` supports two shapes on an ES connection:
|
|
1860
|
+
|
|
1861
|
+
```bash
|
|
1862
|
+
# (a) search DSL + --index → export the hits
|
|
1863
|
+
dbcli export '{"query":{"match":{"status":"active"}}}' --index orders --format jsonl --output orders.ndjson
|
|
1864
|
+
|
|
1865
|
+
# (b) index name as the query → match_all over the whole index (scroll)
|
|
1866
|
+
dbcli export orders --format csv --output orders.csv
|
|
1867
|
+
dbcli export orders --no-limit --format jsonl # full index, scrolled in batches
|
|
1868
|
+
```
|
|
1869
|
+
|
|
1870
|
+
- Outputs JSON / JSONL / CSV. Default cap is 1000 rows; `--no-limit` streams the full index via the scroll API in batches.
|
|
1871
|
+
- Index-level blacklist is checked before export and the run is written to the audit log.
|
|
1872
|
+
|
|
1873
|
+
### Interactive shell (v1.22)
|
|
1874
|
+
|
|
1875
|
+
`dbcli shell` on an ES connection opens a Kibana Dev Tools-style REPL:
|
|
1876
|
+
|
|
1877
|
+
```text
|
|
1878
|
+
$ dbcli --use local-es shell
|
|
1879
|
+
GET /orders/_search
|
|
1880
|
+
{
|
|
1881
|
+
"query": { "match": { "status": "active" } }
|
|
1882
|
+
}
|
|
1883
|
+
# ← blank line submits the whole block
|
|
1884
|
+
```
|
|
1885
|
+
|
|
1886
|
+
- Enter a request line `<METHOD> /<path>`, then an optional multi-line JSON body; a **blank line** submits the block. Responses render as pretty-printed JSON.
|
|
1887
|
+
- Read-focused: index-level blacklist rejects protected indices at the front end; a `_search` whose body omits `size` is auto-capped at 1000 hits.
|
|
1888
|
+
|
|
1661
1889
|
### Doctor and diagnostics
|
|
1662
1890
|
|
|
1663
1891
|
`dbcli doctor` runs a dedicated Elasticsearch path:
|
|
@@ -1669,7 +1897,7 @@ dbcli query 'status:active AND amount:>100' --index orders --limit 100
|
|
|
1669
1897
|
|
|
1670
1898
|
### Limitations
|
|
1671
1899
|
|
|
1672
|
-
- Writes (`insert`/`update`/`delete
|
|
1900
|
+
- Writes (`insert`/`update`/`delete`) are not exposed yet — the adapter implements them, but the CLI currently only routes them for SQL and MongoDB. Read-only `export` (v1.22) and the interactive `shell` (v1.22) are available.
|
|
1673
1901
|
- No `_search/scroll` or PIT pagination at the CLI layer; large pulls need a saved external script.
|
|
1674
1902
|
- `check`, `diff`, `migrate`, and `q` are SQL-only and exit with errors (or fall through to a generic "unsupported" path).
|
|
1675
1903
|
- Blacklist column rules are applied to flattened hit rows on `query`; table-level blacklist rejects an index up front.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analyze-table-perf
|
|
3
|
+
description: Inspect a single table's structure, indexes and read-only performance signals.
|
|
4
|
+
tags: [diagnostics, performance, readonly]
|
|
5
|
+
engines: [postgres, mysql]
|
|
6
|
+
params:
|
|
7
|
+
table:
|
|
8
|
+
type: string
|
|
9
|
+
required: true
|
|
10
|
+
description: The table to analyze (exact name; confirm via `dbcli list`).
|
|
11
|
+
safety:
|
|
12
|
+
mode: plan-only
|
|
13
|
+
requires:
|
|
14
|
+
- blacklist-list
|
|
15
|
+
- schema-check
|
|
16
|
+
steps:
|
|
17
|
+
- type: command
|
|
18
|
+
command: blacklist list
|
|
19
|
+
reason: Confirm sensitive tables and columns are protected before inspection.
|
|
20
|
+
risk: readonly
|
|
21
|
+
- type: command
|
|
22
|
+
command: schema {{table}} --format json
|
|
23
|
+
reason: Inspect the table's columns and existing indexes.
|
|
24
|
+
risk: readonly
|
|
25
|
+
- type: command
|
|
26
|
+
command: guide index-usage --format json
|
|
27
|
+
reason: Review index-usage guidance to spot missing or unused indexes.
|
|
28
|
+
risk: readonly
|
|
29
|
+
---
|
|
30
|
+
# Agent Notes
|
|
31
|
+
|
|
32
|
+
Use this task when a table shows up as the hottest target in recent activity and
|
|
33
|
+
you want safe, read-only next steps to assess its performance characteristics.
|
|
34
|
+
Do not run write operations. For a single heavy query, prefer
|
|
35
|
+
`dbcli guide missing-index-for "<sql>"` instead.
|