codegraphcontext 0.3.8__tar.gz → 0.3.9__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.3.8/src/codegraphcontext.egg-info → codegraphcontext-0.3.9}/PKG-INFO +3 -2
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/README.md +2 -1
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/pyproject.toml +1 -1
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/cli_helpers.py +121 -98
- codegraphcontext-0.3.9/src/codegraphcontext/cli/config_manager.py +878 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/main.py +252 -122
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/__init__.py +11 -12
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/cgc_bundle.py +95 -21
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/database_falkordb.py +24 -11
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/database_kuzu.py +194 -34
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/watcher.py +85 -28
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/server.py +10 -4
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/code_finder.py +37 -36
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/graph_builder.py +716 -575
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9/src/codegraphcontext.egg-info}/PKG-INFO +3 -2
- codegraphcontext-0.3.8/src/codegraphcontext/cli/config_manager.py +0 -412
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/LICENSE +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/MANIFEST.in +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/setup.cfg +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/registry_commands.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/setup_wizard.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/cli/visualizer.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/bundle_registry.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/database.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/falkor_worker.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tool_definitions.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/c.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/cpp.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/csharp.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/dart.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/elixir.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/go.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/haskell.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/java.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/javascript.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/perl.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/php.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/python.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/ruby.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/rust.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/scala.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/swift.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/typescript.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/package_resolver.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/scip_indexer.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/scip_pb2.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/utils/debug_log.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/utils/path_ignore.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/utils/visualize_graph.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext/viz/server.py +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext.egg-info/SOURCES.txt +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/src/codegraphcontext.egg-info/requires.txt +0 -0
- {codegraphcontext-0.3.8 → codegraphcontext-0.3.9}/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.
|
|
3
|
+
Version: 0.3.9
|
|
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
|
|
@@ -73,6 +73,7 @@ Dynamic: license-file
|
|
|
73
73
|
- 🇬🇧 [English](README.md)
|
|
74
74
|
- 🇨🇳 [中文](README.zh-CN.md)
|
|
75
75
|
- 🇰🇷 [한국어](README.kor.md)
|
|
76
|
+
- 🇺🇦 [Українська](README.uk.md)
|
|
76
77
|
- 🇯🇵 日本語 (Soon)
|
|
77
78
|
- 🇷🇺 Русский (Soon)
|
|
78
79
|
- 🇪🇸 Español (Soon)
|
|
@@ -163,7 +164,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
163
164
|
---
|
|
164
165
|
|
|
165
166
|
## Project Details
|
|
166
|
-
- **Version:** 0.3.
|
|
167
|
+
- **Version:** 0.3.9
|
|
167
168
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
168
169
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
169
170
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- 🇬🇧 [English](README.md)
|
|
7
7
|
- 🇨🇳 [中文](README.zh-CN.md)
|
|
8
8
|
- 🇰🇷 [한국어](README.kor.md)
|
|
9
|
+
- 🇺🇦 [Українська](README.uk.md)
|
|
9
10
|
- 🇯🇵 日本語 (Soon)
|
|
10
11
|
- 🇷🇺 Русский (Soon)
|
|
11
12
|
- 🇪🇸 Español (Soon)
|
|
@@ -96,7 +97,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
96
97
|
---
|
|
97
98
|
|
|
98
99
|
## Project Details
|
|
99
|
-
- **Version:** 0.3.
|
|
100
|
+
- **Version:** 0.3.9
|
|
100
101
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
101
102
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
102
103
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codegraphcontext"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.9"
|
|
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"
|
|
@@ -4,7 +4,8 @@ import uuid
|
|
|
4
4
|
import urllib.parse
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import time
|
|
7
|
-
|
|
7
|
+
import os
|
|
8
|
+
from typing import Optional, List, Dict, Any
|
|
8
9
|
from rich.console import Console
|
|
9
10
|
from rich.table import Table
|
|
10
11
|
from rich.progress import (
|
|
@@ -22,18 +23,41 @@ from ..core.jobs import JobManager
|
|
|
22
23
|
from ..tools.code_finder import CodeFinder
|
|
23
24
|
from ..tools.graph_builder import GraphBuilder
|
|
24
25
|
from ..tools.package_resolver import get_local_package_path
|
|
26
|
+
from ..utils.debug_log import info_logger, warning_logger
|
|
27
|
+
from .config_manager import resolve_context, ResolvedContext, register_repo_in_context, ensure_first_run_bootstrap
|
|
25
28
|
|
|
26
29
|
console = Console()
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
def _initialize_services():
|
|
30
|
-
"""
|
|
32
|
+
def _initialize_services(cli_context_flag: Optional[str] = None) -> tuple[Any, Any, Any, ResolvedContext]:
|
|
33
|
+
"""
|
|
34
|
+
Initializes and returns core service managers based on the resolved context.
|
|
35
|
+
Returns (db_manager, graph_builder, code_finder, resolved_context).
|
|
36
|
+
"""
|
|
37
|
+
ensure_first_run_bootstrap()
|
|
38
|
+
console.print("[dim]Resolving context...[/dim]")
|
|
39
|
+
ctx = resolve_context(cli_context_flag)
|
|
40
|
+
|
|
41
|
+
# Let the user know what context we're operating in
|
|
42
|
+
if ctx.mode == "named":
|
|
43
|
+
console.print(f"[cyan]Context:[/cyan] {ctx.context_name} (Database: {ctx.database})")
|
|
44
|
+
elif ctx.mode == "per-repo":
|
|
45
|
+
console.print(f"[cyan]Context:[/cyan] Per-repo local mode (Database: {ctx.database})")
|
|
46
|
+
else:
|
|
47
|
+
# Default global mode — silent to keep CLI clean for existing users
|
|
48
|
+
pass
|
|
49
|
+
|
|
31
50
|
console.print("[dim]Initializing services and database connection...[/dim]")
|
|
32
51
|
try:
|
|
33
|
-
|
|
52
|
+
# Override the database backend with the context's specific choice
|
|
53
|
+
if ctx.database:
|
|
54
|
+
os.environ['CGC_RUNTIME_DB_TYPE'] = ctx.database
|
|
55
|
+
|
|
56
|
+
# Pass the exact DB path resolved from the context
|
|
57
|
+
db_manager = get_database_manager(db_path=ctx.db_path)
|
|
34
58
|
except ValueError as e:
|
|
35
59
|
console.print(f"[bold red]Database Configuration Error:[/bold red] {e}")
|
|
36
|
-
return None, None, None
|
|
60
|
+
return None, None, None, ctx
|
|
37
61
|
|
|
38
62
|
try:
|
|
39
63
|
db_manager.get_driver()
|
|
@@ -58,11 +82,11 @@ def _initialize_services():
|
|
|
58
82
|
console.print("[green]✓[/green] Successfully switched to KùzuDB fallback")
|
|
59
83
|
except Exception as kuzu_e:
|
|
60
84
|
console.print(f"[bold red]Critical Error:[/bold red] Both FalkorDB and KùzuDB failed: {kuzu_e}")
|
|
61
|
-
return None, None, None
|
|
85
|
+
return None, None, None, ctx
|
|
62
86
|
else:
|
|
63
87
|
console.print(f"[bold red]Database Connection Error:[/bold red] {e}")
|
|
64
88
|
console.print("Please ensure your database is configured correctly or run 'cgc doctor'.")
|
|
65
|
-
return None, None, None
|
|
89
|
+
return None, None, None, ctx
|
|
66
90
|
|
|
67
91
|
# The GraphBuilder requires an event loop, even for synchronous-style execution
|
|
68
92
|
try:
|
|
@@ -74,10 +98,10 @@ def _initialize_services():
|
|
|
74
98
|
graph_builder = GraphBuilder(db_manager, JobManager(), loop)
|
|
75
99
|
code_finder = CodeFinder(db_manager)
|
|
76
100
|
console.print("[dim]Services initialized.[/dim]")
|
|
77
|
-
return db_manager, graph_builder, code_finder
|
|
101
|
+
return db_manager, graph_builder, code_finder, ctx
|
|
78
102
|
|
|
79
103
|
|
|
80
|
-
async def _run_index_with_progress(graph_builder: GraphBuilder, path_obj: Path, is_dependency: bool = False):
|
|
104
|
+
async def _run_index_with_progress(graph_builder: GraphBuilder, path_obj: Path, is_dependency: bool = False, cgcignore_path: str = None):
|
|
81
105
|
"""Internal helper to run indexing with a Live progress bar."""
|
|
82
106
|
job_id = graph_builder.job_manager.create_job(str(path_obj), is_dependency=is_dependency)
|
|
83
107
|
|
|
@@ -101,7 +125,7 @@ async def _run_index_with_progress(graph_builder: GraphBuilder, path_obj: Path,
|
|
|
101
125
|
)
|
|
102
126
|
|
|
103
127
|
indexing_task = asyncio.create_task(
|
|
104
|
-
graph_builder.build_graph_from_path_async(path_obj, is_dependency=is_dependency, job_id=job_id)
|
|
128
|
+
graph_builder.build_graph_from_path_async(path_obj, is_dependency=is_dependency, job_id=job_id, cgcignore_path=cgcignore_path)
|
|
105
129
|
)
|
|
106
130
|
|
|
107
131
|
from ..core.jobs import JobStatus
|
|
@@ -135,14 +159,14 @@ async def _run_index_with_progress(graph_builder: GraphBuilder, path_obj: Path,
|
|
|
135
159
|
raise e
|
|
136
160
|
|
|
137
161
|
|
|
138
|
-
def index_helper(path: str):
|
|
139
|
-
"""Synchronously indexes a repository."""
|
|
162
|
+
def index_helper(path: str, context: Optional[str] = None):
|
|
163
|
+
"""Synchronously indexes a repository in a given context."""
|
|
140
164
|
time_start = time.time()
|
|
141
|
-
services = _initialize_services()
|
|
142
|
-
if not all(services):
|
|
165
|
+
services = _initialize_services(context)
|
|
166
|
+
if not all(services[:3]):
|
|
143
167
|
return
|
|
144
168
|
|
|
145
|
-
db_manager, graph_builder, code_finder = services
|
|
169
|
+
db_manager, graph_builder, code_finder, ctx = services
|
|
146
170
|
path_obj = Path(path).resolve()
|
|
147
171
|
|
|
148
172
|
if not path_obj.exists():
|
|
@@ -155,10 +179,12 @@ def index_helper(path: str):
|
|
|
155
179
|
|
|
156
180
|
if repo_exists:
|
|
157
181
|
# Check if the repository actually has files (not just an empty node from interrupted indexing)
|
|
182
|
+
# Use variable-length path to handle both flat (Repository->File) and
|
|
183
|
+
# hierarchical (Repository->Directory->...->File) graph structures
|
|
158
184
|
try:
|
|
159
185
|
with db_manager.get_driver().session() as session:
|
|
160
186
|
result = session.run(
|
|
161
|
-
"MATCH (r:Repository {path: $path})-[:CONTAINS]->(f:File) RETURN count(f) as file_count",
|
|
187
|
+
"MATCH (r:Repository {path: $path})-[:CONTAINS*]->(f:File) RETURN count(f) as file_count",
|
|
162
188
|
path=str(path_obj)
|
|
163
189
|
)
|
|
164
190
|
record = result.single()
|
|
@@ -174,10 +200,14 @@ def index_helper(path: str):
|
|
|
174
200
|
except Exception as e:
|
|
175
201
|
console.print(f"[yellow]Warning: Could not check file count: {e}. Proceeding with indexing...[/yellow]")
|
|
176
202
|
|
|
203
|
+
# Auto-register the repo into the named context (auto-creates if needed)
|
|
204
|
+
if context and ctx.mode == "named":
|
|
205
|
+
register_repo_in_context(context, str(path_obj), auto_create=True)
|
|
206
|
+
|
|
177
207
|
console.print(f"Starting indexing for: {path_obj}")
|
|
178
208
|
|
|
179
209
|
try:
|
|
180
|
-
asyncio.run(_run_index_with_progress(graph_builder, path_obj, is_dependency=False))
|
|
210
|
+
asyncio.run(_run_index_with_progress(graph_builder, path_obj, is_dependency=False, cgcignore_path=ctx.cgcignore_path))
|
|
181
211
|
time_end = time.time()
|
|
182
212
|
elapsed = time_end - time_start
|
|
183
213
|
console.print(f"[green]Successfully finished indexing: {path} in {elapsed:.2f} seconds[/green]")
|
|
@@ -200,13 +230,13 @@ def index_helper(path: str):
|
|
|
200
230
|
db_manager.close_driver()
|
|
201
231
|
|
|
202
232
|
|
|
203
|
-
def add_package_helper(package_name: str, language: str):
|
|
233
|
+
def add_package_helper(package_name: str, language: str, context: Optional[str] = None):
|
|
204
234
|
"""Synchronously indexes a package."""
|
|
205
|
-
services = _initialize_services()
|
|
206
|
-
if not all(services):
|
|
235
|
+
services = _initialize_services(context)
|
|
236
|
+
if not all(services[:3]):
|
|
207
237
|
return
|
|
208
238
|
|
|
209
|
-
db_manager, graph_builder, code_finder = services
|
|
239
|
+
db_manager, graph_builder, code_finder, ctx = services
|
|
210
240
|
|
|
211
241
|
package_path_str = get_local_package_path(package_name, language)
|
|
212
242
|
if not package_path_str:
|
|
@@ -225,7 +255,7 @@ def add_package_helper(package_name: str, language: str):
|
|
|
225
255
|
console.print(f"Starting indexing for package '{package_name}' at: {package_path}")
|
|
226
256
|
|
|
227
257
|
try:
|
|
228
|
-
asyncio.run(_run_index_with_progress(graph_builder, package_path, is_dependency=True))
|
|
258
|
+
asyncio.run(_run_index_with_progress(graph_builder, package_path, is_dependency=True, cgcignore_path=ctx.cgcignore_path))
|
|
229
259
|
console.print(f"[green]Successfully finished indexing package: {package_name}[/green]")
|
|
230
260
|
except Exception as e:
|
|
231
261
|
console.print(f"[bold red]An error occurred during package indexing:[/bold red] {e}")
|
|
@@ -233,13 +263,13 @@ def add_package_helper(package_name: str, language: str):
|
|
|
233
263
|
db_manager.close_driver()
|
|
234
264
|
|
|
235
265
|
|
|
236
|
-
def list_repos_helper():
|
|
266
|
+
def list_repos_helper(context: Optional[str] = None):
|
|
237
267
|
"""Lists all indexed repositories."""
|
|
238
|
-
services = _initialize_services()
|
|
239
|
-
if not all(services):
|
|
268
|
+
services = _initialize_services(context)
|
|
269
|
+
if not all(services[:3]):
|
|
240
270
|
return
|
|
241
271
|
|
|
242
|
-
db_manager, _, code_finder = services
|
|
272
|
+
db_manager, _, code_finder, ctx = services
|
|
243
273
|
|
|
244
274
|
try:
|
|
245
275
|
repos = code_finder.list_indexed_repositories()
|
|
@@ -263,13 +293,13 @@ def list_repos_helper():
|
|
|
263
293
|
db_manager.close_driver()
|
|
264
294
|
|
|
265
295
|
|
|
266
|
-
def delete_helper(repo_path: str):
|
|
296
|
+
def delete_helper(repo_path: str, context: Optional[str] = None):
|
|
267
297
|
"""Deletes a repository from the graph."""
|
|
268
|
-
services = _initialize_services()
|
|
269
|
-
if not all(services):
|
|
298
|
+
services = _initialize_services(context)
|
|
299
|
+
if not all(services[:3]):
|
|
270
300
|
return
|
|
271
301
|
|
|
272
|
-
db_manager, graph_builder, _ = services
|
|
302
|
+
db_manager, graph_builder, _, ctx = services
|
|
273
303
|
|
|
274
304
|
try:
|
|
275
305
|
if graph_builder.delete_repository_from_graph(repo_path):
|
|
@@ -283,13 +313,13 @@ def delete_helper(repo_path: str):
|
|
|
283
313
|
db_manager.close_driver()
|
|
284
314
|
|
|
285
315
|
|
|
286
|
-
def cypher_helper(query: str):
|
|
316
|
+
def cypher_helper(query: str, context: Optional[str] = None):
|
|
287
317
|
"""Executes a read-only Cypher query."""
|
|
288
|
-
services = _initialize_services()
|
|
289
|
-
if not all(services):
|
|
318
|
+
services = _initialize_services(context)
|
|
319
|
+
if not all(services[:3]):
|
|
290
320
|
return
|
|
291
321
|
|
|
292
|
-
db_manager, _, _ = services
|
|
322
|
+
db_manager, _, _, ctx = services
|
|
293
323
|
|
|
294
324
|
# Replicating safety checks from MCPServer
|
|
295
325
|
forbidden_keywords = ['CREATE', 'MERGE', 'DELETE', 'SET', 'REMOVE', 'DROP', 'CALL apoc']
|
|
@@ -309,15 +339,15 @@ def cypher_helper(query: str):
|
|
|
309
339
|
db_manager.close_driver()
|
|
310
340
|
|
|
311
341
|
|
|
312
|
-
def cypher_helper_visual(query: str):
|
|
342
|
+
def cypher_helper_visual(query: str, context: Optional[str] = None):
|
|
313
343
|
"""Executes a read-only Cypher query and visualizes the results."""
|
|
314
344
|
from .visualizer import visualize_cypher_results
|
|
315
345
|
|
|
316
|
-
services = _initialize_services()
|
|
317
|
-
if not all(services):
|
|
346
|
+
services = _initialize_services(context)
|
|
347
|
+
if not all(services[:3]):
|
|
318
348
|
return
|
|
319
349
|
|
|
320
|
-
db_manager, _, _ = services
|
|
350
|
+
db_manager, _, _, ctx = services
|
|
321
351
|
|
|
322
352
|
# Replicating safety checks from MCPServer
|
|
323
353
|
forbidden_keywords = ['CREATE', 'MERGE', 'DELETE', 'SET', 'REMOVE', 'DROP', 'CALL apoc']
|
|
@@ -346,13 +376,13 @@ import uvicorn
|
|
|
346
376
|
import urllib.parse
|
|
347
377
|
from ..viz.server import run_server, set_db_manager
|
|
348
378
|
|
|
349
|
-
def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
|
|
379
|
+
def visualize_helper(repo_path: Optional[str] = None, port: int = 8000, context: Optional[str] = None):
|
|
350
380
|
""""Generates an interactive visualization using the Playground UI."""
|
|
351
|
-
services = _initialize_services()
|
|
352
|
-
if not all(services):
|
|
381
|
+
services = _initialize_services(context)
|
|
382
|
+
if not all(services[:3]):
|
|
353
383
|
return
|
|
354
384
|
|
|
355
|
-
db_manager, _, _ = services
|
|
385
|
+
db_manager, _, _, ctx = services
|
|
356
386
|
|
|
357
387
|
# Set the DB manager for the server
|
|
358
388
|
set_db_manager(db_manager)
|
|
@@ -424,14 +454,14 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
|
|
|
424
454
|
db_manager.close_driver()
|
|
425
455
|
|
|
426
456
|
|
|
427
|
-
def reindex_helper(path: str):
|
|
457
|
+
def reindex_helper(path: str, context: Optional[str] = None):
|
|
428
458
|
"""Force re-index by deleting and rebuilding the repository."""
|
|
429
459
|
time_start = time.time()
|
|
430
|
-
services = _initialize_services()
|
|
431
|
-
if not all(services):
|
|
460
|
+
services = _initialize_services(context)
|
|
461
|
+
if not all(services[:3]):
|
|
432
462
|
return
|
|
433
463
|
|
|
434
|
-
db_manager, graph_builder, code_finder = services
|
|
464
|
+
db_manager, graph_builder, code_finder, ctx = services
|
|
435
465
|
path_obj = Path(path).resolve()
|
|
436
466
|
|
|
437
467
|
if not path_obj.exists():
|
|
@@ -456,7 +486,7 @@ def reindex_helper(path: str):
|
|
|
456
486
|
console.print(f"[cyan]Re-indexing: {path_obj}[/cyan]")
|
|
457
487
|
|
|
458
488
|
try:
|
|
459
|
-
asyncio.run(_run_index_with_progress(graph_builder, path_obj, is_dependency=False))
|
|
489
|
+
asyncio.run(_run_index_with_progress(graph_builder, path_obj, is_dependency=False, cgcignore_path=ctx.cgcignore_path))
|
|
460
490
|
time_end = time.time()
|
|
461
491
|
elapsed = time_end - time_start
|
|
462
492
|
console.print(f"[green]Successfully re-indexed: {path} in {elapsed:.2f} seconds[/green]")
|
|
@@ -466,62 +496,39 @@ def reindex_helper(path: str):
|
|
|
466
496
|
db_manager.close_driver()
|
|
467
497
|
|
|
468
498
|
|
|
469
|
-
def update_helper(path: str):
|
|
499
|
+
def update_helper(path: str, context: Optional[str] = None):
|
|
470
500
|
"""Update/refresh index for a path (alias for reindex)."""
|
|
471
501
|
console.print("[cyan]Updating repository index...[/cyan]")
|
|
472
|
-
reindex_helper(path)
|
|
502
|
+
reindex_helper(path, context)
|
|
473
503
|
|
|
474
504
|
|
|
475
|
-
def clean_helper():
|
|
505
|
+
def clean_helper(context: Optional[str] = None):
|
|
476
506
|
"""Remove orphaned nodes and relationships from the database."""
|
|
477
|
-
services = _initialize_services()
|
|
478
|
-
if not all(services):
|
|
507
|
+
services = _initialize_services(context)
|
|
508
|
+
if not all(services[:3]):
|
|
479
509
|
return
|
|
480
510
|
|
|
481
|
-
db_manager, _, _ = services
|
|
511
|
+
db_manager, _, _, ctx = services
|
|
482
512
|
|
|
483
513
|
console.print("[cyan]🧹 Cleaning database (removing orphaned nodes)...[/cyan]")
|
|
484
514
|
|
|
485
515
|
try:
|
|
486
|
-
# Determine backend type for query compatibility
|
|
487
|
-
db_type = db_manager.__class__.__name__
|
|
488
|
-
is_falkordb = "Falkor" in db_type
|
|
489
|
-
is_kuzu = "Kuzu" in db_type
|
|
490
|
-
|
|
491
516
|
total_deleted = 0
|
|
492
|
-
batch_size =
|
|
517
|
+
batch_size = 500
|
|
493
518
|
|
|
494
519
|
with db_manager.get_driver().session() as session:
|
|
495
|
-
#
|
|
520
|
+
# Layer-by-layer deletion: iteratively delete nodes that lost
|
|
521
|
+
# their CONTAINS parent. Each pass peels one layer of the
|
|
522
|
+
# Repository → File → Class/Function → Variable hierarchy.
|
|
496
523
|
while True:
|
|
497
|
-
|
|
498
|
-
# FalkorDB / KùzuDB-compatible query using OPTIONAL MATCH
|
|
499
|
-
# (KùzuDB does not support the Neo4j `NOT EXISTS { MATCH ... }` subquery syntax)
|
|
500
|
-
query = """
|
|
501
|
-
MATCH (n)
|
|
502
|
-
WHERE NOT (n:Repository)
|
|
503
|
-
OPTIONAL MATCH p = (n)-[*..10]-(r:Repository)
|
|
504
|
-
WITH n, p
|
|
505
|
-
WHERE p IS NULL
|
|
506
|
-
WITH n LIMIT $batch_size
|
|
507
|
-
DETACH DELETE n
|
|
508
|
-
RETURN count(n) as deleted
|
|
509
|
-
"""
|
|
510
|
-
else:
|
|
511
|
-
# Neo4j optimized query using NOT EXISTS with bounded path
|
|
512
|
-
# This is much faster than OPTIONAL MATCH with variable-length paths
|
|
513
|
-
query = """
|
|
524
|
+
result = session.run("""
|
|
514
525
|
MATCH (n)
|
|
515
|
-
WHERE NOT
|
|
516
|
-
AND NOT
|
|
517
|
-
MATCH (n)-[*..10]-(r:Repository)
|
|
518
|
-
}
|
|
526
|
+
WHERE NOT n:Repository
|
|
527
|
+
AND NOT ()-[:CONTAINS]->(n)
|
|
519
528
|
WITH n LIMIT $batch_size
|
|
520
529
|
DETACH DELETE n
|
|
521
530
|
RETURN count(n) as deleted
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
result = session.run(query, batch_size=batch_size)
|
|
531
|
+
""", batch_size=batch_size)
|
|
525
532
|
record = result.single()
|
|
526
533
|
deleted_count = record["deleted"] if record else 0
|
|
527
534
|
total_deleted += deleted_count
|
|
@@ -529,17 +536,13 @@ def clean_helper():
|
|
|
529
536
|
if deleted_count == 0:
|
|
530
537
|
break
|
|
531
538
|
|
|
532
|
-
console.print(f"[dim]Deleted {deleted_count} orphaned nodes (batch)...[/dim]")
|
|
539
|
+
console.print(f"[dim] Deleted {deleted_count} orphaned nodes (batch)...[/dim]")
|
|
533
540
|
|
|
534
541
|
if total_deleted > 0:
|
|
535
542
|
console.print(f"[green]✓[/green] Deleted {total_deleted} orphaned nodes total")
|
|
536
543
|
else:
|
|
537
544
|
console.print("[green]✓[/green] No orphaned nodes found")
|
|
538
545
|
|
|
539
|
-
# Clean up any duplicate relationships (if any)
|
|
540
|
-
console.print("[dim]Checking for duplicate relationships...[/dim]")
|
|
541
|
-
# Note: This is database-specific and might not work for all backends
|
|
542
|
-
|
|
543
546
|
console.print("[green]✅ Database cleanup complete![/green]")
|
|
544
547
|
except Exception as e:
|
|
545
548
|
console.print(f"[bold red]An error occurred during cleanup:[/bold red] {e}")
|
|
@@ -547,13 +550,13 @@ def clean_helper():
|
|
|
547
550
|
db_manager.close_driver()
|
|
548
551
|
|
|
549
552
|
|
|
550
|
-
def stats_helper(path: str = None):
|
|
553
|
+
def stats_helper(path: str = None, context: Optional[str] = None):
|
|
551
554
|
"""Show indexing statistics for a repository or overall."""
|
|
552
|
-
services = _initialize_services()
|
|
553
|
-
if not all(services):
|
|
555
|
+
services = _initialize_services(context)
|
|
556
|
+
if not all(services[:3]):
|
|
554
557
|
return
|
|
555
558
|
|
|
556
|
-
db_manager, _, code_finder = services
|
|
559
|
+
db_manager, _, code_finder, ctx = services
|
|
557
560
|
|
|
558
561
|
try:
|
|
559
562
|
if path:
|
|
@@ -635,7 +638,7 @@ def stats_helper(path: str = None):
|
|
|
635
638
|
db_manager.close_driver()
|
|
636
639
|
|
|
637
640
|
|
|
638
|
-
def watch_helper(path: str):
|
|
641
|
+
def watch_helper(path: str, context: Optional[str] = None):
|
|
639
642
|
"""Watch a directory for changes and auto-update the graph (blocking mode)."""
|
|
640
643
|
import logging
|
|
641
644
|
from ..core.watcher import CodeWatcher
|
|
@@ -645,11 +648,11 @@ def watch_helper(path: str):
|
|
|
645
648
|
logging.getLogger('watchdog.observers').setLevel(logging.WARNING)
|
|
646
649
|
logging.getLogger('watchdog.observers.inotify_buffer').setLevel(logging.WARNING)
|
|
647
650
|
|
|
648
|
-
services = _initialize_services()
|
|
649
|
-
if not all(services):
|
|
651
|
+
services = _initialize_services(context)
|
|
652
|
+
if not all(services[:3]):
|
|
650
653
|
return
|
|
651
654
|
|
|
652
|
-
db_manager, graph_builder, code_finder = services
|
|
655
|
+
db_manager, graph_builder, code_finder, ctx = services
|
|
653
656
|
path_obj = Path(path).resolve()
|
|
654
657
|
|
|
655
658
|
if not path_obj.exists():
|
|
@@ -664,9 +667,29 @@ def watch_helper(path: str):
|
|
|
664
667
|
|
|
665
668
|
console.print(f"[bold cyan]🔍 Watching {path_obj} for changes...[/bold cyan]")
|
|
666
669
|
|
|
667
|
-
# Check if already indexed
|
|
670
|
+
# Check if already indexed — use File node count as a robust fallback so a
|
|
671
|
+
# transient empty result from list_indexed_repositories never triggers a
|
|
672
|
+
# destructive full rescan of an already-populated graph.
|
|
668
673
|
indexed_repos = code_finder.list_indexed_repositories()
|
|
669
674
|
is_indexed = any(Path(repo["path"]).resolve() == path_obj for repo in indexed_repos)
|
|
675
|
+
if not is_indexed:
|
|
676
|
+
# Fallback: count File nodes whose path starts with this repo's path.
|
|
677
|
+
# If > 100 exist, the repo is clearly already indexed — skip the scan.
|
|
678
|
+
try:
|
|
679
|
+
with code_finder.driver.session() as _s:
|
|
680
|
+
_r = _s.run(
|
|
681
|
+
"MATCH (n:File) WHERE n.path STARTS WITH $p RETURN count(n) AS c",
|
|
682
|
+
p=str(path_obj) + "/"
|
|
683
|
+
)
|
|
684
|
+
_count = _r.single()["c"]
|
|
685
|
+
if _count > 100:
|
|
686
|
+
info_logger(
|
|
687
|
+
f"[watch] list_indexed_repositories returned no match for {path_obj} "
|
|
688
|
+
f"but {_count} File nodes exist — treating as already indexed."
|
|
689
|
+
)
|
|
690
|
+
is_indexed = True
|
|
691
|
+
except Exception as _e:
|
|
692
|
+
warning_logger(f"[watch] Fallback indexed check failed: {_e}")
|
|
670
693
|
|
|
671
694
|
# Create watcher instance
|
|
672
695
|
job_manager = JobManager()
|