refactorai-cli 0.2.13__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.13 → refactorai_cli-0.2.15}/PKG-INFO +1 -1
  2. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/pyproject.toml +1 -1
  3. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/run_cmds.py +227 -24
  5. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/PKG-INFO +1 -1
  6. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/README.md +0 -0
  7. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/auth.py +0 -0
  8. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/client.py +0 -0
  9. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/__init__.py +0 -0
  10. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/auth_cmds.py +0 -0
  11. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/engine_cmds.py +0 -0
  12. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/model_cmds.py +0 -0
  13. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/rules_cmds.py +0 -0
  14. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/runtime_cmds.py +0 -0
  15. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  16. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/commands/setup_cmds.py +0 -0
  17. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/control_plane.py +0 -0
  18. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/credentials.py +0 -0
  19. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/local_constitution.py +0 -0
  20. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/local_engine_runtime.py +0 -0
  21. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/local_paths.py +0 -0
  22. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/main.py +0 -0
  23. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/model_policy.py +0 -0
  24. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/runtime_manager.py +0 -0
  25. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/settings.py +0 -0
  26. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli/setup_flow.py +0 -0
  27. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  28. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  29. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/entry_points.txt +0 -0
  30. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/requires.txt +0 -0
  31. {refactorai_cli-0.2.13 → refactorai_cli-0.2.15}/refactorai_cli.egg-info/top_level.txt +0 -0
  32. {refactorai_cli-0.2.13 → 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.13
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.13"
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.13"
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
@@ -68,7 +73,9 @@ from refactor_core.sandbox_runtime import (
68
73
  detect_installer_in_sandbox,
69
74
  ensure_sandbox,
70
75
  exec_in_container,
76
+ exec_in_container_stream,
71
77
  exec_in_sandbox,
78
+ exec_in_sandbox_stream,
72
79
  remove_sandbox_container,
73
80
  sandbox_name,
74
81
  resolve_runtime,
@@ -318,9 +325,14 @@ def _agent_progress_logger(
318
325
  elif kind == "step_limit_reached":
319
326
  _set_status(
320
327
  f"[yellow]{stage}[/yellow] step limit reached "
321
- f"({event.get('steps_used', 0)}/{event.get('max_steps', 0)})"
328
+ f"({event.get('steps_used', 0)}/{event.get('max_steps', 0)}); "
329
+ "awaiting your input [y/N]"
322
330
  )
323
331
  elif kind == "step_limit_extended":
332
+ _set_status(
333
+ f"[green]{stage}[/green] continuing with extended budget "
334
+ f"({event.get('old_max_steps', 0)} -> {event.get('new_max_steps', 0)})"
335
+ )
324
336
  console.print(
325
337
  f"[green]{stage}: continuing[/green] "
326
338
  f"step budget {event.get('old_max_steps', 0)} -> {event.get('new_max_steps', 0)}"
@@ -479,6 +491,20 @@ def _rr_progress_logger(status=None) -> Callable[[dict], None]:
479
491
  elif ev == "run_done":
480
492
  if status:
481
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}")
482
508
 
483
509
  return _log
484
510
 
@@ -500,7 +526,11 @@ def _step_limit_prompt(stage: str) -> Callable[[dict], bool]:
500
526
  f"[yellow]{stage}: reached step limit ({steps_used}/{max_steps}).[/yellow] "
501
527
  f"Increase by {extension} steps and continue?"
502
528
  )
503
- raw = typer.prompt("Continue agent loop? (y/N)", default="N")
529
+ console.print(
530
+ "[dim]Input required: type 'y' then Enter to continue, "
531
+ "or press Enter (or type 'n') to stop.[/dim]"
532
+ )
533
+ raw = typer.prompt("Extend agent step budget and continue? [y/N]", default="N")
504
534
  return raw.strip().lower() in {"y", "yes"}
505
535
 
506
536
  return _ask
@@ -940,14 +970,31 @@ def _sandbox_preflight(
940
970
  )
941
971
  return command_exists_in_sandbox(project_root=project_root, binary=binary)
942
972
 
943
- def _exec(command: str) -> tuple[bool, str]:
973
+ def _exec(command: str, *, stream_output: bool = False) -> tuple[bool, str]:
974
+ def _on_output(line: str) -> None:
975
+ console.print(f"[dim] {line}[/dim]")
976
+
944
977
  if has_container_ctx:
978
+ if stream_output:
979
+ return exec_in_container_stream(
980
+ runtime=str(runtime),
981
+ container_name=str(container_name),
982
+ command=command,
983
+ workdir=resolved_workdir,
984
+ on_output=_on_output,
985
+ )
945
986
  return exec_in_container(
946
987
  runtime=str(runtime),
947
988
  container_name=str(container_name),
948
989
  command=command,
949
990
  workdir=resolved_workdir,
950
991
  )
992
+ if stream_output:
993
+ return exec_in_sandbox_stream(
994
+ project_root=project_root,
995
+ command=command,
996
+ on_output=_on_output,
997
+ )
951
998
  return exec_in_sandbox(project_root=project_root, command=command)
952
999
 
953
1000
  def _detect_installer() -> str | None:
@@ -1107,8 +1154,10 @@ def _sandbox_preflight(
1107
1154
  "plan": plan,
1108
1155
  "results": [],
1109
1156
  }
1110
- for cmd in plan:
1111
- ok, output = _exec(cmd)
1157
+ total_steps = len(plan)
1158
+ for index, cmd in enumerate(plan, start=1):
1159
+ console.print(f"[cyan]Installing step {index}/{total_steps}[/cyan]: [dim]{cmd}[/dim]")
1160
+ ok, output = _exec(cmd, stream_output=True)
1112
1161
  entry["results"].append({"command": cmd, "ok": ok, "output": (output or "")[:500]})
1113
1162
  if not ok:
1114
1163
  detail = output[:400] if output else "unknown error"
@@ -1126,6 +1175,7 @@ def _sandbox_preflight(
1126
1175
  },
1127
1176
  )
1128
1177
  return False, f"Install command failed: `{cmd}` -> {detail}"
1178
+ console.print(f"[green]Install step {index}/{total_steps} completed.[/green]")
1129
1179
  append_sandbox_install_history(project_root, entry)
1130
1180
 
1131
1181
  if missing_binary and not _binary_exists(missing_binary):
@@ -1191,8 +1241,8 @@ def _sandbox_enabled(project_config) -> bool:
1191
1241
 
1192
1242
 
1193
1243
  def _sandbox_lifecycle(project_config) -> str:
1194
- lifecycle = str(_sandbox_settings(project_config).get("lifecycle", "per_run") or "per_run").strip().lower()
1195
- return lifecycle if lifecycle in {"per_project", "per_run"} else "per_run"
1244
+ lifecycle = str(_sandbox_settings(project_config).get("lifecycle", "per_project") or "per_project").strip().lower()
1245
+ return lifecycle if lifecycle in {"per_project", "per_run"} else "per_project"
1196
1246
 
1197
1247
 
1198
1248
  def _ensure_sandbox_ready(
@@ -1542,6 +1592,16 @@ def code(
1542
1592
  "--auto-approve",
1543
1593
  help="Skip interactive issue approvals in agentic code mode.",
1544
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
+ ),
1545
1605
  ) -> None:
1546
1606
  """Work through refactor requests one by one, applying & verifying each.
1547
1607
 
@@ -1625,19 +1685,21 @@ def code(
1625
1685
  return
1626
1686
 
1627
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 {})
1628
1693
  artifact = run_refactor_requests(
1629
1694
  project_root=project_root,
1630
1695
  constitution=constitution,
1631
1696
  provider=selected_provider,
1632
1697
  requests=requests,
1633
- config=(
1634
- dict(getattr(project_config, "settings", {}) or {})
1635
- | ({"sandbox": {"enabled": True}} if sandbox_on else {})
1636
- | ({"_sandbox_context": sandbox_ctx} if sandbox_ctx else {})
1637
- )
1638
- or dict(getattr(constitution, "settings", {}) or {}),
1698
+ config=rr_config,
1639
1699
  progress_cb=_rr_progress_logger(status=status),
1640
1700
  approval_cb=_rr_approval_prompt(auto_approve=auto_approve),
1701
+ on_rr_failure=on_rr_failure,
1702
+ equivalence_mode=equivalence,
1641
1703
  )
1642
1704
  except Exception as exc:
1643
1705
  console.print(f"[red]Refactor failed:[/red] {exc}")
@@ -1655,32 +1717,137 @@ def code(
1655
1717
  )
1656
1718
 
1657
1719
 
1658
- def requests() -> None:
1659
- """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."""
1660
1753
  _require_auth()
1661
- 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
+ )
1662
1781
  rrs = list_requests(project_root)
1663
1782
  if not rrs:
1664
1783
  console.print("[dim]No refactor requests. Run `refactor review` to create them.[/dim]")
1665
1784
  return
1666
1785
  table = Table(title="Refactor requests", show_lines=False)
1667
1786
  table.add_column("issue")
1787
+ table.add_column("operation", no_wrap=True)
1788
+ if show_operation:
1789
+ table.add_column("scope")
1668
1790
  table.add_column("severity", no_wrap=True)
1669
1791
  table.add_column("category")
1670
1792
  table.add_column("status", no_wrap=True)
1671
1793
  table.add_column("attempts", no_wrap=True)
1672
- 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
+ }
1673
1802
  for rr in rrs:
1674
- table.add_row(
1803
+ row = [
1675
1804
  rr.location,
1676
- rr.severity,
1677
- rr.category,
1678
- f"[{color.get(rr.status, 'white')}]{rr.status}[/{color.get(rr.status, 'white')}]",
1679
- 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
+ ]
1680
1816
  )
1817
+ table.add_row(*row)
1681
1818
  console.print(table)
1682
1819
 
1683
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
+
1684
1851
  def apply(
1685
1852
  force: bool = typer.Option(False, "--force", help="Apply even if the quality gate blocks."),
1686
1853
  ) -> None:
@@ -2082,6 +2249,7 @@ def doctor(
2082
2249
  )
2083
2250
 
2084
2251
  _report_intelligence(project_root)
2252
+ _report_structural_readiness(project_root, project_config)
2085
2253
 
2086
2254
  if skip_ping:
2087
2255
  console.print("[dim]Skipping provider live ping (--skip-ping).[/dim]")
@@ -2215,6 +2383,39 @@ def _report_sandbox(project_root: Path, config) -> None:
2215
2383
  console.print(f" [dim]latest command results: {len(latest.get('results', []))} (failures={failures})[/dim]")
2216
2384
 
2217
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
+
2218
2419
  def _maybe_build_graph(project_root: Path, config) -> None:
2219
2420
  """Build + persist the symbol graph when intelligence.graph is enabled."""
2220
2421
  settings = dict(getattr(config, "settings", {}) or {})
@@ -2244,20 +2445,22 @@ def _print_rr_summary(artifact) -> None:
2244
2445
  return
2245
2446
  table = Table(title="Refactor request outcomes", show_lines=False)
2246
2447
  table.add_column("issue")
2448
+ table.add_column("operation", no_wrap=True)
2247
2449
  table.add_column("outcome", no_wrap=True)
2248
2450
  table.add_column("attempts", no_wrap=True)
2249
- color = {"applied": "green", "denied": "yellow", "failed": "red"}
2451
+ color = {"applied": "green", "denied": "yellow", "failed": "red", "deferred": "yellow"}
2250
2452
  for o in outcomes:
2251
2453
  outcome = str(o.get("outcome", "?"))
2252
2454
  table.add_row(
2253
2455
  str(o.get("location", "?")),
2456
+ str(o.get("operation_type", "content_patch")),
2254
2457
  f"[{color.get(outcome, 'white')}]{outcome}[/{color.get(outcome, 'white')}]",
2255
2458
  str(o.get("attempts", 0)),
2256
2459
  )
2257
2460
  console.print(table)
2258
2461
  console.print(
2259
2462
  f"[dim]applied={trace.get('applied', 0)} denied={trace.get('denied', 0)} "
2260
- f"failed={trace.get('failed', 0)}[/dim]"
2463
+ f"failed={trace.get('failed', 0)} deferred={trace.get('deferred', 0)}[/dim]"
2261
2464
  )
2262
2465
 
2263
2466
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.13
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