codegraphcontext 0.3.2__tar.gz → 0.3.3__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 (90) hide show
  1. codegraphcontext-0.3.3/MANIFEST.in +4 -0
  2. {codegraphcontext-0.3.2/src/codegraphcontext.egg-info → codegraphcontext-0.3.3}/PKG-INFO +2 -2
  3. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/README.md +1 -1
  4. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/pyproject.toml +1 -1
  5. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/cli_helpers.py +15 -243
  6. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/main.py +1 -1
  7. codegraphcontext-0.3.3/src/codegraphcontext/cli/visualizer.py +50 -0
  8. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/graph_builder.py +76 -60
  9. codegraphcontext-0.3.3/src/codegraphcontext/tools/handlers/query_handlers.py +87 -0
  10. codegraphcontext-0.3.3/src/codegraphcontext/utils/visualize_graph.py +0 -0
  11. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/viz/server.py +18 -17
  12. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3/src/codegraphcontext.egg-info}/PKG-INFO +2 -2
  13. codegraphcontext-0.3.2/MANIFEST.in +0 -5
  14. codegraphcontext-0.3.2/src/codegraphcontext/cli/visualizer.py +0 -1391
  15. codegraphcontext-0.3.2/src/codegraphcontext/tools/handlers/query_handlers.py +0 -181
  16. codegraphcontext-0.3.2/src/codegraphcontext/utils/visualize_graph.py +0 -150
  17. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/LICENSE +0 -0
  18. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/setup.cfg +0 -0
  19. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/__init__.py +0 -0
  20. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/__main__.py +0 -0
  21. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/__init__.py +0 -0
  22. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/config_manager.py +0 -0
  23. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/registry_commands.py +0 -0
  24. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/setup_macos.py +0 -0
  25. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/cli/setup_wizard.py +0 -0
  26. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/__init__.py +0 -0
  27. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/bundle_registry.py +0 -0
  28. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/cgc_bundle.py +0 -0
  29. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/database.py +0 -0
  30. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/database_falkordb.py +0 -0
  31. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
  32. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/database_kuzu.py +0 -0
  33. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/falkor_worker.py +0 -0
  34. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/jobs.py +0 -0
  35. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/core/watcher.py +0 -0
  36. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/prompts.py +0 -0
  37. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/server.py +0 -0
  38. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tool_definitions.py +0 -0
  39. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/__init__.py +0 -0
  40. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
  41. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/code_finder.py +0 -0
  42. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
  43. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
  44. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
  45. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
  46. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/c.py +0 -0
  47. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/cpp.py +0 -0
  48. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/csharp.py +0 -0
  49. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/dart.py +0 -0
  50. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/elixir.py +0 -0
  51. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/go.py +0 -0
  52. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/haskell.py +0 -0
  53. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/java.py +0 -0
  54. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/javascript.py +0 -0
  55. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
  56. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/perl.py +0 -0
  57. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/php.py +0 -0
  58. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/python.py +0 -0
  59. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/ruby.py +0 -0
  60. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/rust.py +0 -0
  61. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/scala.py +0 -0
  62. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/swift.py +0 -0
  63. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/typescript.py +0 -0
  64. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
  65. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/package_resolver.py +0 -0
  66. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
  67. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
  68. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
  69. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
  70. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
  71. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
  72. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
  73. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
  74. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
  75. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
  76. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
  77. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
  78. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
  79. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
  80. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
  81. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/scip_indexer.py +0 -0
  82. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/scip_pb2.py +0 -0
  83. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/tools/system.py +0 -0
  84. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/utils/debug_log.py +0 -0
  85. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
  86. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext.egg-info/SOURCES.txt +0 -0
  87. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
  88. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
  89. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext.egg-info/requires.txt +0 -0
  90. {codegraphcontext-0.3.2 → codegraphcontext-0.3.3}/src/codegraphcontext.egg-info/top_level.txt +0 -0
@@ -0,0 +1,4 @@
1
+ prune website
2
+ prune images
3
+ recursive-include src/codegraphcontext/viz/dist *
4
+ recursive-include vscode-extension *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.3.2
3
+ Version: 0.3.3
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
@@ -160,7 +160,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
160
160
  ---
