codegraphcontext 0.3.4__tar.gz → 0.3.7__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 (86) hide show
  1. {codegraphcontext-0.3.4/src/codegraphcontext.egg-info → codegraphcontext-0.3.7}/PKG-INFO +3 -3
  2. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/README.md +2 -2
  3. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/pyproject.toml +1 -1
  4. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/setup_wizard.py +6 -0
  5. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/code_finder.py +13 -3
  6. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/graph_builder.py +79 -47
  7. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/viz/server.py +89 -47
  8. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7/src/codegraphcontext.egg-info}/PKG-INFO +3 -3
  9. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/LICENSE +0 -0
  10. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/MANIFEST.in +0 -0
  11. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/setup.cfg +0 -0
  12. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/__init__.py +0 -0
  13. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/__main__.py +0 -0
  14. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/__init__.py +0 -0
  15. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/cli_helpers.py +0 -0
  16. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/config_manager.py +0 -0
  17. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/main.py +0 -0
  18. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/registry_commands.py +0 -0
  19. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/setup_macos.py +0 -0
  20. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/cli/visualizer.py +0 -0
  21. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/__init__.py +0 -0
  22. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/bundle_registry.py +0 -0
  23. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/cgc_bundle.py +0 -0
  24. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/database.py +0 -0
  25. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/database_falkordb.py +0 -0
  26. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
  27. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/database_kuzu.py +0 -0
  28. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/falkor_worker.py +0 -0
  29. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/jobs.py +0 -0
  30. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/core/watcher.py +0 -0
  31. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/prompts.py +0 -0
  32. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/server.py +0 -0
  33. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tool_definitions.py +0 -0
  34. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/__init__.py +0 -0
  35. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
  36. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
  37. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
  38. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
  39. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
  40. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
  41. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/c.py +0 -0
  42. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/cpp.py +0 -0
  43. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/csharp.py +0 -0
  44. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/dart.py +0 -0
  45. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/elixir.py +0 -0
  46. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/go.py +0 -0
  47. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/haskell.py +0 -0
  48. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/java.py +0 -0
  49. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/javascript.py +0 -0
  50. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
  51. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/perl.py +0 -0
  52. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/php.py +0 -0
  53. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/python.py +0 -0
  54. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/ruby.py +0 -0
  55. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/rust.py +0 -0
  56. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/scala.py +0 -0
  57. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/swift.py +0 -0
  58. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/typescript.py +0 -0
  59. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
  60. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/package_resolver.py +0 -0
  61. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
  62. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
  63. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
  64. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
  65. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
  66. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
  67. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
  68. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
  69. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
  70. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
  71. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
  72. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
  73. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
  74. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
  75. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
  76. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/scip_indexer.py +0 -0
  77. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/scip_pb2.py +0 -0
  78. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/tools/system.py +0 -0
  79. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/utils/debug_log.py +0 -0
  80. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
  81. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext/utils/visualize_graph.py +0 -0
  82. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext.egg-info/SOURCES.txt +0 -0
  83. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
  84. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
  85. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/src/codegraphcontext.egg-info/requires.txt +0 -0
  86. {codegraphcontext-0.3.4 → codegraphcontext-0.3.7}/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.4
3
+ Version: 0.3.7
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
@@ -162,7 +162,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
162
162
  ---
163
163
 
164
164
  ## Project Details
165
- - **Version:** 0.3.4
165
+ - **Version:** 0.3.7
166
166
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
167
167
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
168
168
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -249,7 +249,7 @@ _If you’re using CodeGraphContext in your project, feel free to open a PR and
249
249
  - `stdlibs>=2023.11.18`
250
250
  - `typer[all]>=0.9.0`
251
251
  - `rich>=13.7.0`
252
- - `inquirerpy>=0.3.4`
252
+ - `inquirerpy>=0.3.7`
253
253
  - `python-dotenv>=1.0.0`
254
254
  - `tree-sitter>=0.21.0`
255
255
  - `tree-sitter-language-pack>=0.6.0`
