better-code-review-graph 3.26.26__tar.gz → 3.27.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.
Files changed (263) hide show
  1. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.claude-plugin/plugin.json +1 -1
  2. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/CHANGELOG.md +27 -0
  3. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/PKG-INFO +2 -3
  4. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/versions/005_temporal_columns.py +44 -1
  5. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/pyproject.toml +9 -17
  6. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/server.json +2 -2
  7. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/cli.py +59 -17
  8. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/config.py +33 -0
  9. better_code_review_graph-3.27.0/src/better_code_review_graph/credential_state.py +160 -0
  10. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/embeddings.py +138 -174
  11. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/server.py +85 -160
  12. better_code_review_graph-3.27.0/src/better_code_review_graph/summarizer.py +290 -0
  13. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/tools.py +6 -11
  14. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/conftest.py +22 -27
  15. better_code_review_graph-3.27.0/tests/integration/test_multi_user_remote.py +72 -0
  16. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_cohere_dimensions.py +49 -45
  17. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_coverage_gaps.py +65 -54
  18. better_code_review_graph-3.27.0/tests/test_credential_state_dehosted.py +114 -0
  19. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_crg_protocol_harness.py +0 -1
  20. better_code_review_graph-3.27.0/tests/test_embedder_retry_classification.py +130 -0
  21. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_embeddings.py +339 -159
  22. better_code_review_graph-3.27.0/tests/test_g6_ux_status_accuracy.py +120 -0
  23. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_oci_publication_sunset.py +15 -2
  24. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_parser_source_text.py +27 -7
  25. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_server.py +7 -7
  26. better_code_review_graph-3.27.0/tests/test_setup_tool.py +197 -0
  27. better_code_review_graph-3.27.0/tests/test_summarizer.py +468 -0
  28. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_temporal_migration.py +57 -0
  29. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_v17_coverage_gaps.py +8 -7
  30. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/uv.lock +16 -489
  31. better_code_review_graph-3.26.26/src/better_code_review_graph/credential_state.py +0 -363
  32. better_code_review_graph-3.26.26/src/better_code_review_graph/relay_schema.py +0 -133
  33. better_code_review_graph-3.26.26/src/better_code_review_graph/relay_setup.py +0 -150
  34. better_code_review_graph-3.26.26/src/better_code_review_graph/summarizer.py +0 -325
  35. better_code_review_graph-3.26.26/tests/integration/test_multi_user_remote.py +0 -43
  36. better_code_review_graph-3.26.26/tests/test_config_open_relay_registered.py +0 -66
  37. better_code_review_graph-3.26.26/tests/test_credential_state.py +0 -435
  38. better_code_review_graph-3.26.26/tests/test_credential_state_stdio_no_fallback.py +0 -210
  39. better_code_review_graph-3.26.26/tests/test_credential_state_store_errors.py +0 -164
  40. better_code_review_graph-3.26.26/tests/test_embedder_retry_classification.py +0 -139
  41. better_code_review_graph-3.26.26/tests/test_g6_ux_status_accuracy.py +0 -120
  42. better_code_review_graph-3.26.26/tests/test_home_isolation.py +0 -113
  43. better_code_review_graph-3.26.26/tests/test_multi_user.py +0 -181
  44. better_code_review_graph-3.26.26/tests/test_multi_user_dispatch.py +0 -513
  45. better_code_review_graph-3.26.26/tests/test_per_plugin_storage_migration.py +0 -38
  46. better_code_review_graph-3.26.26/tests/test_relay_schema.py +0 -56
  47. better_code_review_graph-3.26.26/tests/test_relay_setup.py +0 -365
  48. better_code_review_graph-3.26.26/tests/test_setup_tool.py +0 -312
  49. better_code_review_graph-3.26.26/tests/test_summarizer.py +0 -967
  50. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.agents/skills/build-graph/SKILL.md +0 -0
  51. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.agents/skills/review-delta/SKILL.md +0 -0
  52. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.agents/skills/review-pr/SKILL.md +0 -0
  53. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.coderabbit.yaml +0 -0
  54. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.dockerignore +0 -0
  55. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/CODEOWNERS +0 -0
  56. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  57. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  58. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  59. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/review-learnings.md +0 -0
  60. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/rulesets/main.json +0 -0
  61. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/workflows/cd.yml +0 -0
  62. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.github/workflows/ci.yml +0 -0
  63. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.gitignore +0 -0
  64. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.infisical.json +0 -0
  65. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.jules/bolt.md +0 -0
  66. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.jules/sentinel.md +0 -0
  67. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/.pre-commit-config.yaml +0 -0
  68. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/AGENTS.md +0 -0
  69. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/BREAKING_CHANGES.md +0 -0
  70. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/CLAUDE.md +0 -0
  71. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/CODE_OF_CONDUCT.md +0 -0
  72. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/CONTRIBUTING.md +0 -0
  73. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/Dockerfile +0 -0
  74. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/LICENSE +0 -0
  75. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/LICENSE-MIT +0 -0
  76. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/NOTICE +0 -0
  77. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/README.md +0 -0
  78. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/SECURITY.md +0 -0
  79. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/alembic.ini +0 -0
  80. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/docs/HANDOVER.md +0 -0
  81. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/hooks/_graph_state.py +0 -0
  82. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/hooks/branch-sync-check.py +0 -0
  83. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/hooks/hooks.json +0 -0
  84. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/hooks/stale-graph-check.py +0 -0
  85. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/README +0 -0
  86. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/env.py +0 -0
  87. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/script.py.mako +0 -0
  88. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/versions/001_baseline.py +0 -0
  89. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/versions/002_summary_columns.py +0 -0
  90. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/versions/003_federation.py +0 -0
  91. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/versions/004_security_tags.py +0 -0
  92. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/migrations/versions/006_commits_table.py +0 -0
  93. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/renovate.json +0 -0
  94. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/rules/heuristic/cwe-22-path-traversal.yaml +0 -0
  95. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/rules/heuristic/cwe-78-shell-command-sink.yaml +0 -0
  96. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/rules/heuristic/cwe-89-sql-string-format.yaml +0 -0
  97. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/rules/heuristic/cwe-95-eval-on-input.yaml +0 -0
  98. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/rules/heuristic/hardcoded-secret.yaml +0 -0
  99. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/rules/semgrep/curated.yaml +0 -0
  100. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/scripts/benchmark_row_deserialization.py +0 -0
  101. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/scripts/enforce-commit.sh +0 -0
  102. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/scripts/preserve-diacritics.py +0 -0
  103. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/scripts/test_preserve_diacritics.py +0 -0
  104. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/semantic-release.toml +0 -0
  105. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/skills/impact-audit/SKILL.md +0 -0
  106. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/skills/onboard-repo/SKILL.md +0 -0
  107. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/skills/refactor-check/SKILL.md +0 -0
  108. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/skills/review-delta/SKILL.md +0 -0
  109. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/skills/review-pr/SKILL.md +0 -0
  110. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/skills/security-sweep/SKILL.md +0 -0
  111. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/smithery.yaml +0 -0
  112. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/__init__.py +0 -0
  113. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/__main__.py +0 -0
  114. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/__init__.py +0 -0
  115. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/config.md +0 -0
  116. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/graph.md +0 -0
  117. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/query.md +0 -0
  118. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/recipes.md +0 -0
  119. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/review.md +0 -0
  120. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/docs/security.md +0 -0
  121. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/exporter.py +0 -0
  122. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/federation.py +0 -0
  123. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/graph.py +0 -0
  124. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/importer.py +0 -0
  125. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/incremental.py +0 -0
  126. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/parser.py +0 -0
  127. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/py.typed +0 -0
  128. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/reranker.py +0 -0
  129. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/__init__.py +0 -0
  130. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/_types.py +0 -0
  131. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/fallback.py +0 -0
  132. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/go.py +0 -0
  133. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/java.py +0 -0
  134. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/python.py +0 -0
  135. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/rust.py +0 -0
  136. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/resolver/typescript.py +0 -0
  137. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/security/__init__.py +0 -0
  138. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/security/heuristic.py +0 -0
  139. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/security/semgrep_engine.py +0 -0
  140. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/temporal.py +0 -0
  141. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/src/better_code_review_graph/xpia.py +0 -0
  142. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/__init__.py +0 -0
  143. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/conftest_e2e.py +0 -0
  144. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/e2e_mcp_protocol_test.py +0 -0
  145. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/Sample.cs +0 -0
  146. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/SampleJava.java +0 -0
  147. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/callback_refs.js +0 -0
  148. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/caller_example.py +0 -0
  149. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/multi_call_example.py +0 -0
  150. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.c +0 -0
  151. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.cpp +0 -0
  152. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.kt +0 -0
  153. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.php +0 -0
  154. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.rb +0 -0
  155. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.sol +0 -0
  156. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample.swift +0 -0
  157. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample_go.go +0 -0
  158. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample_python.py +0 -0
  159. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample_rust.rs +0 -0
  160. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/sample_typescript.ts +0 -0
  161. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/fixtures/test_sample.py +0 -0
  162. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/integration/__init__.py +0 -0
  163. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_alembic_baseline.py +0 -0
  164. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_as_of_diff.py +0 -0
  165. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_auto_resolve_ambiguity.py +0 -0
  166. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_bare_suffix_correctness.py +0 -0
  167. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_callers_not_found_reason.py +0 -0
  168. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_callers_of_coverage.py +0 -0
  169. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_cli.py +0 -0
  170. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_cli_contracts.py +0 -0
  171. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_commits_migration.py +0 -0
  172. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_custom_local_model.py +0 -0
  173. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_dynamic_dispatch_hints.py +0 -0
  174. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_e2e.py +0 -0
  175. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_embedding_migration.py +0 -0
  176. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_embeddings_extra.py +0 -0
  177. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_export_graph_dispatch.py +0 -0
  178. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_export_graph_dispatch_path_traversal.py +0 -0
  179. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_exporter.py +0 -0
  180. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_federation.py +0 -0
  181. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_federation_migration.py +0 -0
  182. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_federation_smoke.py +0 -0
  183. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_full_live.py +0 -0
  184. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_get_docs_section_error_path.py +0 -0
  185. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_get_review_context_no_diff.py +0 -0
  186. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_graph.py +0 -0
  187. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_graph_batch_fetch.py +0 -0
  188. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_graph_extra.py +0 -0
  189. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_graph_get_edges_by_targets_coverage.py +0 -0
  190. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_graph_stats_error_coverage.py +0 -0
  191. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_handle_not_found_coverage.py +0 -0
  192. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_help_recipes.py +0 -0
  193. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_heuristic.py +0 -0
  194. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_impact_payload_truncation.py +0 -0
  195. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_implements_edges.py +0 -0
  196. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_import_graph_dispatch.py +0 -0
  197. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_importer.py +0 -0
  198. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_incremental.py +0 -0
  199. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_incremental_extra.py +0 -0
  200. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_kind_filter_optimization.py +0 -0
  201. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_kinds_error_path.py +0 -0
  202. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_list_graph_stats_coverage.py +0 -0
  203. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_list_kinds_coverage.py +0 -0
  204. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_live_mcp.py +0 -0
  205. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_local_reranker.py +0 -0
  206. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_lookup_node_directly.py +0 -0
  207. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_multilang.py +0 -0
  208. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_no_stale_package_name.py +0 -0
  209. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_oserror_coverage.py +0 -0
  210. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_parser.py +0 -0
  211. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_parser_extra.py +0 -0
  212. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_parser_grammar_failure.py +0 -0
  213. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_path_resolution_fallback.py +0 -0
  214. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_payload_estimation.py +0 -0
  215. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_per_sub_graph_db.py +0 -0
  216. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_performance_n1.py +0 -0
  217. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_performance_n1_embeddings.py +0 -0
  218. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_phase_3_smoke.py +0 -0
  219. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_plugin_assets.py +0 -0
  220. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_pre_2_0_backup.py +0 -0
  221. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_renamed_in_diff.py +0 -0
  222. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_renamed_in_diff_exception.py +0 -0
  223. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_repo_filter.py +0 -0
  224. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_repo_filter_batching.py +0 -0
  225. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_repo_id_persistence.py +0 -0
  226. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolve_search_candidates.py +0 -0
  227. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_dispatcher.py +0 -0
  228. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_fallback.py +0 -0
  229. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_go.py +0 -0
  230. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_java.py +0 -0
  231. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_python.py +0 -0
  232. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_rust.py +0 -0
  233. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_resolver_typescript.py +0 -0
  234. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_response_header.py +0 -0
  235. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_response_payload_honesty.py +0 -0
  236. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_review_delta_line_shifts.py +0 -0
  237. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_review_languages_filter.py +0 -0
  238. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_reviewer_summary.py +0 -0
  239. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_search_edges_by_target_names.py +0 -0
  240. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_search_keyword_warning.py +0 -0
  241. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_security_injection.py +0 -0
  242. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_security_path_traversal.py +0 -0
  243. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_security_scan_batching.py +0 -0
  244. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_security_tags_migration.py +0 -0
  245. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_security_tool.py +0 -0
  246. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_semgrep_engine.py +0 -0
  247. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_server_help_coverage.py +0 -0
  248. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_serverinfo_version.py +0 -0
  249. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_shares_state_edges.py +0 -0
  250. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_spot_check.py +0 -0
  251. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_sql_security.py +0 -0
  252. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_stdio_direct.py +0 -0
  253. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_summarize_graph_dispatch.py +0 -0
  254. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_summary_schema.py +0 -0
  255. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_temporal.py +0 -0
  256. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_tested_by_edges.py +0 -0
  257. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_tools.py +0 -0
  258. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_tools_build_error_coverage.py +0 -0
  259. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_tools_full.py +0 -0
  260. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_tools_get_docs_section_coverage.py +0 -0
  261. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_typescript_coverage_gaps.py +0 -0
  262. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_xpia.py +0 -0
  263. {better_code_review_graph-3.26.26 → better_code_review_graph-3.27.0}/tests/test_xpia_adversarial.py +0 -0
@@ -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.26.26",
4
+ "version": "3.27.0",
5
5
  "userConfig": {
6
6
  "EMBEDDING_MODELS": {
7
7
  "type": "string",
@@ -2,6 +2,33 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v3.27.0 (2026-09-26)
6
+
7
+ ### Bug Fixes
8
+
9
+ - **migrations**: 005 skip git guard for per-subject data-dir stores
10
+ ([`5944956`](https://github.com/n24q02m/crg/commit/5944956259acd70ce76d02dd160587dbb9fa832c))
11
+
12
+ - **server**: Drop dead _maybe_include_setup_hint hook (BYOK cut residual)
13
+ ([`87c9ce4`](https://github.com/n24q02m/crg/commit/87c9ce454501aaa2ec39373f3731739ae7247054))
14
+
15
+ - **server,tools**: Remove deleted-member imports on search/setup/http paths
16
+ ([`edc7b5f`](https://github.com/n24q02m/crg/commit/edc7b5f24d61bd1cf8eec1089396e269750694ab))
17
+
18
+ - **wp2**: Drop mcp-core entirely; port residual tests to hull seams
19
+ ([`91b1ddb`](https://github.com/n24q02m/crg/commit/91b1ddb20ca4957ef32bbf40b478c01a8419a5a7))
20
+
21
+ ### Features
22
+
23
+ - **wp2**: De-host onto hull-core — auth scoping, model cells, config bridge
24
+ ([`0683c62`](https://github.com/n24q02m/crg/commit/0683c62d88fbaf11aa8666a3adcf21cd357c9a63))
25
+
26
+ ### Testing
27
+
28
+ - Port suite to the post-de-host surface (hull cells, no litellm/relay)
29
+ ([`fd7795d`](https://github.com/n24q02m/crg/commit/fd7795da0f3ac268430e5d22cbef4756a2120f0d))
30
+
31
+
5
32
  ## v3.26.26 (2026-09-25)
6
33
 
7
34
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: better-code-review-graph
3
- Version: 3.26.26
3
+ Version: 3.27.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
@@ -18,9 +18,8 @@ Requires-Dist: defusedxml>=0.7.1
18
18
  Requires-Dist: fastmcp<4,>=3.4.7
19
19
  Requires-Dist: fastretrieval<2,>=1.8.2
20
20
  Requires-Dist: httpx
21
- Requires-Dist: litellm>=1.102.0
21
+ Requires-Dist: hull-core
22
22
  Requires-Dist: mcp<2,>=1.30.0
23
- Requires-Dist: n24q02m-mcp-core[llm]==1.24.6
24
23
  Requires-Dist: networkx<4,>=3.7
25
24
  Requires-Dist: pydantic-settings
26
25
  Requires-Dist: pygments>=2.21.0
@@ -118,6 +118,15 @@ _SHA_RE = re.compile(r"^[0-9a-f]{40}$|^[0-9a-f]{64}$")
118
118
  # git-init its own tmp dir, which conflicts with tests that
119
119
  # construct ``tmp_path / ".git"`` themselves.
120
120
  #
121
+ # 3. The DB is a per-subject store under the crg data directory
122
+ # (``<CRG_DATA_DIR>/subs/<sub>/graph.db``, ``~/.crg`` default).
123
+ # Multi-user (hull mode-3) deployments scope each namespace's
124
+ # graph to its own data-directory DB precisely so it does NOT
125
+ # live inside any analyzed repo — there is no single HEAD to
126
+ # backfill with, so the sentinel is the correct value. Repo
127
+ # stores (``<repo>/.better-code-review-graph/graph.db``) still
128
+ # require git.
129
+ #
121
130
  # Production code paths leave ``CRG_TEST_ALLOW_NO_GIT`` unset, so the
122
131
  # migration still aborts with the actionable :class:`RuntimeError`
123
132
  # whenever a file-backed DB has no reachable repo — production data
@@ -152,6 +161,35 @@ def _extract_db_path_from_url(url: str) -> Path | None:
152
161
  return Path(raw).resolve()
153
162
 
154
163
 
164
+ def _data_dir_subs_base() -> Path:
165
+ """Return the resolved ``subs`` root of the crg data directory.
166
+
167
+ Mirrors ``credential_state._sub_data_dir``: ``$CRG_DATA_DIR`` when
168
+ set, otherwise ``~/.crg``. Per-subject stores (multi-user HTTP
169
+ mode) live at ``<base>/subs/<sub>/graph.db`` under this root.
170
+ """
171
+ base = Path(os.environ.get("CRG_DATA_DIR", str(Path.home() / ".crg")))
172
+ return (base / "subs").resolve()
173
+
174
+
175
+ def _is_data_dir_store(db_path: Path) -> bool:
176
+ """True iff ``db_path`` is a per-subject store under the crg data dir.
177
+
178
+ Multi-user (hull mode-3) deployments scope each namespace's graph to
179
+ ``<CRG_DATA_DIR>/subs/<sub>/graph.db`` — a data-directory DB that
180
+ intentionally does NOT live inside any analyzed repo (the analyzed
181
+ repo directory is not a safe shared location for a per-tenant DB).
182
+ Such a store has no single HEAD to backfill ``valid_from_sha``
183
+ with, so the 40-zero sentinel applies. Repo stores
184
+ (``<repo>/.better-code-review-graph/graph.db``) keep the hard git
185
+ requirement enforced by :func:`_find_repo_root`.
186
+ """
187
+ try:
188
+ return db_path.resolve().is_relative_to(_data_dir_subs_base())
189
+ except (OSError, ValueError):
190
+ return False
191
+
192
+
155
193
  def _find_repo_root(db_path: Path) -> Path:
156
194
  """Walk parents of ``db_path`` looking for a ``.git`` entry.
157
195
 
@@ -291,7 +329,10 @@ def _resolve_head_sha() -> str:
291
329
  process so the value is only the DDL default for transient tests, OR
292
330
  * the file-backed DB has no reachable ``.git`` ancestor AND the
293
331
  operator has opted in to the test-only escape hatch via
294
- ``CRG_TEST_ALLOW_NO_GIT=1``.
332
+ ``CRG_TEST_ALLOW_NO_GIT=1``, OR
333
+ * the DB is a per-subject store under the crg data directory
334
+ (``<CRG_DATA_DIR>/subs/<sub>/graph.db``) — a data-directory DB
335
+ intentionally outside every repo, so there is no HEAD to read.
295
336
 
296
337
  File-backed DBs without a reachable ``.git`` ancestor and without
297
338
  the test escape hatch still raise the actionable
@@ -307,6 +348,8 @@ def _resolve_head_sha() -> str:
307
348
  db_path = _extract_db_path_from_url(url)
308
349
  if db_path is None:
309
350
  return _IN_MEMORY_SENTINEL_SHA
351
+ if _is_data_dir_store(db_path):
352
+ return _IN_MEMORY_SENTINEL_SHA
310
353
  try:
311
354
  repo_root = _find_repo_root(db_path)
312
355
  return _read_head_sha(repo_root)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "better-code-review-graph"
7
- version = "3.26.26"
7
+ version = "3.27.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"
@@ -27,27 +27,19 @@ dependencies = [
27
27
  "fastretrieval>=1.8.2,<2",
28
28
  "httpx",
29
29
  "pydantic-settings",
30
- # Floor tracks the current mcp-core stable for cascade parity with
31
- # wet/mnemo. Carries the build_cli `config`/`doctor` PerPluginStore key
32
- # fix (#666) for servers whose plugin slug differs from server_name
33
- # (this repo's slug == server_name already, so no CLI regression here).
34
- # Keep this on a stable release -- a prerelease floor such as 1.20.0b2
35
- # sorts below its own stable under PEP 440 and does not require it.
36
- "n24q02m-mcp-core[llm]==1.24.6",
37
- # litellm security floor (transitive via mcp-core[llm]). 1.83.x-1.87.x carry 4
38
- # advisories in litellm's PROXY server: GHSA-r75f-5x8p-qvmc (SQLi in proxy key
39
- # verify), GHSA-xqmj-j6mv-4862 (SSTI /prompts/test), GHSA-v4p8-mg3p-g94g (cmd-exec
40
- # proxy MCP endpoints), GHSA-wxxx-gvqv-xp7p (sandbox escape custom-code guardrail).
41
- # We use litellm LIBRARY mode only (no proxy) so none are in our attack surface,
42
- # but Dependency Review flags the package version. 1.88.0+ clears all four.
43
- # crg's optional semgrep extra otherwise pins the resolver to litellm 1.83.0; this
44
- # floor lifts it (semgrep resolves to 1.79.x, fine for a dev-only SAST tool).
45
- "litellm>=1.102.0",
30
+ # De-hosted shared core (WP1): token auth (3 modes), per-task model cells
31
+ # (plain-HTTP OpenAI-spec, no litellm), server lifecycle lock. Vendored via
32
+ # git because hull-core is not published to PyPI (spec 2026-09-26 §3);
33
+ # rev pins the WP1-verified hull commit.
34
+ "hull-core",
46
35
  "Pygments>=2.21.0",
47
36
  "alembic>=1.20.0,<2",
48
37
  "defusedxml>=0.7.1",
49
38
  ]
50
39
 
40
+ [tool.uv.sources]
41
+ hull-core = { git = "https://github.com/n24q02m/hull", subdirectory = "packages/core-py", rev = "5262d54f4f0c1214dcedaef20531da98f9519bdf" }
42
+
51
43
  [project.scripts]
52
44
  better-code-review-graph = "better_code_review_graph.cli:main"
53
45
  crg = "better_code_review_graph.cli:main"
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/n24q02m/crg.git",
7
7
  "source": "github"
8
8
  },
9
- "version": "3.26.26",
9
+ "version": "3.27.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "pypi",
13
13
  "identifier": "better-code-review-graph",
14
- "version": "3.26.26",
14
+ "version": "3.27.0",
15
15
  "runtimeHint": "uvx",
16
16
  "runtimeArgs": [
17
17
  "--python",
@@ -1,8 +1,8 @@
1
- """Console-script entry: mounts the shared mcp_core CLI builder.
1
+ """Console-script entry: local argparse dispatch (de-hosted, no shared core).
2
2
 
3
- Bare invocation and any leading-dash argv (e.g. --http) start the server
4
- exactly as before; a leading positional argv[0] routes to a subcommand
5
- (``graph``, ``query``, ``review``, ``security``, etc.) instead.
3
+ Bare invocation and any leading-dash argv (e.g. --http) start the server;
4
+ a leading positional argv[0] routes to a subcommand (``graph``, ``query``,
5
+ ``review``, ``security``) instead of the server.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -519,19 +519,61 @@ def _handle_security(args: argparse.Namespace) -> int:
519
519
 
520
520
 
521
521
  def main() -> int:
522
- from mcp_core import build_cli
523
-
524
- return build_cli(
525
- "better-code-review-graph",
526
- serve=_serve,
527
- extra={
528
- "graph": (_configure_graph, _handle_graph),
529
- "query": (_configure_query, _handle_query),
530
- "review": (_configure_review, _handle_review),
531
- "security": (_configure_security, _handle_security),
532
- },
533
- version=_version(),
534
- )(None)
522
+ """Route argv: server start vs. local subcommands.
523
+
524
+ Same dispatch contract the shared core builder used to provide, now
525
+ local: bare or leading-dash argv goes to the server (``_serve``), a
526
+ known leading subcommand is parsed by its own argparse subparser and
527
+ handed to its handler, anything else is a clean rc-2 usage error.
528
+ """
529
+ import sys
530
+
531
+ handlers: dict[str, tuple[Any, Any]] = {
532
+ "graph": (_configure_graph, _handle_graph),
533
+ "query": (_configure_query, _handle_query),
534
+ "review": (_configure_review, _handle_review),
535
+ "security": (_configure_security, _handle_security),
536
+ }
537
+
538
+ argv = sys.argv[1:]
539
+ if not argv:
540
+ rc = _serve([])
541
+ return 0 if rc is None else rc
542
+
543
+ # Intercept bare flags before argparse: -h/--help/--version must print
544
+ # and exit instead of starting (and blocking on) the stdio server, and
545
+ # any other leading dash is passed through to the server untouched.
546
+ if argv[0] in ("-h", "--help"):
547
+ names = ", ".join(sorted(handlers))
548
+ print("usage: better-code-review-graph [-h] [--version] [<subcommand> ...]")
549
+ print("Any other flags/args are passed through to the MCP server (e.g. --http).")
550
+ print(f"subcommands: {names}")
551
+ return 0
552
+ if argv[0] in ("--version", "-V"):
553
+ print(f"better-code-review-graph {_version()}")
554
+ return 0
555
+ if argv[0].startswith("-"):
556
+ rc = _serve(argv)
557
+ return 0 if rc is None else rc
558
+
559
+ spec = handlers.get(argv[0])
560
+ if spec is None:
561
+ names = ", ".join(sorted(handlers))
562
+ print(
563
+ f"better-code-review-graph: unknown subcommand {argv[0]!r} "
564
+ f"(expected one of: {names})",
565
+ file=sys.stderr,
566
+ )
567
+ return 2
568
+
569
+ configure_fn, handler_fn = spec
570
+ parser = argparse.ArgumentParser(prog="better-code-review-graph")
571
+ subparsers = parser.add_subparsers(dest="subcommand")
572
+ sub = subparsers.add_parser(argv[0])
573
+ if configure_fn is not None:
574
+ configure_fn(sub)
575
+ ns = parser.parse_args(argv)
576
+ return handler_fn(ns)
535
577
 
536
578
 
537
579
  if __name__ == "__main__":
@@ -1,7 +1,11 @@
1
1
  """Cấu hình cho backend embedding cục bộ của CRG."""
2
2
 
3
+ import os
4
+ from pathlib import Path
3
5
  from typing import Any
4
6
 
7
+ from hull_core.config.models import ModelCell, resolve_model_cells
8
+ from hull_core.config.settings import HullSettings, load_settings
5
9
  from pydantic import model_validator
6
10
  from pydantic_settings import BaseSettings
7
11
 
@@ -44,3 +48,32 @@ class Settings(BaseSettings):
44
48
 
45
49
 
46
50
  settings = Settings()
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # Hull-core instance config (WP2 de-host, spec 2026-09-26 §3/§4)
55
+ # ---------------------------------------------------------------------------
56
+
57
+
58
+ def crg_config_dir() -> Path:
59
+ """Host-owned instance config directory: ``$CRG_CONFIG_DIR`` or ``~/.crg``.
60
+
61
+ Same schema as hull's ``config.toml``: ``[server]`` (bind, auth mode,
62
+ users file) + per-task ``[models.<task>]`` provider cells. Keys live
63
+ here (or arrive via ``HULL_<TASK>_API_KEY`` env) — they are host-only
64
+ material and never end-user supplied (spec §4 Q1, BYOK cut).
65
+ """
66
+ return Path(os.environ.get("CRG_CONFIG_DIR") or Path.home() / ".crg")
67
+
68
+
69
+ def load_instance_settings() -> HullSettings:
70
+ """Load the crg instance config through hull-core's loader."""
71
+ return load_settings(crg_config_dir())
72
+
73
+
74
+ def resolve_cells(
75
+ settings: HullSettings | None = None, env: dict[str, str] | None = None
76
+ ) -> dict[str, ModelCell]:
77
+ """Resolve the per-task provider cells (embed / rerank / chat / jev_score)."""
78
+ settings = settings if settings is not None else load_instance_settings()
79
+ return resolve_model_cells(settings.models, env=env)
@@ -0,0 +1,160 @@
1
+ """Per-request subject scoping and per-sub data paths.
2
+
3
+ De-hosted (WP2, spec 2026-09-26 §4): authentication is hull-core's token
4
+ middleware with three config modes (no-auth / token / multi, ``users.toml``).
5
+ There is no credential store, no browser setup flow, and no per-user key
6
+ bucket —
7
+ provider keys are host-only material configured in the instance
8
+ ``config.toml`` ``[models.*]`` cells (or ``HULL_<TASK>_API_KEY`` env); the
9
+ end user never sees or submits them (BYOK cut).
10
+
11
+ What remains here is the data-isolation half: in auth mode ``multi`` the
12
+ authenticated user's namespace scopes the graph database at
13
+ ``subs/<namespace>/graph.db`` under ``CRG_DATA_DIR`` so concurrent users on
14
+ one process never see each other's graph. Modes no-auth/token keep one
15
+ shared namespace and therefore the repo-local ``graph.db`` (same file the
16
+ CLI uses — host indexing and serving stay consistent).
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import contextvars
22
+ import logging
23
+ import os
24
+ from enum import Enum
25
+ from pathlib import Path
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ SERVER_NAME = "better-code-review-graph"
30
+
31
+
32
+ class CredentialState(Enum):
33
+ CONFIGURED = "configured" # at least one per-task model cell has a key
34
+ LOCAL = "local" # no cloud cells configured; local ONNX fallback serves
35
+
36
+
37
+ # Module-level state (host-level; never per-request — per-request identity
38
+ # flows exclusively through the contextvars below).
39
+ _state: CredentialState = CredentialState.LOCAL
40
+
41
+ # Per-request namespace subject (HTTP multi-user mode).
42
+ #
43
+ # Bound by the hull auth middleware's resolved identity: tool handlers call
44
+ # :func:`get_current_sub`, which prefers an explicit :func:`set_current_sub`
45
+ # binding and otherwise falls back to hull-core's ``current_user()``
46
+ # contextvar when the request authenticated in ``multi`` mode. ``None`` in
47
+ # stdio mode, the CLI, and single-namespace HTTP modes (no-auth/token) —
48
+ # those share the repo-local graph.db.
49
+ _current_sub: contextvars.ContextVar[str | None] = contextvars.ContextVar(
50
+ "crg_current_sub", default=None
51
+ )
52
+
53
+
54
+ def get_state() -> CredentialState:
55
+ """Return current credential state."""
56
+ return _state
57
+
58
+
59
+ def resolve_credential_state() -> CredentialState:
60
+ """Resolve whether any per-task model cell is configured (has a key).
61
+
62
+ Host-only check against the instance ``config.toml`` cells + key env
63
+ vars. Local ONNX embedding and everything except cloud summarize/embed
64
+ work regardless, so ``LOCAL`` is a valid steady state, not an error.
65
+ Takes <10ms; called during HTTP lifespan startup and by ``config``.
66
+ """
67
+ global _state
68
+
69
+ from .config import resolve_cells
70
+
71
+ cells = resolve_cells()
72
+ configured = any(cell.configured for cell in cells.values())
73
+ _state = CredentialState.CONFIGURED if configured else CredentialState.LOCAL
74
+ logger.info("Credential state resolved: %s", _state.value)
75
+ return _state
76
+
77
+
78
+ def set_state(state: CredentialState) -> None:
79
+ """For testing and the config tool status action."""
80
+ global _state
81
+ _state = state
82
+
83
+
84
+ def _sub_data_dir(sub: str) -> Path:
85
+ """Return per-sub data directory (creates if missing).
86
+
87
+ Each authenticated namespace gets its own scope under
88
+ ``<CRG_DATA_DIR>/subs/<sub>/`` so graph state never leaks across users
89
+ sharing one deployment process.
90
+ """
91
+ # sub must be a single path component to prevent traversal.
92
+ if (
93
+ not sub
94
+ or sub in (".", "..")
95
+ or "/" in sub
96
+ or "\\" in sub
97
+ or os.path.sep in sub
98
+ or (os.path.altsep and os.path.altsep in sub)
99
+ ):
100
+ raise ValueError(f"Invalid subject (must be a single path component): {sub}")
101
+
102
+ base = Path(os.environ.get("CRG_DATA_DIR", str(Path.home() / ".crg")))
103
+ subs_base = (base / "subs").resolve()
104
+
105
+ # defense-in-depth: still verify the resolved path stays within subs_base
106
+ d = (subs_base / sub).resolve()
107
+ if not d.is_relative_to(subs_base) or d == subs_base:
108
+ raise ValueError(f"Invalid subject: {sub}")
109
+
110
+ d.mkdir(parents=True, exist_ok=True)
111
+ return d
112
+
113
+
114
+ def db_path_for_sub(sub: str) -> Path:
115
+ """Return per-sub graph.db path for multi-user mode."""
116
+ return _sub_data_dir(sub) / "graph.db"
117
+
118
+
119
+ def set_current_sub(sub: str | None) -> None:
120
+ """Bind the namespace subject for the current request (multi-user mode).
121
+
122
+ Normally the hull auth middleware's identity is consumed implicitly via
123
+ :func:`get_current_sub`; this explicit binding exists for tests and
124
+ host tooling that must scope work to one namespace outside a request.
125
+ Pass ``None`` to clear.
126
+ """
127
+ _current_sub.set(sub)
128
+
129
+
130
+ def get_current_sub() -> str | None:
131
+ """Return the namespace subject for the current request, or ``None``.
132
+
133
+ Resolution order:
134
+
135
+ 1. Explicit :func:`set_current_sub` binding.
136
+ 2. hull-core's request identity (``current_user()``) when the request
137
+ authenticated in ``multi`` mode — its namespace is the data root.
138
+ Worker-thread tool handlers inherit the contextvar via anyio's
139
+ context copy, same channel hull's own tools use.
140
+
141
+ ``None`` means shared-namespace context (stdio, CLI, no-auth/token HTTP):
142
+ callers use the repo-local ``graph.db``.
143
+ """
144
+ sub = _current_sub.get()
145
+ if sub is not None:
146
+ return sub
147
+
148
+ from hull_core.auth.context import current_user
149
+
150
+ try:
151
+ ctx = current_user()
152
+ except Exception: # pragma: no cover - hull always present post de-host
153
+ return None
154
+ if ctx.mode == "multi" and ctx.namespace:
155
+ sub = ctx.namespace
156
+ # Reuse the path-safety validation so a hostile users.toml namespace
157
+ # cannot escape the subs/ root.
158
+ _sub_data_dir(sub)
159
+ return sub
160
+ return None