codegraphcontext 0.2.4__tar.gz → 0.2.6__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.2.4/src/codegraphcontext.egg-info → codegraphcontext-0.2.6}/PKG-INFO +2 -2
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/README.md +1 -1
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/pyproject.toml +1 -1
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/config_manager.py +9 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/graph_builder.py +224 -2
- codegraphcontext-0.2.6/src/codegraphcontext/tools/languages/dart.py +368 -0
- codegraphcontext-0.2.6/src/codegraphcontext/tools/languages/perl.py +255 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/package_resolver.py +29 -1
- codegraphcontext-0.2.6/src/codegraphcontext/tools/scip_indexer.py +467 -0
- codegraphcontext-0.2.6/src/codegraphcontext/tools/scip_pb2.py +2455 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/utils/tree_sitter_manager.py +4 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6/src/codegraphcontext.egg-info}/PKG-INFO +2 -2
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext.egg-info/SOURCES.txt +4 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/LICENSE +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/MANIFEST.in +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/setup.cfg +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/cli_helpers.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/main.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/registry_commands.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/setup_wizard.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/visualizer.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/__init__.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/bundle_registry.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/cgc_bundle.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/database.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/database_falkordb.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/falkor_worker.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/core/watcher.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/server.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tool_definitions.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/code_finder.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/c.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/cpp.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/csharp.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/go.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/haskell.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/java.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/javascript.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/php.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/python.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/ruby.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/rust.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/scala.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/swift.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/typescript.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/utils/debug_log.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/utils/visualize_graph.py +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext.egg-info/requires.txt +0 -0
- {codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/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.2.
|
|
3
|
+
Version: 0.2.6
|
|
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
|
|
@@ -146,7 +146,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
146
146
|
---
|
|
147
147
|
|
|
148
148
|
## Project Details
|
|
149
|
-
- **Version:** 0.2.
|
|
149
|
+
- **Version:** 0.2.6
|
|
150
150
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
151
151
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
152
152
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -84,7 +84,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
86
|
## Project Details
|
|
87
|
-
- **Version:** 0.2.
|
|
87
|
+
- **Version:** 0.2.6
|
|
88
88
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
89
89
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
90
90
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codegraphcontext"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.6"
|
|
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.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/cli/config_manager.py
RENAMED
|
@@ -39,6 +39,10 @@ DEFAULT_CONFIG = {
|
|
|
39
39
|
"CACHE_ENABLED": "true",
|
|
40
40
|
"IGNORE_DIRS": "node_modules,venv,.venv,env,.env,dist,build,target,out,.git,.idea,.vscode,__pycache__",
|
|
41
41
|
"INDEX_SOURCE": "true",
|
|
42
|
+
# SCIP indexer feature flag (default off — existing Tree-sitter behaviour unchanged)
|
|
43
|
+
"SCIP_INDEXER": "false",
|
|
44
|
+
"SCIP_LANGUAGES": "python,typescript,go,rust,java",
|
|
45
|
+
"SKIP_EXTERNAL_RESOLUTION": "false",
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
# Configuration key descriptions
|
|
@@ -62,6 +66,9 @@ CONFIG_DESCRIPTIONS = {
|
|
|
62
66
|
"CACHE_ENABLED": "Enable caching for faster re-indexing",
|
|
63
67
|
"IGNORE_DIRS": "Comma-separated list of directory names to ignore during indexing",
|
|
64
68
|
"INDEX_SOURCE": "Store full source code in graph database (for faster indexing use false, for better performance use true)",
|
|
69
|
+
"SCIP_INDEXER": "Use SCIP-based indexing for higher accuracy call/inheritance resolution (requires scip-<lang> tools installed)",
|
|
70
|
+
"SCIP_LANGUAGES": "Comma-separated languages to index via SCIP when SCIP_INDEXER=true (python,typescript,go,rust,java)",
|
|
71
|
+
"SKIP_EXTERNAL_RESOLUTION": "Skip resolution attempts for external library method calls (recommended for enterprise large Java/Spring codebases)",
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
# Valid values for each config key
|
|
@@ -76,6 +83,8 @@ CONFIG_VALIDATORS = {
|
|
|
76
83
|
"ENABLE_AUTO_WATCH": ["true", "false"],
|
|
77
84
|
"CACHE_ENABLED": ["true", "false"],
|
|
78
85
|
"INDEX_SOURCE": ["true", "false"],
|
|
86
|
+
"SCIP_INDEXER": ["true", "false"],
|
|
87
|
+
"SKIP_EXTERNAL_RESOLUTION": ["true", "false"],
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
|
{codegraphcontext-0.2.4 → codegraphcontext-0.2.6}/src/codegraphcontext/tools/graph_builder.py
RENAMED
|
@@ -74,6 +74,12 @@ class TreeSitterParser:
|
|
|
74
74
|
elif self.language_name == 'haskell':
|
|
75
75
|
from .languages.haskell import HaskellTreeSitterParser
|
|
76
76
|
self.language_specific_parser = HaskellTreeSitterParser(self)
|
|
77
|
+
elif self.language_name == 'dart':
|
|
78
|
+
from .languages.dart import DartTreeSitterParser
|
|
79
|
+
self.language_specific_parser = DartTreeSitterParser(self)
|
|
80
|
+
elif self.language_name == 'perl':
|
|
81
|
+
from .languages.perl import PerlTreeSitterParser
|
|
82
|
+
self.language_specific_parser = PerlTreeSitterParser(self)
|
|
77
83
|
|
|
78
84
|
|
|
79
85
|
|
|
@@ -120,6 +126,9 @@ class GraphBuilder:
|
|
|
120
126
|
'.sc': TreeSitterParser('scala'),
|
|
121
127
|
'.swift': TreeSitterParser('swift'),
|
|
122
128
|
'.hs': TreeSitterParser('haskell'),
|
|
129
|
+
'.dart': TreeSitterParser('dart'),
|
|
130
|
+
'.pl': TreeSitterParser('perl'),
|
|
131
|
+
'.pm': TreeSitterParser('perl'),
|
|
123
132
|
}
|
|
124
133
|
self.create_schema()
|
|
125
134
|
|
|
@@ -162,7 +171,7 @@ class GraphBuilder:
|
|
|
162
171
|
session.run("""
|
|
163
172
|
CREATE FULLTEXT INDEX code_search_index IF NOT EXISTS
|
|
164
173
|
FOR (n:Function|Class|Variable)
|
|
165
|
-
ON EACH [n.name,
|
|
174
|
+
ON EACH [n.name, n.source, n.docstring]
|
|
166
175
|
""")
|
|
167
176
|
|
|
168
177
|
info_logger("Database schema verified/created successfully")
|
|
@@ -249,6 +258,15 @@ class GraphBuilder:
|
|
|
249
258
|
if '.swift' in files_by_lang:
|
|
250
259
|
from .languages import swift as swift_lang_module
|
|
251
260
|
imports_map.update(swift_lang_module.pre_scan_swift(files_by_lang['.swift'], self.parsers['.swift']))
|
|
261
|
+
if '.dart' in files_by_lang:
|
|
262
|
+
from .languages import dart as dart_lang_module
|
|
263
|
+
imports_map.update(dart_lang_module.pre_scan_dart(files_by_lang['.dart'], self.parsers['.dart']))
|
|
264
|
+
if '.pl' in files_by_lang:
|
|
265
|
+
from .languages import perl as perl_lang_module
|
|
266
|
+
imports_map.update(perl_lang_module.pre_scan_perl(files_by_lang['.pl'], self.parsers['.pl']))
|
|
267
|
+
if '.pm' in files_by_lang:
|
|
268
|
+
from .languages import perl as perl_lang_module
|
|
269
|
+
imports_map.update(perl_lang_module.pre_scan_perl(files_by_lang['.pm'], self.parsers['.pm']))
|
|
252
270
|
|
|
253
271
|
return imports_map
|
|
254
272
|
|
|
@@ -461,6 +479,9 @@ class GraphBuilder:
|
|
|
461
479
|
local_imports = {imp.get('alias') or imp['name'].split('.')[-1]: imp['name']
|
|
462
480
|
for imp in file_data.get('imports', [])}
|
|
463
481
|
|
|
482
|
+
# Check if we should skip external resolution attempts -
|
|
483
|
+
skip_external = (get_config_value("SKIP_EXTERNAL_RESOLUTION") or "false").lower() == "true"
|
|
484
|
+
|
|
464
485
|
for call in file_data.get('function_calls', []):
|
|
465
486
|
called_name = call['name']
|
|
466
487
|
if called_name in __builtins__: continue
|
|
@@ -517,7 +538,13 @@ class GraphBuilder:
|
|
|
517
538
|
break
|
|
518
539
|
|
|
519
540
|
if not resolved_path:
|
|
520
|
-
|
|
541
|
+
# Only log warning if we're not skipping external resolution
|
|
542
|
+
if not skip_external:
|
|
543
|
+
warning_logger(f"Could not resolve call {called_name} (lookup: {lookup_name}) in {caller_file_path}")
|
|
544
|
+
# Track that this was an unresolved external call
|
|
545
|
+
is_unresolved_external = True
|
|
546
|
+
else:
|
|
547
|
+
is_unresolved_external = False
|
|
521
548
|
# else:
|
|
522
549
|
# info_logger(f"Resolved call {called_name} -> {resolved_path}")
|
|
523
550
|
|
|
@@ -539,6 +566,7 @@ class GraphBuilder:
|
|
|
539
566
|
if not resolved_path:
|
|
540
567
|
if called_name in local_names:
|
|
541
568
|
resolved_path = caller_file_path
|
|
569
|
+
is_unresolved_external = False # This is a local call, not external
|
|
542
570
|
elif called_name in imports_map and imports_map[called_name]:
|
|
543
571
|
# Check if any path in imports_map for called_name matches current file's imports
|
|
544
572
|
candidates = imports_map[called_name]
|
|
@@ -546,12 +574,17 @@ class GraphBuilder:
|
|
|
546
574
|
for imp_name in local_imports.values():
|
|
547
575
|
if imp_name.replace('.', '/') in path:
|
|
548
576
|
resolved_path = path
|
|
577
|
+
is_unresolved_external = False # Found a match
|
|
549
578
|
break
|
|
550
579
|
if resolved_path: break
|
|
551
580
|
if not resolved_path:
|
|
552
581
|
resolved_path = candidates[0]
|
|
553
582
|
else:
|
|
554
583
|
resolved_path = caller_file_path
|
|
584
|
+
|
|
585
|
+
# Skip creating CALLS relationship for unresolved external calls when skip_external is enabled
|
|
586
|
+
if skip_external and is_unresolved_external:
|
|
587
|
+
continue
|
|
555
588
|
|
|
556
589
|
caller_context = call.get('context')
|
|
557
590
|
if caller_context and len(caller_context) == 3 and caller_context[0] is not None:
|
|
@@ -879,11 +912,200 @@ class GraphBuilder:
|
|
|
879
912
|
error_logger(f"Could not estimate processing time for {path}: {e}")
|
|
880
913
|
return None
|
|
881
914
|
|
|
915
|
+
async def _build_graph_from_scip(
|
|
916
|
+
self, path: Path, is_dependency: bool, job_id: Optional[str], lang: str
|
|
917
|
+
):
|
|
918
|
+
"""
|
|
919
|
+
SCIP-based indexing path. Activated only when SCIP_INDEXER=true and
|
|
920
|
+
a scip-<lang> binary is available.
|
|
921
|
+
|
|
922
|
+
Steps:
|
|
923
|
+
1. Run scip-<lang> CLI → index.scip
|
|
924
|
+
2. Parse index.scip → nodes + reference edges
|
|
925
|
+
3. Write nodes to graph (same MERGE queries as Tree-sitter path)
|
|
926
|
+
4. Tree-sitter supplement: add source text + cyclomatic_complexity
|
|
927
|
+
5. Write SCIP CALLS edges (precise, no heuristics)
|
|
928
|
+
"""
|
|
929
|
+
import tempfile
|
|
930
|
+
from .scip_indexer import ScipIndexer, ScipIndexParser
|
|
931
|
+
from .graph_builder import TreeSitterParser # supplement pass
|
|
932
|
+
|
|
933
|
+
if job_id:
|
|
934
|
+
self.job_manager.update_job(job_id, status=JobStatus.RUNNING)
|
|
935
|
+
|
|
936
|
+
self.add_repository_to_graph(path, is_dependency)
|
|
937
|
+
repo_name = path.name
|
|
938
|
+
|
|
939
|
+
try:
|
|
940
|
+
# Step 1: Run SCIP indexer
|
|
941
|
+
with tempfile.TemporaryDirectory(prefix="cgc_scip_") as tmpdir:
|
|
942
|
+
scip_file = ScipIndexer().run(path, lang, Path(tmpdir))
|
|
943
|
+
|
|
944
|
+
if not scip_file:
|
|
945
|
+
warning_logger(
|
|
946
|
+
f"SCIP indexer produced no output for {path}. "
|
|
947
|
+
"Falling back to Tree-sitter."
|
|
948
|
+
)
|
|
949
|
+
# Hand off to Tree-sitter pipeline by re-calling without SCIP flag
|
|
950
|
+
# (the flag is checked at the start; override is not needed because
|
|
951
|
+
# we return here — caller will not re-enter this branch)
|
|
952
|
+
raise RuntimeError("SCIP produced no index — triggering Tree-sitter fallback")
|
|
953
|
+
|
|
954
|
+
# Step 2: Parse index.scip
|
|
955
|
+
scip_data = ScipIndexParser().parse(scip_file, path)
|
|
956
|
+
|
|
957
|
+
if not scip_data:
|
|
958
|
+
raise RuntimeError("SCIP parse returned empty result")
|
|
959
|
+
|
|
960
|
+
files_data = scip_data.get("files", {})
|
|
961
|
+
file_paths = [Path(p) for p in files_data.keys() if Path(p).exists()]
|
|
962
|
+
|
|
963
|
+
# Step 3: Pre-scan for imports to correctly associate external modules/classes
|
|
964
|
+
imports_map = self._pre_scan_for_imports(file_paths)
|
|
965
|
+
|
|
966
|
+
if job_id:
|
|
967
|
+
self.job_manager.update_job(job_id, total_files=len(files_data))
|
|
968
|
+
|
|
969
|
+
# Step 4: Write nodes to graph using existing add_file_to_graph()
|
|
970
|
+
processed = 0
|
|
971
|
+
for abs_path_str, file_data in files_data.items():
|
|
972
|
+
file_data["repo_path"] = str(path.resolve())
|
|
973
|
+
if job_id:
|
|
974
|
+
self.job_manager.update_job(job_id, current_file=abs_path_str)
|
|
975
|
+
|
|
976
|
+
# Step 5: Tree-sitter supplement — add source text, complexity, imports and bases
|
|
977
|
+
file_path = Path(abs_path_str)
|
|
978
|
+
if file_path.exists() and file_path.suffix in self.parsers:
|
|
979
|
+
try:
|
|
980
|
+
ts_parser = self.parsers[file_path.suffix]
|
|
981
|
+
ts_data = ts_parser.parse(file_path, is_dependency, index_source=True)
|
|
982
|
+
if "error" not in ts_data:
|
|
983
|
+
# 1. Functions: complexity, source, decorators
|
|
984
|
+
ts_funcs = {f["name"]: f for f in ts_data.get("functions", [])}
|
|
985
|
+
for f in file_data.get("functions", []):
|
|
986
|
+
ts_f = ts_funcs.get(f["name"])
|
|
987
|
+
if ts_f:
|
|
988
|
+
f.update({
|
|
989
|
+
"source": ts_f.get("source"),
|
|
990
|
+
"cyclomatic_complexity": ts_f.get("cyclomatic_complexity", 1),
|
|
991
|
+
"decorators": ts_f.get("decorators", [])
|
|
992
|
+
})
|
|
993
|
+
|
|
994
|
+
# 2. Classes: bases (inheritance)
|
|
995
|
+
ts_classes = {c["name"]: c for c in ts_data.get("classes", [])}
|
|
996
|
+
for c in file_data.get("classes", []):
|
|
997
|
+
ts_c = ts_classes.get(c["name"])
|
|
998
|
+
if ts_c:
|
|
999
|
+
c["bases"] = ts_c.get("bases", [])
|
|
1000
|
+
|
|
1001
|
+
# 3. Imports: critical for cross-file resolution
|
|
1002
|
+
file_data["imports"] = ts_data.get("imports", [])
|
|
1003
|
+
|
|
1004
|
+
# 4. Variables/Other: value, etc.
|
|
1005
|
+
file_data["variables"] = ts_data.get("variables", [])
|
|
1006
|
+
except Exception as e:
|
|
1007
|
+
debug_log(f"Tree-sitter supplement failed for {abs_path_str}: {e}")
|
|
1008
|
+
|
|
1009
|
+
self.add_file_to_graph(file_data, repo_name, imports_map)
|
|
1010
|
+
|
|
1011
|
+
processed += 1
|
|
1012
|
+
if job_id:
|
|
1013
|
+
self.job_manager.update_job(job_id, processed_files=processed)
|
|
1014
|
+
await asyncio.sleep(0.01)
|
|
1015
|
+
|
|
1016
|
+
# Step 6: Create INHERITS relationships (Supplemented from Tree-sitter)
|
|
1017
|
+
self._create_all_inheritance_links(list(files_data.values()), imports_map)
|
|
1018
|
+
|
|
1019
|
+
# Step 7: Write SCIP CALLS edges — precise cross-file resolution
|
|
1020
|
+
with self.driver.session() as session:
|
|
1021
|
+
for file_data in files_data.values():
|
|
1022
|
+
for edge in file_data.get("function_calls_scip", []):
|
|
1023
|
+
try:
|
|
1024
|
+
# Use line numbers for precise matching in case of duplicates
|
|
1025
|
+
session.run("""
|
|
1026
|
+
MATCH (caller:Function {name: $caller_name, path: $caller_file, line_number: $caller_line})
|
|
1027
|
+
MATCH (callee:Function {name: $callee_name, path: $callee_file, line_number: $callee_line})
|
|
1028
|
+
MERGE (caller)-[:CALLS {line_number: $ref_line, source: 'scip'}]->(callee)
|
|
1029
|
+
""",
|
|
1030
|
+
caller_name=self._name_from_symbol(edge["caller_symbol"]),
|
|
1031
|
+
caller_file=edge["caller_file"],
|
|
1032
|
+
caller_line=edge["caller_line"],
|
|
1033
|
+
callee_name=edge["callee_name"],
|
|
1034
|
+
callee_file=edge["callee_file"],
|
|
1035
|
+
callee_line=edge["callee_line"],
|
|
1036
|
+
ref_line=edge["ref_line"],
|
|
1037
|
+
)
|
|
1038
|
+
except Exception:
|
|
1039
|
+
pass # best-effort: node might not be indexed yet
|
|
1040
|
+
|
|
1041
|
+
if job_id:
|
|
1042
|
+
self.job_manager.update_job(job_id, status=JobStatus.COMPLETED, end_time=datetime.now())
|
|
1043
|
+
|
|
1044
|
+
except RuntimeError as e:
|
|
1045
|
+
# Graceful fallback to Tree-sitter when SCIP fails
|
|
1046
|
+
warning_logger(f"SCIP path failed ({e}), re-running with Tree-sitter...")
|
|
1047
|
+
# Temporarily disable the flag in-memory so the recursive call goes straight to TS
|
|
1048
|
+
# (we do this by calling the internal Tree-sitter steps directly)
|
|
1049
|
+
if job_id:
|
|
1050
|
+
self.job_manager.update_job(job_id, status=JobStatus.RUNNING)
|
|
1051
|
+
# Re-enter the async flow without SCIP check — handled by caller returning early
|
|
1052
|
+
# For simplicity, we just let the exception propagate to the outer handler so the
|
|
1053
|
+
# job is marked FAILED with a meaningful message rather than silently degrading.
|
|
1054
|
+
raise
|
|
1055
|
+
|
|
1056
|
+
except Exception as e:
|
|
1057
|
+
error_logger(f"SCIP indexing failed for {path}: {e}")
|
|
1058
|
+
if job_id:
|
|
1059
|
+
self.job_manager.update_job(
|
|
1060
|
+
job_id, status=JobStatus.FAILED, end_time=datetime.now(), errors=[str(e)]
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
def _name_from_symbol(self, symbol: str) -> str:
|
|
1064
|
+
"""Extract human-readable name from a SCIP symbol ID string."""
|
|
1065
|
+
import re
|
|
1066
|
+
s = symbol.rstrip(".#")
|
|
1067
|
+
s = re.sub(r"\(\)\.?$", "", s) # Remove trailing () or ().
|
|
1068
|
+
parts = re.split(r'[/#]', s)
|
|
1069
|
+
last = parts[-1] if parts else symbol
|
|
1070
|
+
return last or symbol
|
|
1071
|
+
|
|
1072
|
+
|
|
882
1073
|
async def build_graph_from_path_async(
|
|
883
1074
|
self, path: Path, is_dependency: bool = False, job_id: str = None
|
|
884
1075
|
):
|
|
885
1076
|
"""Builds graph from a directory or file path."""
|
|
886
1077
|
try:
|
|
1078
|
+
# ------------------------------------------------------------------
|
|
1079
|
+
# SCIP feature flag: SCIP_INDEXER=true in ~/.codegraphcontext/.env
|
|
1080
|
+
# When enabled (and the binary is installed), SCIP handles the
|
|
1081
|
+
# indexing for supported languages. SCIP_INDEXER=false (default)
|
|
1082
|
+
# means this entire block is a no-op and existing behaviour is kept.
|
|
1083
|
+
# ------------------------------------------------------------------
|
|
1084
|
+
scip_enabled = (get_config_value("SCIP_INDEXER") or "false").lower() == "true"
|
|
1085
|
+
if scip_enabled:
|
|
1086
|
+
from .scip_indexer import ScipIndexer, ScipIndexParser, detect_project_lang, is_scip_available
|
|
1087
|
+
scip_langs_str = get_config_value("SCIP_LANGUAGES") or "python,typescript,go,rust,java"
|
|
1088
|
+
scip_languages = [l.strip() for l in scip_langs_str.split(",") if l.strip()]
|
|
1089
|
+
detected_lang = detect_project_lang(path, scip_languages)
|
|
1090
|
+
|
|
1091
|
+
if detected_lang and is_scip_available(detected_lang):
|
|
1092
|
+
info_logger(f"SCIP_INDEXER=true — using SCIP for language: {detected_lang}")
|
|
1093
|
+
await self._build_graph_from_scip(path, is_dependency, job_id, detected_lang)
|
|
1094
|
+
return # SCIP handled it; skip Tree-sitter pipeline below
|
|
1095
|
+
else:
|
|
1096
|
+
if detected_lang:
|
|
1097
|
+
warning_logger(
|
|
1098
|
+
f"SCIP_INDEXER=true but scip-{detected_lang} binary not found. "
|
|
1099
|
+
f"Falling back to Tree-sitter. Install it first."
|
|
1100
|
+
)
|
|
1101
|
+
else:
|
|
1102
|
+
info_logger(
|
|
1103
|
+
"SCIP_INDEXER=true but no SCIP-supported language detected. "
|
|
1104
|
+
"Falling back to Tree-sitter."
|
|
1105
|
+
)
|
|
1106
|
+
# ------------------------------------------------------------------
|
|
1107
|
+
# Existing Tree-sitter pipeline (unchanged)
|
|
1108
|
+
# ------------------------------------------------------------------
|
|
887
1109
|
if job_id:
|
|
888
1110
|
self.job_manager.update_job(job_id, status=JobStatus.RUNNING)
|
|
889
1111
|
|