@carllee1983/dbcli 9.1.2 → 10.0.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.
@@ -119,24 +119,24 @@ dbcli query '{}' --collection events --format json 2>/dev/null | jq '.rows | len
119
119
  Initialize `.dbcli` configuration file. Typically run manually by the developer — avoid running on behalf of the user unless explicitly requested.
120
120
 
121
121
  ```bash
122
- dbcli init # Single connection (v1 format)
123
- dbcli init --system mysql --host localhost --port 3306 --user root --name mydb
122
+ dbcli init # Prompt for a name; create named v2
123
+ dbcli init --conn-name local --system mysql --host localhost --port 3306 --user root --name mydb
124
124
  dbcli init --use-env-refs # Store env var references
125
- dbcli init --no-interactive --force # Non-interactive mode
125
+ dbcli init --conn-name local --no-interactive --force # Non-interactive; credentials from environment
126
126
 
127
127
  # MongoDB — field-by-field (primary path, same shape as SQL)
128
- dbcli init --system mongodb --host localhost --port 27017 --user admin --password secret --auth-source admin --name mydb
129
- dbcli init --system mongodb --host localhost --port 27017 --name mydb # No auth
128
+ dbcli init --conn-name local --system mongodb --host localhost --port 27017 --user admin --password secret --auth-source admin --name mydb
129
+ dbcli init --conn-name local --system mongodb --host localhost --port 27017 --name mydb # No auth
130
130
  # MongoDB — full URI (advanced fallback: multi-host, non-standard driver options)
131
- dbcli init --system mongodb --uri "mongodb://user:pass@host:27017/mydb?authSource=admin"
131
+ dbcli init --conn-name local --system mongodb --uri "mongodb://user:pass@host:27017/mydb?authSource=admin"
132
132
 
133
133
  # Redis (database = logical DB index)
134
- dbcli init --system redis --host localhost --port 6379
135
- dbcli init --system redis --host localhost --port 6379 --password secret --name 0
134
+ dbcli init --conn-name local --system redis --host localhost --port 6379
135
+ dbcli init --conn-name local --system redis --host localhost --port 6379 --password secret --name 0
136
136
 
137
137
  # Elasticsearch
138
- dbcli init --system elasticsearch --host localhost --port 9200 --user elastic --password changeme
139
- dbcli init --system elasticsearch --cloud-id "myCluster:dXMtZWFzdC0xLmF3..." --api-key "<base64>"
138
+ dbcli init --conn-name local --system elasticsearch --host localhost --port 9200 --user elastic --password changeme
139
+ dbcli init --conn-name local --system elasticsearch --cloud-id "myCluster:dXMtZWFzdC0xLmF3..." --api-key "<base64>"
140
140
 
141
141
  # Multi-connection (v2 format)
142
142
  dbcli init --conn-name staging --env-file .env.staging # Named connection with custom env file
@@ -151,6 +151,12 @@ dbcli --global init --conn-name shared --system postgresql --host db.example.com
151
151
  dbcli --global use --list
152
152
  ```
153
153
 
154
+ Every new or additional connection needs a nonblank connection name. Pass `--conn-name <name>`, or run `dbcli init` in an interactive terminal to be asked for one. The name prompt has no suggested `default`; empty or whitespace-only answers explain the requirement and prompt again. Cancelling or ending the name prompt stops before any connection test or configuration/credential write. An explicit nonblank `--conn-name` skips this prompt, and `--conn-name default` is valid. An explicitly blank `--conn-name` is an error in every mode, before connection tests or writes. `--name` still means the database name (the logical DB index for Redis), never the connection name.
155
+
156
+ Without a TTY, with `--no-interactive`, or when `--use-env-refs` plus all five `--env-*` options automatically disables interaction, a missing or blank `--conn-name` fails with guidance to supply it, before database access or configuration/credential writes. Complete credentials, `--name`, `--env-file`, and `--force` do not supply a connection name.
157
+
158
+ New initialization always creates named v2 configuration, including plain `init` and explicit `--config` paths; it no longer creates an unnamed v1 configuration. Existing v1 configurations remain readable and their established named v2 import is retained. The first explicitly named connection is still the default in a new registry; adding a connection preserves an existing registry's default or no-default state. An explicitly chosen duplicate name still requires interactive overwrite confirmation or `--force`; a noninteractive duplicate without `--force`, or a declined confirmation, leaves files untouched. `--remove` and `--rename` do not ask for a new connection name.
159
+
154
160
  **Key options:** `--system`, `--permission`, `--use-env-refs`, `--skip-test`, `--no-interactive`, `--force`, `--conn-name <name>`, `--env-file <path>`, `--remove <name>`, `--rename <old:new>`
155
161
 
156
162
  **Environment-reference options:** `--env-host <var>`, `--env-port <var>`, `--env-user <var>`, `--env-password <var>`, `--env-database <var>`
@@ -161,7 +167,27 @@ dbcli --global use --list
161
167
 
162
168
  **Redis note:** the `database` (or `--name`) field is the logical DB index (`"0"` … `"15"`), not a database name.
