better-code-review-graph 3.27.2__tar.gz → 3.28.0__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.
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.claude-plugin/plugin.json +1 -1
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/CHANGELOG.md +34 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/PKG-INFO +2 -2
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/pyproject.toml +2 -2
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/server.json +2 -2
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/cli.py +150 -1
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/server.py +5 -1
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/integration/test_multi_user_remote.py +2 -4
- better_code_review_graph-3.28.0/tests/test_cli_control_plane.py +131 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_cohere_dimensions.py +2 -7
- better_code_review_graph-3.28.0/tests/test_credential_state.py +164 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_embeddings.py +7 -5
- better_code_review_graph-3.28.0/tests/test_home_isolation.py +108 -0
- better_code_review_graph-3.28.0/tests/test_http_mcp_e2e.py +201 -0
- better_code_review_graph-3.28.0/tests/test_hull_cells.py +84 -0
- better_code_review_graph-3.28.0/tests/test_multi_user.py +111 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_summarizer.py +1 -5
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/uv.lock +2 -2
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.agents/skills/build-graph/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.agents/skills/review-delta/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.agents/skills/review-pr/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.coderabbit.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.dockerignore +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/CODEOWNERS +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/review-learnings.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/rulesets/main.json +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/workflows/cd.yml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.github/workflows/ci.yml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.gitignore +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.infisical.json +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.jules/bolt.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.jules/sentinel.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.pre-commit-config.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/AGENTS.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/BREAKING_CHANGES.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/CLAUDE.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/CODE_OF_CONDUCT.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/CONTRIBUTING.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/Dockerfile +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/LICENSE +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/LICENSE-MIT +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/NOTICE +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/README.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/SECURITY.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/alembic.ini +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/docs/HANDOVER.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/hooks/_graph_state.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/hooks/branch-sync-check.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/hooks/hooks.json +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/hooks/stale-graph-check.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/README +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/env.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/script.py.mako +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/versions/001_baseline.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/versions/002_summary_columns.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/versions/003_federation.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/versions/004_security_tags.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/versions/005_temporal_columns.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/migrations/versions/006_commits_table.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/renovate.json +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/rules/heuristic/cwe-22-path-traversal.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/rules/heuristic/cwe-78-shell-command-sink.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/rules/heuristic/cwe-89-sql-string-format.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/rules/heuristic/cwe-95-eval-on-input.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/rules/heuristic/hardcoded-secret.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/rules/semgrep/curated.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/scripts/benchmark_row_deserialization.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/scripts/enforce-commit.sh +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/scripts/preserve-diacritics.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/scripts/test_preserve_diacritics.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/semantic-release.toml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/skills/impact-audit/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/skills/onboard-repo/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/skills/refactor-check/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/skills/review-delta/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/skills/review-pr/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/skills/security-sweep/SKILL.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/smithery.yaml +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/__init__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/__main__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/config.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/credential_state.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/__init__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/config.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/graph.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/query.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/recipes.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/review.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/docs/security.md +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/embeddings.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/exporter.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/federation.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/graph.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/importer.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/incremental.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/parser.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/py.typed +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/reranker.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/__init__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/_types.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/fallback.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/go.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/java.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/python.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/rust.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/resolver/typescript.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/security/__init__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/security/heuristic.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/security/semgrep_engine.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/summarizer.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/temporal.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/tools.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/src/better_code_review_graph/xpia.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/__init__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/conftest.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/conftest_e2e.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/Sample.cs +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/SampleJava.java +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/callback_refs.js +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/caller_example.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/multi_call_example.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.c +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.cpp +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.kt +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.php +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.rb +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.sol +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample.swift +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample_go.go +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample_python.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample_rust.rs +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/sample_typescript.ts +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/fixtures/test_sample.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/integration/__init__.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_alembic_baseline.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_as_of_diff.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_auto_resolve_ambiguity.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_bare_suffix_correctness.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_callers_not_found_reason.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_callers_of_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_cli.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_cli_contracts.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_commits_migration.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_coverage_gaps.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_credential_state_dehosted.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_crg_protocol_harness.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_custom_local_model.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_dynamic_dispatch_hints.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_e2e.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_embedder_retry_classification.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_embedding_migration.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_embeddings_extra.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_export_graph_dispatch.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_export_graph_dispatch_path_traversal.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_exporter.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_federation.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_federation_migration.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_federation_smoke.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_full_live.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_g6_ux_status_accuracy.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_get_docs_section_error_path.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_get_review_context_no_diff.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_graph.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_graph_batch_fetch.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_graph_extra.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_graph_get_edges_by_targets_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_graph_stats_error_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_handle_not_found_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_help_recipes.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_heuristic.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_impact_payload_truncation.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_implements_edges.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_import_graph_dispatch.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_importer.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_incremental.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_incremental_extra.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_kind_filter_optimization.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_kinds_error_path.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_list_graph_stats_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_list_kinds_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_live_mcp.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_local_reranker.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_lookup_node_directly.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_multilang.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_no_stale_package_name.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_oci_publication_sunset.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_oserror_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_parser.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_parser_extra.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_parser_grammar_failure.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_parser_source_text.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_path_resolution_fallback.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_payload_estimation.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_per_sub_graph_db.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_performance_n1.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_performance_n1_embeddings.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_phase_3_smoke.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_plugin_assets.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_pre_2_0_backup.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_renamed_in_diff.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_renamed_in_diff_exception.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_repo_filter.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_repo_filter_batching.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_repo_id_persistence.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolve_search_candidates.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_dispatcher.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_fallback.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_go.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_java.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_python.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_rust.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_resolver_typescript.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_response_header.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_response_payload_honesty.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_review_delta_line_shifts.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_review_languages_filter.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_reviewer_summary.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_search_edges_by_target_names.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_search_keyword_warning.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_security_injection.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_security_path_traversal.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_security_scan_batching.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_security_tags_migration.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_security_tool.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_semgrep_engine.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_server.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_server_help_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_serverinfo_version.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_setup_tool.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_shares_state_edges.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_spot_check.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_sql_security.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_stdio_direct.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_summarize_graph_dispatch.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_summary_schema.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_temporal.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_temporal_migration.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_tested_by_edges.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_tools.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_tools_build_error_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_tools_full.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_tools_get_docs_section_coverage.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_typescript_coverage_gaps.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_v17_coverage_gaps.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_xpia.py +0 -0
- {better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_xpia_adversarial.py +0 -0
{better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/.claude-plugin/plugin.json
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-code-review-graph",
|
|
3
3
|
"description": "Knowledge graph for token-efficient code reviews -- semantic search and call-graph resolution across your codebase.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.28.0",
|
|
5
5
|
"userConfig": {
|
|
6
6
|
"EMBEDDING_MODELS": {
|
|
7
7
|
"type": "string",
|
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v3.28.0 (2026-09-27)
|
|
6
|
+
|
|
7
|
+
### Code Style
|
|
8
|
+
|
|
9
|
+
- Apply ruff format repo-wide (de-host format debt)
|
|
10
|
+
([`de1a18f`](https://github.com/n24q02m/crg/commit/de1a18f0dcfa46bc53ce37e8f15898a927de16f6))
|
|
11
|
+
|
|
12
|
+
- Fix ruff F401/I001 left by residuals scrub
|
|
13
|
+
([`194ce5f`](https://github.com/n24q02m/crg/commit/194ce5f2298b0be9d3e67435c5cbdaa5d23ee5c8))
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- Add host control-plane CLI (server start, token hash, config, db path)
|
|
18
|
+
([#1101](https://github.com/n24q02m/crg/pull/1101),
|
|
19
|
+
[`ab10fb5`](https://github.com/n24q02m/crg/commit/ab10fb535c74b88d769d9ff94f654a127dd7cf36))
|
|
20
|
+
|
|
21
|
+
### Testing
|
|
22
|
+
|
|
23
|
+
- **wp2**: Add token-auth HTTP MCP e2e + per-sub storage/auth coverage
|
|
24
|
+
([#1100](https://github.com/n24q02m/crg/pull/1100),
|
|
25
|
+
[`11cf315`](https://github.com/n24q02m/crg/commit/11cf3159af5ef90e133091344e12ad7d95eb4091))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## v3.27.3 (2026-09-26)
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
- **deps**: Lock file maintenance ([#1097](https://github.com/n24q02m/crg/pull/1097),
|
|
33
|
+
[`89ff192`](https://github.com/n24q02m/crg/commit/89ff1928bd90f8834456a59d184a6eb25e3c4936))
|
|
34
|
+
|
|
35
|
+
- **deps**: Update fastretrieval to >=1.11.1,<2 ([#1096](https://github.com/n24q02m/crg/pull/1096),
|
|
36
|
+
[`58b1559`](https://github.com/n24q02m/crg/commit/58b1559028cb323104c83b77e95560eb5c5c3c78))
|
|
37
|
+
|
|
38
|
+
|
|
5
39
|
## v3.27.2 (2026-09-26)
|
|
6
40
|
|
|
7
41
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: better-code-review-graph
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.28.0
|
|
4
4
|
Summary: Token-efficient code review knowledge graph: semantic search and call-graph resolution.
|
|
5
5
|
Author: n24q02m
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -16,7 +16,7 @@ Requires-Python: ==3.13.*
|
|
|
16
16
|
Requires-Dist: alembic<2,>=1.20.0
|
|
17
17
|
Requires-Dist: defusedxml>=0.7.1
|
|
18
18
|
Requires-Dist: fastmcp<4,>=3.4.7
|
|
19
|
-
Requires-Dist: fastretrieval<2,>=1.
|
|
19
|
+
Requires-Dist: fastretrieval<2,>=1.11.1
|
|
20
20
|
Requires-Dist: httpx
|
|
21
21
|
Requires-Dist: hull-core
|
|
22
22
|
Requires-Dist: mcp<2,>=1.30.0
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "better-code-review-graph"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.28.0"
|
|
8
8
|
description = "Token-efficient code review knowledge graph: semantic search and call-graph resolution."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -24,7 +24,7 @@ dependencies = [
|
|
|
24
24
|
"tree-sitter-language-pack>=1.20.0,<2",
|
|
25
25
|
"networkx>=3.7,<4",
|
|
26
26
|
"watchdog>=6.0.0,<7",
|
|
27
|
-
"fastretrieval>=1.
|
|
27
|
+
"fastretrieval>=1.11.1,<2",
|
|
28
28
|
"httpx",
|
|
29
29
|
"pydantic-settings",
|
|
30
30
|
# De-hosted shared core (WP1): token auth (3 modes), per-task model cells
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/n24q02m/crg.git",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.28.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "pypi",
|
|
13
13
|
"identifier": "better-code-review-graph",
|
|
14
|
-
"version": "3.
|
|
14
|
+
"version": "3.28.0",
|
|
15
15
|
"runtimeHint": "uvx",
|
|
16
16
|
"runtimeArgs": [
|
|
17
17
|
"--python",
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
11
|
import json
|
|
12
|
+
import os
|
|
12
13
|
from importlib.metadata import PackageNotFoundError
|
|
13
14
|
from importlib.metadata import version as pkg_version
|
|
14
15
|
from typing import Any
|
|
@@ -513,6 +514,148 @@ def _handle_security(args: argparse.Namespace) -> int:
|
|
|
513
514
|
return _print_result(result)
|
|
514
515
|
|
|
515
516
|
|
|
517
|
+
# ---------------------------------------------------------------------------
|
|
518
|
+
# Host control plane (hull-core pattern): server/token/config/db
|
|
519
|
+
# ---------------------------------------------------------------------------
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _configure_server(sub: argparse.ArgumentParser) -> None:
|
|
523
|
+
server_sub = sub.add_subparsers(dest="server_command", required=True)
|
|
524
|
+
start = server_sub.add_parser(
|
|
525
|
+
"start", help="start the HTTP MCP server (blocking; Ctrl+C stops)"
|
|
526
|
+
)
|
|
527
|
+
start.add_argument("--host", default=None, help="bind host (default from config)")
|
|
528
|
+
start.add_argument(
|
|
529
|
+
"--port", type=int, default=None, help="bind port (default from config)"
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _handle_server_start(args: argparse.Namespace) -> int:
|
|
534
|
+
import sys
|
|
535
|
+
|
|
536
|
+
from .config import load_instance_settings
|
|
537
|
+
from .server import serve_main
|
|
538
|
+
|
|
539
|
+
os.environ["MCP_TRANSPORT"] = "http"
|
|
540
|
+
# run_http() resolves bind address from these (falls back to config).
|
|
541
|
+
host = args.host or os.environ.get("MCP_HOST")
|
|
542
|
+
if args.host:
|
|
543
|
+
os.environ["MCP_HOST"] = args.host
|
|
544
|
+
if args.port:
|
|
545
|
+
os.environ["MCP_PORT"] = str(args.port)
|
|
546
|
+
# Security property carried over from the wp2-wip line: an
|
|
547
|
+
# unauthenticated (no-auth) listener must never bind off-loopback.
|
|
548
|
+
if (
|
|
549
|
+
host
|
|
550
|
+
and host not in {"localhost", "127.0.0.1", "::1"}
|
|
551
|
+
and load_instance_settings().server.auth == "no-auth"
|
|
552
|
+
):
|
|
553
|
+
print(
|
|
554
|
+
"crg refuses to start: auth = 'no-auth' only permits loopback "
|
|
555
|
+
"binds; set [server] auth to 'token' or 'multi' for a shared "
|
|
556
|
+
"listener",
|
|
557
|
+
file=sys.stderr,
|
|
558
|
+
)
|
|
559
|
+
return 2
|
|
560
|
+
try:
|
|
561
|
+
serve_main()
|
|
562
|
+
except KeyboardInterrupt:
|
|
563
|
+
pass
|
|
564
|
+
except SystemExit as exc: # e.g. no-auth non-loopback bind refusal
|
|
565
|
+
if str(exc):
|
|
566
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
567
|
+
return 2
|
|
568
|
+
return 0
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _configure_token(sub: argparse.ArgumentParser) -> None:
|
|
572
|
+
token_sub = sub.add_subparsers(dest="token_command", required=True)
|
|
573
|
+
token_hash_p = token_sub.add_parser(
|
|
574
|
+
"hash", help="mint a scrypt token_hash from a plaintext token"
|
|
575
|
+
)
|
|
576
|
+
token_hash_p.add_argument(
|
|
577
|
+
"token", help="plaintext token (keep it out of shell history)"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def _handle_token_hash(args: argparse.Namespace) -> int:
|
|
582
|
+
from hull_core.auth.tokens import hash_token
|
|
583
|
+
|
|
584
|
+
print(hash_token(args.token))
|
|
585
|
+
return 0
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _configure_config(sub: argparse.ArgumentParser) -> None:
|
|
589
|
+
config_sub = sub.add_subparsers(dest="config_command", required=True)
|
|
590
|
+
init_p = config_sub.add_parser(
|
|
591
|
+
"init", help="write the instance config.toml from the template"
|
|
592
|
+
)
|
|
593
|
+
init_p.add_argument(
|
|
594
|
+
"--force", action="store_true", help="overwrite an existing config"
|
|
595
|
+
)
|
|
596
|
+
config_sub.add_parser("path", help="print the config file path")
|
|
597
|
+
config_sub.add_parser(
|
|
598
|
+
"show", help="print the effective config (template if absent)"
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _handle_config_init(args: argparse.Namespace) -> int:
|
|
603
|
+
import sys
|
|
604
|
+
|
|
605
|
+
from hull_core.config.settings import write_default_config
|
|
606
|
+
|
|
607
|
+
from .config import crg_config_dir
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
path = write_default_config(config_dir=crg_config_dir(), force=args.force)
|
|
611
|
+
except FileExistsError as exc:
|
|
612
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
613
|
+
return 2
|
|
614
|
+
print(f"wrote {path}")
|
|
615
|
+
return 0
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def _handle_config_path(_args: argparse.Namespace) -> int:
|
|
619
|
+
from .config import crg_config_dir
|
|
620
|
+
|
|
621
|
+
print(crg_config_dir() / "config.toml")
|
|
622
|
+
return 0
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _handle_config_show(_args: argparse.Namespace) -> int:
|
|
626
|
+
|
|
627
|
+
from hull_core.config.settings import CONFIG_TEMPLATE
|
|
628
|
+
|
|
629
|
+
from .config import crg_config_dir
|
|
630
|
+
|
|
631
|
+
path = crg_config_dir() / "config.toml"
|
|
632
|
+
if path.is_file():
|
|
633
|
+
print(path.read_text(encoding="utf-8"), end="")
|
|
634
|
+
else:
|
|
635
|
+
print(CONFIG_TEMPLATE, end="")
|
|
636
|
+
return 0
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def _configure_db(sub: argparse.ArgumentParser) -> None:
|
|
640
|
+
db_sub = sub.add_subparsers(dest="db_command", required=True)
|
|
641
|
+
db_sub.add_parser("path", help="print the repo-local graph.db path")
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _handle_db_path(_args: argparse.Namespace) -> int:
|
|
645
|
+
from .incremental import find_project_root, get_db_path
|
|
646
|
+
|
|
647
|
+
print(get_db_path(find_project_root(None)))
|
|
648
|
+
return 0
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def _handle_config(args: argparse.Namespace) -> int:
|
|
652
|
+
if args.config_command == "path":
|
|
653
|
+
return _handle_config_path(args)
|
|
654
|
+
if args.config_command == "show":
|
|
655
|
+
return _handle_config_show(args)
|
|
656
|
+
return _handle_config_init(args)
|
|
657
|
+
|
|
658
|
+
|
|
516
659
|
# ---------------------------------------------------------------------------
|
|
517
660
|
# Main entrypoint
|
|
518
661
|
# ---------------------------------------------------------------------------
|
|
@@ -533,6 +676,10 @@ def main() -> int:
|
|
|
533
676
|
"query": (_configure_query, _handle_query),
|
|
534
677
|
"review": (_configure_review, _handle_review),
|
|
535
678
|
"security": (_configure_security, _handle_security),
|
|
679
|
+
"server": (_configure_server, _handle_server_start),
|
|
680
|
+
"token": (_configure_token, _handle_token_hash),
|
|
681
|
+
"config": (_configure_config, _handle_config),
|
|
682
|
+
"db": (_configure_db, _handle_db_path),
|
|
536
683
|
}
|
|
537
684
|
|
|
538
685
|
argv = sys.argv[1:]
|
|
@@ -546,7 +693,9 @@ def main() -> int:
|
|
|
546
693
|
if argv[0] in ("-h", "--help"):
|
|
547
694
|
names = ", ".join(sorted(handlers))
|
|
548
695
|
print("usage: better-code-review-graph [-h] [--version] [<subcommand> ...]")
|
|
549
|
-
print(
|
|
696
|
+
print(
|
|
697
|
+
"Any other flags/args are passed through to the MCP server (e.g. --http)."
|
|
698
|
+
)
|
|
550
699
|
print(f"subcommands: {names}")
|
|
551
700
|
return 0
|
|
552
701
|
if argv[0] in ("--version", "-V"):
|
|
@@ -990,7 +990,11 @@ async def run_http(port: int = 0) -> None:
|
|
|
990
990
|
if host is None:
|
|
991
991
|
host = settings.server.host
|
|
992
992
|
|
|
993
|
-
users =
|
|
993
|
+
users = (
|
|
994
|
+
load_users(settings.server.users_file)
|
|
995
|
+
if settings.server.auth == "multi"
|
|
996
|
+
else None
|
|
997
|
+
)
|
|
994
998
|
authenticator = Authenticator(settings, users=users, limiter=SlidingWindowLimiter())
|
|
995
999
|
app = mcp.http_app(
|
|
996
1000
|
path="/mcp",
|
|
@@ -58,12 +58,10 @@ def test_sub_stores_do_not_see_each_others_nodes(tmp_path, monkeypatch):
|
|
|
58
58
|
_build(store_b, "bob")
|
|
59
59
|
|
|
60
60
|
names_a = {
|
|
61
|
-
row["name"]
|
|
62
|
-
for row in store_a._conn.execute("SELECT name FROM nodes")
|
|
61
|
+
row["name"] for row in store_a._conn.execute("SELECT name FROM nodes")
|
|
63
62
|
}
|
|
64
63
|
names_b = {
|
|
65
|
-
row["name"]
|
|
66
|
-
for row in store_b._conn.execute("SELECT name FROM nodes")
|
|
64
|
+
row["name"] for row in store_b._conn.execute("SELECT name FROM nodes")
|
|
67
65
|
}
|
|
68
66
|
assert names_a == {"alice_fn"}
|
|
69
67
|
assert names_b == {"bob_fn"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Host control-plane CLI: server/token/config/db subcommands (WP2 lifecycle).
|
|
2
|
+
|
|
3
|
+
Fast, hermetic coverage of the operator surface — the server path is tested
|
|
4
|
+
end-to-end (spawn + bearer + build/query) in tests/test_http_mcp_e2e.py; here
|
|
5
|
+
we pin the dispatch contract and the env handoff to run_http().
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from better_code_review_graph.cli import main
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _run(capsys, *argv: str) -> int:
|
|
17
|
+
sys.argv = ["better-code-review-graph", *argv]
|
|
18
|
+
return main()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_token_hash_matches_hull_primitive(capsys) -> None:
|
|
22
|
+
from hull_core.auth.tokens import verify_token
|
|
23
|
+
|
|
24
|
+
rc = _run(capsys, "token", "hash", "s3cret-token")
|
|
25
|
+
out = capsys.readouterr().out.strip()
|
|
26
|
+
assert rc == 0
|
|
27
|
+
# scrypt hashes are salted per call: verify rather than compare.
|
|
28
|
+
assert verify_token("s3cret-token", out)
|
|
29
|
+
assert not verify_token("wrong-token", out)
|
|
30
|
+
rc2 = _run(capsys, "token", "hash", "s3cret-token")
|
|
31
|
+
out2 = capsys.readouterr().out.strip()
|
|
32
|
+
assert rc2 == 0
|
|
33
|
+
assert out2 != out # fresh salt per mint
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_config_path_prints_instance_config(capsys, tmp_path, monkeypatch) -> None:
|
|
37
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path))
|
|
38
|
+
rc = _run(capsys, "config", "path")
|
|
39
|
+
out = capsys.readouterr().out.strip()
|
|
40
|
+
assert rc == 0
|
|
41
|
+
assert out == str(Path(tmp_path) / "config.toml")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_config_init_writes_then_refuses_without_force(
|
|
45
|
+
capsys, tmp_path, monkeypatch
|
|
46
|
+
) -> None:
|
|
47
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path))
|
|
48
|
+
rc = _run(capsys, "config", "init")
|
|
49
|
+
assert rc == 0
|
|
50
|
+
assert (Path(tmp_path) / "config.toml").is_file()
|
|
51
|
+
|
|
52
|
+
rc = _run(capsys, "config", "init")
|
|
53
|
+
err = capsys.readouterr().err
|
|
54
|
+
assert rc == 2
|
|
55
|
+
assert "error" in err
|
|
56
|
+
|
|
57
|
+
rc = _run(capsys, "config", "init", "--force")
|
|
58
|
+
assert rc == 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_config_show_prints_template_when_absent(capsys, tmp_path, monkeypatch) -> None:
|
|
62
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path / "missing"))
|
|
63
|
+
rc = _run(capsys, "config", "show")
|
|
64
|
+
out = capsys.readouterr().out
|
|
65
|
+
assert rc == 0
|
|
66
|
+
assert "[server]" in out # template content
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_db_path_prints_graph_db(capsys) -> None:
|
|
70
|
+
rc = _run(capsys, "db", "path")
|
|
71
|
+
out = capsys.readouterr().out.strip()
|
|
72
|
+
assert rc == 0
|
|
73
|
+
assert out.endswith("graph.db")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_server_start_forces_http_transport(capsys, monkeypatch) -> None:
|
|
77
|
+
monkeypatch.delenv("MCP_TRANSPORT", raising=False)
|
|
78
|
+
monkeypatch.delenv("MCP_HOST", raising=False)
|
|
79
|
+
monkeypatch.delenv("MCP_PORT", raising=False)
|
|
80
|
+
seen: dict[str, object] = {}
|
|
81
|
+
|
|
82
|
+
def fake_serve_main() -> None:
|
|
83
|
+
seen["transport"] = __import__("os").environ.get("MCP_TRANSPORT")
|
|
84
|
+
seen["host"] = __import__("os").environ.get("MCP_HOST")
|
|
85
|
+
seen["port"] = __import__("os").environ.get("MCP_PORT")
|
|
86
|
+
|
|
87
|
+
monkeypatch.setattr("better_code_review_graph.server.serve_main", fake_serve_main)
|
|
88
|
+
|
|
89
|
+
rc = _run(capsys, "server", "start", "--host", "127.0.0.1", "--port", "60077")
|
|
90
|
+
assert rc == 0
|
|
91
|
+
assert seen == {"transport": "http", "host": "127.0.0.1", "port": "60077"}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_server_start_keyboard_interrupt_is_clean(capsys, monkeypatch) -> None:
|
|
95
|
+
def interrupted() -> None:
|
|
96
|
+
raise KeyboardInterrupt
|
|
97
|
+
|
|
98
|
+
monkeypatch.setattr("better_code_review_graph.server.serve_main", interrupted)
|
|
99
|
+
rc = _run(capsys, "server", "start")
|
|
100
|
+
assert rc == 0
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_server_start_refuses_no_auth_off_loopback(
|
|
104
|
+
capsys, tmp_path, monkeypatch
|
|
105
|
+
) -> None:
|
|
106
|
+
cfg = tmp_path / "cfg"
|
|
107
|
+
cfg.mkdir()
|
|
108
|
+
(cfg / "config.toml").write_text(
|
|
109
|
+
'[server]\nhost = "127.0.0.1"\nport = 61000\nauth = "no-auth"\n'
|
|
110
|
+
)
|
|
111
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(cfg))
|
|
112
|
+
monkeypatch.delenv("MCP_TRANSPORT", raising=False)
|
|
113
|
+
serve_called = False
|
|
114
|
+
|
|
115
|
+
def must_not_serve() -> None:
|
|
116
|
+
nonlocal serve_called # noqa: F841
|
|
117
|
+
serve_called = True # pragma: no cover
|
|
118
|
+
|
|
119
|
+
monkeypatch.setattr("better_code_review_graph.server.serve_main", must_not_serve)
|
|
120
|
+
rc = _run(capsys, "server", "start", "--host", "0.0.0.0")
|
|
121
|
+
err = capsys.readouterr().err
|
|
122
|
+
assert rc == 2
|
|
123
|
+
assert "no-auth" in err and "loopback" in err
|
|
124
|
+
assert serve_called is False
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_unknown_subcommand_is_rc2(capsys) -> None:
|
|
128
|
+
rc = _run(capsys, "nope")
|
|
129
|
+
err = capsys.readouterr().err
|
|
130
|
+
assert rc == 2
|
|
131
|
+
assert "unknown subcommand" in err
|
{better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_cohere_dimensions.py
RENAMED
|
@@ -10,7 +10,6 @@ are unchanged.
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
import os
|
|
14
13
|
import sqlite3
|
|
15
14
|
from unittest.mock import patch
|
|
16
15
|
|
|
@@ -44,9 +43,7 @@ def _dispatch(*vectors):
|
|
|
44
43
|
def test_unsupported_cohere_width_is_rejected_without_dispatch():
|
|
45
44
|
# The width-selection guard lives inside _post_embeddings and must fire
|
|
46
45
|
# BEFORE any client is constructed / request is spent.
|
|
47
|
-
with patch(
|
|
48
|
-
"hull_core.providers.openai_spec.OpenAICompatClient"
|
|
49
|
-
) as client_cls:
|
|
46
|
+
with patch("hull_core.providers.openai_spec.OpenAICompatClient") as client_cls:
|
|
50
47
|
with pytest.raises(ValueError, match="requires dimensions"):
|
|
51
48
|
_post_embeddings(
|
|
52
49
|
["hello"],
|
|
@@ -152,9 +149,7 @@ def test_cohere_reopen_reembed_legacy_width_and_query(tmp_path):
|
|
|
152
149
|
assert [
|
|
153
150
|
call.kwargs["input_type"] for call in dispatch.call_args_list
|
|
154
151
|
] == ["search_document", "search_query"]
|
|
155
|
-
assert all(
|
|
156
|
-
call.args[2] == 1024 for call in dispatch.call_args_list
|
|
157
|
-
)
|
|
152
|
+
assert all(call.args[2] == 1024 for call in dispatch.call_args_list)
|
|
158
153
|
finally:
|
|
159
154
|
store.close()
|
|
160
155
|
with sqlite3.connect(db) as conn:
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Tests for credential_state -- hull-core identity scoping + cell state.
|
|
2
|
+
|
|
3
|
+
WP2 de-host: there is no credential store, no relay, and no per-user key
|
|
4
|
+
bucket. What remains is (a) the host-level CONFIGURED/LOCAL cell state,
|
|
5
|
+
(b) the per-request subject contextvar with hull ``multi``-mode fallback,
|
|
6
|
+
and (c) the ``subs/<namespace>/graph.db`` path helpers.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
import better_code_review_graph.credential_state as cs
|
|
14
|
+
from better_code_review_graph.credential_state import (
|
|
15
|
+
SERVER_NAME,
|
|
16
|
+
CredentialState,
|
|
17
|
+
get_current_sub,
|
|
18
|
+
get_state,
|
|
19
|
+
resolve_credential_state,
|
|
20
|
+
set_current_sub,
|
|
21
|
+
set_state,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Helpers
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture(autouse=True)
|
|
30
|
+
def _reset_module_state():
|
|
31
|
+
"""Reset module-level state before and after each test."""
|
|
32
|
+
set_current_sub(None)
|
|
33
|
+
set_state(CredentialState.LOCAL)
|
|
34
|
+
yield
|
|
35
|
+
set_current_sub(None)
|
|
36
|
+
set_state(CredentialState.LOCAL)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _write_config(tmp_path, body: str):
|
|
40
|
+
tmp_path.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
(tmp_path / "config.toml").write_text(body, encoding="utf-8")
|
|
42
|
+
return tmp_path
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# Constants
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestConstants:
|
|
51
|
+
def test_server_name(self):
|
|
52
|
+
assert SERVER_NAME == "better-code-review-graph"
|
|
53
|
+
|
|
54
|
+
def test_credential_state_enum_values(self):
|
|
55
|
+
assert CredentialState.CONFIGURED.value == "configured"
|
|
56
|
+
assert CredentialState.LOCAL.value == "local"
|
|
57
|
+
|
|
58
|
+
def test_enum_has_exactly_two_states(self):
|
|
59
|
+
# No relay-era states may reappear (awaiting_setup / setup_in_progress).
|
|
60
|
+
assert {member.name for member in CredentialState} == {"CONFIGURED", "LOCAL"}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# get_state / set_state
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TestStateAccessors:
|
|
69
|
+
def test_set_state_changes_state(self):
|
|
70
|
+
set_state(CredentialState.CONFIGURED)
|
|
71
|
+
assert get_state() == CredentialState.CONFIGURED
|
|
72
|
+
|
|
73
|
+
def test_set_state_to_local(self):
|
|
74
|
+
set_state(CredentialState.LOCAL)
|
|
75
|
+
assert get_state() == CredentialState.LOCAL
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# resolve_credential_state (host model cells)
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestResolveCredentialState:
|
|
84
|
+
def test_no_config_is_local(self, tmp_path, monkeypatch):
|
|
85
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path / "absent"))
|
|
86
|
+
assert resolve_credential_state() == CredentialState.LOCAL
|
|
87
|
+
assert get_state() == CredentialState.LOCAL
|
|
88
|
+
|
|
89
|
+
def test_chat_cell_key_sets_configured(self, tmp_path, monkeypatch):
|
|
90
|
+
_write_config(
|
|
91
|
+
tmp_path,
|
|
92
|
+
'[models.chat]\nbase_url = "https://openrouter.ai/api/v1"\napi_key = "sk-h"\nmodel = "m"\n',
|
|
93
|
+
)
|
|
94
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path))
|
|
95
|
+
assert resolve_credential_state() == CredentialState.CONFIGURED
|
|
96
|
+
|
|
97
|
+
def test_embed_cell_env_key_sets_configured(self, tmp_path, monkeypatch):
|
|
98
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path))
|
|
99
|
+
monkeypatch.setenv("HULL_EMBED_API_KEY", "sk-e")
|
|
100
|
+
try:
|
|
101
|
+
assert resolve_credential_state() == CredentialState.CONFIGURED
|
|
102
|
+
finally:
|
|
103
|
+
monkeypatch.delenv("HULL_EMBED_API_KEY")
|
|
104
|
+
|
|
105
|
+
def test_cell_without_key_stays_local(self, tmp_path, monkeypatch):
|
|
106
|
+
_write_config(
|
|
107
|
+
tmp_path,
|
|
108
|
+
'[models.embed]\nbase_url = "https://openrouter.ai/api/v1"\napi_key = ""\nmodel = "voyage-4-lite"\n',
|
|
109
|
+
)
|
|
110
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(tmp_path))
|
|
111
|
+
assert resolve_credential_state() == CredentialState.LOCAL
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
# Credential isolation regression guard
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TestCredentialIsolation:
|
|
120
|
+
"""better-code-review-graph must not write to peer MCP servers' configs."""
|
|
121
|
+
|
|
122
|
+
def test_no_peer_key_sharing_helper(self):
|
|
123
|
+
assert not hasattr(cs, "_share_cloud_keys_to_peers")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestCutSurfaceAbsent:
|
|
127
|
+
"""The relay/BYOK surface must not reappear after the de-host."""
|
|
128
|
+
|
|
129
|
+
@pytest.mark.parametrize(
|
|
130
|
+
"symbol",
|
|
131
|
+
[
|
|
132
|
+
"CLOUD_KEYS",
|
|
133
|
+
"credentials_for_current_request",
|
|
134
|
+
"config_value_for_current_request",
|
|
135
|
+
"save_credentials",
|
|
136
|
+
"store_for_sub",
|
|
137
|
+
"read_for_sub",
|
|
138
|
+
"get_setup_url",
|
|
139
|
+
"reset_state",
|
|
140
|
+
"load_credentials",
|
|
141
|
+
"clear_credentials",
|
|
142
|
+
],
|
|
143
|
+
)
|
|
144
|
+
def test_symbol_removed(self, symbol):
|
|
145
|
+
assert not hasattr(cs, symbol)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# ---------------------------------------------------------------------------
|
|
149
|
+
# Current subject
|
|
150
|
+
# ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class TestCurrentSub:
|
|
154
|
+
def test_default_is_none(self):
|
|
155
|
+
assert get_current_sub() is None
|
|
156
|
+
|
|
157
|
+
def test_set_current_sub_roundtrip(self):
|
|
158
|
+
set_current_sub("user-a")
|
|
159
|
+
assert get_current_sub() == "user-a"
|
|
160
|
+
|
|
161
|
+
def test_clearing_restores_none(self):
|
|
162
|
+
set_current_sub("user-a")
|
|
163
|
+
set_current_sub(None)
|
|
164
|
+
assert get_current_sub() is None
|
{better_code_review_graph-3.27.2 → better_code_review_graph-3.28.0}/tests/test_embeddings.py
RENAMED
|
@@ -167,7 +167,9 @@ class TestResolveBackend:
|
|
|
167
167
|
"""
|
|
168
168
|
import logging
|
|
169
169
|
|
|
170
|
-
with caplog.at_level(
|
|
170
|
+
with caplog.at_level(
|
|
171
|
+
logging.WARNING, logger="better_code_review_graph.embeddings"
|
|
172
|
+
):
|
|
171
173
|
with patch.dict(os.environ, {"EMBEDDING_BACKEND": "cloud"}, clear=True):
|
|
172
174
|
assert resolve_backend() == "local"
|
|
173
175
|
with patch.dict(os.environ, {"EMBEDDING_BACKEND": "litellm"}, clear=True):
|
|
@@ -340,7 +342,9 @@ class TestResolveEmbeddingChain:
|
|
|
340
342
|
"CO_API_KEY",
|
|
341
343
|
):
|
|
342
344
|
monkeypatch.delenv(k, raising=False)
|
|
343
|
-
with caplog.at_level(
|
|
345
|
+
with caplog.at_level(
|
|
346
|
+
logging.WARNING, logger="better_code_review_graph.embeddings"
|
|
347
|
+
):
|
|
344
348
|
for legacy in ("cloud", "litellm", "local"):
|
|
345
349
|
monkeypatch.setenv("EMBEDDING_BACKEND", legacy)
|
|
346
350
|
assert resolve_backend() == "local"
|
|
@@ -610,9 +614,7 @@ class TestCloudEmbeddingBackend:
|
|
|
610
614
|
with _patched_client(vectors=[]) as client_cls:
|
|
611
615
|
with pytest.raises(ValueError, match="vector count"):
|
|
612
616
|
backend.embed_texts(["a"])
|
|
613
|
-
assert client_cls.instances[-1].calls == [
|
|
614
|
-
{"texts": ["a"], "dimensions": None}
|
|
615
|
-
]
|
|
617
|
+
assert client_cls.instances[-1].calls == [{"texts": ["a"], "dimensions": None}]
|
|
616
618
|
|
|
617
619
|
def test_width_mismatch_fails_closed(self):
|
|
618
620
|
"""Provider width != requested dimensions must fail loudly."""
|