@carllee1983/dbcli 1.38.1 → 1.39.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.
package/dist/cli.mjs CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@carllee1983/dbcli",
55
- version: "1.38.1",
55
+ version: "1.39.1",
56
56
  description: "Database CLI for AI agents",
57
57
  type: "module",
58
58
  publishConfig: {
@@ -8809,8 +8809,9 @@ class BlacklistManager {
8809
8809
  }
8810
8810
 
8811
8811
  // src/core/saved-queries/types.ts
8812
- var SavedQueryError;
8812
+ var SUPPORTED_CHART_TYPES, SavedQueryError;
8813
8813
  var init_types2 = __esm(() => {
8814
+ SUPPORTED_CHART_TYPES = ["line", "bar", "area", "pie"];
8814
8815
  SavedQueryError = class SavedQueryError extends Error {
8815
8816
  code;
8816
8817
  file;
@@ -9577,7 +9578,7 @@ function parseFrontmatter(yaml, input) {
9577
9578
  const tags = Array.isArray(raw.tags) ? raw.tags.map(String) : [];
9578
9579
  const index = typeof raw.index === "string" ? raw.index : undefined;
9579
9580
  const intent = normaliseIntent(raw.intent, input);
9580
- const visual = normaliseVisual(raw.visual);
9581
+ const visual = normaliseVisual(raw.visual, input);
9581
9582
  const target = typeof raw.target === "string" ? raw.target : undefined;
9582
9583
  const operation = raw.operation === "find" || raw.operation === "aggregate" ? raw.operation : undefined;
9583
9584
  const verify = normaliseVerify(raw.verify, input);
@@ -9599,7 +9600,7 @@ function parseFrontmatter(yaml, input) {
9599
9600
  warnings
9600
9601
  };
9601
9602
  }
9602
- function normaliseVisual(value) {
9603
+ function normaliseVisual(value, input) {
9603
9604
  if (value === undefined || value === null || typeof value !== "object")
9604
9605
  return;
9605
9606
  const raw = value;
@@ -9625,6 +9626,9 @@ function normaliseVisual(value) {
9625
9626
  if (typeof item === "object" && item !== null) {
9626
9627
  const c2 = item;
9627
9628
  if (typeof c2.type === "string" && typeof c2.x === "string" && Array.isArray(c2.y)) {
9629
+ if (!SUPPORTED_CHART_TYPES.includes(c2.type)) {
9630
+ throw new SavedQueryError(`Snippet '${input.key}' has invalid chart type '${c2.type}' (supported: ${SUPPORTED_CHART_TYPES.join(", ")})`, "PARSE_ERROR", input.file);
9631
+ }
9628
9632
  charts.push({
9629
9633
  type: c2.type,
9630
9634
  title: typeof c2.title === "string" ? c2.title : undefined,
@@ -24863,6 +24867,17 @@ class RedisAdapter {
24863
24867
  const regexes = rules.map((p) => globToRegex(p));
24864
24868
  return keys.filter((k) => !regexes.some((r) => r.test(k))).map((name2) => ({ name: name2 }));
24865
24869
  }
24870
+ async sampleKeyNames(limit) {
24871
+ const client = this.requireClient();
24872
+ const keys = await scanAllKeys(client, "*", limit, limit);
24873
+ const truncated = keys.length >= limit;
24874
+ const rules = this.blacklistRules;
24875
+ if (rules.length === 0)
24876
+ return { names: keys, truncated };
24877
+ const regexes = rules.map((p) => globToRegex(p));
24878
+ const names = keys.filter((k) => !regexes.some((r) => r.test(k)));
24879
+ return { names, truncated };
24880
+ }
24866
24881
  async getDbSize() {
24867
24882
  const client = this.requireClient();
24868
24883
  const reply = await client.send("DBSIZE", []);
@@ -25145,7 +25160,7 @@ function parseRedisCommand(input) {
25145
25160
  push();
25146
25161
  return tokens;
25147
25162
  }
25148
- async function scanAllKeys(client, pattern, count) {
25163
+ async function scanAllKeys(client, pattern, count, maxKeys = 1e5) {
25149
25164
  const seen = new Set;
25150
25165
  let cursor = "0";
25151
25166
  do {
@@ -25160,7 +25175,7 @@ async function scanAllKeys(client, pattern, count) {
25160
25175
  for (const k of batch)
25161
25176
  seen.add(k);
25162
25177
  cursor = next;
25163
- if (seen.size >= 1e5)
25178
+ if (seen.size >= maxKeys)
25164
25179
  break;
25165
25180
  } while (cursor !== "0");
25166
25181
  return Array.from(seen);
@@ -96295,32 +96310,6 @@ var init_meta_commands = __esm(() => {
96295
96310
  VALID_FORMATS = ["table", "json", "csv"];
96296
96311
  });
96297
96312
 
96298
- // src/core/repl/command-registry.ts
96299
- var exports_command_registry = {};
96300
- __export(exports_command_registry, {
96301
- setReplCommandNames: () => setReplCommandNames,
96302
- isReplCommandKnown: () => isReplCommandKnown,
96303
- getReplCommandNames: () => getReplCommandNames,
96304
- REPL_DENYLIST: () => REPL_DENYLIST
96305
- });
96306
- function setReplCommandNames(input) {
96307
- const allowed = input.filter((n) => !REPL_DENYLIST.has(n));
96308
- names = allowed;
96309
- known = new Set(allowed);
96310
- }
96311
- function getReplCommandNames() {
96312
- return names;
96313
- }
96314
- function isReplCommandKnown(name2) {
96315
- return known.has(name2);
96316
- }
96317
- var REPL_DENYLIST, names, known;
96318
- var init_command_registry = __esm(() => {
96319
- REPL_DENYLIST = new Set(["shell"]);
96320
- names = [];
96321
- known = new Set;
96322
- });
96323
-
96324
96313
  // src/core/repl/command-dispatcher.ts
96325
96314
  function parseCommandLine(input) {
96326
96315
  const trimmed = input.trim();
@@ -96356,12 +96345,9 @@ function splitRespectingQuotes(input) {
96356
96345
  }
96357
96346
  return tokens;
96358
96347
  }
96359
- function isKnownCommand(name2) {
96360
- return isReplCommandKnown(name2);
96348
+ function isKnownCommand(name2, commandNames) {
96349
+ return commandNames.includes(name2);
96361
96350
  }
96362
- var init_command_dispatcher = __esm(() => {
96363
- init_command_registry();
96364
- });
96365
96351
 
96366
96352
  // src/core/repl/history-manager.ts
96367
96353
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
@@ -96488,7 +96474,7 @@ class ReplEngine {
96488
96474
  }
96489
96475
  async handleCommand(input) {
96490
96476
  const parsed = parseCommandLine(input);
96491
- if (!isKnownCommand(parsed.command)) {
96477
+ if (!isKnownCommand(parsed.command, this.context.commandNames)) {
96492
96478
  if (this.context.system === "redis") {
96493
96479
  this.history.add(input);
96494
96480
  return this.executeSql(input);
@@ -96631,7 +96617,6 @@ var import_picocolors3;
96631
96617
  var init_repl_engine = __esm(() => {
96632
96618
  init_input_classifier();
96633
96619
  init_meta_commands();
96634
- init_command_dispatcher();
96635
96620
  init_history_manager();
96636
96621
  init_permission_guard();
96637
96622
  init_query_result_formatter();
@@ -96679,7 +96664,7 @@ function createCompleter(ctx) {
96679
96664
  }
96680
96665
  const words = trimmed.split(/\s+/);
96681
96666
  if (words.length === 1) {
96682
- const cmdHits = matchWithSuffix([...getReplCommandNames()], lastWord);
96667
+ const cmdHits = matchWithSuffix([...ctx.commandNames], lastWord);
96683
96668
  const sqlHits = matchWithSuffix([...SQL_KEYWORDS_FOR_COMPLETION], lastWord);
96684
96669
  return [[...cmdHits[0], ...sqlHits[0]], lastWord];
96685
96670
  }
@@ -96732,7 +96717,6 @@ function matchWithSuffix(candidates, prefix) {
96732
96717
  var REDIS_COMMANDS, TABLE_POSITION_KEYWORDS, COMMANDS_TAKING_TABLE_ARG;
96733
96718
  var init_completer = __esm(() => {
96734
96719
  init_types10();
96735
- init_command_registry();
96736
96720
  init_command_metadata();
96737
96721
  REDIS_COMMANDS = Object.keys(REDIS_COMMAND_TABLE);
96738
96722
  TABLE_POSITION_KEYWORDS = new Set([
@@ -96921,6 +96905,20 @@ var init_es_shell = __esm(() => {
96921
96905
  import_picocolors4 = __toESM(require_picocolors(), 1);
96922
96906
  });
96923
96907
 
96908
+ // src/core/repl/command-registry.ts
96909
+ var exports_command_registry = {};
96910
+ __export(exports_command_registry, {
96911
+ deriveReplCommandNames: () => deriveReplCommandNames,
96912
+ REPL_DENYLIST: () => REPL_DENYLIST
96913
+ });
96914
+ function deriveReplCommandNames(topLevel) {
96915
+ return topLevel.filter((n) => !REPL_DENYLIST.has(n));
96916
+ }
96917
+ var REPL_DENYLIST;
96918
+ var init_command_registry = __esm(() => {
96919
+ REPL_DENYLIST = new Set(["shell"]);
96920
+ });
96921
+
96924
96922
  // src/commands/shell.ts
96925
96923
  import { createInterface as createInterface3 } from "readline";
96926
96924
  import { join as join32 } from "path";
@@ -96945,6 +96943,14 @@ async function populateMongoColumns(mongoAdapter, collectionNames, threshold = M
96945
96943
  }
96946
96944
  return columnsByTable;
96947
96945
  }
96946
+ async function populateRedisKeyCompletion(adapter, limit = REDIS_COMPLETION_KEY_LIMIT) {
96947
+ try {
96948
+ const { names, truncated } = await adapter.sampleKeyNames(limit);
96949
+ return { tableNames: names, truncated };
96950
+ } catch {
96951
+ return { tableNames: [], truncated: false };
96952
+ }
96953
+ }
96948
96954
  async function runShell(options, configPath) {
96949
96955
  let config;
96950
96956
  try {
@@ -96972,9 +96978,12 @@ async function runShell(options, configPath) {
96972
96978
  let tableNames = [];
96973
96979
  let columnsByTable = {};
96974
96980
  if (isRedis) {
96975
- const keys = await adapter.listTables();
96976
- tableNames = keys.map((k) => k.name);
96981
+ const completion = await populateRedisKeyCompletion(redisInner);
96982
+ tableNames = completion.tableNames;
96977
96983
  columnsByTable = {};
96984
+ if (completion.truncated) {
96985
+ console.error(import_picocolors5.default.dim(`Redis shell: large keyspace; tab completion limited to the first ${REDIS_COMPLETION_KEY_LIMIT} keys.`));
96986
+ }
96978
96987
  } else if (isMongoDB) {
96979
96988
  const collections = await adapter.listTables();
96980
96989
  tableNames = collections.map((collection) => collection.name);
@@ -96994,17 +97003,18 @@ async function runShell(options, configPath) {
96994
97003
  }
96995
97004
  }
96996
97005
  }
97006
+ const { buildProgram } = await Promise.resolve().then(() => (init_program(), exports_program));
97007
+ const { buildCompletionTree: buildCompletionTree2, listTopLevelCommandNames: listTopLevelCommandNames2 } = await Promise.resolve().then(() => exports_command_tree);
97008
+ const { deriveReplCommandNames: deriveReplCommandNames2 } = await Promise.resolve().then(() => (init_command_registry(), exports_command_registry));
97009
+ const commandNames = deriveReplCommandNames2(listTopLevelCommandNames2(buildCompletionTree2(buildProgram())));
96997
97010
  const context = {
96998
97011
  configPath,
96999
97012
  permission: config.permission,
97000
97013
  system: config.connection.system,
97001
97014
  tableNames,
97002
- columnsByTable
97015
+ columnsByTable,
97016
+ commandNames
97003
97017
  };
97004
- const { buildProgram } = await Promise.resolve().then(() => (init_program(), exports_program));
97005
- const { buildCompletionTree: buildCompletionTree2, listTopLevelCommandNames: listTopLevelCommandNames2 } = await Promise.resolve().then(() => exports_command_tree);
97006
- const { setReplCommandNames: setReplCommandNames2 } = await Promise.resolve().then(() => (init_command_registry(), exports_command_registry));
97007
- setReplCommandNames2(listTopLevelCommandNames2(buildCompletionTree2(buildProgram())));
97008
97018
  const engine = new ReplEngine(adapter, context, HISTORY_PATH, config);
97009
97019
  const complete = createCompleter(context);
97010
97020
  console.error(import_picocolors5.default.bold(t_vars("shell.welcome", {
@@ -97094,7 +97104,7 @@ async function runBatchSession(engine, input) {
97094
97104
  }
97095
97105
  }
97096
97106
  }
97097
- var import_picocolors5, HISTORY_PATH, MONGO_COMPLETION_EAGER_THRESHOLD = 20, shellCommand;
97107
+ var import_picocolors5, HISTORY_PATH, MONGO_COMPLETION_EAGER_THRESHOLD = 20, REDIS_COMPLETION_KEY_LIMIT = 1000, shellCommand;
97098
97108
  var init_shell3 = __esm(() => {
97099
97109
  init_esm();
97100
97110
  init_config();
package/dist/core.mjs CHANGED
@@ -15956,6 +15956,17 @@ class RedisAdapter {
15956
15956
  const regexes = rules.map((p) => globToRegex(p));
15957
15957
  return keys.filter((k) => !regexes.some((r) => r.test(k))).map((name) => ({ name }));
15958
15958
  }
15959
+ async sampleKeyNames(limit) {
15960
+ const client = this.requireClient();
15961
+ const keys = await scanAllKeys(client, "*", limit, limit);
15962
+ const truncated = keys.length >= limit;
15963
+ const rules = this.blacklistRules;
15964
+ if (rules.length === 0)
15965
+ return { names: keys, truncated };
15966
+ const regexes = rules.map((p) => globToRegex(p));
15967
+ const names = keys.filter((k) => !regexes.some((r) => r.test(k)));
15968
+ return { names, truncated };
15969
+ }
15959
15970
  async getDbSize() {
15960
15971
  const client = this.requireClient();
15961
15972
  const reply = await client.send("DBSIZE", []);
@@ -16238,7 +16249,7 @@ function parseRedisCommand(input) {
16238
16249
  push();
16239
16250
  return tokens;
16240
16251
  }
16241
- async function scanAllKeys(client, pattern, count) {
16252
+ async function scanAllKeys(client, pattern, count, maxKeys = 1e5) {
16242
16253
  const seen = new Set;
16243
16254
  let cursor = "0";
16244
16255
  do {
@@ -16253,7 +16264,7 @@ async function scanAllKeys(client, pattern, count) {
16253
16264
  for (const k of batch)
16254
16265
  seen.add(k);
16255
16266
  cursor = next;
16256
- if (seen.size >= 1e5)
16267
+ if (seen.size >= maxKeys)
16257
16268
  break;
16258
16269
  } while (cursor !== "0");
16259
16270
  return Array.from(seen);
package/dist/ui-style.css CHANGED
@@ -1,3 +1,3 @@
1
1
  @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");
2
2
 
3
- /*! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}body{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity));font-family:Inter,system-ui,sans-serif;--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.card{overflow:hidden;border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));--tw-shadow:0 2px 15px -3px #00000012,0 4px 6px -2px #0000000d;--tw-shadow-colored:0 2px 15px -3px var(--tw-shadow-color),0 4px 6px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.sticky{position:sticky}.top-0{top:0}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-8{margin-bottom:2rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[450px\]{height:450px}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(248 250 252/var(--tw-divide-opacity))}.overflow-x-auto{overflow-x:auto}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-primary-100{--tw-border-opacity:1;border-color:rgb(219 234 254/var(--tw-border-opacity))}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity))}.border-l-primary-500{--tw-border-opacity:1;border-left-color:rgb(59 130 246/var(--tw-border-opacity))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.bg-slate-50\/30{background-color:#f8fafc4d}.bg-slate-50\/50{background-color:#f8fafc80}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/80{background-color:#fffc}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1{padding-bottom:.25rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-primary-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.hover\:bg-primary-50\/30:hover{background-color:#eff6ff4d}.hover\:text-primary-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.group:hover .group-hover\:text-primary-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}@media (min-width:640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:p-8{padding:2rem}}
3
+ /*! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}body{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity));font-family:Inter,system-ui,sans-serif;--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.card{overflow:hidden;border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));--tw-shadow:0 2px 15px -3px #00000012,0 4px 6px -2px #0000000d;--tw-shadow-colored:0 2px 15px -3px var(--tw-shadow-color),0 4px 6px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.sticky{position:sticky}.top-0{top:0}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-8{margin-bottom:2rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[450px\]{height:450px}.h-full{height:100%}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(248 250 252/var(--tw-divide-opacity))}.overflow-x-auto{overflow-x:auto}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-primary-100{--tw-border-opacity:1;border-color:rgb(219 234 254/var(--tw-border-opacity))}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity))}.border-l-primary-500{--tw-border-opacity:1;border-left-color:rgb(59 130 246/var(--tw-border-opacity))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.bg-slate-50\/30{background-color:#f8fafc4d}.bg-slate-50\/50{background-color:#f8fafc80}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/80{background-color:#fffc}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1{padding-bottom:.25rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-primary-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.hover\:bg-primary-50\/30:hover{background-color:#eff6ff4d}.hover\:text-primary-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.group:hover .group-hover\:text-primary-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}@media (min-width:640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:p-8{padding:2rem}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carllee1983/dbcli",
3
- "version": "1.38.1",
3
+ "version": "1.39.1",
4
4
  "description": "Database CLI for AI agents",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dbcli
3
- description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
3
+ description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, generate DB reports or interactive HTML dashboards, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, generating a report/dashboard/HTML UI from raw SQL or saved snippets, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
4
4
  ---
5
5
 
6
6
  # dbcli
@@ -30,6 +30,7 @@ the CLI package has not been installed globally.
30
30
  | --- | --- |
31
31
  | A named workflow fits ("diagnose slow query", "audit permissions") | `skill tasks list` → `skill tasks plan <pack>` — **prefer this; do not invent steps** |
32
32
  | A fixed diagnostic goal | `guide <goal>` (`slow-query` / `capacity` / `health` / `index-usage` / `permissions` / `schema-overview`; `guide --list`) |
33
+ | A DB report / dashboard / HTML UI | `blacklist list` → `queries search <keywords>` or `queries suggest <intent>` → `queries show @<name>` → browser: `q @<name> --param k=v --ui`; file: `q @<name> --format html > report.html` or `export "<SQL>" --format html --output report.html` |
33
34
  | Setting up a connection | see **Connection setup** |
34
35
  | Anything else | run commands manually; consult the **Developer workflows** cheat-sheet |
35
36
 
@@ -89,6 +90,7 @@ in **How to use dbcli** still applies.
89
90
  | Situation | Minimum safe path |
90
91
  | --- | --- |
91
92
  | DB-backed feature | `blacklist list` → `schema <object>` → `queries suggest <intent>` |
93
+ | DB report / dashboard request | `blacklist list` → `queries search <keywords>` / `queries suggest <intent>` → `queries show @<name>` → `q @<name> --ui` or `--format html` |
92
94
  | Application data bug | `audit tail --for-agent --n 10` → `blacklist list` → `schema <object>` → narrow query |
93
95
  | ORM or migration work | `schema --format json` → `diff --snapshot <name>` → `migrate add-index`/`add-column` (preview SQL) → `diff --against <snapshot>` |
94
96
  | PR database review | Review changed persistence paths, then propose concrete `schema` / `plan` / `dry-run` / `report` / `guide` commands per material claim. |
@@ -103,6 +105,11 @@ dbcli inspect --for-agent --format json
103
105
  dbcli blacklist list --format json
104
106
  dbcli schema <object> --format json
105
107
  dbcli queries suggest <intent> --format json
108
+ dbcli queries search <report keywords> --format json
109
+ dbcli queries show @<name> --format json
110
+ dbcli q @<name> --param k=v --ui
111
+ dbcli q @<name> --param k=v --format html > report.html
112
+ dbcli export "<SQL>" --format html --output report.html
106
113
  dbcli audit tail --for-agent --n 10
107
114
  dbcli diff --snapshot <name>
108
115
  dbcli report --section perf --format json
@@ -425,14 +432,18 @@ Run with `dbcli q @diag/<topic>` (engine variant auto-picked by the active conne
425
432
 
426
433
  ```bash
427
434
  dbcli query "SELECT day, dau FROM dau_daily" --ui # open in browser
435
+ dbcli q @analytics/revenue --param days=30 --ui # snippet metadata + charts/KPIs
436
+ dbcli q @analytics/revenue --param days=30 --format html > report.html
428
437
  dbcli query "SELECT * FROM orders" --format html > out.html # pipe HTML to stdout
429
438
  dbcli export "SELECT * FROM orders" --format html --output orders.html
430
439
  ```
431
440
 
432
441
  `--ui` implies `--format html` and opens the file; `--format html` alone prints to stdout.
433
- Blacklist redaction is applied **before** rendering. To get KPIs and charts instead of a plain
434
- table, add a `visual:` block (`title`, `kpis[]`, `charts[]`) to the snippet frontmatter — see
435
- reference.md for the full `visual:` schema. Raw `query` invocations render a sortable table only.
442
+ When a saved snippet exists, prefer `q @<name> --ui` / `q @<name> --format html` because snippet
443
+ metadata can drive titles, KPI cards, and charts. Blacklist redaction is applied **before**
444
+ rendering. To get KPIs and charts instead of a plain table, add a `visual:` block (`title`,
445
+ `kpis[]`, `charts[]`) to the snippet frontmatter — see reference.md for the full `visual:`
446
+ schema. Raw `query` / `export` invocations render a sortable table only.
436
447
 
437
448
  ## Common workflows
438
449
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dbcli
3
- description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
3
+ description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, generate DB reports or interactive HTML dashboards, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, generating a report/dashboard/HTML UI from raw SQL or saved snippets, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
4
4
  ---
5
5
 
6
6
  # dbcli
@@ -30,6 +30,7 @@ the CLI package has not been installed globally.
30
30
  | --- | --- |
31
31
  | A named workflow fits ("diagnose slow query", "audit permissions") | `skill tasks list` → `skill tasks plan <pack>` — **prefer this; do not invent steps** |
32
32
  | A fixed diagnostic goal | `guide <goal>` (`slow-query` / `capacity` / `health` / `index-usage` / `permissions` / `schema-overview`; `guide --list`) |
33
+ | A DB report / dashboard / HTML UI | `blacklist list` → `queries search <keywords>` or `queries suggest <intent>` → `queries show @<name>` → browser: `q @<name> --param k=v --ui`; file: `q @<name> --format html > report.html` or `export "<SQL>" --format html --output report.html` |
33
34
  | Setting up a connection | see **Connection setup** |
34
35
  | Anything else | run commands manually; consult the **Developer workflows** cheat-sheet |
35
36
 
@@ -89,6 +90,7 @@ in **How to use dbcli** still applies.
89
90
  | Situation | Minimum safe path |
90
91
  | --- | --- |
91
92
  | DB-backed feature | `blacklist list` → `schema <object>` → `queries suggest <intent>` |
93
+ | DB report / dashboard request | `blacklist list` → `queries search <keywords>` / `queries suggest <intent>` → `queries show @<name>` → `q @<name> --ui` or `--format html` |
92
94
  | Application data bug | `audit tail --for-agent --n 10` → `blacklist list` → `schema <object>` → narrow query |
93
95
  | ORM or migration work | `schema --format json` → `diff --snapshot <name>` → `migrate add-index`/`add-column` (preview SQL) → `diff --against <snapshot>` |
94
96
  | PR database review | Review changed persistence paths, then propose concrete `schema` / `plan` / `dry-run` / `report` / `guide` commands per material claim. |
@@ -103,6 +105,11 @@ dbcli inspect --for-agent --format json
103
105
  dbcli blacklist list --format json
104
106
  dbcli schema <object> --format json
105
107
  dbcli queries suggest <intent> --format json
108
+ dbcli queries search <report keywords> --format json
109
+ dbcli queries show @<name> --format json
110
+ dbcli q @<name> --param k=v --ui
111
+ dbcli q @<name> --param k=v --format html > report.html
112
+ dbcli export "<SQL>" --format html --output report.html
106
113
  dbcli audit tail --for-agent --n 10
107
114
  dbcli diff --snapshot <name>
108
115
  dbcli report --section perf --format json
@@ -425,14 +432,18 @@ Run with `dbcli q @diag/<topic>` (engine variant auto-picked by the active conne
425
432
 
426
433
  ```bash
427
434
  dbcli query "SELECT day, dau FROM dau_daily" --ui # open in browser
435
+ dbcli q @analytics/revenue --param days=30 --ui # snippet metadata + charts/KPIs
436
+ dbcli q @analytics/revenue --param days=30 --format html > report.html
428
437
  dbcli query "SELECT * FROM orders" --format html > out.html # pipe HTML to stdout
429
438
  dbcli export "SELECT * FROM orders" --format html --output orders.html
430
439
  ```
431
440
 
432
441
  `--ui` implies `--format html` and opens the file; `--format html` alone prints to stdout.
433
- Blacklist redaction is applied **before** rendering. To get KPIs and charts instead of a plain
434
- table, add a `visual:` block (`title`, `kpis[]`, `charts[]`) to the snippet frontmatter — see
435
- reference.md for the full `visual:` schema. Raw `query` invocations render a sortable table only.
442
+ When a saved snippet exists, prefer `q @<name> --ui` / `q @<name> --format html` because snippet
443
+ metadata can drive titles, KPI cards, and charts. Blacklist redaction is applied **before**
444
+ rendering. To get KPIs and charts instead of a plain table, add a `visual:` block (`title`,
445
+ `kpis[]`, `charts[]`) to the snippet frontmatter — see reference.md for the full `visual:`
446
+ schema. Raw `query` / `export` invocations render a sortable table only.
436
447
 
437
448
  ## Common workflows
438
449