@carllee1983/dbcli 1.10.0 → 1.10.1

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli.mjs +116 -96
  3. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,22 @@ 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.10.1] - 2026-05-08
9
+
10
+ ### Fixed
11
+
12
+ - **Packaged `dist/cli.mjs` 找不到 assets**:1.10.0 bundle 在 `task-paths.ts` / `snippet-paths.ts` 用 `import.meta.dir + ../../../` 解析 builtin 目錄,bundle 後三層往上會跳出 package root,npm 全域安裝的使用者執行 `dbcli queries list` / `dbcli skill tasks list` 讀不到資源。抽出 `src/utils/package-root.ts` 以 `package.json` 走訪定位 root,dev 與 bundle 都正確;`skill.ts` 內既有的 `findPackageRoot` 也收斂到同一處。
13
+ - **`dbcli q` 略過 blacklist 檢查(安全)**:`q.ts` 把空字串當作 `tableName` 傳給 `BlacklistValidator.filterColumns`,column-level redaction 永遠不命中;同時也沒呼叫 `checkTableBlacklist`,使用者可以透過 saved snippet 直接 SELECT 黑名單表/欄位繞開保護。改為從 `prepared.rewrittenSql` 抽出主表(SQL)或 `prepared.execHints.index`(ES),執行前先 `checkTableBlacklist('SELECT', target)`,並把真正的 `tableName` 餵給 `filterColumns`;Redis 維持原樣。
14
+
15
+ ### Added
16
+
17
+ - **dist/ 整合 smoke 測試**:`tests/integration/dist-smoke.test.ts` 從 OS tmpdir 執行 `dist/cli.mjs`,覆蓋 `--version`、`skill --output`、`queries list`、`skill tasks list`,守住 packaged assets path 不再回退。
18
+ - **`q` blacklist 迴歸測試**:`tests/unit/commands/q-blacklist.test.ts` 覆蓋黑名單表阻擋、欄位 redact、未受影響 snippet 三種情境。
19
+
20
+ ### Changed
21
+
22
+ - **Lint release-blocking**:`bun run lint` / `lint:fix` 加上 `--max-warnings=0`;同時清掉 45 個 `@typescript-eslint/no-explicit-any` warnings(以正型替代為主,`elasticsearch-adapter.ts` 因刻意不引入 `@elastic/elasticsearch` SDK 而以檔案層 `eslint-disable` 標註理由)。任何新 warning 從此會擋住 release。
23
+
8
24
  ## [1.10.0] - 2026-05-08
9
25
 
10
26
  ### Added
package/dist/cli.mjs CHANGED
@@ -78734,26 +78734,55 @@ var init_runner = __esm(() => {
78734
78734
  init_types2();
78735
78735
  });
78736
78736
 
78737
+ // src/utils/package-root.ts
78738
+ import * as path from "path";
78739
+ function findPackageRoot() {
78740
+ if (cached)
78741
+ return cached;
78742
+ let dir = HERE;
78743
+ for (let i = 0;i < 6; i++) {
78744
+ if (Bun.file(path.join(dir, "package.json")).size > 0) {
78745
+ cached = dir;
78746
+ return dir;
78747
+ }
78748
+ const parent = path.dirname(dir);
78749
+ if (parent === dir)
78750
+ break;
78751
+ dir = parent;
78752
+ }
78753
+ cached = path.resolve(HERE, "..", "..");
78754
+ return cached;
78755
+ }
78756
+ function packageAssetPath(...segments) {
78757
+ return path.join(findPackageRoot(), "assets", ...segments);
78758
+ }
78759
+ var cached = null, HERE;
78760
+ var init_package_root = __esm(() => {
78761
+ HERE = import.meta.dir;
78762
+ });
78763
+
78737
78764
  // src/core/saved-queries/snippet-paths.ts
78738
- import { join as join11, resolve as resolve2 } from "path";
78765
+ import { join as join12 } from "path";
78739
78766
  function resolveBuiltinDir() {
78740
- return resolve2(import.meta.dir, "..", "..", "..", "assets", "snippets");
78767
+ return packageAssetPath("snippets");
78741
78768
  }
