refactorai-cli 0.2.15__tar.gz → 0.2.16__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 (32) hide show
  1. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/PKG-INFO +1 -1
  2. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/pyproject.toml +1 -1
  3. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/run_cmds.py +67 -6
  5. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/runtime_cmds.py +18 -1
  6. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/runtime_manager.py +38 -5
  7. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli.egg-info/PKG-INFO +1 -1
  8. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/README.md +0 -0
  9. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/auth.py +0 -0
  10. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/client.py +0 -0
  11. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/__init__.py +0 -0
  12. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/auth_cmds.py +0 -0
  13. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/engine_cmds.py +0 -0
  14. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/model_cmds.py +0 -0
  15. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/rules_cmds.py +0 -0
  16. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  17. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/commands/setup_cmds.py +0 -0
  18. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/control_plane.py +0 -0
  19. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/credentials.py +0 -0
  20. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/local_constitution.py +0 -0
  21. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/local_engine_runtime.py +0 -0
  22. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/local_paths.py +0 -0
  23. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/main.py +0 -0
  24. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/model_policy.py +0 -0
  25. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/settings.py +0 -0
  26. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli/setup_flow.py +0 -0
  27. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  28. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  29. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli.egg-info/entry_points.txt +0 -0
  30. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli.egg-info/requires.txt +0 -0
  31. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/refactorai_cli.egg-info/top_level.txt +0 -0
  32. {refactorai_cli-0.2.15 → refactorai_cli-0.2.16}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.15
3
+ Version: 0.2.16
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "refactorai-cli"
3
- version = "0.2.15"
3
+ version = "0.2.16"
4
4
  description = "Local-first CLI for the refactor platform"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -5,4 +5,4 @@ the shared `refactor_core` pipeline from a project folder while staying
5
5
  authenticated to the hosted platform via a developer key.
6
6
  """
7
7
 
8
- __version__ = "0.2.15"
8
+ __version__ = "0.2.16"
@@ -50,6 +50,8 @@ from refactor_core.execution.structural_ops import structural_compatibility_matr
50
50
  from refactor_core.gate import evaluate_gate
51
51
  from refactor_core.refactor_requests import (
52
52
  DEFERRED,
53
+ RequestSuppression,
54
+ RequestWarning,
53
55
  clear_requests,
54
56
  create_requests,
55
57
  list_requests,
@@ -1555,9 +1557,13 @@ def review(
1555
1557
  _print_findings(artifact.findings)
1556
1558
 
1557
1559
  # A fresh review supersedes prior refactor requests: replace them with one
1558
- # RR per finding (central-only; nothing is written into the project tree).
1559
- clear_requests(project_root)
1560
- requests = create_requests(project_root, artifact.findings, run_id=artifact.run_id)
1560
+ # RR per actionable finding (central-only; nothing is written into project tree).
1561
+ requests = _replace_requests_with_framework_guardrails(
1562
+ project_root=project_root,
1563
+ findings=artifact.findings,
1564
+ run_id=artifact.run_id,
1565
+ artifact=artifact,
1566
+ )
1561
1567
  if requests:
1562
1568
  console.print(
1563
1569
  f"\n[bold]Created {len(requests)} refactor request(s).[/bold] "
@@ -1666,9 +1672,11 @@ def code(
1666
1672
  review_store = create_run(project_root, run_id=review_artifact.run_id)
1667
1673
  review_store.write_artifact(review_artifact, target=target, status="completed")
1668
1674
  _print_findings(review_artifact.findings)
1669
- clear_requests(project_root)
1670
- requests = create_requests(
1671
- project_root, review_artifact.findings, run_id=review_artifact.run_id
1675
+ requests = _replace_requests_with_framework_guardrails(
1676
+ project_root=project_root,
1677
+ findings=review_artifact.findings,
1678
+ run_id=review_artifact.run_id,
1679
+ artifact=review_artifact,
1672
1680
  )
1673
1681
  console.print(f"[bold]Created {len(requests)} refactor request(s).[/bold]")
1674
1682
 
@@ -1848,6 +1856,59 @@ def _format_operation_scope(rr) -> str:
1848
1856
  return "-"
1849
1857
 
1850
1858
 
1859
+ def _replace_requests_with_framework_guardrails(
1860
+ *,
1861
+ project_root: Path,
1862
+ findings: list[Finding],
1863
+ run_id: str,
1864
+ artifact: RunArtifact | None = None,
1865
+ ) -> list:
1866
+ clear_requests(project_root)
1867
+ requests, suppressions, warnings = create_requests(
1868
+ project_root,
1869
+ findings,
1870
+ run_id=run_id,
1871
+ return_diagnostics=True,
1872
+ )
1873
+ _report_framework_suppressions(suppressions, artifact=artifact)
1874
+ _report_framework_warnings(warnings, artifact=artifact)
1875
+ return requests
1876
+
1877
+
1878
+ def _report_framework_suppressions(
1879
+ suppressions: list[RequestSuppression], *, artifact: RunArtifact | None = None
1880
+ ) -> None:
1881
+ if not suppressions:
1882
+ return
1883
+ console.print(
1884
+ f"[yellow]warning:[/yellow] suppressed {len(suppressions)} finding(s) due to framework invariants."
1885
+ )
1886
+ for item in suppressions:
1887
+ console.print(
1888
+ f"[yellow]-[/yellow] {item.location}: {item.message} "
1889
+ f"[dim](framework={item.framework or 'unknown'}, rule={item.rule_id or 'unspecified'})[/dim]"
1890
+ )
1891
+ if artifact is not None:
1892
+ artifact.notes.append(item.note())
1893
+
1894
+
1895
+ def _report_framework_warnings(
1896
+ warnings: list[RequestWarning], *, artifact: RunArtifact | None = None
1897
+ ) -> None:
1898
+ if not warnings:
1899
+ return
1900
+ console.print(
1901
+ f"[yellow]warning:[/yellow] flagged {len(warnings)} finding(s) with framework caution."
1902
+ )
1903
+ for item in warnings:
1904
+ console.print(
1905
+ f"[yellow]-[/yellow] {item.location}: {item.message} "
1906
+ f"[dim](framework={item.framework or 'unknown'}, rule={item.rule_id or 'unspecified'})[/dim]"
1907
+ )
1908
+ if artifact is not None:
1909
+ artifact.notes.append(item.note())
1910
+
1911
+
1851
1912
  def apply(
1852
1913
  force: bool = typer.Option(False, "--force", help="Apply even if the quality gate blocks."),
1853
1914
  ) -> None:
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import typer
6
6
  from rich.console import Console
7
+ from rich.progress import BarColumn, DownloadColumn, Progress, TaskID, TextColumn, TimeRemainingColumn, TransferSpeedColumn
7
8
 
8
9
  from refactorai_cli.auth import AuthError, ensure_authenticated
9
10
  from refactorai_cli.control_plane import ensure_lease, heartbeat, read_cached_lease, resolve_policy
@@ -62,7 +63,23 @@ def update(
62
63
  f"[bold]resolved manifest[/bold]: version={manifest.runtime_version} "
63
64
  f"channel={channel}"
64
65
  )
65
- artifact = download_artifact(manifest.artifact_url)
66
+ progress_columns = (
67
+ TextColumn("[progress.description]{task.description}"),
68
+ BarColumn(),
69
+ DownloadColumn(),
70
+ TransferSpeedColumn(),
71
+ TimeRemainingColumn(),
72
+ )
73
+ with Progress(*progress_columns, console=console, transient=True) as progress:
74
+ task_id: TaskID = progress.add_task("Downloading runtime artifact", total=None)
75
+
76
+ def _on_progress(downloaded: int, total: int | None) -> None:
77
+ update_kwargs = {"completed": int(max(downloaded, 0))}
78
+ if total is not None and total > 0:
79
+ update_kwargs["total"] = total
80
+ progress.update(task_id, **update_kwargs)
81
+
82
+ artifact = download_artifact(manifest.artifact_url, progress_cb=_on_progress)
66
83
  console.print(f"[bold]downloaded artifact[/bold]: {len(artifact)} bytes")
67
84
  if dry_run:
68
85
  # Verification happens in activate_runtime; dry-run still checks
@@ -14,6 +14,7 @@ import platform
14
14
  from dataclasses import dataclass
15
15
  from datetime import datetime, timezone
16
16
  from pathlib import Path
17
+ from typing import Callable
17
18
  from urllib.parse import urlparse
18
19
 
19
20
  import httpx
@@ -153,17 +154,49 @@ def resolve_runtime_manifest(*, channel: str = "stable", timeout: float = 20.0)
153
154
  return manifest
154
155
 
155
156
 
156
- def download_artifact(url: str, *, timeout: float = 60.0) -> bytes:
157
+ def download_artifact(
158
+ url: str,
159
+ *,
160
+ timeout: float = 60.0,
161
+ progress_cb: Callable[[int, int | None], None] | None = None,
162
+ ) -> bytes:
157
163
  headers: dict[str, str] = {}
158
164
  if _should_send_runtime_auth(url):
159
165
  headers["Authorization"] = _runtime_auth_header()
166
+ resolved_timeout = max(float(timeout), 300.0)
167
+ if progress_cb is None:
168
+ try:
169
+ response = httpx.get(url, headers=headers, timeout=resolved_timeout)
170
+ except httpx.HTTPError as exc:
171
+ raise RuntimeError(f"Could not download runtime artifact: {exc}") from exc
172
+ if response.status_code >= 400:
173
+ raise RuntimeError(f"Runtime artifact download failed ({response.status_code})")
174
+ return response.content
160
175
  try:
161
- response = httpx.get(url, headers=headers, timeout=max(float(timeout), 300.0))
176
+ with httpx.stream("GET", url, headers=headers, timeout=resolved_timeout) as response:
177
+ if response.status_code >= 400:
178
+ raise RuntimeError(f"Runtime artifact download failed ({response.status_code})")
179
+ total: int | None = None
180
+ total_header = str(response.headers.get("Content-Length") or "").strip()
181
+ if total_header:
182
+ try:
183
+ parsed_total = int(total_header)
184
+ except ValueError:
185
+ parsed_total = -1
186
+ if parsed_total > 0:
187
+ total = parsed_total
188
+ downloaded = 0
189
+ chunks: list[bytes] = []
190
+ progress_cb(downloaded, total)
191
+ for chunk in response.iter_bytes(chunk_size=64 * 1024):
192
+ if not chunk:
193
+ continue
194
+ chunks.append(chunk)
195
+ downloaded += len(chunk)
196
+ progress_cb(downloaded, total)
162
197
  except httpx.HTTPError as exc:
163
198
  raise RuntimeError(f"Could not download runtime artifact: {exc}") from exc
164
- if response.status_code >= 400:
165
- raise RuntimeError(f"Runtime artifact download failed ({response.status_code})")
166
- return response.content
199
+ return b"".join(chunks)
167
200
 
168
201
 
169
202
  def _should_send_runtime_auth(url: str) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.15
3
+ Version: 0.2.16
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown