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.
Files changed (63) hide show
  1. {codegraphcontext-0.1.16/src/codegraphcontext.egg-info → codegraphcontext-0.1.18}/PKG-INFO +28 -3
  2. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/README.md +24 -1
  3. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/pyproject.toml +5 -3
  4. codegraphcontext-0.1.18/src/codegraphcontext/cli/cli_helpers.py +326 -0
  5. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/main.py +74 -56
  6. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/setup_wizard.py +12 -4
  7. codegraphcontext-0.1.18/src/codegraphcontext/core/__init__.py +92 -0
  8. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/core/database.py +6 -8
  9. codegraphcontext-0.1.18/src/codegraphcontext/core/database_falkordb.py +375 -0
  10. codegraphcontext-0.1.18/src/codegraphcontext/core/falkor_worker.py +68 -0
  11. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/core/watcher.py +14 -16
  12. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/server.py +141 -32
  13. codegraphcontext-0.1.18/src/codegraphcontext/tools/advanced_language_query_tool.py +99 -0
  14. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/code_finder.py +53 -44
  15. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/graph_builder.py +140 -27
  16. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/c.py +2 -5
  17. codegraphcontext-0.1.18/src/codegraphcontext/tools/languages/cpp.py +456 -0
  18. codegraphcontext-0.1.18/src/codegraphcontext/tools/languages/csharp.py +531 -0
  19. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/go.py +2 -3
  20. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/java.py +43 -11
  21. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/javascript.py +27 -6
  22. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/python.py +5 -6
  23. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/ruby.py +73 -8
  24. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/rust.py +30 -12
  25. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/languages/typescript.py +3 -5
  26. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
  27. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
  28. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
  29. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
  30. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
  31. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
  32. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
  33. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
  34. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
  35. codegraphcontext-0.1.18/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
  36. codegraphcontext-0.1.18/src/codegraphcontext/utils/debug_log.py +39 -0
  37. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18/src/codegraphcontext.egg-info}/PKG-INFO +28 -3
  38. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/SOURCES.txt +15 -0
  39. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/requires.txt +2 -0
  40. codegraphcontext-0.1.16/src/codegraphcontext/core/__init__.py +0 -1
  41. codegraphcontext-0.1.16/src/codegraphcontext/tools/languages/cpp.py +0 -253
  42. codegraphcontext-0.1.16/src/codegraphcontext/utils/debug_log.py +0 -10
  43. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/LICENSE +0 -0
  44. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/MANIFEST.in +0 -0
  45. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/setup.cfg +0 -0
  46. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/__init__.py +0 -0
  47. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/__main__.py +0 -0
  48. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/__init__.py +0 -0
  49. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/cli/setup_macos.py +0 -0
  50. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/core/jobs.py +0 -0
  51. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/prompts.py +0 -0
  52. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/__init__.py +0 -0
  53. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/package_resolver.py +0 -0
  54. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext/tools/system.py +0 -0
  55. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
  56. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
  57. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/src/codegraphcontext.egg-info/top_level.txt +0 -0
  58. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_cpp_parser.py +0 -0
  59. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_database_validation.py +0 -0
  60. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_end_to_end.py +0 -0
  61. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_graph_indexing.py +0 -0
  62. {codegraphcontext-0.1.16 → codegraphcontext-0.1.18}/tests/test_graph_indexing_js.py +0 -0
  63. {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.16
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.9
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
  ![Using the MCP server](https://github.com/Shashankss1205/CodeGraphContext/blob/main/images/Usecase.gif)
88
90
 
89
91
  ## Project Details
90
- - **Version:** 0.1.16
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
  ![Using the MCP server](https://github.com/Shashankss1205/CodeGraphContext/blob/main/images/Usecase.gif)
30
30
 
31
31
  ## Project Details
32
- - **Version:** 0.1.16
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.16"
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.9"
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
+