161
161
 
162
162
  ## Project Details
163
- - **Version:** 0.3.2
163
+ - **Version:** 0.3.3
164
164
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
165
165
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
166
166
  - **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.2
99
+ - **Version:** 0.3.3
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.2"
3
+ version = "0.3.3"
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"
@@ -340,7 +340,9 @@ def cypher_helper_visual(query: str):
340
340
  console.print(f"[bold red]An error occurred while executing query:[/bold red] {e}")
341
341
  finally:
342
342
  db_manager.close_driver()
343
- import webbrowser
343
+
344
+
345
+ import uvicorn
344
346
  import urllib.parse
345
347
  from ..viz.server import run_server, set_db_manager
346
348
 
@@ -356,12 +358,18 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
356
358
  set_db_manager(db_manager)
357
359
 
358
360
  # Determine the static directory (built React app)
361
+ # This points to src/codegraphcontext/viz/dist where we build the website
359
362
  static_dir = Path(__file__).parent.parent / "viz" / "dist"
363
+
364
+ # Fallback for development if not yet built in viz/dist
360
365
  if not static_dir.exists():
361
- console.print("[yellow]Warning: Visualizer UI assets not found in package. Using fallback static dir.[/yellow]")
362
- # Fallback for development
363
- static_dir = Path.cwd() / "website" / "dist"
364
-
366
+ dev_static_dir = Path.cwd() / "website" / "dist"
367
+ if dev_static_dir.exists():
368
+ static_dir = dev_static_dir
369
+ 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)
372
+
365
373
  # Construct the URL
366
374
  backend_url = f"http://localhost:{port}"
367
375
  params = {"backend": backend_url}
@@ -369,7 +377,7 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
369
377
  params["repo_path"] = str(Path(repo_path).resolve())
370
378
 
371
379
  query_string = urllib.parse.urlencode(params)
372
- visualization_url = f"{backend_url}/playground?{query_string}"
380
+ visualization_url = f"{backend_url}/explore?{query_string}"
373
381
 
374
382
  console.print(f"[green]Starting visualizer server on {backend_url}...[/green]")
375
383
  console.print(f"[cyan]Opening Playground UI:[/cyan] {visualization_url}")
@@ -377,6 +385,7 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
377
385
  # Open browser in a separate thread/process if possible, or just before starting server
378
386
  def open_browser():
379
387
  import time
388
+ import webbrowser
380
389
  time.sleep(1.5) # Give the server a moment to start
381
390
  webbrowser.open(visualization_url)
382
391
 
@@ -390,243 +399,6 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
390
399
  finally:
391
400
  db_manager.close_driver()
392
401
 
393
- def _visualize_falkordb(db_manager):
394
- console.print("[dim]Generating FalkorDB visualization (showing up to 500 relationships)...[/dim]")
395
- try:
396
- data_nodes = []
397
- data_edges = []
398
-
399
- with db_manager.get_driver().session() as session:
400
- # Fetch nodes and edges
401
- q = "MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 500"
402
- result = session.run(q)
403
-
404
- seen_nodes = set()
405
-
406
- for record in result:
407
- # record values are Node/Relationship objects from falkordb client
408
- n = record['n']
409
- r = record['r']
410
- m = record['m']
411
-
412
- # Process Node helper
413
- def process_node(node):
414
- nid = getattr(node, 'id', -1)
415
- labels = getattr(node, 'labels', [])
416
- lbl = list(labels)[0] if labels else "Node"
417
- props = getattr(node, 'properties', {})
418
- name = props.get('name', str(nid))
419
-
420
- if nid not in seen_nodes:
421
- seen_nodes.add(nid)
422
- color = "#97c2fc" # Default blue
423
- if "Repository" in labels: color = "#ffb3ba" # Red
424
- elif "File" in labels: color = "#baffc9" # Green
425
- elif "Class" in labels: color = "#bae1ff" # Light Blue
426
- elif "Function" in labels: color = "#ffffba" # Yellow
427
- elif "Package" in labels: color = "#ffdfba" # Orange
428
-
429
- data_nodes.append({
430
- "id": nid,
431
- "label": name,
432
- "group": lbl,
433
- "title": str(props),
434
- "color": color
435
- })
436
- return nid
437
-
438
- nid = process_node(n)
439
- mid = process_node(m)
440
-
441
- # Check Edge
442
- e_type = getattr(r, 'relation', '') or getattr(r, 'type', 'REL')
443
- data_edges.append({
444
- "from": nid,
445
- "to": mid,
446
- "label": e_type,
447
- "arrows": "to"
448
- })
449
-
450
- filename = "codegraph_viz.html"
451
- html_content = f"""
452
- <!DOCTYPE html>
453
- <html>
454
- <head>
455
- <title>CodeGraphContext Visualization</title>
456
- <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
457
- <style type="text/css">
458
- #mynetwork {{
459
- width: 100%;
460
- height: 100vh;
461
- border: 1px solid lightgray;
462
- }}
463
- </style>
464
- </head>
465
- <body>
466
- <div id="mynetwork"></div>
467
- <script type="text/javascript">
468
- var nodes = new vis.DataSet({json.dumps(data_nodes)});
469
- var edges = new vis.DataSet({json.dumps(data_edges)});
470
- var container = document.getElementById('mynetwork');
471
- var data = {{ nodes: nodes, edges: edges }};
472
- var options = {{
473
- nodes: {{ shape: 'dot', size: 16 }},
474
- physics: {{ stabilization: false }},
475
- layout: {{ improvedLayout: false }}
476
- }};
477
- var network = new vis.Network(container, data, options);
478
- </script>
479
- </body>
480
- </html>
481
- """
482
-
483
- out_path = Path(filename).resolve()
484
- with open(out_path, "w") as f:
485
- f.write(html_content)
486
-
487
- console.print(f"[green]Visualization generated at:[/green] {out_path}")
488
- console.print("Opening in default browser...")
489
- webbrowser.open(f"file://{out_path}")
490
-
491
- except Exception as e:
492
- console.print(f"[bold red]Visualization failed:[/bold red] {e}")
493
- import traceback
494
- traceback.print_exc()
495
- finally:
496
- db_manager.close_driver()
497
-
498
-
499
- def _visualize_kuzudb(db_manager):
500
- console.print("[dim]Generating KùzuDB visualization (showing up to 500 relationships)...[/dim]")
501
- try:
502
- data_nodes = []
503
- data_edges = []
504
-
505
- with db_manager.get_driver().session() as session:
506
- # Fetch nodes and edges
507
- # KùzuDB returns dicts for n, r, m in the result
508
- q = "MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 500"
509
- result = session.run(q)
510
-
511
- seen_nodes = set()
512
-
513
- # Helper to extract Node ID and props
514
- def process_node(node):
515
- uid = None
516
- lbl = 'Node'
517
- props = {}
518
-
519
- # Handle Kuzu Node Object (processed by wrapper)
520
- if hasattr(node, 'properties'):
521
- props = node.properties or {}
522
- if hasattr(node, 'labels') and node.labels:
523
- lbl = node.labels[0]
524
- if hasattr(node, 'id'):
525
- uid = str(node.id)
526
- # Handle Dictionary (raw Kuzu result)
527
- elif isinstance(node, dict):
528
- if '_id' in node:
529
- uid = f"{node['_id']['table']}_{node['_id']['offset']}"
530
- lbl = node.get('_label', 'Node')
531
- props = {k: v for k, v in node.items() if not k.startswith('_')}
532
-
533
- if not uid:
534
- uid = str(uuid.uuid4())
535
-
536
- name = props.get('name', str(uid))
537
-
538
- if uid not in seen_nodes:
539
- seen_nodes.add(uid)
540
- color = "#97c2fc" # Default blue
541
- if "Repository" == lbl: color = "#ffb3ba"
542
- elif "File" == lbl: color = "#baffc9"
543
- elif "Class" == lbl: color = "#bae1ff"
544
- elif "Function" == lbl: color = "#ffffba"
545
- elif "Module" == lbl: color = "#ffdfba"
546
-
547
- data_nodes.append({
548
- "id": uid,
549
- "label": name,
550
- "group": lbl,
551
- "title": str(props),
552
- "color": color
553
- })
554
- return uid
555
-
556
- # Iterate results
557
- for record in result:
558
- # record is dict-like access to row items
559
- n = record['n']
560
- r = record['r']
561
- m = record['m']
562
-
563
- nid = process_node(n)
564
- mid = process_node(m)
565
-
566
- # Process Edge
567
- e_type = 'REL'
568
- if hasattr(r, 'type'):
569
- e_type = r.type
570
- elif isinstance(r, dict):
571
- e_type = r.get('_label', 'REL')
572
- elif hasattr(r, 'label'): # Some versions
573
- e_type = r.label
574
-
575
- data_edges.append({
576
- "from": nid,
577
- "to": mid,
578
- "label": e_type,
579
- "arrows": "to"
580
- })
581
-
582
- filename = "codegraph_viz.html"
583
- html_content = f"""
584
- <!DOCTYPE html>
585
- <html>
586
- <head>
587
- <title>CodeGraphContext KùzuDB Visualization</title>
588
- <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
589
- <style type="text/css">
590
- #mynetwork {{
591
- width: 100%;
592
- height: 100vh;
593
- border: 1px solid lightgray;
594
- }}
595
- </style>
596
- </head>
597
- <body>
598
- <div id="mynetwork"></div>
599
- <script type="text/javascript">
600
- var nodes = new vis.DataSet({json.dumps(data_nodes)});
601
- var edges = new vis.DataSet({json.dumps(data_edges)});
602
- var container = document.getElementById('mynetwork');
603
- var data = {{ nodes: nodes, edges: edges }};
604
- var options = {{
605
- nodes: {{ shape: 'dot', size: 16 }},
606
- physics: {{ stabilization: false }},
607
- layout: {{ improvedLayout: false }}
608
- }};
609
- var network = new vis.Network(container, data, options);
610
- </script>
611
- </body>
612
- </html>
613
- """
614
-
615
- out_path = Path(filename).resolve()
616
- with open(out_path, "w") as f:
617
- f.write(html_content)
618
-
619
- console.print(f"[green]Visualization generated at:[/green] {out_path}")
620
- console.print("Opening in default browser...")
621
- webbrowser.open(f"file://{out_path}")
622
-
623
- except Exception as e:
624
- console.print(f"[bold red]Visualization failed:[/bold red] {e}")
625
- import traceback
626
- traceback.print_exc()
627
- finally:
628
- db_manager.close_driver()
629
-
630
402
 
631
403
  def reindex_helper(path: str):
632
404
  """Force re-index by deleting and rebuilding the repository."""
@@ -999,7 +999,7 @@ def visualize(
999
999
  port: int = typer.Option(8000, "--port", "-p", help="Port to run the visualizer server on.")
1000
1000
  ):
1001
1001
  """
1002
- Launches the interactive Playground UI to visualize the code graph.
1002
+ Launches the interactive UI to visualize the code graph.
1003
1003
  """
1004
1004
  _load_credentials()
1005
1005
  visualize_helper(repo, port)
@@ -0,0 +1,50 @@
1
+ import urllib.parse
2
+ from typing import Optional, List, Dict, Any, Set
3
+ from pathlib import Path
4
+ from .cli_helpers import visualize_helper
5
+
6
+ def check_visual_flag(ctx, visual: bool, cypher_query: str = None):
7
+ """
8
+ Helper to check the --visual flag and launch the visualizer.
9
+ This is called from within analyze/find commands.
10
+ """
11
+ if visual and cypher_query:
12
+ # We start the visualizer on port 8000
13
+ # Passing empty repo handles showing just the query results
14
+ port = 8000
15
+ encoded_query = urllib.parse.quote(cypher_query)
16
+ visualization_url = f"http://localhost:{port}/explore?cypher_query={encoded_query}"
17
+
18
+ from rich.console import Console
19
+ console = Console(stderr=True)
20
+ console.print(f"[green]Starting visualizer...[/green]")
21
+ console.print(f"[cyan]Visualizing results at:[/cyan] {visualization_url}")
22
+
23
+ # Start the backend server and open the browser
24
+ visualize_helper(repo_path=None, port=port)
25
+ return True
26
+ return False
27
+
28
+ def visualize_call_graph(cypher_query: str):
29
+ """Visualize a call graph result."""
30
+ visualize_helper(repo_path=None, port=8000)
31
+
32
+ def visualize_call_chain(cypher_query: str):
33
+ """Visualize a call chain result."""
34
+ visualize_helper(repo_path=None, port=8000)
35
+
36
+ def visualize_dependencies(cypher_query: str):
37
+ """Visualize code dependencies."""
38
+ visualize_helper(repo_path=None, port=8000)
39
+
40
+ def visualize_inheritance_tree(cypher_query: str):
41
+ """Visualize class inheritance tree."""
42
+ visualize_helper(repo_path=None, port=8000)
43
+
44
+ def visualize_overrides(cypher_query: str):
45
+ """Visualize method overrides."""
46
+ visualize_helper(repo_path=None, port=8000)
47
+
48
+ def visualize_search_results(cypher_query: str):
49
+ """Visualize search results."""
50
+ visualize_helper(repo_path=None, port=8000)
@@ -115,39 +115,54 @@ class GraphBuilder:
115
115
  self.loop = loop
116
116
  self.driver = self.db_manager.get_driver()
117
117
  self.parsers = {
118
- '.py': TreeSitterParser('python'),
119
- '.ipynb': TreeSitterParser('python'),
120
- '.js': TreeSitterParser('javascript'),
121
- '.jsx': TreeSitterParser('javascript'),
122
- '.mjs': TreeSitterParser('javascript'),
123
- '.cjs': TreeSitterParser('javascript'),
124
- '.go': TreeSitterParser('go'),
125
- '.ts': TreeSitterParser('typescript'),
126
- '.tsx': TreeSitterParser('typescript'),
127
- '.cpp': TreeSitterParser('cpp'),
128
- '.h': TreeSitterParser('cpp'),
129
- '.hpp': TreeSitterParser('cpp'),
130
- '.hh': TreeSitterParser('cpp'),
131
- '.rs': TreeSitterParser('rust'),
132
- '.c': TreeSitterParser('c'),
133
- # '.h': TreeSitterParser('c'), # Need to write an algo for distinguishing C vs C++ headers
134
- '.java': TreeSitterParser('java'),
135
- '.rb': TreeSitterParser('ruby'),
136
- '.cs': TreeSitterParser('c_sharp'),
137
- '.php': TreeSitterParser('php'),
138
- '.kt': TreeSitterParser('kotlin'),
139
- '.scala': TreeSitterParser('scala'),
140
- '.sc': TreeSitterParser('scala'),
141
- '.swift': TreeSitterParser('swift'),
142
- '.hs': TreeSitterParser('haskell'),
143
- '.dart': TreeSitterParser('dart'),
144
- '.pl': TreeSitterParser('perl'),
145
- '.pm': TreeSitterParser('perl'),
146
- '.ex': TreeSitterParser('elixir'),
147
- '.exs': TreeSitterParser('elixir'),
118
+ '.py': 'python',
119
+ '.ipynb': 'python',
120
+ '.js': 'javascript',
121
+ '.jsx': 'javascript',
122
+ '.mjs': 'javascript',
123
+ '.cjs': 'javascript',
124
+ '.go': 'go',
125
+ '.ts': 'typescript',
126
+ '.tsx': 'typescript',
127
+ '.cpp': 'cpp',
128
+ '.h': 'cpp',
129
+ '.hpp': 'cpp',
130
+ '.hh': 'cpp',
131
+ '.rs': 'rust',
132
+ '.c': 'c',
133
+ # '.h': 'c', # Need to write an algo for distinguishing C vs C++ headers
134
+ '.java': 'java',
135
+ '.rb': 'ruby',
136
+ '.cs': 'c_sharp',
137
+ '.php': 'php',
138
+ '.kt': 'kotlin',
139
+ '.scala': 'scala',
140
+ '.sc': 'scala',
141
+ '.swift': 'swift',
142
+ '.hs': 'haskell',
143
+ '.dart': 'dart',
144
+ '.pl': 'perl',
145
+ '.pm': 'perl',
146
+ '.ex': 'elixir',
147
+ '.exs': 'elixir',
148
148
  }
