SkillSpector Input Resolution Process: How Git URLs, ZIP Files, and Directories Are Normalized
SkillSpector normalizes Git URLs, ZIP archives, raw file URLs, and local directories into a standardized local directory path through a hierarchical detection system implemented in InputHandler.resolve(), creating temporary workspaces for remote inputs while returning local paths unchanged.
The NVIDIA SkillSpector repository accepts multiple input formats for static analysis, requiring a robust normalization layer. Located in src/skillspector/input_handler.py, the InputHandler class implements the input resolution process that converts any supported source into a scannable filesystem directory. This architecture ensures that whether you point the tool at a GitHub repository, a downloaded ZIP, or a local folder, the analysis engine receives a consistent local path.
Entry Point and Detection Order
The resolution pipeline begins at InputHandler.resolve() (lines 55‑66), which returns a tuple of (Path, source_type) where source_type is one of "git", "url", "zip", "file", or "directory".
The method applies a strict priority order to determine input type:
- Git URL – Validated via
_is_git_url() - File URL – Checked via
_is_file_url()for raw content links - ZIP archive – Identified by
.zipextension - Markdown file – Identified by
.mdextension - Local directory – Verified via
Path.is_dir() - Local file – Verified via
Path.is_file()
If the input matches the first three categories, the handler initializes a temporary working directory (self._temp_dir) that persists until cleanup() is invoked.
Git URL Detection and Cloning
Identifying Git Repositories (_is_git_url)
The _is_git_url() method (lines 105‑117) distinguishes between repository URLs and raw file links using multi-layer validation:
# src/skillspector/input_handler.py, lines 105-117
def _is_git_url(self, path: str) -> bool:
if not path.startswith(("http://", "https://", "git@")):
return False
parsed = urlparse(path)
git_hosts = ["github.com", "gitlab.com", "bitbucket.org"]
if any(host in parsed.netloc for host in git_hosts):
# Exclude raw file paths (e.g. /raw/, /blob/) that look like URLs
if "/raw/" in path or "/blob/" in path or path.endswith((".md", ".py", ".sh")):
return False
return True
if path.endswith(".git"):
return True
return False
The logic requires an HTTP/HTTPS or git@ scheme, checks against major Git hosts (GitHub, GitLab, Bitbucket), and explicitly rejects URLs containing /raw/ or /blob/ paths or file extensions like .md, .py, or .sh. A trailing .git suffix also triggers a positive match regardless of host.
Shallow Clone Implementation (_clone_git)
Once identified as a Git URL, the repository is cloned via _clone_git() (lines 125‑149):
# src/skillspector/input_handler.py, lines 125-149
def _clone_git(self, url: str) -> Path:
temp_dir = self._get_temp_dir()
clone_dir = temp_dir / "repo"
subprocess.run(
["git", "clone", "--depth", "1", url, str(clone_dir)],
check=True,
capture_output=True,
timeout=60,
shell=False,
)
# Errors are wrapped into ValueError with helpful logging
return clone_dir
The implementation performs a shallow clone (--depth 1) to minimize bandwidth and time, places the repository in {temp_dir}/repo, and enforces a 60‑second timeout. Clone failures raise ValueError with detailed logging.
ZIP Archive Processing
For ZIP inputs, the _extract_zip() method (lines 173‑189) handles extraction and normalization:
# src/skillspector/input_handler.py, lines 173-189
def _extract_zip(self, zip_path: Path) -> Path:
temp_dir = self._get_temp_dir()
extract_dir = temp_dir / "extracted"
extract_dir.mkdir(exist_ok=True)
with zipfile.ZipFile(zip_path, "r") as zf:
zf.extractall(extract_dir)
# If the zip contains a single top‑level folder, return that folder
contents = list(extract_dir.iterdir())
if len(contents) == 1 and contents[0].is_dir():
return contents[0]
return extract_dir
The method extracts archives into {temp_dir}/extracted. If the ZIP contains a single top‑level directory (common for GitHub download ZIPs), it returns that subdirectory directly, providing a clean path for the analysis graph.
Local Directory Normalization
When the input resolves to an existing directory on the host filesystem, resolve() returns Path(input_path).resolve() (line 80) with source_type set to "directory". This path requires no temporary storage or cleanup, as the tool reads directly from the provided location.
Temporary Resource Management
Remote inputs (Git, URL, ZIP) trigger temporary directory creation via _get_temp_dir(). The cleanup() method (lines 89‑93) removes these workspaces after analysis completes, preventing file system pollution. The temporary directory reference is tracked in src/skillspector/state.py (temp_dir_for_cleanup) to ensure the graph execution can invoke cleanup even if the handler instance goes out of scope.
Practical Usage Examples
Scanning a Public GitHub Repository
skillspector scan https://github.com/NVIDIA/SkillSpector
Internally, InputHandler.resolve() recognizes the URL via _is_git_url(), invokes _clone_git() for a shallow clone, and scans the resulting temporary directory.
Processing a Remote ZIP Archive
skillspector scan https://example.com/some_tool.zip
The URL passes _is_file_url() detection, downloads to a temporary location, and routes through _extract_zip() to normalize the archive structure.
Using the API Programmatically
from skillspector.input_handler import InputHandler
handler = InputHandler()
path, src_type = handler.resolve("https://github.com/NVIDIA/SkillSpector")
print(f"Resolved {src_type} to {path}")
# ... run the analysis ...
handler.cleanup() # delete any temp directories
Summary
InputHandler.resolve()serves as the unified entry point for all input types, implementing a hierarchical detection system insrc/skillspector/input_handler.py.- Git URLs are identified by scheme, host validation, and exclusion of raw file paths, then cloned with
--depth 1to minimize overhead. - ZIP archives are extracted to temporary directories, with automatic flattening of single-root archive structures.
- Local directories bypass temporary storage and are returned as resolved absolute paths.
- Resource cleanup is mandatory for remote inputs via
handler.cleanup()to prevent temporary directory leakage.
Frequently Asked Questions
How does SkillSpector distinguish between a Git repository URL and a raw file URL?
According to the source code in src/skillspector/input_handler.py, the _is_git_url() method checks for http://, https://, or git@ schemes, validates against known Git hosts (GitHub, GitLab, Bitbucket), and explicitly excludes URLs containing /raw/, /blob/, or file extensions like .md or .py. URLs ending in .git are always treated as Git repositories.
Why does SkillSpector use shallow cloning for Git repositories?
The _clone_git() implementation uses git clone --depth 1 to reduce network bandwidth and cloning time. As implemented in lines 125‑149, this fetches only the most recent commit, which is sufficient for static analysis while minimizing temporary storage requirements.
What happens if a ZIP file contains a single top-level folder?
The _extract_zip() method (lines 173‑189) detects single-directory archives by checking if extract_dir contains exactly one subdirectory. If this condition is met, it returns that subdirectory's path directly rather than the parent extraction directory, eliminating unnecessary nesting in the analysis path.
Is manual cleanup required when using the Python API?
Yes. When resolving remote inputs (Git, ZIP, or URLs), you must call handler.cleanup() after analysis completes to remove temporary directories. The reference is stored in the state object at src/skillspector/state.py to ensure cleanup can be triggered from the graph execution context.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →