refactorai-cli 0.3.0__tar.gz → 0.3.2__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.
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/PKG-INFO +3 -3
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/README.md +1 -1
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/pyproject.toml +2 -2
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/__init__.py +1 -1
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/run_cmds.py +216 -90
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli.egg-info/PKG-INFO +3 -3
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli.egg-info/requires.txt +1 -1
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/auth.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/client.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/__init__.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/auth_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/cloud_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/engine_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/model_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/rules_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/runtime_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/commands/setup_cmds.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/control_plane.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/credentials.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/local_constitution.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/local_engine_runtime.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/local_paths.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/main.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/model_policy.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/runtime_manager.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/settings.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli/setup_flow.py +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli.egg-info/SOURCES.txt +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli.egg-info/dependency_links.txt +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli.egg-info/entry_points.txt +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/refactorai_cli.egg-info/top_level.txt +0 -0
- {refactorai_cli-0.3.0 → refactorai_cli-0.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -8,7 +8,7 @@ Requires-Dist: typer>=0.12.0
|
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
9
|
Requires-Dist: rich>=13.7.0
|
|
10
10
|
Requires-Dist: PyYAML>=6.0.1
|
|
11
|
-
Requires-Dist: refactorai-core>=3.0.
|
|
11
|
+
Requires-Dist: refactorai-core>=3.0.2
|
|
12
12
|
|
|
13
13
|
# refactorai-cli
|
|
14
14
|
|
|
@@ -54,7 +54,7 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
54
54
|
## Install test (local)
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.3.
|
|
57
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.3.2-py3-none-any.whl
|
|
58
58
|
refactor --version
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -42,7 +42,7 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
42
42
|
## Install test (local)
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.3.
|
|
45
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.3.2-py3-none-any.whl
|
|
46
46
|
refactor --version
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "refactorai-cli"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Local-first CLI for the refactor platform"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -12,7 +12,7 @@ dependencies = [
|
|
|
12
12
|
"httpx>=0.27.0",
|
|
13
13
|
"rich>=13.7.0",
|
|
14
14
|
"PyYAML>=6.0.1",
|
|
15
|
-
"refactorai-core>=3.0.
|
|
15
|
+
"refactorai-core>=3.0.2",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[project.scripts]
|
|
@@ -157,19 +157,25 @@ def _default_config_for_init() -> tuple[str, str]:
|
|
|
157
157
|
if backend == BACKEND_LOCAL_SERVER:
|
|
158
158
|
model_id = _setup_recommended_model_id()
|
|
159
159
|
base_url = _setup_engine_base_url()
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
# local_server stays in `mode: local` but swaps the default heuristic
|
|
161
|
+
# provider block for an Ollama block pointing at the local engine.
|
|
162
|
+
config_text = DEFAULT_CONFIG.replace(
|
|
163
|
+
"# Heuristic (local, no LLM) - default for mode: local\n"
|
|
164
|
+
"provider: heuristic\n"
|
|
162
165
|
"model_id: heuristic-local-v1",
|
|
163
|
-
|
|
166
|
+
"# Ollama (local engine) - configured by `refactor init`\n"
|
|
167
|
+
"provider: ollama\n"
|
|
168
|
+
f"model_id: {model_id}\n"
|
|
169
|
+
f"base_url: {base_url}",
|
|
164
170
|
1,
|
|
165
171
|
)
|
|
166
172
|
return config_text, "local_server"
|
|
167
173
|
if backend == BACKEND_BYOK:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
config_text =
|
|
171
|
-
"
|
|
172
|
-
"
|
|
174
|
+
# BYOK only flips the execution mode. The developer chooses and
|
|
175
|
+
# uncomments a provider block in the Provider selection section.
|
|
176
|
+
config_text = DEFAULT_CONFIG.replace(
|
|
177
|
+
"mode: local\n# mode: cloud_byok",
|
|
178
|
+
"# mode: local\nmode: cloud_byok",
|
|
173
179
|
1,
|
|
174
180
|
)
|
|
175
181
|
return config_text, "byok"
|
|
@@ -659,29 +665,7 @@ def _cloud_inference(
|
|
|
659
665
|
}
|
|
660
666
|
|
|
661
667
|
if execution_mode == "cloud_byok":
|
|
662
|
-
|
|
663
|
-
provider_key = str(constitution.get_setting("provider_key", "") or "").strip()
|
|
664
|
-
credential_ref = str(constitution.get_setting("credential_ref", "") or "").strip()
|
|
665
|
-
|
|
666
|
-
# Env-key-first BYOK path: when provider_key is configured (typically via
|
|
667
|
-
# ${ENV_VAR} interpolation), prefer it over credential_ref.
|
|
668
|
-
if provider_key:
|
|
669
|
-
if not provider_name or provider_name.lower() == "cloud":
|
|
670
|
-
raise RuntimeError(
|
|
671
|
-
"cloud_byok with provider_key requires an explicit provider "
|
|
672
|
-
"(e.g. provider: openai)."
|
|
673
|
-
)
|
|
674
|
-
payload["provider_key"] = provider_key
|
|
675
|
-
payload["provider"] = provider_name
|
|
676
|
-
elif credential_ref:
|
|
677
|
-
payload["credential_ref"] = credential_ref
|
|
678
|
-
if provider_name and provider_name.lower() != "cloud":
|
|
679
|
-
payload["provider"] = provider_name
|
|
680
|
-
else:
|
|
681
|
-
raise RuntimeError(
|
|
682
|
-
"cloud_byok requires either `provider_key` (preferred env-key path) "
|
|
683
|
-
"or `credential_ref` (platform-stored credential path) in refactor.config."
|
|
684
|
-
)
|
|
668
|
+
_apply_byok_payload(payload, constitution)
|
|
685
669
|
|
|
686
670
|
idempotency_key = _stable_idempotency_key(
|
|
687
671
|
account_id=auth_ctx.account_id,
|
|
@@ -758,6 +742,185 @@ def _cloud_inference(
|
|
|
758
742
|
)
|
|
759
743
|
|
|
760
744
|
|
|
745
|
+
def _apply_byok_payload(payload: dict, constitution) -> None:
|
|
746
|
+
"""Populate BYOK provider fields on a cloud inference ``payload`` in place.
|
|
747
|
+
|
|
748
|
+
Env-key-first: prefer ``provider_key`` (typically ``${ENV_VAR}``) over a
|
|
749
|
+
platform-stored ``credential_ref``. Shared by the review/refactor path and
|
|
750
|
+
the per-RR proposal path so BYOK resolution stays consistent.
|
|
751
|
+
"""
|
|
752
|
+
provider_name = str(constitution.get_setting("provider", "") or "").strip()
|
|
753
|
+
provider_key = str(constitution.get_setting("provider_key", "") or "").strip()
|
|
754
|
+
credential_ref = str(constitution.get_setting("credential_ref", "") or "").strip()
|
|
755
|
+
|
|
756
|
+
if provider_key:
|
|
757
|
+
if not provider_name or provider_name.lower() == "cloud":
|
|
758
|
+
raise RuntimeError(
|
|
759
|
+
"cloud_byok with provider_key requires an explicit provider "
|
|
760
|
+
"(e.g. provider: openai)."
|
|
761
|
+
)
|
|
762
|
+
payload["provider_key"] = provider_key
|
|
763
|
+
payload["provider"] = provider_name
|
|
764
|
+
elif credential_ref:
|
|
765
|
+
payload["credential_ref"] = credential_ref
|
|
766
|
+
if provider_name and provider_name.lower() != "cloud":
|
|
767
|
+
payload["provider"] = provider_name
|
|
768
|
+
else:
|
|
769
|
+
raise RuntimeError(
|
|
770
|
+
"cloud_byok requires either `provider_key` (preferred env-key path) "
|
|
771
|
+
"or `credential_ref` (platform-stored credential path) in refactor.config."
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def _post_cloud_inference(*, payload: dict, idempotency_key: str, auth_ctx, execution_mode: str) -> dict:
|
|
776
|
+
"""POST a cloud inference request and return the parsed JSON body.
|
|
777
|
+
|
|
778
|
+
Centralizes transport + status-code error mapping so the per-RR proposal
|
|
779
|
+
path surfaces the same remediation messages as review/refactor.
|
|
780
|
+
"""
|
|
781
|
+
try:
|
|
782
|
+
response = httpx.post(
|
|
783
|
+
f"{platform_url()}/v1/inference/refactor",
|
|
784
|
+
headers={
|
|
785
|
+
"Authorization": f"Bearer {auth_ctx.key.key}",
|
|
786
|
+
"Content-Type": "application/json",
|
|
787
|
+
"Idempotency-Key": idempotency_key,
|
|
788
|
+
},
|
|
789
|
+
json=payload,
|
|
790
|
+
timeout=45.0,
|
|
791
|
+
)
|
|
792
|
+
except httpx.HTTPError as exc:
|
|
793
|
+
raise RuntimeError(f"Could not reach cloud inference service: {exc}") from exc
|
|
794
|
+
|
|
795
|
+
if response.status_code == 401:
|
|
796
|
+
raise RuntimeError("Cloud inference unauthorized (developer key invalid or revoked).")
|
|
797
|
+
if response.status_code == 402:
|
|
798
|
+
if execution_mode == "cloud_byok":
|
|
799
|
+
raise RuntimeError(
|
|
800
|
+
"Cloud BYOK is not enabled for this account (missing `run:byok`). "
|
|
801
|
+
"Contact your admin to enable BYOK cloud execution."
|
|
802
|
+
)
|
|
803
|
+
raise RuntimeError("Cloud inference requires paid tier (`mode: cloud_managed`).")
|
|
804
|
+
if response.status_code == 404 and execution_mode == "cloud_byok":
|
|
805
|
+
raise RuntimeError(
|
|
806
|
+
"BYOK credential not found or not owned by this account. "
|
|
807
|
+
"Check `refactor cloud credentials list` and your `credential_ref`."
|
|
808
|
+
)
|
|
809
|
+
if response.status_code == 429:
|
|
810
|
+
raise RuntimeError("Cloud inference quota exhausted or rate-limited.")
|
|
811
|
+
if response.status_code >= 400:
|
|
812
|
+
raise RuntimeError(f"Cloud inference failed ({response.status_code}): {response.text[:500]}")
|
|
813
|
+
|
|
814
|
+
return response.json() if response.content else {}
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
class _CloudRRProvider:
|
|
818
|
+
"""Lightweight provider stub for cloud RR execution (R22).
|
|
819
|
+
|
|
820
|
+
The executor reads ``provider.model_id`` for the artifact and would call
|
|
821
|
+
``provider.complete(...)`` for proposals/tests. In cloud mode the proposal
|
|
822
|
+
and test-gen hooks are supplied, so ``complete`` is never invoked; it raises
|
|
823
|
+
to make any accidental local-prompt path explicit rather than silent.
|
|
824
|
+
"""
|
|
825
|
+
|
|
826
|
+
def __init__(self, *, model_id: str) -> None:
|
|
827
|
+
self.model_id = model_id
|
|
828
|
+
self.provider_name = "cloud"
|
|
829
|
+
self.supports_refactor = True
|
|
830
|
+
|
|
831
|
+
def complete(self, system: str, user: str) -> str: # pragma: no cover - guard
|
|
832
|
+
raise RuntimeError(
|
|
833
|
+
"cloud RR execution must use server-side proposal hooks; "
|
|
834
|
+
"provider.complete should not be called."
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def _cloud_rr_hooks(
|
|
839
|
+
*,
|
|
840
|
+
project_root: Path,
|
|
841
|
+
constitution,
|
|
842
|
+
model_hint: str | None,
|
|
843
|
+
) -> tuple[Callable, Callable, str]:
|
|
844
|
+
"""Build (propose_fn, gen_test_fn, model_id) for cloud per-RR execution (R22).
|
|
845
|
+
|
|
846
|
+
The returned callables match the executor's hook contract. They send the
|
|
847
|
+
single RR's context + current source to the platform, which builds the
|
|
848
|
+
proprietary prompts server-side and returns a proposal / characterization
|
|
849
|
+
test. Idempotency keys include the RR id and attempt so retries of distinct
|
|
850
|
+
RRs/attempts never collapse into a replayed result.
|
|
851
|
+
"""
|
|
852
|
+
execution_mode = _cloud_execution_mode(constitution) or "cloud_managed"
|
|
853
|
+
auth_ctx = ensure_authenticated(project_root)
|
|
854
|
+
constitution_body = getattr(constitution, "body", "") or ""
|
|
855
|
+
resolved_model = model_hint or str(
|
|
856
|
+
constitution.get_setting("model_id", "managed-default") or "managed-default"
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
def _rr_context(rr) -> dict:
|
|
860
|
+
return {
|
|
861
|
+
"location": getattr(rr, "location", ""),
|
|
862
|
+
"file_path": getattr(rr, "file_path", ""),
|
|
863
|
+
"category": getattr(rr, "category", ""),
|
|
864
|
+
"severity": getattr(rr, "severity", ""),
|
|
865
|
+
"recommendation": getattr(rr, "recommendation", ""),
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
def _base_payload(stage: str, rr, source: str) -> dict:
|
|
869
|
+
payload = {
|
|
870
|
+
"stage": stage,
|
|
871
|
+
"execution_mode": execution_mode,
|
|
872
|
+
"constitution_hash": constitution.content_hash,
|
|
873
|
+
"constitution_body": constitution_body,
|
|
874
|
+
"rr_context": _rr_context(rr),
|
|
875
|
+
"source": source,
|
|
876
|
+
"max_alerts": int(constitution.get_setting("max_alerts_per_run", 5) or 5),
|
|
877
|
+
"model_hint": resolved_model,
|
|
878
|
+
}
|
|
879
|
+
if execution_mode == "cloud_byok":
|
|
880
|
+
_apply_byok_payload(payload, constitution)
|
|
881
|
+
return payload
|
|
882
|
+
|
|
883
|
+
def _idempotency(stage: str, rr, attempt: int) -> str:
|
|
884
|
+
seed = "|".join(
|
|
885
|
+
[
|
|
886
|
+
auth_ctx.account_id,
|
|
887
|
+
execution_mode,
|
|
888
|
+
stage,
|
|
889
|
+
constitution.content_hash,
|
|
890
|
+
str(getattr(rr, "rr_id", "")),
|
|
891
|
+
str(int(attempt)),
|
|
892
|
+
]
|
|
893
|
+
)
|
|
894
|
+
return hashlib.sha256(seed.encode("utf-8")).hexdigest()
|
|
895
|
+
|
|
896
|
+
def propose_fn(rr, source: str, prior_failures: list[str]) -> dict:
|
|
897
|
+
# Attempt number is derived from prior failures so each retry gets a
|
|
898
|
+
# distinct idempotency key (1-based).
|
|
899
|
+
attempt = len(prior_failures or []) + 1
|
|
900
|
+
payload = _base_payload("rr_propose", rr, source)
|
|
901
|
+
payload["prior_failures"] = list(prior_failures or [])
|
|
902
|
+
data = _post_cloud_inference(
|
|
903
|
+
payload=payload,
|
|
904
|
+
idempotency_key=_idempotency("rr_propose", rr, attempt),
|
|
905
|
+
auth_ctx=auth_ctx,
|
|
906
|
+
execution_mode=execution_mode,
|
|
907
|
+
)
|
|
908
|
+
proposal = data.get("proposal") or {}
|
|
909
|
+
if not isinstance(proposal, dict):
|
|
910
|
+
return {}
|
|
911
|
+
return proposal
|
|
912
|
+
|
|
913
|
+
def gen_test_fn(rr, source: str) -> dict:
|
|
914
|
+
# Server-side characterization-test generation is not part of Phase 1.
|
|
915
|
+
# Returning no test makes the executor fall back to the configured test
|
|
916
|
+
# command for behavioral verification (parity with a local run that has
|
|
917
|
+
# no generated test). This keeps cloud RR execution safe today and lets
|
|
918
|
+
# a future phase add a dedicated server test-gen stage.
|
|
919
|
+
return {}
|
|
920
|
+
|
|
921
|
+
return propose_fn, gen_test_fn, resolved_model
|
|
922
|
+
|
|
923
|
+
|
|
761
924
|
def _load_project() -> tuple[Path, object, object]:
|
|
762
925
|
files = find_project_files()
|
|
763
926
|
if not files:
|
|
@@ -1796,7 +1959,8 @@ def review(
|
|
|
1796
1959
|
if _sandbox_enabled(project_config):
|
|
1797
1960
|
if cloud:
|
|
1798
1961
|
console.print(
|
|
1799
|
-
"[dim]review: sandbox.enabled is ignored in
|
|
1962
|
+
f"[dim]review: sandbox.enabled is ignored in "
|
|
1963
|
+
f"{_cloud_execution_mode(constitution)} mode.[/dim]"
|
|
1800
1964
|
)
|
|
1801
1965
|
else:
|
|
1802
1966
|
sandbox_ctx = _ensure_sandbox_ready(
|
|
@@ -2023,7 +2187,8 @@ def code(
|
|
|
2023
2187
|
if _sandbox_enabled(project_config):
|
|
2024
2188
|
if cloud:
|
|
2025
2189
|
console.print(
|
|
2026
|
-
"[dim]code: sandbox.enabled is ignored in
|
|
2190
|
+
f"[dim]code: sandbox.enabled is ignored in "
|
|
2191
|
+
f"{_cloud_execution_mode(constitution)} mode.[/dim]"
|
|
2027
2192
|
)
|
|
2028
2193
|
else:
|
|
2029
2194
|
sandbox_ctx = _ensure_sandbox_ready(
|
|
@@ -2127,64 +2292,23 @@ def code(
|
|
|
2127
2292
|
console.print("[green]No issues to refactor.[/green]")
|
|
2128
2293
|
return
|
|
2129
2294
|
|
|
2130
|
-
|
|
2131
|
-
# Cloud modes generate patches via the platform inference API rather
|
|
2132
|
-
# than the local RR interactive loop. Reuse prior review findings as
|
|
2133
|
-
# prompt context; fall back to the latest stored review findings when
|
|
2134
|
-
# requests were carried over from an earlier run.
|
|
2135
|
-
if not cloud_review_findings:
|
|
2136
|
-
existing_dir = head_run_dir(project_root)
|
|
2137
|
-
if existing_dir is not None:
|
|
2138
|
-
try:
|
|
2139
|
-
cloud_review_findings = [
|
|
2140
|
-
Finding.model_validate(item) for item in (read_findings(existing_dir) or [])
|
|
2141
|
-
]
|
|
2142
|
-
except Exception:
|
|
2143
|
-
cloud_review_findings = []
|
|
2144
|
-
with console.status("[cyan]code[/cyan] generating patches (cloud)...", spinner="dots"):
|
|
2145
|
-
artifact = _cloud_inference(
|
|
2146
|
-
stage="refactor",
|
|
2147
|
-
project_root=project_root,
|
|
2148
|
-
constitution=constitution,
|
|
2149
|
-
target=target,
|
|
2150
|
-
model_hint=provider,
|
|
2151
|
-
findings=cloud_review_findings,
|
|
2152
|
-
)
|
|
2153
|
-
cloud_store = create_run(project_root, run_id=artifact.run_id)
|
|
2154
|
-
cloud_store.write_artifact(artifact, target=target, status="completed")
|
|
2295
|
+
from refactor_core.execution.rr_executor import run_refactor_requests
|
|
2155
2296
|
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2297
|
+
# Cloud and local both drive the SAME per-RR interactive loop (R22 cloud
|
|
2298
|
+
# RR parity). The only difference is where proposals come from: cloud
|
|
2299
|
+
# passes server-side proposal hooks; local uses the local provider and
|
|
2300
|
+
# client-side prompt builders. This guarantees identical UX (proposal
|
|
2301
|
+
# diff + approval prompt), status transitions, and outcomes table.
|
|
2302
|
+
cloud_propose_fn = None
|
|
2303
|
+
cloud_gen_test_fn = None
|
|
2304
|
+
rr_provider = selected_provider
|
|
2305
|
+
if cloud:
|
|
2306
|
+
cloud_propose_fn, cloud_gen_test_fn, cloud_model_id = _cloud_rr_hooks(
|
|
2307
|
+
project_root=project_root,
|
|
2308
|
+
constitution=constitution,
|
|
2309
|
+
model_hint=provider,
|
|
2163
2310
|
)
|
|
2164
|
-
|
|
2165
|
-
if apply:
|
|
2166
|
-
gate_mode = str(constitution.get_setting("gate_mode", "strict") or "strict")
|
|
2167
|
-
diffs = [p.diff_text for p in (artifact.patches or [])]
|
|
2168
|
-
if not diffs:
|
|
2169
|
-
console.print("[dim]No patches to apply.[/dim]")
|
|
2170
|
-
else:
|
|
2171
|
-
_apply_diffs(
|
|
2172
|
-
project_root,
|
|
2173
|
-
cloud_store.run_dir,
|
|
2174
|
-
diffs,
|
|
2175
|
-
artifact.gate.status,
|
|
2176
|
-
gate_mode,
|
|
2177
|
-
force,
|
|
2178
|
-
project_config,
|
|
2179
|
-
)
|
|
2180
|
-
elif artifact.patches:
|
|
2181
|
-
console.print(
|
|
2182
|
-
"[dim]Review patches with `refactor diff`, then apply with "
|
|
2183
|
-
"`refactor code --apply` or `refactor apply`.[/dim]"
|
|
2184
|
-
)
|
|
2185
|
-
return
|
|
2186
|
-
|
|
2187
|
-
from refactor_core.execution.rr_executor import run_refactor_requests
|
|
2311
|
+
rr_provider = _CloudRRProvider(model_id=cloud_model_id)
|
|
2188
2312
|
|
|
2189
2313
|
with console.status("[cyan]code[/cyan] running...", spinner="dots") as status:
|
|
2190
2314
|
rr_config = (
|
|
@@ -2195,13 +2319,15 @@ def code(
|
|
|
2195
2319
|
artifact = run_refactor_requests(
|
|
2196
2320
|
project_root=project_root,
|
|
2197
2321
|
constitution=constitution,
|
|
2198
|
-
provider=
|
|
2322
|
+
provider=rr_provider,
|
|
2199
2323
|
requests=requests,
|
|
2200
2324
|
config=rr_config,
|
|
2201
2325
|
progress_cb=_rr_progress_logger(status=status),
|
|
2202
2326
|
approval_cb=_rr_approval_prompt(auto_approve=auto_approve),
|
|
2203
2327
|
on_rr_failure=on_rr_failure,
|
|
2204
2328
|
equivalence_mode=equivalence,
|
|
2329
|
+
propose_fn=cloud_propose_fn,
|
|
2330
|
+
gen_test_fn=cloud_gen_test_fn,
|
|
2205
2331
|
)
|
|
2206
2332
|
except typer.Exit:
|
|
2207
2333
|
# Intentional exits (e.g. gate-blocked apply) keep their own code/message.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -8,7 +8,7 @@ Requires-Dist: typer>=0.12.0
|
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
9
|
Requires-Dist: rich>=13.7.0
|
|
10
10
|
Requires-Dist: PyYAML>=6.0.1
|
|
11
|
-
Requires-Dist: refactorai-core>=3.0.
|
|
11
|
+
Requires-Dist: refactorai-core>=3.0.2
|
|
12
12
|
|
|
13
13
|
# refactorai-cli
|
|
14
14
|
|
|
@@ -54,7 +54,7 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
54
54
|
## Install test (local)
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.3.
|
|
57
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.3.2-py3-none-any.whl
|
|
58
58
|
refactor --version
|
|
59
59
|
```
|
|
60
60
|
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|