163
169
 
164
- **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.
170
+ **Multi-connection:** Every newly created connection uses v2 with an explicitly supplied or interactively entered name. Each connection can have its own env file and permission level; `--env-file` does not replace the name requirement. Existing v1 configs are automatically imported as the `default` connection when upgrading.
171
+
172
+ V2 may omit the top-level `default` field; if supplied, it must be a nonempty
173
+ string naming an existing connection. `null`, `""`, and nonexistent names are
174
+ invalid. The first explicitly named connection defaults a new registry; adding
175
+ another preserves the existing default or no-default state. Existing v1 reads
176
+ and named v1 imports remain supported; neither configuration version changes.
177
+
178
+ Legacy project configuration compatibility: adding a connection cannot import a
179
+ direct-project v2 registry when an existing connection uses an `envFile` other
180
+ than `.env.local`. `init` refuses before writing and retains the existing config
181
+ and env files. An administrator must migrate the registry and custom env files
182
+ to supported home storage before retrying. Bound/home-storage v2 configs and
183
+ direct-project v2 configs with no `envFile` or only `.env.local` remain supported.
184
+
185
+ `init --remove <name>` clears `default` and reports that change when removing its
186
+ connection; it never chooses another connection. Removing a non-default keeps
187
+ the saved default, and removing a connection from a no-default config keeps that
188
+ state. The public `removeConnection` helper follows the same rules. Deleting
189
+ the last connection is rejected without changing the config. Renaming preserves
190
+ the no-default state, or updates the default name when renaming that connection.
165
191
 
166
192
  Use root-level `--global` with `init`, `use`, `status`, `query`, or any other command to read or mutate the user-global v2 registry at `~/.config/dbcli/config.json`. Without it, the current project binding remains the source of truth. The global registry uses the same private file mode and integrity record as project home storage.
167
193
 
@@ -169,11 +195,12 @@ Use root-level `--global` with `init`, `use`, `status`, `query`, or any other co
169
195
 
170
196
  ### use
171
197
 
172
- Switch or display the default database connection (v2 multi-connection config).
198
+ Set, clear, or display the default database connection (v2 multi-connection config).
173
199
 
174
200
  ```bash
175
- dbcli use # Show current default connection
201
+ dbcli use # Show the current default, or describe no default
176
202
  dbcli use staging # Switch default to 'staging'
203
+ dbcli use --unset # Clear the saved default; connections are unchanged
177
204
  dbcli use --list # List all connections (* marks default)
178
205
  dbcli use --list --format json # Credential-free connection identity inventory
179
206
  ```
@@ -188,17 +215,43 @@ Cloud ID-only Elasticsearch connections also return `null` instead of default
188
215
  placeholders. It deliberately excludes user names, passwords, URIs, Cloud IDs,
189
216
  API keys, and env variable names.
190
217
  Misspelled selectors suggest nearby configured connection names.
218
+ Without a default, text inventory marks no connection as default and every JSON
219
+ item has `isDefault: false`. Inventory remains usable, and `doctor` accepts an
220
+ omitted default as valid v2 configuration. Its connection checks still require
221
+ an explicit selector, for example `dbcli --use staging doctor`.
191
222
 
192
- Any command can also use `--use <name>` to temporarily select a connection without changing the default:
223
+ Any command can also use root-level `--use <name>` before the command to
224
+ temporarily select a connection without changing the default. `query`, `schema`,
225
+ `list`, `export`, and `check` also accept it after the command:
193
226
 
194
227
  ```bash
195
228
  dbcli query --use staging "SELECT * FROM users LIMIT 10"
196
229
  dbcli list --use prod
197
230
  ```
198
231
 
232
+ Selection precedence is explicit `--use`, then `DBCLI_CONNECTION`, then the
233
+ saved default. Conflicting root-level and command-level selectors are an error;
234
+ blank CLI selectors are invalid, while a blank environment selector is ignored.
235
+ Without a default, connection-dependent commands require an explicit `--use` or
236
+ `DBCLI_CONNECTION` selection. Otherwise they fail before database access with
237
+ an error naming both selectors and listing available connection names, never
238
+ endpoints or credentials. No caller selects the first connection automatically.
239
+ Unknown names and production targets retain their validation and explicit
240
+ selection safeguards.
241
+
242
+ `use --unset` removes only the persisted v2 `default` field and reports that it
243
+ was cleared. It also succeeds when the field is already absent without changing
244
+ connections. It rejects a connection name, `--list`, `--confirm-production`, or
245
+ `--format json` in the same invocation before writing, and never converts or
246
+ overwrites v1. It uses the existing approved, integrity-protected configuration
247
+ write path and is blocked under `DBCLI_AGENT_MODE=1`. To set a default again, use
248
+ `dbcli use <name>`; production defaults still require the exact name repeated in
249
+ `--confirm-production <name>`, and production database access still requires an
250
+ explicit selector even if that connection is the saved default.
251
+
199
252
  **Requires v2 config** (created with `dbcli init --conn-name`).
200
253
 
201
- **Options:** `--list`, `--format <text|json>`, `--confirm-production <name>` (required when explicitly selecting a production connection as the default).
254
+ **Options:** `--list`, `--unset`, `--format <text|json>`, `--confirm-production <name>` (required when explicitly selecting a production connection as the default).
202
255
 
203
256
  ### password
204
257
 
@@ -3591,7 +3644,7 @@ Parser behaviour (`src/core/saved-queries/parser.ts::normaliseVisual`):
3591
3644
 
3592
3645
  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.
3593
3646
 
3594
- `init --system mongodb` defaults to a field-by-field wizard (`host`, `srv`, `port`, `user`, `password` + `authSource`, then optional `replicaSet` / `tls`); a full `uri` is an explicit advanced choice in the interactive flow and the unchanged non-interactive path via `--uri`. Optional fields `authSource`, `replicaSet`, `tls`, and `srv` express what previously required embedding options in the `uri` query string. Atlas-style `mongodb+srv://` URIs are supported both as a full `uri` and via the per-field `srv: true` option. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
3647
+ `init --system mongodb` defaults to a field-by-field wizard (`host`, `srv`, `port`, `user`, `password` + `authSource`, then optional `replicaSet` / `tls`); a full `uri` is an explicit advanced choice in the interactive flow and the non-interactive path via `--uri` (with the required `--conn-name`). Optional fields `authSource`, `replicaSet`, `tls`, and `srv` express what previously required embedding options in the `uri` query string. Atlas-style `mongodb+srv://` URIs are supported both as a full `uri` and via the per-field `srv: true` option. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
3595
3648
 
3596
3649
  The 5000ms default server-selection timeout is often too tight for a connection over a VPN or to Atlas. Set a `timeout` field (ms) in the connection config, or override it per invocation with root-level `--timeout`, e.g. `dbcli --timeout 20000 --use <conn> list`.
3597
3650
 
@@ -3617,10 +3670,10 @@ The 5000ms default server-selection timeout is often too tight for a connection
3617
3670
 
