codegraphcontext 0.3.3__py3-none-any.whl → 0.3.4__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.
@@ -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}"
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  import uvicorn
7
7
  import json
8
8
  import os
9
+ import sys
9
10
  from typing import Optional, List, Dict, Any
10
11
 
11
12
  from ..core.database import DatabaseManager
@@ -50,13 +51,17 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
50
51
  nodes_dict = {}
51
52
  edges = []
52
53
 
54
+ print(f"DEBUG: Starting get_graph with repo_path={repo_path}", flush=True)
55
+ nodes_dict = {}
56
+ edges = []
57
+
53
58
  with db_manager.get_driver().session() as session:
54
59
  if cypher_query:
55
- # Direct user query (filtered view)
60
+ print(f"DEBUG: Executing custom query: {cypher_query}", flush=True)
56
61
  result = session.run(cypher_query)
57
62
  elif repo_path:
58
63
  repo_path = str(Path(repo_path).resolve())
59
- # Optimized subgraph query
64
+ print(f"DEBUG: Fetching subgraph for: {repo_path}", flush=True)
60
65
  query = """
61
66
  MATCH (r:Repository {path: $repo_path})
62
67
  OPTIONAL MATCH (r)-[:CONTAINS*0..]->(n)
@@ -67,77 +72,123 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
67
72
  """
68
73
  result = session.run(query, repo_path=repo_path)
69
74
  else:
75
+ print("DEBUG: Fetching global graph", flush=True)
70
76
  query = "MATCH (n) OPTIONAL MATCH (n)-[rel]->(m) RETURN n, rel, m LIMIT 5000"
71
77
  result = session.run(query)
72
78
 
79
+ record_count = 0
73
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)
74
83
  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())
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
90
97
 
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
+ # 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
98
129
 
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
- })
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)
124
169
 
125
170
  # 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
- ))
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)))
130
176
 
131
- return {
177
+ response_data = {
132
178
  "nodes": list(nodes_dict.values()),
133
- "edges": edges,
179
+ "links": edges,
134
180
  "files": file_paths,
135
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
136
185
 
137
186
  except Exception as e:
138
187
  debug_log(f"Error fetching graph: {str(e)}")
139
188
  import traceback
140
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
141
192
  raise HTTPException(status_code=500, detail=str(e))
142
193
 
143
194
  @app.get("/api/file")
@@ -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/)
@@ -4,7 +4,7 @@ codegraphcontext/prompts.py,sha256=E5P55paM0oHfBcNVfxkxpXRGZnya1kr_mKDg9i49FwM,6
4
4
  codegraphcontext/server.py,sha256=gcb6V4x0Oh8haBOCm2WBjTCO9FDukrSalAMMApL-oc8,12806
5
5
  codegraphcontext/tool_definitions.py,sha256=_0ahezQSURX_ewWydT2sFcvC6OFGdMoPA7KwgWNVcks,11482
6
6
  codegraphcontext/cli/__init__.py,sha256=v6CMDVKM5d_sXn3S5nZNf0phXn0IdrnhLazUoen9k9w,38
7
- codegraphcontext/cli/cli_helpers.py,sha256=8T7oMy6bBvQtfDjhQik5kKKkkyqWxeG4sXzeM8n-uxI,28374
7
+ codegraphcontext/cli/cli_helpers.py,sha256=QcpEV9VRFuPiiIEUwiXCr_nm7Z7amzphXv9zQY0aWIU,29646
8
8
  codegraphcontext/cli/config_manager.py,sha256=MK7GMGZ4hd8-ZOShXBd_KDtNqHQ3ngdbef5KM_naPrQ,15899
9
9
  codegraphcontext/cli/main.py,sha256=89jWaO8Gr3nRdLQtfwF434wpU77d5bJwt016f_lLnGg,86486
10
10
  codegraphcontext/cli/registry_commands.py,sha256=30rJm4SeS0n1jax4JVuhuv4zYLzyMmlHcCSnsDDhgc8,19964
@@ -71,10 +71,10 @@ codegraphcontext/tools/query_tool_languages/typescript_toolkit.py,sha256=3S4hpmO
71
71
  codegraphcontext/utils/debug_log.py,sha256=Qg7jwyeg7x2h3Ur_2S34bdMCkHdlk_ngHfPwa97A9vE,2836
72
72
  codegraphcontext/utils/tree_sitter_manager.py,sha256=bIuKYN1aj1Zi6BnksGjZSLZzgBwuFRselZzyUpbToAU,9180
73
73
  codegraphcontext/utils/visualize_graph.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- codegraphcontext/viz/server.py,sha256=f2xjgkjMOBgrZd5fZ6kCEO-lzH9hNfPPCcSwfitH4io,6719
75
- codegraphcontext-0.3.3.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
76
- codegraphcontext-0.3.3.dist-info/METADATA,sha256=pw9ykqFiZCaX1PioazQoU9S_Kmb7BRRAuPjveWyykk4,21579
77
- codegraphcontext-0.3.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
78
- codegraphcontext-0.3.3.dist-info/entry_points.txt,sha256=LCxWCWMshdvYGoHBPuQZ8C-e4CiNSHCLXofrNSGHkoE,103
79
- codegraphcontext-0.3.3.dist-info/top_level.txt,sha256=CBgc6LAPZIO5FS0nSYYkylDifHsZTIqw3Gf5UwDxeGI,17
80
- codegraphcontext-0.3.3.dist-info/RECORD,,
74
+ codegraphcontext/viz/server.py,sha256=gBl6yfTDa4F7FW4JxHV9QtG1kVSt8iSKvhCXSKB0lIw,10017
75
+ codegraphcontext-0.3.4.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
76
+ codegraphcontext-0.3.4.dist-info/METADATA,sha256=PHNdbGKtvsw3S0hhTHgCoanKx_T8DMPPdLZ3iGEWe4o,21685
77
+ codegraphcontext-0.3.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
78
+ codegraphcontext-0.3.4.dist-info/entry_points.txt,sha256=LCxWCWMshdvYGoHBPuQZ8C-e4CiNSHCLXofrNSGHkoE,103
79
+ codegraphcontext-0.3.4.dist-info/top_level.txt,sha256=CBgc6LAPZIO5FS0nSYYkylDifHsZTIqw3Gf5UwDxeGI,17
80
+ codegraphcontext-0.3.4.dist-info/RECORD,,