cloudwright-ai-cli 1.2.1__tar.gz → 1.3.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.
Files changed (51) hide show
  1. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/PKG-INFO +1 -1
  2. cloudwright_ai_cli-1.3.0/cloudwright_cli/__init__.py +1 -0
  3. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/chat.py +26 -12
  4. cloudwright_ai_cli-1.3.0/tests/test_chat_debug.py +74 -0
  5. cloudwright_ai_cli-1.2.1/cloudwright_cli/__init__.py +0 -1
  6. cloudwright_ai_cli-1.2.1/tests/test_chat_debug.py +0 -55
  7. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/.gitignore +0 -0
  8. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/README.md +0 -0
  9. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/__main__.py +0 -0
  10. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/__init__.py +0 -0
  11. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/adr.py +0 -0
  12. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/analyze_cmd.py +0 -0
  13. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/catalog_cmd.py +0 -0
  14. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/chat_session.py +0 -0
  15. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/chat_streaming.py +0 -0
  16. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/chat_ui.py +0 -0
  17. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/compare.py +0 -0
  18. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/cost.py +0 -0
  19. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/databricks_cmd.py +0 -0
  20. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/design.py +0 -0
  21. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/diff.py +0 -0
  22. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/drift_cmd.py +0 -0
  23. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/export.py +0 -0
  24. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/import_cmd.py +0 -0
  25. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/init_cmd.py +0 -0
  26. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/lint_cmd.py +0 -0
  27. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/mcp_cmd.py +0 -0
  28. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/modify_cmd.py +0 -0
  29. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/policy.py +0 -0
  30. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/refresh_cmd.py +0 -0
  31. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/schema_cmd.py +0 -0
  32. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/score_cmd.py +0 -0
  33. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/security_cmd.py +0 -0
  34. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/commands/validate.py +0 -0
  35. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/completions.py +0 -0
  36. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/decorators.py +0 -0
  37. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/main.py +0 -0
  38. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/output.py +0 -0
  39. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/project.py +0 -0
  40. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/py.typed +0 -0
  41. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/cloudwright_cli/utils.py +0 -0
  42. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/pyproject.toml +0 -0
  43. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/__init__.py +0 -0
  44. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_chat_commands.py +0 -0
  45. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_chat_persistence.py +0 -0
  46. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_chat_streaming.py +0 -0
  47. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_cli.py +0 -0
  48. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_drift_cmd.py +0 -0
  49. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_init.py +0 -0
  50. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_modify_cmd.py +0 -0
  51. {cloudwright_ai_cli-1.2.1 → cloudwright_ai_cli-1.3.0}/tests/test_project.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudwright-ai-cli
3
- Version: 1.2.1
3
+ Version: 1.3.0
4
4
  Summary: CLI for Cloudwright architecture intelligence
5
5
  Project-URL: Homepage, https://github.com/xmpuspus/cloudwright
6
6
  Project-URL: Repository, https://github.com/xmpuspus/cloudwright
@@ -0,0 +1 @@
1
+ __version__ = "1.3.0"
@@ -1,13 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- import sys
4
+ import os
5
5
  import time
6
6
  from typing import Annotated
7
7
 
8
8
  import typer
9
9
  from cloudwright import ArchSpec, ConversationSession
10
10
  from cloudwright.ascii_diagram import render_ascii
11
+ from cloudwright.logging import configure_logging
11
12
  from cloudwright.session_store import SessionStore
12
13
  from rich.console import Console
13
14
  from rich.live import Live
@@ -23,21 +24,27 @@ from .chat_ui import _HELP, print_cost_summary, print_diff, run_validate
23
24
 
24
25
  console = Console()
25
26
 
27
+ DEFAULT_WEB_PORT = 8765
28
+
26
29
 
27
30
  def chat(
28
31
  web: Annotated[bool, typer.Option("--web", help="Launch web UI instead of terminal chat")] = False,
29
32
  resume: Annotated[str | None, typer.Option("--resume", help="Resume a saved session by ID")] = None,
30
33
  debug: Annotated[bool, typer.Option("--debug", help="Log LLM requests/responses to stderr")] = False,
34
+ port: Annotated[
35
+ int,
36
+ typer.Option("--port", help=f"Port for --web (default: {DEFAULT_WEB_PORT})"),
37
+ ] = DEFAULT_WEB_PORT,
31
38
  ) -> None:
32
39
  """Interactive architecture design chat."""
33
40
  if web:
34
- _launch_web()
41
+ _launch_web(port=port)
35
42
  return
36
43
 
37
44
  _run_terminal_chat(resume=resume, debug=debug)
38
45
 
39
46
 
40
- def _launch_web() -> None:
47
+ def _launch_web(port: int = DEFAULT_WEB_PORT) -> None:
41
48
  try:
42
49
  import cloudwright_web # type: ignore
43
50
  import uvicorn
@@ -49,18 +56,19 @@ def _launch_web() -> None:
49
56
 
50
57
  import socket
51
58
 
52
- port = 8000
53
- for candidate in range(8000, 8100):
54
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
55
- if s.connect_ex(("127.0.0.1", candidate)) != 0:
56
- port = candidate
57
- break
59
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
60
+ if s.connect_ex(("127.0.0.1", port)) == 0:
61
+ console.print(
62
+ f"[red]Error:[/red] port {port} is already in use. "
63
+ f"Pass --port to choose another (e.g. --port {port + 1})."
64
+ )
65
+ raise typer.Exit(1)
58
66
 
59
67
  import threading
60
68
  import webbrowser
61
69
 
62
70
  url = f"http://127.0.0.1:{port}"
63
- console.print(f"[cyan]Launching Cloudwright web UI on {url}[/cyan]")
71
+ console.print(f"\n[bold cyan]Cloudwright web UI:[/bold cyan] {url}\n")
64
72
 
65
73
  def _open_browser():
66
74
  time.sleep(1.5)
@@ -71,8 +79,14 @@ def _launch_web() -> None:
71
79
 
72
80
 
73
81
  def _run_terminal_chat(resume: str | None = None, debug: bool = False) -> None:
74
- if debug:
75
- logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
82
+ configure_logging()
83
+ env_level = os.environ.get("CLOUDWRIGHT_LOG_LEVEL", "").upper()
84
+ if debug or env_level == "DEBUG":
85
+ logging.getLogger().setLevel(logging.DEBUG)
86
+ logging.getLogger("cloudwright").setLevel(logging.DEBUG)
87
+ elif env_level in {"INFO", "WARNING", "ERROR", "CRITICAL"}:
88
+ logging.getLogger().setLevel(getattr(logging, env_level))
89
+ logging.getLogger("cloudwright").setLevel(getattr(logging, env_level))
76
90
 
77
91
  console.print(
78
92
  Panel(
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from unittest.mock import patch
5
+
6
+
7
+ class TestDebugMode:
8
+ def test_debug_enables_logging(self):
9
+ """--debug flag should set the cloudwright logger to DEBUG level.
10
+
11
+ Audit fix v1.3: previously called logging.basicConfig() which is a
12
+ no-op against the structlog-configured root logger. Now it sets
13
+ DEBUG on the root + cloudwright logger directly.
14
+ """
15
+ cloudwright_logger = logging.getLogger("cloudwright")
16
+ prior_level = cloudwright_logger.level
17
+ try:
18
+ with (
19
+ patch("cloudwright_cli.commands.chat.ConversationSession"),
20
+ patch("cloudwright_cli.commands.chat.SessionStore"),
21
+ patch("cloudwright_cli.commands.chat.Prompt.ask", side_effect=[KeyboardInterrupt]),
22
+ ):
23
+ from cloudwright_cli.commands.chat import _run_terminal_chat
24
+
25
+ _run_terminal_chat(debug=True)
26
+
27
+ assert cloudwright_logger.level == logging.DEBUG
28
+ finally:
29
+ cloudwright_logger.setLevel(prior_level)
30
+
31
+ def test_debug_flag_via_chat_entrypoint(self):
32
+ with (
33
+ patch("cloudwright_cli.commands.chat._run_terminal_chat") as mock_run,
34
+ patch("cloudwright_cli.commands.chat._launch_web"),
35
+ ):
36
+ from cloudwright_cli.commands.chat import chat
37
+
38
+ chat(web=False, resume=None, debug=True)
39
+
40
+ # The chat() entrypoint may pass extra args (port). Just assert debug=True.
41
+ assert mock_run.call_count == 1
42
+ kwargs = mock_run.call_args.kwargs
43
+ assert kwargs.get("debug") is True
44
+
45
+ def test_no_debug_flag_via_chat_entrypoint(self):
46
+ with (
47
+ patch("cloudwright_cli.commands.chat._run_terminal_chat") as mock_run,
48
+ patch("cloudwright_cli.commands.chat._launch_web"),
49
+ ):
50
+ from cloudwright_cli.commands.chat import chat
51
+
52
+ chat(web=False, resume=None, debug=False)
53
+
54
+ assert mock_run.call_count == 1
55
+ assert mock_run.call_args.kwargs.get("debug") is False
56
+
57
+ def test_log_level_env_var_debug(self, monkeypatch):
58
+ """CLOUDWRIGHT_LOG_LEVEL=DEBUG should also set DEBUG even without --debug."""
59
+ monkeypatch.setenv("CLOUDWRIGHT_LOG_LEVEL", "DEBUG")
60
+ cloudwright_logger = logging.getLogger("cloudwright")
61
+ prior_level = cloudwright_logger.level
62
+ try:
63
+ with (
64
+ patch("cloudwright_cli.commands.chat.ConversationSession"),
65
+ patch("cloudwright_cli.commands.chat.SessionStore"),
66
+ patch("cloudwright_cli.commands.chat.Prompt.ask", side_effect=[KeyboardInterrupt]),
67
+ ):
68
+ from cloudwright_cli.commands.chat import _run_terminal_chat
69
+
70
+ _run_terminal_chat(debug=False)
71
+
72
+ assert cloudwright_logger.level == logging.DEBUG
73
+ finally:
74
+ cloudwright_logger.setLevel(prior_level)
@@ -1 +0,0 @@
1
- __version__ = "1.2.1"
@@ -1,55 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import logging
4
- import sys
5
- from unittest.mock import patch
6
-
7
-
8
- class TestDebugMode:
9
- def test_debug_enables_logging(self):
10
- with (
11
- patch("cloudwright_cli.commands.chat.ConversationSession"),
12
- patch("cloudwright_cli.commands.chat.SessionStore"),
13
- patch("cloudwright_cli.commands.chat.Prompt.ask", side_effect=[KeyboardInterrupt]),
14
- patch("logging.basicConfig") as mock_basic,
15
- ):
16
- from cloudwright_cli.commands.chat import _run_terminal_chat
17
-
18
- _run_terminal_chat(debug=True)
19
-
20
- mock_basic.assert_called_once_with(stream=sys.stderr, level=logging.DEBUG)
21
-
22
- def test_no_debug_by_default(self):
23
- with (
24
- patch("cloudwright_cli.commands.chat.ConversationSession"),
25
- patch("cloudwright_cli.commands.chat.SessionStore"),
26
- patch("cloudwright_cli.commands.chat.Prompt.ask", side_effect=[KeyboardInterrupt]),
27
- patch("logging.basicConfig") as mock_basic,
28
- ):
29
- from cloudwright_cli.commands.chat import _run_terminal_chat
30
-
31
- _run_terminal_chat(debug=False)
32
-
33
- mock_basic.assert_not_called()
34
-
35
- def test_debug_flag_via_chat_entrypoint(self):
36
- with (
37
- patch("cloudwright_cli.commands.chat._run_terminal_chat") as mock_run,
38
- patch("cloudwright_cli.commands.chat._launch_web"),
39
- ):
40
- from cloudwright_cli.commands.chat import chat
41
-
42
- chat(web=False, resume=None, debug=True)
43
-
44
- mock_run.assert_called_once_with(resume=None, debug=True)
45
-
46
- def test_no_debug_flag_via_chat_entrypoint(self):
47
- with (
48
- patch("cloudwright_cli.commands.chat._run_terminal_chat") as mock_run,
49
- patch("cloudwright_cli.commands.chat._launch_web"),
50
- ):
51
- from cloudwright_cli.commands.chat import chat
52
-
53
- chat(web=False, resume=None, debug=False)
54
-
55
- mock_run.assert_called_once_with(resume=None, debug=False)