Skip to main content
Glama

CodeGraphContext

package_resolver.py17.5 kB
# src/codegraphcontext/tools/package_resolver.py import importlib import stdlibs from pathlib import Path import subprocess from typing import Optional from ..utils.debug_log import debug_log def _get_python_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a Python package. """ try: debug_log(f"Getting local path for Python package: {package_name}") module = importlib.import_module(package_name) if hasattr(module, '__file__') and module.__file__: module_file = Path(module.__file__) if module_file.name == '__init__.py': return str(module_file.parent) elif package_name in stdlibs.module_names: return str(module_file) else: return str(module_file.parent) elif hasattr(module, '__path__'): if isinstance(module.__path__, list) and module.__path__: return str(Path(module.__path__[0])) else: return str(Path(str(module.__path__))) return None except ImportError: return None except Exception as e: debug_log(f"Error getting local path for {package_name}: {e}") return None def _get_npm_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a Node.js package using `npm root`. """ try: debug_log(f"Getting local path for npm package: {package_name}") local_path = Path(f"./node_modules/{package_name}") if local_path.exists(): return str(local_path.resolve()) result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True) if result.returncode == 0: global_root = result.stdout.strip() package_path = Path(global_root) / package_name if package_path.exists(): return str(package_path.resolve()) return None except Exception as e: debug_log(f"Error getting npm package path for {package_name}: {e}") return None def _get_typescript_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a TypeScript package. TypeScript packages are typically npm packages, so this uses the same logic as npm. """ try: debug_log(f"Getting local path for TypeScript package: {package_name}") # Check local node_modules first local_path = Path(f"./node_modules/{package_name}") if local_path.exists(): return str(local_path.resolve()) # Check global npm packages result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True, timeout=5) if result.returncode == 0: global_root = result.stdout.strip() package_path = Path(global_root) / package_name if package_path.exists(): return str(package_path.resolve()) return None except subprocess.TimeoutExpired: debug_log(f"npm command timed out for {package_name}") return None except Exception as e: debug_log(f"Error getting TypeScript package path for {package_name}: {e}") return None def _get_java_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a Java package (JAR). Searches in Maven and Gradle cache directories. Args: package_name: Package name in format "groupId:artifactId" (e.g., "com.google.code.gson:gson") or just "artifactId" for simple search. """ try: debug_log(f"Getting local path for Java package: {package_name}") # Parse package name - expect format "groupId:artifactId" or just "artifactId" if ':' in package_name: group_id, artifact_id = package_name.split(':', 1) # Convert group_id dots to path separators (e.g., com.google.gson -> com/google/gson) group_path = group_id.replace('.', '/') else: # If only artifact_id provided, search for it artifact_id = package_name group_path = None search_paths = [] # Maven repository (~/.m2/repository) maven_repo = Path.home() / ".m2" / "repository" if maven_repo.exists(): if group_path: # Search for specific group/artifact package_path = maven_repo / group_path / artifact_id if package_path.exists(): # Find the latest version directory version_dirs = [d for d in package_path.iterdir() if d.is_dir()] if version_dirs: # Sort by name (assumes semantic versioning) and get the latest latest_version = sorted(version_dirs, key=lambda x: x.name)[-1] return str(latest_version.resolve()) else: # Search for artifact_id in the entire Maven repo search_paths.append(maven_repo) # Gradle cache (~/.gradle/caches/modules-2/files-2.1) gradle_cache = Path.home() / ".gradle" / "caches" / "modules-2" / "files-2.1" if gradle_cache.exists(): if group_path: group_id_full = group_id if ':' in package_name else None if group_id_full: package_path = gradle_cache / group_id_full / artifact_id if package_path.exists(): # Find the latest version directory version_dirs = [d for d in package_path.iterdir() if d.is_dir()] if version_dirs: latest_version = sorted(version_dirs, key=lambda x: x.name)[-1] # Gradle stores files in hash subdirectories hash_dirs = [d for d in latest_version.iterdir() if d.is_dir()] if hash_dirs: return str(hash_dirs[0].resolve()) else: search_paths.append(gradle_cache) # If group_path wasn't provided or not found, search in the cache directories if not group_path or search_paths: for base_path in search_paths: for jar_file in base_path.rglob(f"*{artifact_id}*.jar"): return str(jar_file.parent.resolve()) # Check local lib directories local_lib_paths = [ Path("./lib"), Path("./libs"), Path("/usr/local/lib/java"), Path("/opt/java/lib"), ] for lib_path in local_lib_paths: if not lib_path.exists(): continue # Look for JAR files matching the artifact name for jar_file in lib_path.glob(f"*{artifact_id}*.jar"): return str(jar_file.resolve()) return None except Exception as e: debug_log(f"Error getting Java package path for {package_name}: {e}") def _get_c_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a C package. """ try: debug_log(f"Getting local path for C package: {package_name}") # Try using pkg-config to find the package try: result = subprocess.run( ["pkg-config", "--variable=includedir", package_name], capture_output=True, text=True, timeout=5 ) if result.returncode == 0 and result.stdout.strip(): include_dir = Path(result.stdout.strip()) package_path = include_dir / package_name if package_path.exists(): return str(package_path.resolve()) if include_dir.exists(): return str(include_dir.resolve()) except (subprocess.TimeoutExpired, FileNotFoundError): debug_log(f"pkg-config not available or timed out for {package_name}") # Search in standard system include directories common_include_paths = [ "/usr/include", "/usr/local/include", "/opt/homebrew/include", "/opt/local/include", Path.home() / ".local" / "include", ] for base_path in common_include_paths: base_path = Path(base_path) if not base_path.exists(): continue # Check if package exists as a directory package_dir = base_path / package_name if package_dir.exists() and package_dir.is_dir(): return str(package_dir.resolve()) # Check for header files with package name header_file = base_path / f"{package_name}.h" if header_file.exists(): return str(header_file.resolve()) # Check current directory for local installations local_package = Path(f"./{package_name}") if local_package.exists(): return str(local_package.resolve()) return None except Exception as e: debug_log(f"Error getting C package path for {package_name}: {e}") return None def _get_ruby_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a Ruby gem. """ try: debug_log(f"Getting local path for Ruby gem: {package_name}") result = subprocess.run( ["gem", "which", package_name], capture_output=True, text=True, timeout=5 ) if result.returncode == 0 and result.stdout.strip(): gem_path = Path(result.stdout.strip()) if gem_path.exists(): lib_dir = gem_path.parent if gem_path.is_file() else gem_path # If we are inside a gem (…/gems/foo-x.y.z/lib/foo.rb), prefer the lib/ dir: if (lib_dir.name == "lib") and lib_dir.is_dir(): return str(lib_dir.resolve()) # Try parent/lib in case `gem which` returned .../lib/foo.rb if (lib_dir / "lib").is_dir(): return str((lib_dir / "lib").resolve()) # Fallback: just return the directory containing the file (stdlib case like 'json') return str(lib_dir.resolve()) return None except (subprocess.TimeoutExpired, FileNotFoundError): debug_log(f"gem command not available or timed out for {package_name}") return None except Exception as e: debug_log(f"Error getting Ruby gem path for {package_name}: {e}") return None def _get_go_package_path(package_name: str) -> Optional[str]: """ Finds the local installation path of a Go package using `go list`. Tries multiple approaches in sequence to handle different package scenarios: 1) package dir: go list -f '{{.Dir}}' <pkg> (works for stdlib, GOPATH, or module subpackages) 2) module root: go list -m -f '{{.Dir}}' <module> (works for full module paths) 3) force mod: go list -mod=mod -f '{{.Dir}}' <pkg> (works when outside a module context) 4) GOROOT check: for standard library packages 5) GOPATH check: for packages in GOPATH """ def _first_existing_dir(output: str) -> Optional[str]: for line in (l.strip().strip("'\"") for l in output.splitlines() if l.strip()): p = Path(line) if p.exists() and p.is_dir(): return str(p.resolve()) return None try: debug_log(f"Getting local path for Go package: {package_name}") # 1. Package directory (works for stdlib, GOPATH, or subpackages) cp = subprocess.run( ["go", "list", "-f", "{{.Dir}}", package_name], capture_output=True, text=True, timeout=15 ) if cp.returncode == 0: d = _first_existing_dir(cp.stdout) if d: return d # 2. Module root directory (where go.mod lives) cp2 = subprocess.run( ["go", "list", "-m", "-f", "{{.Dir}}", package_name], capture_output=True, text=True, timeout=15 ) if cp2.returncode == 0: d = _first_existing_dir(cp2.stdout) if d: debug_log(f"Found Go module {package_name} at {d}") return d # 3. Retry forcing module mode cp3 = subprocess.run( ["go", "list", "-mod=mod", "-f", "{{.Dir}}", package_name], capture_output=True, text=True, timeout=15 ) if cp3.returncode == 0: d = _first_existing_dir(cp3.stdout) if d: return d # 4. Check in GOROOT for standard library packages try: cp4 = subprocess.run( ["go", "env", "GOROOT"], capture_output=True, text=True, timeout=5 ) if cp4.returncode == 0: goroot = cp4.stdout.strip() if goroot: std_lib_path = Path(goroot) / "src" / package_name if std_lib_path.exists() and std_lib_path.is_dir(): return str(std_lib_path.resolve()) except Exception as e: debug_log(f"Error checking GOROOT for {package_name}: {e}") # 5. Check in GOPATH as fallback try: cp5 = subprocess.run( ["go", "env", "GOPATH"], capture_output=True, text=True, timeout=5 ) if cp5.returncode == 0: gopath = cp5.stdout.strip() if gopath: gopath_lib_path = Path(gopath) / "src" / package_name if gopath_lib_path.exists() and gopath_lib_path.is_dir(): debug_log(f"Found Go package in GOPATH {package_name} at {gopath_lib_path}") return str(gopath_lib_path.resolve()) except Exception as e: debug_log(f"Error checking GOPATH for {package_name}: {e}") debug_log(f"Could not find Go package: {package_name}") return None except (subprocess.TimeoutExpired, FileNotFoundError): debug_log(f"go command not available or timed out for {package_name}") return None except Exception as e: debug_log(f"Error getting Go package path for {package_name}: {e}") return None def _get_php_package_path(package_name: str) -> Optional[str]: try: debug_log(f"Getting local path for PHP package: {package_name}") local_vendor = Path("./vendor") / package_name if local_vendor.exists() and local_vendor.is_dir(): return str(local_vendor.resolve()) current_dir = Path.cwd() for parent in [current_dir] + list(current_dir.parents): vendor_path = parent / "vendor" / package_name if vendor_path.exists() and vendor_path.is_dir(): return str(vendor_path.resolve()) if (parent / "composer.json").exists(): break composer_home = Path.home() / ".composer" / "vendor" / package_name if composer_home.exists() and composer_home.is_dir(): return str(composer_home.resolve()) composer_global = Path.home() / ".config" / "composer" / "vendor" / package_name if composer_global.exists() and composer_global.is_dir(): return str(composer_global.resolve()) return None except Exception as e: debug_log(f"Error getting PHP package path for {package_name}: {e}") return None def get_local_package_path(package_name: str, language: str) -> Optional[str]: """ Dispatches to the correct package path finder based on the language. """ finders = { "python": _get_python_package_path, "javascript": _get_npm_package_path, "typescript": _get_typescript_package_path, "java": _get_java_package_path, "c": _get_c_package_path, "go": _get_go_package_path, "ruby": _get_ruby_package_path, "php": _get_php_package_path, "cpp": _get_cpp_package_path, } finder = finders.get(language) if finder: return finder(package_name) return None def _get_cpp_package_path(package_name: str) -> Optional[str]: """ C++ package ka local path find karta hai. Pehle pkg-config try karta hai, fir common system paths check karta hai. """ import subprocess import os # Try pkg-config try: result = subprocess.run( ["pkg-config", "--variable=includedir", package_name], capture_output=True, text=True, check=False ) path = result.stdout.strip() if path and os.path.exists(path): return path except FileNotFoundError: pass # Common system include/lib folders common_paths = [ f"/usr/include/{package_name}", f"/usr/local/include/{package_name}", f"/usr/lib/{package_name}", f"/usr/local/lib/{package_name}", ] for path in common_paths: if os.path.exists(path): return path return None

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Shashankss1205/CodeGraphContext'

If you have feedback or need assistance with the MCP directory API, please join our Discord server