codegraphcontext 0.1.41__tar.gz → 0.2.1__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 (76) hide show
  1. {codegraphcontext-0.1.41/src/codegraphcontext.egg-info → codegraphcontext-0.2.1}/PKG-INFO +9 -4
  2. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/README.md +8 -3
  3. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/pyproject.toml +1 -1
  4. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/config_manager.py +2 -2
  5. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/main.py +1 -1
  6. codegraphcontext-0.2.1/src/codegraphcontext/core/bundle_registry.py +171 -0
  7. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/cgc_bundle.py +70 -2
  8. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/database.py +8 -3
  9. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/prompts.py +2 -0
  10. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tool_definitions.py +1 -1
  11. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/code_finder.py +2 -1
  12. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/graph_builder.py +6 -1
  13. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/handlers/management_handlers.py +49 -10
  14. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1/src/codegraphcontext.egg-info}/PKG-INFO +9 -4
  15. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext.egg-info/SOURCES.txt +1 -0
  16. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/LICENSE +0 -0
  17. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/MANIFEST.in +0 -0
  18. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/setup.cfg +0 -0
  19. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/__init__.py +0 -0
  20. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/__main__.py +0 -0
  21. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/__init__.py +0 -0
  22. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/cli_helpers.py +0 -0
  23. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/registry_commands.py +0 -0
  24. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/setup_macos.py +0 -0
  25. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/setup_wizard.py +0 -0
  26. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/cli/visualizer.py +0 -0
  27. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/__init__.py +0 -0
  28. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/database_falkordb.py +0 -0
  29. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/falkor_worker.py +0 -0
  30. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/jobs.py +0 -0
  31. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/core/watcher.py +0 -0
  32. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/server.py +0 -0
  33. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/__init__.py +0 -0
  34. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
  35. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
  36. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
  37. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
  38. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
  39. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/c.py +0 -0
  40. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/cpp.py +0 -0
  41. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/csharp.py +0 -0
  42. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/go.py +0 -0
  43. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/haskell.py +0 -0
  44. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/java.py +0 -0
  45. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/javascript.py +0 -0
  46. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
  47. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/php.py +0 -0
  48. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/python.py +0 -0
  49. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/ruby.py +0 -0
  50. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/rust.py +0 -0
  51. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/scala.py +0 -0
  52. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/swift.py +0 -0
  53. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/typescript.py +0 -0
  54. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
  55. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/package_resolver.py +0 -0
  56. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
  57. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
  58. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
  59. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
  60. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
  61. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
  62. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
  63. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
  64. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
  65. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
  66. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
  67. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
  68. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
  69. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/tools/system.py +0 -0
  70. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/utils/debug_log.py +0 -0
  71. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
  72. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext/utils/visualize_graph.py +0 -0
  73. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
  74. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
  75. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/src/codegraphcontext.egg-info/requires.txt +0 -0
  76. {codegraphcontext-0.1.41 → codegraphcontext-0.2.1}/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.1.41
3
+ Version: 0.2.1
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
@@ -113,6 +113,7 @@ Dynamic: license-file
113
113
  </a>
114
114
  </p>
115
115
 
116
+
116
117
  A powerful **MCP server** and **CLI toolkit** that indexes local code into a graph database to provide context to AI assistants and developers. Use it as a standalone CLI for comprehensive code analysis or connect it to your favorite AI IDE via MCP for AI-powered code understanding.
117
118
 
118
119
  ---
@@ -128,6 +129,12 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
128
129
 
129
130
  ## ✨ Experience CGC
130
131
 
132
+
133
+ ### 👨🏻‍💻 Installation and CLI
134
+ > Install in seconds with pip and unlock a powerful CLI for code graph analysis.
135
+ ![Install and unlock the CLI instantly](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/images/install&cli.gif)
136
+
137
+
131
138
  ### 🛠️ Indexing in Seconds
132
139
  > The CLI intelligently parses your tree-sitter nodes to build the graph.
133
140
  ![Indexing using an MCP client](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/images/Indexing.gif)
@@ -139,7 +146,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
139
146
  ---
140
147
 
141
148
  ## Project Details
142
- - **Version:** 0.1.41
149
+ - **Version:** 0.2.1
143
150
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
144
151
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
145
152
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -418,8 +425,6 @@ Add the following server configuration to your client's settings file (e.g., VS
418
425
  "NEO4J_URI": "YOUR_NEO4J_URI",
419
426
  "NEO4J_USERNAME": "YOUR_NEO4J_USERNAME",
420
427
  "NEO4J_PASSWORD": "YOUR_NEO4J_PASSWORD"
421
- },
422
- "disabled": false
423
428
  },
424
429
  "disabled": false,
425
430
  "alwaysAllow": []
@@ -51,6 +51,7 @@
51
51
  </a>
52
52
  </p>
53
53
 
54
+
54
55
  A powerful **MCP server** and **CLI toolkit** that indexes local code into a graph database to provide context to AI assistants and developers. Use it as a standalone CLI for comprehensive code analysis or connect it to your favorite AI IDE via MCP for AI-powered code understanding.
55
56
 
56
57
  ---
@@ -66,6 +67,12 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
66
67
 
67
68
  ## ✨ Experience CGC
68
69
 
70
+
71
+ ### 👨🏻‍💻 Installation and CLI
72
+ > Install in seconds with pip and unlock a powerful CLI for code graph analysis.
73
+ ![Install and unlock the CLI instantly](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/images/install&cli.gif)
74
+
75
+
69
76
  ### 🛠️ Indexing in Seconds
70
77
  > The CLI intelligently parses your tree-sitter nodes to build the graph.
71
78
  ![Indexing using an MCP client](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/images/Indexing.gif)
@@ -77,7 +84,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
77
84
  ---
78
85
 
79
86
  ## Project Details
80
- - **Version:** 0.1.41
87
+ - **Version:** 0.2.1
81
88
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
82
89
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
83
90
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -356,8 +363,6 @@ Add the following server configuration to your client's settings file (e.g., VS
356
363
  "NEO4J_URI": "YOUR_NEO4J_URI",
357
364
  "NEO4J_USERNAME": "YOUR_NEO4J_USERNAME",
358
365
  "NEO4J_PASSWORD": "YOUR_NEO4J_PASSWORD"
359
- },
360
- "disabled": false
361
366
  },
362
367
  "disabled": false,
363
368
  "alwaysAllow": []
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codegraphcontext"
3
- version = "0.1.41"
3
+ version = "0.2.1"
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"
@@ -38,7 +38,7 @@ DEFAULT_CONFIG = {
38
38
  "PARALLEL_WORKERS": "4",
39
39
  "CACHE_ENABLED": "true",
40
40
  "IGNORE_DIRS": "node_modules,venv,.venv,env,.env,dist,build,target,out,.git,.idea,.vscode,__pycache__",
41
- "INDEX_SOURCE": "false",
41
+ "INDEX_SOURCE": "true",
42
42
  }
43
43
 
44
44
  # Configuration key descriptions
@@ -61,7 +61,7 @@ CONFIG_DESCRIPTIONS = {
61
61
  "PARALLEL_WORKERS": "Number of parallel indexing workers",
62
62
  "CACHE_ENABLED": "Enable caching for faster re-indexing",
63
63
  "IGNORE_DIRS": "Comma-separated list of directory names to ignore during indexing",
64
- "INDEX_SOURCE": "Store full source code in graph database (recommended false)",
64
+ "INDEX_SOURCE": "Store full source code in graph database (for faster indexing use false, for better performance use true)",
65
65
  }
66
66
 
67
67
  # Valid values for each config key
@@ -1332,7 +1332,7 @@ def find_by_content_search(
1332
1332
  results = code_finder.find_by_content(query)
1333
1333
  except Exception as e:
1334
1334
  error_msg = str(e).lower()
1335
- if 'fulltext' in error_msg or 'db.index.fulltext' in error_msg:
1335
+ if ('fulltext' in error_msg or 'db.index.fulltext' in error_msg) and "Falkor" in db_manager.__class__.__name__:
1336
1336
  console.print("\n[bold red]❌ Full-text search is not supported on FalkorDB[/bold red]\n")
1337
1337
  console.print("[yellow]💡 You have two options:[/yellow]\n")
1338
1338
  console.print(" 1. [cyan]Switch to Neo4j:[/cyan]")
@@ -0,0 +1,171 @@
1
+ import requests
2
+ from pathlib import Path
3
+ from typing import Optional, List, Dict, Any, Tuple
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ GITHUB_ORG = "CodeGraphContext"
9
+ GITHUB_REPO = "CodeGraphContext"
10
+ REGISTRY_API_URL = f"https://api.github.com/repos/{GITHUB_ORG}/{GITHUB_REPO}/releases"
11
+ MANIFEST_URL = f"https://github.com/{GITHUB_ORG}/{GITHUB_REPO}/releases/download/on-demand-bundles/manifest.json"
12
+
13
+ class BundleRegistry:
14
+ """
15
+ Core logic for interacting with the CodeGraphContext bundle registry.
16
+ Handles fetching metadata, searching, and downloading bundles without CLI dependencies.
17
+ """
18
+
19
+ @staticmethod
20
+ def fetch_available_bundles() -> List[Dict[str, Any]]:
21
+ """
22
+ Fetch all available bundles from GitHub Releases and the on-demand manifest.
23
+ Returns a list of bundle dictionaries with metadata.
24
+ Preserves all versions - no deduplication.
25
+ """
26
+ all_bundles = []
27
+
28
+ # 1. Fetch on-demand bundles from manifest
29
+ try:
30
+ response = requests.get(MANIFEST_URL, timeout=10)
31
+ if response.status_code == 200:
32
+ manifest = response.json()
33
+ if manifest.get('bundles'):
34
+ for bundle in manifest['bundles']:
35
+ bundle['source'] = 'on-demand'
36
+ # Ensure bundle has a full_name field (with version info)
37
+ if 'bundle_name' in bundle:
38
+ # Extract full name without .cgc extension
39
+ bundle['full_name'] = bundle['bundle_name'].replace('.cgc', '')
40
+ all_bundles.append(bundle)
41
+ except Exception as e:
42
+ logger.warning(f"Could not fetch on-demand bundles from manifest: {e}")
43
+
44
+ # 2. Fetch weekly pre-indexed bundles
45
+ try:
46
+ response = requests.get(REGISTRY_API_URL, timeout=10)
47
+ if response.status_code == 200:
48
+ releases = response.json()
49
+
50
+ # Find weekly releases (bundles-YYYYMMDD pattern)
51
+ weekly_releases = [r for r in releases if r['tag_name'].startswith('bundles-') and r['tag_name'] != 'bundles-latest']
52
+
53
+ if weekly_releases:
54
+ # Get the most recent weekly release
55
+ latest_weekly = weekly_releases[0]
56
+
57
+ for asset in latest_weekly.get('assets', []):
58
+ if asset['name'].endswith('.cgc'):
59
+ # Full bundle name without extension
60
+ full_name = asset['name'].replace('.cgc', '')
61
+
62
+ # Parse bundle name
63
+ name_parts = full_name.split('-')
64
+ bundle = {
65
+ 'name': name_parts[0], # Base package name
66
+ 'full_name': full_name, # Complete name with version
67
+ 'repo': f"{name_parts[0]}/{name_parts[0]}", # Simplified
68
+ 'bundle_name': asset['name'],
69
+ 'version': name_parts[1] if len(name_parts) > 1 else 'latest',
70
+ 'commit': name_parts[2] if len(name_parts) > 2 else 'unknown',
71
+ 'size_bytes': asset.get('size', 0),
72
+ 'size': f"{asset['size'] / 1024 / 1024:.1f}MB",
73
+ 'download_url': asset['browser_download_url'],
74
+ 'generated_at': asset['updated_at'],
75
+ 'source': 'weekly'
76
+ }
77
+ all_bundles.append(bundle)
78
+ except Exception as e:
79
+ logger.warning(f"Could not fetch weekly bundles from GitHub API: {e}")
80
+
81
+ # Normalize all bundles to have required fields
82
+ for bundle in all_bundles:
83
+ # Ensure 'name' field exists (base package name)
84
+ if 'name' not in bundle:
85
+ repo = bundle.get('repo', '')
86
+ if '/' in repo:
87
+ bundle['name'] = repo.split('/')[-1]
88
+ else:
89
+ # Extract from full_name or bundle_name
90
+ full_name = bundle.get('full_name', bundle.get('bundle_name', 'unknown'))
91
+ bundle['name'] = full_name.split('-')[0]
92
+
93
+ # Ensure 'full_name' exists
94
+ if 'full_name' not in bundle:
95
+ bundle['full_name'] = bundle.get('bundle_name', bundle.get('name', 'unknown')).replace('.cgc', '')
96
+
97
+ return all_bundles
98
+
99
+ @staticmethod
100
+ def find_bundle_download_info(name: str) -> Tuple[Optional[str], Optional[Dict[str, Any]], str]:
101
+ """
102
+ Find a download URL and metadata for a bundle by name.
103
+
104
+ Strategies:
105
+ 1. Exact match on full_name (e.g., 'flask-main-abc123')
106
+ 2. Match on base name (e.g., 'flask') - returns most recent version
107
+
108
+ Returns:
109
+ (download_url, bundle_metadata, error_message)
110
+ """
111
+ bundles = BundleRegistry.fetch_available_bundles()
112
+
113
+ if not bundles:
114
+ return None, None, "Could not fetch bundle registry."
115
+
116
+ name_lower = name.lower()
117
+
118
+ # Strategy 1: Exact match on full_name
119
+ for b in bundles:
120
+ if b.get('full_name', '').lower() == name_lower:
121
+ url = b.get('download_url')
122
+ if url:
123
+ return url, b, ""
124
+ return None, b, f"No download URL found for bundle '{name}'"
125
+
126
+ # Strategy 2: Match base package name (most recent)
127
+ matching_bundles = []
128
+ for b in bundles:
129
+ if b.get('name', '').lower() == name_lower:
130
+ matching_bundles.append(b)
131
+
132
+ if matching_bundles:
133
+ # Sort by timestamp (newest first)
134
+ matching_bundles.sort(key=lambda x: x.get('generated_at', ''), reverse=True)
135
+ bundle = matching_bundles[0]
136
+ url = bundle.get('download_url')
137
+ if url:
138
+ return url, bundle, ""
139
+ return None, bundle, f"No download URL found for bundle '{name}'"
140
+
141
+ return None, None, f"Bundle '{name}' not found in registry."
142
+
143
+ @staticmethod
144
+ def download_file(url: str, output_path: Path, progress_callback=None) -> bool:
145
+ """
146
+ Download a file from a URL to a local path.
147
+
148
+ Args:
149
+ url: The URL to download from
150
+ output_path: Local path to save the file
151
+ progress_callback: Optional callable(chunk_size) to report progress
152
+
153
+ Returns:
154
+ True if successful, raises exception otherwise
155
+ """
156
+ try:
157
+ response = requests.get(url, stream=True, timeout=30)
158
+ response.raise_for_status()
159
+
160
+ with open(output_path, 'wb') as f:
161
+ for chunk in response.iter_content(chunk_size=8192):
162
+ if chunk:
163
+ f.write(chunk)
164
+ if progress_callback:
165
+ progress_callback(len(chunk))
166
+ return True
167
+ except Exception as e:
168
+ # Clean up partial file
169
+ if output_path.exists():
170
+ output_path.unlink()
171
+ raise e
@@ -179,10 +179,21 @@ class CGCBundle:
179
179
  info_logger(f"Loading bundle: {metadata.get('repo', 'unknown')}")
180
180
  info_logger(f"Bundle version: {metadata.get('cgc_version', 'unknown')}")
181
181
 
182
- # Step 4: Clear existing data if requested
182
+ # Step 4: Handle existing data
183
+ repo_name = metadata.get('repo', 'unknown')
184
+ repo_path = metadata.get('repo_path')
185
+
183
186
  if clear_existing:
184
- info_logger("Clearing existing graph data...")
187
+ # User explicitly wants to clear - remove everything
188
+ info_logger("Clearing all existing graph data...")
185
189
  self._clear_graph()
190
+ else:
191
+ # Check if this repository already exists (only when NOT clearing)
192
+ existing_repo = self._check_existing_repository(repo_name, repo_path)
193
+
194
+ if existing_repo:
195
+ return False, f"Repository '{repo_name}' already exists in the database. Use clear_existing=True to replace it."
196
+
186
197
 
187
198
  # Step 5: Create schema
188
199
  info_logger("Creating schema...")
@@ -584,6 +595,63 @@ cgc import <bundle-file>.cgc
584
595
 
585
596
  return True, "Valid bundle"
586
597
 
598
+ def _check_existing_repository(self, repo_name: str, repo_path: Optional[str]) -> bool:
599
+ """Check if a repository already exists in the database."""
600
+ with self.db_manager.get_driver().session() as session:
601
+ # Try to find by name first
602
+ result = session.run(
603
+ "MATCH (r:Repository {name: $name}) RETURN r LIMIT 1",
604
+ name=repo_name
605
+ )
606
+ if result.single():
607
+ return True
608
+
609
+ # If repo_path is provided, also check by path
610
+ if repo_path:
611
+ result = session.run(
612
+ "MATCH (r:Repository {path: $path}) RETURN r LIMIT 1",
613
+ path=repo_path
614
+ )
615
+ if result.single():
616
+ return True
617
+
618
+ return False
619
+
620
+ def _delete_repository(self, repo_identifier: str):
621
+ """Delete a specific repository and all its related nodes from the graph."""
622
+ with self.db_manager.get_driver().session() as session:
623
+ # First, try to find the repository by name or path
624
+ result = session.run("""
625
+ MATCH (r:Repository)
626
+ WHERE r.name = $identifier OR r.path = $identifier
627
+ RETURN r.path as path
628
+ LIMIT 1
629
+ """, identifier=repo_identifier)
630
+
631
+ record = result.single()
632
+ if not record:
633
+ warning_logger(f"Repository '{repo_identifier}' not found for deletion")
634
+ return
635
+
636
+ repo_path = record['path']
637
+
638
+ # Delete all nodes that belong to this repository
639
+ # Files, Functions, Classes, Modules all have paths that start with repo_path
640
+ session.run("""
641
+ MATCH (n)
642
+ WHERE n.path STARTS WITH $repo_path
643
+ DETACH DELETE n
644
+ """, repo_path=repo_path)
645
+
646
+ # Delete the repository node itself
647
+ session.run("""
648
+ MATCH (r:Repository)
649
+ WHERE r.path = $repo_path
650
+ DELETE r
651
+ """, repo_path=repo_path)
652
+
653
+ info_logger(f"Deleted repository: {repo_identifier}")
654
+
587
655
  def _clear_graph(self):
588
656
  """Clear all nodes and relationships from the graph."""
589
657
  with self.db_manager.get_driver().session() as session:
@@ -136,7 +136,8 @@ class DatabaseManager:
136
136
  Tuple[bool, Optional[str]]: (is_valid, error_message)
137
137
  """
138
138
  # Validate URI format
139
- uri_pattern = r'^(neo4j|neo4j\+s|neo4j\+ssc|bolt|bolt\+s|bolt\+ssc)://[^:]+:\d+$'
139
+ # Modified regex to make port optional "(:\\d+)?"
140
+ uri_pattern = r'^(neo4j|neo4j\+s|neo4j\+ssc|bolt|bolt\+s|bolt\+ssc)://[^:]+(:\d+)?$'
140
141
  if not re.match(uri_pattern, uri):
141
142
  return False, (
142
143
  "Invalid Neo4j URI format.\n"
@@ -177,8 +178,12 @@ class DatabaseManager:
177
178
  try:
178
179
  # Extract host and port from URI
179
180
  host_port = uri.split('://')[1]
180
- host = host_port.split(':')[0]
181
- port = int(host_port.split(':')[1])
181
+ if ':' in host_port:
182
+ host = host_port.split(':')[0]
183
+ port = int(host_port.split(':')[1])
184
+ else:
185
+ host = host_port
186
+ port = 7687 # Default Neo4j port
182
187
 
183
188
  # Test socket connection
184
189
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -76,6 +76,7 @@ You are an expert AI pair programmer. Your primary goal is to help a developer u
76
76
  * `cyclomatic_complexity` (int)
77
77
  * `decorators` (list)
78
78
  * `lang` (string)
79
+ * `source` (string, the full source code of the function)
79
80
  * `is_dependency` (boolean)
80
81
  * **`Class`**
81
82
  * `name` (string)
@@ -85,6 +86,7 @@ You are an expert AI pair programmer. Your primary goal is to help a developer u
85
86
  * `bases` (list)
86
87
  * `decorators` (list)
87
88
  * `lang` (string)
89
+ * `source` (string, the full source code of the class)
88
90
  * `is_dependency` (boolean)
89
91
 
90
92
  ### Relationships
@@ -59,7 +59,7 @@ TOOLS = {
59
59
  },
60
60
  "execute_cypher_query": {
61
61
  "name": "execute_cypher_query",
62
- "description": "Fallback tool to run a direct, read-only Cypher query against the code graph. Use this for complex questions not covered by other tools. The graph contains nodes representing code structures and relationships between them. **Schema Overview:**\n- **Nodes:** `Repository`, `File`, `Module`, `Class`, `Function`.\n- **Properties:** Nodes have properties like `name`, `path`, `cyclomatic_complexity` (on Function nodes), and `code`.\n- **Relationships:** `CONTAINS` (e.g., File-[:CONTAINS]->Function), `CALLS` (Function-[:CALLS]->Function or File-[:CALLS]->Function), `IMPORTS` (File-[:IMPORTS]->Module), `INHERITS` (Class-[:INHERITS]->Class).",
62
+ "description": "Fallback tool to run a direct, read-only Cypher query against the code graph. Use this for complex questions not covered by other tools. The graph contains nodes representing code structures and relationships between them. **Schema Overview:**\n- **Nodes:** `Repository`, `File`, `Module`, `Class`, `Function`.\n- **Properties:** Nodes have properties like `name`, `path`, `cyclomatic_complexity` (on Function nodes), and `source`.\n- **Relationships:** `CONTAINS` (e.g., File-[:CONTAINS]->Function), `CALLS` (Function-[:CALLS]->Function or File-[:CALLS]->Function), `IMPORTS` (File-[:IMPORTS]->Module), `INHERITS` (Class-[:INHERITS]->Class).",
63
63
  "inputSchema": {
64
64
  "type": "object",
65
65
  "properties": { "cypher_query": {"type": "string", "description": "The read-only Cypher query to execute."} },
@@ -84,13 +84,14 @@ class CodeFinder:
84
84
  CALL db.index.fulltext.queryNodes("code_search_index", $search_term) YIELD node, score
85
85
  WITH node, score
86
86
  WHERE node:Function OR node:Class OR node:Variable
87
+ MATCH (node)<-[:CONTAINS]-(f:File)
87
88
  RETURN
88
89
  CASE
89
90
  WHEN node:Function THEN 'function'
90
91
  WHEN node:Class THEN 'class'
91
92
  ELSE 'variable'
92
93
  END as type,
93
- node.name as name, node.path as path,
94
+ node.name as name, f.path as path,
94
95
  node.line_number as line_number, node.source as source,
95
96
  node.docstring as docstring, node.is_dependency as is_dependency
96
97
  ORDER BY score DESC
@@ -277,7 +277,12 @@ class GraphBuilder:
277
277
  """, path=file_path_str, name=file_name, relative_path=relative_path, is_dependency=is_dependency)
