refactorai-cli 0.4.11__tar.gz → 0.4.14__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.4.11 → refactorai_cli-0.4.14}/PKG-INFO +2 -2
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/pyproject.toml +2 -2
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/__init__.py +1 -1
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/cloud_rr.py +15 -2
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/run_cmds.py +59 -9
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/watch_cmds.py +17 -3
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/PKG-INFO +2 -2
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/requires.txt +1 -1
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/README.md +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/auth.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/client.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/__init__.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/account_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/auth_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/branch_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/cloud_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/engine_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/hook_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/model_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/request_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/rules_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/runtime_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/setup_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/toolchains_cmds.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commit_queue.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/control_plane.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/credentials.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/dotenv_loader.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/git_scope.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/local_constitution.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/local_engine_runtime.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/local_paths.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/main.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/model_policy.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/refactor_branch_store.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/review_runner.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/runtime_manager.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/settings.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/setup_flow.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/watch_state.py +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/SOURCES.txt +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/dependency_links.txt +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/entry_points.txt +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/top_level.txt +0 -0
- {refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.14
|
|
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.2.
|
|
11
|
+
Requires-Dist: refactorai-core>=3.2.8
|
|
12
12
|
|
|
13
13
|
# refactorai-cli
|
|
14
14
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "refactorai-cli"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.14"
|
|
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.2.
|
|
15
|
+
"refactorai-core>=3.2.8",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[project.scripts]
|
|
@@ -651,6 +651,7 @@ def run_cloud_refactor_requests(
|
|
|
651
651
|
apply_byok_payload: Callable[[dict, object], None] | None = None,
|
|
652
652
|
toolchain_consent_cb: Callable[[dict], bool] | None = None,
|
|
653
653
|
commit_cb: Callable[[object], None] | None = None,
|
|
654
|
+
on_rr_failure: str = "continue",
|
|
654
655
|
) -> RunArtifact:
|
|
655
656
|
"""Drive pending RRs through the cloud server-run loop and return a summary.
|
|
656
657
|
|
|
@@ -665,6 +666,12 @@ def run_cloud_refactor_requests(
|
|
|
665
666
|
once per RR that reaches ``APPLIED`` so the CLI can record one git commit per
|
|
666
667
|
resolved request on an isolated refactor branch. Best-effort; git failures
|
|
667
668
|
never break the cloud RR loop.
|
|
669
|
+
|
|
670
|
+
``on_rr_failure`` (``continue`` | ``defer`` | ``stop``) mirrors the local
|
|
671
|
+
executor policy and is forwarded to the server so a single failing RR no
|
|
672
|
+
longer halts the whole run by default (parity with local). The client stays a
|
|
673
|
+
thin executor: it stops only when the server returns a ``failed`` verdict with
|
|
674
|
+
no ``next_rr_id`` (older servers that always stop remain compatible).
|
|
668
675
|
"""
|
|
669
676
|
project_root = Path(project_root)
|
|
670
677
|
progress_cb = progress_cb or (lambda e: None)
|
|
@@ -741,6 +748,7 @@ def run_cloud_refactor_requests(
|
|
|
741
748
|
"constitution_hash": constitution.content_hash,
|
|
742
749
|
"constitution_body": getattr(constitution, "body", "") or "",
|
|
743
750
|
"model_hint": resolved_model,
|
|
751
|
+
"on_rr_failure": str(on_rr_failure or "continue"),
|
|
744
752
|
"rrs": [
|
|
745
753
|
{
|
|
746
754
|
"rr_id": rr.rr_id,
|
|
@@ -875,12 +883,17 @@ def run_cloud_refactor_requests(
|
|
|
875
883
|
attempts_by_id.get(completed_id, 0 if verdict == "deferred" else 1),
|
|
876
884
|
outcomes, counts, progress_cb, commit_cb=commit_cb,
|
|
877
885
|
)
|
|
878
|
-
|
|
886
|
+
next_id = str(action.get("next_rr_id", "") or "")
|
|
887
|
+
# With `continue`/`defer` (the default) the server advances after a
|
|
888
|
+
# failure, so the client resolves the remaining requests in one pass
|
|
889
|
+
# instead of stopping at the first failure (parity with local). Only a
|
|
890
|
+
# `stop` policy halts the run (no next RR named); a failed *last* RR
|
|
891
|
+
# under `continue` simply ends the queue, it is not a halt.
|
|
892
|
+
if verdict == "failed" and not next_id and str(on_rr_failure) == "stop":
|
|
879
893
|
stopped = True
|
|
880
894
|
stop_reason = str(action.get("message", "") or "refactor request failed")
|
|
881
895
|
_emit(progress_cb, phase="rr", event="run_stopped", rr_id=completed_id, reason=stop_reason)
|
|
882
896
|
break
|
|
883
|
-
next_id = str(action.get("next_rr_id", "") or "")
|
|
884
897
|
if not next_id:
|
|
885
898
|
break
|
|
886
899
|
current_rr_id = next_id
|
|
@@ -651,6 +651,26 @@ def _is_cloud_mode(constitution) -> bool:
|
|
|
651
651
|
return bool(_cloud_execution_mode(constitution))
|
|
652
652
|
|
|
653
653
|
|
|
654
|
+
def _documents_digest(documents: list | None) -> str:
|
|
655
|
+
"""Stable digest of the exact documents sent to the platform.
|
|
656
|
+
|
|
657
|
+
Folds each document's path + content so a commit-scoped review keys on the
|
|
658
|
+
code it actually reviews. Without this, two commits share the same
|
|
659
|
+
idempotency key (same account/stage/constitution/target) and the server
|
|
660
|
+
replays the FIRST commit's findings for the second commit -- re-suggesting
|
|
661
|
+
changes that were already applied/merged (docs/41 `R28`).
|
|
662
|
+
"""
|
|
663
|
+
hasher = hashlib.sha256()
|
|
664
|
+
for doc in sorted(documents or [], key=lambda d: str((d or {}).get("path", ""))):
|
|
665
|
+
if not isinstance(doc, dict):
|
|
666
|
+
continue
|
|
667
|
+
hasher.update(str(doc.get("path", "")).encode("utf-8"))
|
|
668
|
+
hasher.update(b"\x00")
|
|
669
|
+
hasher.update(str(doc.get("content", "")).encode("utf-8"))
|
|
670
|
+
hasher.update(b"\x1f")
|
|
671
|
+
return hasher.hexdigest()
|
|
672
|
+
|
|
673
|
+
|
|
654
674
|
def _stable_idempotency_key(
|
|
655
675
|
*,
|
|
656
676
|
account_id: str,
|
|
@@ -659,15 +679,29 @@ def _stable_idempotency_key(
|
|
|
659
679
|
target: str,
|
|
660
680
|
max_alerts: int,
|
|
661
681
|
execution_mode: str,
|
|
682
|
+
scope_digest: str = "",
|
|
662
683
|
) -> str:
|
|
663
684
|
"""Deterministic idempotency key so retries replay instead of re-charging (C10).
|
|
664
685
|
|
|
665
|
-
The key MUST change when inputs change (target/constitution/stage/mode)
|
|
666
|
-
distinct runs are not incorrectly deduplicated, and MUST be stable across
|
|
686
|
+
The key MUST change when inputs change (target/constitution/stage/mode/scope)
|
|
687
|
+
so distinct runs are not incorrectly deduplicated, and MUST be stable across
|
|
667
688
|
retries of the same logical run so managed quota is not double-charged.
|
|
689
|
+
|
|
690
|
+
``scope_digest`` folds the actual reviewed documents into the key so a
|
|
691
|
+
commit-scoped review of a different commit (or of changed code) produces a
|
|
692
|
+
distinct key and a fresh server run, instead of replaying an earlier commit's
|
|
693
|
+
cached findings.
|
|
668
694
|
"""
|
|
669
695
|
seed = "|".join(
|
|
670
|
-
[
|
|
696
|
+
[
|
|
697
|
+
account_id,
|
|
698
|
+
execution_mode,
|
|
699
|
+
stage,
|
|
700
|
+
constitution_hash,
|
|
701
|
+
str(target),
|
|
702
|
+
str(int(max_alerts)),
|
|
703
|
+
scope_digest,
|
|
704
|
+
]
|
|
671
705
|
)
|
|
672
706
|
return hashlib.sha256(seed.encode("utf-8")).hexdigest()
|
|
673
707
|
|
|
@@ -707,6 +741,10 @@ def _cloud_inference(
|
|
|
707
741
|
|
|
708
742
|
execution_mode = _cloud_execution_mode(constitution) or "cloud_managed"
|
|
709
743
|
auth_ctx = ensure_authenticated(project_root)
|
|
744
|
+
# Digest the exact documents so the idempotency key keys on the reviewed
|
|
745
|
+
# code, not just account/stage/target. (The backend independently recomputes
|
|
746
|
+
# a documents hash for its own replay guard, so this need not be sent.)
|
|
747
|
+
documents_hash = _documents_digest(documents)
|
|
710
748
|
payload = {
|
|
711
749
|
"stage": stage,
|
|
712
750
|
"execution_mode": execution_mode,
|
|
@@ -728,6 +766,7 @@ def _cloud_inference(
|
|
|
728
766
|
target=target,
|
|
729
767
|
max_alerts=max_alerts,
|
|
730
768
|
execution_mode=execution_mode,
|
|
769
|
+
scope_digest=documents_hash,
|
|
731
770
|
)
|
|
732
771
|
try:
|
|
733
772
|
response = httpx.post(
|
|
@@ -2955,7 +2994,19 @@ def code(
|
|
|
2955
2994
|
changed=changed,
|
|
2956
2995
|
intent=intent,
|
|
2957
2996
|
)
|
|
2958
|
-
|
|
2997
|
+
# Decide reuse-vs-generate up front. The full-project confirmation only guards
|
|
2998
|
+
# *creating* a large request set; when we are resolving an existing pending
|
|
2999
|
+
# queue (the common `[r]` resume case) there is nothing to confirm, so a root
|
|
3000
|
+
# commit no longer re-prompts every run (docs/42 `R29`, Part L).
|
|
3001
|
+
reused_requests = [] if (code_scope and refresh_requests) else pending_requests(project_root)
|
|
3002
|
+
will_generate_requests = bool(refresh_requests) or not reused_requests
|
|
3003
|
+
if will_generate_requests:
|
|
3004
|
+
_confirm_full_project_scope(code_scope, command="code", assume_yes=yes_full_project)
|
|
3005
|
+
elif code_scope and code_scope.get("full_project"):
|
|
3006
|
+
console.print(
|
|
3007
|
+
f"[dim]code: resuming {len(reused_requests)} pending request(s) for this "
|
|
3008
|
+
"commit (full-project review already ran).[/dim]"
|
|
3009
|
+
)
|
|
2959
3010
|
code_scope_paths = code_scope.get("scope_paths") if code_scope else None
|
|
2960
3011
|
code_changed_paths = code_scope.get("changed_paths") if code_scope else None
|
|
2961
3012
|
if code_scope:
|
|
@@ -3043,11 +3094,9 @@ def code(
|
|
|
3043
3094
|
# Reuse the existing pending requests by default (docs/42 `R29`, Phase 1):
|
|
3044
3095
|
# `[r]`/`code --commit` resolves the queue already created for this commit
|
|
3045
3096
|
# rather than regenerating it. `--refresh-requests` opts back into a fresh
|
|
3046
|
-
# scoped review so the request set is rebuilt from scratch.
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
else:
|
|
3050
|
-
requests = pending_requests(project_root)
|
|
3097
|
+
# scoped review so the request set is rebuilt from scratch. Computed above
|
|
3098
|
+
# (``reused_requests``) so the full-project prompt could be skipped on reuse.
|
|
3099
|
+
requests = reused_requests
|
|
3051
3100
|
if not requests:
|
|
3052
3101
|
if code_scope and refresh_requests:
|
|
3053
3102
|
console.print(
|
|
@@ -3169,6 +3218,7 @@ def code(
|
|
|
3169
3218
|
apply_byok_payload=_apply_byok_payload,
|
|
3170
3219
|
toolchain_consent_cb=_toolchain_consent_prompt(auto_approve=auto_approve),
|
|
3171
3220
|
commit_cb=rr_commit_cb,
|
|
3221
|
+
on_rr_failure=on_rr_failure,
|
|
3172
3222
|
)
|
|
3173
3223
|
else:
|
|
3174
3224
|
# Local mode is unchanged: dispatch to the local executor with the
|
|
@@ -415,6 +415,7 @@ def build_action_command(
|
|
|
415
415
|
executable: str = "refactor",
|
|
416
416
|
branch: str | None = None,
|
|
417
417
|
rr_id: str | None = None,
|
|
418
|
+
yes_full_project: bool = False,
|
|
418
419
|
) -> list[str] | None:
|
|
419
420
|
"""Build the argv for a terminal action. Pure + testable (docs/42, docs/43).
|
|
420
421
|
|
|
@@ -430,10 +431,18 @@ def build_action_command(
|
|
|
430
431
|
"""
|
|
431
432
|
if action == "code":
|
|
432
433
|
# Commit-scoped so `[r]` resolves THIS commit's requests (docs/41 Phase 4).
|
|
433
|
-
|
|
434
|
+
cmd = [executable, "code", "--commit", sha, "--keep"]
|
|
435
|
+
if yes_full_project:
|
|
436
|
+
# Root/full-scope commits would otherwise pop a hidden confirmation in
|
|
437
|
+
# the suspended terminal; auto-confirm so `[r]` never appears to hang.
|
|
438
|
+
cmd.append("--yes-full-project")
|
|
439
|
+
return cmd
|
|
434
440
|
if action == "refresh":
|
|
435
441
|
# `[f]` regenerates the requests for this commit before resolving.
|
|
436
|
-
|
|
442
|
+
cmd = [executable, "code", "--commit", sha, "--refresh-requests", "--keep"]
|
|
443
|
+
if yes_full_project:
|
|
444
|
+
cmd.append("--yes-full-project")
|
|
445
|
+
return cmd
|
|
437
446
|
if action == "diff":
|
|
438
447
|
# Shows the pending refactor-request patch diffs (not the git diff).
|
|
439
448
|
return [executable, "diff"]
|
|
@@ -506,7 +515,12 @@ def _run_action(
|
|
|
506
515
|
) -> None:
|
|
507
516
|
"""Suspend the live graph, run an interactive action, then refresh the node."""
|
|
508
517
|
branch = node.finalize.refactor_branch if getattr(node, "finalize", None) else None
|
|
509
|
-
|
|
518
|
+
# Root/full-scope commits expand `code` to a full-project review; auto-confirm
|
|
519
|
+
# so `[r]`/`[f]` never stall on a hidden prompt in the suspended terminal.
|
|
520
|
+
yes_full_project = getattr(node, "scope_mode", "") == "full"
|
|
521
|
+
cmd = build_action_command(
|
|
522
|
+
action, node.sha, branch=branch, rr_id=rr_id, yes_full_project=yes_full_project
|
|
523
|
+
)
|
|
510
524
|
if not cmd:
|
|
511
525
|
return
|
|
512
526
|
live.stop()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.14
|
|
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.2.
|
|
11
|
+
Requires-Dist: refactorai-core>=3.2.8
|
|
12
12
|
|
|
13
13
|
# refactorai-cli
|
|
14
14
|
|
|
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
|
{refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli/commands/runtime_proxy_cmds.py
RENAMED
|
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
|
{refactorai_cli-0.4.11 → refactorai_cli-0.4.14}/refactorai_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|