@@ -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.4
99
+ - **Version:** 0.3.7
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/)
@@ -183,7 +183,7 @@ _If you’re using CodeGraphContext in your project, feel free to open a PR and
183
183
  - `stdlibs>=2023.11.18`
184
184
  - `typer[all]>=0.9.0`
185
185
  - `rich>=13.7.0`
186
- - `inquirerpy>=0.3.4`
186
+ - `inquirerpy>=0.3.7`
187
187
  - `python-dotenv>=1.0.0`
188
188
  - `tree-sitter>=0.21.0`
189
189
  - `tree-sitter-language-pack>=0.6.0`
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codegraphcontext"
3
- version = "0.3.4"
3
+ version = "0.3.7"
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"
@@ -296,6 +296,12 @@ def run_command(command, console, shell=False, check=True, input_text=None):
296
296
  Returns the completed process object on success, None on failure.
297
297
  """
298
298
  cmd_str = command if isinstance(command, str) else ' '.join(command)
299
+
300
+ # Mask passwords from being printed out
301
+ if "set-initial-password" in cmd_str:
302
+ import re
303
+ cmd_str = re.sub(r'(set-initial-password\s+)(\S+)', r'\g<1>********', cmd_str)
304
+
299
305
  console.print(f"[cyan]$ {cmd_str}[/cyan]")
300
306
  try:
301
307
  process = subprocess.run(
@@ -539,7 +539,9 @@ class CodeFinder:
539
539
 
540
540
  with self.driver.session() as session:
541
541
  repo_filter = "AND func.path STARTS WITH $repo_path" if repo_path else ""
542
- result = session.run(f"""
542
+ decorator_filter = "AND ALL(decorator_name IN $exclude_decorated_with WHERE NOT decorator_name IN func.decorators)" if exclude_decorated_with else ""
543
+
544
+ query = f"""
543
545
  MATCH (func:Function)
544
546
  WHERE func.is_dependency = false {repo_filter}
545
547
  AND NOT func.name IN ['main', 'setup', 'run']
@@ -550,7 +552,7 @@ class CodeFinder:
550
552
  AND NOT toLower(func.name) CONTAINS 'application'
551
553
  AND NOT toLower(func.name) CONTAINS 'entry'
552
554
  AND NOT toLower(func.name) CONTAINS 'entrypoint'
553
- AND ALL(decorator_name IN $exclude_decorated_with WHERE NOT decorator_name IN func.decorators)
555
+ {decorator_filter}
554
556
  WITH func
555
557
  OPTIONAL MATCH (caller:Function)-[:CALLS]->(func)
556
558
  WHERE caller.is_dependency = false
@@ -566,7 +568,15 @@ class CodeFinder:
566
568
  file.name as file_name
567
569
  ORDER BY func.path, func.line_number
568
570
  LIMIT 50
569
- """, exclude_decorated_with=exclude_decorated_with, repo_path=repo_path)
571
+ """
572
+
573
+ params = {}
574
+ if repo_path:
575
+ params["repo_path"] = repo_path
576
+ if exclude_decorated_with:
577
+ params["exclude_decorated_with"] = exclude_decorated_with
578
+
579
+ result = session.run(query, **params)
570
580
 
571
581
  return {
572
582
  "potentially_unused_functions": result.data(),
@@ -703,8 +703,7 @@ class GraphBuilder:
703
703
  'args': call.get('args', []),
704
704
  'full_call_name': call.get('full_name', called_name)
705
705
  }
706
-
707
- # Try Function caller -> Function callee
706
+ # Try Function caller -> Function callee
708
707
  if not self._safe_run_create(session, """
709
708
  OPTIONAL MATCH (caller:Function {name: $caller_name, path: $caller_file_path})
710
709
  OPTIONAL MATCH (called:Function {name: $called_name, path: $called_file_path})
@@ -713,51 +712,73 @@ class GraphBuilder:
713
712
  MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
714
713
  RETURN count(*) as created
715
714
  """, call_params):
716
-
717
- # Try Function caller -> Class callee (with __init__ resolution)
715
+
716
+ # Try Function caller -> Class.__init__ / constructor
718
717
  if not self._safe_run_create(session, """