78742
78769
  function resolveSnippetDirs(workspaceRoot) {
78743
78770
  return {
78744
78771
  builtinDir: resolveBuiltinDir(),
78745
- sharedDir: join11(workspaceRoot, ".dbcli-shared", "queries"),
78746
- localDir: join11(workspaceRoot, ".dbcli", "queries")
78772
+ sharedDir: join12(workspaceRoot, ".dbcli-shared", "queries"),
78773
+ localDir: join12(workspaceRoot, ".dbcli", "queries")
78747
78774
  };
78748
78775
  }
78749
78776
  function snippetKeyToFile(workspaceRoot, key2, source) {
78750
78777
  const rel = key2.replace(/^@/, "") + ".sql";
78751
78778
  if (source === "builtin")
78752
- return join11(resolveBuiltinDir(), rel);
78779
+ return join12(resolveBuiltinDir(), rel);
78753
78780
  const dir = source === "shared" ? ".dbcli-shared/queries" : ".dbcli/queries";
78754
- return join11(workspaceRoot, dir, rel);
78781
+ return join12(workspaceRoot, dir, rel);
78755
78782
  }
78756
- var init_snippet_paths = () => {};
78783
+ var init_snippet_paths = __esm(() => {
78784
+ init_package_root();
78785
+ });
78757
78786
 
78758
78787
  // src/core/saved-queries/engine-map.ts
