refactorai-cli 0.7.22__tar.gz → 0.7.24__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 (54) hide show
  1. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/PKG-INFO +1 -1
  2. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/pyproject.toml +1 -1
  3. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/cloud_rr.py +56 -0
  5. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/run_cmds.py +142 -26
  6. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli.egg-info/PKG-INFO +1 -1
  7. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/README.md +0 -0
  8. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/auth.py +0 -0
  9. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/client.py +0 -0
  10. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/__init__.py +0 -0
  11. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/account_cmds.py +0 -0
  12. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/auth_cmds.py +0 -0
  13. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/branch_cmds.py +0 -0
  14. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/cloud_cmds.py +0 -0
  15. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/engine_cmds.py +0 -0
  16. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/hook_cmds.py +0 -0
  17. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/model_cmds.py +0 -0
  18. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/policy_cmds.py +0 -0
  19. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/pre_push_cmds.py +0 -0
  20. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/request_cmds.py +0 -0
  21. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/rules_cmds.py +0 -0
  22. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/runtime_cmds.py +0 -0
  23. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  24. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/setup_cmds.py +0 -0
  25. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/toolchains_cmds.py +0 -0
  26. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/watch_cmds.py +0 -0
  27. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commands/workspace_cmds.py +0 -0
  28. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commit_queue.py +0 -0
  29. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/commit_telemetry.py +0 -0
  30. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/control_plane.py +0 -0
  31. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/credentials.py +0 -0
  32. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/dotenv_loader.py +0 -0
  33. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/git_scope.py +0 -0
  34. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/local_constitution.py +0 -0
  35. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/local_engine_runtime.py +0 -0
  36. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/local_paths.py +0 -0
  37. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/main.py +0 -0
  38. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/mode_sync.py +0 -0
  39. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/model_policy.py +0 -0
  40. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/pre_push_gate.py +0 -0
  41. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/refactor_branch_store.py +0 -0
  42. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/review_runner.py +0 -0
  43. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/runtime_manager.py +0 -0
  44. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/settings.py +0 -0
  45. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/setup_flow.py +0 -0
  46. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/watch_ledger.py +0 -0
  47. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/watch_state.py +0 -0
  48. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli/watch_supervisor.py +0 -0
  49. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  50. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  51. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli.egg-info/entry_points.txt +0 -0
  52. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli.egg-info/requires.txt +0 -0
  53. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/refactorai_cli.egg-info/top_level.txt +0 -0
  54. {refactorai_cli-0.7.22 → refactorai_cli-0.7.24}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.7.22
