codegraphcontext 0.1.35__tar.gz → 0.1.36__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.
- {codegraphcontext-0.1.35/src/codegraphcontext.egg-info → codegraphcontext-0.1.36}/PKG-INFO +2 -2
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/README.md +1 -1
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/pyproject.toml +1 -1
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/registry_commands.py +74 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/server.py +14 -1
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tool_definitions.py +33 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/graph_builder.py +4 -0
- codegraphcontext-0.1.36/src/codegraphcontext/tools/handlers/management_handlers.py +304 -0
- codegraphcontext-0.1.36/src/codegraphcontext/tools/languages/haskell.py +533 -0
- codegraphcontext-0.1.36/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/utils/tree_sitter_manager.py +1 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36/src/codegraphcontext.egg-info}/PKG-INFO +2 -2
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext.egg-info/SOURCES.txt +2 -0
- codegraphcontext-0.1.35/src/codegraphcontext/tools/handlers/management_handlers.py +0 -113
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/LICENSE +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/MANIFEST.in +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/setup.cfg +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/cli_helpers.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/config_manager.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/main.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/setup_wizard.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/visualizer.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/__init__.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/cgc_bundle.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/database.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/database_falkordb.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/falkor_worker.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/core/watcher.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/code_finder.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/c.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/cpp.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/csharp.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/go.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/java.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/javascript.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/php.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/python.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/ruby.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/rust.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/scala.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/swift.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/typescript.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/package_resolver.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/utils/debug_log.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/utils/visualize_graph.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext.egg-info/requires.txt +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext.egg-info/top_level.txt +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_cpp_parser.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_database_validation.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_end_to_end.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_graph_indexing.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_graph_indexing_js.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_kotlin_parser.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_swift_parser.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_tree_sitter_manager.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_typescript_parser.py +0 -0
- {codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/tests/test_visualization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codegraphcontext
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.36
|
|
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
|
|
@@ -91,7 +91,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
91
91
|

|
|
92
92
|
|
|
93
93
|
## Project Details
|
|
94
|
-
- **Version:** 0.1.
|
|
94
|
+
- **Version:** 0.1.36
|
|
95
95
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
96
96
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
97
97
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -29,7 +29,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
29
29
|

|
|
30
30
|
|
|
31
31
|
## Project Details
|
|
32
|
-
- **Version:** 0.1.
|
|
32
|
+
- **Version:** 0.1.36
|
|
33
33
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
34
34
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
35
35
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codegraphcontext"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.36"
|
|
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"
|
{codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/cli/registry_commands.py
RENAMED
|
@@ -408,3 +408,77 @@ def request_bundle(repo_url: str, wait: bool = False):
|
|
|
408
408
|
if wait:
|
|
409
409
|
console.print("\n[yellow]Note: Automatic waiting not yet implemented.[/yellow]")
|
|
410
410
|
console.print("[dim]Please check back in 5-10 minutes and use 'cgc registry download <name>'[/dim]")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def load_bundle_command(bundle_name: str, clear_existing: bool = False):
|
|
414
|
+
"""
|
|
415
|
+
Load a bundle (for MCP tool integration).
|
|
416
|
+
|
|
417
|
+
This is a wrapper around download_bundle that returns structured data
|
|
418
|
+
instead of using console output and typer.Exit.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
bundle_name: Name of the bundle to load
|
|
422
|
+
clear_existing: Whether to clear existing data before loading
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Tuple of (success: bool, message: str, stats: dict)
|
|
426
|
+
"""
|
|
427
|
+
from pathlib import Path
|
|
428
|
+
from .cli_helpers import _initialize_services
|
|
429
|
+
from ..core.cgc_bundle import CGCBundle
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
# Initialize services
|
|
433
|
+
services = _initialize_services()
|
|
434
|
+
if not all(services):
|
|
435
|
+
return (False, "Failed to initialize database services", {})
|
|
436
|
+
|
|
437
|
+
db_manager, _, _ = services
|
|
438
|
+
|
|
439
|
+
# Check if bundle exists locally
|
|
440
|
+
bundle_path = Path(bundle_name)
|
|
441
|
+
if not bundle_path.exists():
|
|
442
|
+
# Try to download from registry
|
|
443
|
+
try:
|
|
444
|
+
download_bundle(bundle_name, output_dir=None, auto_load=False)
|
|
445
|
+
# After download, the file should exist
|
|
446
|
+
if not bundle_path.exists():
|
|
447
|
+
# Try with .cgc extension
|
|
448
|
+
bundle_path = Path(f"{bundle_name}.cgc")
|
|
449
|
+
if not bundle_path.exists():
|
|
450
|
+
return (False, f"Bundle not found: {bundle_name}", {})
|
|
451
|
+
except Exception as e:
|
|
452
|
+
return (False, f"Failed to download bundle: {str(e)}", {})
|
|
453
|
+
|
|
454
|
+
# Load the bundle
|
|
455
|
+
bundle = CGCBundle(db_manager)
|
|
456
|
+
success, message = bundle.import_from_bundle(
|
|
457
|
+
bundle_path=bundle_path,
|
|
458
|
+
clear_existing=clear_existing
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if success:
|
|
462
|
+
# Extract stats from message if available
|
|
463
|
+
stats = {}
|
|
464
|
+
if "Nodes:" in message and "Edges:" in message:
|
|
465
|
+
try:
|
|
466
|
+
parts = message.split("|")
|
|
467
|
+
for part in parts:
|
|
468
|
+
if "Nodes:" in part:
|
|
469
|
+
stats["nodes"] = int(part.split(":")[1].strip().replace(",", ""))
|
|
470
|
+
elif "Edges:" in part:
|
|
471
|
+
stats["edges"] = int(part.split(":")[1].strip().replace(",", ""))
|
|
472
|
+
except:
|
|
473
|
+
pass
|
|
474
|
+
|
|
475
|
+
return (True, message, stats)
|
|
476
|
+
else:
|
|
477
|
+
return (False, message, {})
|
|
478
|
+
|
|
479
|
+
except Exception as e:
|
|
480
|
+
return (False, f"Error loading bundle: {str(e)}", {})
|
|
481
|
+
finally:
|
|
482
|
+
if 'db_manager' in locals():
|
|
483
|
+
db_manager.close_driver()
|
|
484
|
+
|
|
@@ -164,6 +164,16 @@ class MCPServer:
|
|
|
164
164
|
**args
|
|
165
165
|
)
|
|
166
166
|
|
|
167
|
+
def load_bundle_tool(self, **args) -> Dict[str, Any]:
|
|
168
|
+
return management_handlers.load_bundle(self.code_finder, **args)
|
|
169
|
+
|
|
170
|
+
def search_registry_bundles_tool(self, **args) -> Dict[str, Any]:
|
|
171
|
+
return management_handlers.search_registry_bundles(self.code_finder, **args)
|
|
172
|
+
|
|
173
|
+
def get_repository_stats_tool(self, **args) -> Dict[str, Any]:
|
|
174
|
+
return management_handlers.get_repository_stats(self.code_finder, **args)
|
|
175
|
+
|
|
176
|
+
|
|
167
177
|
async def handle_tool_call(self, tool_name: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
168
178
|
"""
|
|
169
179
|
Routes a tool call from the AI assistant to the appropriate handler function.
|
|
@@ -184,7 +194,10 @@ class MCPServer:
|
|
|
184
194
|
"delete_repository": self.delete_repository_tool,
|
|
185
195
|
"visualize_graph_query": self.visualize_graph_query_tool,
|
|
186
196
|
"list_watched_paths": self.list_watched_paths_tool,
|
|
187
|
-
"unwatch_directory": self.unwatch_directory_tool
|
|
197
|
+
"unwatch_directory": self.unwatch_directory_tool,
|
|
198
|
+
"load_bundle": self.load_bundle_tool,
|
|
199
|
+
"search_registry_bundles": self.search_registry_bundles_tool,
|
|
200
|
+
"get_repository_stats": self.get_repository_stats_tool
|
|
188
201
|
}
|
|
189
202
|
handler = tool_map.get(tool_name)
|
|
190
203
|
if handler:
|
{codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tool_definitions.py
RENAMED
|
@@ -156,5 +156,38 @@ TOOLS = {
|
|
|
156
156
|
},
|
|
157
157
|
"required": ["path"]
|
|
158
158
|
}
|
|
159
|
+
},
|
|
160
|
+
"load_bundle": {
|
|
161
|
+
"name": "load_bundle",
|
|
162
|
+
"description": "Load a pre-indexed .cgc bundle into the database. Can load from local file or automatically download from registry if not found locally. Bundles are portable snapshots of indexed code that load instantly without re-indexing.",
|
|
163
|
+
"inputSchema": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"properties": {
|
|
166
|
+
"bundle_name": {"type": "string", "description": "Name of the bundle to load (e.g., 'flask', 'pandas', 'flask-main-2579ce9.cgc'). Can be a full filename or just the package name."},
|
|
167
|
+
"clear_existing": {"type": "boolean", "description": "Whether to clear existing data before loading. Use with caution.", "default": False}
|
|
168
|
+
},
|
|
169
|
+
"required": ["bundle_name"]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"search_registry_bundles": {
|
|
173
|
+
"name": "search_registry_bundles",
|
|
174
|
+
"description": "Search for available pre-indexed bundles in the registry. Returns bundles matching the search query with details like repository, version, size, and download information.",
|
|
175
|
+
"inputSchema": {
|
|
176
|
+
"type": "object",
|
|
177
|
+
"properties": {
|
|
178
|
+
"query": {"type": "string", "description": "Search query to find bundles (searches in name, repository, and description). Leave empty to list all bundles."},
|
|
179
|
+
"unique_only": {"type": "boolean", "description": "If true, show only the most recent version of each package. If false, show all versions.", "default": False}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"get_repository_stats": {
|
|
184
|
+
"name": "get_repository_stats",
|
|
185
|
+
"description": "Get statistics about indexed repositories, including counts of files, functions, classes, and modules. Can show overall database statistics or stats for a specific repository.",
|
|
186
|
+
"inputSchema": {
|
|
187
|
+
"type": "object",
|
|
188
|
+
"properties": {
|
|
189
|
+
"repo_path": {"type": "string", "description": "Optional: Path to a specific repository. If not provided, returns overall database statistics."}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
159
192
|
}
|
|
160
193
|
}
|
{codegraphcontext-0.1.35 → codegraphcontext-0.1.36}/src/codegraphcontext/tools/graph_builder.py
RENAMED
|
@@ -70,6 +70,9 @@ class TreeSitterParser:
|
|
|
70
70
|
elif self.language_name == 'swift':
|
|
71
71
|
from .languages.swift import SwiftTreeSitterParser
|
|
72
72
|
self.language_specific_parser = SwiftTreeSitterParser(self)
|
|
73
|
+
elif self.language_name == 'haskell':
|
|
74
|
+
from .languages.haskell import HaskellTreeSitterParser
|
|
75
|
+
self.language_specific_parser = HaskellTreeSitterParser(self)
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
|
|
@@ -114,6 +117,7 @@ class GraphBuilder:
|
|
|
114
117
|
'.scala': TreeSitterParser('scala'),
|
|
115
118
|
'.sc': TreeSitterParser('scala'),
|
|
116
119
|
'.swift': TreeSitterParser('swift'),
|
|
120
|
+
'.hs': TreeSitterParser('haskell'),
|
|
117
121
|
}
|
|
118
122
|
self.create_schema()
|
|
119
123
|
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
from dataclasses import asdict
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from ...core.jobs import JobManager, JobStatus
|
|
5
|
+
from ...utils.debug_log import debug_log
|
|
6
|
+
from ..code_finder import CodeFinder
|
|
7
|
+
from ..graph_builder import GraphBuilder
|
|
8
|
+
|
|
9
|
+
def list_indexed_repositories(code_finder: CodeFinder, **args) -> Dict[str, Any]:
|
|
10
|
+
"""Tool to list indexed repositories."""
|
|
11
|
+
try:
|
|
12
|
+
debug_log("Listing indexed repositories.")
|
|
13
|
+
results = code_finder.list_indexed_repositories()
|
|
14
|
+
return {
|
|
15
|
+
"success": True,
|
|
16
|
+
"repositories": results
|
|
17
|
+
}
|
|
18
|
+
except Exception as e:
|
|
19
|
+
debug_log(f"Error listing indexed repositories: {str(e)}")
|
|
20
|
+
return {"error": f"Failed to list indexed repositories: {str(e)}"}
|
|
21
|
+
|
|
22
|
+
def delete_repository(graph_builder: GraphBuilder, **args) -> Dict[str, Any]:
|
|
23
|
+
"""Tool to delete a repository from the graph."""
|
|
24
|
+
repo_path = args.get("repo_path")
|
|
25
|
+
try:
|
|
26
|
+
debug_log(f"Deleting repository: {repo_path}")
|
|
27
|
+
if graph_builder.delete_repository_from_graph(repo_path):
|
|
28
|
+
return {
|
|
29
|
+
"success": True,
|
|
30
|
+
"message": f"Repository '{repo_path}' deleted successfully."
|
|
31
|
+
}
|
|
32
|
+
else:
|
|
33
|
+
return {
|
|
34
|
+
"success": False,
|
|
35
|
+
"message": f"Repository '{repo_path}' not found in the graph."
|
|
36
|
+
}
|
|
37
|
+
except Exception as e:
|
|
38
|
+
debug_log(f"Error deleting repository: {str(e)}")
|
|
39
|
+
return {"error": f"Failed to delete repository: {str(e)}"}
|
|
40
|
+
|
|
41
|
+
def check_job_status(job_manager: JobManager, **args) -> Dict[str, Any]:
|
|
42
|
+
"""Tool to check job status"""
|
|
43
|
+
job_id = args.get("job_id")
|
|
44
|
+
if not job_id:
|
|
45
|
+
return {"error": "Job ID is a required argument."}
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
job = job_manager.get_job(job_id)
|
|
49
|
+
|
|
50
|
+
if not job:
|
|
51
|
+
return {
|
|
52
|
+
"success": True, # Return success to avoid generic error wrapper
|
|
53
|
+
"status": "not_found",
|
|
54
|
+
"message": f"Job with ID '{job_id}' not found. The ID may be incorrect or the job may have been cleared after a server restart."
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
job_dict = asdict(job)
|
|
58
|
+
|
|
59
|
+
if job.status == JobStatus.RUNNING:
|
|
60
|
+
if job.estimated_time_remaining:
|
|
61
|
+
remaining = job.estimated_time_remaining
|
|
62
|
+
job_dict["estimated_time_remaining_human"] = (
|
|
63
|
+
f"{int(remaining // 60)}m {int(remaining % 60)}s"
|
|
64
|
+
if remaining >= 60 else f"{int(remaining)}s"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if job.start_time:
|
|
68
|
+
elapsed = (datetime.now() - job.start_time).total_seconds()
|
|
69
|
+
job_dict["elapsed_time_human"] = (
|
|
70
|
+
f"{int(elapsed // 60)}m {int(elapsed % 60)}s"
|
|
71
|
+
if elapsed >= 60 else f"{int(elapsed)}s"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
elif job.status == JobStatus.COMPLETED and job.start_time and job.end_time:
|
|
75
|
+
duration = (job.end_time - job.start_time).total_seconds()
|
|
76
|
+
job_dict["actual_duration_human"] = (
|
|
77
|
+
f"{int(duration // 60)}m {int(duration % 60)}s"
|
|
78
|
+
if duration >= 60 else f"{int(duration)}s"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
job_dict["start_time"] = job.start_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
82
|
+
if job.end_time:
|
|
83
|
+
job_dict["end_time"] = job.end_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
84
|
+
|
|
85
|
+
job_dict["status"] = job.status.value
|
|
86
|
+
|
|
87
|
+
return {"success": True, "job": job_dict}
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
debug_log(f"Error checking job status: {str(e)}")
|
|
91
|
+
return {"error": f"Failed to check job status: {str(e)}"}
|
|
92
|
+
|
|
93
|
+
def list_jobs(job_manager: JobManager) -> Dict[str, Any]:
|
|
94
|
+
"""Tool to list all jobs"""
|
|
95
|
+
try:
|
|
96
|
+
jobs = job_manager.list_jobs()
|
|
97
|
+
|
|
98
|
+
jobs_data = []
|
|
99
|
+
for job in jobs:
|
|
100
|
+
job_dict = asdict(job)
|
|
101
|
+
job_dict["status"] = job.status.value
|
|
102
|
+
job_dict["start_time"] = job.start_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
103
|
+
if job.end_time:
|
|
104
|
+
job_dict["end_time"] = job.end_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
105
|
+
jobs_data.append(job_dict)
|
|
106
|
+
|
|
107
|
+
jobs_data.sort(key=lambda x: x["start_time"], reverse=True)
|
|
108
|
+
|
|
109
|
+
return {"success": True, "jobs": jobs_data, "total_jobs": len(jobs_data)}
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
debug_log(f"Error listing jobs: {str(e)}")
|
|
113
|
+
return {"error": f"Failed to list jobs: {str(e)}"}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load_bundle(code_finder: CodeFinder, **args) -> Dict[str, Any]:
|
|
117
|
+
"""Tool to load a .cgc bundle into the database."""
|
|
118
|
+
from pathlib import Path
|
|
119
|
+
from ...cli.registry_commands import load_bundle_command
|
|
120
|
+
|
|
121
|
+
bundle_name = args.get("bundle_name")
|
|
122
|
+
clear_existing = args.get("clear_existing", False)
|
|
123
|
+
|
|
124
|
+
if not bundle_name:
|
|
125
|
+
return {"error": "bundle_name is required"}
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
debug_log(f"Loading bundle: {bundle_name}")
|
|
129
|
+
|
|
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,
|
|
134
|
+
clear_existing=clear_existing
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if success:
|
|
138
|
+
return {
|
|
139
|
+
"success": True,
|
|
140
|
+
"message": message,
|
|
141
|
+
"stats": stats
|
|
142
|
+
}
|
|
143
|
+
else:
|
|
144
|
+
return {"error": message}
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
debug_log(f"Error loading bundle: {str(e)}")
|
|
148
|
+
return {"error": f"Failed to load bundle: {str(e)}"}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def search_registry_bundles(code_finder: CodeFinder, **args) -> Dict[str, Any]:
|
|
152
|
+
"""Tool to search for bundles in the registry."""
|
|
153
|
+
from ...cli.registry_commands import fetch_available_bundles
|
|
154
|
+
|
|
155
|
+
query = args.get("query", "").lower()
|
|
156
|
+
unique_only = args.get("unique_only", False)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
debug_log(f"Searching registry for: {query}")
|
|
160
|
+
|
|
161
|
+
# Fetch all bundles from registry
|
|
162
|
+
bundles = fetch_available_bundles()
|
|
163
|
+
|
|
164
|
+
if not bundles:
|
|
165
|
+
return {
|
|
166
|
+
"success": True,
|
|
167
|
+
"bundles": [],
|
|
168
|
+
"total": 0,
|
|
169
|
+
"message": "No bundles found in registry"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# Filter by query if provided
|
|
173
|
+
if query:
|
|
174
|
+
filtered_bundles = []
|
|
175
|
+
for bundle in bundles:
|
|
176
|
+
name = bundle.get('name', '').lower()
|
|
177
|
+
repo = bundle.get('repo', '').lower()
|
|
178
|
+
full_name = bundle.get('full_name', '').lower()
|
|
179
|
+
|
|
180
|
+
if query in name or query in repo or query in full_name:
|
|
181
|
+
filtered_bundles.append(bundle)
|
|
182
|
+
bundles = filtered_bundles
|
|
183
|
+
|
|
184
|
+
# If unique_only, keep only most recent version per package
|
|
185
|
+
if unique_only:
|
|
186
|
+
unique_bundles = {}
|
|
187
|
+
for bundle in bundles:
|
|
188
|
+
base_name = bundle.get('name', 'unknown')
|
|
189
|
+
if base_name not in unique_bundles:
|
|
190
|
+
unique_bundles[base_name] = bundle
|
|
191
|
+
else:
|
|
192
|
+
current_time = bundle.get('generated_at', '')
|
|
193
|
+
existing_time = unique_bundles[base_name].get('generated_at', '')
|
|
194
|
+
if current_time > existing_time:
|
|
195
|
+
unique_bundles[base_name] = bundle
|
|
196
|
+
bundles = list(unique_bundles.values())
|
|
197
|
+
|
|
198
|
+
# Sort by name
|
|
199
|
+
bundles.sort(key=lambda b: (b.get('name', ''), b.get('full_name', '')))
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
"success": True,
|
|
203
|
+
"bundles": bundles,
|
|
204
|
+
"total": len(bundles),
|
|
205
|
+
"query": query if query else "all",
|
|
206
|
+
"unique_only": unique_only
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
debug_log(f"Error searching registry: {str(e)}")
|
|
211
|
+
return {"error": f"Failed to search registry: {str(e)}"}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def get_repository_stats(code_finder: CodeFinder, **args) -> Dict[str, Any]:
|
|
215
|
+
"""Tool to get statistics about indexed repositories."""
|
|
216
|
+
from pathlib import Path
|
|
217
|
+
|
|
218
|
+
repo_path = args.get("repo_path")
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
debug_log(f"Getting stats for: {repo_path or 'all repositories'}")
|
|
222
|
+
|
|
223
|
+
with code_finder.db_manager.get_driver().session() as session:
|
|
224
|
+
if repo_path:
|
|
225
|
+
# Stats for specific repository
|
|
226
|
+
repo_path_obj = str(Path(repo_path).resolve())
|
|
227
|
+
|
|
228
|
+
# Check if repository exists
|
|
229
|
+
repo_query = """
|
|
230
|
+
MATCH (r:Repository {path: $path})
|
|
231
|
+
RETURN r
|
|
232
|
+
"""
|
|
233
|
+
result = session.run(repo_query, path=repo_path_obj)
|
|
234
|
+
if not result.single():
|
|
235
|
+
return {
|
|
236
|
+
"success": False,
|
|
237
|
+
"error": f"Repository not found: {repo_path_obj}"
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# Get stats for specific repo
|
|
241
|
+
stats_query = """
|
|
242
|
+
MATCH (r:Repository {path: $path})-[:CONTAINS]->(f:File)
|
|
243
|
+
WITH r, count(f) as file_count, f
|
|
244
|
+
OPTIONAL MATCH (f)-[:CONTAINS]->(func:Function)
|
|
245
|
+
OPTIONAL MATCH (f)-[:CONTAINS]->(cls:Class)
|
|
246
|
+
OPTIONAL MATCH (f)-[:IMPORTS]->(m:Module)
|
|
247
|
+
RETURN
|
|
248
|
+
file_count,
|
|
249
|
+
count(DISTINCT func) as function_count,
|
|
250
|
+
count(DISTINCT cls) as class_count,
|
|
251
|
+
count(DISTINCT m) as module_count
|
|
252
|
+
"""
|
|
253
|
+
result = session.run(stats_query, path=repo_path_obj)
|
|
254
|
+
record = result.single()
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
"success": True,
|
|
258
|
+
"repository": repo_path_obj,
|
|
259
|
+
"stats": {
|
|
260
|
+
"files": record["file_count"] if record else 0,
|
|
261
|
+
"functions": record["function_count"] if record else 0,
|
|
262
|
+
"classes": record["class_count"] if record else 0,
|
|
263
|
+
"modules": record["module_count"] if record else 0
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else:
|
|
267
|
+
# Overall database stats
|
|
268
|
+
stats_query = """
|
|
269
|
+
MATCH (r:Repository)
|
|
270
|
+
OPTIONAL MATCH (f:File)
|
|
271
|
+
OPTIONAL MATCH (func:Function)
|
|
272
|
+
OPTIONAL MATCH (cls:Class)
|
|
273
|
+
OPTIONAL MATCH (m:Module)
|
|
274
|
+
RETURN
|
|
275
|
+
count(DISTINCT r) as repo_count,
|
|
276
|
+
count(DISTINCT f) as file_count,
|
|
277
|
+
count(DISTINCT func) as function_count,
|
|
278
|
+
count(DISTINCT cls) as class_count,
|
|
279
|
+
count(DISTINCT m) as module_count
|
|
280
|
+
"""
|
|
281
|
+
result = session.run(stats_query)
|
|
282
|
+
record = result.single()
|
|
283
|
+
|
|
284
|
+
if record and record["repo_count"] > 0:
|
|
285
|
+
return {
|
|
286
|
+
"success": True,
|
|
287
|
+
"stats": {
|
|
288
|
+
"repositories": record["repo_count"],
|
|
289
|
+
"files": record["file_count"],
|
|
290
|
+
"functions": record["function_count"],
|
|
291
|
+
"classes": record["class_count"],
|
|
292
|
+
"modules": record["module_count"]
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else:
|
|
296
|
+
return {
|
|
297
|
+
"success": True,
|
|
298
|
+
"stats": {},
|
|
299
|
+
"message": "No data indexed yet"
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
debug_log(f"Error getting stats: {str(e)}")
|
|
304
|
+
return {"error": f"Failed to get stats: {str(e)}"}
|