@carllee1983/dbcli 1.30.0 → 1.32.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/.agents/plugins/marketplace.json +21 -0
- package/.claude-plugin/plugin.json +9 -0
- package/.codex-plugin/plugin.json +41 -0
- package/.cursor/rules/dbcli.mdc +515 -0
- package/.cursor/skills/dbcli/SKILL.md +106 -0
- package/.cursor/skills/dbcli/reference.md +1955 -0
- package/.cursor-plugin/plugin.json +36 -0
- package/.github/skills/dbcli/SKILL.md +515 -0
- package/.github/skills/dbcli/reference.md +1955 -0
- package/CHANGELOG.md +32 -0
- package/README.md +32 -1
- package/README.zh-TW.md +31 -1
- package/assets/SKILL.md +60 -2
- package/assets/SKILL.zh-TW.md +51 -3
- package/assets/reference.md +9 -1
- package/assets/tasks/audit-permissions.md +35 -0
- package/assets/tasks/connection-health.md +37 -0
- package/assets/tasks/safe-backfill.md +42 -0
- package/assets/tasks/schema-drift-review.md +37 -0
- package/dist/cli.mjs +19 -6
- package/dist/core.d.ts +110 -0
- package/dist/core.mjs +12712 -13
- package/gemini-extension.json +6 -0
- package/package.json +18 -5
- package/plugins/dbcli-agent/.codex-plugin/plugin.json +40 -0
- package/plugins/dbcli-agent/INSTALL.md +241 -0
- package/plugins/dbcli-agent/README.md +89 -0
- package/plugins/dbcli-agent/scripts/install-dbcli.sh +15 -0
- package/plugins/dbcli-agent/scripts/install-skills.sh +83 -0
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +515 -0
- package/plugins/dbcli-agent/skills/dbcli/reference.md +1955 -0
- package/skills/dbcli/SKILL.md +515 -0
- package/skills/dbcli/reference.md +1955 -0
|
@@ -0,0 +1,1955 @@
|
|
|
1
|
+
# dbcli — full command reference
|
|
2
|
+
|
|
3
|
+
Companion to [SKILL.md](SKILL.md). Exhaustive flags, copy-paste examples, `shell`, `completion`, `upgrade`, `migrate` DDL, and extended MongoDB examples.
|
|
4
|
+
|
|
5
|
+
For cross-engine support status, see `docs/feature-matrix.md` in the repository.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
### init
|
|
10
|
+
|
|
11
|
+
Initialize `.dbcli` configuration file. Typically run manually by the developer — avoid running on behalf of the user unless explicitly requested.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
dbcli init # Single connection (v1 format)
|
|
15
|
+
dbcli init --system mysql --host localhost --port 3306 --user root --name mydb
|
|
16
|
+
dbcli init --use-env-refs # Store env var references
|
|
17
|
+
dbcli init --no-interactive --force # Non-interactive mode
|
|
18
|
+
|
|
19
|
+
# MongoDB
|
|
20
|
+
dbcli init --system mongodb --uri "mongodb://user:pass@host:27017/mydb?authSource=admin"
|
|
21
|
+
dbcli init --system mongodb --host localhost --port 27017 --user admin --password secret --name mydb
|
|
22
|
+
dbcli init --system mongodb --host localhost --port 27017 --name mydb # No auth
|
|
23
|
+
|
|
24
|
+
# Redis (database = logical DB index)
|
|
25
|
+
dbcli init --system redis --host localhost --port 6379
|
|
26
|
+
dbcli init --system redis --host localhost --port 6379 --password secret --name 0
|
|
27
|
+
|
|
28
|
+
# Elasticsearch
|
|
29
|
+
dbcli init --system elasticsearch --host localhost --port 9200 --user elastic --password changeme
|
|
30
|
+
dbcli init --system elasticsearch --cloud-id "myCluster:dXMtZWFzdC0xLmF3..." --api-key "<base64>"
|
|
31
|
+
|
|
32
|
+
# Multi-connection (v2 format)
|
|
33
|
+
dbcli init --conn-name staging --env-file .env.staging # Named connection with custom env file
|
|
34
|
+
dbcli init --conn-name prod --env-file .env.production --use-env-refs --skip-test
|
|
35
|
+
dbcli init --remove staging # Remove a named connection
|
|
36
|
+
dbcli init --rename staging:production # Rename a connection
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Key options:** `--system`, `--permission`, `--use-env-refs`, `--skip-test`, `--no-interactive`, `--force`, `--conn-name <name>`, `--env-file <path>`, `--remove <name>`, `--rename <old:new>`
|
|
40
|
+
|
|
41
|
+
**MongoDB-specific options:** `--uri <uri>` (full connection URI), `--auth-source <db>` (auth database, default: `admin` when user/password set)
|
|
42
|
+
|
|
43
|
+
**Elasticsearch-specific options:** `--cloud-id <id>` (Elastic Cloud), `--api-key <key>` (ApiKey auth). Other ES fields (`nodes[]`, `protocol`, `caPath`, `rejectUnauthorized`) can be edited directly in `.dbcli`.
|
|
44
|
+
|
|
45
|
+
**Redis note:** the `database` (or `--name`) field is the logical DB index (`"0"` … `"15"`), not a database name.
|
|
46
|
+
|
|
47
|
+
**Multi-connection:** Using `--conn-name` or `--env-file` creates a v2 config with named connections. Each connection can have its own env file and permission level. Existing v1 configs are automatically imported as the `default` connection when upgrading.
|
|
48
|
+
|
|
49
|
+
> **AI agent note on `--use-env-refs`:** If an existing `.dbcli` config contains `{"$env": "DB_HOST"}` style references, the connection values are read from environment variables at runtime. Do NOT re-run `init` to replace these references with actual values — the env-ref format is intentional for CI/CD and multi-environment setups.
|
|
50
|
+
|
|
51
|
+
### use
|
|
52
|
+
|
|
53
|
+
Switch or display the default database connection (v2 multi-connection config).
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
dbcli use # Show current default connection
|
|
57
|
+
dbcli use staging # Switch default to 'staging'
|
|
58
|
+
dbcli use --list # List all connections (* marks default)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Any command can also use `--use <name>` to temporarily select a connection without changing the default:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
dbcli query --use staging "SELECT * FROM users LIMIT 10"
|
|
65
|
+
dbcli list --use prod
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Requires v2 config** (created with `dbcli init --conn-name`).
|
|
69
|
+
|
|
70
|
+
### list
|
|
71
|
+
|
|
72
|
+
List all tables (SQL), collections (MongoDB), keys (Redis), or indices (Elasticsearch).
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
dbcli list
|
|
76
|
+
dbcli list --format json
|
|
77
|
+
dbcli list --include-system # Elasticsearch: include `.system` indices
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Permission:** query-only+
|
|
81
|
+
|
|
82
|
+
> **MongoDB:** Lists collections with estimated document count.
|
|
83
|
+
> **Redis:** Returns up to 100 000 keys via `SCAN MATCH * COUNT 1000`. The header reads `Keys in db <n> (redis):` where `<n>` is the logical DB index.
|
|
84
|
+
> **Elasticsearch:** Returns indices with `documentCount` from `/_stats/docs`; aliases are tagged separately. System indices (names starting with `.`) are hidden unless `--include-system` is passed.
|
|
85
|
+
|
|
86
|
+
### schema
|
|
87
|
+
|
|
88
|
+
Display table schema or scan entire database.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
dbcli schema # Scan all tables, save to .dbcli/schemas/
|
|
92
|
+
dbcli schema users # Show single table schema
|
|
93
|
+
dbcli schema users --format json
|
|
94
|
+
dbcli schema --refresh # Detect and apply schema changes
|
|
95
|
+
dbcli schema --reset # Clear all schema data and re-fetch
|
|
96
|
+
dbcli schema --reset --force # Skip confirmation
|
|
97
|
+
|
|
98
|
+
# Per-connection schema isolation (v2 multi-connection config)
|
|
99
|
+
dbcli schema --use staging # Scan staging DB; saves to .dbcli/schemas/staging/
|
|
100
|
+
dbcli schema --use prod # Scan prod DB; saves to .dbcli/schemas/prod/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Options:** `--format <table|json>`, `--refresh`, `--reset`, `--force`, `--use <connection>`, `--sample-size <n>` (mongo only), `--sample-method <random|natural>` (mongo only)
|
|
104
|
+
**Permission:** query-only+
|
|
105
|
+
|
|
106
|
+
**Schema storage (v1.4+):** Schema is persisted as layered files under `.dbcli/schemas/`. With v2 multi-connection config each connection gets its own subdirectory (`.dbcli/schemas/<connection>/`). Run `dbcli schema --use <connection>` once per connection before querying it — otherwise `schema <table>` may return data from the wrong connection's cache.
|
|
107
|
+
|
|
108
|
+
> **Redis:** `schema <key>` is required (no full scan). The output exposes `type`, `ttl`, `size`, and a small `sample` (e.g. first 5 hash keys). `--reset` / `--refresh` are rejected — Redis caches no schema.
|
|
109
|
+
> **Elasticsearch:** `schema [index]` flattens the `_mapping` properties (nested `a.b.c`) and emits each `.fields` multi-field as a separate column (e.g. `text` + `text.keyword`). Full scan iterates all non-system indices and stores per-connection caches alongside SQL engines.
|
|
110
|
+
> **MongoDB:** schema is sampled via `$sample` (default 100, max 1000). `--sample-method natural` switches to `find().limit()`; `random` (default) falls back to natural order on driver error. Output columns surface nested dot-paths with `presence` (0..1) and `redacted: true` flags for blacklist-matched paths. The persisted cache records `sampleMethod` and `sampleSize`; `dbcli doctor` reports them via a `sampled: method=…, size=…` line.
|
|
111
|
+
|
|
112
|
+
### query
|
|
113
|
+
|
|
114
|
+
Execute SQL query (MySQL/PostgreSQL/MariaDB) or JSON filter/pipeline (MongoDB).
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# SQL databases
|
|
118
|
+
dbcli query "SELECT * FROM users LIMIT 10"
|
|
119
|
+
dbcli query "SELECT id, email FROM users" --format json
|
|
120
|
+
dbcli query "SELECT * FROM logs" --no-limit
|
|
121
|
+
|
|
122
|
+
# MongoDB: JSON filter (find)
|
|
123
|
+
dbcli query '{"status": "active"}' --collection users
|
|
124
|
+
dbcli query '{"age": {"$gt": 18}}' --collection users --format json
|
|
125
|
+
|
|
126
|
+
# MongoDB: aggregation pipeline
|
|
127
|
+
dbcli query '[{"$match": {"status": "active"}}, {"$group": {"_id": "$role", "count": {"$sum": 1}}}]' --collection users
|
|
128
|
+
|
|
129
|
+
# Redis: any whitelisted command (permission-gated by command)
|
|
130
|
+
dbcli query "GET session:abc"
|
|
131
|
+
dbcli query "HGETALL user:42" --format json
|
|
132
|
+
dbcli query "SCAN 0 MATCH user:* COUNT 100"
|
|
133
|
+
dbcli query "SET feature:flag enabled" # requires read-write+
|
|
134
|
+
dbcli query "DEL stale:key" # requires data-admin+
|
|
135
|
+
|
|
136
|
+
# Elasticsearch: DSL body or Lucene q-string
|
|
137
|
+
dbcli query '{"query":{"match":{"status":"active"}}}' --collection orders
|
|
138
|
+
dbcli query 'status:active AND amount:>100' --index orders --limit 50
|
|
139
|
+
|
|
140
|
+
# Interactive HTML dashboard (see "Interactive HTML dashboard" below)
|
|
141
|
+
dbcli query "SELECT day, dau FROM dau_daily" --ui # open in browser
|
|
142
|
+
dbcli query "SELECT * FROM orders" --format html > orders.html # pipe to stdout
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**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`), `--recovery`
|
|
146
|
+
**Permission:** query-only+ (Redis: per-command; Elasticsearch: per HTTP method/path)
|
|
147
|
+
|
|
148
|
+
> **MongoDB notes:**
|
|
149
|
+
> - SQL syntax is rejected — use JSON object (filter) or JSON array (pipeline)
|
|
150
|
+
> - `--collection <name>` is required
|
|
151
|
+
> - Auto-limit does not apply; use `$limit` in your pipeline if needed
|
|
152
|
+
|
|
153
|
+
> **Redis notes:**
|
|
154
|
+
> - The first token must be an allow-listed command (`GET`/`SET`/`HGET`/`HSET`/`DEL`/...). Unknown commands are refused.
|
|
155
|
+
> - Permission tier is derived from the command (read → `query-only`, write → `read-write`, delete → `data-admin`, `KEYS`/`FLUSHDB`/`CONFIG`/... → `admin`).
|
|
156
|
+
> - Output is always shaped into rows: scalar replies become `{value: ...}`; arrays become indexed rows; `HGETALL` is folded into a single object.
|
|
157
|
+
|
|
158
|
+
> **Elasticsearch notes:**
|
|
159
|
+
> - `--collection` (or `--index`) is required.
|
|
160
|
+
> - A body that begins with `{` is sent as DSL via `POST /<index>/_search`; otherwise the value is URL-encoded into `?q=...` (Lucene query string) via `GET`.
|
|
161
|
+
> - Hits are flattened: each result row contains `_id` plus dotted-path fields from `_source`. Pass `--format json` to keep nested structures readable.
|
|
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
|
+
|
|
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
|
+
|
|
198
|
+
### plan
|
|
199
|
+
|
|
200
|
+
Static SQL risk analyzer. Classifies a statement into the same permission tiers
|
|
201
|
+
used by `query` (`query-only` / `read-write` / `data-admin` / `admin`) and lists
|
|
202
|
+
the underlying signals (DML / DDL / multi-statement / unsafe constructs) without
|
|
203
|
+
ever connecting to the database.
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
dbcli plan "SELECT * FROM users"
|
|
207
|
+
dbcli plan "UPDATE users SET name='x'" # human-readable text classification
|
|
208
|
+
dbcli plan "DROP TABLE users" --format json # machine-readable risk report
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Options:** `--format <text|json>` (default `text`).
|
|
212
|
+
**Permission:** n/a (offline analyzer; no connection opened).
|
|
213
|
+
|
|
214
|
+
Use cases:
|
|
215
|
+
- Agents that want to decide whether to call `query` vs `insert` / `update` /
|
|
216
|
+
`delete` before sending SQL.
|
|
217
|
+
- Pre-flight safety check before binding parameters into a saved snippet.
|
|
218
|
+
- Lint hook for code review pipelines that store SQL in source.
|
|
219
|
+
|
|
220
|
+
`plan` does not enforce blacklist or auto-`LIMIT`; those still apply when the
|
|
221
|
+
SQL is actually executed via `query` / `q`.
|
|
222
|
+
|
|
223
|
+
### q
|
|
224
|
+
|
|
225
|
+
Run a saved query snippet by `@name`. Snippets are parameterised SELECT/WITH statements resolved from three layers, with **local > shared > builtin** precedence (a local file always shadows shared and builtin variants of the same key):
|
|
226
|
+
|
|
227
|
+
- `builtin` — bundled with dbcli (e.g. `@diag/*`); read-only at runtime.
|
|
228
|
+
- `.dbcli-shared/queries/` — committed, team-shared.
|
|
229
|
+
- `.dbcli/queries/` — gitignored, personal override.
|
|
230
|
+
|
|
231
|
+
Engine variants (`name.postgres.sql` / `name.mysql.sql`) at the same layer are merged; the variant matching the active connection's engine is selected at execution time.
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
dbcli q @dau # run with declared defaults
|
|
235
|
+
dbcli q @dau --param days=30 --format json # override a param
|
|
236
|
+
dbcli q @analytics/revenue --param-file params.json
|
|
237
|
+
dbcli q @dau --dry-run # show final SQL + bind values
|
|
238
|
+
dbcli q @dau --no-limit # disable size guard wrap
|
|
239
|
+
dbcli q @analytics/revenue --param days=30 --ui # open dashboard
|
|
240
|
+
dbcli q @analytics/revenue --param days=30 --format html > report.html
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Options:**
|
|
244
|
+
- `--format <table|json|csv|html>` — output format (default: `table`)
|
|
245
|
+
- `--ui` — open the rendered HTML dashboard in the system browser (implies `--format html`; writes to a temp file then invokes `open` / `xdg-open` / `start`)
|
|
246
|
+
- `--param <key=value>` — pass a parameter (repeatable)
|
|
247
|
+
- `--param-file <path>` — JSON object whose keys are param names
|
|
248
|
+
- `--no-limit` — skip the `SELECT * FROM (…) AS _dbcli_guard LIMIT 1000` wrap
|
|
249
|
+
- `--dry-run` — print the bound SQL + values without executing
|
|
250
|
+
- `--use <name>` — pick a v2 named connection
|
|
251
|
+
- `--recovery` — emit a `RecoveryEnvelope` on failure (see `recover`)
|
|
252
|
+
|
|
253
|
+
**Permission:** query-only+
|
|
254
|
+
|
|
255
|
+
#### Snippet file format
|
|
256
|
+
|
|
257
|
+
Each `.sql` file is plain SQL with optional YAML frontmatter inside a leading `-- ---` block. Lines outside frontmatter form the SQL body.
|
|
258
|
+
|
|
259
|
+
```sql
|
|
260
|
+
-- ---
|
|
261
|
+
-- name: DAU
|
|
262
|
+
-- description: Daily Active Users
|
|
263
|
+
-- engine: postgres # or [postgres, mysql]
|
|
264
|
+
-- params:
|
|
265
|
+
-- days:
|
|
266
|
+
-- type: int # int | string | float | bool | date | datetime
|
|
267
|
+
-- default: 7
|
|
268
|
+
-- required: false
|
|
269
|
+
-- description: lookback window in days
|
|
270
|
+
-- enum: [7, 30, 90]
|
|
271
|
+
-- tags: [analytics]
|
|
272
|
+
-- intent: perf.slow-query # optional; consumed by `queries suggest`
|
|
273
|
+
-- visual: # optional; consumed by `--ui` / `--format html`
|
|
274
|
+
-- title: Daily Active Users
|
|
275
|
+
-- kpis:
|
|
276
|
+
-- - { label: DAU, value_column: dau, format: number }
|
|
277
|
+
-- charts:
|
|
278
|
+
-- - { type: line, title: DAU trend, x: day, y: [dau] }
|
|
279
|
+
-- ---
|
|
280
|
+
SELECT COUNT(DISTINCT user_id) AS dau
|
|
281
|
+
FROM events
|
|
282
|
+
WHERE created_at > NOW() - (:days || ' days')::interval;
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Param placeholders use `:name`. They are rewritten to `$1, $2, …` (Postgres) or `?, ?, …` (MySQL) at execution time and passed as bind values — string interpolation is never used.
|
|
286
|
+
|
|
287
|
+
The `visual:` block is documented in detail under [Interactive HTML dashboard](#interactive-html-dashboard) below. Unknown / malformed fields are silently dropped at parse time; the snippet still runs and the dashboard falls back to a sortable table.
|
|
288
|
+
|
|
289
|
+
#### Param type coercion
|
|
290
|
+
|
|
291
|
+
| Declared `type` | Accepts |
|
|
292
|
+
|-----------------|---------|
|
|
293
|
+
| `int` | integer literal |
|
|
294
|
+
| `float` | decimal literal |
|
|
295
|
+
| `bool` | `true` / `false` / `1` / `0` / `yes` / `no` |
|
|
296
|
+
| `string` | any value |
|
|
297
|
+
| `date` | `YYYY-MM-DD` |
|
|
298
|
+
| `datetime` | ISO 8601 |
|
|
299
|
+
|
|
300
|
+
`enum` (optional) restricts the accepted values; mismatch is a hard error. CLI `--param` overrides `--param-file`, which overrides the snippet's `default`.
|
|
301
|
+
|
|
302
|
+
#### Safety invariants
|
|
303
|
+
|
|
304
|
+
- Only `SELECT` / `WITH` (CTE) bodies are accepted; `INSERT/UPDATE/DELETE/DDL` are rejected by the parser.
|
|
305
|
+
- Multi-statement bodies (`SELECT 1; DROP TABLE x`) are rejected.
|
|
306
|
+
- Template syntax inside SQL (`${…}`, `{{…}}`) is rejected — use `:name` parameters.
|
|
307
|
+
- Files exceeding 64 KiB are rejected.
|
|
308
|
+
- `--no-limit` is honoured only at the outermost level; nested subqueries are still wrapped by the size guard.
|
|
309
|
+
|
|
310
|
+
##### Elasticsearch snippets
|
|
311
|
+
|
|
312
|
+
Body is a JSON DSL `_search` request body. Frontmatter requires an `index:` field (may contain `:param`).
|
|
313
|
+
|
|
314
|
+
Example:
|
|
315
|
+
|
|
316
|
+
-- ---
|
|
317
|
+
-- name: events-by-day
|
|
318
|
+
-- engine: elasticsearch
|
|
319
|
+
-- index: 'events-:date'
|
|
320
|
+
-- params:
|
|
321
|
+
-- date: { type: date, required: true }
|
|
322
|
+
-- user_id: { type: int, required: true }
|
|
323
|
+
-- ---
|
|
324
|
+
{
|
|
325
|
+
"query": {
|
|
326
|
+
"bool": {
|
|
327
|
+
"filter": [
|
|
328
|
+
{ "term": { "user_id": :user_id } }
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
"size": 100
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
Substitution rules (type-aware JSON injection):
|
|
336
|
+
|
|
337
|
+
- `int` / `float` / `bool` outside string literals → bare value (`42`, `1.5`, `true`)
|
|
338
|
+
- `string` / `date` / `datetime` outside string literals → JSON-quoted (`"Alice"`, `"2026-05-08"`)
|
|
339
|
+
- Any param inside a JSON string literal → escaped inner form (`"prefix-:name"` works)
|
|
340
|
+
|
|
341
|
+
`script` and `script_fields` are rejected anywhere in the body.
|
|
342
|
+
|
|
343
|
+
Size guard: if `size` is missing, `1000` is injected (or `0` when `aggs` is present); explicit `size > 1000` is overridden with a warning unless `--no-limit`.
|
|
344
|
+
|
|
345
|
+
##### Redis snippets
|
|
346
|
+
|
|
347
|
+
Body is a single Redis command on one line. Only read-only commands are allowed:
|
|
348
|
+
`GET MGET HGET HGETALL HMGET HKEYS HVALS HLEN HEXISTS LRANGE LLEN LINDEX SMEMBERS SISMEMBER SCARD ZRANGE ZRANGEBYSCORE ZRANGEBYLEX ZSCORE ZCARD ZCOUNT ZRANK TYPE EXISTS TTL PTTL STRLEN OBJECT SCAN HSCAN SSCAN ZSCAN`.
|
|
349
|
+
|
|
350
|
+
`KEYS`, `EVAL`, `FLUSHDB`, `FLUSHALL`, `CONFIG`, `DEBUG`, `SHUTDOWN`, `SCRIPT` and any write command are rejected.
|
|
351
|
+
|
|
352
|
+
Example:
|
|
353
|
+
|
|
354
|
+
-- ---
|
|
355
|
+
-- name: cache-user
|
|
356
|
+
-- engine: redis
|
|
357
|
+
-- params:
|
|
358
|
+
-- id: { type: int, required: true }
|
|
359
|
+
-- ---
|
|
360
|
+
HGETALL user::id
|
|
361
|
+
|
|
362
|
+
Substitution rules: pure raw text — `:name` becomes the value's `String()` form. **Quoting is the snippet author's responsibility**: wrap `:name` in double quotes if the value may contain whitespace. The parser warns when a `string`-typed `:name` is adjacent to non-whitespace and unquoted.
|
|
363
|
+
|
|
364
|
+
Size guard: `LRANGE` / `ZRANGE` stop overridden when `< 0` or `> 1000`; `SCAN` / `HSCAN` / `SSCAN` / `ZSCAN` get `COUNT 1000` injected if absent. `--no-limit` disables.
|
|
365
|
+
|
|
366
|
+
##### MongoDB snippets
|
|
367
|
+
|
|
368
|
+
File extension: `.mongodb.sql`. Frontmatter must declare `engine: mongodb` and
|
|
369
|
+
`operation: find` or `operation: aggregate`. `target: <collection>` provides a default
|
|
370
|
+
collection that `dbcli q --collection <name>` can override. The body is JSON: an object
|
|
371
|
+
for `find` and an array for `aggregate`. Each `{{param}}` placeholder is JSON-encoded
|
|
372
|
+
at substitution time — strings are quoted and escaped, so an attacker-supplied string
|
|
373
|
+
cannot escape into operator position.
|
|
374
|
+
|
|
375
|
+
Find example (`active-users.mongodb.sql`):
|
|
376
|
+
|
|
377
|
+
-- ---
|
|
378
|
+
-- name: active-users
|
|
379
|
+
-- engine: mongodb
|
|
380
|
+
-- operation: find
|
|
381
|
+
-- target: users
|
|
382
|
+
-- description: Active users matching the given status
|
|
383
|
+
-- params:
|
|
384
|
+
-- status:
|
|
385
|
+
-- type: string
|
|
386
|
+
-- required: true
|
|
387
|
+
-- ---
|
|
388
|
+
{
|
|
389
|
+
"status": {{status}}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
Aggregate example (`top-orders-by-city.mongodb.sql`):
|
|
393
|
+
|
|
394
|
+
-- ---
|
|
395
|
+
-- name: top-orders-by-city
|
|
396
|
+
-- engine: mongodb
|
|
397
|
+
-- operation: aggregate
|
|
398
|
+
-- target: orders
|
|
399
|
+
-- description: Top order counts per city for a given status
|
|
400
|
+
-- params:
|
|
401
|
+
-- status:
|
|
402
|
+
-- type: string
|
|
403
|
+
-- required: true
|
|
404
|
+
-- limit:
|
|
405
|
+
-- type: int
|
|
406
|
+
-- default: 10
|
|
407
|
+
-- ---
|
|
408
|
+
[
|
|
409
|
+
{ "$match": { "status": {{status}} } },
|
|
410
|
+
{ "$group": { "_id": "$city", "n": { "$sum": 1 } } },
|
|
411
|
+
{ "$sort": { "n": -1 } },
|
|
412
|
+
{ "$limit": {{limit}} }
|
|
413
|
+
]
|
|
414
|
+
|
|
415
|
+
Run with `dbcli q @active-users -p status=active` or `dbcli q @top-orders-by-city -p status=open -p limit=5`. The `q` command applies the same nested-blacklist redaction to results that `query` and `export` do.
|
|
416
|
+
|
|
417
|
+
### queries
|
|
418
|
+
|
|
419
|
+
Manage saved snippets — discover, inspect, scaffold, and edit local copies. Mutating
|
|
420
|
+
subcommands (`delete`, `rename`, `copy`, `import`) only operate on the local layer
|
|
421
|
+
(`.dbcli/queries/`); builtin and shared snippets are never modified in place.
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
# Discovery / inspection
|
|
425
|
+
dbcli queries list # all snippets (builtin + shared + local)
|
|
426
|
+
dbcli queries list --tag analytics --engine postgres --format json
|
|
427
|
+
dbcli queries list --source local # only personal overrides
|
|
428
|
+
dbcli queries show @dau # frontmatter + SQL
|
|
429
|
+
dbcli queries show @dau --format json # MCP-shaped contract
|
|
430
|
+
dbcli queries search slow query # fuzzy-ranked keyword search across snippets
|
|
431
|
+
dbcli queries search cache --engine postgres --source builtin --limit 5
|
|
432
|
+
dbcli queries suggest perf # browse snippets by intent prefix (v1.11+)
|
|
433
|
+
dbcli queries suggest perf.cache-hit --format json
|
|
434
|
+
|
|
435
|
+
# Authoring
|
|
436
|
+
dbcli queries new @new/sample # scaffold under .dbcli-shared/queries/
|
|
437
|
+
dbcli queries new @scratch --local # personal copy under .dbcli/queries/
|
|
438
|
+
dbcli queries edit @dau # opens local first, falls back to shared
|
|
439
|
+
dbcli queries edit @dau --shared # always edit the shared file
|
|
440
|
+
dbcli queries check # parse all snippets; exit 1 on errors
|
|
441
|
+
dbcli queries check --strict # promote warnings (e.g. missing engine) to errors
|
|
442
|
+
|
|
443
|
+
# Local-layer file management
|
|
444
|
+
dbcli queries delete @scratch # remove local file(s); prompts unless --force
|
|
445
|
+
dbcli queries delete @scratch --force
|
|
446
|
+
dbcli queries rename @scratch @analytics/dau # rename within local layer; preserves engine suffix
|
|
447
|
+
dbcli queries copy @diag/connections @my/connections # fork builtin/shared into local for editing
|
|
448
|
+
dbcli queries import ./hotfix.sql # import an external .sql into .dbcli/queries/
|
|
449
|
+
dbcli queries import ./hotfix.sql --as @diag/custom # override the snippet key
|
|
450
|
+
dbcli queries export @dau --output dau.sql # write snippet body to a file (stdout if omitted)
|
|
451
|
+
dbcli queries export @diag/connections --engine postgres # pick a variant when multiple engines exist
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**`list` options:** `--format <table|json|csv>`, `--tag <tag>`, `--engine <postgres|mysql|redis|elasticsearch|all>`, `--source <local|shared|builtin|all>`
|
|
455
|
+
**`show` options:** `--format <table|json|csv>`
|
|
456
|
+
**`search` options:** `--format <table|json>`, `--engine <postgres|mysql|redis|elasticsearch|all>`, `--source <local|shared|builtin|all>`, `--limit <n>` (default 10), `--include-internal` (show fuzzy ranking score). Keyword(s) are fuzzy-matched against name, description, tags, intent.
|
|
457
|
+
**`suggest` options:** `--format <table|json>`, `--engine <postgres|mysql|redis|elasticsearch|all>`, `--source <local|shared|builtin|all>`. Intent prefix-matched against the snippet's `intent` frontmatter field. Common intents: `perf.slow-query`, `perf.cache-hit`, `capacity.size`, `safety.connections`, `monitor.cluster-health`.
|
|
458
|
+
**`new` options:** `--local`, `--edit`
|
|
459
|
+
**`edit` options:** `--shared`
|
|
460
|
+
**`check` options:** `--strict`, `--format <table|json|csv>`
|
|
461
|
+
**`delete` options:** `--force` (skip the confirmation prompt). Refuses to run if `@name` has no local copy.
|
|
462
|
+
**`rename` options:** `--force`. Both names must start with `@`. Engine suffix (`.postgres.sql` / `.mysql.sql`) is preserved; frontmatter `name:` is rewritten to the new key.
|
|
463
|
+
**`copy` options:** *(none)*. Copies every variant (all engines) of the source into the local layer; fails if the destination already has a local copy.
|
|
464
|
+
**`import` options:** `--force` (overwrite existing local file), `--as <name>` (override snippet key; defaults to filename without `.postgres` / `.mysql` suffix). Source must be `.sql` and parse cleanly (frontmatter validated, non-SELECT bodies rejected).
|
|
465
|
+
**`export` options:** `--output <path>` (write to file; otherwise stdout), `--engine <postgres|mysql>` (required when the snippet has multiple engine variants).
|
|
466
|
+
|
|
467
|
+
`--format json` on `list` and `show` emits a stable, machine-readable shape — designed to back a future MCP server without further refactor.
|
|
468
|
+
|
|
469
|
+
### insert
|
|
470
|
+
|
|
471
|
+
Insert data into a table.
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
dbcli insert users --data '{"name":"Alice","email":"alice@example.com"}'
|
|
475
|
+
dbcli insert users --data '{"name":"Alice"}' --dry-run
|
|
476
|
+
dbcli insert users --data '{"name":"Alice"}' --force
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Options:** `--data <json>`, `--dry-run`, `--force`
|
|
480
|
+
**Permission:** read-write+
|
|
481
|
+
|
|
482
|
+
### update
|
|
483
|
+
|
|
484
|
+
Update existing data.
|
|
485
|
+
|
|
486
|
+
```bash
|
|
487
|
+
dbcli update users --where "id=1" --set '{"name":"Bob"}'
|
|
488
|
+
dbcli update users --where "id=1" --set '{"name":"Bob"}' --dry-run
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**Options:** `--where <condition>` (required), `--set <json>` (required), `--dry-run`, `--force`
|
|
492
|
+
**Permission:** read-write+
|
|
493
|
+
|
|
494
|
+
### delete
|
|
495
|
+
|
|
496
|
+
Delete data from a table.
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
dbcli delete users --where "id=1"
|
|
500
|
+
dbcli delete users --where "id=1" --dry-run
|
|
501
|
+
dbcli delete users --where "id=1" --force
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Options:** `--where <condition>` (required), `--dry-run`, `--force`
|
|
505
|
+
**Permission:** data-admin+
|
|
506
|
+
|
|
507
|
+
### export
|
|
508
|
+
|
|
509
|
+
Export query results to file or stdout.
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
dbcli export "SELECT * FROM users" --format csv --output users.csv
|
|
513
|
+
dbcli export "SELECT * FROM users" --format csv --output users.csv --force # Skip overwrite confirmation
|
|
514
|
+
dbcli export "SELECT * FROM users" --format json | jq '.[]'
|
|
515
|
+
dbcli export "SELECT * FROM users" --format jsonl --output users.ndjson
|
|
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
|
|
522
|
+
```
|
|
523
|
+
|
|
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.
|
|
526
|
+
|
|
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.
|
|
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
|
+
|
|
531
|
+
### blacklist
|
|
532
|
+
|
|
533
|
+
Manage sensitive data blacklist to prevent AI access to restricted tables/columns.
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
dbcli blacklist list # Show current blacklist
|
|
537
|
+
dbcli blacklist table add payments # Block entire table
|
|
538
|
+
dbcli blacklist table remove payments # Unblock table
|
|
539
|
+
dbcli blacklist column add users.password # Block specific column
|
|
540
|
+
dbcli blacklist column remove users.password
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Subcommands:** `list`, `table add <name>`, `table remove <name>`, `column add <table.column>`, `column remove <table.column>`
|
|
544
|
+
|
|
545
|
+
### check
|
|
546
|
+
|
|
547
|
+
Run data health checks on tables.
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
dbcli check users # Check single table
|
|
551
|
+
dbcli check users --format json # JSON output (default)
|
|
552
|
+
dbcli check --all # Check all tables (huge tables auto-skipped)
|
|
553
|
+
dbcli check --all --include-large # Include huge tables
|
|
554
|
+
dbcli check orders --checks nulls,orphans # Specific checks only
|
|
555
|
+
dbcli check orders --sample 10000 # Sample size for large tables
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Checks:** `nulls`, `duplicates`, `orphans`, `emptyStrings`, `rowCount`, `size`
|
|
559
|
+
**Options:** `--all`, `--include-large`, `--checks <types>`, `--sample <number>`, `--format <json|table>`
|
|
560
|
+
**Permission:** query-only+
|
|
561
|
+
|
|
562
|
+
### diff
|
|
563
|
+
|
|
564
|
+
Compare schema snapshots to detect changes.
|
|
565
|
+
|
|
566
|
+
```bash
|
|
567
|
+
dbcli diff --snapshot before.json # Save current schema snapshot
|
|
568
|
+
dbcli diff --against before.json # Compare current vs snapshot
|
|
569
|
+
dbcli diff --against before.json --format json
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Options:** `--snapshot <path>`, `--against <path>`, `--format <json|table>`
|
|
573
|
+
**Permission:** query-only+
|
|
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
|
+
|
|
619
|
+
### proxy
|
|
620
|
+
|
|
621
|
+
Local-development **observability proxy** for MySQL/MariaDB/PostgreSQL. Inserts dbcli
|
|
622
|
+
between an existing application and its real database: it listens on a configurable
|
|
623
|
+
port, relays TCP frames to the real server, and appends one JSONL event per query to
|
|
624
|
+
`.dbcli/proxy/events.jsonl`. Observe-only — no rewrite, blocking, or query modification.
|
|
625
|
+
Not intended as a production gateway.
|
|
626
|
+
|
|
627
|
+
**Subcommands:** `mysql` · `mariadb` · `postgresql`
|
|
628
|
+
|
|
629
|
+
```bash
|
|
630
|
+
dbcli proxy mysql --listen 127.0.0.1:3307 --target 127.0.0.1:3306
|
|
631
|
+
dbcli proxy postgresql --listen 127.0.0.1:5434 --target 127.0.0.1:5432
|
|
632
|
+
dbcli proxy mysql --slow-ms 500 --redact literals # redact SQL literals in events
|
|
633
|
+
dbcli proxy mariadb --events ./logs/proxy.jsonl # custom event file
|
|
634
|
+
dbcli proxy postgresql --use prod # infer target from named connection
|
|
635
|
+
|
|
636
|
+
dbcli proxy analyze # analyze .dbcli/proxy/events.jsonl (JSON)
|
|
637
|
+
dbcli proxy analyze --format text --top 10 # human-readable top-10 view
|
|
638
|
+
dbcli proxy analyze --slow-ms 200 --n-plus-one 5 # custom thresholds
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Options:**
|
|
642
|
+
- `--listen <addr:port>` — Address dbcli will listen on (e.g. `127.0.0.1:3307`)
|
|
643
|
+
- `--target <addr:port>` — Address of the real database server to relay to. If omitted, inferred from the active (or `--use`) connection config.
|
|
644
|
+
- `--events <path>` — JSONL event log path (default: `.dbcli/proxy/events.jsonl`)
|
|
645
|
+
- `--slow-ms <ms>` — Threshold in milliseconds above which events are flagged `slow: true` (default: `1000`)
|
|
646
|
+
- `--redact <none|literals>` — Whether to strip SQL literal values from event records (default: `none`; `literals` removes quoted strings and numbers)
|
|
647
|
+
- `--format <text|json>` — Startup / status output format (default: `text`)
|
|
648
|
+
- `--use <name>` — Target a named v2 connection for `--target` inference
|
|
649
|
+
|
|
650
|
+
**Event schema (JSONL):** each line is one event. `type` is one of `proxy_started`, `session_started`, `query_observed`, `query_completed`, `query_errored`, `session_ended`, `parse_error`. A representative `query_completed` line:
|
|
651
|
+
```json
|
|
652
|
+
{ "version": 1, "type": "query_completed", "timestamp": "<ISO-8601>", "engine": "mysql", "sessionId": "pxy_1", "queryId": "qry_pxy_1_1", "client": "127.0.0.1:54321", "target": "127.0.0.1:3306", "sql": "SELECT * FROM users WHERE id = 1", "statement": "SELECT", "tables": ["users"], "durationMs": 42, "requestBytes": 128, "responseBytes": 512, "rowCount": null, "slow": false, "error": null, "tags": [] }
|
|
653
|
+
```
|
|
654
|
+
`slow` is `true` when `durationMs >= --slow-ms` (also printed as a terminal warning). `rowCount` is best-effort (PostgreSQL command tags; `null` for MySQL). TLS is relayed but not decrypted in v1. Prepared/extended wire protocols are best-effort tagged.
|
|
655
|
+
|
|
656
|
+
**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.
|
|
657
|
+
|
|
658
|
+
**`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.
|
|
659
|
+
|
|
660
|
+
**Engines:** MySQL / MariaDB / PostgreSQL
|
|
661
|
+
**Permission:** n/a (acts as a TCP relay; does not use dbcli's SQL permission model)
|
|
662
|
+
|
|
663
|
+
### status
|
|
664
|
+
|
|
665
|
+
Show current configuration status (safe for AI agents, no credentials exposed).
|
|
666
|
+
|
|
667
|
+
```bash
|
|
668
|
+
dbcli status # JSON output (default)
|
|
669
|
+
dbcli status --format text # Human-readable text output
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
**Output:** `permission`, `system`, `blacklist` summary, `version`
|
|
673
|
+
**Permission:** query-only+
|
|
674
|
+
|
|
675
|
+
### inspect
|
|
676
|
+
|
|
677
|
+
Read-only snapshot for AI agents. Never emits credentials or blacklisted values.
|
|
678
|
+
|
|
679
|
+
| Flag | Purpose |
|
|
680
|
+
|------|---------|
|
|
681
|
+
| `--format <json\|markdown>` | Output format (default `json`) |
|
|
682
|
+
| `--brief` | Drop sample arrays and trim suggested commands to ≤3 |
|
|
683
|
+
| `--for-agent` | Shortcut for `--format json --brief` |
|
|
684
|
+
| `--no-connect` | Skip the cheap version/object probe (no DB traffic) |
|
|
685
|
+
| `--probe-timeout <ms>` | Hard timeout for the version/object probe (default 1500) |
|
|
686
|
+
| `--require-schema-cache` | Throw `SCHEMA_CACHE_MISSING` (recovery code) when the active SQL connection has no usable schema cache |
|
|
687
|
+
| `--recovery` | On failure, emit a structured `RecoveryEnvelope` to stdout |
|
|
688
|
+
|
|
689
|
+
Example:
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
dbcli inspect --for-agent
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
Output schema is locked at `schemaVersion: 1`. Sections: `connection`, `permission`, `blacklist`, `objects`, `schemaCache`, `snippets`, `suggestedCommands`, `hints` **(v1.23)**, `warnings`.
|
|
696
|
+
|
|
697
|
+
**`suggestedCommands` (context-aware, v1.23)** — a three-tier weighted list:
|
|
698
|
+
1. *Bootstrap* — always-safe orientation commands (`blacklist list`, `schema <table>`, ...).
|
|
699
|
+
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.
|
|
700
|
+
3. *Discovery* — broader exploration commands.
|
|
701
|
+
|
|
702
|
+
**`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).
|
|
703
|
+
|
|
704
|
+
**Permission:** query-only+
|
|
705
|
+
|
|
706
|
+
### report
|
|
707
|
+
|
|
708
|
+
Diagnostic report built on top of `inspect`. Reuses inspect context (connection,
|
|
709
|
+
permission, blacklist, snippet inventory) and additionally runs curated built-in
|
|
710
|
+
`@diag/*` snippets grouped into sections.
|
|
711
|
+
|
|
712
|
+
Flags:
|
|
713
|
+
- `--format json|markdown` (default: json)
|
|
714
|
+
- `--section health,capacity,perf` (default: all three)
|
|
715
|
+
- `--brief` — drop evidence rows; keep counts and statuses
|
|
716
|
+
- `--for-agent` — shortcut for `--format json --brief`
|
|
717
|
+
- `--no-connect` — context-only snapshot (skip diagnostics + inspect probe)
|
|
718
|
+
- `--per-snippet-timeout <ms>` (default 3000)
|
|
719
|
+
- `--max-rows-per-evidence <n>` (default 50)
|
|
720
|
+
- `--probe-timeout <ms>` (default 1500, inherited from inspect)
|
|
721
|
+
|
|
722
|
+
Examples:
|
|
723
|
+
|
|
724
|
+
dbcli report --format json
|
|
725
|
+
dbcli report --format markdown --section health,capacity
|
|
726
|
+
dbcli report --for-agent
|
|
727
|
+
dbcli report --no-connect
|
|
728
|
+
|
|
729
|
+
Boundaries:
|
|
730
|
+
- Read-only. Skips snippets whose required params have no default value.
|
|
731
|
+
- Never connects in `--no-connect` mode.
|
|
732
|
+
- MongoDB and no-config workspaces emit a context-only snapshot with a warning.
|
|
733
|
+
|
|
734
|
+
**Permission:** query-only+
|
|
735
|
+
|
|
736
|
+
### guide
|
|
737
|
+
|
|
738
|
+
Deterministic next-command planner for a fixed set of database goals. Reuses
|
|
739
|
+
`inspect` context (cache-first) and the workspace's saved-query inventory to
|
|
740
|
+
emit an ordered, read-only plan that an AI agent can follow step-by-step.
|
|
741
|
+
|
|
742
|
+
Goals (fixed list):
|
|
743
|
+
- `slow-query` — diagnose slow queries (long-running, locks, cache, indexes).
|
|
744
|
+
- `capacity` — audit storage and memory.
|
|
745
|
+
- `health` — connections, locks, cluster status.
|
|
746
|
+
- `index-usage` — index effectiveness audit.
|
|
747
|
+
- `permissions` — review permission level, blacklist, snippet inventory.
|
|
748
|
+
- `schema-overview` — orient in an unfamiliar database.
|
|
749
|
+
|
|
750
|
+
Flags:
|
|
751
|
+
- `--format json|markdown` (default: json)
|
|
752
|
+
- `--brief` — drop rationale + expects fields
|
|
753
|
+
- `--for-agent` — shortcut for `--format json --brief`
|
|
754
|
+
- `--list` — list available goals and exit
|
|
755
|
+
- `--probe` — refresh inspect context via live probe (default: cache-first)
|
|
756
|
+
- `--probe-timeout <ms>` (default 1500, inherited from inspect)
|
|
757
|
+
|
|
758
|
+
Examples:
|
|
759
|
+
|
|
760
|
+
dbcli guide slow-query
|
|
761
|
+
dbcli guide capacity --format markdown
|
|
762
|
+
dbcli guide --list
|
|
763
|
+
dbcli guide health --for-agent
|
|
764
|
+
dbcli guide schema-overview --probe
|
|
765
|
+
|
|
766
|
+
Boundaries:
|
|
767
|
+
- Read-only. Guide plans commands; it does not execute them.
|
|
768
|
+
- Goal vocabulary is fixed in v1.14.0; user-supplied goals are rejected.
|
|
769
|
+
- Each step carries `risk: 'readonly'` in v1.14.0 (forward-compatible with v1.15.0 recovery).
|
|
770
|
+
- Coexists with `dbcli skill tasks plan` (template-driven). Use guide for ad-hoc goals; use task packs for repeatable workflows.
|
|
771
|
+
|
|
772
|
+
**Permission:** query-only+
|
|
773
|
+
|
|
774
|
+
#### guide missing-index-for (v1.23)
|
|
775
|
+
|
|
776
|
+
A single-query composite-index advisor. Parses one `SELECT`, combines a real
|
|
777
|
+
`EXPLAIN` plan with existing indexes, and emits index candidates each carrying a
|
|
778
|
+
`confidence` (`high` / `medium` / `low`) and a `reason`. Read-only (EXPLAIN +
|
|
779
|
+
index introspection only). MySQL/MariaDB + PostgreSQL.
|
|
780
|
+
|
|
781
|
+
```bash
|
|
782
|
+
dbcli guide missing-index-for "SELECT ... FROM betting_logs b JOIN hoster_machines hm ON ..."
|
|
783
|
+
dbcli guide missing-index-for @analytics/live-summary # @saved-query
|
|
784
|
+
dbcli guide missing-index-for "..." --format json # yaml (default) | json | markdown
|
|
785
|
+
dbcli guide missing-index-for "..." --min-confidence medium # drop candidates below low|medium|high
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
**Options:** `--format <yaml|json|markdown>` (default `yaml`), `--min-confidence <low|medium|high>`.
|
|
789
|
+
|
|
790
|
+
Behaviour:
|
|
791
|
+
- Detects existing-index collisions (a single-column index that can be extended into a composite).
|
|
792
|
+
- Functional/expression columns (e.g. `DATE(settled_at)`) and SQL it cannot parse are reported under `warnings`, never as recommendations.
|
|
793
|
+
- Single `SELECT` only — no INSERT/UPDATE/DELETE, stored procedures, or view bodies.
|
|
794
|
+
- Dialects beyond node-sql-parser support fall back to EXPLAIN-only heuristics.
|
|
795
|
+
|
|
796
|
+
**Permission:** query-only+
|
|
797
|
+
|
|
798
|
+
### recovery
|
|
799
|
+
|
|
800
|
+
Machine-readable error envelope. Two surfaces share one `RecoveryEnvelope`
|
|
801
|
+
shape (`schemaVersion: 1`):
|
|
802
|
+
|
|
803
|
+
1. **Standalone lookup**: `dbcli recovery --code <CODE>` synthesizes an
|
|
804
|
+
envelope for any known recovery code without needing a real failure.
|
|
805
|
+
2. **Failing-command opt-in**: pass `--recovery` to `dbcli query` or
|
|
806
|
+
`dbcli q`. On failure, the envelope is written to stdout as JSON, the
|
|
807
|
+
human stderr message is suppressed, and the process exits non-zero.
|
|
808
|
+
|
|
809
|
+
Recovery codes (fixed in v1.15.0):
|
|
810
|
+
- `CONFIG_MISSING` — no `.dbcli` config; run `dbcli init`.
|
|
811
|
+
- `CONN_REFUSED` / `CONN_AUTH_FAILED` / `CONN_TIMEOUT` / `CONN_HOST_NOT_FOUND` / `CONN_UNKNOWN` — connection failure variants.
|
|
812
|
+
- `PERMISSION_DENIED` — active permission level forbids the operation.
|
|
813
|
+
- `BLACKLIST_TABLE` / `BLACKLIST_COLUMN_WRITE` — blacklist violations.
|
|
814
|
+
- `SNIPPET_NOT_FOUND` / `SNIPPET_AMBIGUOUS` / `SNIPPET_PARAM_MISSING` — saved-query failures.
|
|
815
|
+
- `SCHEMA_CACHE_MISSING` — local schema cache missing or stale.
|
|
816
|
+
- `UNKNOWN` — fallback for unclassified errors.
|
|
817
|
+
|
|
818
|
+
Flags (lookup mode):
|
|
819
|
+
- `--code <CODE>` — required unless `--list` is set.
|
|
820
|
+
- `--list` — list all codes and exit.
|
|
821
|
+
- `--format json|markdown` (default: json).
|
|
822
|
+
- `--brief` — drop `rationale` + `expects` from steps.
|
|
823
|
+
- `--for-agent` — shortcut for `--format json --brief`.
|
|
824
|
+
- `--hint <text>` — bind into placeholder steps.
|
|
825
|
+
- `--snippet <name>` — bind snippet placeholder.
|
|
826
|
+
- `--table <name>` — bind table placeholder.
|
|
827
|
+
|
|
828
|
+
Examples:
|
|
829
|
+
|
|
830
|
+
dbcli recovery --code CONN_REFUSED
|
|
831
|
+
dbcli recovery --code BLACKLIST_TABLE --table users --format markdown
|
|
832
|
+
dbcli recovery --list --for-agent
|
|
833
|
+
dbcli query "SELECT * FROM users" --recovery
|
|
834
|
+
dbcli q @diag/missing --recovery
|
|
835
|
+
|
|
836
|
+
Boundaries:
|
|
837
|
+
- Recovery only **suggests** commands; agents (or humans) execute them. No automatic remediation in v1.15.0.
|
|
838
|
+
- As of v1.16.0, `--recovery` is honored on `query`, `q`, `insert`, `update`, `delete`, `export`, `schema`, and `inspect`. Other commands (`report`, `guide`, `doctor`, `migrate`, `init`, `use`, `status`, `list`, `check`, `diff`, `plan`, `shell`, `blacklist`, `completion`, `upgrade`, `skill`) keep their existing error behavior.
|
|
839
|
+
- `dbcli inspect --require-schema-cache` throws `SCHEMA_CACHE_MISSING` when the active SQL connection has no usable schema cache. Combine with `--recovery` for the structured envelope.
|
|
840
|
+
- `BLACKLIST_COLUMN_WRITE` and `PERMISSION_DENIED` envelopes prepend a `risk: 'dry-run'` step (e.g. `dbcli insert <table> --dry-run`) when the failing operation was an INSERT / UPDATE / DELETE.
|
|
841
|
+
- Recovery steps reuse the v1.14.0 `GuideStep` shape, including the full `risk` enum (`readonly` / `dry-run` / `write` / `unknown`).
|
|
842
|
+
|
|
843
|
+
**Permission:** n/a
|
|
844
|
+
|
|
845
|
+
### recover
|
|
846
|
+
|
|
847
|
+
(v1.17.0+) Inspect or apply the last recovery plan saved by `--recovery`.
|
|
848
|
+
|
|
849
|
+
| Flag | Purpose | Default |
|
|
850
|
+
|---|---|---|
|
|
851
|
+
| `--apply` | Execute the saved plan under risk gating. | off (inspect only) |
|
|
852
|
+
| `--from <path>` | Read the envelope from this file instead of `.dbcli/last-recovery.json`. Accepts raw `RecoveryEnvelope` or `SavedRecoveryEnvelope`. | — |
|
|
853
|
+
| `--allow-write <tier>` | Open the risk gate. Values: `readonly-cmd` (local-side writes) \| `write-cmd` (database writes). | `none` |
|
|
854
|
+
| `--no-verify` | Skip the verify step appended after a successful `--apply`. | off (verify runs by default) |
|
|
855
|
+
| `--format <format>` | `markdown` \| `json`. | `markdown` for inspect, `json` for `--apply` |
|
|
856
|
+
|
|
857
|
+
#### Plan source resolution
|
|
858
|
+
|
|
859
|
+
1. `--from <path>` if provided. The file must be either a raw `RecoveryEnvelope` or a `SavedRecoveryEnvelope` wrapper. When the file is a `SavedRecoveryEnvelope`, its `cwd` is reused for child-process execution. Strict zod validation; malformed → exit 2 with structured reason.
|
|
860
|
+
2. Otherwise, `.dbcli/last-recovery.json` (auto-saved on every recovery emission). Validated with the same schema; missing fields, unknown `error.code`, or `cwd` that no longer exists → exit 2.
|
|
861
|
+
3. Otherwise, exits 2 with `No recovery plan available. Run a command with --recovery to generate one, or pass --from <file>.`
|
|
862
|
+
|
|
863
|
+
#### Code-owned tier (trust boundary)
|
|
864
|
+
|
|
865
|
+
`--apply` derives the canonical execution tier from the per-`error.code` allowlist after parsing argv, **not** from the envelope's `risk` / `dbWrite` / `interactive` fields. Envelope hints can only widen safety (skip more steps); they cannot escalate execution.
|
|
866
|
+
|
|
867
|
+
| Allowlist tier | Meaning | Example commands |
|
|
868
|
+
|---|---|---|
|
|
869
|
+
| `readonly` | local read-only | `dbcli inspect`, `dbcli doctor`, `dbcli blacklist list`, `dbcli schema <table>` |
|
|
870
|
+
| `dry-run` | write subcommand invoked with `--dry-run` | `dbcli update orders --where id=1 --dry-run`, `dbcli q @x --dry-run` |
|
|
871
|
+
| `local-write` | writes local config / cache / blacklist | `dbcli blacklist remove <table>`, `dbcli use <name>`, `dbcli schema --refresh` |
|
|
872
|
+
| `db-write` | mutates the connected database | `dbcli update orders --where id=1 --set …` (no `--dry-run`), `dbcli q @x` (no `--dry-run`) |
|
|
873
|
+
| `interactive` | requires TTY | `dbcli init`, `dbcli init --force` |
|
|
874
|
+
|
|
875
|
+
`insert` / `update` / `delete` / `q` are tier `dry-run` only when argv contains `--dry-run`; otherwise they are tier `db-write` regardless of envelope `risk` claim.
|
|
876
|
+
|
|
877
|
+
#### Risk gate matrix
|
|
878
|
+
|
|
879
|
+
| Allowlist tier | Default | `--allow-write=readonly-cmd` | `--allow-write=write-cmd` |
|
|
880
|
+
|---|---|---|---|
|
|
881
|
+
| `readonly` | run | run | run |
|
|
882
|
+
| `dry-run` | run | run | run |
|
|
883
|
+
| `local-write` | `skipped:risk` | run | run |
|
|
884
|
+
| `db-write` | `skipped:risk` | `skipped:risk` | run |
|
|
885
|
+
| `interactive` | `skipped:interactive` | `skipped:interactive` | `skipped:interactive` |
|
|
886
|
+
| unresolved placeholder in `command` | `skipped:placeholder` | `skipped:placeholder` | `skipped:placeholder` |
|
|
887
|
+
| command fails parse / allowlist | `skipped:unsafe-command` | `skipped:unsafe-command` | `skipped:unsafe-command` |
|
|
888
|
+
|
|
889
|
+
Precedence: envelope `interactive: true` > `placeholder` > `unsafe-command` > allowlist `interactive` > tier-based gating.
|
|
890
|
+
|
|
891
|
+
#### Exit codes
|
|
892
|
+
|
|
893
|
+
| Code | Condition |
|
|
894
|
+
|---|---|
|
|
895
|
+
| 0 | At least one step ran successfully and no step failed. |
|
|
896
|
+
| 1 | A step exited non-zero (fail-fast); see `stoppedAt`. |
|
|
897
|
+
| 2 | Envelope missing or malformed (failed schema validation, or saved `cwd` missing). |
|
|
898
|
+
| 3 | Every step was skipped — open `--allow-write` or fill placeholders. |
|
|
899
|
+
|
|
900
|
+
#### Auto-saved envelope
|
|
901
|
+
|
|
902
|
+
Every command that emits a `RecoveryEnvelope` (`query`, `q`, `insert`, `update`, `delete`, `export`, `schema`, `inspect` — all with `--recovery`) atomically writes the envelope to `.dbcli/last-recovery.json`. The wrapper carries `schemaVersion`, `savedAt`, a sanitized `command` summary, the workspace `cwd`, and the envelope itself. SQL text and `--where` / `--set` / `--data` / `--param` values are redacted as `<sql>` or `<redacted>`. `.dbcli/` is gitignored.
|
|
903
|
+
|
|
904
|
+
#### Verification (P4)
|
|
905
|
+
|
|
906
|
+
Each `RecoveryEnvelope` now carries an optional `verify: GuideStep` (always
|
|
907
|
+
`risk: 'readonly'`, never carries placeholders). `dbcli recover --apply` runs
|
|
908
|
+
the verify step after the main plan, only when `finalStatus === 'ok'` and
|
|
909
|
+
`--no-verify` is not set.
|
|
910
|
+
|
|
911
|
+
| Recovery code | Verify command | Heuristic |
|
|
912
|
+
|---|---|---|
|
|
913
|
+
| CONFIG_MISSING | `dbcli inspect --no-connect --format json` | `connection.name` truthy → passed |
|
|
914
|
+
| CONN_REFUSED / CONN_TIMEOUT / CONN_UNKNOWN / CONN_AUTH_FAILED / CONN_HOST_NOT_FOUND | `dbcli doctor --format json` | exit 0 → passed |
|
|
915
|
+
| PERMISSION_DENIED | `dbcli inspect --for-agent` | exit 0 → passed |
|
|
916
|
+
| BLACKLIST_TABLE | `dbcli inspect --for-agent` | exit 0 → passed |
|
|
917
|
+
| BLACKLIST_COLUMN_WRITE | `dbcli inspect --for-agent` | exit 0 → passed |
|
|
918
|
+
| SNIPPET_NOT_FOUND / SNIPPET_AMBIGUOUS / SNIPPET_PARAM_MISSING | `dbcli queries list --format json` | exit 0 → passed |
|
|
919
|
+
| SCHEMA_CACHE_MISSING | `dbcli inspect --format json` | `schemaCache.available === true` → passed |
|
|
920
|
+
| UNKNOWN | `dbcli doctor --format json` | exit 0 → passed |
|
|
921
|
+
|
|
922
|
+
`verifyStatus` values:
|
|
923
|
+
|
|
924
|
+
- `passed` — heuristic confirmed.
|
|
925
|
+
- `failed` — verifier exited non-zero or timed out.
|
|
926
|
+
- `indeterminate` — verifier exited 0 but expected shape not present, or the
|
|
927
|
+
step was gated (placeholder / unsafe-command); agents should re-check.
|
|
928
|
+
|
|
929
|
+
Exit codes are unchanged — `verifyStatus` is signal, not gate.
|
|
930
|
+
|
|
931
|
+
**Schema additions.** `RecoveryEnvelope.verify?: GuideStep` is additive (no
|
|
932
|
+
`schemaVersion` bump). v1.16 consumers ignore the field.
|
|
933
|
+
|
|
934
|
+
#### Multi-turn `--next` (P2)
|
|
935
|
+
|
|
936
|
+
`dbcli recover --next` returns the single next step in a saved recovery plan,
|
|
937
|
+
given which step the agent just executed and the result of that step. v1 walks
|
|
938
|
+
the plan linearly; future codes may branch on `prevResult.stdoutSummary`
|
|
939
|
+
deterministically.
|
|
940
|
+
|
|
941
|
+
| Flag | Required | Description |
|
|
942
|
+
|---|---|---|
|
|
943
|
+
| `--next` | yes | Activate the multi-turn lookup. |
|
|
944
|
+
| `--after-step <n>` | yes | 1-based order of the step the agent just executed. Range: `[1, envelope.recovery.length]` (or `[1, branches[id].steps.length]` when `--branch` is set). |
|
|
945
|
+
| `--result <value>` | yes | JSON `StepResultSummary` (inline) or `@<path>` to read from a file. |
|
|
946
|
+
| `--branch <id>` | no | Walk a specific branch by id (required on `--next` calls after a fork). See *Connection branching* below. |
|
|
947
|
+
| `--from <path>` | no | Override the auto-saved envelope. |
|
|
948
|
+
| `--format <fmt>` | no | `json` (default) or `markdown`. |
|
|
949
|
+
|
|
950
|
+
`--next` and `--apply` cannot be combined. `--allow-write` and `--no-verify`
|
|
951
|
+
are silently ignored under `--next` (no execution, no verification).
|
|
952
|
+
|
|
953
|
+
**`StepResultSummary` shape**
|
|
954
|
+
|
|
955
|
+
```ts
|
|
956
|
+
interface StepResultSummary {
|
|
957
|
+
status: 'ok' | 'failed' | 'skipped'
|
|
958
|
+
exitCode?: number
|
|
959
|
+
stdoutSummary?: string // last 4 KB; longer rejected
|
|
960
|
+
stderrSummary?: string // last 4 KB; longer rejected
|
|
961
|
+
}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
`@<path>` resolves relative to the dbcli invocation cwd. File whole-size cap is
|
|
965
|
+
64 KB; per-field 4 KB cap still applies.
|
|
966
|
+
|
|
967
|
+
**`NextResult` shape (output)**
|
|
968
|
+
|
|
969
|
+
```ts
|
|
970
|
+
interface NextResult {
|
|
971
|
+
schemaVersion: 1
|
|
972
|
+
kind: 'step' | 'done'
|
|
973
|
+
source: { kind: 'auto' | 'from'; path: string }
|
|
974
|
+
errorCode: RecoveryCode
|
|
975
|
+
cursor: number // step.order when kind='step'; totalSteps when 'done'
|
|
976
|
+
totalSteps: number
|
|
977
|
+
step?: GuideStep // present iff kind='step'
|
|
978
|
+
branchId?: string // set iff agent is currently traversing a branch
|
|
979
|
+
branchDescription?: string // mirror of branches[branchId].description
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
**Connection branching**
|
|
984
|
+
|
|
985
|
+
For `CONN_*` recovery codes, the envelope ships an additional `branches` map and a `branchFork` descriptor. Step 1 (`dbcli doctor --format json`) is the fork point: pass the doctor JSON in `--result.stdoutSummary` and `--next` will pick one of four labeled branches:
|
|
986
|
+
|
|
987
|
+
| Branch id | When chosen |
|
|
988
|
+
|---|---|
|
|
989
|
+
| `doctor-clean` | Doctor reports no errors — likely transient; verify baseline state, then retry. |
|
|
990
|
+
| `doctor-config-missing` | Doctor flagged a config-level failure (missing / invalid config). Re-init before reconnecting. |
|
|
991
|
+
| `doctor-auth-error` | Doctor confirms credentials were rejected. Re-init with `--force` to overwrite credentials. |
|
|
992
|
+
| `doctor-network-error` | Doctor confirms a network-level failure (host / port / DNS / timeout). Inspect and re-init host/port. |
|
|
993
|
+
|
|
994
|
+
NextResult sets `branchId` and `branchDescription` after the fork; subsequent `--next` calls must echo `--branch <id>` to walk that branch. If the doctor JSON cannot be parsed or no keyword matches, `--next` falls back to the linear `recovery` plan — branching never causes `--next` to fail. `--apply` ignores `branches` entirely (linear walk unchanged).
|
|
995
|
+
|
|
996
|
+
**Exit codes**
|
|
997
|
+
|
|
998
|
+
| Exit | Condition |
|
|
999
|
+
|---|---|
|
|
1000
|
+
| 0 | Returned a step or `done`. |
|
|
1001
|
+
| 2 | Envelope missing/malformed; `--after-step` missing/out-of-range; `--result` missing/malformed; `--next` combined with `--apply`. |
|
|
1002
|
+
|
|
1003
|
+
**Examples**
|
|
1004
|
+
|
|
1005
|
+
```bash
|
|
1006
|
+
# Walk a 3-step plan to completion
|
|
1007
|
+
dbcli recover --next --after-step 1 --result '{"status":"ok"}' # → step 2
|
|
1008
|
+
dbcli recover --next --after-step 2 --result '{"status":"ok"}' # → step 3
|
|
1009
|
+
dbcli recover --next --after-step 3 --result '{"status":"ok"}' # → done
|
|
1010
|
+
|
|
1011
|
+
# Result read from file (when stdout is large)
|
|
1012
|
+
dbcli recover --next --after-step 1 --result @/tmp/r1.json
|
|
1013
|
+
|
|
1014
|
+
# Markdown for human inspection
|
|
1015
|
+
dbcli recover --next --after-step 1 --result '{"status":"ok"}' --format markdown
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
**Permission:** n/a (always-allowed lookup; child processes inherit the active permission level).
|
|
1019
|
+
|
|
1020
|
+
### audit
|
|
1021
|
+
|
|
1022
|
+
(v1.20.0+) Inspect, query, and manage the per-connection audit log written to `.dbcli/audit/<connection>.jsonl`.
|
|
1023
|
+
|
|
1024
|
+
Audit entries are metadata-only by design — never raw SQL bodies, `--param` values, or result cell contents (D3 lock). Redaction is sourced from `tests/helpers/sensitive-output.ts` (same source as `inspect` / `guide` / `recover` agent contracts).
|
|
1025
|
+
|
|
1026
|
+
#### Subcommands
|
|
1027
|
+
|
|
1028
|
+
| Subcommand | Side-effect tier | Purpose |
|
|
1029
|
+
|---|---|---|
|
|
1030
|
+
| `audit tail` | `readonly` | List most recent entries on the current (or `--all`) connection. |
|
|
1031
|
+
| `audit show` | `readonly` | Print a single full entry by id prefix or `--recovery-ref`. |
|
|
1032
|
+
| `audit clear` | `local-write` | Delete `<conn>.jsonl` + rotated `.jsonl.1` from local disk. Requires `--yes` or interactive confirm. |
|
|
1033
|
+
| `audit health` | `readonly` | Render `AuditLogger.getHealth()` snapshot (writer state, lock state, rotation usage). |
|
|
1034
|
+
|
|
1035
|
+
#### `audit tail`
|
|
1036
|
+
|
|
1037
|
+
| Flag | Purpose | Default |
|
|
1038
|
+
|---|---|---|
|
|
1039
|
+
| `--n <N>` | Number of recent entries to print (latest at bottom — D5). | `10` |
|
|
1040
|
+
| `--all` | Merge entries across all connections; output is an envelope array `[{ connection, entry }, ...]` (D-39). | off (current connection only) |
|
|
1041
|
+
| `--for-agent` | Shortcut for `--format json --brief`. Single-connection JSON is a flat array; `--all` JSON is an envelope array. | off |
|
|
1042
|
+
| `--brief` | Drop large redaction fields from the entry; keep `ts / command / target / success` (D-33). | off |
|
|
1043
|
+
| `--format <fmt>` | `table` \| `json`. | `table` |
|
|
1044
|
+
|
|
1045
|
+
Reader behavior (D-41): tail merges `<conn>.jsonl.1` (rotated segment, if present) and `<conn>.jsonl`, sorts by `ts` ascending, then takes the last `--n` entries — so `--n 1000` can span a fresh rotation boundary.
|
|
1046
|
+
|
|
1047
|
+
Examples:
|
|
1048
|
+
|
|
1049
|
+
dbcli audit tail --n 10
|
|
1050
|
+
dbcli audit tail --all --for-agent --n 20
|
|
1051
|
+
dbcli audit tail --format json --brief
|
|
1052
|
+
|
|
1053
|
+
#### `audit show`
|
|
1054
|
+
|
|
1055
|
+
| Flag | Purpose | Default |
|
|
1056
|
+
|---|---|---|
|
|
1057
|
+
| `<id-prefix>` | Positional. UUID or prefix ≥ 4 characters; ambiguous prefix exits 1 with disambiguation hint; prefix < 4 chars exits 1. | — |
|
|
1058
|
+
| `--recovery-ref <id>` | Find the audit entry whose `recovery_ref` field matches this id (exact, not prefix). Mutually exclusive with positional `<id-prefix>` (D-38). | — |
|
|
1059
|
+
| `--all` | Search across all connections. Output is an envelope `{ connection, entry }` (single-hit also envelope, for shape stability — D-36). | off |
|
|
1060
|
+
| `--format <fmt>` | `table` \| `json`. | `table` |
|
|
1061
|
+
|
|
1062
|
+
Examples:
|
|
1063
|
+
|
|
1064
|
+
dbcli audit show 1a2b
|
|
1065
|
+
dbcli audit show --recovery-ref 8f0e-1234-... --format json
|
|
1066
|
+
dbcli audit show 1a2b --all
|
|
1067
|
+
|
|
1068
|
+
#### `audit clear`
|
|
1069
|
+
|
|
1070
|
+
| Flag | Purpose | Default |
|
|
1071
|
+
|---|---|---|
|
|
1072
|
+
| `--yes` | Skip interactive confirmation. Required in non-TTY contexts. | off (interactive confirm) |
|
|
1073
|
+
|
|
1074
|
+
Behavior (D-45 / D-46 / D-47): deletes `<conn>.jsonl` + rotated `<conn>.jsonl.1` for the current connection. Does NOT touch other connections (`--all` is not supported — destructive op cross-connection blast-radius is too high; use `dbcli use` to switch and clear each). Does NOT reset `.dbcli/last-session-id` (D-48). In non-TTY contexts without `--yes`, exits 1 with `Cannot prompt for confirmation in non-interactive session. Use --yes to clear without prompt.`
|
|
1075
|
+
|
|
1076
|
+
Examples:
|
|
1077
|
+
|
|
1078
|
+
dbcli audit clear # interactive (TTY only)
|
|
1079
|
+
dbcli audit clear --yes # CI / scripted
|
|
1080
|
+
|
|
1081
|
+
#### `audit health`
|
|
1082
|
+
|
|
1083
|
+
| Flag | Purpose | Default |
|
|
1084
|
+
|---|---|---|
|
|
1085
|
+
| `--format <fmt>` | `table` \| `json`. | `table` |
|
|
1086
|
+
|
|
1087
|
+
Output reports: writer enabled/disabled, last write result, file-lock state, rotation cap usage (`max_bytes` / `max_entries`). When `audit.enabled = false` (D1 opt-out), `tail` / `show` / `health` still exit 0 and print `Audit is disabled (audit.enabled = false in .dbcli). Use 'dbcli audit health' for details.` (E note).
|
|
1088
|
+
|
|
1089
|
+
#### Boundaries
|
|
1090
|
+
|
|
1091
|
+
- Entries are append-only JSONL; rotation triggers at `~10 MB` or `~1000` entries (whichever first). Previous segment is preserved as `.jsonl.1`.
|
|
1092
|
+
- Bi-directional `recovery_ref` / `audit_ref` linkage is wired on every command that accepts `--recovery`: `query`, `inspect`, `insert`, `update`, `delete`, `export`, `q`, and `schema`. Use `audit tail --recovery-ref <id>` to find the audit entry an envelope was emitted alongside.
|
|
1093
|
+
- Audit writer failures are non-fatal (D6): main command result and exit code are preserved; a stderr warning is emitted. `audit health` surfaces the failure reason.
|
|
1094
|
+
- Reader truncation tolerance: a crash-truncated last line is skipped with a stderr warn `[dbcli audit] skipping truncated last line in <file>`; a mid-file non-JSON line is treated as corruption, exits 1, and points at `dbcli audit clear`.
|
|
1095
|
+
|
|
1096
|
+
#### Exit codes
|
|
1097
|
+
|
|
1098
|
+
| Code | Condition |
|
|
1099
|
+
|---|---|
|
|
1100
|
+
| 0 | Read/list/clear/health succeeded; also `audit.enabled = false` opt-out path (E note). |
|
|
1101
|
+
| 1 | `audit show` — id prefix < 4 chars, ambiguous, or not found; `--recovery-ref` not found; `<id>` and `--recovery-ref` both supplied (D-35 / D-37 / D-38). |
|
|
1102
|
+
| 1 | `audit clear` — non-TTY without `--yes` (D-46). |
|
|
1103
|
+
| 1 | Reader corruption — mid-file non-JSON line in a `.jsonl` segment. |
|
|
1104
|
+
|
|
1105
|
+
**Permission:** n/a
|
|
1106
|
+
|
|
1107
|
+
### doctor
|
|
1108
|
+
|
|
1109
|
+
Run diagnostic checks on environment, configuration, connection, and data.
|
|
1110
|
+
|
|
1111
|
+
```bash
|
|
1112
|
+
dbcli doctor # Colored text output
|
|
1113
|
+
dbcli doctor --format json # JSON output for AI agents
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
**Checks:**
|
|
1117
|
+
- Environment: Bun version, dbcli version (compares with npm registry)
|
|
1118
|
+
- Configuration: config file exists/valid, permission level, blacklist completeness (detects unprotected sensitive columns)
|
|
1119
|
+
- Connection & Data: database connectivity, schema cache freshness (warns if > 7 days), large table warnings (> 1M rows)
|
|
1120
|
+
|
|
1121
|
+
> **MongoDB SRV diagnostics:** When the active connection uses `mongodb+srv://`, `doctor` reports whether the current runtime can resolve SRV records directly or only through the DNS-over-HTTPS fallback used by dbcli. This helps spot execution-environment DNS restrictions even when Compass can connect.
|
|
1122
|
+
|
|
1123
|
+
**Exit code:** 0 if all pass or warnings only, 1 if any error
|
|
1124
|
+
**Options:** `--format <text|json>`
|
|
1125
|
+
|
|
1126
|
+
### completion
|
|
1127
|
+
|
|
1128
|
+
Generate shell completion scripts for tab auto-complete.
|
|
1129
|
+
|
|
1130
|
+
```bash
|
|
1131
|
+
dbcli completion bash # Output bash completion script
|
|
1132
|
+
dbcli completion zsh # Output zsh completion script
|
|
1133
|
+
dbcli completion fish # Output fish completion script
|
|
1134
|
+
dbcli completion --install # Auto-detect shell and install
|
|
1135
|
+
dbcli completion --install zsh # Install for specific shell
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
**Supported shells:** bash, zsh, fish
|
|
1139
|
+
|
|
1140
|
+
### upgrade
|
|
1141
|
+
|
|
1142
|
+
Check for updates and self-upgrade dbcli to the latest version from npm.
|
|
1143
|
+
|
|
1144
|
+
```bash
|
|
1145
|
+
dbcli upgrade # Check and upgrade if newer version available
|
|
1146
|
+
dbcli upgrade --check # Only check, do not upgrade
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
**Options:** `--check`
|
|
1150
|
+
|
|
1151
|
+
**Background check:** Every command silently checks the npm registry for a newer version (at most once per 24 hours, cached in `.dbcli/version-check.json`). If a newer version is found, a one-line hint is printed to stderr after the command completes. Pass `-q` / `--quiet` to suppress the hint.
|
|
1152
|
+
|
|
1153
|
+
### `dbcli shell`
|
|
1154
|
+
|
|
1155
|
+
Start an interactive database shell.
|
|
1156
|
+
|
|
1157
|
+
```bash
|
|
1158
|
+
dbcli shell # Interactive mode with SQL + dbcli commands
|
|
1159
|
+
dbcli shell --sql # SQL-only mode
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
Inside the shell:
|
|
1163
|
+
- Type SQL statements ending with `;` to execute
|
|
1164
|
+
- Type dbcli commands without the `dbcli` prefix (e.g., `schema users`)
|
|
1165
|
+
- Use Tab for auto-completion (SQL keywords, table names, column names)
|
|
1166
|
+
- Type `.help` for meta commands (.quit, .clear, .format, .history, .timing)
|
|
1167
|
+
- Multi-line SQL: keeps accumulating until `;` is found
|
|
1168
|
+
- History persists across sessions (~/.dbcli_history)
|
|
1169
|
+
|
|
1170
|
+
The REPL flavor depends on the active engine: SQL engines and MongoDB use the
|
|
1171
|
+
form above; **Redis** opens a single-line command REPL (see [Redis › Interactive
|
|
1172
|
+
shell](#interactive-shell)); **Elasticsearch** opens a Kibana Dev Tools-style
|
|
1173
|
+
REPL (v1.22, see [Elasticsearch › Interactive shell](#interactive-shell-v122)).
|
|
1174
|
+
|
|
1175
|
+
### migrate
|
|
1176
|
+
|
|
1177
|
+
Schema DDL operations. **All commands default to dry-run** — use `--execute` to actually run the SQL. Destructive operations (DROP) also require `--force`.
|
|
1178
|
+
|
|
1179
|
+
```bash
|
|
1180
|
+
# Create table
|
|
1181
|
+
dbcli migrate create posts \
|
|
1182
|
+
--column "id:serial:pk" \
|
|
1183
|
+
--column "title:varchar(200):not-null" \
|
|
1184
|
+
--column "body:text" \
|
|
1185
|
+
--column "created_at:timestamp:default=now()"
|
|
1186
|
+
|
|
1187
|
+
# Drop table (dry-run by default)
|
|
1188
|
+
dbcli migrate drop posts
|
|
1189
|
+
dbcli migrate drop posts --execute --force # Actually drop
|
|
1190
|
+
|
|
1191
|
+
# Add/drop/alter column
|
|
1192
|
+
dbcli migrate add-column users bio text --nullable
|
|
1193
|
+
dbcli migrate drop-column users temp_field --execute --force
|
|
1194
|
+
dbcli migrate alter-column users name --type "varchar(200)"
|
|
1195
|
+
dbcli migrate alter-column users email --rename user_email
|
|
1196
|
+
dbcli migrate alter-column users status --set-default "'active'"
|
|
1197
|
+
dbcli migrate alter-column users bio --drop-default
|
|
1198
|
+
dbcli migrate alter-column users bio --set-nullable
|
|
1199
|
+
dbcli migrate alter-column users email --drop-nullable
|
|
1200
|
+
|
|
1201
|
+
# Index management
|
|
1202
|
+
dbcli migrate add-index users --columns email --unique
|
|
1203
|
+
dbcli migrate add-index users --columns "last_name,first_name" --name idx_fullname
|
|
1204
|
+
dbcli migrate drop-index idx_fullname --execute --force
|
|
1205
|
+
|
|
1206
|
+
# Constraint management
|
|
1207
|
+
dbcli migrate add-constraint orders --fk user_id --references users.id --on-delete cascade
|
|
1208
|
+
dbcli migrate add-constraint users --unique email
|
|
1209
|
+
dbcli migrate add-constraint users --check "age >= 0"
|
|
1210
|
+
dbcli migrate drop-constraint orders fk_orders_user_id --execute --force
|
|
1211
|
+
|
|
1212
|
+
# Enum (PostgreSQL only — MySQL uses inline ENUM in column type)
|
|
1213
|
+
dbcli migrate add-enum status active inactive suspended
|
|
1214
|
+
dbcli migrate alter-enum status --add-value archived
|
|
1215
|
+
dbcli migrate drop-enum status --execute --force
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
**Column spec format:** `name:type[:modifier[:modifier...]]`
|
|
1219
|
+
- Modifiers: `pk`, `not-null`, `unique`, `auto-increment`, `default=<value>`, `references=<table>.<column>`
|
|
1220
|
+
- Serial types: `serial`, `bigserial`, `smallserial` (auto-expand per DB dialect)
|
|
1221
|
+
|
|
1222
|
+
**Options (all subcommands):** `--execute`, `--force`, `--config <path>`
|
|
1223
|
+
**Permission:** admin
|
|
1224
|
+
|
|
1225
|
+
**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.
|
|
1226
|
+
|
|
1227
|
+
### skill
|
|
1228
|
+
|
|
1229
|
+
Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or an
|
|
1230
|
+
AI-agent platform directory. The skill is the source of truth that lets
|
|
1231
|
+
Claude Code / Gemini / Antigravity / Copilot / Cursor know how to drive dbcli safely.
|
|
1232
|
+
|
|
1233
|
+
```bash
|
|
1234
|
+
dbcli skill # print SKILL.md to stdout
|
|
1235
|
+
dbcli skill --output ./SKILL.md # write to a file (no platform install)
|
|
1236
|
+
dbcli skill --install claude # install to ~/.claude/skills/dbcli/
|
|
1237
|
+
dbcli skill --install gemini # install to ~/.gemini/skills/dbcli/ (being phased out)
|
|
1238
|
+
dbcli skill --install antigravity # install to ~/.gemini/antigravity-cli/skills/dbcli/
|
|
1239
|
+
dbcli skill --install copilot # install to .github/skills/dbcli/ (repo-local)
|
|
1240
|
+
dbcli skill --install cursor # install to .cursor/skills/dbcli/ (repo-local)
|
|
1241
|
+
dbcli skill --install codex # install to ~/.codex/skills/dbcli/
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
**Options:**
|
|
1245
|
+
- `--install <platform>` — `claude` | `gemini` | `antigravity` | `copilot` | `cursor` | `codex` | `windsurf`. Writes `SKILL.md` plus `reference.md` next to it so the agent gets progressive disclosure.
|
|
1246
|
+
- `--output <path>` — write `SKILL.md` to a file instead of stdout. Does not install `reference.md`.
|
|
1247
|
+
|
|
1248
|
+
**Notes:**
|
|
1249
|
+
- 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.
|
|
1250
|
+
- `claude` / `gemini` / `antigravity` install paths are user-global; `copilot` / `cursor` are repo-local under `.github/` / `.cursor/`.
|
|
1251
|
+
- Cursor can install through `/add-plugin dbcli-agent` when available in Cursor's plugin marketplace; this repo includes `.cursor-plugin/plugin.json`. Instruction-file fallback options remain documented in `plugins/dbcli-agent/INSTALL.md#cursor`.
|
|
1252
|
+
- Codex can consume the repo through the Ponytail-style marketplace layout at `.agents/plugins/marketplace.json` and `.codex-plugin/plugin.json`; plugin installs provide the skill from `skills/dbcli/`, and the skill falls back to `bunx @carllee1983/dbcli` when `dbcli` is not on `PATH`.
|
|
1253
|
+
- Agent plugin installation details live in `plugins/dbcli-agent/INSTALL.md`, including Codex, Claude Code, GitHub Copilot CLI, Antigravity (`agy`), and Cursor targets.
|
|
1254
|
+
- `gemini` (Gemini CLI) is retained for now but is being phased out in favour of `antigravity` (Antigravity CLI), Google's successor terminal agent.
|
|
1255
|
+
- Re-running `--install` overwrites the existing skill atomically; no prompt.
|
|
1256
|
+
|
|
1257
|
+
**Permission:** n/a.
|
|
1258
|
+
|
|
1259
|
+
### skill tasks (Agent Task Packs)
|
|
1260
|
+
|
|
1261
|
+
```bash
|
|
1262
|
+
dbcli skill tasks list # human table
|
|
1263
|
+
dbcli skill tasks list --format json --tag diagnostics
|
|
1264
|
+
dbcli skill tasks list --engine postgres --source builtin
|
|
1265
|
+
dbcli skill tasks show diagnose-slow-query
|
|
1266
|
+
dbcli skill tasks show diagnose-slow-query --format json
|
|
1267
|
+
dbcli skill tasks plan diagnose-slow-query --param query="SELECT 1"
|
|
1268
|
+
dbcli skill tasks plan diagnose-slow-query --param query="..." --format json
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
- **list filters:** `--tag <tag>`, `--engine <postgres|mysql|mongodb|redis|elasticsearch>`, `--source <builtin|shared|local>`, `--format <table|json>`.
|
|
1272
|
+
- **show:** prints the full task definition (frontmatter + Agent Notes). Use `--format json` for an agent-friendly contract.
|
|
1273
|
+
- **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.
|
|
1274
|
+
|
|
1275
|
+
**Builtin packs:** `diagnose-slow-query` and **(v1.23)** `analyze-table-perf` —
|
|
1276
|
+
a read-only (`plan-only`) pack taking a required `table` parameter that walks
|
|
1277
|
+
`blacklist list` → `schema <table> --format json` → `guide index-usage --format json`.
|
|
1278
|
+
`dbcli inspect` suggests `analyze-table-perf` automatically for the hottest table
|
|
1279
|
+
in recent audit activity. Additional read-only packs ship for common agent
|
|
1280
|
+
workflows: `audit-permissions` (permission/blacklist audit), `safe-backfill`
|
|
1281
|
+
(plan a write with blacklist+schema+risk checks), `schema-drift-review` (cached
|
|
1282
|
+
vs live schema diff), and `connection-health` (reachability/config/capacity
|
|
1283
|
+
triage). Run `dbcli skill tasks list` for the full set.
|
|
1284
|
+
|
|
1285
|
+
```bash
|
|
1286
|
+
dbcli skill tasks plan analyze-table-perf --param table=betting_logs --format json
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
Task storage layers:
|
|
1290
|
+
|
|
1291
|
+
| Source | Path | Notes |
|
|
1292
|
+
| --- | --- | --- |
|
|
1293
|
+
| builtin | `assets/tasks/` | shipped with dbcli |
|
|
1294
|
+
| shared | `.dbcli-shared/tasks/` | team-managed, version-controlled |
|
|
1295
|
+
| local | `.dbcli/tasks/` | personal, gitignored |
|
|
1296
|
+
|
|
1297
|
+
Higher tiers override lower tiers by task name. Task name is derived from the
|
|
1298
|
+
file path under the tier root (e.g. `diag/inspect.md` → `diag/inspect`).
|
|
1299
|
+
|
|
1300
|
+
## Recovery Cookbook (agent walkthroughs)
|
|
1301
|
+
|
|
1302
|
+
End-to-end recovery sessions for the most common failure codes. All examples
|
|
1303
|
+
assume the agent invoked a `--recovery`-capable command and received a
|
|
1304
|
+
`RecoveryEnvelope` (or hit the same envelope via `dbcli recovery --code <CODE>`
|
|
1305
|
+
lookup). See [§recovery](#recovery) for the envelope shape, [§recover](#recover)
|
|
1306
|
+
for `--apply` / `--next` / risk-gate semantics, and [§audit](#audit) for the
|
|
1307
|
+
bi-directional `audit_ref` ⇄ `recovery_ref` pivot.
|
|
1308
|
+
|
|
1309
|
+
### Scenario index
|
|
1310
|
+
|
|
1311
|
+
| Code | Trigger | Primary remediation | Risk tier |
|
|
1312
|
+
|------|---------|---------------------|-----------|
|
|
1313
|
+
| `CONN_REFUSED` | Database process down or wrong host/port. | `dbcli doctor` → fix host/port → retry. | `readonly` |
|
|
1314
|
+
| `CONN_AUTH_FAILED` | Credentials rejected. | Re-check `.dbcli`/env, rotate credentials, `dbcli init --force` only on explicit user nod. | `readonly` → `interactive` |
|
|
1315
|
+
| `PERMISSION_DENIED` | Active permission level forbids the verb. | `dbcli inspect` to confirm level → escalate via `dbcli init` (human) or run a `--dry-run` instead. | `readonly` + `dry-run` |
|
|
1316
|
+
| `BLACKLIST_TABLE` | Target table is blacklisted. | `dbcli blacklist list` → `blacklist table remove <name>` (local-write tier). | `readonly` + `local-write` |
|
|
1317
|
+
| `BLACKLIST_COLUMN_WRITE` | INSERT/UPDATE touches a blacklisted column. | Re-shape payload to drop the column, or `blacklist column remove`. Envelope prepends a `--dry-run` preview step. | `dry-run` + `local-write` |
|
|
1318
|
+
| `SCHEMA_CACHE_MISSING` | Fresh checkout / new v2 connection / cache wiped. | `dbcli schema --refresh --force` (or `--use <conn>` per-connection). | `readonly` |
|
|
1319
|
+
| `SNIPPET_NOT_FOUND` / `SNIPPET_AMBIGUOUS` | Typo or duplicate snippet name. | `dbcli queries list` → `queries search <kw>` → run correct `@name`. | `readonly` |
|
|
1320
|
+
| `SNIPPET_PARAM_MISSING` | `--param k=v` not supplied. | `dbcli queries show @name` lists required params → re-run with full set. | `readonly` |
|
|
1321
|
+
| `CONFIG_MISSING` | No `.dbcli` in cwd. | `dbcli init` (human-driven). | `interactive` |
|
|
1322
|
+
|
|
1323
|
+
`risk` enum: `readonly` / `dry-run` / `write` / `unknown` (see §recovery boundaries).
|
|
1324
|
+
Allowlist tier: `readonly` / `dry-run` / `local-write` / `db-write` / `interactive` (see [§recover Risk gate matrix](#risk-gate-matrix)).
|
|
1325
|
+
|
|
1326
|
+
### S1 — CONN_REFUSED end-to-end
|
|
1327
|
+
|
|
1328
|
+
```bash
|
|
1329
|
+
# 1. Failing call writes envelope to stdout AND .dbcli/last-recovery.json
|
|
1330
|
+
$ dbcli query "SELECT 1" --recovery --format json
|
|
1331
|
+
{
|
|
1332
|
+
"schemaVersion": 1,
|
|
1333
|
+
"error": { "code": "CONN_REFUSED", "message": "..." },
|
|
1334
|
+
"audit_ref": "1f8e...c4d2",
|
|
1335
|
+
"recovery": [
|
|
1336
|
+
{ "order": 1, "command": "dbcli doctor --format json", "risk": "readonly", ... },
|
|
1337
|
+
{ "order": 2, "command": "dbcli inspect --for-agent", "risk": "readonly", ... }
|
|
1338
|
+
],
|
|
1339
|
+
"verify": { "command": "dbcli doctor --format json", "risk": "readonly", ... }
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
# 2. One-shot apply (only readonly + dry-run run by default)
|
|
1343
|
+
$ dbcli recover --apply --format json
|
|
1344
|
+
{ "finalStatus": "ok", "executed": [...], "verifyStatus": "passed" }
|
|
1345
|
+
# Exit 0 → root cause cleared (verify probe succeeded).
|
|
1346
|
+
|
|
1347
|
+
# 3. If verify reported `failed` / `indeterminate`, drop into --next for control
|
|
1348
|
+
$ dbcli recover --next --after-step 1 --result '{"status":"failed","exitCode":1}'
|
|
1349
|
+
# → returns a refined step 2 or `kind:"done"` based on the prevResult
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
### S2 — PERMISSION_DENIED with implicit `--dry-run` preview
|
|
1353
|
+
|
|
1354
|
+
```bash
|
|
1355
|
+
$ dbcli update orders --where "id=1" --set '{"status":"shipped"}' --recovery --format json
|
|
1356
|
+
{
|
|
1357
|
+
"error": { "code": "PERMISSION_DENIED", ... },
|
|
1358
|
+
"audit_ref": "9ab0...e711",
|
|
1359
|
+
"recovery": [
|
|
1360
|
+
{ "order": 1, "command": "dbcli update orders --where 'id=1' --set '<redacted>' --dry-run", "risk": "dry-run" },
|
|
1361
|
+
{ "order": 2, "command": "dbcli inspect --for-agent", "risk": "readonly" }
|
|
1362
|
+
]
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
# Default apply runs both steps (dry-run is in-tier).
|
|
1366
|
+
$ dbcli recover --apply
|
|
1367
|
+
```
|
|
1368
|
+
|
|
1369
|
+
When the failing operation is INSERT/UPDATE/DELETE, the envelope prepends a
|
|
1370
|
+
`risk: 'dry-run'` step (the same write subcommand with `--dry-run`). Run it
|
|
1371
|
+
before any escalation — it both teaches the agent what the SQL looks like and
|
|
1372
|
+
proves the change is well-formed before raising the permission tier.
|
|
1373
|
+
|
|
1374
|
+
### S3 — BLACKLIST_TABLE (local-write remediation)
|
|
1375
|
+
|
|
1376
|
+
```bash
|
|
1377
|
+
$ dbcli query "SELECT * FROM audit_logs" --recovery --format json
|
|
1378
|
+
# error.code: BLACKLIST_TABLE
|
|
1379
|
+
# recovery[0]: dbcli blacklist list (risk: readonly)
|
|
1380
|
+
# recovery[1]: dbcli blacklist table remove audit_logs (risk: write — local-write tier)
|
|
1381
|
+
|
|
1382
|
+
# Default --apply: step 1 runs, step 2 skipped:risk → exit 3.
|
|
1383
|
+
$ dbcli recover --apply
|
|
1384
|
+
# To proceed: open the gate to local-write tier ONLY (does not touch DB).
|
|
1385
|
+
$ dbcli recover --apply --allow-write=readonly-cmd
|
|
1386
|
+
{ "finalStatus": "ok", "executed": [step1, step2], "verifyStatus": "passed" }
|
|
1387
|
+
```
|
|
1388
|
+
|
|
1389
|
+
### S4 — BLACKLIST_COLUMN_WRITE (preview-then-drop)
|
|
1390
|
+
|
|
1391
|
+
```bash
|
|
1392
|
+
$ dbcli insert users --data '{"name":"a","ssn":"123"}' --recovery
|
|
1393
|
+
# recovery[0]: dbcli insert users --data '<redacted>' --dry-run (risk: dry-run)
|
|
1394
|
+
# recovery[1]: dbcli blacklist list (risk: readonly)
|
|
1395
|
+
# recovery[2]: dbcli blacklist column remove users.ssn (risk: write — local-write)
|
|
1396
|
+
|
|
1397
|
+
# Preferred path: don't widen the blacklist — re-shape the agent's payload to drop ssn.
|
|
1398
|
+
# Apply only the diagnostic prefix (steps 1+2) to confirm what columns are masked:
|
|
1399
|
+
$ dbcli recover --apply
|
|
1400
|
+
# Then re-issue insert without `ssn`.
|
|
1401
|
+
```
|
|
1402
|
+
|
|
1403
|
+
### S5 — SCHEMA_CACHE_MISSING (fresh / multi-conn)
|
|
1404
|
+
|
|
1405
|
+
```bash
|
|
1406
|
+
$ dbcli inspect --require-schema-cache --recovery --format json
|
|
1407
|
+
# error.code: SCHEMA_CACHE_MISSING
|
|
1408
|
+
# recovery[0]: dbcli schema --refresh --force (risk: readonly — populates .dbcli/schemas/)
|
|
1409
|
+
# verify: dbcli inspect --format json (schemaCache.available === true)
|
|
1410
|
+
|
|
1411
|
+
$ dbcli recover --apply
|
|
1412
|
+
# Per-connection cache lives at .dbcli/schemas/<connection>/. If the failure was on
|
|
1413
|
+
# a v2 named connection, the envelope's command already carries `--use <name>`.
|
|
1414
|
+
```
|
|
1415
|
+
|
|
1416
|
+
### S6 — SNIPPET_NOT_FOUND with disambiguation
|
|
1417
|
+
|
|
1418
|
+
```bash
|
|
1419
|
+
$ dbcli q @anaytics/revenue --recovery
|
|
1420
|
+
# typo: anaytics → analytics
|
|
1421
|
+
# recovery[0]: dbcli queries list --format json
|
|
1422
|
+
# recovery[1]: dbcli queries search analytics (or whatever --hint suggests)
|
|
1423
|
+
$ dbcli recover --apply
|
|
1424
|
+
# Agent reads stdoutSummary, identifies the correct @name, then re-issues:
|
|
1425
|
+
$ dbcli q @analytics/revenue --param days=30
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
### Multi-turn `--next` walkthrough (3-step plan)
|
|
1429
|
+
|
|
1430
|
+
Use `--next` instead of `--apply` when:
|
|
1431
|
+
|
|
1432
|
+
- `--apply` is too coarse-grained (the agent wants step-by-step inspection).
|
|
1433
|
+
- The plan contains an `interactive` step that `--apply` would skip.
|
|
1434
|
+
- The agent uses its own runner / sandbox and just wants dbcli to drive cursoring.
|
|
1435
|
+
|
|
1436
|
+
`--next` returns one step at a time, given which step the agent **just executed**
|
|
1437
|
+
and a `StepResultSummary` of how it went. dbcli does not persist the cursor —
|
|
1438
|
+
the agent owns `--after-step`.
|
|
1439
|
+
|
|
1440
|
+
```bash
|
|
1441
|
+
# Envelope already saved at .dbcli/last-recovery.json (3-step plan, CONN_REFUSED).
|
|
1442
|
+
|
|
1443
|
+
# Round 1 — agent reads step 1 from the envelope, executes it itself, then asks
|
|
1444
|
+
# dbcli for the next step.
|
|
1445
|
+
$ dbcli recover --next --after-step 1 --result '{"status":"ok","exitCode":0}' --format json
|
|
1446
|
+
{
|
|
1447
|
+
"schemaVersion": 1,
|
|
1448
|
+
"kind": "step",
|
|
1449
|
+
"errorCode": "CONN_REFUSED",
|
|
1450
|
+
"cursor": 2,
|
|
1451
|
+
"totalSteps": 3,
|
|
1452
|
+
"step": { "order": 2, "command": "dbcli inspect --for-agent", "risk": "readonly", ... }
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
# Round 2 — bigger stdout, save to file and reference it.
|
|
1456
|
+
$ ./run-step.sh > /tmp/r2.json # agent's own runner; result is StepResultSummary JSON
|
|
1457
|
+
$ dbcli recover --next --after-step 2 --result @/tmp/r2.json
|
|
1458
|
+
{ "kind": "step", "cursor": 3, "step": { "order": 3, ... } }
|
|
1459
|
+
|
|
1460
|
+
# Round 3 — last step done.
|
|
1461
|
+
$ dbcli recover --next --after-step 3 --result '{"status":"ok"}'
|
|
1462
|
+
{ "kind": "done", "cursor": 3, "totalSteps": 3 }
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
`StepResultSummary` contract (recap of [§recover Multi-turn](#multi-turn---next-p2)):
|
|
1466
|
+
|
|
1467
|
+
```ts
|
|
1468
|
+
interface StepResultSummary {
|
|
1469
|
+
status: 'ok' | 'failed' | 'skipped'
|
|
1470
|
+
exitCode?: number
|
|
1471
|
+
stdoutSummary?: string // last 4 KB
|
|
1472
|
+
stderrSummary?: string // last 4 KB
|
|
1473
|
+
}
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
Truncate to the **last** 4 KB before passing — the head of a huge stdout is
|
|
1477
|
+
usually not what disambiguates next steps.
|
|
1478
|
+
|
|
1479
|
+
Verification is **not** automatic under `--next`. If the agent wants the same
|
|
1480
|
+
verify probe `--apply` runs, it must execute the envelope's `verify` step
|
|
1481
|
+
itself after the plan completes.
|
|
1482
|
+
|
|
1483
|
+
### Bi-directional pivot (envelope ⇄ audit)
|
|
1484
|
+
|
|
1485
|
+
Every `--recovery`-capable failure (`query`, `inspect`, `insert`, `update`,
|
|
1486
|
+
`delete`, `export`, `q`, `schema`) writes **both** sides of a UUID link:
|
|
1487
|
+
|
|
1488
|
+
- `RecoveryEnvelope.audit_ref` → the `audit.id` for the same failure.
|
|
1489
|
+
- `AuditEntry.recovery_ref` → the envelope's id (also the auto-saved
|
|
1490
|
+
`.dbcli/last-recovery.json` filename trace).
|
|
1491
|
+
|
|
1492
|
+
```bash
|
|
1493
|
+
# From envelope → audit (forensics on a saved failure)
|
|
1494
|
+
$ ENV_ID=$(jq -r '.id' .dbcli/last-recovery.json) # or read from stdout
|
|
1495
|
+
$ dbcli audit show --recovery-ref "$ENV_ID" --format json
|
|
1496
|
+
# Returns the matching audit entry (full, not brief).
|
|
1497
|
+
|
|
1498
|
+
# From audit → envelope (you have an audit hit, want the structured plan)
|
|
1499
|
+
$ AUDIT_ID=$(dbcli audit tail --for-agent --n 1 | jq -r '.[0].id')
|
|
1500
|
+
$ dbcli audit show "$AUDIT_ID" --format json
|
|
1501
|
+
# Read `recovery_ref` from the entry, then either re-run --recovery against
|
|
1502
|
+
# the original command or load the saved envelope:
|
|
1503
|
+
$ jq '.recovery_ref' .dbcli/last-recovery.json | grep -q "$RECOVERY_REF" \
|
|
1504
|
+
&& dbcli recover --format markdown # human inspect
|
|
1505
|
+
|| dbcli recover --from /path/to/archived.json --format markdown
|
|
1506
|
+
```
|
|
1507
|
+
|
|
1508
|
+
Session handoff: a fresh agent that opens `dbcli inspect --for-agent`,
|
|
1509
|
+
`dbcli guide`, `dbcli recover`, or `dbcli recover --apply` gets an
|
|
1510
|
+
`audit_recent: AuditEntryBrief[]` field (last 5 entries) embedded in the JSON
|
|
1511
|
+
output — no extra round-trip to the audit CLI needed for immediate history
|
|
1512
|
+
context.
|
|
1513
|
+
|
|
1514
|
+
### Risk gate cheat sheet
|
|
1515
|
+
|
|
1516
|
+
Quick reference for what `--apply` runs at each `--allow-write` level. The
|
|
1517
|
+
canonical matrix lives at [§recover Risk gate matrix](#risk-gate-matrix); this
|
|
1518
|
+
table maps it onto common agent intents.
|
|
1519
|
+
|
|
1520
|
+
| Agent intent | Recommended flag | What runs | What's skipped |
|
|
1521
|
+
|---|---|---|---|
|
|
1522
|
+
| Probe-only (read state, learn) | `--apply` (default) | `readonly` + `dry-run` steps | `local-write`, `db-write`, `interactive` |
|
|
1523
|
+
| Local config remediation (e.g. `blacklist remove`) | `--apply --allow-write=readonly-cmd` | + `local-write` | `db-write`, `interactive` |
|
|
1524
|
+
| Database write recovery (rare; trusted plan) | `--apply --allow-write=write-cmd` | + `db-write` | `interactive` |
|
|
1525
|
+
| Interactive step (e.g. `dbcli init`) | Drive manually OR use `--next` | n/a | All interactive steps always skip under `--apply` |
|
|
1526
|
+
| Walk plan step-by-step with own runner | `--next --after-step N --result …` | one step per call | n/a — agent owns cursor + execution |
|
|
1527
|
+
|
|
1528
|
+
Three rules that always apply regardless of `--allow-write`:
|
|
1529
|
+
|
|
1530
|
+
1. **Tier is code-owned, not envelope-claimed.** The risk gate reads the
|
|
1531
|
+
per-error-code allowlist after parsing argv. An envelope cannot escalate
|
|
1532
|
+
itself by setting `risk: 'readonly'` on a write subcommand — argv decides.
|
|
1533
|
+
2. **Placeholders block.** A step with unresolved `<token>` placeholders is
|
|
1534
|
+
skipped as `skipped:placeholder` even at `--allow-write=write-cmd`. Bind
|
|
1535
|
+
them at `recovery` lookup time with `--hint` / `--snippet` / `--table`, or
|
|
1536
|
+
ask the user.
|
|
1537
|
+
3. **Verify is signal, not gate.** `verifyStatus` ∈ `{passed, failed,
|
|
1538
|
+
indeterminate}` reports whether the original failure looks resolved.
|
|
1539
|
+
`recover --apply` exit code is set by step execution, not verification.
|
|
1540
|
+
|
|
1541
|
+
### Common pitfalls
|
|
1542
|
+
|
|
1543
|
+
- **Stale `.dbcli/last-recovery.json`.** `recover` (no `--apply`) shows the
|
|
1544
|
+
*saved* plan, which may be hours old. Re-run the original command with
|
|
1545
|
+
`--recovery` to refresh it, or pass `--from <file>` to load an archived one.
|
|
1546
|
+
- **`.dbcli/` is gitignored.** Do not check `last-recovery.json` into a repo
|
|
1547
|
+
for "reproducibility"; it contains sanitized command snapshots but the
|
|
1548
|
+
workspace `cwd` only makes sense locally. Use `recover --from <archived.json>`
|
|
1549
|
+
for cross-machine replay.
|
|
1550
|
+
- **`--apply` exit 3 means every step skipped.** Not a failure — it means the
|
|
1551
|
+
default gate was too tight. Either widen with `--allow-write`, fill
|
|
1552
|
+
placeholders, or fall back to `--next` and drive steps manually.
|
|
1553
|
+
- **`--next` does not run verify.** Re-run the original failing command with
|
|
1554
|
+
`--recovery` once the plan is done; if it now succeeds (no envelope on
|
|
1555
|
+
stdout), recovery is complete. Or invoke `envelope.verify.command` yourself.
|
|
1556
|
+
- **Audit writer failures are non-fatal.** If `audit health` reports
|
|
1557
|
+
`lastWriteOk: false`, the main command still completed — but `recovery_ref`
|
|
1558
|
+
⇄ `audit_ref` linkage is broken for that one call. `audit health` surfaces
|
|
1559
|
+
the underlying error (disk full, EACCES, etc.).
|
|
1560
|
+
- **Cross-connection forensics.** `audit tail --all --for-agent` merges all
|
|
1561
|
+
connections; `audit show <id-prefix> --all` returns an envelope `{connection,
|
|
1562
|
+
entry}` so a fresh agent can tell which DB the failure was against.
|
|
1563
|
+
|
|
1564
|
+
## Interactive HTML dashboard
|
|
1565
|
+
|
|
1566
|
+
`query`, `q`, and `export` can render results as a single, fully self-contained
|
|
1567
|
+
HTML file backed by a bundled React + Recharts template. The template lives at
|
|
1568
|
+
`assets/ui-template.html` and is installed alongside the binary; no external
|
|
1569
|
+
network, CDN, or runtime is required to view the report.
|
|
1570
|
+
|
|
1571
|
+
### Entry points
|
|
1572
|
+
|
|
1573
|
+
| Command form | Behaviour |
|
|
1574
|
+
|--------------|-----------|
|
|
1575
|
+
| `dbcli query "<sql>" --ui` | Render to a temp file under `$TMPDIR/dbcli-query-<ts>.html` and open with `open` / `xdg-open` / `start`. |
|
|
1576
|
+
| `dbcli q @<name> --ui` | Same, with snippet metadata (`name`, `description`, `visual:` block). |
|
|
1577
|
+
| `dbcli query "<sql>" --format html` | Print HTML to stdout (pipe, redirect, attach). |
|
|
1578
|
+
| `dbcli q @<name> --format html` | Same, snippet-aware. |
|
|
1579
|
+
| `dbcli export "<sql>" --format html --output report.html` | Write HTML to an explicit path; respects `--force` / overwrite confirmation. |
|
|
1580
|
+
|
|
1581
|
+
`--ui` is a convenience flag — it implies `--format html` and then opens the
|
|
1582
|
+
file. `--ui` and `--format` are mutually compatible; passing both is allowed and
|
|
1583
|
+
behaves as `--ui`.
|
|
1584
|
+
|
|
1585
|
+
### Data injection contract
|
|
1586
|
+
|
|
1587
|
+
The template ships with a single placeholder, `/*DBCLI_PAYLOAD*/`, which dbcli
|
|
1588
|
+
replaces with:
|
|
1589
|
+
|
|
1590
|
+
```js
|
|
1591
|
+
window.__DBCLI_PAYLOAD__ = { "meta": {...}, "rows": [...] };
|
|
1592
|
+
```
|
|
1593
|
+
|
|
1594
|
+
Hardening rules applied before injection:
|
|
1595
|
+
|
|
1596
|
+
- Payload is `JSON.stringify(...)`-encoded.
|
|
1597
|
+
- Every `<` is replaced with `<` so a malicious row containing `</script>`
|
|
1598
|
+
cannot terminate the inline script tag.
|
|
1599
|
+
- Blacklist redaction (`dbcli blacklist`) runs **before** the formatter — masked
|
|
1600
|
+
columns never reach the dashboard.
|
|
1601
|
+
- The replacement uses a function callback (`html.replace(..., () => injection)`)
|
|
1602
|
+
so `$&`-style backreferences in the payload are not interpreted.
|
|
1603
|
+
|
|
1604
|
+
### `meta` shape
|
|
1605
|
+
|
|
1606
|
+
`meta` is the `SavedQueryMeta` object (see `dbcli queries show @<name> --format json`):
|
|
1607
|
+
|
|
1608
|
+
```jsonc
|
|
1609
|
+
{
|
|
1610
|
+
"name": "Revenue Trend", // display title
|
|
1611
|
+
"key": "@analytics/revenue", // snippet key, or "raw-sql" / "export"
|
|
1612
|
+
"description": "...", // free text (SQL preview for raw query)
|
|
1613
|
+
"params": [...], // ParamSpec[]
|
|
1614
|
+
"tags": ["analytics"],
|
|
1615
|
+
"intent": "perf.slow-query",
|
|
1616
|
+
"visual": { ... } // optional, see below
|
|
1617
|
+
}
|
|
1618
|
+
```
|
|
1619
|
+
|
|
1620
|
+
For raw `query` / `export` invocations, `meta.params` is `[]` and
|
|
1621
|
+
`meta.visual` is absent — the dashboard renders a sortable / filterable table.
|
|
1622
|
+
|
|
1623
|
+
### `visual:` block (snippet frontmatter)
|
|
1624
|
+
|
|
1625
|
+
```yaml
|
|
1626
|
+
visual:
|
|
1627
|
+
title: Revenue (last :days days) # optional override of meta.name
|
|
1628
|
+
kpis:
|
|
1629
|
+
- label: Total Revenue
|
|
1630
|
+
value_column: total_revenue # must exist in result rows
|
|
1631
|
+
format: currency # currency | number | percent (optional)
|
|
1632
|
+
- label: Orders
|
|
1633
|
+
value_column: order_count
|
|
1634
|
+
format: number
|
|
1635
|
+
charts:
|
|
1636
|
+
- type: line # line | bar | area | pie | scatter
|
|
1637
|
+
title: Daily Revenue
|
|
1638
|
+
x: day # column for X axis
|
|
1639
|
+
y: [revenue] # 1..N columns for series
|
|
1640
|
+
- type: bar
|
|
1641
|
+
title: By Channel
|
|
1642
|
+
x: channel
|
|
1643
|
+
y: [revenue, refunds]
|
|
1644
|
+
```
|
|
1645
|
+
|
|
1646
|
+
Parser behaviour (`src/core/saved-queries/parser.ts::normaliseVisual`):
|
|
1647
|
+
|
|
1648
|
+
- The block is **optional**. Missing → table-only render.
|
|
1649
|
+
- Items missing required fields (`kpi.label` + `kpi.value_column`, or
|
|
1650
|
+
`chart.type` + `chart.x` + `chart.y[]`) are silently dropped.
|
|
1651
|
+
- Unknown `format` / `type` values are forwarded as strings; the dashboard
|
|
1652
|
+
decides how to render them (unknown chart types fall back gracefully).
|
|
1653
|
+
- The snippet still executes as a normal SQL/DSL query — `visual:` only affects
|
|
1654
|
+
the HTML renderer.
|
|
1655
|
+
|
|
1656
|
+
### Limitations
|
|
1657
|
+
|
|
1658
|
+
- The dashboard is read-only; there is no in-page editor or re-run button.
|
|
1659
|
+
- Raw `query` / `export` HTML output never shows KPIs or charts (no snippet
|
|
1660
|
+
metadata is available). Use `dbcli q @<name>` for the charted view.
|
|
1661
|
+
- Engine support follows the underlying command: SQL, MongoDB (`--collection`),
|
|
1662
|
+
Redis, and Elasticsearch (`--collection`) all render through the same template.
|
|
1663
|
+
- Very wide / very long result sets render as a single client-side table; for
|
|
1664
|
+
>10k rows prefer `--format csv` / `--format jsonl` and a downstream tool.
|
|
1665
|
+
|
|
1666
|
+
## MongoDB Support
|
|
1667
|
+
|
|
1668
|
+
MongoDB connections use a JSON-based query model instead of SQL. Treat MongoDB support as a narrower document-database path, not as a full SQL feature equivalent.
|
|
1669
|
+
|
|
1670
|
+
Atlas-style `mongodb+srv://` URIs are supported. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
|
|
1671
|
+
|
|
1672
|
+
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `q`, `insert`, `update`, `delete`, `export`, `status`, `shell`, `doctor`, `upgrade`, `completion`
|
|
1673
|
+
|
|
1674
|
+
**Limited support:**
|
|
1675
|
+
|
|
1676
|
+
- `schema` samples collection documents to infer field names/types. It does not provide relational constraints, primary keys, foreign keys, or reliable index metadata.
|
|
1677
|
+
- `query` accepts only JSON object filters or aggregation pipeline arrays and always requires `--collection <name>`.
|
|
1678
|
+
- `q` saved-query execution accepts JSON `find` / `aggregate` bodies, requires a `collection` frontmatter field (CLI `--collection` overrides), JSON-encodes every `{{param}}` substitution, and enforces table-level blacklist plus document field masking before rendering.
|
|
1679
|
+
- `insert` inserts one JSON document into the named collection.
|
|
1680
|
+
- `update` accepts a JSON filter in `--where` or simple `key=value` conditions. If `--set` does not use MongoDB update operators such as `$set`, dbcli wraps it in `$set`.
|
|
1681
|
+
- `delete` deletes all documents matching the JSON/simple filter.
|
|
1682
|
+
- `export` accepts the same JSON filter / aggregation syntax as `query`.
|
|
1683
|
+
- MongoDB write paths do not currently provide the same SQL dry-run, relational schema validation, or column-level blacklist filtering guarantees as SQL writes.
|
|
1684
|
+
- `shell` blocks raw SQL for MongoDB; use `query <json> --collection <name>` inside the shell.
|
|
1685
|
+
|
|
1686
|
+
**Not supported (exit with error):** `diff`, `migrate`
|
|
1687
|
+
|
|
1688
|
+
**Not a supported MongoDB target:** `check` is designed for relational health checks and emits SQL-style checks.
|
|
1689
|
+
|
|
1690
|
+
### MongoDB-specific workflow
|
|
1691
|
+
|
|
1692
|
+
```bash
|
|
1693
|
+
# 1. Initialize (URI or individual params)
|
|
1694
|
+
dbcli init --system mongodb --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
|
|
1695
|
+
|
|
1696
|
+
# 2. List collections
|
|
1697
|
+
dbcli list --format json
|
|
1698
|
+
|
|
1699
|
+
# 3. Query with JSON filter (find) or pipeline (aggregate)
|
|
1700
|
+
dbcli query '{}' --collection orders --format json # All documents
|
|
1701
|
+
dbcli query '{"status": "paid"}' --collection orders # Filter
|
|
1702
|
+
dbcli query '[{"$match": {"status":"paid"}}, {"$count":"total"}]' --collection orders # Pipeline
|
|
1703
|
+
|
|
1704
|
+
# 4. Document writes (permission-gated; no SQL dry-run semantics)
|
|
1705
|
+
dbcli insert orders --data '{"status":"paid","total":42}'
|
|
1706
|
+
dbcli update orders --where '{"status":"pending"}' --set '{"status":"paid"}'
|
|
1707
|
+
dbcli delete orders --where '{"status":"cancelled"}' --force
|
|
1708
|
+
```
|
|
1709
|
+
|
|
1710
|
+
### Query syntax
|
|
1711
|
+
|
|
1712
|
+
| Intent | Syntax |
|
|
1713
|
+
|--------|--------|
|
|
1714
|
+
| All documents | `'{}'` |
|
|
1715
|
+
| Field filter | `'{"field": "value"}'` |
|
|
1716
|
+
| Comparison | `'{"age": {"$gt": 18}}'` |
|
|
1717
|
+
| Aggregation | `'[{"$match": {...}}, {"$group": {...}}]'` |
|
|
1718
|
+
|
|
1719
|
+
## Redis Support
|
|
1720
|
+
|
|
1721
|
+
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.
|
|
1722
|
+
|
|
1723
|
+
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `shell`, `status`, `doctor`, `upgrade`, `completion`
|
|
1724
|
+
|
|
1725
|
+
**Saved queries:** `q` is supported for read-only Redis commands (see "Redis snippets" below).
|
|
1726
|
+
|
|
1727
|
+
**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.
|
|
1728
|
+
|
|
1729
|
+
### Connection and configuration
|
|
1730
|
+
|
|
1731
|
+
- Required fields: `system: redis`, `host`, `port`. `password` and `database` are optional.
|
|
1732
|
+
- `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.
|
|
1733
|
+
- `connection.timeout` (ms, default 5000) maps to the client's `connectionTimeout`.
|
|
1734
|
+
|
|
1735
|
+
### Permission classification
|
|
1736
|
+
|
|
1737
|
+
Permission is derived from the command's first token (case-insensitive). Unknown commands are denied even at `admin` tier — they must be added to the allow-list.
|
|
1738
|
+
|
|
1739
|
+
| Tier | Commands |
|
|
1740
|
+
|------|----------|
|
|
1741
|
+
| `query-only` | `GET`, `MGET`, `STRLEN`, `EXISTS`, `TTL`, `PTTL`, `TYPE`, `SCAN`, `HGET`, `HGETALL`, `HKEYS`, `HVALS`, `HLEN`, `HEXISTS`, `HMGET`, `LRANGE`, `LLEN`, `LINDEX`, `SMEMBERS`, `SCARD`, `SISMEMBER`, `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZCARD`, `ZSCORE`, `PING`, `ECHO` |
|
|
1742
|
+
| `read-write` | `SET`, `SETEX`, `SETNX`, `PSETEX`, `MSET`, `MSETNX`, `APPEND`, `INCR`/`INCRBY`, `DECR`/`DECRBY`, `HSET`/`HSETNX`/`HMSET`/`HINCRBY`, `LPUSH`/`RPUSH`/`LPOP`/`RPOP`/`LSET`, `SADD`/`SREM`, `ZADD`/`ZREM`, `EXPIRE`/`EXPIREAT`/`PEXPIRE`/`PERSIST`, `RENAME` |
|
|
1743
|
+
| `data-admin` | `DEL`, `UNLINK`, `HDEL` |
|
|
1744
|
+
| `admin` | `FLUSHDB`, `FLUSHALL`, `CONFIG`, `INFO`, `CLIENT`, `DEBUG`, `SHUTDOWN`, `KEYS`, `MONITOR`, `SAVE`, `BGSAVE`, `BGREWRITEAOF`, `REPLICAOF`, `SLAVEOF`, `ACL` |
|
|
1745
|
+
|
|
1746
|
+
### Schema inspection
|
|
1747
|
+
|
|
1748
|
+
`schema <key>` returns one synthetic row per key with these columns:
|
|
1749
|
+
|
|
1750
|
+
| column | meaning |
|
|
1751
|
+
|--------|---------|
|
|
1752
|
+
| `type` | Redis type (`string` / `hash` / `list` / `set` / `zset` / `stream` / `none`) |
|
|
1753
|
+
| `ttl` | `<n>s`, `no expiry`, or `missing` |
|
|
1754
|
+
| `size` | `STRLEN` / `HLEN` / `LLEN` / `SCARD` / `ZCARD` / `XLEN` depending on type |
|
|
1755
|
+
| `sample` | First 5 hash field names (hash only) |
|
|
1756
|
+
|
|
1757
|
+
`schema` (no key) and `--refresh` / `--reset` are rejected — there is no full-database schema cache for Redis.
|
|
1758
|
+
|
|
1759
|
+
### Recommended `query` patterns
|
|
1760
|
+
|
|
1761
|
+
```bash
|
|
1762
|
+
# Read
|
|
1763
|
+
dbcli query "GET feature:flag"
|
|
1764
|
+
dbcli query "HGETALL user:42" --format json
|
|
1765
|
+
dbcli query "LRANGE queue:jobs 0 9"
|
|
1766
|
+
|
|
1767
|
+
# Iterate keys (paginated; never use KEYS — admin-only)
|
|
1768
|
+
dbcli query "SCAN 0 MATCH session:* COUNT 200"
|
|
1769
|
+
|
|
1770
|
+
# Write (requires read-write+)
|
|
1771
|
+
dbcli query "SET counter 1"
|
|
1772
|
+
dbcli query "EXPIRE session:abc 3600"
|
|
1773
|
+
dbcli query "HSET user:42 name Alice"
|
|
1774
|
+
|
|
1775
|
+
# Delete (requires data-admin+)
|
|
1776
|
+
dbcli query "DEL temp:lock"
|
|
1777
|
+
dbcli query "HDEL user:42 lastLogin"
|
|
1778
|
+
```
|
|
1779
|
+
|
|
1780
|
+
### Size guard (`query --no-limit` / shell `.no-limit`)
|
|
1781
|
+
|
|
1782
|
+
The adapter rewrites unbounded reads before dispatch and truncates oversized replies after:
|
|
1783
|
+
|
|
1784
|
+
| Strategy | Commands | Behavior |
|
|
1785
|
+
|----------|----------|----------|
|
|
1786
|
+
| inject/cap `COUNT` | `SCAN`, `HSCAN`, `SSCAN`, `ZSCAN` | adds `COUNT 1000` when absent; caps a larger `COUNT` to 1000 |
|
|
1787
|
+
| clamp `stop` | `LRANGE`, `ZRANGE`, `ZREVRANGE` | rewrites `stop` so the span ≤ 1000 (`-1` becomes `start+999`) |
|
|
1788
|
+
| inject/cap `LIMIT` | `ZRANGEBYSCORE` | appends `LIMIT 0 1000` when absent; caps a larger count |
|
|
1789
|
+
| client truncate | `HGETALL`, `HKEYS`, `HVALS`, `SMEMBERS`, `KEYS` | keeps the first 1000 entries |
|
|
1790
|
+
|
|
1791
|
+
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.
|
|
1792
|
+
|
|
1793
|
+
### Blacklist enforcement
|
|
1794
|
+
|
|
1795
|
+
Blacklist rules are enforced as **Redis-native key globs** (`*`, `?`, `[abc]`, `[a-z]`):
|
|
1796
|
+
|
|
1797
|
+
```bash
|
|
1798
|
+
dbcli blacklist add 'secrets:*' # register a key-glob rule
|
|
1799
|
+
dbcli query "GET secrets:api_key" # → BlacklistRejection (exit non-zero)
|
|
1800
|
+
dbcli query "MGET safe:k secrets:api" # → rejected (any matching key fails the whole command)
|
|
1801
|
+
dbcli query "KEYS secrets:*" # → rejected (pattern overlaps a rule)
|
|
1802
|
+
dbcli query "KEYS *" # → returns only non-blacklisted keys
|
|
1803
|
+
```
|
|
1804
|
+
|
|
1805
|
+
Rejections are written to the audit log with `success: false` and `metadata.rejection_reason: 'blacklist'` + `matched_pattern`.
|
|
1806
|
+
|
|
1807
|
+
### Value / hash-field masking (v1.22)
|
|
1808
|
+
|
|
1809
|
+
Where the key-glob blacklist *rejects*, masking instead *redacts*: a matched read still
|
|
1810
|
+
runs, but the sensitive value comes back as `[REDACTED]` so an agent can use the command
|
|
1811
|
+
without ever seeing it. Add an optional `redis.mask` block to `.dbcli`:
|
|
1812
|
+
|
|
1813
|
+
```yaml
|
|
1814
|
+
redis:
|
|
1815
|
+
mask:
|
|
1816
|
+
- keyPattern: 'session:*' # whole value redacted on read
|
|
1817
|
+
- keyPattern: 'user:*'
|
|
1818
|
+
fields: [password, token] # only these hash fields redacted
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
- Applies on reads: `GET`, `GETRANGE`, `HGETALL`, `HGET`, `HMGET`, `HVALS`.
|
|
1822
|
+
- A rule without `fields` redacts the entire value; with `fields` only the named hash fields are redacted.
|
|
1823
|
+
- Masking and key-glob rejection coexist, and **rejection always wins over masking** — a key that matches a blacklist rule is rejected, never merely masked.
|
|
1824
|
+
|
|
1825
|
+
### Interactive shell
|
|
1826
|
+
|
|
1827
|
+
`dbcli shell` on a Redis connection opens a single-line REPL:
|
|
1828
|
+
|
|
1829
|
+
```text
|
|
1830
|
+
$ dbcli --use local-redis shell
|
|
1831
|
+
Redis shell: single-line commands; SCAN/LRANGE auto-capped at 1000. Type `.no-limit on` to bypass (unsafe).
|
|
1832
|
+
redis> SCAN 0 # wire args become: SCAN 0 COUNT 1000 (REDIS_SIZE_REWRITE)
|
|
1833
|
+
redis> HGETALL bighash # >1000 fields → kept 1000 (REDIS_SIZE_TRUNCATE)
|
|
1834
|
+
redis> .no-limit on # bypass size guard for this session
|
|
1835
|
+
redis> GET secrets:api_key # → REDIS_BLACKLIST / BlacklistRejection if blacklisted
|
|
1836
|
+
redis> .exit
|
|
1837
|
+
```
|
|
1838
|
+
|
|
1839
|
+
Tab completion offers Redis command names and known key prefixes; history persists to `~/.dbcli_history`.
|
|
1840
|
+
|
|
1841
|
+
### Limitations
|
|
1842
|
+
|
|
1843
|
+
- No `--dry-run` for writes — Redis commands execute immediately. Pair writes with a confirming read (`GET`, `HGETALL`, `EXISTS`).
|
|
1844
|
+
- No transaction wrapping (`MULTI`/`EXEC`). Submit one command at a time.
|
|
1845
|
+
- `KEYS` requires `admin`. Prefer `SCAN` for routine work.
|
|
1846
|
+
- Blacklist enforcement covers **keys** (Redis-native globs); value / hash-field **masking** is available via the `redis.mask` config block (v1.22).
|
|
1847
|
+
|
|
1848
|
+
## Elasticsearch Support
|
|
1849
|
+
|
|
1850
|
+
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.
|
|
1851
|
+
|
|
1852
|
+
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `export` (v1.22), `shell` (v1.22), `status`, `doctor`, `upgrade`, `completion`
|
|
1853
|
+
|
|
1854
|
+
**Saved queries:** `q` is supported for ES JSON DSL bodies (see "Elasticsearch snippets" below).
|
|
1855
|
+
|
|
1856
|
+
**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.
|
|
1857
|
+
|
|
1858
|
+
### Connection and configuration
|
|
1859
|
+
|
|
1860
|
+
- Either `host` + `port` (default `https://localhost:9200`) or `nodes: [...]` (first node is used) or `cloudId`.
|
|
1861
|
+
- Auth precedence: `apiKey` → `user`/`password` (HTTP Basic). Leave both unset for an open cluster.
|
|
1862
|
+
- `protocol` defaults to `https`. For TLS quirks: `caPath` (path to a PEM bundle) and `rejectUnauthorized: false` (last resort).
|
|
1863
|
+
- `connection.timeout` (ms, default 5000) is wired to `AbortController` on every request.
|
|
1864
|
+
|
|
1865
|
+
### Permission classification
|
|
1866
|
+
|
|
1867
|
+
Each REST request is mapped to a SQL-shaped tier based on method + path:
|
|
1868
|
+
|
|
1869
|
+
| ES surface | Mapped to | Permission |
|
|
1870
|
+
|------------|-----------|------------|
|
|
1871
|
+
| `GET _search` / `_count` / `_mapping` / `_settings` / `_alias` / `GET _doc` / `_source` | `SELECT` | `query-only` |
|
|
1872
|
+
| `POST _update` / `POST _doc` | `UPDATE` | `read-write` |
|
|
1873
|
+
| `PUT _doc` / `_create` | `INSERT` | `read-write` |
|
|
1874
|
+
| `DELETE` (any) | `DELETE` | `data-admin` |
|
|
1875
|
+
| `_bulk` | highest tier among the NDJSON actions (`delete` ⇒ `data-admin`) | derived |
|
|
1876
|
+
| Anything else | `DROP` | `admin` (deny by default) |
|
|
1877
|
+
|
|
1878
|
+
### Schema inspection
|
|
1879
|
+
|
|
1880
|
+
`schema [index]` calls `GET /<index>/_mapping` and flattens nested properties into dotted-path columns. Multi-fields under `.fields` (e.g. `text` → `text.keyword`) are emitted as separate columns. All fields are reported as nullable. There is no PK / FK / index info.
|
|
1881
|
+
|
|
1882
|
+
`schema` (no argument) iterates all non-system indices through the standard full-scan code path and writes per-connection caches under `.dbcli/schemas/<connection>/`.
|
|
1883
|
+
|
|
1884
|
+
### Query semantics
|
|
1885
|
+
|
|
1886
|
+
- `--collection <index>` (or `--index <index>`) is required.
|
|
1887
|
+
- Body that starts with `{` → sent as JSON DSL via `POST /<index>/_search`. Body otherwise → URL-encoded into `?q=...` (Lucene query string) on `GET`.
|
|
1888
|
+
- Hits are flattened: each row carries `_id` plus dotted-path fields lifted from `_source`. Use `--format json` to inspect raw nested structure.
|
|
1889
|
+
- Query-only mode caps `size` at 1000. `--no-limit` is internally capped at 10 000; for deeper pagination use the API directly with `search_after` or PIT.
|
|
1890
|
+
|
|
1891
|
+
### Recommended `query` patterns
|
|
1892
|
+
|
|
1893
|
+
```bash
|
|
1894
|
+
# DSL match
|
|
1895
|
+
dbcli query '{"query":{"match":{"status":"active"}}}' --collection orders --format json
|
|
1896
|
+
|
|
1897
|
+
# DSL with sort + size
|
|
1898
|
+
dbcli query '{"query":{"range":{"created_at":{"gte":"2026-01-01"}}},"sort":[{"created_at":"desc"}],"size":50}' \
|
|
1899
|
+
--collection orders
|
|
1900
|
+
|
|
1901
|
+
# Aggregation
|
|
1902
|
+
dbcli query '{"size":0,"aggs":{"by_status":{"terms":{"field":"status.keyword"}}}}' \
|
|
1903
|
+
--collection orders --format json
|
|
1904
|
+
|
|
1905
|
+
# Lucene query string
|
|
1906
|
+
dbcli query 'status:active AND amount:>100' --index orders --limit 100
|
|
1907
|
+
```
|
|
1908
|
+
|
|
1909
|
+
### Export (v1.22)
|
|
1910
|
+
|
|
1911
|
+
`dbcli export` supports two shapes on an ES connection:
|
|
1912
|
+
|
|
1913
|
+
```bash
|
|
1914
|
+
# (a) search DSL + --index → export the hits
|
|
1915
|
+
dbcli export '{"query":{"match":{"status":"active"}}}' --index orders --format jsonl --output orders.ndjson
|
|
1916
|
+
|
|
1917
|
+
# (b) index name as the query → match_all over the whole index (scroll)
|
|
1918
|
+
dbcli export orders --format csv --output orders.csv
|
|
1919
|
+
dbcli export orders --no-limit --format jsonl # full index, scrolled in batches
|
|
1920
|
+
```
|
|
1921
|
+
|
|
1922
|
+
- Outputs JSON / JSONL / CSV. Default cap is 1000 rows; `--no-limit` streams the full index via the scroll API in batches.
|
|
1923
|
+
- Index-level blacklist is checked before export and the run is written to the audit log.
|
|
1924
|
+
|
|
1925
|
+
### Interactive shell (v1.22)
|
|
1926
|
+
|
|
1927
|
+
`dbcli shell` on an ES connection opens a Kibana Dev Tools-style REPL:
|
|
1928
|
+
|
|
1929
|
+
```text
|
|
1930
|
+
$ dbcli --use local-es shell
|
|
1931
|
+
GET /orders/_search
|
|
1932
|
+
{
|
|
1933
|
+
"query": { "match": { "status": "active" } }
|
|
1934
|
+
}
|
|
1935
|
+
# ← blank line submits the whole block
|
|
1936
|
+
```
|
|
1937
|
+
|
|
1938
|
+
- 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.
|
|
1939
|
+
- Read-focused: index-level blacklist rejects protected indices at the front end; a `_search` whose body omits `size` is auto-capped at 1000 hits.
|
|
1940
|
+
|
|
1941
|
+
### Doctor and diagnostics
|
|
1942
|
+
|
|
1943
|
+
`dbcli doctor` runs a dedicated Elasticsearch path:
|
|
1944
|
+
|
|
1945
|
+
- Verifies REST connectivity to `GET /`.
|
|
1946
|
+
- Reads `version.number` and runs the standard version freshness check.
|
|
1947
|
+
- Walks every index via `listTables()` + `getTableSchema()` to feed the blacklist completeness check and the large-table heuristic (using `documentCount`).
|
|
1948
|
+
- Standard schema-cache freshness using `schemaLastUpdated`.
|
|
1949
|
+
|
|
1950
|
+
### Limitations
|
|
1951
|
+
|
|
1952
|
+
- 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.
|
|
1953
|
+
- No `_search/scroll` or PIT pagination at the CLI layer; large pulls need a saved external script.
|
|
1954
|
+
- `check`, `diff`, `migrate`, and `q` are SQL-only and exit with errors (or fall through to a generic "unsupported" path).
|
|
1955
|
+
- Blacklist column rules are applied to flattened hit rows on `query`; table-level blacklist rejects an index up front.
|