3618
3671
  ```bash
3619
3672
  # 1. Initialize — field-by-field (primary path)
3620
- dbcli init --system mongodb --host localhost --port 27017 \
3673
+ dbcli init --conn-name local --system mongodb --host localhost --port 27017 \
3621
3674
  --user admin --password '<secret>' --auth-source admin --name mydb
3622
3675
  # ...or a full URI (advanced fallback, e.g. Atlas SRV clusters)
3623
- dbcli init --system mongodb --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
3676
+ dbcli init --conn-name local --system mongodb --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
3624
3677
 
3625
3678
  # 2. List collections
3626
3679
  dbcli list --format json
package/CHANGELOG.md CHANGED
@@ -5,6 +5,113 @@ All notable changes to dbcli are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [10.0.0] - 2026-10-13
11
+
12
+ 這次發布整合 npm 9.1.2 之後合併的變更:明確命名連線、可不設預設連線、Bun 1.4.0
13
+ 最低版本、PostgreSQL schema 修正、帶期限的 audit 豁免,以及實錄的 agent 操作影片。
14
+ 9.2.0 曾完成發布準備,但未發布到 npm;原本的 9.2.0 與 Unreleased 更新紀錄合併於此。
15
+
16
+ 版本為 **10.0.0**。新連線不再自動取名,刪除預設連線也不再自動改選另一條;這兩項
17
+ 行為會影響既有腳本,所以採用 major 版本。
18
+
19
+ ### Breaking changes and upgrade notes
20
+
21
+ - **非互動式初始化必須指定 `--conn-name <連線名稱>`。** 適用於非 TTY、
22
+ `--no-interactive` 與完整 env-reference 自動非互動模式;互動模式會詢問名稱。
23
+ `--name` 仍表示資料庫名稱,不能拿它代替 `--conn-name`。原先依賴自動命名的腳本
24
+ 須補上連線名稱;明確指定 `--conn-name default` 仍合法。
25
+ - **升級 Bun 至 1.4.0 以上。** 仍使用 1.3.x 的環境須先升級,原因與相依套件細節見下方。
26
+ - **既有 v1 設定讀取與具名匯入仍保留。** 單純 `init` 與明確 `--config` 新建連線
27
+ 改用具名 v2;全新 registry 的第一條具名連線仍會成為預設,追加連線則保留既有的
28
+ default/no-default 狀態,不會偷偷建立或替換預設。
29
+ - **預設連線可以清除,刪除後不再自動補選。** 使用 `dbcli use --unset` 清除預設;
30
+ 刪除預設連線也會留下無預設狀態,而不是選取下一條。之後須透過 `--use` 或
31
+ `DBCLI_CONNECTION` 選取,或以 `dbcli use <連線名稱>` 明確設定新預設;仍禁止刪除
32
+ 最後一條連線。
33
+ - **帶自訂 `envFile` 的直接專案 v2 設定須先遷移。** 若新增連線需要匯入舊版直接
34
+ 專案 v2 設定,而它的 `envFile` 不是 `.env.local`,`init` 會在寫入前拒絕並保留
35
+ 原檔。須先由管理員完成 home storage 遷移,再新增連線。
36
+
37
+ ### Added
38
+
39
+ - **介紹頁與 README 加上一段實錄的 agent 操作影片。** 一段真實的 Claude Code session
40
+ 對著一個用完即丟的 PostgreSQL 操作:agent 透過 dbcli 讀資料,被列入黑名單的
41
+ `password_hash` 欄位帶著 dbcli 的安全提示被略去,它在 query-only 連線上的寫入被拒絕。
42
+ 影片放在兩個語系介紹頁的 #safety 區,兩份 README 各以縮圖連到自己語系的頁面。
43
+ `scripts/demo/` 保留 seed、設定、錄製與剪輯腳本,CLI 輸出改變時可以重錄。
44
+
45
+ ### Changed
46
+
47
+ - **新建或新增連線必須明確命名。** `init` 未提供 `--conn-name` 時會在互動式
48
+ 終端機主動詢問名稱,不再代填 `default`;空白回答會說明並再次詢問,取消或
49
+ 結束提示不會寫入;明確傳入空白名稱則在任何模式都報錯。非 TTY、
50
+ `--no-interactive` 或完整 env-reference 自動非互動
51
+ 模式缺少名稱時,會在連線測試、設定及憑證寫入前報錯。`--name` 仍是資料庫名,
52
+ 明確指定 `--conn-name default` 仍合法。單純 `init` 與明確 `--config` 都改用
53
+ 具名 v2 新建,既有 v1 讀取與具名匯入保留。全新 registry 仍以第一條具名連線
54
+ 為預設,追加連線保留既有 default/no-default;同名覆寫確認及 `--force` 保護
55
+ 維持不變(#212)。
56
+
57
+ - **v2 可以不設定預設連線。** 省略 `default` 即可要求每次透過 `--use` 或
58
+ `DBCLI_CONNECTION` 明確選取;未選取時會在連線前失敗,列出可用名稱而不洩漏
59
+ 端點或憑證。新增 `dbcli use --unset`,透過既有核准與 integrity record 保護
60
+ 流程清除預設值,重複執行也安全。刪除預設連線會清除預設值,不再依 JSON
61
+ 順序改選另一條;仍禁止刪除最後一條連線。清單與 doctor 支援無預設狀態,
62
+ 初始化與 v1 遷移建立預設值的方式、selector 優先序與正式環境防護不變(#212)。
63
+ 若新增連線會匯入帶有自訂 `envFile`(非 `.env.local`)的舊版直接專案 v2
64
+ 設定,`init` 會在寫入前拒絕並保留原檔,須先由管理員完成 home storage 遷移。
65
+
66
+ - **Bun 最低版本從 1.3.3 提高到 1.4.0。** `mongodb@7.2.0` 依賴 `bson: "^7.2.0"`,
67
+ 不帶 lockfile 的使用者安裝會解析到 bson 7.3+。bson 7.3.0 起,`ObjectId` 載入時會呼叫
68
+ `v8.startupSnapshot.isBuildingSnapshot()`,Bun 1.3.x 對此丟出
69
+ `NotImplementedError: node:v8 isBuildingSnapshot is not yet implemented in Bun.`,
70
+ 任何碰到 MongoDB 的指令都會失敗。實測 Bun 1.3.3 與 1.3.10–1.3.14 都會失敗,
71
+ 1.4.0–1.4.3 都正常。本 repo 的 `bun.lock` 把 bson 鎖在 7.2.0,所以 CI 的 1.3.3 那一列
72
+ 從來沒碰到這個問題。`engines.bun`、CI 最低版本、postinstall 提示、`doctor` 的預設值與
73
+ 使用者文件都改為 1.4.0。
74
+
75
+ - **audit gate 可以帶有期限、附理由的豁免。** `scripts/audit-exemptions.json` 逐筆列出
76
+ GHSA id、套件、理由與 UTC 到期日。未到期的項目轉成 `bun audit --ignore` 參數,每次執行
77
+ 都印出來;過期或格式錯誤的項目讓 gate 失敗,所以豁免不會悄悄變成永久。第一筆豁免
78
+ GHSA-vfj7-8cjw-p6xm(`braces` <= 3.0.3,沒有修正版,只經由建置期的 tailwindcss 引入)
79
+ 到 2026-11-30。決定記在 ADR-0042。
80
+
81
+ - **發布流程改由版本 tag 驅動。** 合併發布準備 PR 後才在 `main` 建立 `vX.Y.Z`;
82
+ 工作流程核對版本與來源、啟動測試資料庫並執行 `make verify`,通過後等待
83
+ `npm-publish` 環境審批,再透過 npm trusted publishing 發布附 provenance 的套件,
84
+ 最後從本檔對應版本章節建立 GitHub Release。須先完成 trusted publisher 與審批環境
85
+ 設定(#214)。
86
+
87
+ ### Fixed
88
+
89
+ - **Elasticsearch 整合驗證不再把未執行的 roundtrip 算成通過。** 測試的 DOM preload
90
+ 同時保留 Bun 的 `fetch`、`AbortController` 與 `AbortSignal`,避免混用不同實作而在
91
+ HTTP 送出前失敗。有服務時的連線/初始化錯誤會讓測試失敗;只有明確停用整合測試,
92
+ 或本機未要求必備服務且 TCP 端點不存在時,才記為真正的 skip。CI 必須實跑建立索引、
93
+ 寫入、refresh、查詢、schema 斷言與清理,完成後才輸出 roundtrip 驗證紀錄。
94
+ 這是測試基礎設施修正,沒有改變發布套件的 adapter 行為。
95
+
96
+ - **PostgreSQL 的 schema 重新回報主鍵、索引與外鍵欄位。** 對 `pg_attribute.attname` 做
97
+ `array_agg` 得到的是 `name[]`,node-postgres 把它當字串回傳,schema 因此丟掉了這些欄位,
98
+ 寫入閘門也就拒絕了每一筆結構化寫入。改為先轉成 `text`。
99
+
100
+ - **PostgreSQL 的 enum 欄位重新回報 `enumValues`。** `pg_enum.enumlabel` 的 `array_agg`
101
+ 同樣是 `name[]`,以相同方式修正。
102
+
103
+ - **SQL 引擎的 `update` 先檢查權限等級再連線。** 與 Redis、MongoDB 分支以及 `delete`
104
+ 一致:query-only 連線現在因為權限不足被拒,而不是被寫入閘門擋下;權限檢查也排在
105
+ executor 的黑名單檢查之前。
106
+
107
+ ### Security
108
+
109
+ - **兩筆開發依賴的安全公告。** `source-map-js` 1.2.1 → 1.2.2(high,GHSA-68fv-2mgg-jv7q),
110
+ 修正版在 `postcss` 的 `^1.2.1` 範圍內,只更新 lockfile。`postcss-selector-parser`
111
+ (moderate,GHSA-rj75-hqrm-r3gf)只在 7.1.6 修正,但 `tailwindcss@3.4.19` 與
112
+ `postcss-nested@6.2.0` 都要求 6.x,因此依 ADR-0042 在 `scripts/audit-exemptions.json`
113
+ 豁免到 2026-11-30:它只在建置時解析本 repo 自己的樣式表,執行期與使用者輸入都碰不到。
114
+
8
115
  ## [9.1.2] - 2026-10-01
9
116
 
10
117
  一個開發依賴的安全公告、工作規格協定從 ForgeFlow 換成 Warrant,以及三個讓測試與建置
package/README.dev.md CHANGED
@@ -6,7 +6,7 @@ Scoped package: **`@carllee1983/dbcli`**. All `npm` / `npx` examples below use t
6
6
 
7
7
  ### Pre-Publication Checklist
8
8
 
9
- Before running `npm publish`:
9
+ Before tagging a release (the tag triggers `npm publish` in CI):
10
10
 
11
11
  1. **Verify build is clean:**
12
12
  ```bash
@@ -34,7 +34,7 @@ Before running `npm publish`:
34
34
 
35
35
  3. **Update version in package.json:**
36
36
  ```bash
37
- npm version minor # bumps version + creates git tag (in this repo)
37
+ npm version minor --no-git-tag-version # bumps version without creating a tag
38
38
  # OR edit the "version" field in package.json by hand
39
39
  ```
40
40
 
@@ -53,13 +53,26 @@ Before running `npm publish`:
53
53
 
54
54
  ### Publication
55
55
 
56
- Publication uses the `prepublishOnly` script in `package.json`:
56
+ Publication is tag-driven. After the release PR is merged, tag `main` and push the tag:
57
57
 
58
58
  ```bash
59
- npm publish
59
+ git checkout main && git pull
60
+ git tag vX.Y.Z
61
+ git push origin vX.Y.Z
60
62
  ```
61
63
 
62
- What runs (conceptually):
64
+ `.github/workflows/release.yml` then runs `check-tag` (tag equals `v` + `package.json` version, commit is an ancestor of `origin/main`), `verify` (`docker-compose.test.yml` databases, `bun run services:check`, `make verify`, attestation artifact), `publish` (waits for approval in the `npm-publish` environment, then `npm publish` through npm trusted publishing with provenance, no stored token), and `github-release` (GitHub Release whose notes are the `## [X.Y.Z]` section of `CHANGELOG.md`, extracted by `scripts/changelog-section.ts`).
65
+
66
+ **One-time owner setup:**
67
+
68
+ - On npmjs.com, add a trusted publisher for `@carllee1983/dbcli`: owner `CarlLee1983`, repository `dbcli`, workflow `release.yml`, environment `npm-publish`, allowed action `npm publish`. npm expires a configuration that has not published within 2 days.
69
+ - In GitHub repository Settings → Environments, create `npm-publish` with a required reviewer, and set "Deployment branches and tags" to "Selected branches and tags" with only a tag rule `v*.*.*`.
70
+
71
+ **Recovery:** if `check-tag` or `verify` fails, nothing was published; fix the cause, delete the tag remotely and locally (`git push origin :refs/tags/vX.Y.Z` and `git tag -d vX.Y.Z`), and tag again. If `publish` succeeded but `github-release` failed, use "Re-run failed jobs", not "Re-run all jobs" (which would fail at `npm publish` because the version exists). Only plain `vX.Y.Z` tags are accepted; pre-release and build tags are rejected.
72
+
73
+ **Emergency fallback only** (workflow cannot run): from a clean checkout of the tagged commit on `main`, `npm login` and `npm publish` locally. That ships without provenance.
74
+
75
+ What `npm publish` does (in CI or locally):
63
76
 
64
77
  1. **`prepublishOnly`:** `bun run build` — rebuilds `dist/cli.mjs` from `src/cli.ts` via `scripts/build.ts`.
65
78
  2. **Tarball** — paths from the **`files`** field in `package.json`, further filtered by **`.npmignore`**. (Some npm versions also merge in extra root `README*` files; use dry-run to see exactly what will ship.)
@@ -106,7 +119,7 @@ Then ship a patch version with the fix. Prefer **deprecate** over **unpublish**
106
119
 
107
120
  - **`files` (in `package.json`):** Publishes `dist/`, `assets/`, `README.md`, `CHANGELOG.md`, `LICENSE`. The `assets/` tree is required for `dbcli skill` to copy bundled `SKILL.md` / `reference.md` from the installed package.
108
121
  - **`prepublishOnly`:** `bun run build` so `dist/cli.mjs` matches current source.
109
- - **`engines`:** Declares `bun >= 1.3.3` only. `node` was removed because the published bundles cannot run on Node: `dist/cli.mjs` dynamic-imports the extensionless `./cli-runtime` (Bun's resolver appends `.mjs`, Node's does not) and `dist/core.mjs` calls Bun globals. `tests/integration/runtime-contract.test.ts` fails if `engines.node` comes back without the bundles being fixed to match, and if `dist/agent-core.mjs` — the one entry point that is Node-importable — regresses. See `docs/adr/ADR-0008-dbcli-is-a-bun-program-and-engines-says-so.md`.
122
+ - **`engines`:** Declares `bun >= 1.4.0` only. `node` was removed because the published bundles cannot run on Node: `dist/cli.mjs` dynamic-imports the extensionless `./cli-runtime` (Bun's resolver appends `.mjs`, Node's does not) and `dist/core.mjs` calls Bun globals. `tests/integration/runtime-contract.test.ts` fails if `engines.node` comes back without the bundles being fixed to match, and if `dist/agent-core.mjs` — the one entry point that is Node-importable — regresses. See `docs/adr/ADR-0008-dbcli-is-a-bun-program-and-engines-says-so.md`.
110
123
  - **Shebang:** `scripts/build.ts` prepends `#!/usr/bin/env bun` to `dist/cli.mjs`; the `bin` field in `package.json` points at that file.
111
124
  - **`exports`:** `./core` and `./agent-core` only — there is deliberately no `.` entry. It used to point at `dist/cli.mjs`, and `src/cli-runtime.ts` calls `outputHelp()` and `parseAsync(process.argv)` at module top level, so importing the package ran the CLI against the host's argv and never returned. `bin` does not resolve through `exports`, so the executable is unaffected. `tests/integration/runtime-contract.test.ts` pins both the absence of `.` and the side effect that justifies it.
112
125
  - **`postinstall`:** `scripts/postinstall-check-bun.mjs` reports a missing Bun — npm ignores `engines.bun`, so this is the only signal an npm-only machine gets before `dbcli` refuses to start. The command is `bun … || node …` so it runs under whichever runtime is present: Bun-only machines have no `node` to invoke it with, and a hard `node` dependency there would break the primary install path. It must **not** end in `|| exit 0`; v1.55.0 shipped with that mask and npm, which hides lifecycle output unless a script fails, therefore showed nothing at all. On a global install (`npm_config_global === 'true'`) a missing Bun exits 1, so the reason is printed and npm rolls the `bin` back; a dependency install only warns, because that is the `./agent-core` consumer. Both outcomes are pinned in `tests/integration/runtime-contract.test.ts`.
package/README.md CHANGED
@@ -6,6 +6,10 @@ A unified database CLI tool that enables AI agents (Claude Code, Gemini, Copilot
6
6
 
7
7
  **Core Value:** AI agents can safely and intelligently access project databases through a single, permission-controlled CLI tool with sensitive data protection.
8
8
 
9
+ [![Watch a Claude Code agent use dbcli: the blacklisted column is omitted and a write on a query-only connection is refused](docs/assets/demo/dbcli-agent-demo-thumb.webp)](https://carllee1983.github.io/dbcli/dbcli-intro.en.html#safety)
10
+
11
+ *Watch a real session (42 s): the blacklist hides a sensitive column, and the agent's write is refused on a query-only connection.*
12
+
9
13
  > **Security update:** `dbcli init` now writes only a small project binding stub into `./.dbcli/config.json`. The full connection configuration is stored under `~/.config/dbcli/projects/<project-id>/config.json`, so sensitive settings do not live inside the project workspace by default.
10
14
 
11
15
  ## Internationalization (i18n)
@@ -43,7 +47,7 @@ bun install -g @carllee1983/dbcli
43
47
  ```
44
48
 
45
49
  **dbcli runs on Bun.** npm and npx are supported as distribution channels, but the
46
- installed `dbcli` executable requires Bun 1.3.3+ on your `PATH` — install it first with
50
+ installed `dbcli` executable requires Bun 1.4.0+ on your `PATH` — install it first with
47
51
  `curl -fsSL https://bun.sh/install | bash`. Only the `./agent-core` subpath export is
48
52
  importable from a plain Node process.
49
53
 
@@ -224,7 +228,7 @@ dbcli extends its unified interface to Redis and Elasticsearch, providing consis
224
228
 
225
229
  ```bash
226
230
  # Connect to Redis
227
- dbcli init --system redis --host localhost --port 6379
231
+ dbcli init --conn-name local --system redis --host localhost --port 6379
228
232
 
229
233
  # List keys (uses SCAN)
230
234
  dbcli list
@@ -241,7 +245,7 @@ dbcli query "HGETALL user:1"
241
245
 
242
246
  ```bash
243
247
  # Connect to Elasticsearch
244
- dbcli init --system elasticsearch --host localhost --port 9200
248
+ dbcli init --conn-name local --system elasticsearch --host localhost --port 9200
245
249
 
246
250
  # List indices and document counts
247
251
  dbcli list
@@ -264,7 +268,13 @@ The project `.dbcli` directory now acts as a binding + cache layer. The actual c
264
268
 
265
269
  ### Initializing Named Connections
266
270
 
267
- To create a named connection, use the `--conn-name` option during `init`. You can also specify a custom `.env` file for that connection.
271
+ Every new or additional connection needs a nonblank connection name. Pass `--conn-name <name>`, or run `dbcli init` in an interactive terminal to be asked for one. The name prompt has no suggested `default`; empty or whitespace-only answers explain the requirement and prompt again. Cancelling or ending the name prompt stops before any connection test or configuration/credential write. An explicit nonblank `--conn-name` skips this prompt, and `--conn-name default` is valid. An explicitly blank `--conn-name` is an error in every mode, before connection tests or writes. `--name` still means the database name (the logical DB index for Redis), never the connection name.
272
+
273
+ Without a TTY, with `--no-interactive`, or when `--use-env-refs` plus all five `--env-*` options automatically disables interaction, a missing or blank `--conn-name` fails with guidance to supply it, before database access or configuration/credential writes. Complete credentials, `--name`, `--env-file`, and `--force` do not supply a connection name.
274
+
275
+ New initialization always creates named v2 configuration, including plain `init` and explicit `--config` paths; it no longer creates an unnamed v1 configuration. Existing v1 configurations remain readable and their established named v2 import is retained. The first explicitly named connection is still the default in a new registry; adding a connection preserves an existing registry's default or no-default state. An explicitly chosen duplicate name still requires interactive overwrite confirmation or `--force`; a noninteractive duplicate without `--force`, or a declined confirmation, leaves files untouched. `--remove` and `--rename` do not ask for a new connection name.
276
+
277
+ Use `--env-file` to specify a custom `.env` file for a named connection:
268
278
 
269
279
  ```bash
270
280
  # Add a staging connection using .env.staging
@@ -300,6 +310,9 @@ dbcli use staging
300
310
  # Show the current default connection
301
311
  dbcli use
302
312
 
313
+ # Clear the default without removing any connection (v2)
314
+ dbcli use --unset
315
+
303
316
  # Remove a connection
304
317
  dbcli init --remove staging
305
318
 
@@ -307,6 +320,21 @@ dbcli init --remove staging
307
320
  dbcli init --rename staging:production
308
321
  ```
309
322
 
323
+ V2 configurations may omit `default`; when present, it must be a nonempty name
324
+ of an existing connection, not `null` or `""`. Without a default, commands that
325
+ need a connection require `--use` or `DBCLI_CONNECTION`; otherwise they fail
326
+ before database access and list available names without endpoints or credentials.
327
+ `dbcli use` describes this state, text lists mark no default, and JSON inventory
328
+ uses `isDefault: false` for every connection. `doctor` accepts this configuration;
329
+ its connection checks still require a selector, such as `dbcli --use staging doctor`.
330
+
331
+ Removing the current default clears it and reports that change, without choosing
332
+ a replacement. Other removals preserve the default or its absence; deleting the
333
+ last connection is refused without writing. Renaming preserves a missing default
334
+ or updates its name as needed. A new registry defaults to its first explicitly
335
+ named connection; adding another preserves the existing default or no-default
336
+ state. Existing v1 imports and production safeguards remain unchanged.
337
+
310
338
  ### Using a Specific Connection Temporarily
311
339
 
312
340
  You can use the `--use <name>` global flag, the supported command-level form, or `DBCLI_CONNECTION` to execute against a specific connection without changing the default. Selection precedence is explicit `--use`, then `DBCLI_CONNECTION`, then the configured default. A selector is rejected for legacy v1 single-connection configuration instead of being silently ignored.
@@ -350,23 +378,23 @@ dbcli init [OPTIONS]
350
378
  - `--port <port>` — Database port
351
379
  - `--user <user>` — Database user
352
380
  - `--password <pass>` — Database password
353
- - `--name <db>` — Database name
381
+ - `--name <db>` — Database name, distinct from the required `--conn-name`
354
382
  - `--permission <level>` — Permission level: `query-only`, `read-write`, `data-admin`, `admin`
355
383
  - **MongoDB only:** `--uri <uri>` — full connection URI (`mongodb://…` or `mongodb+srv://…`); `--auth-source <db>` — auth database (default `admin` when using user/password)
356
384
  - `--use-env-refs` — Store environment variable references instead of actual values in config
357
385
  - `--skip-test` — Skip connection test
358
386
  - `--no-interactive` — Non-interactive mode (requires all options)
359
- - `--force` — Overwrite existing config without confirmation
387
+ - `--force` — Overwrite the explicitly named existing connection without confirmation
360
388
 
361
389
  **Options (Multi-connection v2):**
362
- - `--conn-name <name>` — Create a named connection (e.g., `staging`, `prod`)
390
+ - `--conn-name <name>` — Required connection name (e.g., `staging`, `prod`); prompts when omitted in an interactive terminal
363
391
  - `--env-file <path>` — Load credentials from a specific `.env` file for this connection
364
392
  - `--remove <name>` — Remove a named connection from the config
365
393
  - `--rename <old:new>` — Rename an existing connection (format: `old:new`)
366
394
 
367
395
  **Behavior:**
368
396
  - Reads `.env` file if present (auto-fills DATABASE_URL, DB_* variables)
369
- - Prompts for missing values (host, port, user, password, database name, permission level)
397
+ - Obtains a nonblank connection name first; interactive terminals then prompt for missing values (host, port, user, password, database name, permission level)
370
398
  - Creates a project binding stub in `.dbcli/config.json` and stores the full config under `~/.config/dbcli/projects/<project-id>/`
371
399
  - Tests database connection before saving
372
400
 
@@ -380,7 +408,7 @@ dbcli init --conn-name staging --env-file .env.staging
380
408
  dbcli init --conn-name prod --env-file .env.production --use-env-refs
381
409
 
382
410
  # Store env var references (non-interactive)
383
- dbcli init --use-env-refs --system mysql \
411
+ dbcli init --conn-name local --use-env-refs --system mysql \
384
412
  --env-host DB_HOST --env-port DB_PORT \
385
413
  --env-user DB_USER --env-password DB_PASSWORD \
386
414
  --env-database DB_DATABASE \
@@ -400,6 +428,17 @@ dbcli use [connection-name] [OPTIONS]
400
428
 
401
429
  **Options:**
402
430
  - `--list` — List all connections and show the current default
431
+ - `--unset` — Clear the saved default; succeeds if already absent
432
+ - `--format <text|json>` — Select inventory output format
433
+ - `--confirm-production <name>` — Repeat the exact name to set a production default
434
+
435
+ `--unset` rejects a connection name, `--list`, `--confirm-production`, or
436
+ `--format json` in the same invocation without writing. It never converts or
437
+ overwrites v1 and uses the same approved, integrity-protected write path as
438
+ switching the default; it is blocked in agent mode. Selection still follows
439
+ `--use` > `DBCLI_CONNECTION` > saved default. Conflicting root/command selectors
440
+ and blank CLI selectors fail; blank environment selectors are ignored. See
441
+ [Connection Management](docs/user/en/index.md#connection-management) for details.
403
442
 
404
443
  **Examples:**
405
444
  ```bash
@@ -1329,7 +1368,7 @@ After installation, the AI agent will have access to dbcli commands and can use
1329
1368
 
1330
1369
  ### Platform-Specific Setup
1331
1370
 
1332
- Every flow below assumes Bun 1.3.3+ on your `PATH`: the installed `dbcli` executable runs under Bun regardless of which package manager fetched it.
1371
+ Every flow below assumes Bun 1.4.0+ on your `PATH`: the installed `dbcli` executable runs under Bun regardless of which package manager fetched it.
1333
1372
 
1334
1373
  #### Claude Code (Anthropic)
1335
1374
 
@@ -1658,7 +1697,7 @@ chmod +x dist/cli.mjs
1658
1697
 
1659
1698
  ### Runtime
1660
1699
 
1661
- - **Bun:** 1.3.3+ — required. The published bundles use Bun APIs and Bun module
1700
+ - **Bun:** 1.4.0+ — required. The published bundles use Bun APIs and Bun module
1662
1701
  resolution; `dbcli` does not run on Node.
1663
1702
  - **Node.js:** only for the `./agent-core` subpath export, which is Node-importable
1664
1703
  (18.0.0+). `npm install -g` / `npx` work as distribution channels but still shell out