codegraphcontext 0.3.3__tar.gz → 0.3.4__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 (87) hide show
  1. {codegraphcontext-0.3.3/src/codegraphcontext.egg-info → codegraphcontext-0.3.4}/PKG-INFO +4 -2
  2. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/README.md +1 -1
  3. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/pyproject.toml +3 -1
  4. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/cli_helpers.py +28 -4
  5. codegraphcontext-0.3.4/src/codegraphcontext/viz/server.py +230 -0
  6. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4/src/codegraphcontext.egg-info}/PKG-INFO +4 -2
  7. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext.egg-info/requires.txt +2 -0
  8. codegraphcontext-0.3.3/src/codegraphcontext/viz/server.py +0 -179
  9. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/LICENSE +0 -0
  10. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/MANIFEST.in +0 -0
  11. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/setup.cfg +0 -0
  12. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/__init__.py +0 -0
  13. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/__main__.py +0 -0
  14. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/__init__.py +0 -0
  15. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/config_manager.py +0 -0
  16. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/main.py +0 -0
  17. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/registry_commands.py +0 -0
  18. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/setup_macos.py +0 -0
  19. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/setup_wizard.py +0 -0
  20. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/cli/visualizer.py +0 -0
  21. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/__init__.py +0 -0
  22. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/bundle_registry.py +0 -0
  23. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/cgc_bundle.py +0 -0
  24. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/database.py +0 -0
  25. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/database_falkordb.py +0 -0
  26. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
  27. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/database_kuzu.py +0 -0
  28. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/falkor_worker.py +0 -0
  29. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/jobs.py +0 -0
  30. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/core/watcher.py +0 -0
  31. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/prompts.py +0 -0
  32. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/server.py +0 -0
  33. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tool_definitions.py +0 -0
  34. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/__init__.py +0 -0
  35. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
  36. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/code_finder.py +0 -0
  37. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/graph_builder.py +0 -0
  38. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
  39. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
  40. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
  41. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
  42. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
  43. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/c.py +0 -0
  44. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/cpp.py +0 -0
  45. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/csharp.py +0 -0
  46. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/dart.py +0 -0
  47. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/elixir.py +0 -0
  48. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/go.py +0 -0
  49. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/haskell.py +0 -0
  50. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/java.py +0 -0
  51. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/javascript.py +0 -0
  52. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
  53. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/perl.py +0 -0
  54. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/php.py +0 -0
  55. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/python.py +0 -0
  56. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/ruby.py +0 -0
  57. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/rust.py +0 -0
  58. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/scala.py +0 -0
  59. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/swift.py +0 -0
  60. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/typescript.py +0 -0
  61. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
  62. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/package_resolver.py +0 -0
  63. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
  64. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
  65. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
  66. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
  67. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
  68. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
  69. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
  70. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
  71. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
  72. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
  73. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
  74. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
  75. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
  76. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
  77. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
  78. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/scip_indexer.py +0 -0
  79. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/scip_pb2.py +0 -0
  80. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/tools/system.py +0 -0
  81. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/utils/debug_log.py +0 -0
  82. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
  83. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext/utils/visualize_graph.py +0 -0
  84. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext.egg-info/SOURCES.txt +0 -0
  85. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
  86. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
  87. {codegraphcontext-0.3.3 → codegraphcontext-0.3.4}/src/codegraphcontext.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.3.3
3
+ Version: 0.3.4
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
@@ -45,6 +45,7 @@ Requires-Dist: inquirerpy>=0.3.4
45
45
  Requires-Dist: python-dotenv>=1.0.0
46
46
  Requires-Dist: tree-sitter>=0.21.0
47
47
  Requires-Dist: tree-sitter-language-pack>=0.6.0
48
+ Requires-Dist: tree-sitter-c-sharp>=0.21.0
48
49
  Requires-Dist: pyyaml
49
50
  Requires-Dist: nbformat
50
51
  Requires-Dist: nbconvert>=7.16.6
@@ -56,6 +57,7 @@ Requires-Dist: uvicorn>=0.22.0
56
57
  Provides-Extra: parsing
57
58
  Requires-Dist: tree-sitter>=0.21.0; extra == "parsing"
58
59
  Requires-Dist: tree-sitter-language-pack>=0.6.0; extra == "parsing"
60
+ Requires-Dist: tree-sitter-c-sharp>=0.21.0; extra == "parsing"
59
61
  Provides-Extra: dev