719
718
  OPTIONAL MATCH (caller:Function {name: $caller_name, path: $caller_file_path})
720
719
  OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
721
720
  OPTIONAL MATCH (called)-[:CONTAINS]->(init:Function)
722
721
  WHERE init.name IN ["__init__", "constructor"]
723
- WITH caller, COALESCE(init, called) as final_target
724
- WHERE caller IS NOT NULL AND final_target IS NOT NULL
725
- MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(final_target)
722
+ WITH caller, init
723
+ WHERE caller IS NOT NULL AND init IS NOT NULL
724
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(init)
726
725
  RETURN count(*) as created
727
726
  """, call_params):
728
-
729
- # Try Class caller -> Function callee
727
+ # No __init__ found - link directly to the Class node
728
+ self._safe_run_create(session, """
729
+ OPTIONAL MATCH (caller:Function {name: $caller_name, path: $caller_file_path})
730
+ OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
731
+ WITH caller, called
732
+ WHERE caller IS NOT NULL AND called IS NOT NULL
733
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
734
+ RETURN count(*) as created
735
+ """, call_params)
736
+
737
+ # Try Class caller -> Function callee
738
+ if not self._safe_run_create(session, """
739
+ OPTIONAL MATCH (caller:Class {name: $caller_name, path: $caller_file_path})
740
+ OPTIONAL MATCH (called:Function {name: $called_name, path: $called_file_path})
741
+ WITH caller, called
742
+ WHERE caller IS NOT NULL AND called IS NOT NULL
743
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
744
+ RETURN count(*) as created
745
+ """, call_params):
746
+
747
+ # Try Class caller -> Class.__init__ / constructor
748
+ if not self._safe_run_create(session, """
749
+ OPTIONAL MATCH (caller:Class {name: $caller_name, path: $caller_file_path})
750
+ OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
751
+ OPTIONAL MATCH (called)-[:CONTAINS]->(init:Function)
752
+ WHERE init.name IN ["__init__", "constructor"]
753
+ WITH caller, init
754
+ WHERE caller IS NOT NULL AND init IS NOT NULL
755
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(init)
756
+ RETURN count(*) as created
757
+ """, call_params):
758
+ # No __init__ - link directly to the Class node
730
759
  if not self._safe_run_create(session, """
731
760
  OPTIONAL MATCH (caller:Class {name: $caller_name, path: $caller_file_path})
732
- OPTIONAL MATCH (called:Function {name: $called_name, path: $called_file_path})
761
+ OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
733
762
  WITH caller, called
734
763
  WHERE caller IS NOT NULL AND called IS NOT NULL
735
764
  MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
736
765
  RETURN count(*) as created
737
766
  """, call_params):
738
-
739
- # Try Class caller -> Class callee
767
+ # Fallback: Relaxed Global Search (Function caller -> any Function callee)
740
768
  if not self._safe_run_create(session, """
741
- OPTIONAL MATCH (caller:Class {name: $caller_name, path: $caller_file_path})
742
- OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
743
- OPTIONAL MATCH (called)-[:CONTAINS]->(init:Function)
744
- WHERE init.name IN ["__init__", "constructor"]
745
- WITH caller, COALESCE(init, called) as final_target
746
- WHERE caller IS NOT NULL AND final_target IS NOT NULL
747
- MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(final_target)
748
- RETURN count(*) as created
769
+ OPTIONAL MATCH (caller:Function {name: $caller_name, path: $caller_file_path})
770
+ OPTIONAL MATCH (called:Function {name: $called_name})
771
+ WITH caller, called
772
+ WHERE caller IS NOT NULL AND called IS NOT NULL
773
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
749
774
  """, call_params):
750
-
751
- # Fallback: Relaxed Global Search (Caller: Function/Class -> Callee: Function)
752
- # Used when path resolution failed or was ambiguous
753
- self._safe_run_create(session, """
754
- OPTIONAL MATCH (caller:Function {name: $caller_name, path: $caller_file_path})
755
- OPTIONAL MATCH (callerClass:Class {name: $caller_name, path: $caller_file_path})
756
- WITH COALESCE(caller, callerClass) as final_caller
775
+ # Fallback: Class caller -> any Function callee
776
+ self._safe_run_create(session, """
777
+ OPTIONAL MATCH (caller:Class {name: $caller_name, path: $caller_file_path})
757
778
  OPTIONAL MATCH (called:Function {name: $called_name})
758
- WITH final_caller, called
759
- WHERE final_caller IS NOT NULL AND called IS NOT NULL
760
- MERGE (final_caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
779
+ WITH caller, called
780
+ WHERE caller IS NOT NULL AND called IS NOT NULL
781
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
761
782
  """, call_params)
