Repository navigation
[Feature]: Phase out first-party shell scripts while retaining extension script preferences #4884
Description
Problem Statement
Spec Kit maintains Bash (sh), PowerShell (ps), and Python (py) implementations of core scripts and scripts shipped with bundled extensions. Keeping these first-party implementations in parity adds maintenance, packaging, documentation, and testing work.
Specify CLI requires Python 3.11+, but installing the CLI does not guarantee Python is available where an agent later executes generated commands. Python-backed first-party commands require Python 3.11+ installed and invokable on the agent’s execution machine.
Existing projects contain generated commands and installed script copies. Updating the CLI must not silently rewrite or break those files. A project should move to Python-backed first-party commands when it is explicitly refreshed or rescaffolded, through a safe migration.
This proposal does not remove Bash or PowerShell support for external extensions. specify init --script sh|ps|py remains available; its saved value continues to signal the project’s preferred extension script variant.
Proposed Solution
Deprecate first-party sh and ps implementations across successive stable releases, then remove them in a later minor release. Keep generic extension variant selection. Track the work through these sequenced PRs, adding links and shipped versions to this issue as they land:
- PR 1 — Establish the execution-host contract. Document when Python 3.11+ is required where an agent executes first-party commands. Test interpreter resolution on Windows, macOS, and Linux, including relocated projects, isolated CLI installations, and changed or missing virtualenvs. Make failures actionable. The CLI must validate installed preset YAML and generate a small JSON representation of the template-resolution fields for project scripts to read with the Python standard library, so the agent execution environment does not need PyYAML. Preserve the behavior of unrefreshed projects. See the execution-host report.
- PR 2 — Complete first-party Python parity. Inventory all core and bundled-extension commands, including direct shell-script references. Close Python coverage gaps and test generated commands and packaged assets. Verify external extension behavior with each saved
--scriptvalue. - PR 3 — Announce deprecation. Publish scope, prerequisites, timing, and migration instructions. Warn when a choice causes a first-party shell implementation to be used—not merely because an external extension prefers one. Preserve current defaults and functionality. Ship as deprecation release A.
- PR 4 — Make Python the default for new first-party projects. Update init, bundle init, workflow init, and integration paths consistently. Continue supporting existing and explicitly selected first-party shell implementations during the notice period. Keep accepting and persisting
--script sh|ps|pyfor extensions. Ship in a subsequent stable release B. - PR 5 — Remove first-party shell implementations and migrate safely. Remove core and bundled-extension
sh/psscripts, template entries, and package assets from the distribution. New and refreshed projects must install and invoke their Python implementations regardless of the saved--scriptvalue. Leave unrefreshed projects’ installed commands and scripts usable. During refresh, install working Python scripts before replacing command invocations; do not delete an installed shell script while a remaining or customized command references it. Test successful, interrupted, and failed refreshes so a failure is reported without leaving broken command references. Retain shell support for external extensions and include release-critical migration documentation. - PR 6 — Audit remaining documentation. After PR 5 merges, correct remaining first-party shell defaults, examples, and references across guides, reference pages, and bundled-extension docs. Preserve accurate documentation for external shell extensions. This focused audit does not defer documentation needed in PRs 1–5.
Release sequence: Ship PRs 5 and 6 in a later minor release C, after releases A and B. Keep this issue open until that release is complete.
Alternatives Considered
Maintaining three first-party variants indefinitely retains parity costs. Removing them immediately gives users no migration window. Removing sh and ps globally would break external extensions and is out of scope.
Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
All agents
Use Cases
A team installs Python on a remote agent machine even though Specify CLI was installed locally. An existing project upgrades the CLI and continues using its installed shell-backed commands until the project is refreshed. A project selects --script ps for an external extension while its refreshed core commands use Python.
Acceptance Criteria
- Documentation distinguishes Python required to install the CLI from Python required on the agent execution machine, and explains which first-party commands need it during migration.
- New and explicitly refreshed projects resolve installed preset templates with Python 3.11+ even when that interpreter cannot import PyYAML. Preset installation and update generate the validated JSON projection; removal cleans it up. Missing or stale generated data produces actionable refresh guidance rather than silently using outdated data, without requiring a new YAML parser or installing packages into the project. Unrefreshed projects retain their existing behavior.
- Cross-platform tests cover new and upgraded projects, relocated projects, interpreter failures, and generated commands.
- Upgrading the CLI without refreshing a project does not rewrite or break that project’s installed commands and scripts.
- Existing first-party
sh/psprojects remain functional throughout the deprecation releases and have a documented refresh path. - After removal, new and refreshed projects install and invoke first-party Python scripts with each of
--script sh,ps, andpy. - Refresh installs replacement scripts before switching command references, retains shell scripts still referenced by installed or customized commands, and reports failures without leaving commands pointing to missing scripts.
-
specify init --script sh|ps|pyremains available and persists the extension preference; external extensions, including shell-only ones, continue to work. - Refresh preserves customized files unless the user explicitly chooses an overwriting option.
-
docs/upgrade.mdand release notes describe the minor-release migration accurately. Each PR updates documentation for its own behavior; the final audit leaves no outdated first-party shell instructions. - Regression coverage is added without reducing total test collection.
Additional Context
src/specify_cli/command_init.py accepts and persists the script choice; src/specify_cli/integrations/base.py selects command variants; and src/specify_cli/shared_infra.py determines which core scripts are installed. Extension command and event paths use the saved preference. docs/installation.md documents current defaults, and docs/upgrade.md describes project upgrades.
AI Disclosure
Drafted with GitHub Copilot, powered by GPT-6 Sol, in an interactive human-supervised session (reasoning-effort setting not specified). Assistance covered repository inspection and issue drafting. No code was changed, and no PR was filed. A subsequent Copilot-assisted update incorporated the preset execution-host requirement and the reporter’s observation.
Activity
github-actions commentedon Oct 8, 2026
Feature assessment — phase-out-shell · Stage 1/5: Intake
Idea Intake: Phase out first-party shell scripts
Idea (as captured)
Restated
Spec Kit would consolidate first-party core and bundled-extension command implementations on Python over several releases while retaining shell variant selection for external extensions. The transition would preserve unrefreshed projects and provide an explicit, failure-safe refresh path before first-party shell implementations are removed.
Origin & Context
First-Glance Unknowns
github-actions commentedon Oct 8, 2026
Feature assessment — phase-out-shell · Stage 2/5: Research
Idea Research: Phase out first-party shell scripts
Users & Demand
mnriem, identifying parity, packaging, documentation, and testing work as the pain of maintaining three first-party variants — [source: [Feature]: Phase out first-party shell scripts while retaining extension script preferences #4884] (confidence: medium, cited).Prior Art
scripts/bash,scripts/powershell,scripts/python] (confidence: high, cited).extensions/git/scripts/*,tests/extensions/git/test_git_extension_python_parity.py] (confidence: high, cited).tests/test_check_prerequisites_python_parity.py,tests/test_resolve_template_python_parity.py] (confidence: high, cited).sh,ps, andpy; tests exercise script selection and non-interactive initialization — [source:src/specify_cli/command_init.py,tests/integrations/test_cli.py,tests/integrations/test_integration_generic.py] (confidence: high, cited).docs/upgrade.md] (confidence: high, cited).Market & Context
--scriptcan force a variant — [source:docs/installation.md] (confidence: high, cited).extensions/git/commands/*.md,extensions/git/README.md] (confidence: high, cited).Data & Constraints
docs/installation.md, [Feature]: Phase out first-party shell scripts while retaining extension script preferences #4884] (confidence: high, cited).docs/upgrade.md,src/specify_cli/shared_infra.py] (confidence: high, cited).tests/test_check_prerequisites_paths_only.py,tests/test_shared_infra_integrity.py, parity tests] (confidence: high, cited).Evidence Against the Idea
Gaps & Open Questions
Sources
docs/installation.md(repository source)docs/upgrade.md(repository source)src/specify_cli/command_init.pyandsrc/specify_cli/shared_infra.py(repository source)extensions/git/scripts/*,extensions/git/commands/*, and parity tests (repository source)github-actions commentedon Oct 8, 2026
Feature assessment — phase-out-shell · Stage 3/5: Problem
Problem Definition: First-party script variant parity
Problem Statement
Spec Kit maintainers must keep first-party core and bundled-extension automation behavior aligned across Bash, PowerShell, and Python, while users run generated commands on execution machines whose Python availability may differ from the machine where the CLI was installed. This creates ongoing maintenance cost and a migration reliability risk for existing projects, especially when installed or customized commands refer to concrete script paths.
Affected Users & Stakeholders
sh,ps, orpypreference — they require that external extension behavior remain supported (source: issue [Feature]: Phase out first-party shell scripts while retaining extension script preferences #4884).Goals
Non-Goals
specify init --script sh|ps|pyfor extension selection.Success Metrics
Cost of Inaction
Without a change, maintainers continue updating and testing parallel first-party implementations and their packaging and documentation, while users remain exposed to platform-specific defaults and possible differences between CLI installation and command execution environments. The magnitude of maintenance cost, defect frequency, and user impact is not quantified, so the benefit of change remains uncertain.
Open Questions
github-actions commentedon Oct 8, 2026
Feature assessment — phase-out-shell · Stage 4/5: Concept
Concept: Phase out first-party shell scripts
Options
Option A — Staged Python transition with compatibility window
Option B — Python-first for new projects, retain shell indefinitely for existing projects
Option C — No first-party consolidation; invest only in parity and diagnostics
Recommendation
Recommend Option A only if specification work first confirms the complete script/reference inventory, execution-host support policy, and a safe refresh/rollback boundary. It is the only option that directly targets the stated parity cost while honoring the acceptance criteria for unrefreshed projects and external extensions. Because demand and maintenance savings are not quantified, the recommendation is conditional rather than evidence of a ready-to-implement commitment; Option B is the safer fallback if migration safety or usage data cannot be established.
Out of Scope (for the recommended option)
specify init --script sh|ps|pyfor extension selection.Assumptions to Validate
github-actions commentedon Oct 8, 2026
Feature assessment — phase-out-shell · Stage 5/5: Decision — verdict needs-clarification
Decision: Phase out first-party shell scripts
Scorecard
Verdict & Rationale
Needs clarification. The problem is plausible and a staged concept is shaped, but the evidence threshold for a go verdict is not met: evidence strength is weak, and the highest-impact migration risks are unvalidated. The request should return to research and then shape/define once the complete script/reference inventory, observed usage and maintenance impact, execution-host policy, and refresh safety guarantees are established. This is preferable to committing to a large multi-release migration on maintainer assertion alone.
If needs-clarification
+- Revisit stage: research, then define and shape if the new evidence changes the problem boundary or viable option.
mnriem commentedon Oct 8, 2026
As we want to lower the maintenance burden we are going ahead with this
cacl-dis commentedon Oct 8, 2026
A data point for PR 1 (execution-host contract)
With a preset installed, template resolution parses preset.yml with whatever Python the command runs on. That is true in all three variants (common.sh, Get-Python3Command in common.ps1, and scripts/python/common.py). If that Python cannot import yaml, every resolving command fails with:
PyYAML is required to resolve preset template compositionspecify-clideclares pyyaml>=6.0, so the CLI's own environment always has it. The Python on PATH may or may not. On macOS with Homebrew Python 3.14 anduv tool install specify-cli(1.0.10):~/.local/share/uv/tools/specify-cli/bin/python3 -c "import yaml"succeeds (6.0.3)./opt/homebrew/bin/python3 -c "import yaml"fails.The workarounds people reach for are all fragile:
pip install --userhits PEP 668 (externally-managed-environment) on Homebrew and Debian, and the common next step is--break-system-packages.We currently work around it with a dedicated uv venv, put first on PATH in
.zshrcand.bashrcto accommodate different agent and user preferences.Two suggestions for PR 1:
specify preset addcould write a JSON copy of the resolved manifest that the scripts read with the standard library. That would make "Python 3.11+" the whole requirement, as the issue currently states it.