78759
78788
  function mapSystemToEngine(system) {
@@ -78827,7 +78856,7 @@ __export(exports_queries_rename, {
78827
78856
  queriesRename: () => queriesRename
78828
78857
  });
78829
78858
  import { rename, mkdir } from "fs/promises";
78830
- import { dirname } from "path";
78859
+ import { dirname as dirname2 } from "path";
78831
78860
  async function queriesRename(oldName, newName, options = {}) {
78832
78861
  if (!oldName.startsWith("@") || !newName.startsWith("@")) {
78833
78862
  throw new Error(`Both names must start with '@'`);
@@ -78848,7 +78877,7 @@ async function queriesRename(oldName, newName, options = {}) {
78848
78877
  for (const v of local) {
78849
78878
  const dst = snippetKeyToFile(cwd, newName, "local");
78850
78879
  const dstWithSuffix = preserveEngineSuffix(v.query.file, dst);
78851
- await mkdir(dirname(dstWithSuffix), { recursive: true });
78880
+ await mkdir(dirname2(dstWithSuffix), { recursive: true });
78852
78881
  await rename(v.query.file, dstWithSuffix);
78853
78882
  await rewriteFrontmatterName(dstWithSuffix, newName.slice(1));
78854
78883
  console.log(`renamed ${v.query.file} \u2192 ${dstWithSuffix}`);
@@ -78875,7 +78904,7 @@ __export(exports_queries_copy, {
78875
78904
  queriesCopy: () => queriesCopy
78876
78905
  });
78877
78906
  import { mkdir as mkdir2, copyFile } from "fs/promises";
78878
- import { dirname as dirname2, basename as basename2 } from "path";
78907
+ import { dirname as dirname3, basename as basename2 } from "path";
78879
78908
  async function queriesCopy(src, dst, options = {}) {
78880
78909
  if (!src.startsWith("@") || !dst.startsWith("@")) {
78881
78910
  throw new Error(`Both names must start with '@'`);
@@ -78893,7 +78922,7 @@ async function queriesCopy(src, dst, options = {}) {
78893
78922
  }
78894
78923
  for (const v of variants) {
78895
78924
  const dstFile = mapEngineSuffix(v.query.file, snippetKeyToFile(cwd, dst, "local"));
78896
- await mkdir2(dirname2(dstFile), { recursive: true });
78925
+ await mkdir2(dirname3(dstFile), { recursive: true });
78897
78926
  await copyFile(v.query.file, dstFile);
78898
78927
  console.log(`copied ${v.query.file} \u2192 ${dstFile}`);
78899
78928
  }
@@ -78912,7 +78941,7 @@ __export(exports_queries_import, {
78912
78941
  queriesImport: () => queriesImport
78913
78942
  });
78914
78943
  import { stat, mkdir as mkdir3, copyFile as copyFile2 } from "fs/promises";
78915
- import { basename as basename3, join as join12, extname } from "path";
78944
+ import { basename as basename3, join as join13, extname } from "path";
78916
78945
  async function queriesImport(filePath, options = {}) {
78917
78946
  const cwd = options.cwd ?? process.cwd();
78918
78947
  if (extname(filePath) !== ".sql") {
@@ -78923,9 +78952,9 @@ async function queriesImport(filePath, options = {}) {
78923
78952
  const baseName = options.as ? options.as.replace(/^@/, "") : basename3(filePath, ".sql").replace(/\.(postgres|mysql)$/, "");
78924
78953
  const key2 = "@" + baseName;
78925
78954
  parseSavedQuery({ key: key2, file: filePath, source: "local", text: text2 });
78926
- const targetDir = join12(cwd, ".dbcli/queries");
78955
+ const targetDir = join13(cwd, ".dbcli/queries");
78927
78956
  await mkdir3(targetDir, { recursive: true });
78928
- const target = join12(targetDir, basename3(filePath));
78957
+ const target = join13(targetDir, basename3(filePath));
78929
78958
  if (await Bun.file(target).exists()) {
78930
78959
  if (!options.force) {
78931
78960
  const ok = await esm_default4({ message: `Overwrite ${target}?`, default: false });
@@ -78995,7 +79024,7 @@ var {
78995
79024
  // package.json
78996
79025
  var package_default = {
78997
79026
  name: "@carllee1983/dbcli",
78998
- version: "1.10.0",
79027
+ version: "1.10.1",
78999
79028
  description: "Database CLI for AI agents",
79000
79029
  type: "module",
79001
79030
  publishConfig: {
@@ -79046,8 +79075,8 @@ var package_default = {
79046
79075
  "test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration/adapters; docker compose -f docker-compose.test.yml down",
79047
79076
  typecheck: "tsc --noEmit --pretty false",
79048
79077
  "test:perf": "bun test ./tests/perf/*.bench.ts",
79049
- lint: "eslint src tests --ext .ts",
79050
- "lint:fix": "eslint src tests --ext .ts --fix",
79078
+ lint: "eslint src tests --ext .ts --max-warnings=0",
79079
+ "lint:fix": "eslint src tests --ext .ts --fix --max-warnings=0",
79051
79080
  format: 'prettier --write "src/**/*.ts" "tests/**/*.ts"'
79052
79081
  },
79053
79082
  dependencies: {
@@ -84767,18 +84796,22 @@ async function qCommand(name, options, command) {
84767
84796
  }));
84768
84797
  return;
84769
84798
  }
84799
+ const blacklistManager = new BlacklistManager(config);
84800
+ const blacklistValidator = new BlacklistValidator(blacklistManager);
84801
+ const family = engineFamily(engine);
84802
+ const targetName = family === "sql" ? extractTableName(prepared.rewrittenSql) ?? "" : family === "es" ? prepared.execHints?.index ?? "" : "";
84803
+ if (family !== "redis" && targetName) {
84804
+ blacklistValidator.checkTableBlacklist("SELECT", targetName);
84805
+ }
84770
84806
  const adapter = AdapterFactory.createAdapter(config.connection);
84771
84807
  await adapter.connect();
84772
84808
  try {
84773
- const blacklistManager = new BlacklistManager(config);
84774
- const blacklistValidator = new BlacklistValidator(blacklistManager);
84775
- const family = engineFamily(engine);
84776
84809
  const indexParams = family === "es" && prepared.execHints?.index ? [prepared.execHints.index] : [];
84777
84810
  const start = performance.now();
84778
84811
  const result = await adapter.execute(prepared.driver.sql, family === "sql" ? prepared.driver.values : indexParams);
84779
84812
  const executionTimeMs = Math.round(performance.now() - start);
84780
84813
  const columnNames = result.rows[0] ? Object.keys(result.rows[0]) : [];
84781
- const filtered = family === "redis" ? { filteredRows: result.rows, omittedColumns: [] } : blacklistValidator.filterColumns("", result.rows, columnNames);
84814
+ const filtered = family === "redis" ? { filteredRows: result.rows, omittedColumns: [] } : blacklistValidator.filterColumns(targetName, result.rows, columnNames);
84782
84815
  const formatter = new QueryResultFormatter;
84783
84816
  const out = formatter.format({
84784
84817
  rows: filtered.filteredRows,
@@ -84790,7 +84823,7 @@ async function qCommand(name, options, command) {
84790
84823
  statement: "SELECT",
84791
84824
  affectedRows: 0,
84792
84825
  ...filtered.omittedColumns.length > 0 ? {
84793
- securityNotification: blacklistValidator.buildSecurityNotification("", filtered.omittedColumns)
84826
+ securityNotification: blacklistValidator.buildSecurityNotification(targetName, filtered.omittedColumns)
84794
84827
  } : {}
84795
84828
  }
84796
84829
  }, { format: options.format ?? "table" });
@@ -84812,10 +84845,10 @@ function parseCliParams(list) {
84812
84845
  }
84813
84846
  return out;
84814
84847
  }
84815
- async function readParamFile(path) {
84816
- if (!path)
84848
+ async function readParamFile(path2) {
84849
+ if (!path2)
84817
84850
  return {};
84818
- const text2 = await Bun.file(path).text();
84851
+ const text2 = await Bun.file(path2).text();
84819
84852
  const parsed = JSON.parse(text2);
84820
84853
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
84821
84854
  throw new Error(`--param-file must be a JSON object (got ${typeof parsed})`);
@@ -84845,7 +84878,7 @@ function handleQError(error) {
84845
84878
 
84846
84879
  // src/commands/queries.ts
84847
84880
  import { mkdir as mkdir4, writeFile as writeFile2 } from "fs/promises";
84848
- import { dirname as dirname3 } from "path";
84881
+ import { dirname as dirname4 } from "path";
84849
84882
  import { spawn } from "child_process";
84850
84883
  init_saved_queries();
84851
84884
  async function deriveEngine() {
@@ -84975,7 +85008,7 @@ async function queriesNew(name, options) {
84975
85008
  process.exit(1);
84976
85009
  return;
84977
85010
  }
84978
- await mkdir4(dirname3(file), { recursive: true });
85011
+ await mkdir4(dirname4(file), { recursive: true });
84979
85012
  await writeFile2(file, scaffold(name), "utf8");
84980
85013
  console.log(`Created ${file}`);
84981
85014
  if (source === "shared")
@@ -85102,10 +85135,10 @@ queriesCommand.command("copy <src> <dst>").description(t("queries.copy_descripti
85102
85135
  process.exit(1);
85103
85136
  }
85104
85137
  });
85105
- queriesCommand.command("import <path>").description(t("queries.import_description")).option("--force", "Overwrite existing file without prompting").option("--as <name>", "Override snippet name (defaults to filename)").action(async (path, options) => {
85138
+ queriesCommand.command("import <path>").description(t("queries.import_description")).option("--force", "Overwrite existing file without prompting").option("--as <name>", "Override snippet name (defaults to filename)").action(async (path2, options) => {
85106
85139
  try {
85107
85140
  const { queriesImport: queriesImport2 } = await Promise.resolve().then(() => (init_queries_import(), exports_queries_import));
85108
- await queriesImport2(path, options);
85141
+ await queriesImport2(path2, options);
85109
85142
  } catch (e) {
85110
85143
  console.error(e.message);
85111
85144
  process.exit(1);
@@ -85626,18 +85659,17 @@ function parseWhereClause(whereClause) {
85626
85659
  if (valueStr === undefined || column === undefined) {
85627
85660
  throw new Error(`Cannot parse WHERE clause: "${part}". Use format "column=value" or "col1=val1 AND col2=val2"`);
85628
85661
  }
85629
- let value = valueStr.trim();
85630
- if (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"')) {
85631
- value = value.slice(1, -1);
85632
- }
85633
- if (!isNaN(value) && value !== "") {
85634
- value = Number(value);
85662
+ const trimmed = valueStr.trim();
85663
+ const stripped = trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1) : trimmed;
85664
+ let value = stripped;
85665
+ if (stripped !== "" && !isNaN(Number(stripped))) {
85666
+ value = Number(stripped);
85635
85667
  }
85636
- if (value === "true")
85668
+ if (stripped === "true")
85637
85669
  value = true;
85638
- if (value === "false")
85670
+ else if (stripped === "false")
85639
85671
  value = false;
85640
- if (value === "null")
85672
+ else if (stripped === "null")
85641
85673
  value = null;
85642
85674
  conditions[column] = value;
85643
85675
  }
@@ -85845,19 +85877,17 @@ function parseWhereClause2(whereClause) {
85845
85877
  throw new Error(`Cannot parse WHERE clause: "${part}". Use format "column=value" or "col1=val1 AND col2=val2"`);
85846
85878
  }
85847
85879
  const column = match[1];
85848
- const valueStr = match[2];
85849
- let value = valueStr.trim();
85850
- if (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"')) {
85851
- value = value.slice(1, -1);
85852
- }
85853
- if (!isNaN(value) && value !== "") {
85854
- value = Number(value);
85880
+ const trimmed = match[2].trim();
85881
+ const stripped = trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1) : trimmed;
85882
+ let value = stripped;
85883
+ if (stripped !== "" && !isNaN(Number(stripped))) {
85884
+ value = Number(stripped);
85855
85885
  }
85856
- if (value === "true")
85886
+ if (stripped === "true")
85857
85887
  value = true;
85858
- if (value === "false")
85888
+ else if (stripped === "false")
85859
85889
  value = false;
85860
- if (value === "null")
85890
+ else if (stripped === "null")
85861
85891
  value = null;
85862
85892
  conditions[column] = value;
85863
85893
  }
@@ -86137,9 +86167,7 @@ async function redisExportBranch(command, options, config) {
86137
86167
  columnNames
86138
86168
  };
86139
86169
  const formatter = new QueryResultFormatter;
86140
- const formatted = formatter.format(queryResult, {
86141
- format: options.format
86142
- });
86170
+ const formatted = formatter.format(queryResult, { format: options.format });
86143
86171
  if (options.output) {
86144
86172
  const file = Bun.file(options.output);
86145
86173
  const exists = await file.exists();
@@ -86277,20 +86305,11 @@ function escapeCsvField(value) {
86277
86305
 
86278
86306
  // src/commands/skill.ts
86279
86307
  var {$ } = globalThis.Bun;
86280
- import * as path from "path";
86308
+ import * as path2 from "path";
86281
86309
  import { homedir as homedir2 } from "os";
86282
- function findPackageRoot() {
86283
- let dir = import.meta.dir;
86284
- for (let i = 0;i < 5; i++) {
86285
- if (Bun.file(path.join(dir, "package.json")).size > 0) {
86286
- return dir;
86287
- }
86288
- dir = path.dirname(dir);
86289
- }
86290
- return path.resolve(import.meta.dir, "../..");
86291
- }
86292
- var SKILL_SOURCE_PATH = path.join(findPackageRoot(), "assets", "SKILL.md");
86293
- var REFERENCE_SOURCE_PATH = path.join(findPackageRoot(), "assets", "reference.md");
86310
+ init_package_root();
86311
+ var SKILL_SOURCE_PATH = packageAssetPath("SKILL.md");
86312
+ var REFERENCE_SOURCE_PATH = packageAssetPath("reference.md");
86294
86313
  var SUPPORTED_PLATFORMS = ["claude", "gemini", "copilot", "cursor"];
86295
86314
  async function skillCommand(_program, options) {
86296
86315
  try {
@@ -86349,28 +86368,28 @@ function getInstallPath(platform) {
86349
86368
  const platformLower = platform.toLowerCase();
86350
86369
  switch (platformLower) {
86351
86370
  case "claude":
86352
- return path.join(home, ".claude", "skills", "dbcli", "SKILL.md");
86371
+ return path2.join(home, ".claude", "skills", "dbcli", "SKILL.md");
86353
86372
  case "gemini":
86354
- return path.join(home, ".gemini", "skills", "dbcli", "SKILL.md");
86373
+ return path2.join(home, ".gemini", "skills", "dbcli", "SKILL.md");
86355
86374
  case "copilot":
86356
- return path.join(process.cwd(), ".github", "skills", "dbcli", "SKILL.md");
86375
+ return path2.join(process.cwd(), ".github", "skills", "dbcli", "SKILL.md");
86357
86376
  case "cursor":
86358
- return path.join(process.cwd(), ".cursor", "rules", "dbcli.mdc");
86377
+ return path2.join(process.cwd(), ".cursor", "rules", "dbcli.mdc");
86359
86378
  default:
86360
86379
  throw new Error(`Unknown platform: ${platform}. Supported platforms: ${SUPPORTED_PLATFORMS.join(", ")}`);
86361
86380
  }
86362
86381
  }
86363
86382
  async function writeSkillInstall(platform, installPath, skillMarkdown, referenceMarkdown) {
86364
86383
  const platformLower = platform.toLowerCase();
86365
- await ensureDir(path.dirname(installPath));
86384
+ await ensureDir(path2.dirname(installPath));
86366
86385
  await Bun.file(installPath).write(skillMarkdown);
86367
86386
  if (platformLower === "cursor") {
86368
- const refPath2 = path.join(process.cwd(), ".cursor", "skills", "dbcli", "reference.md");
86369
- await ensureDir(path.dirname(refPath2));
86387
+ const refPath2 = path2.join(process.cwd(), ".cursor", "skills", "dbcli", "reference.md");
86388
+ await ensureDir(path2.dirname(refPath2));
86370
86389
  await Bun.file(refPath2).write(referenceMarkdown);
86371
86390
  return { referencePath: refPath2 };
86372
86391
  }
86373
- const refPath = path.join(path.dirname(installPath), "reference.md");
86392
+ const refPath = path2.join(path2.dirname(installPath), "reference.md");
86374
86393
  await Bun.file(refPath).write(referenceMarkdown);
86375
86394
  return { referencePath: refPath };
86376
86395
  }
@@ -86410,15 +86429,16 @@ class AgentTaskError extends Error {
86410
86429
  }
86411
86430
  }
86412
86431
  // src/core/agent-tasks/task-paths.ts
86413
- import { join as join14, resolve as resolve4 } from "path";
86432
+ init_package_root();
86433
+ import { join as join15 } from "path";
86414
86434
  function resolveBuiltinDir2() {
86415
- return resolve4(import.meta.dir, "..", "..", "..", "assets", "tasks");
86435
+ return packageAssetPath("tasks");
86416
86436
  }
86417
86437
  function resolveAgentTaskDirs(workspaceRoot) {
86418
86438
  return {
86419
86439
  builtinDir: resolveBuiltinDir2(),
86420
- sharedDir: join14(workspaceRoot, ".dbcli-shared", "tasks"),
86421
- localDir: join14(workspaceRoot, ".dbcli", "tasks")
86440
+ sharedDir: join15(workspaceRoot, ".dbcli-shared", "tasks"),
86441
+ localDir: join15(workspaceRoot, ".dbcli", "tasks")
86422
86442
  };
86423
86443
  }
86424
86444
  // src/core/agent-tasks/parser.ts
@@ -86556,7 +86576,7 @@ function parseSteps(value, input) {
86556
86576
  }
86557
86577
  // src/core/agent-tasks/loader.ts
86558
86578
  import { readdir as readdir2 } from "fs/promises";
86559
- import { join as join15, relative as relative2, sep as sep2 } from "path";
86579
+ import { join as join16, relative as relative2, sep as sep2 } from "path";
86560
86580
  async function loadAgentTasks(opts, flags) {
86561
86581
  const errors3 = [];
86562
86582
  const builtin = await walkAndParse2(opts.builtinDir, "builtin", errors3);
@@ -86613,7 +86633,7 @@ async function safeCollectMd(root) {
86613
86633
  return;
86614
86634
  }
86615
86635
  for (const e of entries) {
86616
- const full = join15(dir, e.name);
86636
+ const full = join16(dir, e.name);
86617
86637
  if (e.isDirectory())
86618
86638
  await walk(full);
86619
86639
  else
@@ -87561,7 +87581,7 @@ var statusCommand = new Command("status").description("Show current configuratio
87561
87581
  // src/commands/doctor.ts
87562
87582
  init_validation();
87563
87583
  init_schema_path();
87564
- import { join as join16 } from "path";
87584
+ import { join as join17 } from "path";
87565
87585
  import { resolveSrv as resolveSrv2 } from "dns/promises";
87566
87586
  var ALLOWED_FORMATS8 = ["text", "json"];
87567
87587
  var SENSITIVE_PATTERNS = [
@@ -87635,7 +87655,7 @@ var runDoctorChecks = {
87635
87655
  }
87636
87656
  },
87637
87657
  async checkConfigExists(configPath, existsFn) {
87638
- const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join16(configPath, "config.json")).exists();
87658
+ const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join17(configPath, "config.json")).exists();
87639
87659
  return {
87640
87660
  group: "Configuration",
87641
87661
  label: "Config exists",
@@ -87792,7 +87812,7 @@ var runDoctorChecks = {
87792
87812
  async checkV2Config(configPath) {
87793
87813
  const results = [];
87794
87814
  const storagePath = await resolveConfigStoragePath(configPath);
87795
- const configFile = Bun.file(join16(storagePath, "config.json"));
87815
+ const configFile = Bun.file(join17(storagePath, "config.json"));
87796
87816
  if (!await configFile.exists())
87797
87817
  return results;
87798
87818
  let raw;
@@ -87832,7 +87852,7 @@ var runDoctorChecks = {
87832
87852
  }
87833
87853
  for (const [name, conn] of Object.entries(config.connections)) {
87834
87854
  if (conn.envFile) {
87835
- const envPath = join16(storagePath, conn.envFile);
87855
+ const envPath = join17(storagePath, conn.envFile);
87836
87856
  const exists = await Bun.file(envPath).exists();
87837
87857
  results.push({
87838
87858
  group: "Configuration",
@@ -88051,7 +88071,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
88051
88071
  }
88052
88072
  try {
88053
88073
  const schemaConnName = await getSchemaIsolationConnectionName(configPath);
88054
- const indexPath = join16(resolveSchemaPath(storagePath, schemaConnName), "index.json");
88074
+ const indexPath = join17(resolveSchemaPath(storagePath, schemaConnName), "index.json");
88055
88075
  const indexFile = Bun.file(indexPath);
88056
88076
  let indexParsed = null;
88057
88077
  if (await indexFile.exists()) {
@@ -88093,7 +88113,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
88093
88113
  });
88094
88114
 
88095
88115
  // src/commands/completion.ts
88096
- import { join as join17 } from "path";
88116
+ import { join as join18 } from "path";
88097
88117
  import { homedir as homedir3 } from "os";
88098
88118
  function extractCommands(program2) {
88099
88119
  return program2.commands.map((cmd) => ({
@@ -88195,11 +88215,11 @@ function getInstallPath2(shell) {
88195
88215
  const home = homedir3();
88196
88216
  switch (shell) {
88197
88217
  case "bash":
88198
- return join17(home, ".bashrc");
88218
+ return join18(home, ".bashrc");
88199
88219
  case "zsh":
88200
- return join17(home, ".zshrc");
88220
+ return join18(home, ".zshrc");
88201
88221
  case "fish":
88202
- return join17(home, ".config", "fish", "completions", "dbcli.fish");
88222
+ return join18(home, ".config", "fish", "completions", "dbcli.fish");
88203
88223
  default:
88204
88224
  throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
88205
88225
  }
@@ -88219,7 +88239,7 @@ var MARKER_END = "# <<< dbcli completion <<<";
88219
88239
  async function installCompletion(shell, script) {
88220
88240
  const targetPath = getInstallPath2(shell);
88221
88241
  if (shell === "fish") {
88222
- const dir = join17(homedir3(), ".config", "fish", "completions");
88242
+ const dir = join18(homedir3(), ".config", "fish", "completions");
88223
88243
  await Bun.$`mkdir -p ${dir}`.quiet();
88224
88244
  await Bun.file(targetPath).write(script);
88225
88245
  console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
@@ -88446,7 +88466,7 @@ ${t("upgrade.failed")}`));
88446
88466
 
88447
88467
  // src/commands/shell.ts
88448
88468
  import { createInterface as createInterface2 } from "readline";
88449
- import { join as join18 } from "path";
88469
+ import { join as join19 } from "path";
88450
88470
  import { homedir as homedir4 } from "os";
88451
88471
 
88452
88472
  // src/core/repl/types.ts
@@ -88791,7 +88811,7 @@ function isKnownCommand(name) {
88791
88811
 
88792
88812
  // src/core/repl/history-manager.ts
88793
88813
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
88794
- import { dirname as dirname5 } from "path";
88814
+ import { dirname as dirname6 } from "path";
88795
88815
  var DEFAULT_MAX = 1000;
88796
88816
 
88797
88817
  class HistoryManager {
@@ -88819,7 +88839,7 @@ class HistoryManager {
88819
88839
  return this.entries;
88820
88840
  }
88821
88841
  async save() {
88822
- const dir = dirname5(this.filePath);
88842
+ const dir = dirname6(this.filePath);
88823
88843
  if (!existsSync(dir)) {
88824
88844
  mkdirSync(dir, { recursive: true });
88825
88845
  }
@@ -89169,7 +89189,7 @@ class MongoShellAdapter {
89169
89189
  }
89170
89190
 
89171
89191
  // src/commands/shell.ts
89172
- var HISTORY_PATH = join18(homedir4(), ".dbcli_history");
89192
+ var HISTORY_PATH = join19(homedir4(), ".dbcli_history");
89173
89193
  var MONGO_COMPLETION_EAGER_THRESHOLD = 20;
89174
89194
  async function populateMongoColumns(mongoAdapter, collectionNames, threshold = MONGO_COMPLETION_EAGER_THRESHOLD) {
89175
89195
  const columnsByTable = {};
@@ -90140,7 +90160,7 @@ addExecOpts(migrateCommand.command("drop-enum <name>").description(t("migrate.dr
90140
90160
  });
90141
90161
 
90142
90162
  // src/commands/use.ts
90143
- import { join as join19 } from "path";
90163
+ import { join as join20 } from "path";
90144
90164
  async function switchDefault(configPath, name, config) {
90145
90165
  if (!config.connections[name]) {
90146
90166
  const available = Object.keys(config.connections).join(", ");
@@ -90163,7 +90183,7 @@ function listConnectionsForDisplay(config) {
90163
90183
  }
90164
90184
  async function ensureV2Config(configPath) {
90165
90185
  const storagePath = await resolveConfigStoragePath(configPath);
90166
- const configFile = Bun.file(join19(storagePath, "config.json"));
90186
+ const configFile = Bun.file(join20(storagePath, "config.json"));
90167
90187
  const legacyFile = Bun.file(configPath);
90168
90188
  if (!await configFile.exists() && !await legacyFile.exists()) {
90169
90189
  throw new ConfigError(t("init.config_not_found"));
@@ -90225,7 +90245,7 @@ var useCommand = new Command("use").description("Switch or display the default d
90225
90245
  });
90226
90246
 
90227
90247
  // src/cli.ts
90228
- import { join as join20 } from "path";
90248
+ import { join as join21 } from "path";
90229
90249
  var _bgVersionCheckResult;
90230
90250
  function shouldSkipBackgroundChecks() {
90231
90251
  return process.env.DBCLI_NO_UPDATE_CHECK === "1" || process.env.DBCLI_NO_UPDATE_CHECK === "true" || false;
@@ -90254,7 +90274,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
90254
90274
  try {
90255
90275
  let cache = null;
90256
90276
  try {
90257
- const cacheFile = Bun.file(join20(configPath, "version-check.json"));
90277
+ const cacheFile = Bun.file(join21(configPath, "version-check.json"));
90258
90278
  if (await cacheFile.exists()) {
90259
90279
  cache = await cacheFile.json();
90260
90280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carllee1983/dbcli",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "Database CLI for AI agents",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -51,8 +51,8 @@
51
51
  "test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration/adapters; docker compose -f docker-compose.test.yml down",
52
52
  "typecheck": "tsc --noEmit --pretty false",
53
53
  "test:perf": "bun test ./tests/perf/*.bench.ts",
54
- "lint": "eslint src tests --ext .ts",
55
- "lint:fix": "eslint src tests --ext .ts --fix",
54
+ "lint": "eslint src tests --ext .ts --max-warnings=0",
55
+ "lint:fix": "eslint src tests --ext .ts --fix --max-warnings=0",
56
56
  "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\""
57
57
  },
58
58
  "dependencies": {