guard-cli 0.1.3__tar.gz → 0.1.5__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.
- {guard_cli-0.1.3 → guard_cli-0.1.5}/PKG-INFO +1 -1
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/api.py +9 -6
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/cli.py +1 -1
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/db.py +3 -1
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/hook.py +8 -18
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli.egg-info/PKG-INFO +1 -1
- {guard_cli-0.1.3 → guard_cli-0.1.5}/pyproject.toml +1 -1
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_api_and_hook.py +33 -5
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_coverage.py +14 -5
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_db.py +14 -5
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_feedback.py +14 -5
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_thin_slice.py +14 -5
- {guard_cli-0.1.3 → guard_cli-0.1.5}/LICENSE +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/README.md +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/__init__.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/agent_integration.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/backend.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/decision.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/doctor_cmd.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/events.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/feedback.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/ingest/__init__.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/ingest/coverage.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/ingest/static.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/init_cmd.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/integrations/__init__.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/integrations/antigravity.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/integrations/claude.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/state.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli/thin_slice.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli.egg-info/SOURCES.txt +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli.egg-info/dependency_links.txt +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli.egg-info/entry_points.txt +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli.egg-info/requires.txt +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/guard_cli.egg-info/top_level.txt +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/setup.cfg +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_antigravity_integration.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_antigravity_pre_recording_fixes.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_backend.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_commit_bypass.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_commit_bypass_fresh_session.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_decision.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_doctor_cmd.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_embedded_platform.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_event_reasons.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_fingerprint_invalidation.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_hook_timeout.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_init_cmd.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_integration_init_doctor.py +0 -0
- {guard_cli-0.1.3 → guard_cli-0.1.5}/tests/test_static.py +0 -0
|
@@ -396,12 +396,13 @@ def run(
|
|
|
396
396
|
def covering_tests(
|
|
397
397
|
function_id: str,
|
|
398
398
|
min_confidence: float = 0.0,
|
|
399
|
-
graph_name: str =
|
|
399
|
+
graph_name: str | None = None,
|
|
400
|
+
repo_path: str | Path | None = None,
|
|
400
401
|
strict: bool = False,
|
|
401
402
|
) -> list[dict[str, Any]]:
|
|
402
403
|
"""Find tests covering a given function, ordered by confidence."""
|
|
403
404
|
try:
|
|
404
|
-
g = db.get_graph(graph_name)
|
|
405
|
+
g = db.get_graph(graph_name, repo_path=repo_path)
|
|
405
406
|
return db.get_covering_tests(g, function_id, min_confidence=min_confidence)
|
|
406
407
|
except Exception as exc:
|
|
407
408
|
if strict:
|
|
@@ -414,12 +415,13 @@ def callers(
|
|
|
414
415
|
function_id: str,
|
|
415
416
|
hops: int = 4,
|
|
416
417
|
min_confidence: float = 0.0,
|
|
417
|
-
graph_name: str =
|
|
418
|
+
graph_name: str | None = None,
|
|
419
|
+
repo_path: str | Path | None = None,
|
|
418
420
|
strict: bool = False,
|
|
419
421
|
) -> list[dict[str, Any]]:
|
|
420
422
|
"""Traverse inbound CALLS edges up to N hops filtered by min_confidence."""
|
|
421
423
|
try:
|
|
422
|
-
g = db.get_graph(graph_name)
|
|
424
|
+
g = db.get_graph(graph_name, repo_path=repo_path)
|
|
423
425
|
return db.get_callers(g, function_id, hops=hops, min_confidence=min_confidence)
|
|
424
426
|
except Exception as exc:
|
|
425
427
|
if strict:
|
|
@@ -432,12 +434,13 @@ def report_miss(
|
|
|
432
434
|
function_id: str,
|
|
433
435
|
test_id: str,
|
|
434
436
|
new_confidence: float = 1.0,
|
|
435
|
-
graph_name: str =
|
|
437
|
+
graph_name: str | None = None,
|
|
438
|
+
repo_path: str | Path | None = None,
|
|
436
439
|
strict: bool = False,
|
|
437
440
|
) -> dict[str, Any]:
|
|
438
441
|
"""Records a missed failure by repairing the graph with a miss_repair edge."""
|
|
439
442
|
try:
|
|
440
|
-
g = db.get_graph(graph_name)
|
|
443
|
+
g = db.get_graph(graph_name, repo_path=repo_path)
|
|
441
444
|
return feedback.record_miss(
|
|
442
445
|
g,
|
|
443
446
|
function_id=function_id,
|
|
@@ -239,7 +239,7 @@ def main(argv=None) -> int:
|
|
|
239
239
|
|
|
240
240
|
p = argparse.ArgumentParser(prog="guard")
|
|
241
241
|
p.add_argument("-v", "--version", action="version", version=f"guard {guard_cli.__version__}")
|
|
242
|
-
p.add_argument("--graph", default="guard")
|
|
242
|
+
p.add_argument("--graph", default=None, help="FalkorDB graph name (defaults to repository graph from .guard/config.toml)")
|
|
243
243
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
244
244
|
|
|
245
245
|
ini = sub.add_parser("init", help="Initialize Guard for repository")
|
|
@@ -47,7 +47,9 @@ def get_graph(
|
|
|
47
47
|
if backend is None:
|
|
48
48
|
backend = get_backend(repo_path=repo_path)
|
|
49
49
|
if name is None:
|
|
50
|
-
|
|
50
|
+
from guard_cli.backend import load_config
|
|
51
|
+
cfg = load_config(repo_path)
|
|
52
|
+
name = cfg.get("backend", {}).get("graph_name") or os.getenv("GUARD_GRAPH", GRAPH_NAME)
|
|
51
53
|
return backend.get_graph(name)
|
|
52
54
|
|
|
53
55
|
|
|
@@ -530,16 +530,11 @@ def run_stop_hook(
|
|
|
530
530
|
if is_antigravity:
|
|
531
531
|
resp: dict[str, Any] = {"decision": "stop", "reason": reason or ""}
|
|
532
532
|
payload = json.dumps(resp) + "\n"
|
|
533
|
-
if
|
|
533
|
+
target_out = real_out if real_out is not None else saved_stdout
|
|
534
|
+
if target_out is not None:
|
|
534
535
|
try:
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
except Exception:
|
|
538
|
-
pass
|
|
539
|
-
if real_out is not None and real_out is not saved_stdout:
|
|
540
|
-
try:
|
|
541
|
-
real_out.write(payload)
|
|
542
|
-
real_out.flush()
|
|
536
|
+
target_out.write(payload)
|
|
537
|
+
target_out.flush()
|
|
543
538
|
except Exception:
|
|
544
539
|
pass
|
|
545
540
|
return 0
|
|
@@ -548,16 +543,11 @@ def run_stop_hook(
|
|
|
548
543
|
if is_antigravity:
|
|
549
544
|
resp: dict[str, Any] = {"decision": "continue", "reason": reason_text}
|
|
550
545
|
payload = json.dumps(resp) + "\n"
|
|
551
|
-
if
|
|
552
|
-
|
|
553
|
-
saved_stdout.write(payload)
|
|
554
|
-
saved_stdout.flush()
|
|
555
|
-
except Exception:
|
|
556
|
-
pass
|
|
557
|
-
if real_out is not None and real_out is not saved_stdout:
|
|
546
|
+
target_out = real_out if real_out is not None else saved_stdout
|
|
547
|
+
if target_out is not None:
|
|
558
548
|
try:
|
|
559
|
-
|
|
560
|
-
|
|
549
|
+
target_out.write(payload)
|
|
550
|
+
target_out.flush()
|
|
561
551
|
except Exception:
|
|
562
552
|
pass
|
|
563
553
|
sys.stderr.write(reason_text)
|
|
@@ -58,10 +58,16 @@ def seeded_guard_graph():
|
|
|
58
58
|
"""Seeds a dedicated graph so check() exercises the 'select' path without
|
|
59
59
|
depending on an externally pre-populated 'guard' graph (which may lack
|
|
60
60
|
COVERED_BY evidence and silently fall back to run-all)."""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
backend = db.get_backend()
|
|
62
|
+
if not backend.is_healthy():
|
|
63
|
+
pytest.skip("FalkorDB server is not reachable")
|
|
64
|
+
try:
|
|
65
|
+
g = db.get_graph("guard_api_check_test", backend=backend)
|
|
66
|
+
db.reset_graph(g)
|
|
67
|
+
g = db.get_graph("guard_api_check_test", backend=backend)
|
|
68
|
+
db.ensure_schema(g)
|
|
69
|
+
except Exception as exc:
|
|
70
|
+
pytest.skip(f"FalkorDB server error: {exc}")
|
|
65
71
|
db.write_static(
|
|
66
72
|
g,
|
|
67
73
|
StaticResult(
|
|
@@ -74,7 +80,10 @@ def seeded_guard_graph():
|
|
|
74
80
|
)
|
|
75
81
|
db.write_coverage(g, {"tests/test_mod.py::test_calc": ["pkg/mod.py::calc"]}, confidence=0.9)
|
|
76
82
|
yield g
|
|
77
|
-
|
|
83
|
+
try:
|
|
84
|
+
db.reset_graph(g)
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
78
87
|
|
|
79
88
|
|
|
80
89
|
def test_api_check_schema_version_and_keys(temp_repo, seeded_guard_graph):
|
|
@@ -452,3 +461,22 @@ def test_cli_hook_subcommands(temp_repo, capsys):
|
|
|
452
461
|
ret = main(["hook", "uninstall", "--repo", str(temp_repo), "--antigravity"])
|
|
453
462
|
assert ret == 0
|
|
454
463
|
|
|
464
|
+
|
|
465
|
+
def test_cli_default_graph_resolution(temp_repo):
|
|
466
|
+
"""Verify that when graph name is omitted, db and api resolve graph_name from .guard/config.toml."""
|
|
467
|
+
from guard_cli.backend import save_config
|
|
468
|
+
save_config(temp_repo, {"backend": {"type": "server", "graph_name": "custom_repo_graph"}})
|
|
469
|
+
|
|
470
|
+
with patch("guard_cli.db.get_backend") as mock_get_backend:
|
|
471
|
+
mock_backend = MagicMock()
|
|
472
|
+
mock_get_backend.return_value = mock_backend
|
|
473
|
+
db.get_graph(name=None, repo_path=temp_repo)
|
|
474
|
+
mock_backend.get_graph.assert_called_with("custom_repo_graph")
|
|
475
|
+
|
|
476
|
+
with patch("guard_cli.db.get_backend") as mock_get_backend, patch("guard_cli.db.get_covering_tests") as mock_get_cov:
|
|
477
|
+
mock_backend = MagicMock()
|
|
478
|
+
mock_get_backend.return_value = mock_backend
|
|
479
|
+
mock_get_cov.return_value = []
|
|
480
|
+
api.covering_tests("pkg/mod.py::calc", repo_path=temp_repo)
|
|
481
|
+
mock_backend.get_graph.assert_called_with("custom_repo_graph")
|
|
482
|
+
|
|
@@ -51,12 +51,21 @@ def test_compute_flakiness():
|
|
|
51
51
|
|
|
52
52
|
@pytest.fixture
|
|
53
53
|
def clean_test_graph():
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
backend = db.get_backend()
|
|
55
|
+
if not backend.is_healthy():
|
|
56
|
+
pytest.skip("FalkorDB server is not reachable")
|
|
57
|
+
try:
|
|
58
|
+
g = db.get_graph("guard_cov_test", backend=backend)
|
|
59
|
+
db.reset_graph(g)
|
|
60
|
+
g = db.get_graph("guard_cov_test", backend=backend)
|
|
61
|
+
db.ensure_schema(g)
|
|
62
|
+
except Exception as exc:
|
|
63
|
+
pytest.skip(f"FalkorDB server error: {exc}")
|
|
58
64
|
yield g
|
|
59
|
-
|
|
65
|
+
try:
|
|
66
|
+
db.reset_graph(g)
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
60
69
|
|
|
61
70
|
|
|
62
71
|
def test_coverage_write_to_graph(clean_test_graph):
|
|
@@ -6,12 +6,21 @@ from guard.ingest.static import StaticResult, FuncDef
|
|
|
6
6
|
@pytest.fixture
|
|
7
7
|
def test_graph():
|
|
8
8
|
"""Provides a fresh test graph in FalkorDB."""
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
backend = db.get_backend()
|
|
10
|
+
if not backend.is_healthy():
|
|
11
|
+
pytest.skip("FalkorDB server is not reachable")
|
|
12
|
+
try:
|
|
13
|
+
g = db.get_graph("guard_test", backend=backend)
|
|
14
|
+
db.reset_graph(g)
|
|
15
|
+
g = db.get_graph("guard_test", backend=backend)
|
|
16
|
+
db.ensure_schema(g)
|
|
17
|
+
except Exception as exc:
|
|
18
|
+
pytest.skip(f"FalkorDB server error: {exc}")
|
|
13
19
|
yield g
|
|
14
|
-
|
|
20
|
+
try:
|
|
21
|
+
db.reset_graph(g)
|
|
22
|
+
except Exception:
|
|
23
|
+
pass
|
|
15
24
|
|
|
16
25
|
|
|
17
26
|
def test_ensure_schema_creates_indexes(test_graph):
|
|
@@ -7,12 +7,21 @@ from guard.ingest.static import StaticResult, FuncDef
|
|
|
7
7
|
|
|
8
8
|
@pytest.fixture
|
|
9
9
|
def feedback_test_graph():
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
backend = db.get_backend()
|
|
11
|
+
if not backend.is_healthy():
|
|
12
|
+
pytest.skip("FalkorDB server is not reachable")
|
|
13
|
+
try:
|
|
14
|
+
g = db.get_graph("guard_feedback_test", backend=backend)
|
|
15
|
+
db.reset_graph(g)
|
|
16
|
+
g = db.get_graph("guard_feedback_test", backend=backend)
|
|
17
|
+
db.ensure_schema(g)
|
|
18
|
+
except Exception as exc:
|
|
19
|
+
pytest.skip(f"FalkorDB server error: {exc}")
|
|
14
20
|
yield g
|
|
15
|
-
|
|
21
|
+
try:
|
|
22
|
+
db.reset_graph(g)
|
|
23
|
+
except Exception:
|
|
24
|
+
pass
|
|
16
25
|
|
|
17
26
|
|
|
18
27
|
def test_record_miss_creates_repair_edge(feedback_test_graph):
|
|
@@ -52,12 +52,21 @@ def test_thin_slice_decision_fallback_on_test_files_touched():
|
|
|
52
52
|
|
|
53
53
|
@pytest.fixture
|
|
54
54
|
def slice_test_graph():
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
backend = db.get_backend()
|
|
56
|
+
if not backend.is_healthy():
|
|
57
|
+
pytest.skip("FalkorDB server is not reachable")
|
|
58
|
+
try:
|
|
59
|
+
g = db.get_graph("guard_slice_test", backend=backend)
|
|
60
|
+
db.reset_graph(g)
|
|
61
|
+
g = db.get_graph("guard_slice_test", backend=backend)
|
|
62
|
+
db.ensure_schema(g)
|
|
63
|
+
except Exception as exc:
|
|
64
|
+
pytest.skip(f"FalkorDB server error: {exc}")
|
|
59
65
|
yield g
|
|
60
|
-
|
|
66
|
+
try:
|
|
67
|
+
db.reset_graph(g)
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
61
70
|
|
|
62
71
|
|
|
63
72
|
def test_thin_slice_selects_covering_tests(slice_test_graph):
|
|
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
|
|
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
|