# SkillSpector Input Resolution Process: How Git URLs, ZIP Files, and Directories Are Normalized

> Learn how SkillSpector normalizes Git URLs, ZIP files, and directories into a single local path using its hierarchical detection system. Discover the InputHandler resolve process.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: internals
- Published: 2026-06-20

---

**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`](https://github.com/NVIDIA/SkillSpector/blob/main/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:

1. **Git URL** – Validated via `_is_git_url()`
2. **File URL** – Checked via `_is_file_url()` for raw content links
3. **ZIP archive** – Identified by `.zip` extension
4. **Markdown file** – Identified by `.md` extension
5. **Local directory** – Verified via `Path.is_dir()`
6. **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:

```python

# 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):

```python

# 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:

```python

# 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`](https://github.com/NVIDIA/SkillSpector/blob/main/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

```bash
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

```bash
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

```python
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 in [`src/skillspector/input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/input_handler.py).
- **Git URLs** are identified by scheme, host validation, and exclusion of raw file paths, then cloned with `--depth 1` to 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`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/state.py) to ensure cleanup can be triggered from the graph execution context.