How the Specify CLI Handles Template Downloading and Extraction
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:
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:
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:
pattern = f"spec-kit-template-{ai_assistant}-{script_type}.zip"
This pattern matching occurs between lines 70–84 in 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:
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 files using handle_vscode_settings from 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:
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:
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-kitrepository. download_template_from_githubhandles metadata fetching and authenticated streaming downloads with progress indicators and rate-limit error handling.download_and_extract_templatemanages complex extraction logic including single-folder flattening, in-place initialization, and VS Code settings merging viasrc/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, the CLI calls handle_vscode_settings from 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.
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 →