dbagent-cli 0.3.5__tar.gz → 0.3.7__tar.gz
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.
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/PKG-INFO +2 -2
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/cli.py +30 -0
- dbagent_cli-0.3.7/dbagent/ui/console.py +176 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent_cli.egg-info/PKG-INFO +2 -2
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/pyproject.toml +2 -2
- dbagent_cli-0.3.5/dbagent/ui/console.py +0 -92
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/README.md +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/confidence.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/conversation.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/doctor.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/error_classifier.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/generator.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/pipeline.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/recommender.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/config.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/connectors/base.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/connectors/relational.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/__init__.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/cache.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/changeset.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/cost.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/recovery.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/sandbox.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/execution/streaming.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/gemini_provider.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/schema/cache.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/schema/graph.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/schema/retriever.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/security/__init__.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/security/audit.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/security/environment.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/security/masking.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/security/modes.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/security/rbac.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/semantic/__init__.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/semantic/dictionary.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/semantic/templates.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/sql/__init__.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/sql/optimizer.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/sql/validator.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent/ui/visualizer.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent_cli.egg-info/SOURCES.txt +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/setup.cfg +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_cache.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_cli.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_doctor_and_viz.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_generator.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_graph.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_recovery.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_safety.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_sandbox.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_schema.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_security.py +0 -0
- {dbagent_cli-0.3.5 → dbagent_cli-0.3.7}/tests/test_semantic.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbagent-cli
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary: Production-Grade AI Database Copilot with
|
|
3
|
+
Version: 0.3.7
|
|
4
|
+
Summary: Production-Grade AI Database Copilot with Seamless Column Scroller (CLI)
|
|
5
5
|
Author: Santhosh Gupta
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -56,6 +56,8 @@ from dbagent.ui.console import (
|
|
|
56
56
|
print_info,
|
|
57
57
|
print_code,
|
|
58
58
|
print_results_table,
|
|
59
|
+
print_results_vertical,
|
|
60
|
+
open_interactive_scroller,
|
|
59
61
|
)
|
|
60
62
|
from dbagent.ui.viewer import SchemaViewer
|
|
61
63
|
|
|
@@ -409,6 +411,9 @@ def chat_command(
|
|
|
409
411
|
console.print(" [bold]:provider <name>[/bold] -> Switch AI provider (ollama, gemini, groq, openrouter)")
|
|
410
412
|
console.print(" [bold]:model <name>[/bold] -> Switch active model name (e.g. gemini-3.6-flash)")
|
|
411
413
|
console.print(" [bold]:models[/bold] -> Show active AI provider and model status")
|
|
414
|
+
console.print(" [bold]:page <n>[/bold] -> View column page <n> for wide multi-column tables")
|
|
415
|
+
console.print(" [bold]:card[/bold] -> View last result in expanded vertical card format (all columns)")
|
|
416
|
+
console.print(" [bold]:scroll[/bold] -> Open full-screen interactive horizontal/vertical scroller")
|
|
412
417
|
console.print(" [bold]:explain[/bold] -> Show generated SQL & query analysis for last turn")
|
|
413
418
|
console.print(" [bold]:history[/bold] -> View history of queries in this session")
|
|
414
419
|
console.print(" [bold]:auto[/bold] / [bold]:noauto[/bold] -> Toggle automatic execution of read queries")
|
|
@@ -606,6 +611,31 @@ def chat_command(
|
|
|
606
611
|
console.print(f" {i}. {status} [{r.query_type}] {sql_preview}")
|
|
607
612
|
continue
|
|
608
613
|
|
|
614
|
+
if cmd_name in [":page", ":cols", ":p"]:
|
|
615
|
+
if not last_result or not last_result.rows:
|
|
616
|
+
print_warning("No previous query results to page.")
|
|
617
|
+
continue
|
|
618
|
+
try:
|
|
619
|
+
p_num = int(cmd_arg) if cmd_arg else 1
|
|
620
|
+
except ValueError:
|
|
621
|
+
p_num = 1
|
|
622
|
+
print_results_table(last_result.columns, last_result.rows, title="Results", col_page=p_num)
|
|
623
|
+
continue
|
|
624
|
+
|
|
625
|
+
if cmd_name in [":card", ":vertical", ":x", ":v"]:
|
|
626
|
+
if not last_result or not last_result.rows:
|
|
627
|
+
print_warning("No previous query results to display.")
|
|
628
|
+
continue
|
|
629
|
+
print_results_vertical(last_result.columns, last_result.rows)
|
|
630
|
+
continue
|
|
631
|
+
|
|
632
|
+
if cmd_name in [":scroll", ":scroller", ":pager"]:
|
|
633
|
+
if not last_result or not last_result.rows:
|
|
634
|
+
print_warning("No previous query results to scroll.")
|
|
635
|
+
continue
|
|
636
|
+
open_interactive_scroller(last_result.columns, last_result.rows)
|
|
637
|
+
continue
|
|
638
|
+
|
|
609
639
|
if cmd_name == ":auto":
|
|
610
640
|
auto_execute = True
|
|
611
641
|
print_success("Auto-execute enabled. Read queries will run automatically.")
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rich terminal UI utilities, console formatting, and interactive table scrolling.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import math
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
from typing import Optional, List, Dict, Any
|
|
13
|
+
|
|
14
|
+
# Ensure UTF-8 on Windows terminal streams
|
|
15
|
+
if sys.platform == "win32":
|
|
16
|
+
try:
|
|
17
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
18
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
19
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
20
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
21
|
+
except Exception:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def print_banner() -> None:
|
|
28
|
+
"""Print the DB-Agent terminal banner."""
|
|
29
|
+
banner_text = Text()
|
|
30
|
+
banner_text.append("[*] DB-AGENT ", style="bold cyan")
|
|
31
|
+
banner_text.append("- Universal Database Introspector & AI Script Generator\n", style="bold white")
|
|
32
|
+
banner_text.append("Supports PostgreSQL | MySQL | SQLite | MSSQL | Oracle | DuckDB | MongoDB", style="dim")
|
|
33
|
+
panel = Panel(banner_text, border_style="cyan", padding=(0, 2))
|
|
34
|
+
console.print(panel)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def print_success(message: str) -> None:
|
|
38
|
+
console.print(f"[bold green][OK][/bold green] {message}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def print_error(message: str) -> None:
|
|
42
|
+
console.print(f"[bold red][ERROR][/bold red] {message}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def print_warning(message: str) -> None:
|
|
46
|
+
console.print(f"[bold yellow][WARN][/bold yellow] {message}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def print_info(message: str) -> None:
|
|
50
|
+
console.print(f"[bold blue][INFO][/bold blue] {message}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def print_code(code: str, language: str = "sql", title: Optional[str] = None) -> None:
|
|
54
|
+
"""Print syntax-highlighted code block."""
|
|
55
|
+
syntax = Syntax(code, language, theme="monokai", line_numbers=True)
|
|
56
|
+
panel = Panel(syntax, title=f"[bold cyan]{title or language.upper()}[/bold cyan]", border_style="dim")
|
|
57
|
+
console.print(panel)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def print_results_table(
|
|
61
|
+
columns: List[str],
|
|
62
|
+
rows: List[Dict[str, Any]],
|
|
63
|
+
title: str = "Results",
|
|
64
|
+
col_page: int = 1,
|
|
65
|
+
cols_per_page: int = 8,
|
|
66
|
+
) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Print query results in a clean, readable Rich table with multi-column horizontal paging.
|
|
69
|
+
"""
|
|
70
|
+
if not columns or not rows:
|
|
71
|
+
console.print("[dim]Query returned 0 rows.[/dim]\n")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
total_cols = len(columns)
|
|
75
|
+
total_pages = max(1, math.ceil(total_cols / cols_per_page))
|
|
76
|
+
current_page = max(1, min(col_page, total_pages))
|
|
77
|
+
|
|
78
|
+
start_idx = (current_page - 1) * cols_per_page
|
|
79
|
+
end_idx = min(start_idx + cols_per_page, total_cols)
|
|
80
|
+
display_cols = columns[start_idx:end_idx]
|
|
81
|
+
|
|
82
|
+
tbl_title = f"[bold cyan]{title}[/bold cyan]"
|
|
83
|
+
if total_pages > 1:
|
|
84
|
+
tbl_title += f" [dim](Cols {start_idx + 1}–{end_idx} of {total_cols} | Page {current_page}/{total_pages})[/dim]"
|
|
85
|
+
|
|
86
|
+
table = Table(title=tbl_title, show_header=True, header_style="bold magenta", border_style="dim")
|
|
87
|
+
|
|
88
|
+
for col in display_cols:
|
|
89
|
+
table.add_column(str(col), overflow="ellipsis", max_width=35)
|
|
90
|
+
|
|
91
|
+
for row in rows:
|
|
92
|
+
row_vals = []
|
|
93
|
+
for col in display_cols:
|
|
94
|
+
v = row.get(col)
|
|
95
|
+
val_str = str(v) if v is not None else "[dim]NULL[/dim]"
|
|
96
|
+
if len(val_str) > 40:
|
|
97
|
+
val_str = val_str[:37] + "..."
|
|
98
|
+
row_vals.append(val_str)
|
|
99
|
+
table.add_row(*row_vals)
|
|
100
|
+
|
|
101
|
+
console.print("")
|
|
102
|
+
console.print(table)
|
|
103
|
+
|
|
104
|
+
if total_pages > 1:
|
|
105
|
+
console.print(
|
|
106
|
+
f"[dim]⚡ [bold]:page <1-{total_pages}>[/bold] to switch column view | [bold]:card[/bold] for expanded vertical view | [bold]:scroll[/bold] for full scroller[/dim]"
|
|
107
|
+
)
|
|
108
|
+
console.print(f"[bold green]Total rows:[/bold green] {len(rows)}\n")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def print_results_vertical(
|
|
112
|
+
columns: List[str],
|
|
113
|
+
rows: List[Dict[str, Any]],
|
|
114
|
+
max_rows: int = 10,
|
|
115
|
+
) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Renders rows in an expanded vertical card format (showing all columns key: value).
|
|
118
|
+
"""
|
|
119
|
+
if not columns or not rows:
|
|
120
|
+
console.print("[dim]No data to display.[/dim]\n")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
console.print(f"\n[bold cyan]Expanded Record View ({len(columns)} columns per record):[/bold cyan]\n")
|
|
124
|
+
for idx, row in enumerate(rows[:max_rows], 1):
|
|
125
|
+
console.print(f"[bold yellow]*************************** Record {idx} of {len(rows)} ***************************[/bold yellow]")
|
|
126
|
+
for col in columns:
|
|
127
|
+
val = row.get(col)
|
|
128
|
+
val_str = str(val) if val is not None else "[dim]NULL[/dim]"
|
|
129
|
+
console.print(f" [bold white]{col}:[/bold white] {val_str}")
|
|
130
|
+
console.print("")
|
|
131
|
+
|
|
132
|
+
if len(rows) > max_rows:
|
|
133
|
+
console.print(f"[dim](Showing first {max_rows} of {len(rows)} records)[/dim]\n")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def open_interactive_scroller(columns: List[str], rows: List[Dict[str, Any]]) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Interactive Column Scroller allowing seamless navigation through wide tables.
|
|
139
|
+
"""
|
|
140
|
+
if not columns or not rows:
|
|
141
|
+
console.print("[dim]No data to display.[/dim]\n")
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
cols_per_page = 6
|
|
145
|
+
total_cols = len(columns)
|
|
146
|
+
total_pages = max(1, math.ceil(total_cols / cols_per_page))
|
|
147
|
+
current_page = 1
|
|
148
|
+
|
|
149
|
+
from rich.prompt import Prompt
|
|
150
|
+
|
|
151
|
+
console.print(f"\n[bold cyan]─── Interactive Multi-Column Scroller ({total_cols} columns, {len(rows)} rows) ───[/bold cyan]")
|
|
152
|
+
while True:
|
|
153
|
+
print_results_table(columns, rows, title="Column Scroller", col_page=current_page, cols_per_page=cols_per_page)
|
|
154
|
+
|
|
155
|
+
console.print(f"[bold cyan]Scroller Navigation:[/bold cyan] [bold]n[/bold]=Next Cols | [bold]p[/bold]=Prev Cols | [bold]1-{total_pages}[/bold]=Jump to Page | [bold]c[/bold]=Vertical Cards | [bold]q[/bold]=Exit Scroller")
|
|
156
|
+
choice = Prompt.ask("[dim]scroller>[/dim]", default="n").strip().lower()
|
|
157
|
+
|
|
158
|
+
if choice in ("q", "quit", "exit", "back"):
|
|
159
|
+
console.print("[dim]Exited scroller.[/dim]\n")
|
|
160
|
+
break
|
|
161
|
+
elif choice in ("n", "next", ""):
|
|
162
|
+
current_page = 1 if current_page >= total_pages else current_page + 1
|
|
163
|
+
elif choice in ("p", "prev", "previous"):
|
|
164
|
+
current_page = total_pages if current_page <= 1 else current_page - 1
|
|
165
|
+
elif choice in ("c", "card", "v", "vertical"):
|
|
166
|
+
print_results_vertical(columns, rows)
|
|
167
|
+
Prompt.ask("[dim]Press Enter to return to scroller...[/dim]")
|
|
168
|
+
else:
|
|
169
|
+
try:
|
|
170
|
+
page_num = int(choice)
|
|
171
|
+
if 1 <= page_num <= total_pages:
|
|
172
|
+
current_page = page_num
|
|
173
|
+
else:
|
|
174
|
+
console.print(f"[yellow]Invalid page. Please choose between 1 and {total_pages}.[/yellow]")
|
|
175
|
+
except ValueError:
|
|
176
|
+
pass
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbagent-cli
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary: Production-Grade AI Database Copilot with
|
|
3
|
+
Version: 0.3.7
|
|
4
|
+
Summary: Production-Grade AI Database Copilot with Seamless Column Scroller (CLI)
|
|
5
5
|
Author: Santhosh Gupta
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dbagent-cli"
|
|
7
|
-
version = "0.3.
|
|
8
|
-
description = "Production-Grade AI Database Copilot with
|
|
7
|
+
version = "0.3.7"
|
|
8
|
+
description = "Production-Grade AI Database Copilot with Seamless Column Scroller (CLI)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
11
|
authors = [
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Rich terminal UI utilities and console formatting.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import sys
|
|
6
|
-
from rich.console import Console
|
|
7
|
-
from rich.panel import Panel
|
|
8
|
-
from rich.syntax import Syntax
|
|
9
|
-
from rich.text import Text
|
|
10
|
-
from rich.table import Table
|
|
11
|
-
from typing import Optional, List, Dict, Any
|
|
12
|
-
|
|
13
|
-
# Ensure UTF-8 on Windows terminal streams
|
|
14
|
-
if sys.platform == "win32":
|
|
15
|
-
try:
|
|
16
|
-
if hasattr(sys.stdout, "reconfigure"):
|
|
17
|
-
sys.stdout.reconfigure(encoding="utf-8")
|
|
18
|
-
if hasattr(sys.stderr, "reconfigure"):
|
|
19
|
-
sys.stderr.reconfigure(encoding="utf-8")
|
|
20
|
-
except Exception:
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
console = Console()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def print_banner() -> None:
|
|
27
|
-
"""Print the DB-Agent terminal banner."""
|
|
28
|
-
banner_text = Text()
|
|
29
|
-
banner_text.append("[*] DB-AGENT ", style="bold cyan")
|
|
30
|
-
banner_text.append("- Universal Database Introspector & AI Script Generator\n", style="bold white")
|
|
31
|
-
banner_text.append("Supports PostgreSQL | MySQL | SQLite | MSSQL | Oracle | DuckDB | MongoDB", style="dim")
|
|
32
|
-
panel = Panel(banner_text, border_style="cyan", padding=(0, 2))
|
|
33
|
-
console.print(panel)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def print_success(message: str) -> None:
|
|
37
|
-
console.print(f"[bold green][OK][/bold green] {message}")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def print_error(message: str) -> None:
|
|
41
|
-
console.print(f"[bold red][ERROR][/bold red] {message}")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def print_warning(message: str) -> None:
|
|
45
|
-
console.print(f"[bold yellow][WARN][/bold yellow] {message}")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def print_info(message: str) -> None:
|
|
49
|
-
console.print(f"[bold blue][INFO][/bold blue] {message}")
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def print_code(code: str, language: str = "sql", title: Optional[str] = None) -> None:
|
|
53
|
-
"""Print syntax-highlighted code block."""
|
|
54
|
-
syntax = Syntax(code, language, theme="monokai", line_numbers=True)
|
|
55
|
-
panel = Panel(syntax, title=f"[bold cyan]{title or language.upper()}[/bold cyan]", border_style="dim")
|
|
56
|
-
console.print(panel)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def print_results_table(columns: List[str], rows: List[Dict[str, Any]], title: str = "Results") -> None:
|
|
60
|
-
"""Print query results in a clean, readable Rich table with wide-table protection."""
|
|
61
|
-
if not columns or not rows:
|
|
62
|
-
console.print("[dim]Query returned 0 rows.[/dim]\n")
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
MAX_DISPLAY_COLS = 8
|
|
66
|
-
is_truncated = len(columns) > MAX_DISPLAY_COLS
|
|
67
|
-
display_cols = columns[:MAX_DISPLAY_COLS] if is_truncated else columns
|
|
68
|
-
|
|
69
|
-
table = Table(title=f"[bold cyan]{title}[/bold cyan]", show_header=True, header_style="bold magenta", border_style="dim")
|
|
70
|
-
|
|
71
|
-
for col in display_cols:
|
|
72
|
-
table.add_column(str(col), overflow="ellipsis", max_width=35)
|
|
73
|
-
|
|
74
|
-
for row in rows:
|
|
75
|
-
row_vals = []
|
|
76
|
-
for col in display_cols:
|
|
77
|
-
v = row.get(col)
|
|
78
|
-
val_str = str(v) if v is not None else "[dim]NULL[/dim]"
|
|
79
|
-
# Clean up excessively long raw json/strings
|
|
80
|
-
if len(val_str) > 40:
|
|
81
|
-
val_str = val_str[:37] + "..."
|
|
82
|
-
row_vals.append(val_str)
|
|
83
|
-
table.add_row(*row_vals)
|
|
84
|
-
|
|
85
|
-
console.print("")
|
|
86
|
-
console.print(table)
|
|
87
|
-
if is_truncated:
|
|
88
|
-
remaining = len(columns) - MAX_DISPLAY_COLS
|
|
89
|
-
console.print(f"[dim](Showing {MAX_DISPLAY_COLS} of {len(columns)} columns. Hidden: {', '.join(columns[MAX_DISPLAY_COLS:MAX_DISPLAY_COLS+5])}{'...' if remaining > 5 else ''})[/dim]")
|
|
90
|
-
console.print(f"[dim]Tip: Ask for specific fields (e.g., 'list id, username, email, status from ivis_user') for targeted view.[/dim]")
|
|
91
|
-
console.print(f"[bold green]Total rows:[/bold green] {len(rows)}\n")
|
|
92
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|