refactorai-cli 0.2.20__tar.gz → 0.2.22__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.2.20 → refactorai_cli-0.2.22}/PKG-INFO +2 -2
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/README.md +1 -1
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/pyproject.toml +1 -1
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/__init__.py +1 -1
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/run_cmds.py +164 -4
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/PKG-INFO +2 -2
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/auth.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/client.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/__init__.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/auth_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/engine_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/model_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/rules_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/runtime_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/commands/setup_cmds.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/control_plane.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/credentials.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/local_constitution.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/local_engine_runtime.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/local_paths.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/main.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/model_policy.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/runtime_manager.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/settings.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli/setup_flow.py +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/SOURCES.txt +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/dependency_links.txt +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/entry_points.txt +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/requires.txt +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/top_level.txt +0 -0
- {refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.22
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -53,6 +53,6 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
53
53
|
## Install test (local)
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.
|
|
56
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.22-py3-none-any.whl
|
|
57
57
|
refactor --version
|
|
58
58
|
```
|
|
@@ -42,6 +42,6 @@ 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.2.
|
|
45
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.22-py3-none-any.whl
|
|
46
46
|
refactor --version
|
|
47
47
|
```
|
|
@@ -72,6 +72,9 @@ from refactor_core.models import Finding, GeneratedTest, PatchGroup, RunArtifact
|
|
|
72
72
|
from refactor_core.pipeline import build_refactor_prompt, build_review_prompt
|
|
73
73
|
from refactor_core.providers import get_provider, resolve_provider_name, supported_providers
|
|
74
74
|
from refactor_core.rules import annotate_documents_with_rules, filter_documents_by_rules
|
|
75
|
+
from refactor_core.security import merge_refactor_findings, suppress_findings_for_rr
|
|
76
|
+
from refactor_core.security.semgrep_runner import EMBEDDED_SEMGREP_SENTINEL
|
|
77
|
+
from refactor_core.security.suppression import SecuritySuppression
|
|
75
78
|
from refactor_core.sandbox_runtime import (
|
|
76
79
|
DEFAULT_IMAGE as SANDBOX_DEFAULT_IMAGE,
|
|
77
80
|
DEFAULT_SHELL as SANDBOX_DEFAULT_SHELL,
|
|
@@ -110,6 +113,7 @@ from refactor_core.testcmd import resolve_test_command
|
|
|
110
113
|
|
|
111
114
|
from refactorai_cli.auth import AuthError, ensure_authenticated
|
|
112
115
|
from refactorai_cli.local_engine_runtime import DEFAULT_ENGINE_PORT, read_engine_state
|
|
116
|
+
from refactorai_cli.runtime_manager import runtime_status, runtime_version_dir
|
|
113
117
|
from refactorai_cli.settings import platform_url
|
|
114
118
|
from refactorai_cli.setup_flow import (
|
|
115
119
|
BACKEND_BYOK,
|
|
@@ -689,6 +693,82 @@ def _warn_raw_key(constitution, config) -> None:
|
|
|
689
693
|
)
|
|
690
694
|
|
|
691
695
|
|
|
696
|
+
def _resolve_runtime_semgrep_binary() -> str:
|
|
697
|
+
try:
|
|
698
|
+
status = runtime_status()
|
|
699
|
+
except Exception:
|
|
700
|
+
return ""
|
|
701
|
+
active = str(status.get("active_version") or "").strip()
|
|
702
|
+
if not active:
|
|
703
|
+
return ""
|
|
704
|
+
try:
|
|
705
|
+
base = runtime_version_dir(active)
|
|
706
|
+
except Exception:
|
|
707
|
+
return ""
|
|
708
|
+
candidates = (
|
|
709
|
+
base / "semgrep",
|
|
710
|
+
base / "semgrep.bin",
|
|
711
|
+
base / "bin" / "semgrep",
|
|
712
|
+
base / "tools" / "semgrep",
|
|
713
|
+
)
|
|
714
|
+
for path in candidates:
|
|
715
|
+
if path.is_file():
|
|
716
|
+
return str(path)
|
|
717
|
+
return ""
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def _active_runtime_version() -> str:
|
|
721
|
+
try:
|
|
722
|
+
status = runtime_status()
|
|
723
|
+
except Exception:
|
|
724
|
+
return ""
|
|
725
|
+
return str(status.get("active_version") or "").strip()
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def _inject_runtime_scanner_settings(constitution) -> None:
|
|
729
|
+
settings = dict(getattr(constitution, "settings", {}) or {})
|
|
730
|
+
if not bool(settings.get("security_scanner_enabled", False)):
|
|
731
|
+
constitution.settings = settings
|
|
732
|
+
return
|
|
733
|
+
active_version = _active_runtime_version()
|
|
734
|
+
scanner_path = _resolve_runtime_semgrep_binary()
|
|
735
|
+
settings["security_scan_require_runtime_binary"] = True
|
|
736
|
+
if scanner_path:
|
|
737
|
+
settings["security_scan_binary"] = scanner_path
|
|
738
|
+
elif active_version:
|
|
739
|
+
# Runtime artifact can include semgrep as an embedded python module.
|
|
740
|
+
settings["security_scan_binary"] = EMBEDDED_SEMGREP_SENTINEL
|
|
741
|
+
else:
|
|
742
|
+
settings["security_scan_binary"] = ""
|
|
743
|
+
constitution.settings = settings
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def _report_runtime_scanner_status() -> None:
|
|
747
|
+
scanner_path = _resolve_runtime_semgrep_binary()
|
|
748
|
+
try:
|
|
749
|
+
status = runtime_status()
|
|
750
|
+
except Exception as exc:
|
|
751
|
+
console.print(f"[yellow]security scanner[/yellow]: status unavailable ({exc})")
|
|
752
|
+
return
|
|
753
|
+
active = str(status.get("active_version") or "").strip()
|
|
754
|
+
if scanner_path:
|
|
755
|
+
console.print(
|
|
756
|
+
f"[green]security scanner[/green]: ready "
|
|
757
|
+
f"[dim](runtime={active or 'unknown'}, path={scanner_path})[/dim]"
|
|
758
|
+
)
|
|
759
|
+
return
|
|
760
|
+
if active:
|
|
761
|
+
console.print(
|
|
762
|
+
"[green]security scanner[/green]: ready "
|
|
763
|
+
f"[dim](runtime={active}, embedded in runtime artifact)[/dim]"
|
|
764
|
+
)
|
|
765
|
+
return
|
|
766
|
+
console.print(
|
|
767
|
+
"[yellow]security scanner[/yellow]: unavailable "
|
|
768
|
+
"[dim](no active runtime; run `refactor runtime update`)[/dim]"
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
|
|
692
772
|
def _resolve_compliance_context(
|
|
693
773
|
*,
|
|
694
774
|
project_config,
|
|
@@ -1562,6 +1642,7 @@ def review(
|
|
|
1562
1642
|
"""Run review (findings only) and store the result. Requires a valid key."""
|
|
1563
1643
|
_require_auth()
|
|
1564
1644
|
project_root, constitution, project_config = _load_project()
|
|
1645
|
+
_inject_runtime_scanner_settings(constitution)
|
|
1565
1646
|
try:
|
|
1566
1647
|
(
|
|
1567
1648
|
compliance_frameworks,
|
|
@@ -1665,13 +1746,16 @@ def review(
|
|
|
1665
1746
|
run_store = create_run(project_root, run_id=artifact.run_id)
|
|
1666
1747
|
run_store.write_artifact(artifact, target=target, status="completed")
|
|
1667
1748
|
|
|
1668
|
-
|
|
1749
|
+
rr_findings, security_suppressions = _prepare_rr_findings(artifact)
|
|
1750
|
+
_print_findings(rr_findings)
|
|
1751
|
+
_report_security_suppressions(security_suppressions, artifact=artifact)
|
|
1752
|
+
_print_security_overview(artifact)
|
|
1669
1753
|
|
|
1670
1754
|
# A fresh review supersedes prior refactor requests: replace them with one
|
|
1671
1755
|
# RR per actionable finding (central-only; nothing is written into project tree).
|
|
1672
1756
|
requests = _replace_requests_with_framework_guardrails(
|
|
1673
1757
|
project_root=project_root,
|
|
1674
|
-
findings=
|
|
1758
|
+
findings=rr_findings,
|
|
1675
1759
|
run_id=artifact.run_id,
|
|
1676
1760
|
compliance_frameworks=compliance_frameworks,
|
|
1677
1761
|
compliance_level=resolved_compliance_level,
|
|
@@ -1781,6 +1865,7 @@ def code(
|
|
|
1781
1865
|
"""
|
|
1782
1866
|
_require_auth()
|
|
1783
1867
|
project_root, constitution, project_config = _load_project()
|
|
1868
|
+
_inject_runtime_scanner_settings(constitution)
|
|
1784
1869
|
try:
|
|
1785
1870
|
(
|
|
1786
1871
|
compliance_frameworks,
|
|
@@ -1870,10 +1955,13 @@ def code(
|
|
|
1870
1955
|
)
|
|
1871
1956
|
review_store = create_run(project_root, run_id=review_artifact.run_id)
|
|
1872
1957
|
review_store.write_artifact(review_artifact, target=target, status="completed")
|
|
1873
|
-
|
|
1958
|
+
rr_findings, security_suppressions = _prepare_rr_findings(review_artifact)
|
|
1959
|
+
_print_findings(rr_findings)
|
|
1960
|
+
_report_security_suppressions(security_suppressions, artifact=review_artifact)
|
|
1961
|
+
_print_security_overview(review_artifact)
|
|
1874
1962
|
requests = _replace_requests_with_framework_guardrails(
|
|
1875
1963
|
project_root=project_root,
|
|
1876
|
-
findings=
|
|
1964
|
+
findings=rr_findings,
|
|
1877
1965
|
run_id=review_artifact.run_id,
|
|
1878
1966
|
compliance_frameworks=compliance_frameworks,
|
|
1879
1967
|
compliance_level=resolved_compliance_level,
|
|
@@ -2294,6 +2382,37 @@ def _format_operation_scope(rr) -> str:
|
|
|
2294
2382
|
return "-"
|
|
2295
2383
|
|
|
2296
2384
|
|
|
2385
|
+
def _security_findings_from_artifact(artifact: RunArtifact) -> list[Finding]:
|
|
2386
|
+
summary = dict(getattr(artifact, "security_summary", {}) or {})
|
|
2387
|
+
raw = summary.get("normalized_findings", [])
|
|
2388
|
+
if not isinstance(raw, list):
|
|
2389
|
+
return []
|
|
2390
|
+
out: list[Finding] = []
|
|
2391
|
+
for item in raw:
|
|
2392
|
+
try:
|
|
2393
|
+
out.append(Finding.model_validate(item))
|
|
2394
|
+
except Exception:
|
|
2395
|
+
continue
|
|
2396
|
+
return out
|
|
2397
|
+
|
|
2398
|
+
|
|
2399
|
+
def _prepare_rr_findings(artifact: RunArtifact) -> tuple[list[Finding], list[SecuritySuppression]]:
|
|
2400
|
+
scanner_findings = _security_findings_from_artifact(artifact)
|
|
2401
|
+
merged, _stats = merge_refactor_findings(list(artifact.findings or []), scanner_findings)
|
|
2402
|
+
actionable, suppressed = suppress_findings_for_rr(merged)
|
|
2403
|
+
summary = dict(getattr(artifact, "security_summary", {}) or {})
|
|
2404
|
+
summary["rr_prefilter_input_count"] = len(merged)
|
|
2405
|
+
summary["rr_prefilter_actionable_count"] = len(actionable)
|
|
2406
|
+
summary["rr_prefilter_suppressed_count"] = len(suppressed)
|
|
2407
|
+
reasons: dict[str, int] = {}
|
|
2408
|
+
for item in suppressed:
|
|
2409
|
+
code = str(item.reason_code or "unknown")
|
|
2410
|
+
reasons[code] = int(reasons.get(code, 0) or 0) + 1
|
|
2411
|
+
summary["rr_prefilter_reason_counts"] = reasons
|
|
2412
|
+
artifact.security_summary = summary
|
|
2413
|
+
return actionable, suppressed
|
|
2414
|
+
|
|
2415
|
+
|
|
2297
2416
|
def _replace_requests_with_framework_guardrails(
|
|
2298
2417
|
*,
|
|
2299
2418
|
project_root: Path,
|
|
@@ -2348,6 +2467,46 @@ def _replace_requests_with_framework_guardrails(
|
|
|
2348
2467
|
return requests
|
|
2349
2468
|
|
|
2350
2469
|
|
|
2470
|
+
def _report_security_suppressions(
|
|
2471
|
+
suppressions: list[SecuritySuppression], *, artifact: RunArtifact | None = None
|
|
2472
|
+
) -> None:
|
|
2473
|
+
if not suppressions:
|
|
2474
|
+
return
|
|
2475
|
+
console.print(
|
|
2476
|
+
f"[yellow]warning:[/yellow] suppressed {len(suppressions)} finding(s) "
|
|
2477
|
+
"before RR creation (security prefilter)."
|
|
2478
|
+
)
|
|
2479
|
+
for item in suppressions:
|
|
2480
|
+
console.print(f"[yellow]-[/yellow] {item.location}: {item.message} [dim]({item.reason_code})[/dim]")
|
|
2481
|
+
if artifact is not None:
|
|
2482
|
+
artifact.notes.append(item.note())
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
def _print_security_overview(artifact: RunArtifact) -> None:
|
|
2486
|
+
summary = dict(getattr(artifact, "security_summary", {}) or {})
|
|
2487
|
+
if not summary:
|
|
2488
|
+
return
|
|
2489
|
+
enabled = bool(summary.get("enabled", False))
|
|
2490
|
+
status = str(summary.get("status", "unknown") or "unknown")
|
|
2491
|
+
raw_count = int(summary.get("raw_count", 0) or 0)
|
|
2492
|
+
normalized = int(summary.get("normalized_count", 0) or 0)
|
|
2493
|
+
actionable = int(summary.get("rr_prefilter_actionable_count", len(getattr(artifact, "findings", []) or [])) or 0)
|
|
2494
|
+
suppressed = int(summary.get("rr_prefilter_suppressed_count", 0) or 0)
|
|
2495
|
+
duplicates = int(summary.get("duplicates_with_base", 0) or 0)
|
|
2496
|
+
scanner_version = str(summary.get("scanner_version", "") or "")
|
|
2497
|
+
version_note = f" v{scanner_version}" if scanner_version else ""
|
|
2498
|
+
enabled_note = "enabled" if enabled else "disabled"
|
|
2499
|
+
console.print(
|
|
2500
|
+
"[dim]security baseline: "
|
|
2501
|
+
f"{enabled_note} status={status}{version_note} raw={raw_count} "
|
|
2502
|
+
f"normalized={normalized} duplicates={duplicates} "
|
|
2503
|
+
f"actionable={actionable} suppressed={suppressed}[/dim]"
|
|
2504
|
+
)
|
|
2505
|
+
errors = summary.get("errors", [])
|
|
2506
|
+
if isinstance(errors, list) and errors:
|
|
2507
|
+
console.print("[dim]security notes:[/dim] " + ", ".join(str(item) for item in errors))
|
|
2508
|
+
|
|
2509
|
+
|
|
2351
2510
|
def _apply_compliance_gate_to_artifact(artifact: RunArtifact) -> None:
|
|
2352
2511
|
"""Promote compliance blocking decisions into gate outcome (R18 Phase 7)."""
|
|
2353
2512
|
summary = dict(getattr(artifact, "compliance_summary", {}) or {})
|
|
@@ -2801,6 +2960,7 @@ def doctor(
|
|
|
2801
2960
|
console.print(f"[green]developer key[/green]: OK account={auth_ctx.account_id}{cache_note}")
|
|
2802
2961
|
except AuthError as exc:
|
|
2803
2962
|
console.print(f"[yellow]developer key[/yellow]: {exc}")
|
|
2963
|
+
_report_runtime_scanner_status()
|
|
2804
2964
|
|
|
2805
2965
|
missing = _missing_requirements(provider_name)
|
|
2806
2966
|
if missing:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.22
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -53,6 +53,6 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
53
53
|
## Install test (local)
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.
|
|
56
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.22-py3-none-any.whl
|
|
57
57
|
refactor --version
|
|
58
58
|
```
|
|
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.2.20 → refactorai_cli-0.2.22}/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
|
{refactorai_cli-0.2.20 → refactorai_cli-0.2.22}/refactorai_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|