cloudwright-ai-cli 0.3.1__tar.gz → 0.3.2__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.
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/PKG-INFO +1 -1
- cloudwright_ai_cli-0.3.2/cloudwright_cli/__init__.py +1 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/adr.py +10 -7
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/cost.py +20 -1
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/design.py +11 -8
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/lint_cmd.py +9 -7
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/mcp_cmd.py +1 -2
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/modify_cmd.py +18 -12
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/schema_cmd.py +5 -7
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/security_cmd.py +9 -7
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/validate.py +9 -7
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/completions.py +20 -0
- cloudwright_ai_cli-0.3.1/cloudwright_cli/__init__.py +0 -1
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/.gitignore +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/README.md +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/__main__.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/__init__.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/analyze_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/catalog_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/chat.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/compare.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/databricks_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/diff.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/drift_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/export.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/import_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/init_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/policy.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/refresh_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/score_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/main.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/output.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/project.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/py.typed +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/utils.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/pyproject.toml +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/tests/__init__.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/tests/test_cli.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/tests/test_drift_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/tests/test_init.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/tests/test_modify_cmd.py +0 -0
- {cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/tests/test_project.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudwright-ai-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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__ = "0.3.2"
|
|
@@ -71,13 +71,16 @@ def adr(
|
|
|
71
71
|
from cloudwright.llm.anthropic import GENERATE_MODEL
|
|
72
72
|
|
|
73
73
|
spec_json = spec.model_dump_json(indent=2, exclude_none=True)
|
|
74
|
-
emit_dry_run(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
emit_dry_run(
|
|
75
|
+
ctx,
|
|
76
|
+
{
|
|
77
|
+
"model": GENERATE_MODEL,
|
|
78
|
+
"estimated_tokens": len(spec_json + _ADR_SYSTEM) // 4,
|
|
79
|
+
"max_tokens": 2000,
|
|
80
|
+
"system_prompt_preview": _ADR_SYSTEM,
|
|
81
|
+
"user_prompt_preview": f"Generate ADR for: {spec.name}",
|
|
82
|
+
},
|
|
83
|
+
)
|
|
81
84
|
|
|
82
85
|
text = _generate_adr(spec, title=title, decision=decision)
|
|
83
86
|
|
|
@@ -21,14 +21,33 @@ def cost(
|
|
|
21
21
|
pricing_tier: Annotated[
|
|
22
22
|
str | None, typer.Option(help="Pricing tier (on_demand, reserved_1yr, reserved_3yr, spot)")
|
|
23
23
|
] = None,
|
|
24
|
+
workload_profile: Annotated[
|
|
25
|
+
str | None,
|
|
26
|
+
typer.Option(
|
|
27
|
+
"--workload-profile",
|
|
28
|
+
"-w",
|
|
29
|
+
help="Workload sizing profile (small, medium, large, enterprise). "
|
|
30
|
+
"Sets realistic defaults for request volumes, storage, node counts, and data transfer.",
|
|
31
|
+
),
|
|
32
|
+
] = None,
|
|
24
33
|
) -> None:
|
|
25
34
|
"""Show cost breakdown for an architecture spec."""
|
|
35
|
+
if workload_profile:
|
|
36
|
+
from cloudwright.cost import VALID_WORKLOAD_PROFILES
|
|
37
|
+
|
|
38
|
+
if workload_profile not in VALID_WORKLOAD_PROFILES:
|
|
39
|
+
console.print(
|
|
40
|
+
f"[red]Invalid workload profile:[/red] {workload_profile!r}. "
|
|
41
|
+
f"Choose from: {', '.join(sorted(VALID_WORKLOAD_PROFILES))}"
|
|
42
|
+
)
|
|
43
|
+
raise typer.Exit(1)
|
|
44
|
+
|
|
26
45
|
spec = ArchSpec.from_file(spec_file)
|
|
27
46
|
|
|
28
47
|
# Compute cost estimate if not present
|
|
29
48
|
if not spec.cost_estimate:
|
|
30
49
|
engine = CostEngine()
|
|
31
|
-
spec.cost_estimate = engine.estimate(spec)
|
|
50
|
+
spec.cost_estimate = engine.estimate(spec, workload_profile=workload_profile)
|
|
32
51
|
|
|
33
52
|
if is_json_mode(ctx):
|
|
34
53
|
emit_success(ctx, {"estimate": spec.cost_estimate.model_dump(exclude_none=True)})
|
|
@@ -45,14 +45,17 @@ def design(
|
|
|
45
45
|
system = Architect._select_system_prompt(description)
|
|
46
46
|
if constraints:
|
|
47
47
|
system += _build_constraint_prompt(constraints)
|
|
48
|
-
emit_dry_run(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
emit_dry_run(
|
|
49
|
+
ctx,
|
|
50
|
+
{
|
|
51
|
+
"model": GENERATE_MODEL,
|
|
52
|
+
"estimated_tokens": len(system + description) // 4,
|
|
53
|
+
"max_tokens": 10000,
|
|
54
|
+
"system_prompt_preview": system[:200],
|
|
55
|
+
"user_prompt_preview": description,
|
|
56
|
+
"constraints": constraints.model_dump(exclude_none=True),
|
|
57
|
+
},
|
|
58
|
+
)
|
|
56
59
|
|
|
57
60
|
try:
|
|
58
61
|
architect = Architect()
|
|
@@ -32,13 +32,15 @@ def lint(
|
|
|
32
32
|
if is_json_mode(ctx):
|
|
33
33
|
if should_stream(ctx):
|
|
34
34
|
for w in warnings:
|
|
35
|
-
emit_stream(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
emit_stream(
|
|
36
|
+
{
|
|
37
|
+
"rule": w.rule,
|
|
38
|
+
"severity": w.severity,
|
|
39
|
+
"component": w.component,
|
|
40
|
+
"message": w.message,
|
|
41
|
+
"recommendation": w.recommendation,
|
|
42
|
+
}
|
|
43
|
+
)
|
|
42
44
|
return
|
|
43
45
|
result = [
|
|
44
46
|
{
|
|
@@ -21,8 +21,7 @@ def mcp_serve(
|
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
|
|
23
23
|
Console(stderr=True).print(
|
|
24
|
-
"[red]Error:[/red] cloudwright-ai-mcp not installed.\n"
|
|
25
|
-
" Install: pip install cloudwright-ai-mcp"
|
|
24
|
+
"[red]Error:[/red] cloudwright-ai-mcp not installed.\n Install: pip install cloudwright-ai-mcp"
|
|
26
25
|
)
|
|
27
26
|
raise typer.Exit(1) from None
|
|
28
27
|
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/modify_cmd.py
RENAMED
|
@@ -50,13 +50,16 @@ def modify(
|
|
|
50
50
|
from cloudwright.llm.anthropic import GENERATE_MODEL
|
|
51
51
|
|
|
52
52
|
spec_text = original.to_yaml()
|
|
53
|
-
emit_dry_run(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
emit_dry_run(
|
|
54
|
+
ctx,
|
|
55
|
+
{
|
|
56
|
+
"model": GENERATE_MODEL,
|
|
57
|
+
"estimated_tokens": len(spec_text + instruction) // 4,
|
|
58
|
+
"max_tokens": 8000,
|
|
59
|
+
"user_prompt_preview": f"Modify: {instruction}",
|
|
60
|
+
"constraints": {"spec_file": spec_file, "instruction": instruction},
|
|
61
|
+
},
|
|
62
|
+
)
|
|
60
63
|
|
|
61
64
|
with console.status("Applying modification..."):
|
|
62
65
|
modified = architect.modify(original, instruction)
|
|
@@ -73,11 +76,14 @@ def modify(
|
|
|
73
76
|
diff_result = Differ().diff(original_costed, modified_costed)
|
|
74
77
|
|
|
75
78
|
if is_json_mode(ctx):
|
|
76
|
-
emit_success(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
emit_success(
|
|
80
|
+
ctx,
|
|
81
|
+
{
|
|
82
|
+
"original": original.model_dump(),
|
|
83
|
+
"modified": modified.model_dump(),
|
|
84
|
+
"diff": diff_result.model_dump(),
|
|
85
|
+
},
|
|
86
|
+
)
|
|
81
87
|
return
|
|
82
88
|
|
|
83
89
|
console.print(Rule("[bold]Changes[/bold]"))
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/schema_cmd.py
RENAMED
|
@@ -47,8 +47,7 @@ def _show_service(ctx: typer.Context, query: str) -> None:
|
|
|
47
47
|
if not svc_def:
|
|
48
48
|
available = [s.service_key for s in registry.list_services(provider)[:15]]
|
|
49
49
|
raise ValueError(
|
|
50
|
-
f"Service '{service_key}' not found for provider '{provider}'. "
|
|
51
|
-
f"Available: {', '.join(available)}"
|
|
50
|
+
f"Service '{service_key}' not found for provider '{provider}'. Available: {', '.join(available)}"
|
|
52
51
|
)
|
|
53
52
|
|
|
54
53
|
equivalents = {}
|
|
@@ -80,7 +79,9 @@ def _show_service(ctx: typer.Context, query: str) -> None:
|
|
|
80
79
|
Panel(
|
|
81
80
|
f"[bold]{svc_def.name}[/bold] ({provider}.{service_key})\n"
|
|
82
81
|
f"Category: {svc_def.category} | Pricing: {svc_def.pricing_formula}\n"
|
|
83
|
-
f"{svc_def.description}"
|
|
82
|
+
f"{svc_def.description}"
|
|
83
|
+
if svc_def.description
|
|
84
|
+
else "",
|
|
84
85
|
title="Service Schema",
|
|
85
86
|
)
|
|
86
87
|
)
|
|
@@ -147,10 +148,7 @@ def _show_compliance(ctx: typer.Context, framework: str) -> None:
|
|
|
147
148
|
data = {
|
|
148
149
|
"framework": result.framework,
|
|
149
150
|
"total_checks": len(result.checks),
|
|
150
|
-
"checks": [
|
|
151
|
-
{"name": c.name, "category": c.category, "severity": c.severity}
|
|
152
|
-
for c in result.checks
|
|
153
|
-
],
|
|
151
|
+
"checks": [{"name": c.name, "category": c.category, "severity": c.severity} for c in result.checks],
|
|
154
152
|
}
|
|
155
153
|
|
|
156
154
|
if is_json_mode(ctx):
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/security_cmd.py
RENAMED
|
@@ -32,13 +32,15 @@ def security_scan(
|
|
|
32
32
|
if is_json_mode(ctx):
|
|
33
33
|
if should_stream(ctx):
|
|
34
34
|
for f in report.findings:
|
|
35
|
-
emit_stream(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
emit_stream(
|
|
36
|
+
{
|
|
37
|
+
"severity": f.severity,
|
|
38
|
+
"rule": f.rule,
|
|
39
|
+
"component_id": f.component_id,
|
|
40
|
+
"message": f.message,
|
|
41
|
+
"remediation": f.remediation,
|
|
42
|
+
}
|
|
43
|
+
)
|
|
42
44
|
else:
|
|
43
45
|
result = {
|
|
44
46
|
"passed": report.passed,
|
|
@@ -68,13 +68,15 @@ def validate(
|
|
|
68
68
|
if should_stream(ctx):
|
|
69
69
|
for result in results:
|
|
70
70
|
for check in result.checks:
|
|
71
|
-
emit_stream(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
emit_stream(
|
|
72
|
+
{
|
|
73
|
+
"framework": result.framework,
|
|
74
|
+
"check": check.name,
|
|
75
|
+
"passed": check.passed,
|
|
76
|
+
"detail": check.detail,
|
|
77
|
+
"recommendation": check.recommendation,
|
|
78
|
+
}
|
|
79
|
+
)
|
|
78
80
|
else:
|
|
79
81
|
emit_success(ctx, {"results": [r.model_dump(exclude_none=True) for r in results]})
|
|
80
82
|
return
|
|
@@ -24,6 +24,26 @@ def complete_compliance(incomplete: str) -> list[tuple[str, str]]:
|
|
|
24
24
|
return [(f, h) for f, h in frameworks if f.startswith(incomplete)]
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def complete_pricing_tier(incomplete: str) -> list[tuple[str, str]]:
|
|
28
|
+
tiers = [
|
|
29
|
+
("on_demand", "Standard on-demand pricing"),
|
|
30
|
+
("reserved_1yr", "1-year reserved (40% savings)"),
|
|
31
|
+
("reserved_3yr", "3-year reserved (60% savings)"),
|
|
32
|
+
("spot", "Spot/preemptible (70% savings)"),
|
|
33
|
+
]
|
|
34
|
+
return [(t, h) for t, h in tiers if t.startswith(incomplete)]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def complete_workload_profile(incomplete: str) -> list[tuple[str, str]]:
|
|
38
|
+
profiles = [
|
|
39
|
+
("small", "Startup/dev — low traffic, minimal redundancy"),
|
|
40
|
+
("medium", "Production — moderate traffic, multi-AZ databases"),
|
|
41
|
+
("large", "Scale — high traffic, large clusters and storage"),
|
|
42
|
+
("enterprise", "Enterprise — very high traffic, full redundancy"),
|
|
43
|
+
]
|
|
44
|
+
return [(p, h) for p, h in profiles if p.startswith(incomplete)]
|
|
45
|
+
|
|
46
|
+
|
|
27
47
|
def complete_export_format(incomplete: str) -> list[tuple[str, str]]:
|
|
28
48
|
formats = [
|
|
29
49
|
("terraform", "HashiCorp Terraform HCL"),
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/analyze_cmd.py
RENAMED
|
File without changes
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/catalog_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/databricks_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/import_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.3.1 → cloudwright_ai_cli-0.3.2}/cloudwright_cli/commands/refresh_cmd.py
RENAMED
|
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
|