149
+ self._parsed_cache = {}
149
150
  self.create_schema()
150
151
 
152
+ def get_parser(self, extension: str) -> Optional[TreeSitterParser]:
153
+ """Gets or creates a TreeSitterParser for the given extension."""
154
+ lang_name = self.parsers.get(extension)
155
+ if not lang_name:
156
+ return None
157
+
158
+ if lang_name not in self._parsed_cache:
159
+ try:
160
+ self._parsed_cache[lang_name] = TreeSitterParser(lang_name)
161
+ except Exception as e:
162
+ warning_logger(f"Failed to initialize parser for {lang_name}: {e}")
163
+ return None
164
+ return self._parsed_cache[lang_name]
165
+
151
166
  # A general schema creation based on common features across languages
152
167
  def create_schema(self):
153
168
  """Create constraints and indexes in Neo4j."""
@@ -175,6 +190,7 @@ class GraphBuilder:
175
190
  session.run("CREATE INDEX function_lang IF NOT EXISTS FOR (f:Function) ON (f.lang)")
176
191
  session.run("CREATE INDEX class_lang IF NOT EXISTS FOR (c:Class) ON (c.lang)")
177
192
  session.run("CREATE INDEX annotation_lang IF NOT EXISTS FOR (a:Annotation) ON (a.lang)")
193
+
178
194
  is_falkordb = getattr(self.db_manager, 'get_backend_type', lambda: 'neo4j')() != 'neo4j'
179
195
  if is_falkordb:
180
196
  # FalkorDB uses db.idx.fulltext.createNodeIndex per label
@@ -242,85 +258,85 @@ class GraphBuilder:
242
258
 
243
259
  if '.py' in files_by_lang:
244
260
  from .languages import python as python_lang_module
245
- imports_map.update(python_lang_module.pre_scan_python(files_by_lang['.py'], self.parsers['.py']))
261
+ imports_map.update(python_lang_module.pre_scan_python(files_by_lang['.py'], self.get_parser('.py')))
246
262
  if '.ipynb' in files_by_lang:
247
263
  from .languages import python as python_lang_module
248
- imports_map.update(python_lang_module.pre_scan_python(files_by_lang['.ipynb'], self.parsers['.ipynb']))
264
+ imports_map.update(python_lang_module.pre_scan_python(files_by_lang['.ipynb'], self.get_parser('.ipynb')))
249
265
  if '.js' in files_by_lang:
250
266
  from .languages import javascript as js_lang_module
251
- imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.js'], self.parsers['.js']))
267
+ imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.js'], self.get_parser('.js')))
252
268
  if '.jsx' in files_by_lang:
253
269
  from .languages import javascript as js_lang_module
254
- imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.jsx'], self.parsers['.jsx']))
270
+ imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.jsx'], self.get_parser('.jsx')))
255
271
  if '.mjs' in files_by_lang:
256
272
  from .languages import javascript as js_lang_module
257
- imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.mjs'], self.parsers['.mjs']))
273
+ imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.mjs'], self.get_parser('.mjs')))
258
274
  if '.cjs' in files_by_lang:
259
275
  from .languages import javascript as js_lang_module
260
- imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.cjs'], self.parsers['.cjs']))
276
+ imports_map.update(js_lang_module.pre_scan_javascript(files_by_lang['.cjs'], self.get_parser('.cjs')))
261
277
  if '.go' in files_by_lang:
262
278
  from .languages import go as go_lang_module
263
- imports_map.update(go_lang_module.pre_scan_go(files_by_lang['.go'], self.parsers['.go']))
279
+ imports_map.update(go_lang_module.pre_scan_go(files_by_lang['.go'], self.get_parser('.go')))
264
280
  if '.ts' in files_by_lang:
265
281
  from .languages import typescript as ts_lang_module
266
- imports_map.update(ts_lang_module.pre_scan_typescript(files_by_lang['.ts'], self.parsers['.ts']))
282
+ imports_map.update(ts_lang_module.pre_scan_typescript(files_by_lang['.ts'], self.get_parser('.ts')))
267
283
  if '.tsx' in files_by_lang:
268
284
  from .languages import typescriptjsx as tsx_lang_module
269
- imports_map.update(tsx_lang_module.pre_scan_typescript(files_by_lang['.tsx'], self.parsers['.tsx']))
285
+ imports_map.update(tsx_lang_module.pre_scan_typescript(files_by_lang['.tsx'], self.get_parser('.tsx')))
270
286
  if '.cpp' in files_by_lang:
271
287
  from .languages import cpp as cpp_lang_module
272
- imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.cpp'], self.parsers['.cpp']))
288
+ imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.cpp'], self.get_parser('.cpp')))
273
289
  if '.h' in files_by_lang:
274
290
  from .languages import cpp as cpp_lang_module
275
- imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.h'], self.parsers['.h']))
291
+ imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.h'], self.get_parser('.h')))
276
292
  if '.hpp' in files_by_lang:
277
293
  from .languages import cpp as cpp_lang_module
278
- imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.hpp'], self.parsers['.hpp']))
294
+ imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.hpp'], self.get_parser('.hpp')))
279
295
  if '.hh' in files_by_lang:
280
296
  from .languages import cpp as cpp_lang_module
281
- imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.hh'], self.parsers['.hh']))
297
+ imports_map.update(cpp_lang_module.pre_scan_cpp(files_by_lang['.hh'], self.get_parser('.hh')))
282
298
  if '.rs' in files_by_lang:
283
299
  from .languages import rust as rust_lang_module
284
- imports_map.update(rust_lang_module.pre_scan_rust(files_by_lang['.rs'], self.parsers['.rs']))
300
+ imports_map.update(rust_lang_module.pre_scan_rust(files_by_lang['.rs'], self.get_parser('.rs')))
285
301
  if '.c' in files_by_lang:
286
302
  from .languages import c as c_lang_module
287
- imports_map.update(c_lang_module.pre_scan_c(files_by_lang['.c'], self.parsers['.c']))
303
+ imports_map.update(c_lang_module.pre_scan_c(files_by_lang['.c'], self.get_parser('.c')))
288
304
  elif '.java' in files_by_lang:
289
305
  from .languages import java as java_lang_module
290
- imports_map.update(java_lang_module.pre_scan_java(files_by_lang['.java'], self.parsers['.java']))
306
+ imports_map.update(java_lang_module.pre_scan_java(files_by_lang['.java'], self.get_parser('.java')))
291
307
  elif '.rb' in files_by_lang:
292
308
  from .languages import ruby as ruby_lang_module
293
- imports_map.update(ruby_lang_module.pre_scan_ruby(files_by_lang['.rb'], self.parsers['.rb']))
309
+ imports_map.update(ruby_lang_module.pre_scan_ruby(files_by_lang['.rb'], self.get_parser('.rb')))
294
310
  elif '.cs' in files_by_lang:
295
311
  from .languages import csharp as csharp_lang_module
296
- imports_map.update(csharp_lang_module.pre_scan_csharp(files_by_lang['.cs'], self.parsers['.cs']))
312
+ imports_map.update(csharp_lang_module.pre_scan_csharp(files_by_lang['.cs'], self.get_parser('.cs')))
297
313
  if '.kt' in files_by_lang:
298
314
  from .languages import kotlin as kotlin_lang_module
299
- imports_map.update(kotlin_lang_module.pre_scan_kotlin(files_by_lang['.kt'], self.parsers['.kt']))
315
+ imports_map.update(kotlin_lang_module.pre_scan_kotlin(files_by_lang['.kt'], self.get_parser('.kt')))
300
316
  if '.scala' in files_by_lang:
301
317
  from .languages import scala as scala_lang_module
302
- imports_map.update(scala_lang_module.pre_scan_scala(files_by_lang['.scala'], self.parsers['.scala']))
318
+ imports_map.update(scala_lang_module.pre_scan_scala(files_by_lang['.scala'], self.get_parser('.scala')))
303
319
  if '.sc' in files_by_lang:
304
320
  from .languages import scala as scala_lang_module
305
- imports_map.update(scala_lang_module.pre_scan_scala(files_by_lang['.sc'], self.parsers['.sc']))
321
+ imports_map.update(scala_lang_module.pre_scan_scala(files_by_lang['.sc'], self.get_parser('.sc')))
306
322
  if '.swift' in files_by_lang:
307
323
  from .languages import swift as swift_lang_module
308
- imports_map.update(swift_lang_module.pre_scan_swift(files_by_lang['.swift'], self.parsers['.swift']))
324
+ imports_map.update(swift_lang_module.pre_scan_swift(files_by_lang['.swift'], self.get_parser('.swift')))
309
325
  if '.dart' in files_by_lang:
310
326
  from .languages import dart as dart_lang_module
311
- imports_map.update(dart_lang_module.pre_scan_dart(files_by_lang['.dart'], self.parsers['.dart']))
327
+ imports_map.update(dart_lang_module.pre_scan_dart(files_by_lang['.dart'], self.get_parser('.dart')))
312
328
  if '.pl' in files_by_lang:
313
329
  from .languages import perl as perl_lang_module
314
- imports_map.update(perl_lang_module.pre_scan_perl(files_by_lang['.pl'], self.parsers['.pl']))
330
+ imports_map.update(perl_lang_module.pre_scan_perl(files_by_lang['.pl'], self.get_parser('.pl')))
315
331
  if '.pm' in files_by_lang:
316
332
  from .languages import perl as perl_lang_module
317
- imports_map.update(perl_lang_module.pre_scan_perl(files_by_lang['.pm'], self.parsers['.pm']))
333
+ imports_map.update(perl_lang_module.pre_scan_perl(files_by_lang['.pm'], self.get_parser('.pm')))
318
334
  if '.ex' in files_by_lang:
319
335
  from .languages import elixir as elixir_lang_module
320
- imports_map.update(elixir_lang_module.pre_scan_elixir(files_by_lang['.ex'], self.parsers['.ex']))
336
+ imports_map.update(elixir_lang_module.pre_scan_elixir(files_by_lang['.ex'], self.get_parser('.ex')))
321
337
  if '.exs' in files_by_lang:
322
338
  from .languages import elixir as elixir_lang_module
323
- imports_map.update(elixir_lang_module.pre_scan_elixir(files_by_lang['.exs'], self.parsers['.exs']))
339
+ imports_map.update(elixir_lang_module.pre_scan_elixir(files_by_lang['.exs'], self.get_parser('.exs')))
324
340
 
325
341
  return imports_map
326
342
 
@@ -996,7 +1012,7 @@ class GraphBuilder:
996
1012
 
997
1013
  def parse_file(self, repo_path: Path, path: Path, is_dependency: bool = False) -> Dict:
998
1014
  """Parses a file with the appropriate language parser and extracts code elements."""
999
- parser = self.parsers.get(path.suffix)
1015
+ parser = self.get_parser(path.suffix)
1000
1016
  if not parser:
1001
1017
  warning_logger(f"No parser found for file extension {path.suffix}. Skipping {path}")
1002
1018
  return {"path": str(path), "error": f"No parser for {path.suffix}"}
@@ -1123,9 +1139,9 @@ class GraphBuilder:
1123
1139
 
1124
1140
  # Step 5: Tree-sitter supplement — add source text, complexity, imports and bases
1125
1141
  file_path = Path(abs_path_str)
1126
- if file_path.exists() and file_path.suffix in self.parsers:
1142
+ ts_parser = self.get_parser(file_path.suffix)
1143
+ if file_path.exists() and ts_parser:
1127
1144
  try:
1128
- ts_parser = self.parsers[file_path.suffix]
1129
1145
  ts_data = ts_parser.parse(file_path, is_dependency, index_source=True)
1130
1146
  if "error" not in ts_data:
1131
1147
  # 1. Functions: complexity, source, decorators