* feat(extensions): port git extension scripts to Python Ports git-common, initialize-repo, auto-commit, and create-new-feature-branch to extensions/git/scripts/python/, mirroring the bash/PowerShell twins. Parity tests run each bash script and its Python twin in identical projects and compare output, exit codes, and resulting git state. Fixes #3282 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: match bash error message for whitespace-only descriptions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Handle unreadable git-config.yml and assert stderr parity An unreadable config file raised OSError with a full traceback from _parse_auto_commit_config. Treat it like a missing config: auto-commit stays disabled. Covered by a chmod-000 test (skipped on non-POSIX and as root). _assert_parity now also compares stderr so warning or usage-text regressions between the bash and Python twins fail the suite. All existing parity tests pass with the stricter assertion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(extensions/git): pass script path to core.get_repo_root for cwd-outside-repo callers Without script_file, core.get_repo_root() falls back to Path.cwd() when SPECIFY_INIT_DIR is unset and no .specify root is found upward — the bash twin instead falls back to the script's install location (.specify/scripts/...). Pass script_file so both twins resolve the same repo_root; TypeError fallback keeps older cores working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: exercise SPECIFY_INIT_DIR from outside the project Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(extensions/git): handle UnicodeDecodeError and USER/USERNAME fallback - Catch (OSError, UnicodeDecodeError) when reading git-config.yml in create_new_feature_branch.py, initialize_repo.py, and auto_commit.py so invalid UTF-8 config falls back to defaults instead of crashing with a traceback. - Fall back to USERNAME (then "unknown") when USER is unset when deriving the branch author token, matching the PowerShell twin's Windows-friendly fallback chain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(extensions/git): platform-aware persist hint and stronger SPECIFY_INIT_DIR test - Add a shared _persist_hint() helper in create_new_feature_branch.py and use it for both the JSON-mode stderr hint and the human-readable stdout hint, so there is a single place emitting the SPECIFY_FEATURE persistence guidance. On Windows (os.name == "nt") it prints PowerShell $env:VAR = "..." syntax; elsewhere it keeps the existing POSIX export VAR=... syntax (parity with the bash twin). - Rework test_specify_init_dir_resolves_target_project so SPECIFY_INIT_DIR is the only thing that can produce the observed result: the script now runs from a separate host_proj (no existing specs, so script/cwd-based discovery would yield 001) while SPECIFY_INIT_DIR points at a different target_proj that already has an existing spec (007-existing, so the override must yield 008). The old version pointed SPECIFY_INIT_DIR at the same project the script was installed in, so it passed even if the env var were ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(extensions): tolerate missing Git executable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(extensions): quote PowerShell persist hint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(git): match bash persist hint escaping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(git): ignore unterminated config record Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(git): handle Windows persist hint parity Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(init): install Python shared scripts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(git): normalize Windows persistence hints Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Spec Kit Extensions
Extension system for Spec Kit - add new functionality without bloating the core framework.
Extension Catalogs
Spec Kit provides two catalog files with different purposes:
Your Catalog (catalog.json)
- Purpose: Default upstream catalog of extensions used by the Spec Kit CLI
- Default State: Empty by design in the upstream project - you or your organization populate a fork/copy with extensions you trust
- Location (upstream):
extensions/catalog.jsonin the GitHub-hosted spec-kit repo - CLI Default: The
specify extensioncommands use the upstream catalog URL by default, unless overridden - Org Catalog: Point
SPECKIT_CATALOG_URLat your organization's fork or hosted catalog JSON to use it instead of the upstream default - Customization: Copy entries from the community catalog into your org catalog, or add your own extensions directly
Example override:
# Override the default upstream catalog with your organization's catalog
export SPECKIT_CATALOG_URL="https://your-org.com/spec-kit/catalog.json"
specify extension search # Now uses your organization's catalog instead of the upstream default
Community Reference Catalog (catalog.community.json)
Note
Community extensions are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do not review, audit, endorse, or support the extension code itself. Review extension source code before installation and use at your own discretion.
- Purpose: Browse available community-contributed extensions
- Status: Active - contains extensions submitted by the community
- Location:
extensions/catalog.community.json - Usage: Reference catalog for discovering available extensions
- Submission: Open to community contributions via issue template
How It Works:
Making Extensions Available
You control which extensions your team can discover and install:
Option 1: Curated Catalog (Recommended for Organizations)
Populate your catalog.json with approved extensions:
- Discover extensions from various sources:
- Browse
catalog.community.jsonfor community extensions - Find private/internal extensions in your organization's repos
- Discover extensions from trusted third parties
- Browse
- Review extensions and choose which ones you want to make available
- Add those extension entries to your own
catalog.json - Team members can now discover and install them:
specify extension searchshows your curated catalogspecify extension add <name>installs from your catalog
Benefits: Full control over available extensions, team consistency, organizational approval workflow
Example: Copy an entry from catalog.community.json to your catalog.json, then your team can discover and install it by name.
Option 2: Direct URLs (For Ad-hoc Use)
Skip catalog curation - team members install directly using URLs:
specify extension add <extension-name> --from https://github.com/org/spec-kit-ext/archive/refs/tags/v1.0.0.zip
Benefits: Quick for one-off testing or private extensions
Tradeoff: Extensions installed this way won't appear in specify extension search for other team members unless you also add them to your catalog.json.
Available Community Extensions
Note
Community extensions are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do not review, audit, endorse, or support the extension code itself. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion.
🔍 Browse and search community extensions on the Community Extensions website.
See the Community Extensions page for the full list of available community-contributed extensions.
For the raw catalog data, see catalog.community.json.
Adding Your Extension
Submission Process
To add your extension to the community catalog:
- Prepare your extension following the Extension Development Guide
- Create a GitHub release for your extension
- File an issue using the Extension Submission template with all required metadata
- Wait for review — a maintainer will review the submission, update the catalog, and close the issue
See the Extension Publishing Guide for detailed step-by-step instructions.
Submission Checklist
Before submitting, ensure:
- ✅ Valid
extension.ymlmanifest - ✅ Complete README with installation and usage instructions
- ✅ LICENSE file included
- ✅ GitHub release created with semantic version (e.g., v1.0.0)
- ✅ Extension tested on a real project
- ✅ All commands working as documented
Installing Extensions
Once extensions are available (either in your catalog or via direct URL), install them:
# From your curated catalog (by name)
specify extension search # See what's in your catalog
specify extension add <extension-name> # Install by name
# Direct from URL (bypasses catalog)
specify extension add <extension-name> --from https://github.com/<org>/<repo>/archive/refs/tags/<version>.zip
# List installed extensions
specify extension list
For more information, see the Extension User Guide.