762
783
  else:
763
784
  # File-level calls: Try Function first, then Class
@@ -769,7 +790,7 @@ class GraphBuilder:
769
790
  'args': call.get('args', []),
770
791
  'full_call_name': call.get('full_name', called_name)
771
792
  }
772
-
793
+
773
794
  if not self._safe_run_create(session, """
774
795
  OPTIONAL MATCH (caller:File {path: $caller_file_path})
775
796
  OPTIONAL MATCH (called:Function {name: $called_name, path: $called_file_path})
@@ -778,26 +799,35 @@ class GraphBuilder:
778
799
  MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
779
800
  RETURN count(*) as created
780
801
  """, call_params):
781
-
802
+
803
+ # Try File caller -> Class.__init__ / constructor
782
804
  if not self._safe_run_create(session, """
783
805
  OPTIONAL MATCH (caller:File {path: $caller_file_path})
784
806
  OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
785
807
  OPTIONAL MATCH (called)-[:CONTAINS]->(init:Function)
786
808
  WHERE init.name IN ["__init__", "constructor"]
787
- WITH caller, COALESCE(init, called) as final_target
788
- WHERE caller IS NOT NULL AND final_target IS NOT NULL
789
- MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(final_target)
809
+ WITH caller, init
810
+ WHERE caller IS NOT NULL AND init IS NOT NULL
811
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(init)
790
812
  RETURN count(*) as created
791
813
  """, call_params):
792
-
793
- # Fallback: Relaxed Global Search (Caller: File -> Callee: Function)
794
- self._safe_run_create(session, """
814
+ # No __init__ - link directly to the Class node
815
+ if not self._safe_run_create(session, """
795
816
  OPTIONAL MATCH (caller:File {path: $caller_file_path})
796
- OPTIONAL MATCH (called:Function {name: $called_name})
817
+ OPTIONAL MATCH (called:Class {name: $called_name, path: $called_file_path})
797
818
  WITH caller, called
798
819
  WHERE caller IS NOT NULL AND called IS NOT NULL
799
820
  MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
800
- """, call_params)
821
+ RETURN count(*) as created
822
+ """, call_params):
823
+ # Fallback: Relaxed Global Search (File -> any Function)
824
+ self._safe_run_create(session, """
825
+ OPTIONAL MATCH (caller:File {path: $caller_file_path})
826
+ OPTIONAL MATCH (called:Function {name: $called_name})
827
+ WITH caller, called
828
+ WHERE caller IS NOT NULL AND called IS NOT NULL
829
+ MERGE (caller)-[:CALLS {line_number: $line_number, args: $args, full_call_name: $full_call_name}]->(called)
830
+ """, call_params)
801
831
 
802
832
  def _create_all_function_calls(self, all_file_data: list[Dict], imports_map: dict):
803
833
  """Create CALLS relationships for all functions after all files have been processed."""
@@ -1278,20 +1308,22 @@ class GraphBuilder:
1278
1308
 
1279
1309
  # Search for .cgcignore upwards
1280
1310
  cgcignore_path = None
1281
- ignore_root = path.resolve()
1282
-
1311
+ # ignore_root is always the indexed path itself so that file paths
1312
+ # are matched relative to the project being indexed. A parent
1313
+ # .cgcignore is still loaded (for monorepo support), but anchoring
1314
+ # to its directory would make patterns like "website/" incorrectly
1315
+ # filter out every file when indexing the website sub-directory.
1316
+ ignore_root = path.resolve() if path.is_dir() else path.resolve().parent
1317
+
1283
1318
  # Start search from path (or parent if path is file)
