# How the Specify CLI Handles Template Downloading and Extraction

> Learn how the Specify CLI downloads and extracts project templates from GitHub. Discover its intelligent merging of VS Code settings and preservation of existing files.

- Repository: [GitHub/spec-kit](https://github.com/github/spec-kit)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The Specify CLI downloads project templates by querying the GitHub Releases API for the spec-kit repository, streaming the matching ZIP archive to a temporary file, and extracting its contents while intelligently merging VS Code settings and preserving existing files.**

The **Specify CLI** automates project scaffolding by orchestrating a four-stage pipeline for **template downloading and extraction**. When you run `specify init`, the tool communicates with the GitHub API to retrieve the latest release assets from the `github/spec-kit` repository, then handles the entire process of downloading, unpacking, and merging template files into your target directory.

## The Template Acquisition Pipeline

The CLI splits the workflow between two primary functions in [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py):

- **`download_template_from_github`** – Handles API negotiation and streaming downloads (lines 64–99)
- **`download_and_extract_template`** – Manages extraction, merging, and cleanup (lines 154–221)

This separation allows the CLI to isolate network operations from filesystem manipulation, making error recovery and cleanup more reliable.

## Step 1: Fetching Release Metadata

The process begins when `download_template_from_github` constructs the GitHub API endpoint:

```python
url = "https://api.github.com/repos/github/spec-kit/releases/latest"

```

The function performs an authenticated GET request (if a `GITHUB_TOKEN` is present) and parses the JSON response to locate the correct asset. It builds a search pattern using the user-supplied parameters:

```python
pattern = f"spec-kit-template-{ai_assistant}-{script_type}.zip"

```

This pattern matching occurs between lines 70–84 in [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py). If the asset is not found, the CLI raises a descriptive error before any network bandwidth is wasted on incorrect downloads.

## Step 2: Streaming the Archive

Once the asset URL is identified, the CLI uses `httpx.Client.stream` to download the ZIP file in 8 KB chunks. This streaming approach minimizes memory usage for large templates and allows the CLI to write directly to a temporary file:

```python
with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as tmp:
    with httpx.Client() as client:
        with client.stream("GET", asset_url) as response:
            for chunk in response.iter_bytes(chunk_size=8192):
                tmp.write(chunk)

```

When the response includes a `content-length` header, the CLI activates a **Rich progress bar** to display real-time download status. Error handling is robust: non-200 responses trigger `_format_rate_limit_error`, which surfaces GitHub API rate limits, reset times, and `Retry-After` headers to help users diagnose authentication or throttling issues.

## Step 3: Extracting and Merging Templates

The `download_and_extract_template` function (lines 154–221) orchestrates the complex logic of unpacking the archive while preserving user customizations. It handles three critical scenarios:

**Flattening Single Top-Level Folders**
If the ZIP contains exactly one root directory, the CLI flattens the structure so that project files land directly in the target directory rather than nested inside a redundant subfolder.

**In-Place Initialization (`--here`)**
When the user passes `--here`, the CLI unpacks to a temporary directory first, then merges the contents into the current working directory. This prevents partial extraction states from corrupting existing projects.

**VS Code Settings Preservation**
The CLI intelligently merges [`.vscode/settings.json`](https://github.com/github/spec-kit/blob/main/.vscode/settings.json) files using `handle_vscode_settings` from [`src/specify_cli/extensions.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/extensions.py). Rather than overwriting user settings, it performs a deep merge of JSON objects, preserving existing configurations while adding template-specific defaults.

## Step 4: Cleanup and Finalization

After successful extraction, the CLI performs cleanup in a `finally` block (lines 250–259) to ensure the temporary ZIP file is deleted regardless of whether extraction succeeded. If any step fails, the CLI removes the partially created project directory to prevent leaving broken scaffolds on the filesystem.

## Practical CLI Examples

Create a new project directory with the Claude agent template and POSIX shell scripts:

```bash
specify init my-app --ai claude --script sh

```

This command triggers `download_and_extract_template` to fetch `spec-kit-template-claude-sh.zip` and expand it into `./my-app`.

Initialize a project in the current directory using OpenCode agent templates with PowerShell scripts:

```bash
specify init --here --ai opencode --script ps

```

The `--here` flag sets `is_current_dir=True`, causing the CLI to merge the template directly into `.` while preserving existing files.

## Summary

- The **Specify CLI** downloads templates via the GitHub Releases API, streaming ZIP archives from the `github/spec-kit` repository.
- **`download_template_from_github`** handles metadata fetching and authenticated streaming downloads with progress indicators and rate-limit error handling.
- **`download_and_extract_template`** manages complex extraction logic including single-folder flattening, in-place initialization, and VS Code settings merging via [`src/specify_cli/extensions.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/extensions.py).
- The CLI preserves existing user files, performs atomic operations using temporary directories, and cleans up temporary ZIP files in all exit paths.

## Frequently Asked Questions

### How does the Specify CLI handle GitHub API rate limits during template downloads?

The CLI catches non-200 HTTP responses and invokes `_format_rate_limit_error` to parse GitHub's rate limit headers. It surfaces the remaining quota, reset timestamps, and `Retry-After` values to help users diagnose whether they need to authenticate with a `GITHUB_TOKEN` or wait before retrying.

### What happens if the template ZIP contains a single root folder?

The extraction logic in `download_and_extract_template` detects when the archive contains exactly one top-level directory. It automatically flattens this structure during extraction, ensuring that project files land directly in the target directory rather than nested inside a redundant subfolder that would require users to navigate an extra level.

### How does the Specify CLI preserve existing VS Code settings when extracting templates?

Rather than overwriting [`.vscode/settings.json`](https://github.com/github/spec-kit/blob/main/.vscode/settings.json), the CLI calls `handle_vscode_settings` from [`src/specify_cli/extensions.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/extensions.py) to perform a deep merge. This utility combines the template's default settings with the user's existing configuration, preserving custom preferences while adding any new required values from the template.

### Can I initialize a Specify project in the current directory without creating a new folder?

Yes. Pass the `--here` flag to `specify init`. This sets `is_current_dir=True` in `download_and_extract_template`, causing the CLI to extract the template to a temporary location first, then merge its contents into the current working directory. This mode preserves existing files and is useful for adding Specify scaffolding to an existing codebase.