60
62
  Requires-Dist: pytest>=7.4.0; extra == "dev"
61
63
  Requires-Dist: black>=23.11.0; extra == "dev"
@@ -160,7 +162,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
160
162
  ---
161
163
 
162
164
  ## Project Details
163
- - **Version:** 0.3.3
165
+ - **Version:** 0.3.4
164
166
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
165
167
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
166
168
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -96,7 +96,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
96
96
  ---
97
97
 
98
98
  ## Project Details
99
- - **Version:** 0.3.3
99
+ - **Version:** 0.3.4
100
100
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
101
101
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
102
102
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codegraphcontext"
3
- version = "0.3.3"
3
+ version = "0.3.4"
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"
@@ -24,6 +24,7 @@ dependencies = [
24
24
  "python-dotenv>=1.0.0",
25
25
  "tree-sitter>=0.21.0",
26
26
  "tree-sitter-language-pack>=0.6.0",
27
+ "tree-sitter-c-sharp>=0.21.0",
27
28
  "pyyaml",
28
29
  "nbformat",
29
30
  "nbconvert>=7.16.6",
@@ -38,6 +39,7 @@ dependencies = [
38
39
  parsing = [
39
40
  "tree-sitter>=0.21.0",
40
41
  "tree-sitter-language-pack>=0.6.0",
42
+ "tree-sitter-c-sharp>=0.21.0",
41
43
  ]
42
44
  dev = [
43
45
  "pytest>=7.4.0",
@@ -359,16 +359,40 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
359
359
 
360
360
  # Determine the static directory (built React app)
361
361
  # This points to src/codegraphcontext/viz/dist where we build the website
362
- static_dir = Path(__file__).parent.parent / "viz" / "dist"
362
+ # (relative to src/codegraphcontext/cli/cli_helpers.py)
363
+ # Using .resolve() is more robust for path comparison and existence checks
364
+ this_file = Path(__file__).resolve()
365
+ package_root = this_file.parent.parent
366
+ static_dir = package_root / "viz" / "dist"
363
367
 
364
368
  # Fallback for development if not yet built in viz/dist
365
369
  if not static_dir.exists():
366
- dev_static_dir = Path.cwd() / "website" / "dist"
370
+ # Look for website/dist in the project root (3 levels up from cli/cli_helpers.py, 4 parents)
371
+ # 1: cli/, 2: codegraphcontext/, 3: src/, 4: project_root/
372
+ project_root = this_file.parent.parent.parent.parent
373
+ dev_static_dir = project_root / "website" / "dist"
374
+
375
+ # Also try one level up from package_root just in case of different layouts
376
+ alt_dev_dir = package_root.parent.parent / "website" / "dist"
377
+
367
378
  if dev_static_dir.exists():
368
379
  static_dir = dev_static_dir
380
+ elif alt_dev_dir.exists():
381
+ static_dir = alt_dev_dir
369
382
  else:
370
- console.print("[yellow]Warning: Visualization assets not found. Please run 'cd website && npm run build' first.[/yellow]")
371
- # We continue anyway to let the server start (helpful for dev)
383
+ # Last resort: try current working directory
384
+ cwd_static_dir = Path.cwd() / "website" / "dist"
385
+ if cwd_static_dir.exists():
386
+ static_dir = cwd_static_dir
387
+ else:
388
+ console.print(f"[yellow]Warning: Visualization assets not found.[/yellow]")
389
+ console.print(f"[dim]Checked paths:[/dim]")
390
+ console.print(f" [dim]- {static_dir}[/dim]")
391
+ console.print(f" [dim]- {dev_static_dir}[/dim]")
392
+ console.print(f" [dim]- {alt_dev_dir}[/dim]")
393
+ console.print(f" [dim]- {cwd_static_dir}[/dim]")
394
+ console.print("[dim]Please run 'cd website && npm run build' first.[/dim]")
395
+ # We continue anyway to let the server start (helpful for dev)
372
396
 
373
397
  # Construct the URL
374
398
  backend_url = f"http://localhost:{port}"
@@ -0,0 +1,230 @@
1
+ from fastapi import FastAPI, HTTPException, Query, Request
2
+ from fastapi.staticfiles import StaticFiles
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.responses import HTMLResponse, FileResponse
5
+ from pathlib import Path
6
+ import uvicorn
7
+ import json
8
+ import os
9
+ import sys
10
+ from typing import Optional, List, Dict, Any
11
+
12
+ from ..core.database import DatabaseManager
13
+ from ..utils.debug_log import debug_log
14
+
15
+ app = FastAPI()
16
+
17
+ # Enable CORS for development
18
+ app.add_middleware(
19
+ CORSMiddleware,
20
+ allow_origins=["*"],
21
+ allow_methods=["*"],
22
+ allow_headers=["*"],
23
+ )
24
+
25
+ # Global database manager (will be initialized when server starts)
26
+ db_manager: Optional[DatabaseManager] = None
27
+ # Path to static directory
28
+ _static_dir: Optional[str] = None
29
+
30
+ def set_db_manager(manager: DatabaseManager):
31
+ global db_manager
32
+ db_manager = manager
33
+
34
+ @app.get("/api/graph")
35
+ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str] = None):
36
+ if not db_manager:
37
+ raise HTTPException(status_code=500, detail="Database not initialized")
38
+
39
+ def get_eid(element):
40
+ if not element: return None
41
+ if isinstance(element, (int, str)):
42
+ return str(element)
43
+ # Try various ways to get ID (Neo4j, FalkorDB, etc.)
44
+ for attr in ['element_id', 'id', '_id']:
45
+ if hasattr(element, attr):
46
+ val = getattr(element, attr)
47
+ if val is not None: return str(val)
48
+ return str(id(element))
49
+
50
+ try:
51
+ nodes_dict = {}
52
+ edges = []
53
+
54
+ print(f"DEBUG: Starting get_graph with repo_path={repo_path}", flush=True)
55
+ nodes_dict = {}
56
+ edges = []
57
+
58
+ with db_manager.get_driver().session() as session:
59
+ if cypher_query:
60
+ print(f"DEBUG: Executing custom query: {cypher_query}", flush=True)
61
+ result = session.run(cypher_query)
62
+ elif repo_path:
63
+ repo_path = str(Path(repo_path).resolve())
64
+ print(f"DEBUG: Fetching subgraph for: {repo_path}", flush=True)
65
+ query = """
66
+ MATCH (r:Repository {path: $repo_path})
67
+ OPTIONAL MATCH (r)-[:CONTAINS*0..]->(n)
68
+ WITH DISTINCT n
69
+ WHERE n IS NOT NULL
70
+ OPTIONAL MATCH (n)-[rel]->(m)
71
+ RETURN n, rel, m
72
+ """
73
+ result = session.run(query, repo_path=repo_path)
74
+ else:
75
+ print("DEBUG: Fetching global graph", flush=True)
76
+ query = "MATCH (n) OPTIONAL MATCH (n)-[rel]->(m) RETURN n, rel, m LIMIT 5000"
77
+ result = session.run(query)
78
+
79
+ record_count = 0
80
+ for record in result:
81
+ record_count += 1
82
+ # Use .get() to avoid KeyError if the query doesn't return all fields (n, rel, m)
83
+ for key in ['n', 'm']:
84
+ try:
85
+ node = record.get(key)
86
+ if node:
87
+ eid = get_eid(node)
88
+ if eid and eid not in nodes_dict:
89
+ # Extract labels
90
+ labels = []
91
+ for label_attr in ['_labels', 'labels']:
92
+ if hasattr(node, label_attr):
93
+ attr_val = getattr(node, label_attr)
94
+ if attr_val:
95
+ labels = list(attr_val)
96
+ break
97
+
98
+ # Extract properties
99
+ props = {}
100
+ for prop_attr in ['properties', '_properties']:
101
+ if hasattr(node, prop_attr):
102
+ attr_val = getattr(node, prop_attr)
103
+ if attr_val:
104
+ props = dict(attr_val)
105
+ break
106
+
107
+ # Fallback if props still empty but node acts like dict
108
+ if not props and hasattr(node, 'items'):
109
+ try:
110
+ props = dict(node.items())
111
+ except: pass
112
+
113
+ # Extract name/label for frontend
114
+ # Prefer 'name' property, fallback to 'label', then 'path' or 'Unknown'
115
+ display_name = str(props.get('name', props.get('label', props.get('path', 'Unknown'))))
116
+
117
+ nodes_dict[eid] = {
118
+ "id": eid,
119
+ "name": display_name,
120
+ "label": display_name,
121
+ "type": str(labels[0]).capitalize() if labels else "Other",
122
+ "file": str(props.get('path', props.get('file', ''))),
123
+ "val": 4 if (labels and labels[0] in ['Repository', 'Class', 'Interface', 'Trait']) else 2,
124
+ "properties": props
125
+ }
126
+ except Exception as e:
127
+ print(f"DEBUG: Error parsing node: {e}", file=sys.stderr, flush=True)
128
+ continue
129
+
130
+ try:
131
+ rel = record.get('rel')
132
+ if rel:
133
+ rid = get_eid(rel)
134
+
135
+ # Try various ways to get start/end nodes
136
+ start_node = None
137
+ end_node = None
138
+ for src_attr in ['start_node', 'src_node', '_src_node']:
139
+ if hasattr(rel, src_attr):
140
+ start_node = getattr(rel, src_attr)
141
+ break
142
+ for dest_attr in ['end_node', 'dest_node', '_dest_node']:
143
+ if hasattr(rel, dest_attr):
144
+ end_node = getattr(rel, dest_attr)
145
+ break
146
+
147
+ source = get_eid(start_node) if start_node is not None else None
148
+ target = get_eid(end_node) if end_node is not None else None
149
+
150
+ if source and target:
151
+ # Extract relationship type
152
+ rel_type = "RELATED"
153
+ for rel_attr in ['type', 'relation', '_relation']:
154
+ if hasattr(rel, rel_attr):
155
+ rel_type = getattr(rel, rel_attr)
156
+ break
157
+
158
+ edges.append({
159
+ "id": rid,
160
+ "source": source,
161
+ "target": target,
162
+ "type": str(rel_type).upper()
163
+ })
164
+ except Exception as e:
165
+ print(f"DEBUG: Error parsing relationship: {e}", file=sys.stderr, flush=True)
166
+ pass
167
+
168
+ print(f"DEBUG: Processed {record_count} records. extracted {len(nodes_dict)} nodes and {len(edges)} edges.", file=sys.stderr, flush=True)
169
+
170
+ # Build a list of unique file paths from File-type nodes for the tree
171
+ file_paths = []
172
+ for n in nodes_dict.values():
173
+ if n.get("file") and str(n.get("type", "")).lower() == "file":
174
+ file_paths.append(str(n["file"]))
175
+ file_paths = sorted(list(set(file_paths)))
176
+
177
+ response_data = {
178
+ "nodes": list(nodes_dict.values()),
179
+ "links": edges,
180
+ "files": file_paths,
181
+ }
182
+
183
+ print(f"API SUCCESS: Returning graph with {len(response_data['nodes'])} nodes and {len(response_data['links'])} links.", file=sys.stderr, flush=True)
184
+ return response_data
185
+
186
+ except Exception as e:
187
+ debug_log(f"Error fetching graph: {str(e)}")
188
+ import traceback
189
+ traceback.print_exc()
190
+ # Still return a valid structure so the frontend doesn't crash, but with 500 status if raised
191
+ # Actually, let's just return a 500 error but with JSON body if possible
192
+ raise HTTPException(status_code=500, detail=str(e))
193
+
194
+ @app.get("/api/file")
195
+ async def get_file(path: str):
196
+ file_path = Path(path)
197
+ if not file_path.exists():
198
+ raise HTTPException(status_code=404, detail="File not found")
199
+
200
+ try:
201
+ with open(file_path, "r", encoding="utf-8") as f:
202
+ return {"content": f.read()}
203
+ except Exception as e:
204
+ raise HTTPException(status_code=500, detail=str(e))
205
+
206
+ # SPA fallback handler
207
+ @app.get("/{full_path:path}")
208
+ async def spa_fallback(request: Request, full_path: str):
209
+ global _static_dir
210
+ if not _static_dir:
211
+ return HTMLResponse("Static directory not configured", status_code=500)
212
+
213
+ # Filesystem path
214
+ file_path = Path(_static_dir) / full_path
215
+
216
+ # If the file exists and is a file, serve it normally
217
+ if file_path.exists() and file_path.is_file():
218
+ return FileResponse(file_path)
219
+
220
+ # Otherwise serve index.html (Standard SPA routing)
221
+ index_path = Path(_static_dir) / "index.html"
222
+ if index_path.exists():
223
+ return FileResponse(index_path)
224
+
225
+ return HTMLResponse("Not Found (Built UI not found in viz/dist)", status_code=404)
226
+
227
+ def run_server(host: str = "127.0.0.1", port: int = 8000, static_dir: Optional[str] = None):
228
+ global _static_dir
229
+ _static_dir = static_dir
230
+ uvicorn.run(app, host=host, port=port)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.3.3
3
+ Version: 0.3.4
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
@@ -45,6 +45,7 @@ Requires-Dist: inquirerpy>=0.3.4
45
45
  Requires-Dist: python-dotenv>=1.0.0
46
46
  Requires-Dist: tree-sitter>=0.21.0
47
47
  Requires-Dist: tree-sitter-language-pack>=0.6.0
48
+ Requires-Dist: tree-sitter-c-sharp>=0.21.0
48
49
  Requires-Dist: pyyaml
49
50
  Requires-Dist: nbformat
50
51
  Requires-Dist: nbconvert>=7.16.6
@@ -56,6 +57,7 @@ Requires-Dist: uvicorn>=0.22.0
56
57
  Provides-Extra: parsing
57
58
  Requires-Dist: tree-sitter>=0.21.0; extra == "parsing"
58
59
  Requires-Dist: tree-sitter-language-pack>=0.6.0; extra == "parsing"
60
+ Requires-Dist: tree-sitter-c-sharp>=0.21.0; extra == "parsing"
59
61
  Provides-Extra: dev
60
62
  Requires-Dist: pytest>=7.4.0; extra == "dev"
61
63
  Requires-Dist: black>=23.11.0; extra == "dev"
@@ -160,7 +162,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
160
162
  ---
161
163
 
162
164
  ## Project Details
163
- - **Version:** 0.3.3
165
+ - **Version:** 0.3.4
164
166
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
165
167
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
166
168
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -7,6 +7,7 @@ inquirerpy>=0.3.4
7
7
  python-dotenv>=1.0.0
8
8
  tree-sitter>=0.21.0
9
9
  tree-sitter-language-pack>=0.6.0
10
+ tree-sitter-c-sharp>=0.21.0
10
11
  pyyaml
11
12
  nbformat
12
13
  nbconvert>=7.16.6
@@ -26,3 +27,4 @@ pytest-asyncio>=0.21.0
26
27
  [parsing]
27
28
  tree-sitter>=0.21.0
28
29
  tree-sitter-language-pack>=0.6.0
30
+ tree-sitter-c-sharp>=0.21.0
@@ -1,179 +0,0 @@
1
- from fastapi import FastAPI, HTTPException, Query, Request
2
- from fastapi.staticfiles import StaticFiles
3
- from fastapi.middleware.cors import CORSMiddleware
4
- from fastapi.responses import HTMLResponse, FileResponse
5
- from pathlib import Path
6
- import uvicorn
7
- import json
8
- import os
9
- from typing import Optional, List, Dict, Any
10
-
11
- from ..core.database import DatabaseManager
12
- from ..utils.debug_log import debug_log
13
-
14
- app = FastAPI()
15
-
16
- # Enable CORS for development
17
- app.add_middleware(
18
- CORSMiddleware,
19
- allow_origins=["*"],
20
- allow_methods=["*"],
21
- allow_headers=["*"],
22
- )
23
-
24
- # Global database manager (will be initialized when server starts)
25
- db_manager: Optional[DatabaseManager] = None
26
- # Path to static directory
27
- _static_dir: Optional[str] = None
28
-
29
- def set_db_manager(manager: DatabaseManager):
30
- global db_manager
31
- db_manager = manager
32
-
33
- @app.get("/api/graph")
34
- async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str] = None):
35
- if not db_manager:
36
- raise HTTPException(status_code=500, detail="Database not initialized")
37
-
38
- def get_eid(element):
39
- if not element: return None
40
- if isinstance(element, (int, str)):
41
- return str(element)
42
- # Try various ways to get ID (Neo4j, FalkorDB, etc.)
43
- for attr in ['element_id', 'id', '_id']:
44
- if hasattr(element, attr):
45
- val = getattr(element, attr)
46
- if val is not None: return str(val)
47
- return str(id(element))
48
-
49
- try:
50
- nodes_dict = {}
51
- edges = []
52
-
53
- with db_manager.get_driver().session() as session:
54
- if cypher_query:
55
- # Direct user query (filtered view)
56
- result = session.run(cypher_query)
57
- elif repo_path:
58
- repo_path = str(Path(repo_path).resolve())
59
- # Optimized subgraph query
60
- query = """
61
- MATCH (r:Repository {path: $repo_path})
62
- OPTIONAL MATCH (r)-[:CONTAINS*0..]->(n)
63
- WITH DISTINCT n
64
- WHERE n IS NOT NULL
65
- OPTIONAL MATCH (n)-[rel]->(m)
66
- RETURN n, rel, m
67
- """
68
- result = session.run(query, repo_path=repo_path)
69
- else:
70
- query = "MATCH (n) OPTIONAL MATCH (n)-[rel]->(m) RETURN n, rel, m LIMIT 5000"
71
- result = session.run(query)
72
-
73
- for record in result:
74
- for key in ['n', 'm']:
75
- node = record[key]
76
- if node:
77
- eid = get_eid(node)
78
- if eid not in nodes_dict:
79
- # FalkorDB / Neo4j labels compatibility
80
- labels = []
81
- if hasattr(node, 'labels'):
82
- labels = list(node.labels)
83
-
84
- # FalkorDB / Neo4j properties compatibility
85
- props = {}
86
- if hasattr(node, 'properties'):
87
- props = node.properties
88
- elif hasattr(node, 'items'):
89
- props = dict(node.items())
90
-
91
- nodes_dict[eid] = {
92
- "id": eid,
93
- "label": props.get('name', props.get('label', 'Unknown')),
94
- "type": labels[0].capitalize() if labels else "Other",
95
- "file": props.get('path', props.get('file', '')),
96
- "properties": props
97
- }
98
-
99
- rel = record['rel']
100
- if rel:
101
- rid = get_eid(rel)
102
-
103
- # FalkorDB / Neo4j compatibility for source/target nodes
104
- start_node = getattr(rel, 'start_node', getattr(rel, 'src_node', None))
105
- end_node = getattr(rel, 'end_node', getattr(rel, 'dest_node', None))
106
-
107
- source = get_eid(start_node)
108
- target = get_eid(end_node)
109
-
110
- if source and target:
111
- # relationship type/relation
112
- rel_type = "related"
113
- if hasattr(rel, 'type'):
114
- rel_type = rel.type
115
- elif hasattr(rel, 'relation'):
116
- rel_type = rel.relation
117
-
118
- edges.append({
119
- "id": rid,
120
- "source": source,
121
- "target": target,
122
- "type": str(rel_type).upper()
123
- })
124
-
125
- # Build a list of unique file paths from File-type nodes for the tree
126
- file_paths = sorted(set(
127
- n["file"] for n in nodes_dict.values()
128
- if n.get("file") and n.get("type", "").lower() == "file"
129
- ))
130
-
131
- return {
132
- "nodes": list(nodes_dict.values()),
133
- "edges": edges,
134
- "files": file_paths,
135
- }
136
-
137
- except Exception as e:
138
- debug_log(f"Error fetching graph: {str(e)}")
139
- import traceback
140
- traceback.print_exc()
141
- raise HTTPException(status_code=500, detail=str(e))
142
-
143
- @app.get("/api/file")
144
- async def get_file(path: str):
145
- file_path = Path(path)
146
- if not file_path.exists():
147
- raise HTTPException(status_code=404, detail="File not found")
148
-
149
- try:
150
- with open(file_path, "r", encoding="utf-8") as f:
151
- return {"content": f.read()}
152
- except Exception as e:
153
- raise HTTPException(status_code=500, detail=str(e))
154
-
155
- # SPA fallback handler
156
- @app.get("/{full_path:path}")
157
- async def spa_fallback(request: Request, full_path: str):
158
- global _static_dir
159
- if not _static_dir:
160
- return HTMLResponse("Static directory not configured", status_code=500)
161
-
162
- # Filesystem path
163
- file_path = Path(_static_dir) / full_path
164
-
165
- # If the file exists and is a file, serve it normally
166
- if file_path.exists() and file_path.is_file():
167
- return FileResponse(file_path)
168
-
169
- # Otherwise serve index.html (Standard SPA routing)
170
- index_path = Path(_static_dir) / "index.html"
171
- if index_path.exists():
172
- return FileResponse(index_path)
173
-
174
- return HTMLResponse("Not Found (Built UI not found in viz/dist)", status_code=404)
175
-
176
- def run_server(host: str = "127.0.0.1", port: int = 8000, static_dir: Optional[str] = None):
177
- global _static_dir
178
- _static_dir = static_dir
179
- uvicorn.run(app, host=host, port=port)