code-review-graph-codeblackwell 2.3.6.post1__py3-none-any.whl

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 (74) hide show
  1. code_review_graph/__init__.py +20 -0
  2. code_review_graph/__main__.py +4 -0
  3. code_review_graph/analysis.py +410 -0
  4. code_review_graph/changes.py +409 -0
  5. code_review_graph/cli.py +1255 -0
  6. code_review_graph/communities.py +874 -0
  7. code_review_graph/constants.py +23 -0
  8. code_review_graph/context_savings.py +317 -0
  9. code_review_graph/custom_languages.py +322 -0
  10. code_review_graph/daemon.py +1009 -0
  11. code_review_graph/daemon_cli.py +320 -0
  12. code_review_graph/docs/LLM-OPTIMIZED-REFERENCE.md +71 -0
  13. code_review_graph/embeddings.py +1006 -0
  14. code_review_graph/enrich.py +303 -0
  15. code_review_graph/eval/__init__.py +33 -0
  16. code_review_graph/eval/benchmarks/__init__.py +1 -0
  17. code_review_graph/eval/benchmarks/agent_baseline.py +193 -0
  18. code_review_graph/eval/benchmarks/build_performance.py +60 -0
  19. code_review_graph/eval/benchmarks/flow_completeness.py +36 -0
  20. code_review_graph/eval/benchmarks/impact_accuracy.py +220 -0
  21. code_review_graph/eval/benchmarks/multi_hop_retrieval.py +125 -0
  22. code_review_graph/eval/benchmarks/search_quality.py +59 -0
  23. code_review_graph/eval/benchmarks/token_efficiency.py +143 -0
  24. code_review_graph/eval/configs/code-review-graph.yaml +50 -0
  25. code_review_graph/eval/configs/express.yaml +45 -0
  26. code_review_graph/eval/configs/fastapi.yaml +48 -0
  27. code_review_graph/eval/configs/flask.yaml +50 -0
  28. code_review_graph/eval/configs/gin.yaml +51 -0
  29. code_review_graph/eval/configs/httpx.yaml +48 -0
  30. code_review_graph/eval/reporter.py +301 -0
  31. code_review_graph/eval/runner.py +211 -0
  32. code_review_graph/eval/scorer.py +85 -0
  33. code_review_graph/eval/token_benchmark.py +182 -0
  34. code_review_graph/exports.py +409 -0
  35. code_review_graph/flows.py +698 -0
  36. code_review_graph/graph.py +1427 -0
  37. code_review_graph/graph_diff.py +122 -0
  38. code_review_graph/hints.py +384 -0
  39. code_review_graph/incremental.py +1245 -0
  40. code_review_graph/jedi_resolver.py +303 -0
  41. code_review_graph/main.py +1079 -0
  42. code_review_graph/memory.py +142 -0
  43. code_review_graph/migrations.py +284 -0
  44. code_review_graph/parser.py +6957 -0
  45. code_review_graph/postprocessing.py +134 -0
  46. code_review_graph/prompts.py +159 -0
  47. code_review_graph/refactor.py +852 -0
  48. code_review_graph/registry.py +319 -0
  49. code_review_graph/rescript_resolver.py +206 -0
  50. code_review_graph/search.py +447 -0
  51. code_review_graph/skills.py +1481 -0
  52. code_review_graph/spring_resolver.py +200 -0
  53. code_review_graph/temporal_resolver.py +199 -0
  54. code_review_graph/token_benchmark.py +125 -0
  55. code_review_graph/tools/__init__.py +156 -0
  56. code_review_graph/tools/_common.py +176 -0
  57. code_review_graph/tools/analysis_tools.py +184 -0
  58. code_review_graph/tools/build.py +541 -0
  59. code_review_graph/tools/community_tools.py +246 -0
  60. code_review_graph/tools/context.py +152 -0
  61. code_review_graph/tools/docs.py +274 -0
  62. code_review_graph/tools/flows_tools.py +176 -0
  63. code_review_graph/tools/query.py +692 -0
  64. code_review_graph/tools/refactor_tools.py +168 -0
  65. code_review_graph/tools/registry_tools.py +125 -0
  66. code_review_graph/tools/review.py +477 -0
  67. code_review_graph/tsconfig_resolver.py +257 -0
  68. code_review_graph/visualization.py +2184 -0
  69. code_review_graph/wiki.py +305 -0
  70. code_review_graph_codeblackwell-2.3.6.post1.dist-info/METADATA +718 -0
  71. code_review_graph_codeblackwell-2.3.6.post1.dist-info/RECORD +74 -0
  72. code_review_graph_codeblackwell-2.3.6.post1.dist-info/WHEEL +4 -0
  73. code_review_graph_codeblackwell-2.3.6.post1.dist-info/entry_points.txt +3 -0
  74. code_review_graph_codeblackwell-2.3.6.post1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,168 @@
1
+ """Tools 17, 18: refactor_func, apply_refactor_func."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from ..hints import generate_hints, get_session
9
+ from ..incremental import find_project_root
10
+ from ..refactor import (
11
+ apply_refactor,
12
+ find_dead_code,
13
+ rename_preview,
14
+ suggest_refactorings,
15
+ )
16
+ from ._common import _get_store, _validate_repo_root
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Tool 17: refactor_tool [REFACTOR]
20
+ # ---------------------------------------------------------------------------
21
+
22
+
23
+ def refactor_func(
24
+ mode: str = "rename",
25
+ old_name: str | None = None,
26
+ new_name: str | None = None,
27
+ kind: str | None = None,
28
+ file_pattern: str | None = None,
29
+ repo_root: str | None = None,
30
+ ) -> dict[str, Any]:
31
+ """Unified refactoring entry point.
32
+
33
+ [REFACTOR] Supports three modes:
34
+ - ``rename``: Preview renaming a symbol (requires *old_name* and
35
+ *new_name*).
36
+ - ``dead_code``: Find unreferenced functions/classes.
37
+ - ``suggest``: Get community-driven refactoring suggestions.
38
+
39
+ Args:
40
+ mode: One of ``"rename"``, ``"dead_code"``, or ``"suggest"``.
41
+ old_name: (rename mode) Current symbol name.
42
+ new_name: (rename mode) Desired new name.
43
+ kind: (dead_code mode) Optional node kind filter.
44
+ file_pattern: (dead_code mode) Optional file path substring filter.
45
+ repo_root: Repository root path. Auto-detected if omitted.
46
+
47
+ Returns:
48
+ Mode-specific results dict.
49
+ """
50
+ valid_modes = {"rename", "dead_code", "suggest"}
51
+ if mode not in valid_modes:
52
+ return {
53
+ "status": "error",
54
+ "error": (
55
+ f"Invalid mode '{mode}'. "
56
+ f"Must be one of: {', '.join(sorted(valid_modes))}"
57
+ ),
58
+ }
59
+
60
+ store, root = _get_store(repo_root)
61
+ try:
62
+ if mode == "rename":
63
+ if not old_name or not new_name:
64
+ return {
65
+ "status": "error",
66
+ "error": (
67
+ "rename mode requires both old_name and new_name."
68
+ ),
69
+ }
70
+ preview = rename_preview(store, old_name, new_name)
71
+ if preview is None:
72
+ return {
73
+ "status": "not_found",
74
+ "summary": f"No node found matching '{old_name}'.",
75
+ }
76
+ result = {
77
+ "status": "ok",
78
+ "summary": (
79
+ f"Rename preview: {old_name} -> {new_name}, "
80
+ f"{len(preview['edits'])} edit(s). "
81
+ f"Use apply_refactor_tool(refactor_id="
82
+ f"'{preview['refactor_id']}') to apply."
83
+ ),
84
+ **preview,
85
+ }
86
+ result["_hints"] = generate_hints(
87
+ "refactor", result, get_session()
88
+ )
89
+ return result
90
+
91
+ elif mode == "dead_code":
92
+ dead = find_dead_code(
93
+ store, kind=kind, file_pattern=file_pattern, root=root
94
+ )
95
+ result = {
96
+ "status": "ok",
97
+ "summary": f"Found {len(dead)} dead code symbol(s).",
98
+ "dead_code": dead,
99
+ "total": len(dead),
100
+ }
101
+ result["_hints"] = generate_hints(
102
+ "refactor", result, get_session()
103
+ )
104
+ return result
105
+
106
+ else: # suggest
107
+ suggestions = suggest_refactorings(store)
108
+ result = {
109
+ "status": "ok",
110
+ "summary": (
111
+ f"Generated {len(suggestions)} "
112
+ "refactoring suggestion(s)."
113
+ ),
114
+ "suggestions": suggestions,
115
+ "total": len(suggestions),
116
+ }
117
+ result["_hints"] = generate_hints(
118
+ "refactor", result, get_session()
119
+ )
120
+ return result
121
+
122
+ except Exception as exc:
123
+ return {"status": "error", "error": str(exc)}
124
+ finally:
125
+ store.close()
126
+
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # Tool 18: apply_refactor_tool [REFACTOR]
130
+ # ---------------------------------------------------------------------------
131
+
132
+
133
+ def apply_refactor_func(
134
+ refactor_id: str,
135
+ repo_root: str | None = None,
136
+ dry_run: bool = False,
137
+ ) -> dict[str, Any]:
138
+ """Apply a previously previewed refactoring to source files.
139
+
140
+ [REFACTOR] Validates the refactor_id, checks expiry, ensures all edit
141
+ paths are within the repo root, then performs exact string replacements.
142
+
143
+ Args:
144
+ refactor_id: ID returned by a prior ``refactor_tool(mode="rename")``
145
+ call.
146
+ repo_root: Repository root path. Auto-detected if omitted.
147
+ dry_run: If True, return a unified diff of what would change
148
+ without touching disk. The refactor_id remains valid so the
149
+ user can review the diff, then call again with ``dry_run=False``
150
+ to actually write the changes. See: #176
151
+
152
+ Returns:
153
+ Status with count of applied edits and modified files. When
154
+ ``dry_run=True`` the response additionally contains ``would_modify``
155
+ (list of file paths) and ``diffs`` (map of file -> unified-diff
156
+ string).
157
+ """
158
+ try:
159
+ root = (
160
+ _validate_repo_root(Path(repo_root))
161
+ if repo_root
162
+ else find_project_root()
163
+ )
164
+ except (RuntimeError, ValueError) as exc:
165
+ return {"status": "error", "error": str(exc)}
166
+
167
+ result = apply_refactor(refactor_id, root, dry_run=dry_run)
168
+ return result
@@ -0,0 +1,125 @@
1
+ """Tools 21, 22: list_repos_func, cross_repo_search_func."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from ..graph import GraphStore
10
+ from ..incremental import get_db_path
11
+ from ..search import hybrid_search
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # Tool 21: list_repos [REGISTRY]
18
+ # ---------------------------------------------------------------------------
19
+
20
+
21
+ def list_repos_func() -> dict[str, Any]:
22
+ """List all registered repositories.
23
+
24
+ [REGISTRY] Returns the list of repositories registered in the global
25
+ multi-repo registry at ``~/.code-review-graph/registry.json``.
26
+
27
+ Returns:
28
+ List of registered repos with paths and aliases.
29
+ """
30
+ from ..registry import Registry
31
+
32
+ try:
33
+ registry = Registry()
34
+ repos = registry.list_repos()
35
+ return {
36
+ "status": "ok",
37
+ "summary": f"{len(repos)} registered repository(ies)",
38
+ "repos": repos,
39
+ }
40
+ except Exception as exc:
41
+ return {"status": "error", "error": str(exc)}
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Tool 22: cross_repo_search [REGISTRY]
46
+ # ---------------------------------------------------------------------------
47
+
48
+
49
+ def cross_repo_search_func(
50
+ query: str,
51
+ kind: str | None = None,
52
+ limit: int = 20,
53
+ ) -> dict[str, Any]:
54
+ """Search across all registered repositories.
55
+
56
+ [REGISTRY] Runs hybrid_search on each registered repo's graph database
57
+ and merges the results.
58
+
59
+ Args:
60
+ query: Search query string.
61
+ kind: Optional node kind filter (e.g. "Function", "Class").
62
+ limit: Maximum results per repo (default: 20).
63
+
64
+ Returns:
65
+ Combined search results from all registered repos.
66
+ """
67
+ from ..registry import Registry
68
+
69
+ try:
70
+ registry = Registry()
71
+ repos = registry.list_repos()
72
+ if not repos:
73
+ return {
74
+ "status": "ok",
75
+ "summary": (
76
+ "No repositories registered. "
77
+ "Use 'register' to add repos."
78
+ ),
79
+ "results": [],
80
+ }
81
+
82
+ all_results: list[dict[str, Any]] = []
83
+ searched_repos: list[str] = []
84
+
85
+ for repo_entry in repos:
86
+ repo_path = Path(repo_entry["path"])
87
+ db_path = get_db_path(repo_path)
88
+ if not db_path.exists():
89
+ continue
90
+
91
+ try:
92
+ store = GraphStore(str(db_path))
93
+ try:
94
+ results = hybrid_search(
95
+ store, query, kind=kind, limit=limit
96
+ )
97
+ alias = repo_entry.get("alias", repo_path.name)
98
+ for r in results:
99
+ r["repo"] = alias
100
+ r["repo_path"] = str(repo_path)
101
+ all_results.extend(results)
102
+ searched_repos.append(alias)
103
+ finally:
104
+ store.close()
105
+ except Exception as exc:
106
+ logger.warning(
107
+ "Search failed for %s: %s", repo_path, exc
108
+ )
109
+
110
+ # Sort all results by score descending
111
+ all_results.sort(
112
+ key=lambda r: r.get("score", 0), reverse=True
113
+ )
114
+
115
+ return {
116
+ "status": "ok",
117
+ "summary": (
118
+ f"Found {len(all_results)} result(s) across "
119
+ f"{len(searched_repos)} repo(s) for '{query}'"
120
+ ),
121
+ "results": all_results[:limit],
122
+ "repos_searched": searched_repos,
123
+ }
124
+ except Exception as exc:
125
+ return {"status": "error", "error": str(exc)}