codegraphcontext 0.1.16__tar.gz → 0.1.18__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.
- {codegraphcontext-0.1.16/src/codegraphcontext.egg-info → codegraphcontext-0.1.18}/PKG-INFO +28 -3
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/README.md +24 -1
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/pyproject.toml +5 -3
- codegraphcontext-0.1.18/src/codegraphcontext/cli/cli_helpers.py +326 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/main.py +74 -56
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/setup_wizard.py +12 -4
- codegraphcontext-0.1.18/src/codegraphcontext/core/__init__.py +92 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/core/database.py +6 -8
- codegraphcontext-0.1.18/src/codegraphcontext/core/database_falkordb.py +375 -0
- codegraphcontext-0.1.18/src/codegraphcontext/core/falkor_worker.py +68 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/core/watcher.py +14 -16
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/server.py +141 -32
- codegraphcontext-0.1.18/src/codegraphcontext/tools/advanced_language_query_tool.py +99 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/code_finder.py +53 -44
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/graph_builder.py +140 -27
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/c.py +2 -5
- codegraphcontext-0.1.18/src/codegraphcontext/tools/languages/cpp.py +456 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/languages/csharp.py +531 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/go.py +2 -3
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/java.py +43 -11
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/javascript.py +27 -6
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/python.py +5 -6
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/ruby.py +73 -8
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/rust.py +30 -12
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/typescript.py +3 -5
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
- codegraphcontext-0.1.18/src/codegraphcontext/utils/debug_log.py +39 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18/src/codegraphcontext.egg-info}/PKG-INFO +28 -3
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/SOURCES.txt +15 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/requires.txt +2 -0
- codegraphcontext-0.1.16/src/codegraphcontext/core/__init__.py +0 -1
- codegraphcontext-0.1.16/src/codegraphcontext/tools/languages/cpp.py +0 -253
- codegraphcontext-0.1.16/src/codegraphcontext/utils/debug_log.py +0 -10
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/LICENSE +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/MANIFEST.in +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/setup.cfg +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/package_resolver.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/top_level.txt +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_cpp_parser.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_database_validation.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_end_to_end.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_graph_indexing.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_graph_indexing_js.py +0 -0
- {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_typescript_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codegraphcontext
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
4
4
|
Summary: An MCP server that indexes local code into a graph database to provide context to AI assistants.
|
|
5
5
|
Author-email: Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -33,7 +33,7 @@ Classifier: Operating System :: OS Independent
|
|
|
33
33
|
Classifier: Development Status :: 3 - Alpha
|
|
34
34
|
Classifier: Intended Audience :: Developers
|
|
35
35
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
36
|
-
Requires-Python: >=3.
|
|
36
|
+
Requires-Python: >=3.12
|
|
37
37
|
Description-Content-Type: text/markdown
|
|
38
38
|
License-File: LICENSE
|
|
39
39
|
Requires-Dist: neo4j>=5.15.0
|
|
@@ -50,6 +50,8 @@ Requires-Dist: pyyaml
|
|
|
50
50
|
Requires-Dist: pytest
|
|
51
51
|
Requires-Dist: nbformat
|
|
52
52
|
Requires-Dist: nbconvert>=7.16.6
|
|
53
|
+
Requires-Dist: pathspec>=0.12.1
|
|
54
|
+
Requires-Dist: falkordblite>=0.1.0
|
|
53
55
|
Provides-Extra: dev
|
|
54
56
|
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
55
57
|
Requires-Dist: black>=23.11.0; extra == "dev"
|
|
@@ -87,7 +89,7 @@ An MCP server that indexes local code into a graph database to provide context t
|
|
|
87
89
|

|
|
88
90
|
|
|
89
91
|
## Project Details
|
|
90
|
-
- **Version:** 0.1.
|
|
92
|
+
- **Version:** 0.1.17
|
|
91
93
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
92
94
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
93
95
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -124,6 +126,11 @@ If you’re using CodeGraphContext in your project, feel free to open a PR and a
|
|
|
124
126
|
- `python-dotenv>=1.0.0`
|
|
125
127
|
- `tree-sitter==0.20.4`
|
|
126
128
|
- `tree-sitter-languages==1.10.2`
|
|
129
|
+
- `pyyaml`
|
|
130
|
+
- `pytest`
|
|
131
|
+
- `nbformat`
|
|
132
|
+
- `nbconvert>=7.16.6`
|
|
133
|
+
- `pathspec>=0.12.1`
|
|
127
134
|
|
|
128
135
|
## Getting Started
|
|
129
136
|
|
|
@@ -189,6 +196,24 @@ If you’re using CodeGraphContext in your project, feel free to open a PR and a
|
|
|
189
196
|
|
|
190
197
|
3. **Start:** `cgc start`
|
|
191
198
|
|
|
199
|
+
## Ignoring Files (`.cgcignore`)
|
|
200
|
+
|
|
201
|
+
You can tell CodeGraphContext to ignore specific files and directories by creating a `.cgcignore` file in the root of your project. This file uses the same syntax as `.gitignore`.
|
|
202
|
+
|
|
203
|
+
**Example `.cgcignore` file:**
|
|
204
|
+
```
|
|
205
|
+
# Ignore build artifacts
|
|
206
|
+
/build/
|
|
207
|
+
/dist/
|
|
208
|
+
|
|
209
|
+
# Ignore dependencies
|
|
210
|
+
/node_modules/
|
|
211
|
+
/vendor/
|
|
212
|
+
|
|
213
|
+
# Ignore logs
|
|
214
|
+
*.log
|
|
215
|
+
```
|
|
216
|
+
|
|
192
217
|
## MCP Client Configuration
|
|
193
218
|
|
|
194
219
|
The `cgc setup` command attempts to automatically configure your IDE/CLI. If you choose not to use the automatic setup, or if your tool is not supported, you can configure it manually.
|
|
@@ -29,7 +29,7 @@ An MCP server that indexes local code into a graph database to provide context t
|
|
|
29
29
|

|
|
30
30
|
|
|
31
31
|
## Project Details
|
|
32
|
-
- **Version:** 0.1.
|
|
32
|
+
- **Version:** 0.1.17
|
|
33
33
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
34
34
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
35
35
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -66,6 +66,11 @@ If you’re using CodeGraphContext in your project, feel free to open a PR and a
|
|
|
66
66
|
- `python-dotenv>=1.0.0`
|
|
67
67
|
- `tree-sitter==0.20.4`
|
|
68
68
|
- `tree-sitter-languages==1.10.2`
|
|
69
|
+
- `pyyaml`
|
|
70
|
+
- `pytest`
|
|
71
|
+
- `nbformat`
|
|
72
|
+
- `nbconvert>=7.16.6`
|
|
73
|
+
- `pathspec>=0.12.1`
|
|
69
74
|
|
|
70
75
|
## Getting Started
|
|
71
76
|
|
|
@@ -131,6 +136,24 @@ If you’re using CodeGraphContext in your project, feel free to open a PR and a
|
|
|
131
136
|
|
|
132
137
|
3. **Start:** `cgc start`
|
|
133
138
|
|
|
139
|
+
## Ignoring Files (`.cgcignore`)
|
|
140
|
+
|
|
141
|
+
You can tell CodeGraphContext to ignore specific files and directories by creating a `.cgcignore` file in the root of your project. This file uses the same syntax as `.gitignore`.
|
|
142
|
+
|
|
143
|
+
**Example `.cgcignore` file:**
|
|
144
|
+
```
|
|
145
|
+
# Ignore build artifacts
|
|
146
|
+
/build/
|
|
147
|
+
/dist/
|
|
148
|
+
|
|
149
|
+
# Ignore dependencies
|
|
150
|
+
/node_modules/
|
|
151
|
+
/vendor/
|
|
152
|
+
|
|
153
|
+
# Ignore logs
|
|
154
|
+
*.log
|
|
155
|
+
```
|
|
156
|
+
|
|
134
157
|
## MCP Client Configuration
|
|
135
158
|
|
|
136
159
|
The `cgc setup` command attempts to automatically configure your IDE/CLI. If you choose not to use the automatic setup, or if your tool is not supported, you can configure it manually.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codegraphcontext"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.18"
|
|
4
4
|
description = "An MCP server that indexes local code into a graph database to provide context to AI assistants."
|
|
5
5
|
authors = [{ name = "Shashank Shekhar Singh", email = "shashankshekharsingh1205@gmail.com" }]
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = { file = "LICENSE" }
|
|
8
|
-
requires-python = ">=3.
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
9
|
classifiers = [
|
|
10
10
|
"Programming Language :: Python :: 3",
|
|
11
11
|
"License :: OSI Approved :: MIT License",
|
|
@@ -28,7 +28,9 @@ dependencies = [
|
|
|
28
28
|
"pyyaml",
|
|
29
29
|
"pytest",
|
|
30
30
|
"nbformat",
|
|
31
|
-
"nbconvert>=7.16.6"
|
|
31
|
+
"nbconvert>=7.16.6",
|
|
32
|
+
"pathspec>=0.12.1",
|
|
33
|
+
"falkordblite>=0.1.0"
|
|
32
34
|
]
|
|
33
35
|
|
|
34
36
|
[project.urls]
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# src/codegraphcontext/cli/cli_helpers.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import json
|
|
4
|
+
import urllib.parse
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import time
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from ..core import get_database_manager
|
|
11
|
+
from ..core.jobs import JobManager
|
|
12
|
+
from ..tools.code_finder import CodeFinder
|
|
13
|
+
from ..tools.graph_builder import GraphBuilder
|
|
14
|
+
from ..tools.package_resolver import get_local_package_path
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _initialize_services():
|
|
20
|
+
"""Initializes and returns core service managers."""
|
|
21
|
+
console.print("[dim]Initializing services and database connection...[/dim]")
|
|
22
|
+
try:
|
|
23
|
+
db_manager = get_database_manager()
|
|
24
|
+
except ValueError as e:
|
|
25
|
+
console.print(f"[bold red]Database Configuration Error:[/bold red] {e}")
|
|
26
|
+
return None, None, None
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
db_manager.get_driver()
|
|
30
|
+
except ValueError as e:
|
|
31
|
+
console.print(f"[bold red]Database Connection Error:[/bold red] {e}")
|
|
32
|
+
console.print("Please ensure your Neo4j credentials are correct and the database is running.")
|
|
33
|
+
return None, None, None
|
|
34
|
+
|
|
35
|
+
# The GraphBuilder requires an event loop, even for synchronous-style execution
|
|
36
|
+
try:
|
|
37
|
+
loop = asyncio.get_running_loop()
|
|
38
|
+
except RuntimeError:
|
|
39
|
+
loop = asyncio.new_event_loop()
|
|
40
|
+
asyncio.set_event_loop(loop)
|
|
41
|
+
|
|
42
|
+
graph_builder = GraphBuilder(db_manager, JobManager(), loop)
|
|
43
|
+
code_finder = CodeFinder(db_manager)
|
|
44
|
+
console.print("[dim]Services initialized.[/dim]")
|
|
45
|
+
return db_manager, graph_builder, code_finder
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def index_helper(path: str):
|
|
49
|
+
"""Synchronously indexes a repository."""
|
|
50
|
+
time_start = time.time()
|
|
51
|
+
services = _initialize_services()
|
|
52
|
+
if not all(services):
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
db_manager, graph_builder, code_finder = services
|
|
56
|
+
path_obj = Path(path).resolve()
|
|
57
|
+
|
|
58
|
+
if not path_obj.exists():
|
|
59
|
+
console.print(f"[red]Error: Path does not exist: {path_obj}[/red]")
|
|
60
|
+
db_manager.close_driver()
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
indexed_repos = code_finder.list_indexed_repositories()
|
|
64
|
+
if any(Path(repo["path"]).resolve() == path_obj for repo in indexed_repos):
|
|
65
|
+
console.print(f"[yellow]Repository '{path}' is already indexed. Skipping.[/yellow]")
|
|
66
|
+
db_manager.close_driver()
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
console.print(f"Starting indexing for: {path_obj}")
|
|
70
|
+
console.print("[yellow]This may take a few minutes for large repositories...[/yellow]")
|
|
71
|
+
|
|
72
|
+
async def do_index():
|
|
73
|
+
await graph_builder.build_graph_from_path_async(path_obj, is_dependency=False)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
asyncio.run(do_index())
|
|
77
|
+
time_end = time.time()
|
|
78
|
+
elapsed = time_end - time_start
|
|
79
|
+
console.print(f"[green]Successfully finished indexing: {path} in {elapsed:.2f} seconds[/green]")
|
|
80
|
+
except Exception as e:
|
|
81
|
+
console.print(f"[bold red]An error occurred during indexing:[/bold red] {e}")
|
|
82
|
+
finally:
|
|
83
|
+
db_manager.close_driver()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def add_package_helper(package_name: str, language: str):
|
|
87
|
+
"""Synchronously indexes a package."""
|
|
88
|
+
services = _initialize_services()
|
|
89
|
+
if not all(services):
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
db_manager, graph_builder, code_finder = services
|
|
93
|
+
|
|
94
|
+
package_path_str = get_local_package_path(package_name, language)
|
|
95
|
+
if not package_path_str:
|
|
96
|
+
console.print(f"[red]Error: Could not find package '{package_name}' for language '{language}'.[/red]")
|
|
97
|
+
db_manager.close_driver()
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
package_path = Path(package_path_str)
|
|
101
|
+
|
|
102
|
+
indexed_repos = code_finder.list_indexed_repositories()
|
|
103
|
+
if any(repo.get("name") == package_name for repo in indexed_repos if repo.get("is_dependency")):
|
|
104
|
+
console.print(f"[yellow]Package '{package_name}' is already indexed. Skipping.[/yellow]")
|
|
105
|
+
db_manager.close_driver()
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
console.print(f"Starting indexing for package '{package_name}' at: {package_path}")
|
|
109
|
+
console.print("[yellow]This may take a few minutes...[/yellow]")
|
|
110
|
+
|
|
111
|
+
async def do_index():
|
|
112
|
+
await graph_builder.build_graph_from_path_async(package_path, is_dependency=True)
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
asyncio.run(do_index())
|
|
116
|
+
console.print(f"[green]Successfully finished indexing package: {package_name}[/green]")
|
|
117
|
+
except Exception as e:
|
|
118
|
+
console.print(f"[bold red]An error occurred during package indexing:[/bold red] {e}")
|
|
119
|
+
finally:
|
|
120
|
+
db_manager.close_driver()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def list_repos_helper():
|
|
124
|
+
"""Lists all indexed repositories."""
|
|
125
|
+
services = _initialize_services()
|
|
126
|
+
if not all(services):
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
db_manager, _, code_finder = services
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
repos = code_finder.list_indexed_repositories()
|
|
133
|
+
if not repos:
|
|
134
|
+
console.print("[yellow]No repositories indexed yet.[/yellow]")
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
138
|
+
table.add_column("Name", style="dim")
|
|
139
|
+
table.add_column("Path")
|
|
140
|
+
table.add_column("Type")
|
|
141
|
+
|
|
142
|
+
for repo in repos:
|
|
143
|
+
repo_type = "Dependency" if repo.get("is_dependency") else "Project"
|
|
144
|
+
table.add_row(repo["name"], repo["path"], repo_type)
|
|
145
|
+
|
|
146
|
+
console.print(table)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
console.print(f"[bold red]An error occurred:[/bold red] {e}")
|
|
149
|
+
finally:
|
|
150
|
+
db_manager.close_driver()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def delete_helper(repo_path: str):
|
|
154
|
+
"""Deletes a repository from the graph."""
|
|
155
|
+
services = _initialize_services()
|
|
156
|
+
if not all(services):
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
db_manager, graph_builder, _ = services
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
graph_builder.delete_repository_from_graph(repo_path)
|
|
163
|
+
console.print(f"[green]Successfully deleted repository: {repo_path}[/green]")
|
|
164
|
+
except Exception as e:
|
|
165
|
+
console.print(f"[bold red]An error occurred:[/bold red] {e}")
|
|
166
|
+
finally:
|
|
167
|
+
db_manager.close_driver()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def cypher_helper(query: str):
|
|
171
|
+
"""Executes a read-only Cypher query."""
|
|
172
|
+
services = _initialize_services()
|
|
173
|
+
if not all(services):
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
db_manager, _, _ = services
|
|
177
|
+
|
|
178
|
+
# Replicating safety checks from MCPServer
|
|
179
|
+
forbidden_keywords = ['CREATE', 'MERGE', 'DELETE', 'SET', 'REMOVE', 'DROP', 'CALL apoc']
|
|
180
|
+
if any(keyword in query.upper() for keyword in forbidden_keywords):
|
|
181
|
+
console.print("[bold red]Error: This command only supports read-only queries.[/bold red]")
|
|
182
|
+
db_manager.close_driver()
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
with db_manager.get_driver().session() as session:
|
|
187
|
+
result = session.run(query)
|
|
188
|
+
records = [record.data() for record in result]
|
|
189
|
+
console.print(json.dumps(records, indent=2))
|
|
190
|
+
except Exception as e:
|
|
191
|
+
console.print(f"[bold red]An error occurred while executing query:[/bold red] {e}")
|
|
192
|
+
finally:
|
|
193
|
+
db_manager.close_driver()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
import webbrowser
|
|
197
|
+
|
|
198
|
+
def visualize_helper(query: str):
|
|
199
|
+
"""Generates a visualization."""
|
|
200
|
+
services = _initialize_services()
|
|
201
|
+
if not all(services):
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
db_manager, _, _ = services
|
|
205
|
+
|
|
206
|
+
# Check if FalkorDB
|
|
207
|
+
if "FalkorDB" in db_manager.__class__.__name__:
|
|
208
|
+
_visualize_falkordb(db_manager)
|
|
209
|
+
else:
|
|
210
|
+
try:
|
|
211
|
+
encoded_query = urllib.parse.quote(query)
|
|
212
|
+
visualization_url = f"http://localhost:7474/browser/?cmd=edit&arg={encoded_query}"
|
|
213
|
+
console.print("[green]Graph visualization URL:[/green]")
|
|
214
|
+
console.print(visualization_url)
|
|
215
|
+
console.print("Open the URL in your browser to see the graph.")
|
|
216
|
+
except Exception as e:
|
|
217
|
+
console.print(f"[bold red]An error occurred while generating URL:[/bold red] {e}")
|
|
218
|
+
finally:
|
|
219
|
+
db_manager.close_driver()
|
|
220
|
+
|
|
221
|
+
def _visualize_falkordb(db_manager):
|
|
222
|
+
console.print("[dim]Generating FalkorDB visualization (showing up to 500 relationships)...[/dim]")
|
|
223
|
+
try:
|
|
224
|
+
data_nodes = []
|
|
225
|
+
data_edges = []
|
|
226
|
+
|
|
227
|
+
with db_manager.get_driver().session() as session:
|
|
228
|
+
# Fetch nodes and edges
|
|
229
|
+
q = "MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 500"
|
|
230
|
+
result = session.run(q)
|
|
231
|
+
|
|
232
|
+
seen_nodes = set()
|
|
233
|
+
|
|
234
|
+
for record in result:
|
|
235
|
+
# record values are Node/Relationship objects from falkordb client
|
|
236
|
+
n = record['n']
|
|
237
|
+
r = record['r']
|
|
238
|
+
m = record['m']
|
|
239
|
+
|
|
240
|
+
# Process Node helper
|
|
241
|
+
def process_node(node):
|
|
242
|
+
nid = getattr(node, 'id', -1)
|
|
243
|
+
labels = getattr(node, 'labels', [])
|
|
244
|
+
lbl = list(labels)[0] if labels else "Node"
|
|
245
|
+
props = getattr(node, 'properties', {})
|
|
246
|
+
name = props.get('name', str(nid))
|
|
247
|
+
|
|
248
|
+
if nid not in seen_nodes:
|
|
249
|
+
seen_nodes.add(nid)
|
|
250
|
+
color = "#97c2fc" # Default blue
|
|
251
|
+
if "Repository" in labels: color = "#ffb3ba" # Red
|
|
252
|
+
elif "File" in labels: color = "#baffc9" # Green
|
|
253
|
+
elif "Class" in labels: color = "#bae1ff" # Light Blue
|
|
254
|
+
elif "Function" in labels: color = "#ffffba" # Yellow
|
|
255
|
+
elif "Package" in labels: color = "#ffdfba" # Orange
|
|
256
|
+
|
|
257
|
+
data_nodes.append({
|
|
258
|
+
"id": nid,
|
|
259
|
+
"label": name,
|
|
260
|
+
"group": lbl,
|
|
261
|
+
"title": str(props),
|
|
262
|
+
"color": color
|
|
263
|
+
})
|
|
264
|
+
return nid
|
|
265
|
+
|
|
266
|
+
nid = process_node(n)
|
|
267
|
+
mid = process_node(m)
|
|
268
|
+
|
|
269
|
+
# Check Edge
|
|
270
|
+
e_type = getattr(r, 'relation', '') or getattr(r, 'type', 'REL')
|
|
271
|
+
data_edges.append({
|
|
272
|
+
"from": nid,
|
|
273
|
+
"to": mid,
|
|
274
|
+
"label": e_type,
|
|
275
|
+
"arrows": "to"
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
filename = "codegraph_viz.html"
|
|
279
|
+
html_content = f"""
|
|
280
|
+
<!DOCTYPE html>
|
|
281
|
+
<html>
|
|
282
|
+
<head>
|
|
283
|
+
<title>CodeGraphContext Visualization</title>
|
|
284
|
+
<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
|
|
285
|
+
<style type="text/css">
|
|
286
|
+
#mynetwork {{
|
|
287
|
+
width: 100%;
|
|
288
|
+
height: 100vh;
|
|
289
|
+
border: 1px solid lightgray;
|
|
290
|
+
}}
|
|
291
|
+
</style>
|
|
292
|
+
</head>
|
|
293
|
+
<body>
|
|
294
|
+
<div id="mynetwork"></div>
|
|
295
|
+
<script type="text/javascript">
|
|
296
|
+
var nodes = new vis.DataSet({json.dumps(data_nodes)});
|
|
297
|
+
var edges = new vis.DataSet({json.dumps(data_edges)});
|
|
298
|
+
var container = document.getElementById('mynetwork');
|
|
299
|
+
var data = {{ nodes: nodes, edges: edges }};
|
|
300
|
+
var options = {{
|
|
301
|
+
nodes: {{ shape: 'dot', size: 16 }},
|
|
302
|
+
physics: {{ stabilization: false }},
|
|
303
|
+
layout: {{ improvedLayout: false }}
|
|
304
|
+
}};
|
|
305
|
+
var network = new vis.Network(container, data, options);
|
|
306
|
+
</script>
|
|
307
|
+
</body>
|
|
308
|
+
</html>
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
out_path = Path(filename).resolve()
|
|
312
|
+
with open(out_path, "w") as f:
|
|
313
|
+
f.write(html_content)
|
|
314
|
+
|
|
315
|
+
console.print(f"[green]Visualization generated at:[/green] {out_path}")
|
|
316
|
+
console.print("Opening in default browser...")
|
|
317
|
+
webbrowser.open(f"file://{out_path}")
|
|
318
|
+
|
|
319
|
+
except Exception as e:
|
|
320
|
+
console.print(f"[bold red]Visualization failed:[/bold red] {e}")
|
|
321
|
+
import traceback
|
|
322
|
+
traceback.print_exc()
|
|
323
|
+
finally:
|
|
324
|
+
db_manager.close_driver()
|
|
325
|
+
|
|
326
|
+
|