codegraphcontext-rust 0.5.5__cp311-cp311-win_amd64.whl
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/__init__.py +2 -0
- codegraphcontext/__main__.py +5 -0
- codegraphcontext/_cgc_rust.cp311-win_amd64.pyd +0 -0
- codegraphcontext/cli/__init__.py +1 -0
- codegraphcontext/cli/cli_helpers.py +774 -0
- codegraphcontext/cli/config_manager.py +1051 -0
- codegraphcontext/cli/main.py +2386 -0
- codegraphcontext/cli/registry_commands.py +404 -0
- codegraphcontext/cli/setup_macos.py +93 -0
- codegraphcontext/cli/setup_wizard.py +991 -0
- codegraphcontext/cli/visualizer.py +50 -0
- codegraphcontext/core/__init__.py +165 -0
- codegraphcontext/core/bundle_registry.py +181 -0
- codegraphcontext/core/cgc_bundle.py +858 -0
- codegraphcontext/core/cgcignore.py +118 -0
- codegraphcontext/core/database.py +273 -0
- codegraphcontext/core/database_falkordb.py +480 -0
- codegraphcontext/core/database_falkordb_remote.py +199 -0
- codegraphcontext/core/database_kuzu.py +636 -0
- codegraphcontext/core/falkor_worker.py +132 -0
- codegraphcontext/core/jobs.py +132 -0
- codegraphcontext/core/watcher.py +260 -0
- codegraphcontext/prompts.py +125 -0
- codegraphcontext/server.py +471 -0
- codegraphcontext/tool_definitions.py +220 -0
- codegraphcontext/tools/__init__.py +0 -0
- codegraphcontext/tools/advanced_language_query_tool.py +103 -0
- codegraphcontext/tools/code_finder.py +1160 -0
- codegraphcontext/tools/graph_builder.py +337 -0
- codegraphcontext/tools/handlers/analysis_handlers.py +114 -0
- codegraphcontext/tools/handlers/indexing_handlers.py +116 -0
- codegraphcontext/tools/handlers/management_handlers.py +339 -0
- codegraphcontext/tools/handlers/query_handlers.py +83 -0
- codegraphcontext/tools/handlers/watcher_handlers.py +83 -0
- codegraphcontext/tools/indexing/__init__.py +1 -0
- codegraphcontext/tools/indexing/constants.py +25 -0
- codegraphcontext/tools/indexing/discovery.py +64 -0
- codegraphcontext/tools/indexing/engine.py +109 -0
- codegraphcontext/tools/indexing/persistence/__init__.py +3 -0
- codegraphcontext/tools/indexing/persistence/parquet_loader.py +643 -0
- codegraphcontext/tools/indexing/persistence/writer.py +1114 -0
- codegraphcontext/tools/indexing/pipeline.py +171 -0
- codegraphcontext/tools/indexing/pre_scan.py +105 -0
- codegraphcontext/tools/indexing/resolution/__init__.py +8 -0
- codegraphcontext/tools/indexing/resolution/calls.py +253 -0
- codegraphcontext/tools/indexing/resolution/inheritance.py +91 -0
- codegraphcontext/tools/indexing/sanitize.py +41 -0
- codegraphcontext/tools/indexing/schema.py +79 -0
- codegraphcontext/tools/indexing/schema_contract.py +44 -0
- codegraphcontext/tools/indexing/scip_pipeline.py +140 -0
- codegraphcontext/tools/languages/c.py +563 -0
- codegraphcontext/tools/languages/cpp.py +616 -0
- codegraphcontext/tools/languages/csharp.py +551 -0
- codegraphcontext/tools/languages/dart.py +378 -0
- codegraphcontext/tools/languages/elixir.py +460 -0
- codegraphcontext/tools/languages/go.py +499 -0
- codegraphcontext/tools/languages/haskell.py +426 -0
- codegraphcontext/tools/languages/java.py +500 -0
- codegraphcontext/tools/languages/javascript.py +589 -0
- codegraphcontext/tools/languages/kotlin.py +640 -0
- codegraphcontext/tools/languages/perl.py +260 -0
- codegraphcontext/tools/languages/php.py +519 -0
- codegraphcontext/tools/languages/python.py +576 -0
- codegraphcontext/tools/languages/ruby.py +537 -0
- codegraphcontext/tools/languages/rust.py +295 -0
- codegraphcontext/tools/languages/scala.py +519 -0
- codegraphcontext/tools/languages/swift.py +491 -0
- codegraphcontext/tools/languages/typescript.py +575 -0
- codegraphcontext/tools/languages/typescriptjsx.py +151 -0
- codegraphcontext/tools/package_resolver.py +472 -0
- codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
- codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/dart_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/perl_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/scala_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/swift_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
- codegraphcontext/tools/scip_indexer.py +467 -0
- codegraphcontext/tools/scip_pb2.py +2455 -0
- codegraphcontext/tools/system.py +133 -0
- codegraphcontext/tools/tree_sitter_parser.py +103 -0
- codegraphcontext/utils/debug_log.py +90 -0
- codegraphcontext/utils/path_ignore.py +55 -0
- codegraphcontext/utils/repo_path.py +27 -0
- codegraphcontext/utils/tree_sitter_manager.py +285 -0
- codegraphcontext/utils/visualize_graph.py +0 -0
- codegraphcontext/viz/server.py +282 -0
- codegraphcontext_rust-0.5.5.dist-info/METADATA +537 -0
- codegraphcontext_rust-0.5.5.dist-info/RECORD +101 -0
- codegraphcontext_rust-0.5.5.dist-info/WHEEL +4 -0
- codegraphcontext_rust-0.5.5.dist-info/entry_points.txt +3 -0
- codegraphcontext_rust-0.5.5.dist-info/licenses/LICENSE +21 -0
- codegraphcontext_rust-0.5.5.dist-info/sboms/cgc-python.cyclonedx.json +2333 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
def pre_scan_typescript(files: list[Path], parser_wrapper) -> dict:
|
|
4
|
+
"""
|
|
5
|
+
Scans TypeScript JSX (.tsx) files to create a map of class/function names to their file paths.
|
|
6
|
+
Reuses the logic from TypeScript parser, but can be extended for JSX-specific extraction.
|
|
7
|
+
"""
|
|
8
|
+
imports_map = {}
|
|
9
|
+
# Use the same queries as TypeScript
|
|
10
|
+
query_strings = [
|
|
11
|
+
"(class_declaration) @class",
|
|
12
|
+
"(function_declaration) @function",
|
|
13
|
+
"(variable_declarator) @var_decl",
|
|
14
|
+
"(method_definition) @method",
|
|
15
|
+
"(interface_declaration) @interface",
|
|
16
|
+
"(type_alias_declaration) @type_alias",
|
|
17
|
+
]
|
|
18
|
+
for path in files:
|
|
19
|
+
try:
|
|
20
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
21
|
+
source_code = f.read()
|
|
22
|
+
tree = parser_wrapper.parser.parse(bytes(source_code, "utf8"))
|
|
23
|
+
for query_str in query_strings:
|
|
24
|
+
try:
|
|
25
|
+
for node, capture_name in execute_query(parser_wrapper.language, query_str, tree.root_node):
|
|
26
|
+
name = None
|
|
27
|
+
if capture_name == 'class':
|
|
28
|
+
name_node = node.child_by_field_name('name')
|
|
29
|
+
if name_node:
|
|
30
|
+
name = name_node.text.decode('utf-8')
|
|
31
|
+
elif capture_name == 'function':
|
|
32
|
+
name_node = node.child_by_field_name('name')
|
|
33
|
+
if name_node:
|
|
34
|
+
name = name_node.text.decode('utf-8')
|
|
35
|
+
elif capture_name == 'var_decl':
|
|
36
|
+
name_node = node.child_by_field_name('name')
|
|
37
|
+
value_node = node.child_by_field_name('value')
|
|
38
|
+
if name_node and value_node:
|
|
39
|
+
if value_node.type in ('function', 'arrow_function'):
|
|
40
|
+
name = name_node.text.decode('utf-8')
|
|
41
|
+
elif capture_name == 'method':
|
|
42
|
+
name_node = node.child_by_field_name('name')
|
|
43
|
+
if name_node:
|
|
44
|
+
name = name_node.text.decode('utf-8')
|
|
45
|
+
elif capture_name == 'interface':
|
|
46
|
+
name_node = node.child_by_field_name('name')
|
|
47
|
+
if name_node:
|
|
48
|
+
name = name_node.text.decode('utf-8')
|
|
49
|
+
elif capture_name == 'type_alias':
|
|
50
|
+
name_node = node.child_by_field_name('name')
|
|
51
|
+
if name_node:
|
|
52
|
+
name = name_node.text.decode('utf-8')
|
|
53
|
+
if name:
|
|
54
|
+
if name not in imports_map:
|
|
55
|
+
imports_map[name] = []
|
|
56
|
+
file_path_str = str(path.resolve())
|
|
57
|
+
if file_path_str not in imports_map[name]:
|
|
58
|
+
imports_map[name].append(file_path_str)
|
|
59
|
+
except Exception as query_error:
|
|
60
|
+
warning_logger(f"Query failed for pattern '{query_str}': {query_error}")
|
|
61
|
+
except Exception as e:
|
|
62
|
+
warning_logger(f"Tree-sitter pre-scan failed for {path}: {e}")
|
|
63
|
+
return imports_map
|
|
64
|
+
from typing import Dict, Any
|
|
65
|
+
from codegraphcontext.utils.debug_log import warning_logger
|
|
66
|
+
from codegraphcontext.utils.tree_sitter_manager import execute_query
|
|
67
|
+
from .typescript import TypescriptTreeSitterParser
|
|
68
|
+
|
|
69
|
+
class TypescriptJSXTreeSitterParser(TypescriptTreeSitterParser):
|
|
70
|
+
"""
|
|
71
|
+
A parser for TypeScript JSX (.tsx) files.
|
|
72
|
+
"""
|
|
73
|
+
def __init__(self, generic_parser_wrapper):
|
|
74
|
+
super().__init__(generic_parser_wrapper)
|
|
75
|
+
self.language_name = 'typescript'
|
|
76
|
+
self.jsx_enabled = True
|
|
77
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict[str, Any]:
|
|
78
|
+
"""
|
|
79
|
+
Parse a .tsx file, reusing TypeScript logic and ensuring JSX nodes are handled.
|
|
80
|
+
Indexes components, functions, imports, and exports.
|
|
81
|
+
"""
|
|
82
|
+
self.index_source = index_source
|
|
83
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
84
|
+
source_code = f.read()
|
|
85
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
86
|
+
root_node = tree.root_node
|
|
87
|
+
|
|
88
|
+
# Reuse TypeScript logic for functions, classes, interfaces, type aliases, imports, calls, variables
|
|
89
|
+
functions = self._find_functions(root_node)
|
|
90
|
+
classes = self._find_classes(root_node)
|
|
91
|
+
interfaces = self._find_interfaces(root_node)
|
|
92
|
+
type_aliases = self._find_type_aliases(root_node)
|
|
93
|
+
imports = self._find_imports(root_node)
|
|
94
|
+
function_calls = self._find_calls(root_node)
|
|
95
|
+
variables = self._find_variables(root_node)
|
|
96
|
+
|
|
97
|
+
# Index React components (function and class components)
|
|
98
|
+
components = self._find_react_components(root_node)
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"path": str(path),
|
|
102
|
+
"functions": functions,
|
|
103
|
+
"classes": classes,
|
|
104
|
+
"interfaces": interfaces,
|
|
105
|
+
"type_aliases": type_aliases,
|
|
106
|
+
"variables": variables,
|
|
107
|
+
"imports": imports,
|
|
108
|
+
"function_calls": function_calls,
|
|
109
|
+
"components": components,
|
|
110
|
+
"is_dependency": is_dependency,
|
|
111
|
+
"lang": self.language_name,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
def _find_react_components(self, root_node):
|
|
115
|
+
"""
|
|
116
|
+
Find React components in .tsx files (function and class components).
|
|
117
|
+
"""
|
|
118
|
+
components = []
|
|
119
|
+
# Function components: exported arrow/function assigned to const, returning JSX
|
|
120
|
+
# Class components: class extending React.Component or React.PureComponent
|
|
121
|
+
# This is a simplified query, can be extended for more cases
|
|
122
|
+
query_strings = [
|
|
123
|
+
'(class_declaration name: (type_identifier) @name)',
|
|
124
|
+
'(variable_declarator name: (identifier) @name value: (arrow_function) @fn)',
|
|
125
|
+
'(variable_declarator name: (identifier) @name value: (function_expression) @fn)',
|
|
126
|
+
'(function_declaration name: (identifier) @name)',
|
|
127
|
+
]
|
|
128
|
+
for query_str in query_strings:
|
|
129
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
130
|
+
if capture_name == 'name':
|
|
131
|
+
name = node.text.decode('utf-8')
|
|
132
|
+
line_number = node.start_point[0] + 1
|
|
133
|
+
component_data = {
|
|
134
|
+
"name": name,
|
|
135
|
+
"line_number": line_number,
|
|
136
|
+
"type": "component",
|
|
137
|
+
"lang": self.language_name,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if self.index_source:
|
|
141
|
+
# Assuming node is the name node, we might want the parent declaration node for source
|
|
142
|
+
# The query captures 'name', so node is the identifier.
|
|
143
|
+
# We need the parent node.
|
|
144
|
+
# (class_declaration ... name: (identifier) @name) -> parent is class_declaration
|
|
145
|
+
# (variable_declarator name: (identifier) @name ...) -> parent is variable_declarator
|
|
146
|
+
# (function_declaration name: (identifier) @name) -> parent is function_declaration
|
|
147
|
+
parent = node.parent
|
|
148
|
+
component_data["source"] = parent.text.decode('utf-8')
|
|
149
|
+
|
|
150
|
+
components.append(component_data)
|
|
151
|
+
return components
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# src/codegraphcontext/tools/package_resolver.py
|
|
2
|
+
import importlib.util
|
|
3
|
+
import stdlibs
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import subprocess
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from ..utils.debug_log import debug_log
|
|
9
|
+
|
|
10
|
+
def _get_python_package_path(package_name: str) -> Optional[str]:
|
|
11
|
+
"""
|
|
12
|
+
Finds the local installation path of a Python package.
|
|
13
|
+
Uses importlib.util.find_spec() to locate the module without executing its code.
|
|
14
|
+
"""
|
|
15
|
+
try:
|
|
16
|
+
debug_log(f"Getting local path for Python package: {package_name}")
|
|
17
|
+
spec = importlib.util.find_spec(package_name)
|
|
18
|
+
if spec is None:
|
|
19
|
+
return None
|
|
20
|
+
if spec.origin and spec.origin != "frozen":
|
|
21
|
+
module_file = Path(spec.origin)
|
|
22
|
+
if module_file.name == '__init__.py':
|
|
23
|
+
return str(module_file.parent)
|
|
24
|
+
elif package_name in stdlibs.module_names:
|
|
25
|
+
return str(module_file)
|
|
26
|
+
else:
|
|
27
|
+
return str(module_file.parent)
|
|
28
|
+
elif spec.submodule_search_locations:
|
|
29
|
+
locations = list(spec.submodule_search_locations)
|
|
30
|
+
if locations:
|
|
31
|
+
return str(Path(locations[0]))
|
|
32
|
+
return None
|
|
33
|
+
except (ModuleNotFoundError, ValueError):
|
|
34
|
+
return None
|
|
35
|
+
except Exception as e:
|
|
36
|
+
debug_log(f"Error getting local path for {package_name}: {e}")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def _get_npm_package_path(package_name: str) -> Optional[str]:
|
|
40
|
+
"""
|
|
41
|
+
Finds the local installation path of a Node.js package using `npm root`.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
debug_log(f"Getting local path for npm package: {package_name}")
|
|
45
|
+
local_path = Path(f"./node_modules/{package_name}")
|
|
46
|
+
if local_path.exists():
|
|
47
|
+
return str(local_path.resolve())
|
|
48
|
+
|
|
49
|
+
result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True)
|
|
50
|
+
if result.returncode == 0:
|
|
51
|
+
global_root = result.stdout.strip()
|
|
52
|
+
package_path = Path(global_root) / package_name
|
|
53
|
+
if package_path.exists():
|
|
54
|
+
return str(package_path.resolve())
|
|
55
|
+
return None
|
|
56
|
+
except Exception as e:
|
|
57
|
+
debug_log(f"Error getting npm package path for {package_name}: {e}")
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
def _get_typescript_package_path(package_name: str) -> Optional[str]:
|
|
61
|
+
"""
|
|
62
|
+
Finds the local installation path of a TypeScript package.
|
|
63
|
+
TypeScript packages are typically npm packages, so this uses the same logic as npm.
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
debug_log(f"Getting local path for TypeScript package: {package_name}")
|
|
67
|
+
|
|
68
|
+
# Check local node_modules first
|
|
69
|
+
local_path = Path(f"./node_modules/{package_name}")
|
|
70
|
+
if local_path.exists():
|
|
71
|
+
return str(local_path.resolve())
|
|
72
|
+
|
|
73
|
+
# Check global npm packages
|
|
74
|
+
result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True, timeout=5)
|
|
75
|
+
if result.returncode == 0:
|
|
76
|
+
global_root = result.stdout.strip()
|
|
77
|
+
package_path = Path(global_root) / package_name
|
|
78
|
+
if package_path.exists():
|
|
79
|
+
return str(package_path.resolve())
|
|
80
|
+
|
|
81
|
+
return None
|
|
82
|
+
except subprocess.TimeoutExpired:
|
|
83
|
+
debug_log(f"npm command timed out for {package_name}")
|
|
84
|
+
return None
|
|
85
|
+
except Exception as e:
|
|
86
|
+
debug_log(f"Error getting TypeScript package path for {package_name}: {e}")
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def _get_java_package_path(package_name: str) -> Optional[str]:
|
|
90
|
+
"""
|
|
91
|
+
Finds the local installation path of a Java package (JAR).
|
|
92
|
+
Searches in Maven and Gradle cache directories.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
package_name: Package name in format "groupId:artifactId" (e.g., "com.google.code.gson:gson")
|
|
96
|
+
or just "artifactId" for simple search.
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
debug_log(f"Getting local path for Java package: {package_name}")
|
|
100
|
+
|
|
101
|
+
# Parse package name - expect format "groupId:artifactId" or just "artifactId"
|
|
102
|
+
if ':' in package_name:
|
|
103
|
+
group_id, artifact_id = package_name.split(':', 1)
|
|
104
|
+
# Convert group_id dots to path separators (e.g., com.google.gson -> com/google/gson)
|
|
105
|
+
group_path = group_id.replace('.', '/')
|
|
106
|
+
else:
|
|
107
|
+
# If only artifact_id provided, search for it
|
|
108
|
+
artifact_id = package_name
|
|
109
|
+
group_path = None
|
|
110
|
+
|
|
111
|
+
search_paths = []
|
|
112
|
+
|
|
113
|
+
# Maven repository (~/.m2/repository)
|
|
114
|
+
maven_repo = Path.home() / ".m2" / "repository"
|
|
115
|
+
if maven_repo.exists():
|
|
116
|
+
if group_path:
|
|
117
|
+
# Search for specific group/artifact
|
|
118
|
+
package_path = maven_repo / group_path / artifact_id
|
|
119
|
+
if package_path.exists():
|
|
120
|
+
# Find the latest version directory
|
|
121
|
+
version_dirs = [d for d in package_path.iterdir() if d.is_dir()]
|
|
122
|
+
if version_dirs:
|
|
123
|
+
# Sort by name (assumes semantic versioning) and get the latest
|
|
124
|
+
latest_version = sorted(version_dirs, key=lambda x: x.name)[-1]
|
|
125
|
+
return str(latest_version.resolve())
|
|
126
|
+
else:
|
|
127
|
+
# Search for artifact_id in the entire Maven repo
|
|
128
|
+
search_paths.append(maven_repo)
|
|
129
|
+
|
|
130
|
+
# Gradle cache (~/.gradle/caches/modules-2/files-2.1)
|
|
131
|
+
gradle_cache = Path.home() / ".gradle" / "caches" / "modules-2" / "files-2.1"
|
|
132
|
+
if gradle_cache.exists():
|
|
133
|
+
if group_path:
|
|
134
|
+
group_id_full = group_id if ':' in package_name else None
|
|
135
|
+
if group_id_full:
|
|
136
|
+
package_path = gradle_cache / group_id_full / artifact_id
|
|
137
|
+
if package_path.exists():
|
|
138
|
+
# Find the latest version directory
|
|
139
|
+
version_dirs = [d for d in package_path.iterdir() if d.is_dir()]
|
|
140
|
+
if version_dirs:
|
|
141
|
+
latest_version = sorted(version_dirs, key=lambda x: x.name)[-1]
|
|
142
|
+
# Gradle stores files in hash subdirectories
|
|
143
|
+
hash_dirs = [d for d in latest_version.iterdir() if d.is_dir()]
|
|
144
|
+
if hash_dirs:
|
|
145
|
+
return str(hash_dirs[0].resolve())
|
|
146
|
+
else:
|
|
147
|
+
search_paths.append(gradle_cache)
|
|
148
|
+
|
|
149
|
+
# If group_path wasn't provided or not found, search in the cache directories
|
|
150
|
+
if not group_path or search_paths:
|
|
151
|
+
for base_path in search_paths:
|
|
152
|
+
for jar_file in base_path.rglob(f"*{artifact_id}*.jar"):
|
|
153
|
+
return str(jar_file.parent.resolve())
|
|
154
|
+
|
|
155
|
+
# Check local lib directories
|
|
156
|
+
local_lib_paths = [
|
|
157
|
+
Path("./lib"),
|
|
158
|
+
Path("./libs"),
|
|
159
|
+
Path("/usr/local/lib/java"),
|
|
160
|
+
Path("/opt/java/lib"),
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
for lib_path in local_lib_paths:
|
|
164
|
+
if not lib_path.exists():
|
|
165
|
+
continue
|
|
166
|
+
|
|
167
|
+
# Look for JAR files matching the artifact name
|
|
168
|
+
for jar_file in lib_path.glob(f"*{artifact_id}*.jar"):
|
|
169
|
+
return str(jar_file.resolve())
|
|
170
|
+
|
|
171
|
+
return None
|
|
172
|
+
except Exception as e:
|
|
173
|
+
debug_log(f"Error getting Java package path for {package_name}: {e}")
|
|
174
|
+
def _get_c_package_path(package_name: str) -> Optional[str]:
|
|
175
|
+
"""
|
|
176
|
+
Finds the local installation path of a C package.
|
|
177
|
+
"""
|
|
178
|
+
try:
|
|
179
|
+
debug_log(f"Getting local path for C package: {package_name}")
|
|
180
|
+
|
|
181
|
+
# Try using pkg-config to find the package
|
|
182
|
+
try:
|
|
183
|
+
result = subprocess.run(
|
|
184
|
+
["pkg-config", "--variable=includedir", package_name],
|
|
185
|
+
capture_output=True,
|
|
186
|
+
text=True,
|
|
187
|
+
timeout=5
|
|
188
|
+
)
|
|
189
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
190
|
+
include_dir = Path(result.stdout.strip())
|
|
191
|
+
package_path = include_dir / package_name
|
|
192
|
+
if package_path.exists():
|
|
193
|
+
return str(package_path.resolve())
|
|
194
|
+
if include_dir.exists():
|
|
195
|
+
return str(include_dir.resolve())
|
|
196
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
197
|
+
debug_log(f"pkg-config not available or timed out for {package_name}")
|
|
198
|
+
|
|
199
|
+
# Search in standard system include directories
|
|
200
|
+
common_include_paths = [
|
|
201
|
+
"/usr/include",
|
|
202
|
+
"/usr/local/include",
|
|
203
|
+
"/opt/homebrew/include",
|
|
204
|
+
"/opt/local/include",
|
|
205
|
+
Path.home() / ".local" / "include",
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
for base_path in common_include_paths:
|
|
209
|
+
base_path = Path(base_path)
|
|
210
|
+
if not base_path.exists():
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
# Check if package exists as a directory
|
|
214
|
+
package_dir = base_path / package_name
|
|
215
|
+
if package_dir.exists() and package_dir.is_dir():
|
|
216
|
+
return str(package_dir.resolve())
|
|
217
|
+
|
|
218
|
+
# Check for header files with package name
|
|
219
|
+
header_file = base_path / f"{package_name}.h"
|
|
220
|
+
if header_file.exists():
|
|
221
|
+
return str(header_file.resolve())
|
|
222
|
+
|
|
223
|
+
# Check current directory for local installations
|
|
224
|
+
local_package = Path(f"./{package_name}")
|
|
225
|
+
if local_package.exists():
|
|
226
|
+
return str(local_package.resolve())
|
|
227
|
+
|
|
228
|
+
return None
|
|
229
|
+
except Exception as e:
|
|
230
|
+
debug_log(f"Error getting C package path for {package_name}: {e}")
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
def _get_ruby_package_path(package_name: str) -> Optional[str]:
|
|
234
|
+
"""
|
|
235
|
+
Finds the local installation path of a Ruby gem.
|
|
236
|
+
"""
|
|
237
|
+
try:
|
|
238
|
+
debug_log(f"Getting local path for Ruby gem: {package_name}")
|
|
239
|
+
result = subprocess.run(
|
|
240
|
+
["gem", "which", package_name],
|
|
241
|
+
capture_output=True,
|
|
242
|
+
text=True,
|
|
243
|
+
timeout=5
|
|
244
|
+
)
|
|
245
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
246
|
+
gem_path = Path(result.stdout.strip())
|
|
247
|
+
if gem_path.exists():
|
|
248
|
+
lib_dir = gem_path.parent if gem_path.is_file() else gem_path
|
|
249
|
+
# If we are inside a gem (…/gems/foo-x.y.z/lib/foo.rb), prefer the lib/ dir:
|
|
250
|
+
if (lib_dir.name == "lib") and lib_dir.is_dir():
|
|
251
|
+
return str(lib_dir.resolve())
|
|
252
|
+
# Try parent/lib in case `gem which` returned .../lib/foo.rb
|
|
253
|
+
if (lib_dir / "lib").is_dir():
|
|
254
|
+
return str((lib_dir / "lib").resolve())
|
|
255
|
+
# Fallback: just return the directory containing the file (stdlib case like 'json')
|
|
256
|
+
return str(lib_dir.resolve())
|
|
257
|
+
return None
|
|
258
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
259
|
+
debug_log(f"gem command not available or timed out for {package_name}")
|
|
260
|
+
return None
|
|
261
|
+
except Exception as e:
|
|
262
|
+
debug_log(f"Error getting Ruby gem path for {package_name}: {e}")
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
def _get_go_package_path(package_name: str) -> Optional[str]:
|
|
266
|
+
"""
|
|
267
|
+
Finds the local installation path of a Go package using `go list`.
|
|
268
|
+
Tries multiple approaches in sequence to handle different package scenarios:
|
|
269
|
+
1) package dir: go list -f '{{.Dir}}' <pkg> (works for stdlib, GOPATH, or module subpackages)
|
|
270
|
+
2) module root: go list -m -f '{{.Dir}}' <module> (works for full module paths)
|
|
271
|
+
3) force mod: go list -mod=mod -f '{{.Dir}}' <pkg> (works when outside a module context)
|
|
272
|
+
4) GOROOT check: for standard library packages
|
|
273
|
+
5) GOPATH check: for packages in GOPATH
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
def _first_existing_dir(output: str) -> Optional[str]:
|
|
277
|
+
for line in (l.strip().strip("'\"") for l in output.splitlines() if l.strip()):
|
|
278
|
+
p = Path(line)
|
|
279
|
+
if p.exists() and p.is_dir():
|
|
280
|
+
return str(p.resolve())
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
debug_log(f"Getting local path for Go package: {package_name}")
|
|
285
|
+
|
|
286
|
+
# 1. Package directory (works for stdlib, GOPATH, or subpackages)
|
|
287
|
+
cp = subprocess.run(
|
|
288
|
+
["go", "list", "-f", "{{.Dir}}", package_name],
|
|
289
|
+
capture_output=True, text=True, timeout=15
|
|
290
|
+
)
|
|
291
|
+
if cp.returncode == 0:
|
|
292
|
+
d = _first_existing_dir(cp.stdout)
|
|
293
|
+
if d:
|
|
294
|
+
return d
|
|
295
|
+
|
|
296
|
+
# 2. Module root directory (where go.mod lives)
|
|
297
|
+
cp2 = subprocess.run(
|
|
298
|
+
["go", "list", "-m", "-f", "{{.Dir}}", package_name],
|
|
299
|
+
capture_output=True, text=True, timeout=15
|
|
300
|
+
)
|
|
301
|
+
if cp2.returncode == 0:
|
|
302
|
+
d = _first_existing_dir(cp2.stdout)
|
|
303
|
+
if d:
|
|
304
|
+
debug_log(f"Found Go module {package_name} at {d}")
|
|
305
|
+
return d
|
|
306
|
+
|
|
307
|
+
# 3. Retry forcing module mode
|
|
308
|
+
cp3 = subprocess.run(
|
|
309
|
+
["go", "list", "-mod=mod", "-f", "{{.Dir}}", package_name],
|
|
310
|
+
capture_output=True, text=True, timeout=15
|
|
311
|
+
)
|
|
312
|
+
if cp3.returncode == 0:
|
|
313
|
+
d = _first_existing_dir(cp3.stdout)
|
|
314
|
+
if d:
|
|
315
|
+
return d
|
|
316
|
+
|
|
317
|
+
# 4. Check in GOROOT for standard library packages
|
|
318
|
+
try:
|
|
319
|
+
cp4 = subprocess.run(
|
|
320
|
+
["go", "env", "GOROOT"],
|
|
321
|
+
capture_output=True, text=True, timeout=5
|
|
322
|
+
)
|
|
323
|
+
if cp4.returncode == 0:
|
|
324
|
+
goroot = cp4.stdout.strip()
|
|
325
|
+
if goroot:
|
|
326
|
+
std_lib_path = Path(goroot) / "src" / package_name
|
|
327
|
+
if std_lib_path.exists() and std_lib_path.is_dir():
|
|
328
|
+
return str(std_lib_path.resolve())
|
|
329
|
+
except Exception as e:
|
|
330
|
+
debug_log(f"Error checking GOROOT for {package_name}: {e}")
|
|
331
|
+
|
|
332
|
+
# 5. Check in GOPATH as fallback
|
|
333
|
+
try:
|
|
334
|
+
cp5 = subprocess.run(
|
|
335
|
+
["go", "env", "GOPATH"],
|
|
336
|
+
capture_output=True, text=True, timeout=5
|
|
337
|
+
)
|
|
338
|
+
if cp5.returncode == 0:
|
|
339
|
+
gopath = cp5.stdout.strip()
|
|
340
|
+
if gopath:
|
|
341
|
+
gopath_lib_path = Path(gopath) / "src" / package_name
|
|
342
|
+
if gopath_lib_path.exists() and gopath_lib_path.is_dir():
|
|
343
|
+
debug_log(f"Found Go package in GOPATH {package_name} at {gopath_lib_path}")
|
|
344
|
+
return str(gopath_lib_path.resolve())
|
|
345
|
+
except Exception as e:
|
|
346
|
+
debug_log(f"Error checking GOPATH for {package_name}: {e}")
|
|
347
|
+
|
|
348
|
+
debug_log(f"Could not find Go package: {package_name}")
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
352
|
+
debug_log(f"go command not available or timed out for {package_name}")
|
|
353
|
+
return None
|
|
354
|
+
except Exception as e:
|
|
355
|
+
debug_log(f"Error getting Go package path for {package_name}: {e}")
|
|
356
|
+
return None
|
|
357
|
+
|
|
358
|
+
def _get_php_package_path(package_name: str) -> Optional[str]:
|
|
359
|
+
try:
|
|
360
|
+
debug_log(f"Getting local path for PHP package: {package_name}")
|
|
361
|
+
|
|
362
|
+
local_vendor = Path("./vendor") / package_name
|
|
363
|
+
if local_vendor.exists() and local_vendor.is_dir():
|
|
364
|
+
return str(local_vendor.resolve())
|
|
365
|
+
|
|
366
|
+
current_dir = Path.cwd()
|
|
367
|
+
for parent in [current_dir] + list(current_dir.parents):
|
|
368
|
+
vendor_path = parent / "vendor" / package_name
|
|
369
|
+
if vendor_path.exists() and vendor_path.is_dir():
|
|
370
|
+
return str(vendor_path.resolve())
|
|
371
|
+
|
|
372
|
+
if (parent / "composer.json").exists():
|
|
373
|
+
break
|
|
374
|
+
|
|
375
|
+
composer_home = Path.home() / ".composer" / "vendor" / package_name
|
|
376
|
+
if composer_home.exists() and composer_home.is_dir():
|
|
377
|
+
return str(composer_home.resolve())
|
|
378
|
+
|
|
379
|
+
composer_global = Path.home() / ".config" / "composer" / "vendor" / package_name
|
|
380
|
+
if composer_global.exists() and composer_global.is_dir():
|
|
381
|
+
return str(composer_global.resolve())
|
|
382
|
+
|
|
383
|
+
return None
|
|
384
|
+
except Exception as e:
|
|
385
|
+
debug_log(f"Error getting PHP package path for {package_name}: {e}")
|
|
386
|
+
return None
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _get_dart_package_path(package_name: str) -> Optional[str]:
|
|
391
|
+
"""
|
|
392
|
+
Finds the local installation path of a Dart package.
|
|
393
|
+
Uses 'dart pub cache list' or looks in PUB_CACHE.
|
|
394
|
+
"""
|
|
395
|
+
try:
|
|
396
|
+
import os
|
|
397
|
+
debug_log(f"Getting local path for Dart package: {package_name}")
|
|
398
|
+
|
|
399
|
+
# Check environment variable
|
|
400
|
+
pub_cache = os.environ.get("PUB_CACHE")
|
|
401
|
+
if not pub_cache:
|
|
402
|
+
pub_cache = str(Path.home() / ".pub-cache")
|
|
403
|
+
|
|
404
|
+
hosted_path = Path(pub_cache) / "hosted" / "pub.dev" / package_name
|
|
405
|
+
if hosted_path.exists():
|
|
406
|
+
# Find the latest version if multiple exist
|
|
407
|
+
versions = [d for d in hosted_path.parent.glob(f"{package_name}-*") if d.is_dir()]
|
|
408
|
+
if versions:
|
|
409
|
+
return str(sorted(versions)[-1].resolve())
|
|
410
|
+
return str(hosted_path.resolve())
|
|
411
|
+
|
|
412
|
+
return None
|
|
413
|
+
except Exception as e:
|
|
414
|
+
debug_log(f"Error getting Dart package path for {package_name}: {e}")
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
def get_local_package_path(package_name: str, language: str) -> Optional[str]:
|
|
418
|
+
"""
|
|
419
|
+
Dispatches to the correct package path finder based on the language.
|
|
420
|
+
"""
|
|
421
|
+
finders = {
|
|
422
|
+
"python": _get_python_package_path,
|
|
423
|
+
"javascript": _get_npm_package_path,
|
|
424
|
+
"typescript": _get_typescript_package_path,
|
|
425
|
+
"java": _get_java_package_path,
|
|
426
|
+
"c": _get_c_package_path,
|
|
427
|
+
"go": _get_go_package_path,
|
|
428
|
+
"ruby": _get_ruby_package_path,
|
|
429
|
+
"php": _get_php_package_path,
|
|
430
|
+
"cpp": _get_cpp_package_path,
|
|
431
|
+
"dart": _get_dart_package_path,
|
|
432
|
+
}
|
|
433
|
+
finder = finders.get(language)
|
|
434
|
+
if finder:
|
|
435
|
+
return finder(package_name)
|
|
436
|
+
return None
|
|
437
|
+
|
|
438
|
+
def _get_cpp_package_path(package_name: str) -> Optional[str]:
|
|
439
|
+
"""
|
|
440
|
+
C++ package ka local path find karta hai.
|
|
441
|
+
Pehle pkg-config try karta hai, fir common system paths check karta hai.
|
|
442
|
+
"""
|
|
443
|
+
import subprocess
|
|
444
|
+
import os
|
|
445
|
+
|
|
446
|
+
# Try pkg-config
|
|
447
|
+
try:
|
|
448
|
+
result = subprocess.run(
|
|
449
|
+
["pkg-config", "--variable=includedir", package_name],
|
|
450
|
+
capture_output=True,
|
|
451
|
+
text=True,
|
|
452
|
+
check=False
|
|
453
|
+
)
|
|
454
|
+
path = result.stdout.strip()
|
|
455
|
+
if path and os.path.exists(path):
|
|
456
|
+
return path
|
|
457
|
+
except FileNotFoundError:
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
# Common system include/lib folders
|
|
461
|
+
common_paths = [
|
|
462
|
+
f"/usr/include/{package_name}",
|
|
463
|
+
f"/usr/local/include/{package_name}",
|
|
464
|
+
f"/usr/lib/{package_name}",
|
|
465
|
+
f"/usr/local/lib/{package_name}",
|
|
466
|
+
]
|
|
467
|
+
for path in common_paths:
|
|
468
|
+
if os.path.exists(path):
|
|
469
|
+
return path
|
|
470
|
+
|
|
471
|
+
return None
|
|
472
|
+
|