How the SkillSpector Input Resolver Handles Git URLs, Zip Files, and Directories

The SkillSpector input resolver normalizes Git URLs, zip archives, and local directories into a unified local directory path using the InputHandler class, enabling the analysis pipeline to treat any input source as a scannable filesystem.

The NVIDIA SkillSpector input resolver serves as the entry point for the analysis graph, converting diverse input formats into a standardized local directory. Implemented in src/skillspector/input_handler.py, the InputHandler class detects whether the user provided a remote Git repository, a compressed archive, or a local folder, then transforms it into a path ready for scanning. This normalization ensures that downstream nodes never handle protocol-specific logic or archive extraction.

Entry Point: The resolve_input Node

When the analysis graph starts, the resolve_input node in src/skillspector/nodes/resolve_input.py extracts state["input_path"] and instantiates InputHandler to process it. Lines 46-57 of this file orchestrate the resolution:

handler = InputHandler()
resolved, source_type = handler.resolve(input_path.strip())

The handler returns a Path object pointing to a local directory and a source_type string ("git", "url", "zip", "file", or "directory"). The node records this as skill_path and stores any temporary directory in temp_dir_for_cleanup so the CLI can purge it after analysis completes.

Input Type Detection Logic

The InputHandler.resolve method examines the input string in a strict priority order to determine the source type. Lines 75-87 of input_handler.py implement the following checks:

  • Git URL: Validated by _is_git_url (lines 105-117), which matches http(s):// or git@ patterns from hosts like github.com, gitlab.com, or bitbucket.org, excluding raw file URLs.
  • File URL: Any http(s):// that is not a Git repository (lines 119-124).
  • Local zip file: Detected via input_path.endswith(".zip") (line 77).
  • Single markdown file: Detected via input_path.endswith(".md") (line 79).
  • Directory: Verified using Path(input_path).is_dir() (line 80).
  • Fallback file: Any other existing file passed to Path(input_path).is_file() (line 83).

If none match, the resolver raises a ValueError (lines 85-87).

Handling Git URLs

When _is_git_url returns True, resolve delegates to _clone_git (lines 125-149 of input_handler.py). This method creates a temporary workspace, executes git clone --depth 1 <url> <temp_dir>/repo, and returns the path to the cloned repository. The "git" source type signals that the content originated from a remote repository.

Errors such as missing Git binaries, authentication failures, or network timeouts are caught, logged, and re-raised as ValueError instances to halt the graph with a clear message.

Handling Zip Files

The SkillSpector input resolver processes zip archives from two sources: local files ending in .zip (line 77) and remote URLs that download zip payloads (lines 63-66). Both routes invoke _extract_zip (lines 173-189):

return self._extract_zip(Path(input_path)), "zip"

The _extract_zip method extracts the archive into <temp_dir>/extracted using zipfile.ZipFile. It implements smart extraction: if the archive contains exactly one top-level directory, the method returns that folder's path (common for GitHub "download zip" packages); otherwise, it returns the full extraction directory. Malformed archives trigger a ValueError.

Handling Local Directories

For existing directories, the resolver takes the simplest path. When Path(input_path).is_dir() evaluates to True (line 80), the method returns the resolved absolute path immediately:

return Path(input_path).resolve(), "directory"

No temporary directory is created, and the source directory becomes the scan root. This avoids unnecessary copying and preserves file metadata for local analysis.

Temporary Directory Management

All operations requiring sandboxing—Git cloning, file downloads, zip extraction, and single-file wrapping—share a lazily-created temporary directory. The _get_temp_dir method (lines 95-103) creates this directory under the system temp area with a skillspector_ prefix.

The resolve_input node retrieves this path via handler.temp_dir_for_cleanup() and stores it in the graph state. After the analysis completes, the CLI (src/skillspector/cli.py) deletes this folder to prevent disk pollution.

Practical Code Examples

Direct Use of InputHandler

from skillspector.input_handler import InputHandler

handler = InputHandler()

# Clone a Git repository

git_path, src_type = handler.resolve("https://github.com/NVIDIA/SkillSpector.git")
print(f"Cloned to {git_path}, type: {src_type}")

# Extract a remote zip archive

zip_path, src_type = handler.resolve("https://example.com/skills/archive.zip")
print(f"Extracted to {zip_path}, type: {src_type}")

# Reference a local directory

local_path, src_type = handler.resolve("/home/user/my-skill")
print(f"Scanning {local_path}, type: {src_type}")

CLI Integration

When running skill-spector scan <input_path>, the CLI initializes the state and triggers the resolver:


# Simplified from src/skillspector/cli.py

state = {"input_path": args.input_path}
updates = resolve_input(state)  # Returns skill_path and temp_dir_for_cleanup

state.update(updates)

Summary

  • The InputHandler class in src/skillspector/input_handler.py centralizes all input resolution logic for Git URLs, zip files, and directories.
  • Detection priority follows a strict order: Git URLs first, then remote file URLs, then local zip files, markdown files, and finally directories.
  • Git repositories are cloned with --depth 1 into temporary directories prefixed with skillspector_.
  • Zip archives are extracted intelligently, returning the sole root folder if one exists to flatten unnecessary nesting.
  • Local directories are passed through unchanged after absolute path resolution.
  • Cleanup is managed via temp_dir_for_cleanup, ensuring temporary resources are deleted after the analysis graph completes.

Frequently Asked Questions

Does SkillSpector support private Git repositories?

Yes. The _clone_git method invokes the system git command directly (lines 125-149 of input_handler.py). If your Git credentials are configured in the environment (SSH keys or Git credential helpers), the clone will authenticate automatically. Authentication failures are caught and raised as ValueError with the underlying Git error message.

What happens if a zip file contains multiple root directories?

If the archive contains more than one top-level entry, the _extract_zip method (lines 173-189) returns the full extraction directory path rather than drilling into a single subfolder. This ensures no files are lost during extraction, though your skill configuration must account for the directory structure.

How does the resolver handle single markdown files?

When the input path ends with .md (line 79 of input_handler.py), the resolver treats it as a single-file skill. The file is wrapped in a temporary directory structure so the analysis graph can process it as a standard skill package, returning the source type "file".

Where are temporary files stored during processing?

Temporary directories are created under the system's default temp location using the prefix skillspector_ (lines 99-103 of input_handler.py). The resolve_input node stores this path in temp_dir_for_cleanup, which the CLI uses to purge all downloaded or extracted data after the analysis completes.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →