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.
- code_review_graph/__init__.py +20 -0
- code_review_graph/__main__.py +4 -0
- code_review_graph/analysis.py +410 -0
- code_review_graph/changes.py +409 -0
- code_review_graph/cli.py +1255 -0
- code_review_graph/communities.py +874 -0
- code_review_graph/constants.py +23 -0
- code_review_graph/context_savings.py +317 -0
- code_review_graph/custom_languages.py +322 -0
- code_review_graph/daemon.py +1009 -0
- code_review_graph/daemon_cli.py +320 -0
- code_review_graph/docs/LLM-OPTIMIZED-REFERENCE.md +71 -0
- code_review_graph/embeddings.py +1006 -0
- code_review_graph/enrich.py +303 -0
- code_review_graph/eval/__init__.py +33 -0
- code_review_graph/eval/benchmarks/__init__.py +1 -0
- code_review_graph/eval/benchmarks/agent_baseline.py +193 -0
- code_review_graph/eval/benchmarks/build_performance.py +60 -0
- code_review_graph/eval/benchmarks/flow_completeness.py +36 -0
- code_review_graph/eval/benchmarks/impact_accuracy.py +220 -0
- code_review_graph/eval/benchmarks/multi_hop_retrieval.py +125 -0
- code_review_graph/eval/benchmarks/search_quality.py +59 -0
- code_review_graph/eval/benchmarks/token_efficiency.py +143 -0
- code_review_graph/eval/configs/code-review-graph.yaml +50 -0
- code_review_graph/eval/configs/express.yaml +45 -0
- code_review_graph/eval/configs/fastapi.yaml +48 -0
- code_review_graph/eval/configs/flask.yaml +50 -0
- code_review_graph/eval/configs/gin.yaml +51 -0
- code_review_graph/eval/configs/httpx.yaml +48 -0
- code_review_graph/eval/reporter.py +301 -0
- code_review_graph/eval/runner.py +211 -0
- code_review_graph/eval/scorer.py +85 -0
- code_review_graph/eval/token_benchmark.py +182 -0
- code_review_graph/exports.py +409 -0
- code_review_graph/flows.py +698 -0
- code_review_graph/graph.py +1427 -0
- code_review_graph/graph_diff.py +122 -0
- code_review_graph/hints.py +384 -0
- code_review_graph/incremental.py +1245 -0
- code_review_graph/jedi_resolver.py +303 -0
- code_review_graph/main.py +1079 -0
- code_review_graph/memory.py +142 -0
- code_review_graph/migrations.py +284 -0
- code_review_graph/parser.py +6957 -0
- code_review_graph/postprocessing.py +134 -0
- code_review_graph/prompts.py +159 -0
- code_review_graph/refactor.py +852 -0
- code_review_graph/registry.py +319 -0
- code_review_graph/rescript_resolver.py +206 -0
- code_review_graph/search.py +447 -0
- code_review_graph/skills.py +1481 -0
- code_review_graph/spring_resolver.py +200 -0
- code_review_graph/temporal_resolver.py +199 -0
- code_review_graph/token_benchmark.py +125 -0
- code_review_graph/tools/__init__.py +156 -0
- code_review_graph/tools/_common.py +176 -0
- code_review_graph/tools/analysis_tools.py +184 -0
- code_review_graph/tools/build.py +541 -0
- code_review_graph/tools/community_tools.py +246 -0
- code_review_graph/tools/context.py +152 -0
- code_review_graph/tools/docs.py +274 -0
- code_review_graph/tools/flows_tools.py +176 -0
- code_review_graph/tools/query.py +692 -0
- code_review_graph/tools/refactor_tools.py +168 -0
- code_review_graph/tools/registry_tools.py +125 -0
- code_review_graph/tools/review.py +477 -0
- code_review_graph/tsconfig_resolver.py +257 -0
- code_review_graph/visualization.py +2184 -0
- code_review_graph/wiki.py +305 -0
- code_review_graph_codeblackwell-2.3.6.post1.dist-info/METADATA +718 -0
- code_review_graph_codeblackwell-2.3.6.post1.dist-info/RECORD +74 -0
- code_review_graph_codeblackwell-2.3.6.post1.dist-info/WHEEL +4 -0
- code_review_graph_codeblackwell-2.3.6.post1.dist-info/entry_points.txt +3 -0
- 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)}
|