Skip to content

Commit bf33980

Browse files
mnriemCopilot
andauthored
feat(cli): embed core pack in wheel for offline/air-gapped deployment (#1803)
* feat(cli): embed core pack in wheel + offline-first init (#1711, #1752) Bundle templates, commands, and scripts inside the specify-cli wheel so that `specify init` works without any network access by default. Changes: - pyproject.toml: add hatchling force-include for core_pack assets; bump version to 0.2.1 - __init__.py: add _locate_core_pack(), _generate_agent_commands() (Python port of generate_commands() shell function), and scaffold_from_core_pack(); modify init() to scaffold from bundled assets by default; add --from-github flag to opt back in to the GitHub download path - release.yml: build wheel during CI release job - create-github-release.sh: attach .whl as a release asset - docs/installation.md: add Enterprise/Air-Gapped Installation section - README.md: add Option 3 enterprise install with accurate offline story Closes #1711 Addresses #1752 * fix(tests): update kiro alias test for offline-first scaffold path * feat(cli): invoke bundled release script at runtime for offline scaffold - Embed release scripts (bash + PowerShell) in wheel via pyproject.toml - Replace Python _generate_agent_commands() with subprocess invocation of the canonical create-release-packages.sh, guaranteeing byte-for-byte parity between 'specify init --offline' and GitHub release ZIPs - Fix macOS bash 3.2 compat in release script: replace cp --parents, local -n (nameref), and mapfile with POSIX-safe alternatives - Fix _TOML_AGENTS: remove qwen (uses markdown per release script) - Rename --from-github to --offline (opt-in to bundled assets) - Add _locate_release_script() for cross-platform script discovery - Update tests: remove bash 4+/GNU coreutils requirements, handle Kimi directory-per-skill layout, 576 tests passing - Update CHANGELOG and docs/installation.md * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(offline): error out if --offline fails instead of falling back to network - _locate_core_pack() docstring now accurately describes that it only finds wheel-bundled core_pack/; source-checkout fallback lives in callers - init() --offline + no bundled assets now exits with a clear error (previously printed a warning and silently fell back to GitHub download) - init() scaffold failure under --offline now exits with an error instead of retrying via download_and_extract_template Addresses reviewer comment: #1803 * fix(offline): address PR review comments - fix(shell): harden validate_subset against glob injection in case patterns - fix(shell): make GENRELEASES_DIR overridable via env var for test isolation - fix(cli): probe pwsh then powershell on Windows instead of hardcoding pwsh - fix(cli): remove unreachable fallback branch when --offline fails - fix(cli): improve --offline error message with common failure causes - fix(release): move wheel build step after create-release-packages.sh - fix(docs): add --offline to installation.md air-gapped example - fix(tests): remove unused genreleases_dir param from _run_release_script - fix(tests): rewrite parity test to run one agent at a time with isolated temp dirs, preventing cross-agent interference from rm -rf * fix(offline): address second round of review comments - fix(shell): replace case-pattern membership with explicit loop + == check for unambiguous glob-safety in validate_subset() - fix(cli): require pwsh (PowerShell 7) only; drop powershell (PS5) fallback since the bundled script uses #requires -Version 7.0 - fix(cli): add bash and zip preflight checks in scaffold_from_core_pack() with clear error messages if either is missing - fix(build): list individual template files in pyproject.toml force-include to avoid duplicating templates/commands/ in the wheel * fix(offline): address third round of review comments - Add 120s timeout to subprocess.run in scaffold_from_core_pack to prevent indefinite hangs during offline scaffolding - Add test_pyproject_force_include_covers_all_templates to catch missing template files in wheel bundling - Tighten kiro alias test to assert specific scaffold path (download vs offline) * fix(offline): address Copilot review round 4 - fix(offline): use handle_vscode_settings() merge for --here --offline to prevent data loss on existing .vscode/settings.json - fix(release): glob wheel filename in create-github-release.sh instead of hardcoding version, preventing upload failures on version mismatch - docs(release): add comment noting pyproject.toml version is synced by release-trigger.yml before the tag is pushed * fix(offline): address review round 5 + offline bundle ZIP - fix(offline): pwsh-only, no powershell.exe fallback; clarify error message - fix(offline): tighten _has_bundled to check scripts dir for source checkouts - feat(release): build specify-bundle-v*.zip with all deps at release time - feat(release): attach offline bundle ZIP to GitHub release assets - docs: simplify air-gapped install to single ZIP download from releases - docs: add Windows PowerShell 7+ (pwsh) requirement note * fix(tests): session-scoped scaffold cache + timeout + dead code removal - Add timeout=300 and returncode check to _run_release_script() to fail fast with clear output on script hangs or failures - Remove unused import specify_cli, _SOURCE_TEMPLATES, bundled_project fixture - Add session-scoped scaffolded_sh/scaffolded_ps fixtures that scaffold once per agent and reuse the output directory across all invariant tests - Reduces test_core_pack_scaffold runtime from ~175s to ~51s (3.4x faster) - Parity tests still scaffold independently for isolation * fix(offline): remove wheel from release, update air-gapped docs to use pip download * fix(tests): handle codex skills layout and iflow agent in scaffold tests Codex now uses create_skills() with hyphenated separator (speckit-plan/SKILL.md) instead of generate_commands(). Update _SKILL_AGENTS, _expected_ext, and _list_command_files to handle both codex ('-') and kimi ('.') skill agents. Also picks up iflow as a new testable agent automatically via AGENT_CONFIG. * fix(offline): require wheel core_pack for --offline, remove source-checkout fallback --offline now strictly requires _locate_core_pack() to find the wheel's bundled core_pack/ directory. Source-checkout fallbacks are no longer accepted at the init() level — if core_pack/ is missing, the CLI errors out with a clear message pointing to the installation docs. scaffold_from_core_pack() retains its internal source-checkout fallbacks so parity tests can call it directly from a source checkout. * fix(offline): remove stale [Unreleased] CHANGELOG section, scope httpx.Client to download path - Remove entire [Unreleased] section — CHANGELOG is auto-generated at release - Move httpx.Client into use_github branch with context manager so --offline path doesn't allocate an unused network client * fix(offline): remove dead --from-github flag, fix typer.Exit handling, add page templates validation - Remove unused --from-github CLI option and docstring example - Add (typer.Exit, SystemExit) re-raise before broad except Exception to prevent duplicate error panel on offline scaffold failure - Validate page templates directory exists in scaffold_from_core_pack() to fail fast on incomplete wheel installs - Fix ruff lint: remove unused shutil import, remove f-prefix on strings without placeholders in test_core_pack_scaffold.py * docs(offline): add v0.6.0 deprecation notice with rationale - Help text: note bundled assets become default in v0.6.0 - Docstring: explain why GitHub download is being retired (no network dependency, no proxy/firewall issues, guaranteed version match) - Runtime nudge: when bundled assets are available but user takes the GitHub download path, suggest --offline with rationale - docs/installation.md: add deprecation notice with full rationale * fix(offline): allow --offline in source checkouts, fix CHANGELOG truncation - Simplify use_github logic: use_github = not offline (let scaffold_from_core_pack handle fallback to source-checkout paths) - Remove hard-fail when core_pack/ is absent — scaffold_from_core_pack already falls back to repo-root templates/scripts/commands - Fix truncated 'skill…' → 'skills' in CHANGELOG.md * fix(offline): sandbox GENRELEASES_DIR and clean up on failure - Pin GENRELEASES_DIR to temp dir in scaffold_from_core_pack() so a user-exported value cannot redirect output or cause rm -rf outside the sandbox - Clean up partial project directory on --offline scaffold failure (same behavior as the GitHub-download failure path) * fix(tests): use shutil.which for bash discovery, add ps parity tests - _find_bash() now tries shutil.which('bash') first so non-standard install locations (Nix, custom CI images) are found - Parametrize parity test over both 'sh' and 'ps' script types to ensure PowerShell variant stays byte-for-byte identical to release script output (353 scaffold tests, 810 total) * fix(tests): parse pyproject.toml with tomllib, remove unused fixture - Use tomllib to parse force-include keys from the actual TOML table instead of raw substring search (avoids false positives) - Remove unused source_template_stems fixture from test_scaffold_command_dir_location * fix: guard GENRELEASES_DIR against unsafe values, update docstring - Add safety check in create-release-packages.sh: reject empty, '/', '.', '..' values for GENRELEASES_DIR before rm -rf - Strip trailing slash to avoid path surprises - Update scaffold_from_core_pack() docstring to accurately describe all failure modes (not just 'assets not found') * fix: harden GENRELEASES_DIR guard, cache parity tests, safe iterdir - Reject '..' path segments in GENRELEASES_DIR to prevent traversal - Session-cache both scaffold and release-script results in parity tests — runtime drops from ~74s to ~45s (40% faster) - Guard cmd_dir.iterdir() in assertion message against missing dirs * fix(tests): exclude YAML frontmatter source metadata from path rewrite check The codex and kimi SKILL.md files have 'source: templates/commands/...' in their YAML frontmatter — this is provenance metadata, not a runtime path that needs rewriting. Strip frontmatter before checking for bare scripts/ and templates/ paths. * fix(offline): surface scaffold failure detail in error output When --offline scaffold fails, look up the tracker's 'scaffold' step detail and print it alongside the generic error message so users see the specific root cause (e.g. missing zip/pwsh, script stderr). --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent a7606c0 commit bf33980

File tree

9 files changed

+1082
-104
lines changed

9 files changed

+1082
-104
lines changed

.github/workflows/scripts/create-release-packages.sh

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,27 @@ fi
2626
echo "Building release packages for $NEW_VERSION"
2727

2828
# Create and use .genreleases directory for all build artifacts
29-
GENRELEASES_DIR=".genreleases"
29+
# Override via GENRELEASES_DIR env var (e.g. for tests writing to a temp dir)
30+
GENRELEASES_DIR="${GENRELEASES_DIR:-.genreleases}"
31+
32+
# Guard against unsafe GENRELEASES_DIR values before cleaning
33+
if [[ -z "$GENRELEASES_DIR" ]]; then
34+
echo "GENRELEASES_DIR must not be empty" >&2
35+
exit 1
36+
fi
37+
case "$GENRELEASES_DIR" in
38+
'/'|'.'|'..')
39+
echo "Refusing to use unsafe GENRELEASES_DIR value: $GENRELEASES_DIR" >&2
40+
exit 1
41+
;;
42+
esac
43+
if [[ "$GENRELEASES_DIR" == *".."* ]]; then
44+
echo "Refusing to use GENRELEASES_DIR containing '..' path segments: $GENRELEASES_DIR" >&2
45+
exit 1
46+
fi
47+
3048
mkdir -p "$GENRELEASES_DIR"
31-
rm -rf "$GENRELEASES_DIR"/* || true
49+
rm -rf "${GENRELEASES_DIR%/}/"* || true
3250

3351
rewrite_paths() {
3452
sed -E \
@@ -228,7 +246,7 @@ build_variant() {
228246
esac
229247
fi
230248

231-
[[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -not -name "vscode-settings.json" -exec cp --parents {} "$SPEC_DIR"/ \; ; echo "Copied templates -> .specify/templates"; }
249+
[[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -not -name "vscode-settings.json" | while IFS= read -r f; do d="$SPEC_DIR/$(dirname "$f")"; mkdir -p "$d"; cp "$f" "$d/"; done; echo "Copied templates -> .specify/templates"; }
232250

233251
case $agent in
234252
claude)
@@ -325,34 +343,35 @@ build_variant() {
325343
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf junie codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli kimi trae pi iflow generic)
326344
ALL_SCRIPTS=(sh ps)
327345

328-
norm_list() {
329-
tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?"\n":"") $i);out=1}}}END{printf("\n")}'
330-
}
331-
332346
validate_subset() {
333-
local type=$1; shift; local -n allowed=$1; shift; local items=("$@")
347+
local type=$1; shift
348+
local allowed_str="$1"; shift
334349
local invalid=0
335-
for it in "${items[@]}"; do
350+
for it in "$@"; do
336351
local found=0
337-
for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done
352+
for a in $allowed_str; do
353+
if [[ "$it" == "$a" ]]; then found=1; break; fi
354+
done
338355
if [[ $found -eq 0 ]]; then
339-
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
356+
echo "Error: unknown $type '$it' (allowed: $allowed_str)" >&2
340357
invalid=1
341358
fi
342359
done
343360
return $invalid
344361
}
345362

363+
read_list() { tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?" ":"") $i);out=1}}}END{printf("\n")}'; }
364+
346365
if [[ -n ${AGENTS:-} ]]; then
347-
mapfile -t AGENT_LIST < <(printf '%s' "$AGENTS" | norm_list)
348-
validate_subset agent ALL_AGENTS "${AGENT_LIST[@]}" || exit 1
366+
read -ra AGENT_LIST <<< "$(printf '%s' "$AGENTS" | read_list)"
367+
validate_subset agent "${ALL_AGENTS[*]}" "${AGENT_LIST[@]}" || exit 1
349368
else
350369
AGENT_LIST=("${ALL_AGENTS[@]}")
351370
fi
352371

353372
if [[ -n ${SCRIPTS:-} ]]; then
354-
mapfile -t SCRIPT_LIST < <(printf '%s' "$SCRIPTS" | norm_list)
355-
validate_subset script ALL_SCRIPTS "${SCRIPT_LIST[@]}" || exit 1
373+
read -ra SCRIPT_LIST <<< "$(printf '%s' "$SCRIPTS" | read_list)"
374+
validate_subset script "${ALL_SCRIPTS[*]}" "${SCRIPT_LIST[@]}" || exit 1
356375
else
357376
SCRIPT_LIST=("${ALL_SCRIPTS[@]}")
358377
fi

CHANGELOG.md

Lines changed: 13 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Changes
66

7+
- chore: bump version to 0.3.2
78
- Add conduct extension to community catalog (#1908)
89
- feat(extensions): add verify-tasks extension to community catalog (#1871)
910
- feat(presets): add enable/disable toggle and update semantics (#1891)
@@ -20,11 +21,11 @@
2021
- Feature/spec kit add pi coding agent pullrequest (#1853)
2122
- feat: register spec-kit-learn extension (#1883)
2223

23-
2424
## [0.3.1] - 2026-03-17
2525

2626
### Changed
2727

28+
- chore: bump version to 0.3.1
2829
- docs: add greenfield Spring Boot pirate-speak preset demo to README (#1878)
2930
- fix(ai-skills): exclude non-speckit copilot agent markdown from skills (#1867)
3031
- feat: add Trae IDE support as a new agent (#1817)
@@ -40,52 +41,21 @@
4041
- feat(extensions): add Archive and Reconcile extensions to community catalog (#1844)
4142
- feat: Add DocGuard CDD enforcement extension to community catalog (#1838)
4243

43-
4444
## [0.3.0] - 2026-03-13
4545

4646
### Changed
4747

48-
- No changes have been documented for this release yet.
49-
50-
<!-- Entries for 0.2.x and earlier releases are documented in their respective sections below. -->
51-
- make c ignores consistent with c++ (#1747)
52-
- chore: bump version to 0.1.13 (#1746)
53-
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
54-
- feat: add verify extension to community catalog (#1726)
55-
- Add Retrospective Extension to community catalog README table (#1741)
56-
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
57-
- fix: correct Copilot extension command registration (#1724)
58-
- fix(implement): remove Makefile from C ignore patterns (#1558)
59-
- Add sync extension to community catalog (#1728)
60-
- fix(checklist): clarify file handling behavior for append vs create (#1556)
61-
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
62-
- chore: bump version to 0.1.12 (#1737)
63-
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
64-
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
65-
- fix: Split release process to sync pyproject.toml version with git tags (#1732)
66-
67-
68-
## [Unreleased]
69-
70-
### Added
71-
72-
- feat(cli): polite deep merge for VSCode settings.json with JSONC support via `json5` and zero-data-loss fallbacks
73-
- feat(presets): Pluggable preset system with preset catalog and template resolver
74-
- Preset manifest (`preset.yml`) with validation for artifact, command, and script types
75-
- `PresetManifest`, `PresetRegistry`, `PresetManager`, `PresetCatalog`, `PresetResolver` classes in `src/specify_cli/presets.py`
76-
- CLI commands: `specify preset search`, `specify preset add`, `specify preset list`, `specify preset remove`, `specify preset resolve`, `specify preset info`
77-
- CLI commands: `specify preset catalog list`, `specify preset catalog add`, `specify preset catalog remove` for multi-catalog management
78-
- `PresetCatalogEntry` dataclass and multi-catalog support mirroring the extension catalog system
79-
- `--preset` option for `specify init` to install presets during initialization
80-
- Priority-based preset resolution: presets with lower priority number win (`--priority` flag)
81-
- `resolve_template()` / `Resolve-Template` helpers in bash and PowerShell common scripts
82-
- Template resolution priority stack: overrides → presets → extensions → core
83-
- Preset catalog files (`presets/catalog.json`, `presets/catalog.community.json`)
84-
- Preset scaffold directory (`presets/scaffold/`)
85-
- Scripts updated to use template resolution instead of hardcoded paths
86-
- feat(presets): Preset command overrides now propagate to agent skills when `--ai-skills` was used during init
87-
- feat: `specify init` persists CLI options to `.specify/init-options.json` for downstream operations
88-
- feat(extensions): support `.extensionignore` to exclude files/folders during `specify extension add` (#1781)
48+
- chore: bump version to 0.3.0
49+
- feat(presets): Pluggable preset system with catalog, resolver, and skills propagation (#1787)
50+
- fix: match 'Last updated' timestamp with or without bold markers (#1836)
51+
- Add specify doctor command for project health diagnostics (#1828)
52+
- fix: harden bash scripts against shell injection and improve robustness (#1809)
53+
- fix: clean up command templates (specify, analyze) (#1810)
54+
- fix: migrate Qwen Code CLI from TOML to Markdown format (#1589) (#1730)
55+
- fix(cli): deprecate explicit command support for agy (#1798) (#1808)
56+
- Add /selftest.extension core extension to test other extensions (#1758)
57+
- feat(extensions): Quality of life improvements for RFC-aligned catalog integration (#1776)
58+
- Add Java brownfield walkthrough to community walkthroughs (#1820)
8959

9060
## [0.2.1] - 2026-03-11
9161

@@ -312,12 +282,3 @@
312282

313283
- Add pytest and Python linting (ruff) to CI (#1637)
314284
- feat: add pull request template for better contribution guidelines (#1634)
315-
316-
## [0.0.99] - 2026-02-19
317-
318-
- Feat/ai skills (#1632)
319-
320-
## [0.0.98] - 2026-02-19
321-
322-
- chore(deps): bump actions/stale from 9 to 10 (#1623)
323-
- feat: add dependabot configuration for pip and GitHub Actions updates (#1622)

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ Choose your preferred installation method:
4949

5050
#### Option 1: Persistent Installation (Recommended)
5151

52-
Install once and use everywhere:
52+
Install once and use everywhere. Pin a specific release tag for stability (check [Releases](https://github.com/github/spec-kit/releases) for the latest):
5353

5454
```bash
55+
# Install a specific stable release (recommended — replace vX.Y.Z with the latest tag)
56+
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git@vX.Y.Z
57+
58+
# Or install latest from main (may include unreleased changes)
5559
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
5660
```
5761

@@ -73,21 +77,21 @@ specify check
7377
To upgrade Specify, see the [Upgrade Guide](./docs/upgrade.md) for detailed instructions. Quick upgrade:
7478

7579
```bash
76-
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
80+
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git@vX.Y.Z
7781
```
7882

7983
#### Option 2: One-time Usage
8084

8185
Run directly without installing:
8286

8387
```bash
84-
# Create new project
85-
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
88+
# Create new project (pinned to a stable release — replace vX.Y.Z with the latest tag)
89+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <PROJECT_NAME>
8690

8791
# Or initialize in existing project
88-
uvx --from git+https://github.com/github/spec-kit.git specify init . --ai claude
92+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init . --ai claude
8993
# or
90-
uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai claude
94+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here --ai claude
9195
```
9296

9397
**Benefits of persistent installation:**
@@ -97,6 +101,10 @@ uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai c
97101
- Better tool management with `uv tool list`, `uv tool upgrade`, `uv tool uninstall`
98102
- Cleaner shell configuration
99103

104+
#### Option 3: Enterprise / Air-Gapped Installation
105+
106+
If your environment blocks access to PyPI or GitHub, see the [Enterprise / Air-Gapped Installation](./docs/installation.md#enterprise--air-gapped-installation) guide for step-by-step instructions on using `pip download` to create portable, OS-specific wheel bundles on a connected machine.
107+
100108
### 2. Establish project principles
101109

102110
Launch your AI assistant in the project directory. Most agents expose spec-kit as `/speckit.*` slash commands; Codex CLI in skills mode uses `$speckit-*` instead.

docs/installation.md

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,34 @@
1212

1313
### Initialize a New Project
1414

15-
The easiest way to get started is to initialize a new project:
15+
The easiest way to get started is to initialize a new project. Pin a specific release tag for stability (check [Releases](https://github.com/github/spec-kit/releases) for the latest):
1616

1717
```bash
18+
# Install from a specific stable release (recommended — replace vX.Y.Z with the latest tag)
19+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <PROJECT_NAME>
20+
21+
# Or install latest from main (may include unreleased changes)
1822
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
1923
```
2024

2125
Or initialize in the current directory:
2226

2327
```bash
24-
uvx --from git+https://github.com/github/spec-kit.git specify init .
28+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init .
2529
# or use the --here flag
26-
uvx --from git+https://github.com/github/spec-kit.git specify init --here
30+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here
2731
```
2832

2933
### Specify AI Agent
3034

3135
You can proactively specify your AI agent during initialization:
3236

3337
```bash
34-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai claude
35-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai gemini
36-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai copilot
37-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai codebuddy
38-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai pi
38+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --ai claude
39+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --ai gemini
40+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --ai copilot
41+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --ai codebuddy
42+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --ai pi
3943
```
4044

4145
### Specify Script Type (Shell vs PowerShell)
@@ -51,16 +55,16 @@ Auto behavior:
5155
Force a specific script type:
5256

5357
```bash
54-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --script sh
55-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --script ps
58+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --script sh
59+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --script ps
5660
```
5761

5862
### Ignore Agent Tools Check
5963

6064
If you prefer to get the templates without checking for the right tools:
6165

6266
```bash
63-
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai claude --ignore-agent-tools
67+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <project_name> --ai claude --ignore-agent-tools
6468
```
6569

6670
## Verification
@@ -75,6 +79,52 @@ The `.specify/scripts` directory will contain both `.sh` and `.ps1` scripts.
7579

7680
## Troubleshooting
7781

82+
### Enterprise / Air-Gapped Installation
83+
84+
If your environment blocks access to PyPI (you see 403 errors when running `uv tool install` or `pip install`), you can create a portable wheel bundle on a connected machine and transfer it to the air-gapped target.
85+
86+
**Step 1: Build the wheel on a connected machine (same OS and Python version as the target)**
87+
88+
```bash
89+
# Clone the repository
90+
git clone https://github.com/github/spec-kit.git
91+
cd spec-kit
92+
93+
# Build the wheel
94+
pip install build
95+
python -m build --wheel --outdir dist/
96+
97+
# Download the wheel and all its runtime dependencies
98+
pip download -d dist/ dist/specify_cli-*.whl
99+
```
100+
101+
> **Important:** `pip download` resolves platform-specific wheels (e.g., PyYAML includes native extensions). You must run this step on a machine with the **same OS and Python version** as the air-gapped target. If you need to support multiple platforms, repeat this step on each target OS (Linux, macOS, Windows) and Python version.
102+
103+
**Step 2: Transfer the `dist/` directory to the air-gapped machine**
104+
105+
Copy the entire `dist/` directory (which contains the `specify-cli` wheel and all dependency wheels) to the target machine via USB, network share, or other approved transfer method.
106+
107+
**Step 3: Install on the air-gapped machine**
108+
109+
```bash
110+
pip install --no-index --find-links=./dist specify-cli
111+
```
112+
113+
**Step 4: Initialize a project (no network required)**
114+
115+
```bash
116+
# Initialize a project — no GitHub access needed
117+
specify init my-project --ai claude --offline
118+
```
119+
120+
The `--offline` flag tells the CLI to use the templates, commands, and scripts bundled inside the wheel instead of downloading from GitHub.
121+
122+
> **Deprecation notice:** Starting with v0.6.0, `specify init` will use bundled assets by default and the `--offline` flag will be removed. The GitHub download path will be retired because bundled assets eliminate the need for network access, avoid proxy/firewall issues, and guarantee that templates always match the installed CLI version. No action will be needed — `specify init` will simply work without network access out of the box.
123+
124+
> **Note:** Python 3.11+ is required.
125+
126+
> **Windows note:** Offline scaffolding requires PowerShell 7+ (`pwsh`), not Windows PowerShell 5.x (`powershell.exe`). Install from https://aka.ms/powershell.
127+
78128
### Git Credential Manager on Linux
79129

80130
If you're having issues with Git authentication on Linux, you can install Git Credential Manager:

docs/upgrade.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
| What to Upgrade | Command | When to Use |
1010
|----------------|---------|-------------|
11-
| **CLI Tool Only** | `uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git` | Get latest CLI features without touching project files |
11+
| **CLI Tool Only** | `uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git@vX.Y.Z` | Get latest CLI features without touching project files |
1212
| **Project Files** | `specify init --here --force --ai <your-agent>` | Update slash commands, templates, and scripts in your project |
1313
| **Both** | Run CLI upgrade, then project update | Recommended for major version updates |
1414

@@ -20,16 +20,18 @@ The CLI tool (`specify`) is separate from your project files. Upgrade it to get
2020

2121
### If you installed with `uv tool install`
2222

23+
Upgrade to a specific release (check [Releases](https://github.com/github/spec-kit/releases) for the latest tag):
24+
2325
```bash
24-
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
26+
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git@vX.Y.Z
2527
```
2628

2729
### If you use one-shot `uvx` commands
2830

29-
No upgrade needed—`uvx` always fetches the latest version. Just run your commands as normal:
31+
Specify the desired release tag:
3032

3133
```bash
32-
uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai copilot
34+
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here --ai copilot
3335
```
3436

3537
### Verify the upgrade

0 commit comments

Comments
 (0)