sqlsaber 0.8.2__tar.gz → 0.9.0__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.

Potentially problematic release.


This version of sqlsaber might be problematic. Click here for more details.

Files changed (68) hide show
  1. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/.gitignore +2 -0
  2. sqlsaber-0.9.0/AGENT.md +24 -0
  3. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/CHANGELOG.md +12 -0
  4. sqlsaber-0.9.0/CLAUDE.md +22 -0
  5. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/PKG-INFO +19 -13
  6. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/README.md +17 -11
  7. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/pyproject.toml +2 -3
  8. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/auth.py +9 -10
  9. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/commands.py +62 -53
  10. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/database.py +88 -62
  11. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/memory.py +83 -73
  12. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/models.py +15 -15
  13. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/uv.lock +58 -3
  14. sqlsaber-0.8.2/CLAUDE.md +0 -5
  15. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/.github/workflows/publish.yml +0 -0
  16. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/.python-version +0 -0
  17. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/LICENSE +0 -0
  18. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/pytest.ini +0 -0
  19. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/sqlsaber.svg +0 -0
  20. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/__init__.py +0 -0
  21. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/__main__.py +0 -0
  22. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/__init__.py +0 -0
  23. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/anthropic.py +0 -0
  24. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/base.py +0 -0
  25. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/mcp.py +0 -0
  26. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/agents/streaming.py +0 -0
  27. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/__init__.py +0 -0
  28. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/completers.py +0 -0
  29. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/display.py +0 -0
  30. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/interactive.py +0 -0
  31. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/cli/streaming.py +0 -0
  32. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/__init__.py +0 -0
  33. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/anthropic.py +0 -0
  34. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/base.py +0 -0
  35. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/exceptions.py +0 -0
  36. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/models.py +0 -0
  37. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/clients/streaming.py +0 -0
  38. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/__init__.py +0 -0
  39. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/api_keys.py +0 -0
  40. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/auth.py +0 -0
  41. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/database.py +0 -0
  42. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/oauth_flow.py +0 -0
  43. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/oauth_tokens.py +0 -0
  44. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/config/settings.py +0 -0
  45. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/database/__init__.py +0 -0
  46. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/database/connection.py +0 -0
  47. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/database/schema.py +0 -0
  48. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/mcp/__init__.py +0 -0
  49. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/mcp/mcp.py +0 -0
  50. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/memory/__init__.py +0 -0
  51. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/memory/manager.py +0 -0
  52. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/memory/storage.py +0 -0
  53. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/models/__init__.py +0 -0
  54. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/models/events.py +0 -0
  55. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/src/sqlsaber/models/types.py +0 -0
  56. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/__init__.py +0 -0
  57. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/conftest.py +0 -0
  58. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_agents/test_anthropic_oauth.py +0 -0
  59. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_cli/__init__.py +0 -0
  60. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_cli/test_commands.py +0 -0
  61. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_clients/test_anthropic_client.py +0 -0
  62. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_clients/test_streaming.py +0 -0
  63. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/__init__.py +0 -0
  64. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/test_database.py +0 -0
  65. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/test_oauth.py +0 -0
  66. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_config/test_settings.py +0 -0
  67. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_database/__init__.py +0 -0
  68. {sqlsaber-0.8.2 → sqlsaber-0.9.0}/tests/test_database/test_connection.py +0 -0
@@ -8,3 +8,5 @@ wheels/
8
8
 
9
9
  # Virtual environments
10
10
  .venv
11
+
12
+ *.local.*
@@ -0,0 +1,24 @@
1
+ # SQLSaber Agent Guide
2
+
3
+ ## Commands
4
+
5
+ - **Run Python**: `uv run python`
6
+ - **Run tests**: `uv run python -m pytest`
7
+ - **Run single test**: `uv run python -m pytest tests/test_path/test_file.py::test_function`
8
+ - **Lint**: `uv run ruff check --fix`
9
+ - **Format**: `uv run ruff format`
10
+
11
+ ## Architecture
12
+
13
+ - **CLI App**: Agentic SQL assistant for natural language to SQL
14
+ - **Core modules**: `agents/` (AI logic), `cli/` (commands), `clients/` (LLM clients), `database/` (connections), `mcp/` (Model Context Protocol server)
15
+ - **Database support**: PostgreSQL, SQLite, MySQL via asyncpg/aiosqlite/aiomysql
16
+ - **MCP integration**: Exposes tools via `sqlsaber-mcp` for Claude Code and other MCP clients
17
+
18
+ ## Code Style
19
+
20
+ - **Imports**: stdlib → 3rd party → local, use relative imports within modules
21
+ - **Naming**: snake_case functions/vars, PascalCase classes, UPPER_SNAKE constants, `_private` methods
22
+ - **Types**: Always use modern type hints (3.12+), async functions for I/O
23
+ - **Errors**: Custom exception hierarchy from `LLMClientError`, use try/finally for cleanup
24
+ - **Docstrings**: Triple-quoted with Args/Returns sections
@@ -4,6 +4,18 @@ All notable changes to SQLSaber will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.0] - 2025-07-08
8
+
9
+ ### Changed
10
+
11
+ - Migrated from Typer to Cyclopts for CLI framework
12
+ - Improved command structure and parameter handling
13
+ - Better support for sub-commands and help documentation
14
+ - Made interactive mode more ergonomic
15
+ - `saber` now directly starts interactive mode (previously `saber query`)
16
+ - `saber "question"` executes a single query (previously `saber query "question"`)
17
+ - Removed the `query` subcommand for a cleaner interface
18
+
7
19
  ## [0.8.2] - 2025-07-08
8
20
 
9
21
  ## Changed
@@ -0,0 +1,22 @@
1
+ ## Commands
2
+
3
+ - **Run Python**: `uv run python`
4
+ - **Run tests**: `uv run python -m pytest`
5
+ - **Run single test**: `uv run python -m pytest tests/test_path/test_file.py::test_function`
6
+ - **Lint**: `uv run ruff check --fix`
7
+ - **Format**: `uv run ruff format`
8
+
9
+ ## Architecture
10
+
11
+ - **CLI App**: Agentic SQL assistant for natural language to SQL
12
+ - **Core modules**: `agents/` (AI logic), `cli/` (commands), `clients/` (LLM clients), `database/` (connections), `mcp/` (Model Context Protocol server)
13
+ - **Database support**: PostgreSQL, SQLite, MySQL via asyncpg/aiosqlite/aiomysql
14
+ - **MCP integration**: Exposes tools via `sqlsaber-mcp` for Claude Code and other MCP clients
15
+
16
+ ## Code Style
17
+
18
+ - **Imports**: stdlib → 3rd party → local, use relative imports within modules
19
+ - **Naming**: snake_case functions/vars, PascalCase classes, UPPER_SNAKE constants, `_private` methods
20
+ - **Types**: Always use modern type hints (3.12+), async functions for I/O
21
+ - **Errors**: Custom exception hierarchy from `LLMClientError`, use try/finally for cleanup
22
+ - **Docstrings**: Triple-quoted with Args/Returns sections
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlsaber
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: SQLSaber - Agentic SQL assistant like Claude Code
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -8,6 +8,7 @@ Requires-Dist: aiomysql>=0.2.0
8
8
  Requires-Dist: aiosqlite>=0.21.0
9
9
  Requires-Dist: anthropic>=0.54.0
10
10
  Requires-Dist: asyncpg>=0.30.0
11
+ Requires-Dist: cyclopts>=3.22.1
11
12
  Requires-Dist: fastmcp>=2.9.0
12
13
  Requires-Dist: httpx>=0.28.1
13
14
  Requires-Dist: keyring>=25.6.0
@@ -15,7 +16,6 @@ Requires-Dist: pandas>=2.0.0
15
16
  Requires-Dist: platformdirs>=4.0.0
16
17
  Requires-Dist: questionary>=2.1.0
17
18
  Requires-Dist: rich>=13.7.0
18
- Requires-Dist: typer>=0.16.0
19
19
  Requires-Dist: uniplot>=0.21.2
20
20
  Description-Content-Type: text/markdown
21
21
 
@@ -126,7 +126,7 @@ saber memory list
126
126
  Start an interactive session:
127
127
 
128
128
  ```bash
129
- saber query
129
+ saber
130
130
  ```
131
131
 
132
132
  > You can also add memories in an interactive session by starting your message with the `#` sign
@@ -136,7 +136,7 @@ saber query
136
136
  Execute a single natural language query:
137
137
 
138
138
  ```bash
139
- saber query "show me all users created this month"
139
+ saber "show me all users created this month"
140
140
  ```
141
141
 
142
142
  ### Database Selection
@@ -144,33 +144,39 @@ saber query "show me all users created this month"
144
144
  Use a specific database connection:
145
145
 
146
146
  ```bash
147
- # Use named database from config
148
- saber query -d mydb "count all orders"
147
+ # Interactive mode with specific database
148
+ saber -d mydb
149
+
150
+ # Single query with specific database
151
+ saber -d mydb "count all orders"
149
152
  ```
150
153
 
151
154
  ## Examples
152
155
 
153
156
  ```bash
154
157
  # Show database schema
155
- saber query "what tables are in my database?"
158
+ saber "what tables are in my database?"
156
159
 
157
160
  # Count records
158
- saber query "how many active users do we have?"
161
+ saber "how many active users do we have?"
159
162
 
160
163
  # Complex queries with joins
161
- saber query "show me orders with customer details for this week"
164
+ saber "show me orders with customer details for this week"
162
165
 
163
166
  # Aggregations
164
- saber query "what's the total revenue by product category?"
167
+ saber "what's the total revenue by product category?"
165
168
 
166
169
  # Date filtering
167
- saber query "list users who haven't logged in for 30 days"
170
+ saber "list users who haven't logged in for 30 days"
168
171
 
169
172
  # Data exploration
170
- saber query "show me the distribution of customer ages"
173
+ saber "show me the distribution of customer ages"
171
174
 
172
175
  # Business analytics
173
- saber query "which products had the highest sales growth last quarter?"
176
+ saber "which products had the highest sales growth last quarter?"
177
+
178
+ # Start interactive mode
179
+ saber
174
180
  ```
175
181
 
176
182
  ## MCP Server Integration
@@ -105,7 +105,7 @@ saber memory list
105
105
  Start an interactive session:
106
106
 
107
107
  ```bash
108
- saber query
108
+ saber
109
109
  ```
110
110
 
111
111
  > You can also add memories in an interactive session by starting your message with the `#` sign
@@ -115,7 +115,7 @@ saber query
115
115
  Execute a single natural language query:
116
116
 
117
117
  ```bash
118
- saber query "show me all users created this month"
118
+ saber "show me all users created this month"
119
119
  ```
120
120
 
121
121
  ### Database Selection
@@ -123,33 +123,39 @@ saber query "show me all users created this month"
123
123
  Use a specific database connection:
124
124
 
125
125
  ```bash
126
- # Use named database from config
127
- saber query -d mydb "count all orders"
126
+ # Interactive mode with specific database
127
+ saber -d mydb
128
+
129
+ # Single query with specific database
130
+ saber -d mydb "count all orders"
128
131
  ```
129
132
 
130
133
  ## Examples
131
134
 
132
135
  ```bash
133
136
  # Show database schema
134
- saber query "what tables are in my database?"
137
+ saber "what tables are in my database?"
135
138
 
136
139
  # Count records
137
- saber query "how many active users do we have?"
140
+ saber "how many active users do we have?"
138
141
 
139
142
  # Complex queries with joins
140
- saber query "show me orders with customer details for this week"
143
+ saber "show me orders with customer details for this week"
141
144
 
142
145
  # Aggregations
143
- saber query "what's the total revenue by product category?"
146
+ saber "what's the total revenue by product category?"
144
147
 
145
148
  # Date filtering
146
- saber query "list users who haven't logged in for 30 days"
149
+ saber "list users who haven't logged in for 30 days"
147
150
 
148
151
  # Data exploration
149
- saber query "show me the distribution of customer ages"
152
+ saber "show me the distribution of customer ages"
150
153
 
151
154
  # Business analytics
152
- saber query "which products had the highest sales growth last quarter?"
155
+ saber "which products had the highest sales growth last quarter?"
156
+
157
+ # Start interactive mode
158
+ saber
153
159
  ```
154
160
 
155
161
  ## MCP Server Integration
@@ -1,12 +1,11 @@
1
1
  [project]
2
2
  name = "sqlsaber"
3
- version = "0.8.2"
3
+ version = "0.9.0"
4
4
  description = "SQLSaber - Agentic SQL assistant like Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  dependencies = [
8
8
  "asyncpg>=0.30.0",
9
- "typer>=0.16.0",
10
9
  "rich>=13.7.0",
11
10
  "anthropic>=0.54.0",
12
11
  "keyring>=25.6.0",
@@ -18,6 +17,7 @@ dependencies = [
18
17
  "pandas>=2.0.0",
19
18
  "fastmcp>=2.9.0",
20
19
  "uniplot>=0.21.2",
20
+ "cyclopts>=3.22.1",
21
21
  ]
22
22
 
23
23
  [tool.uv]
@@ -33,6 +33,5 @@ packages = ["src/sqlsaber"]
33
33
  [project.scripts]
34
34
  sqlsaber = "sqlsaber.cli.commands:main"
35
35
  saber = "sqlsaber.cli.commands:main"
36
- sql = "sqlsaber.cli.commands:main"
37
36
  sqlsaber-mcp = "sqlsaber.mcp.mcp:main"
38
37
  saber-mcp = "sqlsaber.mcp.mcp:main"
@@ -1,7 +1,7 @@
1
1
  """Authentication CLI commands."""
2
2
 
3
3
  import questionary
4
- import typer
4
+ import cyclopts
5
5
  from rich.console import Console
6
6
 
7
7
  from sqlsaber.config.auth import AuthConfigManager, AuthMethod
@@ -12,15 +12,14 @@ console = Console()
12
12
  config_manager = AuthConfigManager()
13
13
 
14
14
  # Create the authentication management CLI app
15
- auth_app = typer.Typer(
15
+ auth_app = cyclopts.App(
16
16
  name="auth",
17
17
  help="Manage authentication configuration",
18
- add_completion=True,
19
18
  )
20
19
 
21
20
 
22
- @auth_app.command("setup")
23
- def setup_auth():
21
+ @auth_app.command
22
+ def setup():
24
23
  """Configure authentication method for SQLSaber."""
25
24
  console.print("\n[bold]SQLSaber Authentication Setup[/bold]\n")
26
25
 
@@ -79,8 +78,8 @@ def setup_auth():
79
78
  )
80
79
 
81
80
 
82
- @auth_app.command("status")
83
- def show_auth_status():
81
+ @auth_app.command
82
+ def status():
84
83
  """Show current authentication configuration."""
85
84
  auth_method = config_manager.get_auth_method()
86
85
 
@@ -104,8 +103,8 @@ def show_auth_status():
104
103
  console.print("[yellow]OAuth token missing or expired[/yellow]")
105
104
 
106
105
 
107
- @auth_app.command("reset")
108
- def reset_auth():
106
+ @auth_app.command
107
+ def reset():
109
108
  """Reset authentication configuration."""
110
109
  if not config_manager.has_auth_configured():
111
110
  console.print("[yellow]No authentication configuration to reset.[/yellow]")
@@ -137,6 +136,6 @@ def reset_auth():
137
136
  console.print("Reset cancelled.")
138
137
 
139
138
 
140
- def create_auth_app() -> typer.Typer:
139
+ def create_auth_app() -> cyclopts.App:
141
140
  """Return the authentication management CLI app."""
142
141
  return auth_app
@@ -1,9 +1,11 @@
1
1
  """CLI command definitions and handlers."""
2
2
 
3
3
  import asyncio
4
+ import sys
4
5
  from pathlib import Path
6
+ from typing import Annotated
5
7
 
6
- import typer
8
+ import cyclopts
7
9
  from rich.console import Console
8
10
 
9
11
  from sqlsaber.agents.anthropic import AnthropicSQLAgent
@@ -16,10 +18,18 @@ from sqlsaber.cli.streaming import StreamingQueryHandler
16
18
  from sqlsaber.config.database import DatabaseConfigManager
17
19
  from sqlsaber.database.connection import DatabaseConnection
18
20
 
19
- app = typer.Typer(
21
+
22
+ class CLIError(Exception):
23
+ """Exception raised for CLI errors that should result in exit."""
24
+
25
+ def __init__(self, message: str, exit_code: int = 1):
26
+ super().__init__(message)
27
+ self.exit_code = exit_code
28
+
29
+
30
+ app = cyclopts.App(
20
31
  name="sqlsaber",
21
32
  help="SQLSaber - Use the agent Luke!\n\nSQL assistant for your database",
22
- add_completion=True,
23
33
  )
24
34
 
25
35
 
@@ -27,40 +37,49 @@ console = Console()
27
37
  config_manager = DatabaseConfigManager()
28
38
 
29
39
 
30
- @app.callback()
31
- def main_callback(
32
- database: str | None = typer.Option(
33
- None,
34
- "--database",
35
- "-d",
36
- help="Database connection name (uses default if not specified)",
37
- ),
40
+ @app.meta.default
41
+ def meta_handler(
42
+ database: Annotated[
43
+ str | None,
44
+ cyclopts.Parameter(
45
+ ["--database", "-d"],
46
+ help="Database connection name (uses default if not specified)",
47
+ ),
48
+ ] = None,
38
49
  ):
39
50
  """
40
51
  Query your database using natural language.
41
52
 
42
53
  Examples:
43
- sb query # Start interactive mode
44
- sb query "show me all users" # Run a single query with default database
45
- sb query -d mydb "show me users" # Run a query with specific database
54
+ saber # Start interactive mode
55
+ saber "show me all users" # Run a single query with default database
56
+ saber -d mydb "show me users" # Run a query with specific database
46
57
  """
47
- pass
58
+ # Store database in app context for commands to access
59
+ app.meta["database"] = database
48
60
 
49
61
 
50
- @app.command()
62
+ @app.default
51
63
  def query(
52
- query_text: str | None = typer.Argument(
53
- None,
54
- help="SQL query in natural language (if not provided, starts interactive mode)",
55
- ),
56
- database: str | None = typer.Option(
57
- None,
58
- "--database",
59
- "-d",
60
- help="Database connection name (uses default if not specified)",
61
- ),
64
+ query_text: Annotated[
65
+ str | None,
66
+ cyclopts.Parameter(
67
+ help="SQL query in natural language (if not provided, starts interactive mode)",
68
+ ),
69
+ ] = None,
70
+ database: Annotated[
71
+ str | None,
72
+ cyclopts.Parameter(
73
+ ["--database", "-d"],
74
+ help="Database connection name (uses default if not specified)",
75
+ ),
76
+ ] = None,
62
77
  ):
63
- """Run a query against the database or start interactive mode."""
78
+ """Run a query against the database or start interactive mode.
79
+
80
+ When called without arguments, starts interactive mode.
81
+ When called with a query string, executes that query and exits.
82
+ """
64
83
 
65
84
  async def run_session():
66
85
  # Get database configuration or handle direct CSV file
@@ -69,35 +88,24 @@ def query(
69
88
  if database.endswith(".csv"):
70
89
  csv_path = Path(database).expanduser().resolve()
71
90
  if not csv_path.exists():
72
- console.print(
73
- f"[bold red]Error:[/bold red] CSV file '{database}' not found."
74
- )
75
- raise typer.Exit(1)
91
+ raise CLIError(f"CSV file '{database}' not found.")
76
92
  connection_string = f"csv:///{csv_path}"
77
93
  db_name = csv_path.stem
78
94
  else:
79
95
  # Look up configured database connection
80
96
  db_config = config_manager.get_database(database)
81
97
  if not db_config:
82
- console.print(
83
- f"[bold red]Error:[/bold red] Database connection '{database}' not found."
84
- )
85
- console.print(
86
- "Use 'sqlsaber db list' to see available connections."
98
+ raise CLIError(
99
+ f"Database connection '{database}' not found. Use 'sqlsaber db list' to see available connections."
87
100
  )
88
- raise typer.Exit(1)
89
101
  connection_string = db_config.to_connection_string()
90
102
  db_name = db_config.name
91
103
  else:
92
104
  db_config = config_manager.get_default_database()
93
105
  if not db_config:
94
- console.print(
95
- "[bold red]Error:[/bold red] No database connections configured."
96
- )
97
- console.print(
98
- "Use 'sqlsaber db add <name>' to add a database connection."
106
+ raise CLIError(
107
+ "No database connections configured. Use 'sqlsaber db add <name>' to add a database connection."
99
108
  )
100
- raise typer.Exit(1)
101
109
  connection_string = db_config.to_connection_string()
102
110
  db_name = db_config.name
103
111
 
@@ -105,10 +113,7 @@ def query(
105
113
  try:
106
114
  db_conn = DatabaseConnection(connection_string)
107
115
  except Exception as e:
108
- console.print(
109
- f"[bold red]Error creating database connection:[/bold red] {e}"
110
- )
111
- raise typer.Exit(1)
116
+ raise CLIError(f"Error creating database connection: {e}")
112
117
 
113
118
  # Create agent instance with database name for memory context
114
119
  agent = AnthropicSQLAgent(db_conn, db_name)
@@ -132,25 +137,29 @@ def query(
132
137
  await db_conn.close()
133
138
  console.print("\n[green]Goodbye![/green]")
134
139
 
135
- # Run the async function
136
- asyncio.run(run_session())
140
+ # Run the async function with proper error handling
141
+ try:
142
+ asyncio.run(run_session())
143
+ except CLIError as e:
144
+ console.print(f"[bold red]Error:[/bold red] {e}")
145
+ sys.exit(e.exit_code)
137
146
 
138
147
 
139
148
  # Add authentication management commands
140
149
  auth_app = create_auth_app()
141
- app.add_typer(auth_app, name="auth")
150
+ app.command(auth_app, name="auth")
142
151
 
143
152
  # Add database management commands after main callback is defined
144
153
  db_app = create_db_app()
145
- app.add_typer(db_app, name="db")
154
+ app.command(db_app, name="db")
146
155
 
147
156
  # Add memory management commands
148
157
  memory_app = create_memory_app()
149
- app.add_typer(memory_app, name="memory")
158
+ app.command(memory_app, name="memory")
150
159
 
151
160
  # Add model management commands
152
161
  models_app = create_models_app()
153
- app.add_typer(models_app, name="models")
162
+ app.command(models_app, name="models")
154
163
 
155
164
 
156
165
  def main():