278
278
 
279
279
  file_path_obj = Path(file_path_str)
280
- repo_path_obj = Path(repo_result['path'])
280
+ if repo_result:
281
+ repo_path_obj = Path(repo_result['path'])
282
+ else:
283
+ # Fallback to the path we queried for
284
+ warning_logger(f"Repository node not found for {file_data['repo_path']} during indexing of {file_name}. Using original path.")
285
+ repo_path_obj = Path(file_data['repo_path']).resolve()
281
286
 
282
287
  relative_path_to_file = file_path_obj.relative_to(repo_path_obj)
283
288
 
@@ -116,7 +116,8 @@ def list_jobs(job_manager: JobManager) -> Dict[str, Any]:
116
116
  def load_bundle(code_finder: CodeFinder, **args) -> Dict[str, Any]:
117
117
  """Tool to load a .cgc bundle into the database."""
118
118
  from pathlib import Path
119
- from ...cli.registry_commands import load_bundle_command
119
+ from ...core.bundle_registry import BundleRegistry
120
+ from ...core.cgc_bundle import CGCBundle
120
121
 
121
122
  bundle_name = args.get("bundle_name")
122
123
  clear_existing = args.get("clear_existing", False)
@@ -127,22 +128,60 @@ def load_bundle(code_finder: CodeFinder, **args) -> Dict[str, Any]:
127
128
  try:
128
129
  debug_log(f"Loading bundle: {bundle_name}")
129
130
 
130
- # Use the existing load_bundle_command from CLI
131
- # This handles both local files and auto-download from registry
132
- success, message, stats = load_bundle_command(
133
- bundle_name=bundle_name,
131
+ # Check if bundle exists locally
132
+ bundle_path = Path(bundle_name)
133
+
134
+ # If it doesn't exist as-is, try with .cgc extension
135
+ if not bundle_path.exists() and not str(bundle_name).endswith('.cgc'):
136
+ bundle_path = Path(f"{bundle_name}.cgc")
137
+
138
+ if not bundle_path.exists():
139
+ # Try to download from registry
140
+ debug_log(f"Bundle {bundle_name} not found locally, checking registry...")
141
+ download_url, bundle_meta, error = BundleRegistry.find_bundle_download_info(bundle_name)
142
+
143
+ if not download_url:
144
+ return {"error": f"Bundle not found locally or in registry: {bundle_name}. {error}"}
145
+
146
+ # Determine output filename from metadata
147
+ filename = bundle_meta.get('bundle_name', f"{bundle_name}.cgc")
148
+ # Save to current working directory
149
+ target_path = Path.cwd() / filename
150
+
151
+ debug_log(f"Downloading bundle to {target_path}...")
152
+ try:
153
+ BundleRegistry.download_file(download_url, target_path)
154
+ bundle_path = target_path
155
+ debug_log(f"Successfully downloaded to {bundle_path}")
156
+ except Exception as e:
157
+ return {"error": f"Failed to download bundle: {str(e)}"}
158
+
159
+ # Verify the downloaded file exists
160
+ if not bundle_path.exists():
161
+ return {"error": f"Download completed but file not found at {bundle_path}"}
162
+
163
+ # Load the bundle using CGCBundle core class
164
+ bundle = CGCBundle(code_finder.db_manager)
165
+ success, message = bundle.import_from_bundle(
166
+ bundle_path=bundle_path,
134
167
  clear_existing=clear_existing
135
168
  )
136
169
 
137
170
  if success:
171
+ stats = {}
172
+ # Parse simple stats from message if possible, or just return success
173
+ if "Nodes:" in message:
174
+ # Best effort parsing, not critical
175
+ pass
176
+
138
177
  return {
139
178
  "success": True,
140
179
  "message": message,
141
180
  "stats": stats
142
181
  }
143
182
  else:
144
- return {"error": message}
145
-
183
+ return {"error": message}
184
+
146
185
  except Exception as e:
147
186
  debug_log(f"Error loading bundle: {str(e)}")
148
187
  return {"error": f"Failed to load bundle: {str(e)}"}
@@ -150,7 +189,7 @@ def load_bundle(code_finder: CodeFinder, **args) -> Dict[str, Any]:
150
189
 
151
190
  def search_registry_bundles(code_finder: CodeFinder, **args) -> Dict[str, Any]:
152
191
  """Tool to search for bundles in the registry."""
153
- from ...cli.registry_commands import fetch_available_bundles
192
+ from ...core.bundle_registry import BundleRegistry
154
193
 
155
194
  query = args.get("query", "").lower()
156
195
  unique_only = args.get("unique_only", False)
@@ -158,8 +197,8 @@ def search_registry_bundles(code_finder: CodeFinder, **args) -> Dict[str, Any]:
158
197
  try:
159
198
  debug_log(f"Searching registry for: {query}")
160
199
 
161
- # Fetch all bundles from registry
162
- bundles = fetch_available_bundles()
200
+ # Fetch directly from core registry
201
+ bundles = BundleRegistry.fetch_available_bundles()
163
202
 
164
203
  if not bundles:
165
204
  return {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.1.41
3
+ Version: 0.2.1
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
@@ -113,6 +113,7 @@ Dynamic: license-file
113
113
  </a>
114
114
  </p>
115
115
 
116
+
116
117
  A powerful **MCP server** and **CLI toolkit** that indexes local code into a graph database to provide context to AI assistants and developers. Use it as a standalone CLI for comprehensive code analysis or connect it to your favorite AI IDE via MCP for AI-powered code understanding.
117
118
 
118
119
  ---
@@ -128,6 +129,12 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
128
129
 
129
130
  ## ✨ Experience CGC
130
131
 
132
+
133
+ ### 👨🏻‍💻 Installation and CLI
134
+ > Install in seconds with pip and unlock a powerful CLI for code graph analysis.
135
+ ![Install and unlock the CLI instantly](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/images/install&cli.gif)
136
+
137
+
131
138
  ### 🛠️ Indexing in Seconds
132
139
  > The CLI intelligently parses your tree-sitter nodes to build the graph.
133
140
  ![Indexing using an MCP client](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/images/Indexing.gif)
@@ -139,7 +146,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
139
146
  ---
140
147
 
141
148
  ## Project Details
142
- - **Version:** 0.1.41
149
+ - **Version:** 0.2.1
143
150
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
144
151
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
145
152
  - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
@@ -418,8 +425,6 @@ Add the following server configuration to your client's settings file (e.g., VS
418
425
  "NEO4J_URI": "YOUR_NEO4J_URI",
419
426
  "NEO4J_USERNAME": "YOUR_NEO4J_USERNAME",
420
427
  "NEO4J_PASSWORD": "YOUR_NEO4J_PASSWORD"
421
- },
422
- "disabled": false
423
428
  },
424
429
  "disabled": false,
425
430
  "alwaysAllow": []
@@ -22,6 +22,7 @@ src/codegraphcontext/cli/setup_macos.py
22
22
  src/codegraphcontext/cli/setup_wizard.py
23
23
  src/codegraphcontext/cli/visualizer.py
24
24
  src/codegraphcontext/core/__init__.py
25
+ src/codegraphcontext/core/bundle_registry.py
25
26
  src/codegraphcontext/core/cgc_bundle.py
26
27
  src/codegraphcontext/core/database.py
27
28
  src/codegraphcontext/core/database_falkordb.py