cloudwright-ai-cli 1.3.0__tar.gz → 1.4.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.3.0 → cloudwright_ai_cli-1.4.0}/PKG-INFO +1 -1
  2. cloudwright_ai_cli-1.4.0/cloudwright_cli/__init__.py +1 -0
  3. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/chat_ui.py +1 -1
  4. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/export.py +32 -6
  5. cloudwright_ai_cli-1.4.0/cloudwright_cli/commands/import_live_cmd.py +136 -0
  6. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/completions.py +2 -0
  7. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/main.py +2 -0
  8. cloudwright_ai_cli-1.3.0/cloudwright_cli/__init__.py +0 -1
  9. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/.gitignore +0 -0
  10. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/README.md +0 -0
  11. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/__main__.py +0 -0
  12. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/__init__.py +0 -0
  13. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/adr.py +0 -0
  14. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/analyze_cmd.py +0 -0
  15. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/catalog_cmd.py +0 -0
  16. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/chat.py +0 -0
  17. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/chat_session.py +0 -0
  18. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/chat_streaming.py +0 -0
  19. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/compare.py +0 -0
  20. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/cost.py +0 -0
  21. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/databricks_cmd.py +0 -0
  22. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/design.py +0 -0
  23. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/diff.py +0 -0
  24. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/drift_cmd.py +0 -0
  25. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/import_cmd.py +0 -0
  26. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/init_cmd.py +0 -0
  27. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/lint_cmd.py +0 -0
  28. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/mcp_cmd.py +0 -0
  29. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/modify_cmd.py +0 -0
  30. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/policy.py +0 -0
  31. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/refresh_cmd.py +0 -0
  32. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/schema_cmd.py +0 -0
  33. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/score_cmd.py +0 -0
  34. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/security_cmd.py +0 -0
  35. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/commands/validate.py +0 -0
  36. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/decorators.py +0 -0
  37. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/output.py +0 -0
  38. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/project.py +0 -0
  39. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/py.typed +0 -0
  40. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/cloudwright_cli/utils.py +0 -0
  41. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/pyproject.toml +0 -0
  42. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/__init__.py +0 -0
  43. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_chat_commands.py +0 -0
  44. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_chat_debug.py +0 -0
  45. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_chat_persistence.py +0 -0
  46. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_chat_streaming.py +0 -0
  47. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_cli.py +0 -0
  48. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_drift_cmd.py +0 -0
  49. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_init.py +0 -0
  50. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.0}/tests/test_modify_cmd.py +0 -0
  51. {cloudwright_ai_cli-1.3.0 → cloudwright_ai_cli-1.4.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.3.0
3
+ Version: 1.4.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.4.0"
@@ -16,7 +16,7 @@ Commands:
16
16
  /yaml Show YAML for last architecture
17
17
  /cost Show cost estimate for last architecture
18
18
  /validate [fw] Run compliance check (hipaa, pci-dss, soc2, fedramp, gdpr)
19
- /export <fmt> Export last architecture (terraform, mermaid, d2, cloudformation, sbom, aibom)
19
+ /export <fmt> Export last architecture (terraform, pulumi-ts, pulumi-python, mermaid, d2, cloudformation, sbom, aibom)
20
20
  /terraform Export last architecture as Terraform
21
21
  /new Start a new architecture from scratch
22
22
  /help, /? Show this help
@@ -15,6 +15,10 @@ console = Console()
15
15
 
16
16
  _SYNTAX_MAP = {
17
17
  "terraform": "hcl",
18
+ "pulumi-ts": "typescript",
19
+ "pulumi-typescript": "typescript",
20
+ "pulumi-python": "python",
21
+ "pulumi-py": "python",
18
22
  "cloudformation": "yaml",
19
23
  "mermaid": "text",
20
24
  "d2": "text",
@@ -25,6 +29,26 @@ _SYNTAX_MAP = {
25
29
  "aibom": "json",
26
30
  }
27
31
 
32
+ # Formats that produce a multi-file project layout when --output points at a
33
+ # directory (or any extensionless path). Keep in sync with the dispatch in
34
+ # cloudwright.exporter.export_spec.
35
+ _DIRECTORY_FORMATS = {
36
+ "terraform",
37
+ "pulumi-ts",
38
+ "pulumi-typescript",
39
+ "pulumi-python",
40
+ "pulumi-py",
41
+ }
42
+
43
+ # Primary entry filename per directory-format, used only for status messages.
44
+ _DIRECTORY_ENTRY = {
45
+ "terraform": "main.tf",
46
+ "pulumi-ts": "index.ts",
47
+ "pulumi-typescript": "index.ts",
48
+ "pulumi-python": "__main__.py",
49
+ "pulumi-py": "__main__.py",
50
+ }
51
+
28
52
 
29
53
  def export(
30
54
  ctx: typer.Context,
@@ -39,9 +63,10 @@ def export(
39
63
  ],
40
64
  output: Annotated[Path | None, typer.Option("--output", "-o", help="Output file or directory")] = None,
41
65
  ) -> None:
42
- """Export an architecture spec to Terraform, CloudFormation, Mermaid, SVG, PNG, SBOM, or AIBOM."""
66
+ """Export an architecture spec to Terraform, Pulumi (TS/Python), CloudFormation, Mermaid, SVG, PNG, SBOM, or AIBOM."""
43
67
  fmt = format.lower().strip()
44
- if fmt not in FORMATS and fmt != "cfn":
68
+ _aliases = {"cfn", "pulumi-typescript", "pulumi-py"}
69
+ if fmt not in FORMATS and fmt not in _aliases:
45
70
  emit_error(ctx, ValueError(f"Unknown format {fmt!r}"), action=f"Use one of: {', '.join(FORMATS)}")
46
71
 
47
72
  if output:
@@ -55,11 +80,11 @@ def export(
55
80
  output_str = str(output) if output else None
56
81
  output_dir_str = None
57
82
 
58
- # Terraform with a directory target writes main.tf inside the dir
59
- if fmt == "terraform" and output and output.is_dir():
83
+ # Terraform / Pulumi with a directory target writes a project layout in the dir
84
+ if fmt in _DIRECTORY_FORMATS and output and output.is_dir():
60
85
  output_dir_str = output_str
61
86
  output_str = None
62
- elif fmt == "terraform" and output and not output.suffix:
87
+ elif fmt in _DIRECTORY_FORMATS and output and not output.suffix:
63
88
  # Treat extensionless output as a directory path
64
89
  output_dir_str = output_str
65
90
  output_str = None
@@ -104,7 +129,8 @@ def export(
104
129
 
105
130
  if output:
106
131
  if output_dir_str:
107
- console.print(f"[green]Written to {output_dir_str}/main.tf[/green]")
132
+ entry = _DIRECTORY_ENTRY.get(fmt, "main.tf")
133
+ console.print(f"[green]Written to {output_dir_str}/{entry}[/green]")
108
134
  else:
109
135
  console.print(f"[green]Written to {output}[/green]")
110
136
  else:
@@ -0,0 +1,136 @@
1
+ """Import live cloud infrastructure into an ArchSpec via provider APIs.
2
+
3
+ Currently supports AWS via boto3 (``cloudwright import-live --provider aws``).
4
+ GCP and Azure surface a clear ``not yet implemented`` error.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Annotated
13
+
14
+ import typer
15
+ from rich.console import Console
16
+
17
+ from cloudwright_cli.output import emit_success, err_console, is_json_mode, validate_output_path
18
+ from cloudwright_cli.utils import handle_error
19
+
20
+ console = Console()
21
+
22
+
23
+ def import_live(
24
+ ctx: typer.Context,
25
+ provider: Annotated[
26
+ str,
27
+ typer.Option("--provider", help="Cloud provider to scan: aws (gcp, azure not yet implemented)"),
28
+ ] = "aws",
29
+ region: Annotated[
30
+ str,
31
+ typer.Option("--region", help="Cloud region (e.g. us-east-1)"),
32
+ ] = "us-east-1",
33
+ profile: Annotated[
34
+ str | None,
35
+ typer.Option("--profile", help="AWS named profile (~/.aws/credentials)"),
36
+ ] = None,
37
+ services: Annotated[
38
+ str | None,
39
+ typer.Option(
40
+ "--services",
41
+ help="Comma-separated subset of services to scan (e.g. ec2,rds,s3). Default: all.",
42
+ ),
43
+ ] = None,
44
+ output: Annotated[
45
+ str | None,
46
+ typer.Option("--output", "-o", help="Write ArchSpec YAML to this file instead of stdout"),
47
+ ] = None,
48
+ name: Annotated[
49
+ str | None,
50
+ typer.Option("--name", help="Override the architecture name"),
51
+ ] = None,
52
+ ) -> None:
53
+ """Walk live AWS APIs and produce an ArchSpec from running infrastructure."""
54
+ try:
55
+ provider_norm = (provider or "aws").lower()
56
+ if provider_norm in {"gcp", "google", "azure"}:
57
+ raise typer.BadParameter(
58
+ f"--provider {provider!r} is not yet implemented. Only --provider aws is supported in v1.4."
59
+ )
60
+ if provider_norm != "aws":
61
+ raise typer.BadParameter(f"Unknown --provider {provider!r}. Supported: aws.")
62
+
63
+ try:
64
+ from cloudwright.importer.live_aws import (
65
+ SUPPORTED_SERVICES,
66
+ LiveImportError,
67
+ import_live_aws,
68
+ )
69
+ except ImportError as exc:
70
+ # boto3 not installed at all (cloudwright.importer.live_aws import boto3 lazily,
71
+ # but a missing core import surfaces here as a final fallback).
72
+ err_console.print(
73
+ "[red]boto3 is required for live AWS import.[/red] "
74
+ "Install with: pip install 'cloudwright-ai[live-import]'"
75
+ )
76
+ raise typer.Exit(code=1) from exc
77
+
78
+ services_list: list[str] | None = None
79
+ if services:
80
+ services_list = [s.strip().lower() for s in services.split(",") if s.strip()]
81
+ unknown = [s for s in services_list if s not in SUPPORTED_SERVICES]
82
+ if unknown:
83
+ raise typer.BadParameter(
84
+ f"Unknown service(s): {sorted(set(unknown))}. Supported: {list(SUPPORTED_SERVICES)}"
85
+ )
86
+
87
+ json_mode = is_json_mode(ctx)
88
+
89
+ def _progress(msg: str) -> None:
90
+ if not json_mode:
91
+ err_console.print(msg)
92
+
93
+ try:
94
+ spec = import_live_aws(
95
+ region=region,
96
+ profile=profile,
97
+ services=services_list,
98
+ progress=_progress,
99
+ name=name,
100
+ )
101
+ except LiveImportError as exc:
102
+ # Clean error path — credentials missing, profile not found, etc.
103
+ err_console.print(f"[red]error:[/red] {exc}")
104
+ raise typer.Exit(code=1) from exc
105
+
106
+ if name:
107
+ spec = spec.model_copy(update={"name": name})
108
+
109
+ if json_mode:
110
+ emit_success(ctx, {"spec": json.loads(spec.to_json())})
111
+ return
112
+
113
+ content = spec.to_yaml()
114
+ n_comps = len(spec.components)
115
+ n_bounds = len(spec.boundaries)
116
+
117
+ if output:
118
+ validate_output_path(output)
119
+ Path(output).write_text(content)
120
+ err_console.print()
121
+ err_console.print(
122
+ f"[green]Imported[/green] {n_comps} component(s), {n_bounds} boundary(ies) "
123
+ f"from {region} -> [bold]{output}[/bold]"
124
+ )
125
+ err_console.print(f"Run [bold]cloudwright cost {output}[/bold] to estimate.")
126
+ else:
127
+ sys.stdout.write(content)
128
+ err_console.print()
129
+ err_console.print(f"[green]Imported[/green] {n_comps} component(s), {n_bounds} boundary(ies) from {region}")
130
+
131
+ except typer.Exit:
132
+ raise
133
+ except typer.BadParameter:
134
+ raise
135
+ except Exception as e:
136
+ handle_error(ctx, e)
@@ -47,6 +47,8 @@ def complete_workload_profile(incomplete: str) -> list[tuple[str, str]]:
47
47
  def complete_export_format(incomplete: str) -> list[tuple[str, str]]:
48
48
  formats = [
49
49
  ("terraform", "HashiCorp Terraform HCL"),
50
+ ("pulumi-ts", "Pulumi TypeScript (@pulumi/aws, @pulumi/gcp, @pulumi/azure-native)"),
51
+ ("pulumi-python", "Pulumi Python (pulumi_aws, pulumi_gcp, pulumi_azure_native)"),
50
52
  ("cloudformation", "AWS CloudFormation YAML"),
51
53
  ("mermaid", "Mermaid diagram syntax"),
52
54
  ("d2", "D2 diagram language"),
@@ -13,6 +13,7 @@ from cloudwright_cli.commands.diff import diff
13
13
  from cloudwright_cli.commands.drift_cmd import drift
14
14
  from cloudwright_cli.commands.export import export
15
15
  from cloudwright_cli.commands.import_cmd import import_infra
16
+ from cloudwright_cli.commands.import_live_cmd import import_live
16
17
  from cloudwright_cli.commands.init_cmd import init
17
18
  from cloudwright_cli.commands.lint_cmd import lint
18
19
  from cloudwright_cli.commands.mcp_cmd import mcp_serve
@@ -68,6 +69,7 @@ app.command()(diff)
68
69
  app.command()(drift)
69
70
  app.command()(modify)
70
71
  app.command(name="import")(import_infra)
72
+ app.command(name="import-live")(import_live)
71
73
  app.command()(chat)
72
74
  app.command()(init)
73
75
  app.command()(policy)
@@ -1 +0,0 @@
1
- __version__ = "1.3.0"