3
+ Version: 0.7.24
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.7.22"
3
+ version = "0.7.24"
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.7.22"
8
+ __version__ = "0.7.24"
@@ -769,6 +769,62 @@ def _python_restore_commands(project_root: Path, config: dict) -> list[tuple[str
769
769
  return cmds
770
770
 
771
771
 
772
+ def resolve_dependency_commands(
773
+ project_root: Path,
774
+ *,
775
+ config: dict,
776
+ runtimes: tuple[str, ...] | None = None,
777
+ dep_locator_cb: Callable[[Path], list[tuple[str, str]]] | None = None,
778
+ ) -> list[tuple[str, str]]:
779
+ """Ordered ``(label, shell-command)`` dependency-install steps for a project.
780
+
781
+ Single source of truth shared by the managed-host restore
782
+ (:func:`_restore_dependencies`) and the container sandbox preflight, so both
783
+ resolve dependencies identically instead of re-implementing per-ecosystem
784
+ heuristics. Resolution tiers (docs/35):
785
+
786
+ 1. **Escape hatch** -- ``verification.deps_install`` is honored verbatim and
787
+ overrides everything, so nonstandard layouts have a deterministic path.
788
+ 2. **Deterministic detection** -- lockfile-aware node install
789
+ (:func:`_node_install_command`) and the python manifest family
790
+ (:func:`_python_restore_commands`: requirements*, ``requirements/``,
791
+ Poetry/uv/PDM, PEP 621 extras).
792
+ 3. **Validated agentic fallback** -- when python detection finds nothing and
793
+ ``dep_locator_cb`` is supplied (and not disabled via
794
+ ``verification.llm_dep_discovery``), the locator points at *where* deps are
795
+ declared; the callback re-validates every path/kind on disk before
796
+ returning a command (never a fabricated package or raw LLM command).
797
+
798
+ ``runtimes`` scopes detection (e.g. ``("node",)`` for an ``npm`` test
799
+ command); ``None`` considers both node and python.
800
+ """
801
+ configured = _configured_deps_install(config)
802
+ if configured:
803
+ return [(f"deps_install[{i}]", cmd) for i, cmd in enumerate(configured, start=1)]
804
+
805
+ selected = runtimes if runtimes is not None else ("node", "python")
806
+ out: list[tuple[str, str]] = []
807
+ seen: set[str] = set()
808
+
809
+ def _add(label: str, command: str) -> None:
810
+ if command not in seen:
811
+ seen.add(command)
812
+ out.append((label, command))
813
+
814
+ if "node" in selected and (project_root / "package.json").is_file():
815
+ _, node_command = _node_install_command(project_root)
816
+ _add("node", node_command)
817
+
818
+ if "python" in selected:
819
+ steps = _python_restore_commands(project_root, config)
820
+ if not steps and dep_locator_cb is not None and _llm_dep_discovery_enabled(config):
821
+ steps = dep_locator_cb(project_root)
822
+ for label, command in steps:
823
+ _add(label, command)
824
+
825
+ return out
826
+
827
+
772
828
  # --- LLM locator fallback (find WHERE deps are declared; never WHAT) --------- #
773
829
  # The model only points at existing files/dirs + a kind label; deterministic code
774
830
  # maps kind -> install command and re-verifies the path on disk. The model can
@@ -1770,36 +1770,95 @@ def _command_binary(command: str) -> str | None:
1770
1770
  return tokens[0] if tokens else None
1771
1771
 
1772
1772
 
1773
- def _install_plan_for_missing_binary(
1774
- *, project_root: Path, binary: str, installer: str | None
1775
- ) -> list[str]:
1776
- """Return a conservative install plan for missing toolchain commands."""
1773
+ def _binary_runtimes(binary: str) -> tuple[str, ...]:
1774
+ """Map a test-command binary to the language runtime(s) whose dependencies it
1775
+ needs, so dependency resolution is scoped to the relevant ecosystem."""
1776
+ b = (binary or "").strip().lower()
1777
+ if b in {"npm", "node", "npx", "yarn", "pnpm", "corepack"}:
1778
+ return ("node",)
1779
+ if b in {"python", "python3", "pip", "pip3", "pytest"}:
1780
+ return ("python",)
1781
+ return ()
1782
+
1783
+
1784
+ def _toolchain_pkg_command(*, binary: str, installer: str | None) -> str:
1785
+ """Finite, bounded language-runtime provisioning for the sandbox image.
1786
+
1787
+ This is the one place that legitimately enumerates (a small, closed set of
1788
+ language runtimes) -- installing the interpreter/toolchain the test command
1789
+ needs. Dependency *resolution* is NOT done here; it is delegated to the shared
1790
+ ``cloud_rr`` resolver so no per-project dependency logic is hardcoded.
1791
+ """
1777
1792
  if not installer:
1778
- return []
1779
- pkg_cmd = ""
1793
+ return ""
1794
+ is_node = binary in {"npm", "node", "npx", "yarn", "pnpm"}
1795
+ is_python = binary in {"python", "python3", "pip", "pip3", "pytest"}
1780
1796
  if installer == "apk":
1781
- if binary in {"npm", "node"}:
1782
- pkg_cmd = "apk add --no-cache nodejs npm"
1783
- elif binary in {"python", "python3", "pip", "pip3", "pytest"}:
1784
- pkg_cmd = "apk add --no-cache python3 py3-pip"
1797
+ if is_node:
1798
+ return "apk add --no-cache nodejs npm"
1799
+ if is_python:
1800
+ return "apk add --no-cache python3 py3-pip"
1785
1801
  elif installer == "apt-get":
1786
- if binary in {"npm", "node"}:
1787
- pkg_cmd = "apt-get update && apt-get install -y nodejs npm"
1788
- elif binary in {"python", "python3", "pip", "pip3", "pytest"}:
1789
- pkg_cmd = "apt-get update && apt-get install -y python3 python3-pip"
1802
+ if is_node:
1803
+ return "apt-get update && apt-get install -y nodejs npm"
1804
+ if is_python:
1805
+ return "apt-get update && apt-get install -y python3 python3-pip"
1790
1806
  elif installer in {"dnf", "yum"}:
1791
- if binary in {"npm", "node"}:
1792
- pkg_cmd = f"{installer} install -y nodejs npm"
1793
- elif binary in {"python", "python3", "pip", "pip3", "pytest"}:
1794
- pkg_cmd = f"{installer} install -y python3 python3-pip"
1807
+ if is_node:
1808
+ return f"{installer} install -y nodejs npm"
1809
+ if is_python:
1810
+ return f"{installer} install -y python3 python3-pip"
1811
+ return ""
1812
+
1813
+
1814
+ def _install_plan_for_missing_binary(
1815
+ *,
1816
+ project_root: Path,
1817
+ binary: str,
1818
+ installer: str | None,
1819
+ config: dict | None = None,
1820
+ dep_locator_cb: Callable[[Path], list[tuple[str, str]]] | None = None,
1821
+ ) -> list[str]:
1822
+ """Install plan for a missing test-command binary: provision the language
1823
+ runtime, then restore dependencies via the shared resolver.
1824
+
1825
+ Two clearly separated concerns:
1826
+
1827
+ * **Toolchain provisioning** (:func:`_toolchain_pkg_command`) -- a small,
1828
+ finite map of language -> package-manager install for the container image.
1829
+ * **Dependency restore** (:func:`cloud_rr.resolve_dependency_commands`) -- the
1830
+ SAME resolver the cloud/managed-host path uses, so the sandbox honors the
1831
+ ``verification.deps_install`` escape hatch, lockfile-aware node installs,
1832
+ the full python manifest family (requirements*/Poetry/uv/PDM/PEP 621
1833
+ extras), and the validated agentic fallback. Previously this path hardcoded
1834
+ ``npm install`` / ``pip install -r requirements.txt``, which both rewrote
1835
+ committed lockfiles and failed to scale to other ecosystems.
1836
+ """
1837
+ if not installer:
1838
+ return []
1839
+ from refactorai_cli.cloud_rr import resolve_dependency_commands
1840
+
1795
1841
  plan: list[str] = []
1842
+ pkg_cmd = _toolchain_pkg_command(binary=binary, installer=installer)
1796
1843
  if pkg_cmd:
1797
1844
  plan.append(pkg_cmd)
1798
- # Dependency restore after toolchain install (best effort).
1799
- if (project_root / "package.json").is_file():
1800
- plan.append("npm install")
1801
- elif (project_root / "requirements.txt").is_file():
1802
- plan.append("pip install -r requirements.txt")
1845
+
1846
+ dep_commands = [
1847
+ cmd
1848
+ for _label, cmd in resolve_dependency_commands(
1849
+ project_root,
1850
+ config=config or {},
1851
+ runtimes=_binary_runtimes(binary),
1852
+ dep_locator_cb=dep_locator_cb,
1853
+ )
1854
+ ]
1855
+ # yarn/pnpm run through Corepack (the modern, version-pinned launcher). Minimal
1856
+ # base images ship Node + npm but omit the corepack shim, so provision it on
1857
+ # demand before any corepack command -- else the step dies with
1858
+ # ``sh: corepack: not found``. Idempotent: skipped when already on PATH.
1859
+ if any(cmd.startswith("corepack") for cmd in dep_commands):
1860
+ plan.append("command -v corepack >/dev/null 2>&1 || npm install -g corepack")
1861
+ plan.extend(dep_commands)
1803
1862
  return plan
1804
1863
 
1805
1864
 
@@ -2005,8 +2064,14 @@ def _sandbox_preflight(
2005
2064
  runtime: str | None = None,
2006
2065
  container_name: str | None = None,
2007
2066
  workdir: str | None = None,
2067
+ dep_locator_cb: Callable[[Path], list[tuple[str, str]]] | None = None,
2008
2068
  ) -> tuple[bool, str]:
2009
- """Run sandbox preflight and optionally perform approved install plan."""
2069
+ """Run sandbox preflight and optionally perform approved install plan.
2070
+
2071
+ ``dep_locator_cb`` is the optional validated agentic dependency-file locator
2072
+ (see :func:`cloud_rr.resolve_dependency_commands`); when omitted, dependency
2073
+ resolution uses the escape hatch + deterministic detection only.
2074
+ """
2010
2075
  _sr = _sandbox_runtime()
2011
2076
  command_exists_in_container = _sr.command_exists_in_container
2012
2077
  command_exists_in_sandbox = _sr.command_exists_in_sandbox
@@ -2109,7 +2174,11 @@ def _sandbox_preflight(
2109
2174
  if missing_binary:
2110
2175
  plan.extend(
2111
2176
  _install_plan_for_missing_binary(
2112
- project_root=project_root, binary=missing_binary, installer=installer
2177
+ project_root=project_root,
2178
+ binary=missing_binary,
2179
+ installer=installer,
2180
+ config=config_settings,
2181
+ dep_locator_cb=dep_locator_cb,
2113
2182
  )
2114
2183
  )
2115
2184
  lsp_plan, unsupported_lsp = _install_plan_for_missing_lsp(
@@ -2305,6 +2374,44 @@ def _sandbox_lifecycle(project_config) -> str:
2305
2374
  return lifecycle if lifecycle in {"per_project", "per_run"} else "per_project"
2306
2375
 
2307
2376
 
2377
+ def _build_sandbox_dep_locator(
2378
+ project_root: Path, constitution, provider: str | None
2379
+ ) -> Callable[[Path], list[tuple[str, str]]] | None:
2380
+ """Best-effort validated agentic dependency-file locator for sandbox preflight.
2381
+
2382
+ Returns a ``callback(root) -> [(label, command)]`` that asks the platform
2383
+ *where* dependencies are declared (nonstandard layouts), re-validating every
2384
+ path/kind on disk before use -- the same locator the cloud RR loop uses
2385
+ (docs/35). Returns ``None`` when a locator cannot be built (e.g. auth/context
2386
+ unavailable), in which case preflight silently uses the escape hatch +
2387
+ deterministic detection only. Never raises.
2388
+ """
2389
+ try:
2390
+ from refactorai_cli.cloud_rr import _located_restore_commands
2391
+
2392
+ variant = _execution_variant(constitution)
2393
+ execution_mode = platform_inference_mode(variant) or variant or "cloud_managed"
2394
+ auth_ctx = ensure_authenticated(project_root)
2395
+ model_hint = provider or str(constitution.get_setting("model_id", "") or "") or None
2396
+
2397
+ def _locator(root: Path) -> list[tuple[str, str]]:
2398
+ try:
2399
+ return _located_restore_commands(
2400
+ root,
2401
+ constitution=constitution,
2402
+ execution_mode=execution_mode,
2403
+ auth_ctx=auth_ctx,
2404
+ model_hint=model_hint,
2405
+ apply_byok_payload=_apply_byok_payload,
2406
+ )
2407
+ except Exception:
2408
+ return []
2409
+
2410
+ return _locator
2411
+ except Exception:
2412
+ return None
2413
+
2414
+
2308
2415
  def _ensure_sandbox_ready(
2309
2416
  *,
2310
2417
  project_root: Path,
@@ -2313,8 +2420,13 @@ def _ensure_sandbox_ready(
2313
2420
  auto_approve_install: bool = False,
2314
2421
  no_install: bool = False,
2315
2422
  continue_on_preflight_failure: bool = False,
2423
+ dep_locator_cb: Callable[[Path], list[tuple[str, str]]] | None = None,
2316
2424
  ) -> dict | None:
2317
- """Ensure sandbox lifecycle + preflight for stage execution."""
2425
+ """Ensure sandbox lifecycle + preflight for stage execution.
2426
+
2427
+ ``dep_locator_cb`` is forwarded to preflight as the validated agentic
2428
+ dependency-file locator (best-effort; ``None`` disables that fallback tier).
2429
+ """
2318
2430
  _sr = _sandbox_runtime()
2319
2431
  resolve_runtime = _sr.resolve_runtime
2320
2432
  sandbox_name = _sr.sandbox_name
@@ -2367,6 +2479,7 @@ def _ensure_sandbox_ready(
2367
2479
  runtime=resolved_runtime,
2368
2480
  container_name=str(state.get("container_name", "") or ""),
2369
2481
  workdir=str(state.get("workdir") or workdir),
2482
+ dep_locator_cb=dep_locator_cb,
2370
2483
  )
2371
2484
  if not preflight_ok:
2372
2485
  if continue_on_preflight_failure:
@@ -3716,6 +3829,9 @@ def code(
3716
3829
  stage="code",
3717
3830
  auto_approve_install=auto_approve,
3718
3831
  no_install=False,
3832
+ dep_locator_cb=_build_sandbox_dep_locator(
3833
+ project_root, constitution, provider
3834
+ ),
3719
3835
  )
3720
3836
  else:
3721
3837
  variant = _execution_variant(constitution)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.7.22
3
+ Version: 0.7.24
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown