cloudwright-ai-cli 0.2.27__tar.gz → 0.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.
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/PKG-INFO +1 -1
- cloudwright_ai_cli-0.3.0/cloudwright_cli/__init__.py +1 -0
- cloudwright_ai_cli-0.3.0/cloudwright_cli/commands/adr.py +193 -0
- cloudwright_ai_cli-0.3.0/cloudwright_cli/commands/security_cmd.py +98 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/main.py +4 -0
- cloudwright_ai_cli-0.2.27/cloudwright_cli/__init__.py +0 -1
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/.gitignore +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/README.md +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/__main__.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/__init__.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/analyze_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/catalog_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/chat.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/compare.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/cost.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/databricks_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/design.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/diff.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/drift_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/export.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/import_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/init_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/lint_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/modify_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/policy.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/refresh_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/score_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/validate.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/project.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/py.typed +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/utils.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/pyproject.toml +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/tests/__init__.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/tests/test_cli.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/tests/test_drift_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/tests/test_init.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/tests/test_modify_cmd.py +0 -0
- {cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.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: 0.
|
|
3
|
+
Version: 0.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__ = "0.3.0"
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from cloudwright_cli.utils import handle_error
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
_ADR_SYSTEM = """You generate Architecture Decision Records (ADRs) in MADR format.
|
|
14
|
+
|
|
15
|
+
Given an architecture spec as JSON, produce a markdown ADR with this exact structure:
|
|
16
|
+
|
|
17
|
+
# ADR: {name} — {key decision}
|
|
18
|
+
|
|
19
|
+
## Status
|
|
20
|
+
Proposed
|
|
21
|
+
|
|
22
|
+
## Context
|
|
23
|
+
{problem and why a decision is needed}
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
{the chosen architecture and key choices}
|
|
27
|
+
|
|
28
|
+
## Components
|
|
29
|
+
| ID | Service | Provider | Purpose |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
{component rows}
|
|
32
|
+
|
|
33
|
+
## Consequences
|
|
34
|
+
### Positive
|
|
35
|
+
- {benefits}
|
|
36
|
+
|
|
37
|
+
### Negative
|
|
38
|
+
- {trade-offs and risks}
|
|
39
|
+
|
|
40
|
+
## Alternatives Considered
|
|
41
|
+
{alternatives if any, otherwise note none documented}
|
|
42
|
+
|
|
43
|
+
## Cost Estimate
|
|
44
|
+
{monthly cost if available, otherwise omit}
|
|
45
|
+
|
|
46
|
+
Be concise. Focus on the WHY, not just what the architecture contains.
|
|
47
|
+
Respond with ONLY the markdown — no explanation, no code fences."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def adr(
|
|
51
|
+
ctx: typer.Context,
|
|
52
|
+
spec_file: Annotated[Path, typer.Argument(help="Path to ArchSpec YAML file", exists=True)],
|
|
53
|
+
output: Annotated[str | None, typer.Option("--output", "-o", help="Write ADR to this file")] = None,
|
|
54
|
+
title: Annotated[str | None, typer.Option("--title", help="ADR title (default: auto-generated)")] = None,
|
|
55
|
+
decision: Annotated[str | None, typer.Option("--decision", help="Specific decision to document")] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Generate an Architecture Decision Record from an ArchSpec."""
|
|
58
|
+
try:
|
|
59
|
+
from cloudwright import ArchSpec
|
|
60
|
+
|
|
61
|
+
spec = ArchSpec.from_file(spec_file)
|
|
62
|
+
text = _generate_adr(spec, title=title, decision=decision)
|
|
63
|
+
|
|
64
|
+
if output:
|
|
65
|
+
Path(output).write_text(text)
|
|
66
|
+
console.print(f"[green]ADR written to {output}[/green]")
|
|
67
|
+
else:
|
|
68
|
+
print(text)
|
|
69
|
+
|
|
70
|
+
except typer.Exit:
|
|
71
|
+
raise
|
|
72
|
+
except Exception as e:
|
|
73
|
+
handle_error(ctx, e)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _generate_adr(spec, *, title: str | None = None, decision: str | None = None) -> str:
|
|
77
|
+
try:
|
|
78
|
+
return _llm_adr(spec, title=title, decision=decision)
|
|
79
|
+
except Exception:
|
|
80
|
+
return _deterministic_adr(spec, title=title, decision=decision)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _llm_adr(spec, *, title: str | None, decision: str | None) -> str:
|
|
84
|
+
from cloudwright.architect import Architect
|
|
85
|
+
|
|
86
|
+
arch = Architect()
|
|
87
|
+
spec_summary = spec.model_dump_json(indent=2, exclude_none=True)
|
|
88
|
+
|
|
89
|
+
decision_hint = f"\nDocument this specific decision: {decision}" if decision else ""
|
|
90
|
+
title_hint = f"\nUse this ADR title: {title}" if title else ""
|
|
91
|
+
prompt = f"Generate an ADR for this architecture:{title_hint}{decision_hint}\n\n{spec_summary}"
|
|
92
|
+
|
|
93
|
+
text, _ = arch.llm.generate([{"role": "user", "content": prompt}], _ADR_SYSTEM, max_tokens=2000)
|
|
94
|
+
if not text.strip().startswith("#"):
|
|
95
|
+
raise ValueError("LLM did not return markdown ADR")
|
|
96
|
+
return text.strip()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _deterministic_adr(spec, *, title: str | None = None, decision: str | None = None) -> str:
|
|
100
|
+
adr_title = title or spec.name
|
|
101
|
+
key_decision = decision or _infer_key_decision(spec)
|
|
102
|
+
|
|
103
|
+
lines = [
|
|
104
|
+
f"# ADR: {adr_title} — {key_decision}",
|
|
105
|
+
"",
|
|
106
|
+
"## Status",
|
|
107
|
+
"Proposed",
|
|
108
|
+
"",
|
|
109
|
+
"## Context",
|
|
110
|
+
_build_context(spec),
|
|
111
|
+
"",
|
|
112
|
+
"## Decision",
|
|
113
|
+
_build_decision(spec),
|
|
114
|
+
"",
|
|
115
|
+
"## Components",
|
|
116
|
+
"| ID | Service | Provider | Purpose |",
|
|
117
|
+
"|---|---|---|---|",
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
for c in spec.components:
|
|
121
|
+
purpose = c.description or c.label
|
|
122
|
+
lines.append(f"| {c.id} | {c.service} | {c.provider} | {purpose} |")
|
|
123
|
+
|
|
124
|
+
lines += ["", "## Consequences"]
|
|
125
|
+
lines += _build_consequences(spec)
|
|
126
|
+
|
|
127
|
+
rationale = spec.metadata.get("rationale") or []
|
|
128
|
+
if rationale:
|
|
129
|
+
lines += ["", "## Alternatives Considered"]
|
|
130
|
+
for r in rationale:
|
|
131
|
+
if isinstance(r, dict):
|
|
132
|
+
lines.append(f"- **{r.get('decision', '')}**: {r.get('reason', '')}")
|
|
133
|
+
|
|
134
|
+
if spec.cost_estimate:
|
|
135
|
+
lines += [
|
|
136
|
+
"",
|
|
137
|
+
"## Cost Estimate",
|
|
138
|
+
f"Estimated monthly cost: ${spec.cost_estimate.monthly_total:,.2f} USD",
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
return "\n".join(lines)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _infer_key_decision(spec) -> str:
|
|
145
|
+
rationale = spec.metadata.get("rationale") or []
|
|
146
|
+
if rationale and isinstance(rationale[0], dict):
|
|
147
|
+
return rationale[0].get("decision", f"{spec.provider.upper()} architecture")
|
|
148
|
+
return f"{spec.provider.upper()} architecture"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _build_context(spec) -> str:
|
|
152
|
+
parts = [f"This architecture, {spec.name!r}, targets the {spec.provider.upper()} platform in region {spec.region}."]
|
|
153
|
+
if spec.constraints:
|
|
154
|
+
if spec.constraints.compliance:
|
|
155
|
+
parts.append(f"Compliance requirements: {', '.join(spec.constraints.compliance)}.")
|
|
156
|
+
if spec.constraints.budget_monthly:
|
|
157
|
+
parts.append(f"Monthly budget constraint: ${spec.constraints.budget_monthly:,.0f}.")
|
|
158
|
+
parts.append(f"It consists of {len(spec.components)} components across {len(spec.connections)} connections.")
|
|
159
|
+
return " ".join(parts)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _build_decision(spec) -> str:
|
|
163
|
+
rationale = spec.metadata.get("rationale") or []
|
|
164
|
+
if rationale:
|
|
165
|
+
items = []
|
|
166
|
+
for r in rationale:
|
|
167
|
+
if isinstance(r, dict):
|
|
168
|
+
items.append(f"- **{r.get('decision', '')}**: {r.get('reason', '')}")
|
|
169
|
+
if items:
|
|
170
|
+
return "\n".join(items)
|
|
171
|
+
|
|
172
|
+
services = ", ".join(c.service for c in spec.components[:5])
|
|
173
|
+
suffix = ", ..." if len(spec.components) > 5 else ""
|
|
174
|
+
return f"Selected architecture using: {services}{suffix}."
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _build_consequences(spec) -> list[str]:
|
|
178
|
+
lines = ["### Positive"]
|
|
179
|
+
suggestions = spec.metadata.get("suggestions") or []
|
|
180
|
+
|
|
181
|
+
positives = [
|
|
182
|
+
f"Established {spec.provider.upper()} native services reduce operational overhead.",
|
|
183
|
+
f"{len(spec.components)} components provide clear separation of concerns.",
|
|
184
|
+
]
|
|
185
|
+
lines += [f"- {p}" for p in positives]
|
|
186
|
+
|
|
187
|
+
lines += ["", "### Negative"]
|
|
188
|
+
negatives = ["Vendor lock-in to selected provider and service tier."]
|
|
189
|
+
if suggestions:
|
|
190
|
+
negatives.append("Additional configuration required: " + suggestions[0].lower() + ".")
|
|
191
|
+
lines += [f"- {n}" for n in negatives]
|
|
192
|
+
|
|
193
|
+
return lines
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
from cloudwright_cli.utils import handle_error
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
_SEVERITY_ORDER = {"critical": 0, "high": 1, "medium": 2, "low": 3, "none": 4}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def security_scan(
|
|
19
|
+
ctx: typer.Context,
|
|
20
|
+
spec_file: Annotated[Path, typer.Argument(help="Path to ArchSpec YAML file", exists=True)],
|
|
21
|
+
fail_on: Annotated[str, typer.Option("--fail-on", help="Fail on: critical, high, medium, none")] = "high",
|
|
22
|
+
output: Annotated[str | None, typer.Option("--output", "-o")] = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Scan an ArchSpec for security anti-patterns and misconfigurations."""
|
|
25
|
+
try:
|
|
26
|
+
from cloudwright import ArchSpec
|
|
27
|
+
from cloudwright.security import SecurityScanner
|
|
28
|
+
|
|
29
|
+
spec = ArchSpec.from_file(spec_file)
|
|
30
|
+
report = SecurityScanner().scan(spec)
|
|
31
|
+
|
|
32
|
+
if ctx.obj and ctx.obj.get("json"):
|
|
33
|
+
result = {
|
|
34
|
+
"passed": report.passed,
|
|
35
|
+
"findings": [
|
|
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
|
+
for f in report.findings
|
|
44
|
+
],
|
|
45
|
+
}
|
|
46
|
+
print(json.dumps(result, indent=2))
|
|
47
|
+
_maybe_exit(report, fail_on)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
console.print(f"\nSecurity Scan: {spec_file.name}\n")
|
|
51
|
+
|
|
52
|
+
if not report.findings:
|
|
53
|
+
console.print("[green][PASS][/green] No security findings detected.")
|
|
54
|
+
else:
|
|
55
|
+
for f in report.findings:
|
|
56
|
+
sev_upper = f.severity.upper()
|
|
57
|
+
if f.severity == "critical":
|
|
58
|
+
sev_text = Text(f"[{sev_upper}]", style="bold red")
|
|
59
|
+
elif f.severity == "high":
|
|
60
|
+
sev_text = Text(f"[{sev_upper}]", style="red")
|
|
61
|
+
elif f.severity == "medium":
|
|
62
|
+
sev_text = Text(f"[{sev_upper}]", style="yellow")
|
|
63
|
+
else:
|
|
64
|
+
sev_text = Text(f"[{sev_upper}]", style="dim")
|
|
65
|
+
|
|
66
|
+
line = Text(" ")
|
|
67
|
+
line.append_text(sev_text)
|
|
68
|
+
line.append(f" {f.message}")
|
|
69
|
+
console.print(line)
|
|
70
|
+
console.print(f" Remediation: {f.remediation}", style="dim")
|
|
71
|
+
console.print()
|
|
72
|
+
|
|
73
|
+
total = len(report.findings)
|
|
74
|
+
crit = report.critical_count
|
|
75
|
+
high = report.high_count
|
|
76
|
+
med = sum(1 for f in report.findings if f.severity == "medium")
|
|
77
|
+
|
|
78
|
+
console.print(f"{total} finding(s) ({crit} critical, {high} high, {med} medium)")
|
|
79
|
+
|
|
80
|
+
threshold = _SEVERITY_ORDER.get(fail_on, 1)
|
|
81
|
+
worst = min((_SEVERITY_ORDER.get(f.severity, 4) for f in report.findings), default=4)
|
|
82
|
+
status = "PASSED" if worst > threshold else "FAILED"
|
|
83
|
+
style = "green" if status == "PASSED" else "red"
|
|
84
|
+
console.print(f"Status: [{style}]{status}[/{style}] (fail-on={fail_on})")
|
|
85
|
+
|
|
86
|
+
_maybe_exit(report, fail_on)
|
|
87
|
+
|
|
88
|
+
except typer.Exit:
|
|
89
|
+
raise
|
|
90
|
+
except Exception as e:
|
|
91
|
+
handle_error(ctx, e)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _maybe_exit(report, fail_on: str) -> None:
|
|
95
|
+
threshold = _SEVERITY_ORDER.get(fail_on, 1)
|
|
96
|
+
for f in report.findings:
|
|
97
|
+
if _SEVERITY_ORDER.get(f.severity, 4) <= threshold:
|
|
98
|
+
raise typer.Exit(1)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
|
|
3
3
|
from cloudwright_cli import __version__
|
|
4
|
+
from cloudwright_cli.commands.adr import adr
|
|
4
5
|
from cloudwright_cli.commands.analyze_cmd import analyze
|
|
5
6
|
from cloudwright_cli.commands.catalog_cmd import catalog_app
|
|
6
7
|
from cloudwright_cli.commands.chat import chat
|
|
@@ -18,6 +19,7 @@ from cloudwright_cli.commands.modify_cmd import modify
|
|
|
18
19
|
from cloudwright_cli.commands.policy import policy
|
|
19
20
|
from cloudwright_cli.commands.refresh_cmd import refresh
|
|
20
21
|
from cloudwright_cli.commands.score_cmd import score
|
|
22
|
+
from cloudwright_cli.commands.security_cmd import security_scan
|
|
21
23
|
from cloudwright_cli.commands.validate import validate
|
|
22
24
|
|
|
23
25
|
|
|
@@ -65,4 +67,6 @@ app.command()(analyze)
|
|
|
65
67
|
app.command()(refresh)
|
|
66
68
|
app.command()(lint)
|
|
67
69
|
app.command()(databricks_validate)
|
|
70
|
+
app.command(name="security")(security_scan)
|
|
71
|
+
app.command(name="adr")(adr)
|
|
68
72
|
app.add_typer(catalog_app, name="catalog")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.27"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/analyze_cmd.py
RENAMED
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/catalog_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/databricks_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/drift_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/import_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/modify_cmd.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/refresh_cmd.py
RENAMED
|
File without changes
|
{cloudwright_ai_cli-0.2.27 → cloudwright_ai_cli-0.3.0}/cloudwright_cli/commands/score_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
|