1284
- curr = path.resolve()
1285
- if not curr.is_dir():
1286
- curr = curr.parent
1319
+ curr = ignore_root
1287
1320
 
1288
1321
  # Walk up looking for .cgcignore
1289
1322
  while True:
1290
1323
  candidate = curr / ".cgcignore"
1291
1324
  if candidate.exists():
1292
1325
  cgcignore_path = candidate
1293
- ignore_root = curr
1294
- debug_log(f"Found .cgcignore at {ignore_root}")
1326
+ debug_log(f"Found .cgcignore at {curr} (filtering relative to {ignore_root})")
1295
1327
  break
1296
1328
  if curr.parent == curr: # Root hit
1297
1329
  break
@@ -37,14 +37,35 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
37
37
  raise HTTPException(status_code=500, detail="Database not initialized")
38
38
 
39
39
  def get_eid(element):
40
- if not element: return None
40
+ if element is None: return None
41
41
  if isinstance(element, (int, str)):
42
42
  return str(element)
43
- # Try various ways to get ID (Neo4j, FalkorDB, etc.)
43
+
44
+ # If element is a dict (like Neo4j returned items or KuzuDB node/rel dicts)
45
+ if isinstance(element, dict):
46
+ # KuzuDB _src / _dst are directly {'offset': X, 'table': Y}
47
+ if 'offset' in element and 'table' in element:
48
+ return f"{element.get('table')}_{element.get('offset')}"
49
+
50
+ for key in ['_id', 'id', 'element_id']:
51
+ if key in element:
52
+ val = element[key]
53
+ if val is not None:
54
+ # KuzuDB returns dict IDs like {'offset': 1, 'table': 0} inside nodes
55
+ if isinstance(val, dict):
56
+ return f"{val.get('table', 0)}_{val.get('offset', 0)}"
57
+ return str(val)
58
+ return str(id(element))
59
+
60
+ # Try various ways to get ID (Neo4j, FalkorDB, etc. objects)
44
61
  for attr in ['element_id', 'id', '_id']:
45
62
  if hasattr(element, attr):
46
63
  val = getattr(element, attr)
47
- if val is not None: return str(val)
64
+ if val is not None:
65
+ # KuzuDB objects if any
66
+ if isinstance(val, dict):
67
+ return f"{val.get('table', 0)}_{val.get('offset', 0)}"
68
+ return str(val)
48
69
  return str(id(element))
49
70
 
50
71
  try:
@@ -52,8 +73,6 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
52
73
  edges = []
53
74
 
54
75
  print(f"DEBUG: Starting get_graph with repo_path={repo_path}", flush=True)
55
- nodes_dict = {}
56
- edges = []
57
76
 
58
77
  with db_manager.get_driver().session() as session:
59
78
  if cypher_query:
@@ -73,7 +92,7 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
73
92
  result = session.run(query, repo_path=repo_path)
74
93
  else:
75
94
  print("DEBUG: Fetching global graph", flush=True)
76
- query = "MATCH (n) OPTIONAL MATCH (n)-[rel]->(m) RETURN n, rel, m LIMIT 5000"
95
+ query = "MATCH (n) OPTIONAL MATCH (n)-[rel]->(m) RETURN n, rel, m LIMIT 50000"
77
96
  result = session.run(query)
78
97
 
79
98
  record_count = 0
@@ -88,27 +107,37 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
88
107
  if eid and eid not in nodes_dict:
89
108
  # Extract labels
90
109
  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
110
+ if isinstance(node, dict):
111
+ # KuzuDB node label is under '_label'
112
+ if '_label' in node:
113
+ labels = [node['_label']]
114
+ elif 'label' in node:
115
+ labels = [node['label']]
116
+ else:
117
+ for label_attr in ['_labels', 'labels']:
118
+ if hasattr(node, label_attr):
119
+ attr_val = getattr(node, label_attr)
120
+ if attr_val:
121
+ labels = list(attr_val)
122
+ break
97
123
 
98
124
  # Extract properties
99
125
  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
126
+ if isinstance(node, dict):
127
+ props = {k: v for k, v in node.items() if not k.startswith('_')}
128
+ else:
129
+ for prop_attr in ['properties', '_properties']:
130
+ if hasattr(node, prop_attr):
131
+ attr_val = getattr(node, prop_attr)
132
+ if attr_val:
133
+ props = dict(attr_val)
134
+ break
135
+
136
+ # Fallback if props still empty but node acts like dict
137
+ if not props and hasattr(node, 'items'):
138
+ try:
139
+ props = dict(node.items())
140
+ except: pass
112
141
 
113
142
  # Extract name/label for frontend
114
143
  # Prefer 'name' property, fallback to 'label', then 'path' or 'Unknown'
@@ -129,37 +158,50 @@ async def get_graph(repo_path: Optional[str] = None, cypher_query: Optional[str]
129
158
 
130
159
  try:
131
160
  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
161
+ if rel is not None:
162
+ # One-shot debug: print type and keys/attrs of first rel
163
+ if not edges:
164
+ if isinstance(rel, dict):
165
+ print(f"DEBUG rel (dict): keys={list(rel.keys())}", file=sys.stderr, flush=True)
166
+ else:
167
+ print(f"DEBUG rel (obj): type={type(rel).__name__}, attrs={[a for a in dir(rel) if not a.startswith('__')]}", file=sys.stderr, flush=True)
168
+
169
+ # FalkorDB / KuzuDB may return rels as dicts OR objects
170
+ if isinstance(rel, dict):
171
+ rid = get_eid(rel)
172
+ # KuzuDB uses _src and _dst, FalkorDB uses src_node/dest_node
173
+ src = rel.get('_src', rel.get('src_node'))
174
+ dst = rel.get('_dst', rel.get('dest_node'))
175
+
176
+ source = get_eid(src) if src is not None else None
177
+ target = get_eid(dst) if dst is not None else None
178
+ rel_type = str(rel.get('_label', rel.get('relation', rel.get('type', 'RELATED')))).upper()
179
+ else:
180
+ rid = get_eid(rel)
181
+ start_node = None
182
+ end_node = None
183
+ for src_attr in ['start_node', 'src_node', '_src_node']:
184
+ if hasattr(rel, src_attr):
185
+ start_node = getattr(rel, src_attr)
186
+ break
187
+ for dest_attr in ['end_node', 'dest_node', '_dest_node']:
188
+ if hasattr(rel, dest_attr):
189
+ end_node = getattr(rel, dest_attr)
190
+ break
191
+ source = get_eid(start_node) if start_node is not None else None
192
+ target = get_eid(end_node) if end_node is not None else None
152
193
  rel_type = "RELATED"
153
194
  for rel_attr in ['type', 'relation', '_relation']:
154
195
  if hasattr(rel, rel_attr):
155
- rel_type = getattr(rel, rel_attr)
196
+ rel_type = str(getattr(rel, rel_attr)).upper()
156
197
  break
157
-
198
+
199
+ if source and target:
158
200
  edges.append({
159
201
  "id": rid,
160
202
  "source": source,
161
203
  "target": target,
162
- "type": str(rel_type).upper()
204
+ "type": rel_type
163
205
  })
164
206
  except Exception as e:
165
207
  print(f"DEBUG: Error parsing relationship: {e}", file=sys.stderr, flush=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.3.4
3
+ Version: 0.3.7
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
@@ -162,7 +162,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
162
162
  ---
163
163
 
164
164
  ## Project Details
165
- - **Version:** 0.3.4
165
+ - **Version:** 0.3.7
166
166
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
167
167
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
168
168
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -249,7 +249,7 @@ _If you’re using CodeGraphContext in your project, feel free to open a PR and
249
249
  - `stdlibs>=2023.11.18`
250
250
  - `typer[all]>=0.9.0`
251
251
  - `rich>=13.7.0`
252
- - `inquirerpy>=0.3.4`
252
+ - `inquirerpy>=0.3.7`
253
253
  - `python-dotenv>=1.0.0`
254
254
  - `tree-sitter>=0.21.0`
255
255
  - `tree-sitter-language-pack>=0.6.0`