refactorai-cli 0.2.14__tar.gz → 0.2.15__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.14 → refactorai_cli-0.2.15}/PKG-INFO +1 -1
  2. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/pyproject.toml +1 -1
  3. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/run_cmds.py +189 -17
  5. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/PKG-INFO +1 -1
  6. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/README.md +0 -0
  7. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/auth.py +0 -0
  8. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/client.py +0 -0
  9. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/__init__.py +0 -0
  10. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/auth_cmds.py +0 -0
  11. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/engine_cmds.py +0 -0
  12. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/model_cmds.py +0 -0
  13. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/rules_cmds.py +0 -0
  14. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/runtime_cmds.py +0 -0
  15. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  16. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/commands/setup_cmds.py +0 -0
  17. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/control_plane.py +0 -0
  18. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/credentials.py +0 -0
  19. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/local_constitution.py +0 -0
  20. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/local_engine_runtime.py +0 -0
  21. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/local_paths.py +0 -0
  22. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/main.py +0 -0
  23. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/model_policy.py +0 -0
  24. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/runtime_manager.py +0 -0
  25. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/settings.py +0 -0
  26. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli/setup_flow.py +0 -0
  27. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  28. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  29. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/entry_points.txt +0 -0
  30. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/requires.txt +0 -0
  31. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/top_level.txt +0 -0
  32. {refactorai_cli-0.2.14 → refactorai_cli-0.2.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.14
3
+ Version: 0.2.15
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.14"
3
+ version = "0.2.15"
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.14"
8
+ __version__ = "0.2.15"
@@ -46,13 +46,18 @@ from refactor_core.budgeting import compute_budget
46
46
  from refactor_core.capabilities import resolve_capabilities
47
47
  from refactor_core.execution import resolve_mode, run_with_mode
48
48
  from refactor_core.execution.rr_executor import run_refactor_requests
49
+ from refactor_core.execution.structural_ops import structural_compatibility_matrix
49
50
  from refactor_core.gate import evaluate_gate
50
51
  from refactor_core.refactor_requests import (
52
+ DEFERRED,
51
53
  clear_requests,
52
54
  create_requests,
53
55
  list_requests,
56
+ load_request,
54
57
  pending_requests,
58
+ save_request,
55
59
  )
60
+ from refactor_core.refactor_ops import remediation_hint
56
61
  from refactor_core.indexing import index_target
57
62
  from refactor_core.models import Finding, GeneratedTest, PatchGroup, RunArtifact
58
63
  from refactor_core.pipeline import build_refactor_prompt, build_review_prompt
@@ -486,6 +491,20 @@ def _rr_progress_logger(status=None) -> Callable[[dict], None]:
486
491
  elif ev == "run_done":
487
492
  if status:
488
493
  status.update("[green]code[/green] completed.")
494
+ elif ev == "rr_deferred":
495
+ if status:
496
+ status.update("[yellow]code[/yellow] deferred unsupported operation...")
497
+ reason = str(event.get("reason", "") or "")[:300]
498
+ hint = remediation_hint(reason)
499
+ suffix = f" [dim]hint: {hint}[/dim]" if hint else ""
500
+ console.print(f"[yellow] ↷ deferred:[/yellow] {reason}{suffix}")
501
+ elif ev == "rr_failed":
502
+ if status:
503
+ status.update("[red]code[/red] failed unsupported operation.")
504
+ reason = str(event.get("reason", "") or "")[:300]
505
+ hint = remediation_hint(reason)
506
+ suffix = f" [dim]hint: {hint}[/dim]" if hint else ""
507
+ console.print(f"[red] ✗ failed:[/red] {reason}{suffix}")
489
508
 
490
509
  return _log
491
510
 
@@ -1573,6 +1592,16 @@ def code(
1573
1592
  "--auto-approve",
1574
1593
  help="Skip interactive issue approvals in agentic code mode.",
1575
1594
  ),
1595
+ on_rr_failure: str = typer.Option(
1596
+ "stop",
1597
+ "--on-rr-failure",
1598
+ help="RR failure behavior: stop | continue | defer.",
1599
+ ),
1600
+ equivalence: str = typer.Option(
1601
+ "strict",
1602
+ "--equivalence",
1603
+ help="Equivalence policy: strict | balanced.",
1604
+ ),
1576
1605
  ) -> None:
1577
1606
  """Work through refactor requests one by one, applying & verifying each.
1578
1607
 
@@ -1656,19 +1685,21 @@ def code(
1656
1685
  return
1657
1686
 
1658
1687
  with console.status("[cyan]code[/cyan] running...", spinner="dots") as status:
1688
+ rr_config = (
1689
+ dict(getattr(project_config, "settings", {}) or {})
1690
+ | ({"sandbox": {"enabled": True}} if sandbox_on else {})
1691
+ | ({"_sandbox_context": sandbox_ctx} if sandbox_ctx else {})
1692
+ ) or dict(getattr(constitution, "settings", {}) or {})
1659
1693
  artifact = run_refactor_requests(
1660
1694
  project_root=project_root,
1661
1695
  constitution=constitution,
1662
1696
  provider=selected_provider,
1663
1697
  requests=requests,
1664
- config=(
1665
- dict(getattr(project_config, "settings", {}) or {})
1666
- | ({"sandbox": {"enabled": True}} if sandbox_on else {})
1667
- | ({"_sandbox_context": sandbox_ctx} if sandbox_ctx else {})
1668
- )
1669
- or dict(getattr(constitution, "settings", {}) or {}),
1698
+ config=rr_config,
1670
1699
  progress_cb=_rr_progress_logger(status=status),
1671
1700
  approval_cb=_rr_approval_prompt(auto_approve=auto_approve),
1701
+ on_rr_failure=on_rr_failure,
1702
+ equivalence_mode=equivalence,
1672
1703
  )
1673
1704
  except Exception as exc:
1674
1705
  console.print(f"[red]Refactor failed:[/red] {exc}")
@@ -1686,32 +1717,137 @@ def code(
1686
1717
  )
1687
1718
 
1688
1719
 
1689
- def requests() -> None:
1690
- """List refactor requests and their status for this project."""
1720
+ def requests(
1721
+ show_operation: bool = typer.Option(
1722
+ False,
1723
+ "--show-operation",
1724
+ help="Show operation scope details (source/target/split map).",
1725
+ ),
1726
+ retry_deferred: bool = typer.Option(
1727
+ False,
1728
+ "--retry-deferred",
1729
+ help="Re-run deferred requests immediately.",
1730
+ ),
1731
+ provider: str | None = typer.Option(
1732
+ None,
1733
+ "--provider",
1734
+ help="Provider override when --retry-deferred is used.",
1735
+ ),
1736
+ auto_approve: bool = typer.Option(
1737
+ False,
1738
+ "--auto-approve",
1739
+ help="Auto-approve retry-deferred proposals.",
1740
+ ),
1741
+ on_rr_failure: str = typer.Option(
1742
+ "stop",
1743
+ "--on-rr-failure",
1744
+ help="RR failure behavior for retry: stop | continue | defer.",
1745
+ ),
1746
+ equivalence: str = typer.Option(
1747
+ "strict",
1748
+ "--equivalence",
1749
+ help="Equivalence policy for retry: strict | balanced.",
1750
+ ),
1751
+ ) -> None:
1752
+ """List refactor requests and optionally re-run deferred requests."""
1691
1753
  _require_auth()
1692
- project_root, _constitution, _config = _load_project()
1754
+ project_root, constitution, project_config = _load_project()
1755
+ if retry_deferred:
1756
+ deferred_rrs = [rr for rr in list_requests(project_root) if rr.status == DEFERRED]
1757
+ if not deferred_rrs:
1758
+ console.print("[dim]No deferred requests to retry.[/dim]")
1759
+ else:
1760
+ selected_provider = get_provider(
1761
+ constitution, project_root=project_root, provider_name=provider
1762
+ )
1763
+ for rr in deferred_rrs:
1764
+ rr.status = "pending"
1765
+ loaded = load_request(project_root, rr.rr_id)
1766
+ if loaded is not None:
1767
+ loaded.status = "pending"
1768
+ save_request(project_root, loaded)
1769
+ with console.status("[cyan]requests[/cyan] retrying deferred...", spinner="dots") as status:
1770
+ _ = run_refactor_requests(
1771
+ project_root=project_root,
1772
+ constitution=constitution,
1773
+ provider=selected_provider,
1774
+ requests=[rr for rr in list_requests(project_root) if rr.status == "pending"],
1775
+ config=dict(getattr(project_config, "settings", {}) or {}),
1776
+ progress_cb=_rr_progress_logger(status=status),
1777
+ approval_cb=_rr_approval_prompt(auto_approve=auto_approve),
1778
+ on_rr_failure=on_rr_failure,
1779
+ equivalence_mode=equivalence,
1780
+ )
1693
1781
  rrs = list_requests(project_root)
1694
1782
  if not rrs:
1695
1783
  console.print("[dim]No refactor requests. Run `refactor review` to create them.[/dim]")
1696
1784
  return
1697
1785
  table = Table(title="Refactor requests", show_lines=False)
1698
1786
  table.add_column("issue")
1787
+ table.add_column("operation", no_wrap=True)
1788
+ if show_operation:
1789
+ table.add_column("scope")
1699
1790
  table.add_column("severity", no_wrap=True)
1700
1791
  table.add_column("category")
1701
1792
  table.add_column("status", no_wrap=True)
1702
1793
  table.add_column("attempts", no_wrap=True)
1703
- color = {"applied": "green", "denied": "yellow", "failed": "red", "pending": "cyan", "in_progress": "cyan"}
1794
+ color = {
1795
+ "applied": "green",
1796
+ "denied": "yellow",
1797
+ "failed": "red",
1798
+ "deferred": "yellow",
1799
+ "pending": "cyan",
1800
+ "in_progress": "cyan",
1801
+ }
1704
1802
  for rr in rrs:
1705
- table.add_row(
1803
+ row = [
1706
1804
  rr.location,
1707
- rr.severity,
1708
- rr.category,
1709
- f"[{color.get(rr.status, 'white')}]{rr.status}[/{color.get(rr.status, 'white')}]",
1710
- str(rr.attempt_count),
1805
+ rr.operation_type,
1806
+ ]
1807
+ if show_operation:
1808
+ row.append(_format_operation_scope(rr))
1809
+ row.extend(
1810
+ [
1811
+ rr.severity,
1812
+ rr.category,
1813
+ f"[{color.get(rr.status, 'white')}]{rr.status}[/{color.get(rr.status, 'white')}]",
1814
+ str(rr.attempt_count),
1815
+ ]
1711
1816
  )
1817
+ table.add_row(*row)
1712
1818
  console.print(table)
1713
1819
 
1714
1820
 
1821
+ def _format_operation_scope(rr) -> str:
1822
+ scope = getattr(rr, "scope", None)
1823
+ if scope is None:
1824
+ return "-"
1825
+ source = str(getattr(scope, "source_path", "") or "").strip()
1826
+ target = str(getattr(scope, "target_path", "") or "").strip()
1827
+ split_map = dict(getattr(scope, "split_map", {}) or {})
1828
+ symbol = str(getattr(scope, "symbol", "") or "").strip()
1829
+ new_name = str(getattr(scope, "new_name", "") or "").strip()
1830
+ if split_map:
1831
+ if "__sources__" in split_map:
1832
+ srcs = ", ".join(str(item) for item in split_map.get("__sources__", []))
1833
+ return f"merge: [{srcs}] -> {target or '?'}"
1834
+ chunks = []
1835
+ for dst, symbols in split_map.items():
1836
+ if symbols:
1837
+ chunks.append(f"{dst}({','.join(str(s) for s in symbols)})")
1838
+ else:
1839
+ chunks.append(str(dst))
1840
+ src_label = source or "?"
1841
+ return f"split: {src_label} -> {', '.join(chunks)}"
1842
+ if source or target:
1843
+ if source and target:
1844
+ return f"{source} -> {target}"
1845
+ return source or target
1846
+ if symbol and new_name:
1847
+ return f"{symbol} -> {new_name}"
1848
+ return "-"
1849
+
1850
+
1715
1851
  def apply(
1716
1852
  force: bool = typer.Option(False, "--force", help="Apply even if the quality gate blocks."),
1717
1853
  ) -> None:
@@ -2113,6 +2249,7 @@ def doctor(
2113
2249
  )
2114
2250
 
2115
2251
  _report_intelligence(project_root)
2252
+ _report_structural_readiness(project_root, project_config)
2116
2253
 
2117
2254
  if skip_ping:
2118
2255
  console.print("[dim]Skipping provider live ping (--skip-ping).[/dim]")
@@ -2246,6 +2383,39 @@ def _report_sandbox(project_root: Path, config) -> None:
2246
2383
  console.print(f" [dim]latest command results: {len(latest.get('results', []))} (failures={failures})[/dim]")
2247
2384
 
2248
2385
 
2386
+ def _report_structural_readiness(project_root: Path, config) -> None:
2387
+ settings = dict(getattr(config, "settings", {}) or {})
2388
+ refactor_cfg = settings.get("refactor", {})
2389
+ if not isinstance(refactor_cfg, dict):
2390
+ refactor_cfg = {}
2391
+ structural = refactor_cfg.get("structural", {})
2392
+ if not isinstance(structural, dict):
2393
+ structural = {}
2394
+ eq_mode = str(structural.get("equivalence_mode", "strict") or "strict")
2395
+ rr_policy = str(structural.get("on_rr_failure", "stop") or "stop")
2396
+ rewrite_mode = str(structural.get("reference_rewrite", "auto") or "auto")
2397
+ langs = structural.get("languages", [])
2398
+ rollout = str(structural.get("rollout", "default") or "default")
2399
+ canary_percent = int(structural.get("canary_percent", 20) or 20)
2400
+ if not isinstance(langs, list):
2401
+ langs = []
2402
+ console.print(
2403
+ f"[bold]structural mode[/bold]: equivalence={eq_mode} on_rr_failure={rr_policy} "
2404
+ f"rollout={rollout} canary_percent={canary_percent}"
2405
+ )
2406
+ console.print(f"[bold]reference rewrite[/bold]: {rewrite_mode} [{', '.join(str(x) for x in langs) or 'defaults'}]")
2407
+ matrix = structural_compatibility_matrix()
2408
+ enabled_langs = [name for name, ok in matrix.items() if ok]
2409
+ disabled_langs = [name for name, ok in matrix.items() if not ok]
2410
+ console.print(
2411
+ f"[bold]adapter matrix[/bold]: enabled={', '.join(enabled_langs) or '-'}; "
2412
+ f"degraded={', '.join(disabled_langs) or '-'}"
2413
+ )
2414
+ rrs = list_requests(project_root)
2415
+ deferred = sum(1 for rr in rrs if rr.status == DEFERRED)
2416
+ console.print(f"[bold]deferred requests[/bold]: {deferred}")
2417
+
2418
+
2249
2419
  def _maybe_build_graph(project_root: Path, config) -> None:
2250
2420
  """Build + persist the symbol graph when intelligence.graph is enabled."""
2251
2421
  settings = dict(getattr(config, "settings", {}) or {})
@@ -2275,20 +2445,22 @@ def _print_rr_summary(artifact) -> None:
2275
2445
  return
2276
2446
  table = Table(title="Refactor request outcomes", show_lines=False)
2277
2447
  table.add_column("issue")
2448
+ table.add_column("operation", no_wrap=True)
2278
2449
  table.add_column("outcome", no_wrap=True)
2279
2450
  table.add_column("attempts", no_wrap=True)
2280
- color = {"applied": "green", "denied": "yellow", "failed": "red"}
2451
+ color = {"applied": "green", "denied": "yellow", "failed": "red", "deferred": "yellow"}
2281
2452
  for o in outcomes:
2282
2453
  outcome = str(o.get("outcome", "?"))
2283
2454
  table.add_row(
2284
2455
  str(o.get("location", "?")),
2456
+ str(o.get("operation_type", "content_patch")),
2285
2457
  f"[{color.get(outcome, 'white')}]{outcome}[/{color.get(outcome, 'white')}]",
2286
2458
  str(o.get("attempts", 0)),
2287
2459
  )
2288
2460
  console.print(table)
2289
2461
  console.print(
2290
2462
  f"[dim]applied={trace.get('applied', 0)} denied={trace.get('denied', 0)} "
2291
- f"failed={trace.get('failed', 0)}[/dim]"
2463
+ f"failed={trace.get('failed', 0)} deferred={trace.get('deferred', 0)}[/dim]"
2292
2464
  )
2293
2465
 
2294
2466
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.14
3
+ Version: 0.2.15
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown