@carllee1983/dbcli 1.54.0 → 1.55.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.
@@ -1660,6 +1660,21 @@ shape (`schemaVersion: 1`):
1660
1660
  Recovery codes (fixed in v1.15.0):
1661
1661
  - `CONFIG_MISSING` — no `.dbcli` config; run `dbcli init`.
1662
1662
  - `CONN_REFUSED` / `CONN_AUTH_FAILED` / `CONN_TIMEOUT` / `CONN_HOST_NOT_FOUND` / `CONN_UNKNOWN` — connection failure variants.
1663
+ `CONN_TIMEOUT` also covers a statement the server canceled for exceeding the statement
1664
+ timeout (PostgreSQL `57014`, MySQL `3024`, MariaDB `1969`); `details.connectionCode` is
1665
+ `STATEMENT_TIMEOUT` there instead of `ETIMEDOUT`, the plan targets the query (`lint` →
1666
+ `explain` → re-run with `--statement-timeout <ms>`) rather than `doctor`, and no
1667
+ `branches` / `branchFork` / `verify` is emitted. PostgreSQL `57014` only counts as a
1668
+ statement timeout when the server says so — a `pg_cancel_backend()` cancel keeps the
1669
+ verbatim `Database error (57014): …` form instead of claiming a ceiling.
1670
+ The same `details.connectionCode` field distinguishes the other causes that share a
1671
+ connection code: `CONNECTION_LOST` (server closed the connection mid-session — PostgreSQL
1672
+ class 08 and `57P01`/`57P02`, MySQL `1053`), `TOO_MANY_CONNECTIONS` (PostgreSQL `53300`,
1673
+ MySQL `1040` — its plan counts current connections instead of running `doctor`, because
1674
+ rewriting host/port cannot create a slot), `SERVER_NOT_READY` (`57P03`, still starting up),
1675
+ `CONNECTION_REJECTED` (`08004` — the server answered and refused), `EHOSTUNREACH` (resolved
1676
+ but unroutable) and `TLS_ERROR` (handshake failure; reported as `CONN_UNKNOWN` rather than
1677
+ `CONN_AUTH_FAILED`, whose plan re-runs `init` for credentials that are not the problem).
1663
1678
  - `PERMISSION_DENIED` — active permission level forbids the operation.
1664
1679
  - `BLACKLIST_TABLE` / `BLACKLIST_COLUMN_WRITE` — blacklist violations.
1665
1680
  - `SNIPPET_NOT_FOUND` / `SNIPPET_AMBIGUOUS` / `SNIPPET_PARAM_MISSING` — saved-query failures.
@@ -1660,6 +1660,21 @@ shape (`schemaVersion: 1`):
1660
1660
  Recovery codes (fixed in v1.15.0):
1661
1661
  - `CONFIG_MISSING` — no `.dbcli` config; run `dbcli init`.
1662
1662
  - `CONN_REFUSED` / `CONN_AUTH_FAILED` / `CONN_TIMEOUT` / `CONN_HOST_NOT_FOUND` / `CONN_UNKNOWN` — connection failure variants.
1663
+ `CONN_TIMEOUT` also covers a statement the server canceled for exceeding the statement
1664
+ timeout (PostgreSQL `57014`, MySQL `3024`, MariaDB `1969`); `details.connectionCode` is
1665
+ `STATEMENT_TIMEOUT` there instead of `ETIMEDOUT`, the plan targets the query (`lint` →
1666
+ `explain` → re-run with `--statement-timeout <ms>`) rather than `doctor`, and no
1667
+ `branches` / `branchFork` / `verify` is emitted. PostgreSQL `57014` only counts as a
1668
+ statement timeout when the server says so — a `pg_cancel_backend()` cancel keeps the
1669
+ verbatim `Database error (57014): …` form instead of claiming a ceiling.
1670
+ The same `details.connectionCode` field distinguishes the other causes that share a
1671
+ connection code: `CONNECTION_LOST` (server closed the connection mid-session — PostgreSQL
1672
+ class 08 and `57P01`/`57P02`, MySQL `1053`), `TOO_MANY_CONNECTIONS` (PostgreSQL `53300`,
1673
+ MySQL `1040` — its plan counts current connections instead of running `doctor`, because
1674
+ rewriting host/port cannot create a slot), `SERVER_NOT_READY` (`57P03`, still starting up),
1675
+ `CONNECTION_REJECTED` (`08004` — the server answered and refused), `EHOSTUNREACH` (resolved
1676
+ but unroutable) and `TLS_ERROR` (handshake failure; reported as `CONN_UNKNOWN` rather than
1677
+ `CONN_AUTH_FAILED`, whose plan re-runs `init` for credentials that are not the problem).
1663
1678
  - `PERMISSION_DENIED` — active permission level forbids the operation.
1664
1679
  - `BLACKLIST_TABLE` / `BLACKLIST_COLUMN_WRITE` — blacklist violations.
1665
1680
  - `SNIPPET_NOT_FOUND` / `SNIPPET_AMBIGUOUS` / `SNIPPET_PARAM_MISSING` — saved-query failures.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to dbcli are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.55.0] - 2026-08-13 - Packaging: the declared runtime matches the artifact
9
+
10
+ ### Removed
11
+
12
+ - **`engines.node` — the runtime declaration now matches the artifact.** `package.json` claimed `node: ">=18.0.0"` since the first release, but measured on Node v22.17.1 against the `v1.54.1` `dist/`, only one published entry point actually loaded. `node dist/cli.mjs` threw `ERR_MODULE_NOT_FOUND` as soon as the launcher reached its dynamic import: `src/cli.ts` keeps the runtime path as a non-literal `'./cli-runtime'` so the `--version` launcher does not inline the heavy runtime, and Bun's resolver appends `.mjs` where Node's ESM resolver requires the extension. `import('dist/core.mjs')` — the `./core` subpath export — threw `Bun is not defined`, the bundle holding 30 Bun global references against `dist/cli-runtime.mjs`'s 143. The `bin` shebang is `#!/usr/bin/env bun` regardless, and the `--version` fast path branches on `import.meta.main`, which is `undefined` before Node 24. `engines` now declares `bun >= 1.3.3` alone. `dist/agent-core.mjs` was and remains Node-importable, and is documented as the one entry point that is. npm and npx stay supported as distribution channels, with the docs stating plainly that the installed executable still runs under Bun. `tests/integration/runtime-contract.test.ts` holds both ends: re-declaring `engines.node` fails unless `dist/cli.mjs` and `dist/core.mjs` really import in a bare Node process, and `dist/agent-core.mjs` must keep importing there whatever else changes. The alternative — `--target node` plus ~169 Bun API replacements in `src/` and a Node runtime CI suite — is rejected and its cost recorded in `docs/adr/0008-dbcli-is-a-bun-program-and-engines-says-so.md` (#65).
13
+
14
+ ### Added
15
+
16
+ - **An install on a machine without Bun now says so.** npm validates no `engines` field it does not recognize, so dropping the false `engines.node` would otherwise have left `npm install -g` with no signal at all — success, then a `dbcli` on `PATH` that dies on its shebang. `scripts/postinstall-check-bun.mjs` prints the reason and the Bun install command at that moment, and never fails the install. It runs as `bun … || node … || exit 0` because the primary install path is Bun-only machines, which have no `node` to invoke a postinstall with. `plugins/dbcli-agent/scripts/install-dbcli.sh` stopped falling back to npm when Bun is absent: it used to leave a non-functional `dbcli` behind, and now refuses before changing anything (#65).
17
+
18
+ ## [1.54.1] - 2026-08-13 - Error classification: a failure now says which layer broke
19
+
20
+ ### Fixed
21
+
22
+ - **A dropped connection, an exhausted connection pool, an unreachable host, and a TLS failure each say so.** `TRANSPORT_CODES` listed seven keys, so `ECONNRESET`, `EPIPE`, `EHOSTUNREACH`, `ENETUNREACH`, TLS certificate codes, PostgreSQL SQLSTATE class `08` and `53300`, and MySQL `1040` all carried a code, skipped the message-pattern fallback (which only runs when there is none), and landed on `Database error (ECONNRESET): …` with hints about `dbcli schema`. They now map to four new categories — `CONNECTION_LOST`, `TOO_MANY_CONNECTIONS`, `EHOSTUNREACH`, `TLS_ERROR` — each with the remedy that actually applies: retry a transient drop, inspect `pg_stat_activity` / `Threads_connected` against `max_connections`, check routing and VPN rather than DNS, or point `caPath` / `rejectUnauthorized` at the right certificate. PostgreSQL class `57` (`57P01` / `57P02` / `57P03`) and MySQL `1053` / `2006` / `2013` join them: restarting the server under a running query was reported as `Database error (57P01)` with hints about confirming the statement's objects. The codeless wordings drivers use for the same event — `Connection terminated unexpectedly`, `server closed the connection`, `server has gone away` — are recognized too. `57P03` (still starting up) and `08004` (the server answered and refused) get their own categories rather than borrowing a message that says the opposite, and TLS is matched by code prefix because enumerating OpenSSL's verify codes would keep missing some. The envelope states each cause in its own words instead of inheriting the shared recovery code's description — `CONN_HOST_NOT_FOUND` says the name could not be resolved, which is the one thing `EHOSTUNREACH` rules out — and TLS failures no longer route to the credentials plan, whose second step re-runs `init` for a certificate it never asks about. The adapter-code-to-envelope-code and the connection-versus-statement tables are both exhaustive `Record`s over the code union now, so a future code cannot silently inherit a generic category, and the REPL's reconnect decision reads the same table instead of its own list of codes and message substrings (#62).
23
+
24
+ - **`insert` / `update` / `delete` / `q` no longer report every failure as "failed to connect".** All four branched on `instanceof ConnectionError` and applied one message key, but that class carries every adapter-level error and only four of its nine codes are transport failures — so a missing table, a syntax error, or a statement timeout all arrived as `Failed to connect to database: …` while the connection was fine. The wording is now chosen by code, and rendering goes through the same `formatCliError` the central presenter uses, so these commands print the stable `Code:` line and the error's hints — both previously dropped on this path — exactly as `query` does (#61).
25
+
26
+ - **A statement the server canceled is no longer reported as a connection failure.** PostgreSQL `57014`, MySQL `3024`, and MariaDB `1969` fell through to `UNKNOWN` / `CONN_UNKNOWN`, so the CLI answered a query that ran out of statement time with connection-troubleshooting hints and a recovery plan that opened with `dbcli doctor` — the one thing that was not broken. They now map to a `STATEMENT_TIMEOUT` adapter code whose hints point at the query (`dbcli lint`, `dbcli explain`, re-run with an explicit `--statement-timeout <ms>`). The `--recovery` envelope keeps `schemaVersion` 1 and reports `CONN_TIMEOUT` with `details.connectionCode: "STATEMENT_TIMEOUT"`; that field selects the query-oriented plan, replaces the network-flavored message with one that states the ceiling that was in force, and suppresses both the `doctor-*` branches — whose `branchFork.after: 1` assumed step 1 was `doctor` — and the `verify` step, since nothing verifies this error except re-running the statement, which only the caller has. PostgreSQL `57014` is `query_canceled`, not only `statement_timeout`, so a `pg_cancel_backend()` or recovery-conflict cancel keeps the verbatim `Database error (57014): …` form rather than asserting a ceiling nobody set.
27
+
8
28
  ## [1.54.0] - 2026-08-13 - Query engine hardening: timeout semantics, load-on-demand, deterministic builds
9
29
 
10
30
  ### Added
package/README.dev.md CHANGED
@@ -73,20 +73,22 @@ After publishing:
73
73
 
74
74
  1. **Global install:**
75
75
  ```bash
76
- npm install -g @carllee1983/dbcli
76
+ bun install -g @carllee1983/dbcli
77
77
  which dbcli
78
78
  dbcli --version
79
79
  ```
80
+ `npm install -g` also works, but the installed executable still runs under Bun via its
81
+ `#!/usr/bin/env bun` shebang — verify on a machine that has Bun on `PATH`.
80
82
 
81
- 2. **Zero-install (npx / bunx):**
83
+ 2. **Zero-install (bunx / npx):**
82
84
  ```bash
83
85
  cd /tmp && mkdir -p test-dbcli && cd test-dbcli
84
- npx @carllee1983/dbcli --help
85
- npx @carllee1983/dbcli --version
86
- # or: bunx @carllee1983/dbcli --help
86
+ bunx @carllee1983/dbcli --help
87
+ bunx @carllee1983/dbcli --version
88
+ # or: npx @carllee1983/dbcli --help
87
89
  ```
88
90
 
89
- 3. **Windows (if available):** `npm install -g @carllee1983/dbcli`, then `dbcli --help`. npm creates the `.cmd` stub for the `bin` entry; no hand-written `.cmd` in the repo.
91
+ 3. **Windows (if available):** `npm install -g @carllee1983/dbcli`, then `dbcli --help`. npm creates the `.cmd` stub for the `bin` entry; no hand-written `.cmd` in the repo. Bun must be on `PATH` there too.
90
92
 
91
93
  ### Rollback (if needed)
92
94
 
@@ -104,8 +106,9 @@ Then ship a patch version with the fix. Prefer **deprecate** over **unpublish**
104
106
 
105
107
  - **`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.
106
108
  - **`prepublishOnly`:** `bun run build` so `dist/cli.mjs` matches current source.
107
- - **`engines`:** Declares `node >= 18.0.0` and `bun >= 1.3.3` so npm can warn on outdated runtimes.
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/0008-dbcli-is-a-bun-program-and-engines-says-so.md`.
108
110
  - **Shebang:** `scripts/build.ts` prepends `#!/usr/bin/env bun` to `dist/cli.mjs`; the `bin` field in `package.json` points at that file.
111
+ - **`postinstall`:** `scripts/postinstall-check-bun.mjs` warns (never fails) when Bun is missing after install — npm ignores `engines.bun`, so this is the only signal an npm-only machine gets before `dbcli` refuses to start. The script command is `bun … || node … || exit 0` so it runs 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.
109
112
  - **Live DB tests:** `tests/integration/live-db.test.ts` uses project `.dbcli` by default or `LIVE_DB_CONFIG_PATH` when you point at another config directory. Set `SKIP_INTEGRATION_TESTS=true` to skip all integration tests.
110
113
 
111
114
  For contributor workflow and release process, see **[CONTRIBUTING.md](./CONTRIBUTING.md)** and the main **[README.md](./README.md)** Development section.
package/README.md CHANGED
@@ -38,16 +38,21 @@ All messages, help text, error messages, and command output respond to the langu
38
38
  #### Global Installation (Recommended)
39
39
 
40
40
  ```bash
41
- npm install -g @carllee1983/dbcli
42
- # or: bun install -g @carllee1983/dbcli
41
+ bun install -g @carllee1983/dbcli
42
+ # or: npm install -g @carllee1983/dbcli
43
43
  ```
44
44
 
45
+ **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
47
+ `curl -fsSL https://bun.sh/install | bash`. Only the `./agent-core` subpath export is
48
+ importable from a plain Node process.
49
+
45
50
  #### Zero-Install (No Installation Needed)
46
51
 
47
52
  ```bash
48
- npx @carllee1983/dbcli init
49
- npx @carllee1983/dbcli query "SELECT * FROM users"
50
- # or with Bun: bunx @carllee1983/dbcli init
53
+ bunx @carllee1983/dbcli init
54
+ bunx @carllee1983/dbcli query "SELECT * FROM users"
55
+ # or with npm: npx @carllee1983/dbcli init
51
56
  ```
52
57
 
53
58
  #### Update
@@ -1324,9 +1329,11 @@ After installation, the AI agent will have access to dbcli commands and can use
1324
1329
 
1325
1330
  ### Platform-Specific Setup
1326
1331
 
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.
1333
+
1327
1334
  #### Claude Code (Anthropic)
1328
1335
 
1329
- 1. Install dbcli globally: `npm install -g @carllee1983/dbcli`
1336
+ 1. Install dbcli globally: `bun install -g @carllee1983/dbcli`
1330
1337
  2. Initialize: `dbcli init` (choose permission level)
1331
1338
  3. Install skill: `dbcli skill --install claude`
1332
1339
  4. Restart Claude Code extension
@@ -1338,7 +1345,7 @@ After installation, the AI agent will have access to dbcli commands and can use
1338
1345
 
1339
1346
  #### Gemini CLI (Google)
1340
1347
 
1341
- 1. Install dbcli globally: `npm install -g @carllee1983/dbcli`
1348
+ 1. Install dbcli globally: `bun install -g @carllee1983/dbcli`
1342
1349
  2. Initialize: `dbcli init`
1343
1350
  3. Install skill: `dbcli skill --install gemini`
1344
1351
  4. Start Gemini: `gemini start`
@@ -1350,7 +1357,7 @@ After installation, the AI agent will have access to dbcli commands and can use
1350
1357
 
1351
1358
  #### GitHub Copilot CLI
1352
1359
 
1353
- 1. Install dbcli globally: `npm install -g @carllee1983/dbcli`
1360
+ 1. Install dbcli globally: `bun install -g @carllee1983/dbcli`
1354
1361
  2. Initialize: `dbcli init`
1355
1362
  3. Install skill: `dbcli skill --install copilot`
1356
1363
  4. Install Copilot CLI: `npm install -g @github-next/github-copilot-cli`
@@ -1362,7 +1369,7 @@ After installation, the AI agent will have access to dbcli commands and can use
1362
1369
 
1363
1370
  #### Cursor IDE
1364
1371
 
1365
- 1. Install dbcli globally: `npm install -g @carllee1983/dbcli`
1372
+ 1. Install dbcli globally: `bun install -g @carllee1983/dbcli`
1366
1373
  2. Initialize: `dbcli init`
1367
1374
  3. Install skill: `dbcli skill --install cursor`
1368
1375
  4. Open Cursor editor
@@ -1378,7 +1385,7 @@ After installation, the AI agent will have access to dbcli commands and can use
1378
1385
 
1379
1386
  1. Add the marketplace: `codex plugin marketplace add CarlLee1983/dbcli`.
1380
1387
  2. Open `/plugins`, select the dbcli Agent marketplace, and install `dbcli-agent`.
1381
- 3. For a persistent CLI, install dbcli globally: `bun install -g @carllee1983/dbcli` or `npm install -g @carllee1983/dbcli`.
1388
+ 3. For a persistent CLI, install dbcli globally: `bun install -g @carllee1983/dbcli` (or `npm install -g`, which still needs Bun on `PATH`).
1382
1389
  4. Without a global install, the plugin skill uses `bunx @carllee1983/dbcli <command>` as its fallback.
1383
1390
  5. Initialize: `dbcli init` or `bunx @carllee1983/dbcli init`.
1384
1391
 
@@ -1394,7 +1401,7 @@ After installation, the AI agent will have access to dbcli commands and can use
1394
1401
 
1395
1402
  ```bash
1396
1403
  # 1. Install and initialize
1397
- npm install -g @carllee1983/dbcli
1404
+ bun install -g @carllee1983/dbcli
1398
1405
  dbcli init # Choose "query-only" for safety
1399
1406
 
1400
1407
  # 2. Install skill to Claude Code
@@ -1590,16 +1597,16 @@ dbcli query "SELECT * FROM users LIMIT 100 OFFSET 100"
1590
1597
 
1591
1598
  #### "CLI startup takes 30+ seconds (first run)"
1592
1599
 
1593
- npx is downloading and caching package.
1600
+ bunx (or npx) is downloading and caching the package.
1594
1601
 
1595
1602
  **Solution:** This is normal on first run. Subsequent runs are instant:
1596
1603
 
1597
1604
  ```bash
1598
- npx @carllee1983/dbcli init # First run: 30s (downloads)
1599
- npx @carllee1983/dbcli init # Second run: <1s (cached)
1605
+ bunx @carllee1983/dbcli init # First run: 30s (downloads)
1606
+ bunx @carllee1983/dbcli init # Second run: <1s (cached)
1600
1607
 
1601
1608
  # Or install globally for faster startup
1602
- npm install -g @carllee1983/dbcli
1609
+ bun install -g @carllee1983/dbcli
1603
1610
  dbcli init # All future runs: <1s
1604
1611
  ```
1605
1612
 
@@ -1609,15 +1616,19 @@ dbcli init # All future runs: <1s
1609
1616
 
1610
1617
  #### Windows: "Command not found: dbcli"
1611
1618
 
1612
- npm .cmd wrapper not created or PATH not updated.
1619
+ The shim was not created, PATH was not updated, or Bun is missing — `dbcli` runs under
1620
+ Bun on Windows too.
1613
1621
 
1614
1622
  **Solutions:**
1615
1623
 
1616
1624
  ```bash
1625
+ # Confirm Bun is installed and on PATH first
1626
+ bun --version
1627
+
1617
1628
  # Restart terminal to refresh PATH
1618
1629
  # OR reinstall globally
1619
- npm uninstall -g @carllee1983/dbcli
1620
- npm install -g @carllee1983/dbcli
1630
+ bun uninstall -g @carllee1983/dbcli
1631
+ bun install -g @carllee1983/dbcli
1621
1632
 
1622
1633
  # Verify installation
1623
1634
  where dbcli # Windows command to find executable
@@ -1647,8 +1658,11 @@ chmod +x dist/cli.mjs
1647
1658
 
1648
1659
  ### Runtime
1649
1660
 
1650
- - **Node.js:** 18.0.0+
1651
- - **Bun:** 1.3.3+
1661
+ - **Bun:** 1.3.3+ — required. The published bundles use Bun APIs and Bun module
1662
+ resolution; `dbcli` does not run on Node.
1663
+ - **Node.js:** only for the `./agent-core` subpath export, which is Node-importable
1664
+ (18.0.0+). `npm install -g` / `npx` work as distribution channels but still shell out
1665
+ to Bun at run time.
1652
1666
 
1653
1667
  ### Platforms
1654
1668
 
package/README.zh-TW.md CHANGED
@@ -38,16 +38,20 @@ dbcli init
38
38
  #### 全域安裝(建議)
39
39
 
40
40
  ```bash
41
- npm install -g @carllee1983/dbcli
42
- # 或使用 Bunbun install -g @carllee1983/dbcli
41
+ bun install -g @carllee1983/dbcli
42
+ # 或使用 npmnpm install -g @carllee1983/dbcli
43
43
  ```
44
44
 
45
+ **dbcli 執行於 Bun。** npm 與 npx 可作為發布通道,但安裝後的 `dbcli` 執行檔需要 `PATH`
46
+ 上有 Bun 1.3.3+,請先以 `curl -fsSL https://bun.sh/install | bash` 安裝。只有
47
+ `./agent-core` 這個 subpath export 能被純 Node 程序 import。
48
+
45
49
  #### 免安裝(無需事先安裝)
46
50
 
47
51
  ```bash
48
- npx @carllee1983/dbcli init
49
- npx @carllee1983/dbcli query "SELECT * FROM users"
50
- # 或使用 Bunbunx @carllee1983/dbcli init
52
+ bunx @carllee1983/dbcli init
53
+ bunx @carllee1983/dbcli query "SELECT * FROM users"
54
+ # 或使用 npmnpx @carllee1983/dbcli init
51
55
  ```
52
56
 
53
57
  #### 更新
@@ -1214,9 +1218,11 @@ dbcli skill --install cursor
1214
1218
 
1215
1219
  ### 各平台設定
1216
1220
 
1221
+ 以下所有流程都假設 `PATH` 上有 Bun 1.3.3+:無論用哪個套件管理器安裝,`dbcli` 執行檔都以 Bun 執行。
1222
+
1217
1223
  #### Claude Code(Anthropic)
1218
1224
 
1219
- 1. 全域安裝 dbcli:`npm install -g @carllee1983/dbcli`
1225
+ 1. 全域安裝 dbcli:`bun install -g @carllee1983/dbcli`
1220
1226
  2. 初始化:`dbcli init`(選擇權限等級)
1221
1227
  3. 安裝 skill:`dbcli skill --install claude`
1222
1228
  4. 重新啟動 Claude Code 擴充
@@ -1228,7 +1234,7 @@ dbcli skill --install cursor
1228
1234
 
1229
1235
  #### Gemini CLI(Google)
1230
1236
 
1231
- 1. 全域安裝:`npm install -g @carllee1983/dbcli`
1237
+ 1. 全域安裝:`bun install -g @carllee1983/dbcli`
1232
1238
  2. 初始化:`dbcli init`
1233
1239
  3. 安裝 skill:`dbcli skill --install gemini`
1234
1240
  4. 啟動 Gemini:`gemini start`
@@ -1240,7 +1246,7 @@ dbcli skill --install cursor
1240
1246
 
1241
1247
  #### GitHub Copilot CLI
1242
1248
 
1243
- 1. 全域安裝:`npm install -g @carllee1983/dbcli`
1249
+ 1. 全域安裝:`bun install -g @carllee1983/dbcli`
1244
1250
  2. 初始化:`dbcli init`
1245
1251
  3. 安裝 skill:`dbcli skill --install copilot`
1246
1252
  4. 安裝 Copilot CLI:`npm install -g @github-next/github-copilot-cli`
@@ -1252,7 +1258,7 @@ dbcli skill --install cursor
1252
1258
 
1253
1259
  #### Cursor IDE
1254
1260
 
1255
- 1. 全域安裝:`npm install -g @carllee1983/dbcli`
1261
+ 1. 全域安裝:`bun install -g @carllee1983/dbcli`
1256
1262
  2. 初始化:`dbcli init`
1257
1263
  3. 安裝 skill:`dbcli skill --install cursor`
1258
1264
  4. 開啟 Cursor
@@ -1268,7 +1274,7 @@ dbcli skill --install cursor
1268
1274
 
1269
1275
  1. 加入 marketplace:`codex plugin marketplace add CarlLee1983/dbcli`。
1270
1276
  2. 開啟 `/plugins`,選擇 dbcli Agent marketplace,並安裝 `dbcli-agent`。
1271
- 3. 若要常駐 CLI,請全域安裝 dbcli:`bun install -g @carllee1983/dbcli` `npm install -g @carllee1983/dbcli`。
1277
+ 3. 若要常駐 CLI,請全域安裝 dbcli:`bun install -g @carllee1983/dbcli`(或 `npm install -g`,但仍需 `PATH` 上有 Bun)。
1272
1278
  4. 若未全域安裝,plugin 內的 skill 會以 `bunx @carllee1983/dbcli <command>` 作為 fallback。
1273
1279
  5. 初始化:`dbcli init` 或 `bunx @carllee1983/dbcli init`。
1274
1280
 
@@ -1284,7 +1290,7 @@ dbcli skill --install cursor
1284
1290
 
1285
1291
  ```bash
1286
1292
  # 1. 安裝並初始化
1287
- npm install -g @carllee1983/dbcli
1293
+ bun install -g @carllee1983/dbcli
1288
1294
  dbcli init # 選擇「query-only」較安全
1289
1295
 
1290
1296
  # 2. 為 Claude Code 安裝 skill
@@ -1471,16 +1477,16 @@ dbcli query "SELECT * FROM users LIMIT 100 OFFSET 100"
1471
1477
 
1472
1478
  #### 「第一次執行 CLI 超過 30 秒」
1473
1479
 
1474
- npx 正在下載並快取套件。
1480
+ bunx(或 npx)正在下載並快取套件。
1475
1481
 
1476
1482
  **處理方式:** 首次較慢屬正常,之後會很快:
1477
1483
 
1478
1484
  ```bash
1479
- npx @carllee1983/dbcli init # 首次約 30s
1480
- npx @carllee1983/dbcli init # 之後 <1s
1485
+ bunx @carllee1983/dbcli init # 首次約 30s
1486
+ bunx @carllee1983/dbcli init # 之後 <1s
1481
1487
 
1482
1488
  # 或全域安裝
1483
- npm install -g @carllee1983/dbcli
1489
+ bun install -g @carllee1983/dbcli
1484
1490
  dbcli init
1485
1491
  ```
1486
1492
 
@@ -1490,15 +1496,18 @@ dbcli init
1490
1496
 
1491
1497
  #### Windows:「Command not found: dbcli」
1492
1498
 
1493
- npm 未建立 .cmd PATH 未更新。
1499
+ shim 未建立、PATH 未更新,或機器上沒有 Bun —— `dbcli` 在 Windows 上同樣以 Bun 執行。
1494
1500
 
1495
1501
  **處理方式:**
1496
1502
 
1497
1503
  ```bash
1504
+ # 先確認 Bun 已安裝且在 PATH 上
1505
+ bun --version
1506
+
1498
1507
  # 重開終端機以更新 PATH
1499
1508
  # 或重新全域安裝
1500
- npm uninstall -g @carllee1983/dbcli
1501
- npm install -g @carllee1983/dbcli
1509
+ bun uninstall -g @carllee1983/dbcli
1510
+ bun install -g @carllee1983/dbcli
1502
1511
 
1503
1512
  where dbcli
1504
1513
  ```
@@ -1527,8 +1536,10 @@ chmod +x dist/cli.mjs
1527
1536
 
1528
1537
  ### 執行環境
1529
1538
 
1530
- - **Node.js:** 18.0.0+
1531
- - **Bun:** 1.3.3+
1539
+ - **Bun:** 1.3.3+ —— 必要。發布的 bundle 使用 Bun API 與 Bun 的模組解析,`dbcli`
1540
+ 無法在 Node 上執行。
1541
+ - **Node.js:** 僅 `./agent-core` 這個 subpath export 可被 Node import(18.0.0+)。
1542
+ `npm install -g` / `npx` 可作為發布通道,但執行時仍會轉交給 Bun。
1532
1543
 
1533
1544
  ### 平台
1534
1545
 
@@ -1660,6 +1660,21 @@ shape (`schemaVersion: 1`):
1660
1660
  Recovery codes (fixed in v1.15.0):
1661
1661
  - `CONFIG_MISSING` — no `.dbcli` config; run `dbcli init`.
1662
1662
  - `CONN_REFUSED` / `CONN_AUTH_FAILED` / `CONN_TIMEOUT` / `CONN_HOST_NOT_FOUND` / `CONN_UNKNOWN` — connection failure variants.
1663
+ `CONN_TIMEOUT` also covers a statement the server canceled for exceeding the statement
1664
+ timeout (PostgreSQL `57014`, MySQL `3024`, MariaDB `1969`); `details.connectionCode` is
1665
+ `STATEMENT_TIMEOUT` there instead of `ETIMEDOUT`, the plan targets the query (`lint` →
1666
+ `explain` → re-run with `--statement-timeout <ms>`) rather than `doctor`, and no
1667
+ `branches` / `branchFork` / `verify` is emitted. PostgreSQL `57014` only counts as a
1668
+ statement timeout when the server says so — a `pg_cancel_backend()` cancel keeps the
1669
+ verbatim `Database error (57014): …` form instead of claiming a ceiling.
1670
+ The same `details.connectionCode` field distinguishes the other causes that share a
1671
+ connection code: `CONNECTION_LOST` (server closed the connection mid-session — PostgreSQL
1672
+ class 08 and `57P01`/`57P02`, MySQL `1053`), `TOO_MANY_CONNECTIONS` (PostgreSQL `53300`,
1673
+ MySQL `1040` — its plan counts current connections instead of running `doctor`, because
1674
+ rewriting host/port cannot create a slot), `SERVER_NOT_READY` (`57P03`, still starting up),
1675
+ `CONNECTION_REJECTED` (`08004` — the server answered and refused), `EHOSTUNREACH` (resolved
1676
+ but unroutable) and `TLS_ERROR` (handshake failure; reported as `CONN_UNKNOWN` rather than
1677
+ `CONN_AUTH_FAILED`, whose plan re-runs `init` for credentials that are not the problem).
1663
1678
  - `PERMISSION_DENIED` — active permission level forbids the operation.
1664
1679
  - `BLACKLIST_TABLE` / `BLACKLIST_COLUMN_WRITE` — blacklist violations.
1665
1680
  - `SNIPPET_NOT_FOUND` / `SNIPPET_AMBIGUOUS` / `SNIPPET_PARAM_MISSING` — saved-query failures.