specify-cli 0.13.4__tar.gz → 0.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.devcontainer/devcontainer.json +2 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/AGENTS.md +41 -2
- {specify_cli-0.13.4 → specify_cli-0.14.0}/CHANGELOG.md +25 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/PKG-INFO +1 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/friends.md +3 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/local-development.md +2 -2
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/quickstart.md +1 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/integrations.md +23 -24
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/auto-commit.sh +6 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/auto_commit.py +9 -1
- specify_cli-0.14.0/presets/lean/commands/speckit.constitution.md +33 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/pyproject.toml +2 -1
- specify_cli-0.14.0/src/specify_cli/_download_security.py +218 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_github_http.py +9 -4
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_version.py +10 -3
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/azure_devops.py +49 -7
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/http.py +30 -5
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/yamlio.py +7 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/manifest.py +8 -4
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/packager.py +7 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_runtime.py +8 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/cline/__init__.py +9 -3
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/kiro_cli/__init__.py +7 -7
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/lingma/__init__.py +1 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/presets/_commands.py +10 -19
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/_commands.py +15 -74
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/merge.py +15 -3
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/gate/__init__.py +1 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/init/__init__.py +1 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/constitution.md +20 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/specify.md +2 -2
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/contract/test_manifest_schema.py +31 -0
- specify_cli-0.14.0/tests/contract/test_wheel_core_pack_scripts.py +40 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/git/test_git_extension_python_parity.py +26 -0
- specify_cli-0.14.0/tests/http_helpers.py +46 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_catalog.py +2 -0
- specify_cli-0.14.0/tests/integrations/test_integration_lingma.py +18 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_state.py +28 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/self_upgrade_helpers.py +2 -1
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_authentication.py +342 -28
- specify_cli-0.14.0/tests/test_download_security.py +227 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extensions.py +7 -5
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_github_http.py +52 -8
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_post_process.py +31 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_presets.py +33 -5
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_detection.py +1 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_execution.py +1 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_guidance.py +1 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_verification.py +1 -0
- specify_cli-0.14.0/tests/test_specify_template_numbering.py +39 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_upgrade.py +51 -6
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_workflows.py +59 -50
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_packager.py +17 -0
- specify_cli-0.14.0/tests/unit/test_bundler_yamlio.py +26 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_merge.py +17 -0
- specify_cli-0.13.4/presets/lean/commands/speckit.constitution.md +0 -15
- specify_cli-0.13.4/tests/http_helpers.py +0 -15
- specify_cli-0.13.4/tests/integrations/test_integration_lingma.py +0 -10
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.devcontainer/post-create.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.editorconfig +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.gitattributes +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/CODEOWNERS +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/agent_request.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/bundle_submission.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/extension_submission.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/preset_submission.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/aw/actions-lock.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/dependabot.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/scripts/check_security_requirements.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/security-audit-requirements.txt +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/skills/add-community-extension/SKILL.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/RELEASE-PROCESS.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-bundle.lock.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-bundle.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-extension.lock.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-extension.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-preset.lock.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-preset.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-assess.lock.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-assess.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-fix.lock.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-fix.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-test.lock.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-test.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/catalog-assign.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/codeql.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/docs.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/lint.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/publish-pypi.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/release-trigger.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/release.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/security.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/stale.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/test.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.gitignore +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.markdownlint-cli2.jsonc +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.pre-commit-config.yaml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/.zenodo.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/CITATION.cff +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/CODE_OF_CONDUCT.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/CONTRIBUTING.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/DEVELOPMENT.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/LICENSE +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/SECURITY.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/SUPPORT.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/bundles/catalog.community.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/.gitignore +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/bundles.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/extensions.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/overview.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/presets.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/walkthroughs.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/complex-features.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/sdd.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/spec-of-specs.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/spec-persistence.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/docfx.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/guides/evolving-specs.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/guides/monorepo.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/index.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/air-gapped.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/one-time.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/pipx.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/pypi.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/uv.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/installation.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/agentic-bugfix.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/agentic-sdd.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/authentication.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/bundles.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/core.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/extensions.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/overview.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/presets.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/workflows.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/template/public/main.css +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/toc.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/upgrade.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/business-analyst/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/business-analyst/bundle.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/developer/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/developer/bundle.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/product-manager/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/product-manager/bundle.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/security-researcher/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/security-researcher/bundle.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-API-REFERENCE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-DEVELOPMENT-GUIDE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-PUBLISHING-GUIDE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-USER-GUIDE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/RFC-EXTENSION-SYSTEM.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/agent-context-config.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/agent-context-defaults.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/commands/speckit.agent-context.update.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/extension.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/scripts/python/update_agent_context.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.decide.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.define.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.intake.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.research.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.shape.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/extension.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/commands/speckit.bug.assess.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/commands/speckit.bug.fix.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/commands/speckit.bug.test.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/extension.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/catalog.community.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/catalog.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.commit.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.feature.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.initialize.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.remote.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.validate.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/config-template.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/extension.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/git-config.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/create-new-feature-branch.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/git-common.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/initialize-repo.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/git-common.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/create_new_feature_branch.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/git_common.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/initialize_repo.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/selftest/commands/selftest.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/selftest/extension.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/.gitignore +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/CHANGELOG.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/EXAMPLE-README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/LICENSE +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/commands/example.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/config-template.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/extension.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/CONTRIBUTING.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/catalog.community.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/catalog.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/media/bootstrap-claude-code.gif +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/media/logo_large.webp +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/media/logo_small.webp +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/media/spec-kit-video-header.jpg +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/media/specify_cli.gif +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-April.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-February.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-June.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-March.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-May.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/ARCHITECTURE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/PUBLISHING.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/catalog.community.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/catalog.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.implement.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.plan.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.specify.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.tasks.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/preset.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/commands/speckit.myext.myextcmd.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/commands/speckit.specify.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/preset.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/templates/myext-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/templates/spec-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/commands/speckit.specify.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/commands/speckit.wrap-test.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/preset.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/checklist-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/constitution-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/plan-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/spec-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/tasks-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/check-prerequisites.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/common.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/create-new-feature.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/setup-plan.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/setup-tasks.sh +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/check-prerequisites.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/common.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/create-new-feature.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/setup-plan.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/setup-tasks.ps1 +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/check_prerequisites.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/common.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/create_new_feature.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/setup_plan.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/setup_tasks.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/spec-driven.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/spec-kit.code-workspace +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_agent_config.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_assets.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_console.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_init_options.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_invocation_style.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_project.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_toml_string.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_utils.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/agents.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/base.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/config.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/github.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/commands_impl/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/commands_impl/catalog_config.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/project.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/versioning.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/catalog.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/records.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/adapters.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/catalog_stack.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/conflict.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/installer.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/primitives.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/references.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/resolver.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/validator.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/catalogs.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/commands/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/commands/bundle/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/commands/init.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/extensions/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/extensions/_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_scaffold.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_state.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_status.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_helpers.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_install_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_migrate_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_query_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_scaffold_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/agy/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/amp/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/auggie/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/base.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/bob/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/catalog.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/claude/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/codebuddy/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/codex/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/copilot/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/cursor_agent/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/devin/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/droid/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/firebender/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/forge/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/gemini/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/generic/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/goose/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/grok/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/hermes/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/junie/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/kilocode/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/kimi/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/manifest.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/omp/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/opencode/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/pi/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/qodercli/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/qwen/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/rovodev/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/shai/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/tabnine/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/trae/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/vibe/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/zcode/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/zed/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/presets/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/shared_infra.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/base.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/catalog.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/engine.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/expressions.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/composer.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/layer_sources.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/schema.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/command/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/do_while/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/fan_in/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/fan_out/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/if_then/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/prompt/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/shell/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/switch/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/while_loop/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/checklist-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/analyze.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/checklist.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/clarify.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/converge.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/implement.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/plan.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/tasks.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/taskstoissues.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/constitution-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/plan-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/spec-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/tasks-template.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/vscode-settings.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/auth_helpers.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/bundler_helpers.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/conftest.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/contract/test_bundle_cli.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/contract/test_catalog_schema.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/assess/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/assess/test_assess_extension.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/bug/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/bug/test_bug_extension.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/git/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/git/test_git_extension.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_agent_context_cli_free.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_extension_agent_context.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_update_agent_context_feature_json.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_update_agent_context_python_parity.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/TESTING.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/plan.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/spec.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/tasks.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_catalog_stack.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_init_install.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_install_flow.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_local_install.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_offline.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_security_paths.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/__init__.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/conftest.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_base.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_cli.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_extra_args.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_home_isolation.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_agy.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_amp.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_auggie.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_markdown.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_skills.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_toml.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_yaml.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_bob.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_claude.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_cline.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_codebuddy.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_codex.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_copilot.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_cursor_agent.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_devin.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_droid.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_firebender.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_forge.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_gemini.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_generic.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_goose.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_grok.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_hermes.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_junie.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_kilocode.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_kimi.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_kiro_cli.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_omp.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_opencode.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_pi.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_qodercli.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_qwen.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_rovodev.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_scaffold.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_shai.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_subcommand.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_tabnine.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_trae.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_vibe.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_zcode.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_zed.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_manifest.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_registry.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_skill_frontmatter_quoting.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/parity_helpers.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_agent_config_consistency.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_branch_numbering.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_check_prerequisites_paths_only.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_check_prerequisites_python_parity.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_check_tool.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_cli_version.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_command_template_py_scripts.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_commands_package.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_console_imports.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_create_new_feature_python_parity.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extension_registration.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extension_skills.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extension_update_hardening.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_github_workflows.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_init_dir.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_init_dir_cli.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_live_transient_windows.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_merge.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_ps1_encoding.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_registrar_path_traversal.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_security_workflow.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_plan_feature_json.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_plan_no_overwrite.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_plan_python_parity.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_tasks.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_tasks_python_parity.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_shared_infra_integrity.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_skill_placeholder_py.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_timestamp_branches.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_utils.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_utils_assets_imports.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_version_imports.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_workflow_run_without_project.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundle_download_url.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_adapters.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_catalog_config.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_conflict.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_primitives.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_records.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_references.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_resolver.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_validator.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_versioning.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/conftest.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_commands.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_composer.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_layer_sources.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_schema.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_security.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_resolver_integration.py +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/ARCHITECTURE.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/PUBLISHING.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/README.md +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/catalog.community.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/catalog.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/speckit/workflow.yml +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/step-catalog.community.json +0 -0
- {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/step-catalog.json +0 -0
|
@@ -187,7 +187,7 @@ context_markers:
|
|
|
187
187
|
end: "<!-- SPECKIT END -->"
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
- The Specify CLI does **not** write this config. When `context_file` is empty, the extension's bundled scripts self-seed it by looking up the active integration's key in the extension's own `agent-context-defaults.json` map (`extensions/agent-context/scripts/bash/update-agent-context.sh
|
|
190
|
+
- The Specify CLI does **not** write this config. When `context_file` is empty, the extension's bundled scripts self-seed it by looking up the active integration's key in the extension's own `agent-context-defaults.json` map (`extensions/agent-context/scripts/bash/update-agent-context.sh`, `.ps1`, and `extensions/agent-context/scripts/python/update_agent_context.py`). The CLI registry is never consulted — all agent→context-file knowledge lives inside the extension.
|
|
191
191
|
- `context_markers.{start,end}` are read solely by the extension's scripts; they default to the Spec Kit markers shown above and can be customized by editing `agent-context-config.yml` directly.
|
|
192
192
|
|
|
193
193
|
Existing projects created by older Spec Kit versions keep working: any previously written managed section or extension config is left intact and is only ever updated by the extension when run.
|
|
@@ -268,6 +268,25 @@ echo "✅ Done"
|
|
|
268
268
|
|
|
269
269
|
## Command File Formats
|
|
270
270
|
|
|
271
|
+
### Script References (`scripts:` frontmatter)
|
|
272
|
+
|
|
273
|
+
Core command templates (`templates/commands/*.md`) that invoke a helper script declare it in a `scripts:` frontmatter block with one line per supported script type. The `{SCRIPT}` placeholder in the command body is replaced at install time with the entry matching the project's selected script type (`--script sh|ps|py`):
|
|
274
|
+
|
|
275
|
+
```yaml
|
|
276
|
+
scripts:
|
|
277
|
+
sh: scripts/bash/setup-plan.sh --json
|
|
278
|
+
ps: scripts/powershell/setup-plan.ps1 -Json
|
|
279
|
+
py: scripts/python/setup_plan.py --json
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
| Key | Script type | Location |
|
|
283
|
+
| ---- | ---------------------- | -------------------------- |
|
|
284
|
+
| `sh` | POSIX shell (bash/zsh) | `scripts/bash/*.sh` |
|
|
285
|
+
| `ps` | PowerShell | `scripts/powershell/*.ps1` |
|
|
286
|
+
| `py` | Python | `scripts/python/*.py` |
|
|
287
|
+
|
|
288
|
+
All three entries must be present and behaviorally equivalent — agents parse the same stdout contract (`FEATURE_DIR:…`, `AVAILABLE_DOCS:…`, `--json` shapes) regardless of which one runs. (The bundled `agent-context` and `git` extension command templates also invoke helpers but do not yet use `scripts:` frontmatter — see [Script Types and Migration](#script-types-and-migration).)
|
|
289
|
+
|
|
271
290
|
### Markdown Format
|
|
272
291
|
|
|
273
292
|
**Standard format:**
|
|
@@ -328,9 +347,29 @@ Different agents use different argument placeholders. The placeholder used in co
|
|
|
328
347
|
- **TOML-based**: `{{args}}` (e.g., Gemini)
|
|
329
348
|
- **YAML-based**: `{{args}}` (e.g., Goose)
|
|
330
349
|
- **Custom**: some agents override the default (e.g., Forge uses `{{parameters}}`)
|
|
331
|
-
- **Script placeholders**: `{SCRIPT}` (replaced with
|
|
350
|
+
- **Script placeholders**: `{SCRIPT}` (replaced with the resolved command from the template's `scripts:` frontmatter, per the project's `--script sh|ps|py` selection)
|
|
332
351
|
- **Agent placeholders**: `__AGENT__` (replaced with agent name)
|
|
333
352
|
|
|
353
|
+
## Script Types and Migration
|
|
354
|
+
|
|
355
|
+
Spec Kit ships every core workflow script in three interchangeable variants — POSIX shell (`sh`), PowerShell (`ps`), and Python (`py`) — selected per project with `specify init --script sh|ps|py`. Each core command template that invokes a helper script carries all three in its `scripts:` frontmatter (templates that don't call a script, e.g. `constitution`/`specify`, have no `scripts:` block); see [Script References](#script-references-scripts-frontmatter).
|
|
356
|
+
|
|
357
|
+
### Why Python is recommended
|
|
358
|
+
|
|
359
|
+
- **No extra runtime.** The `specify` CLI is already Python, so the interpreter is guaranteed present — `py` adds no new dependency.
|
|
360
|
+
- **Path toward a single source of truth.** The shell variants require paired `.sh` + `.ps1` maintenance and diverge on JSON handling (`jq` vs manual parsing). The Python variant avoids `jq` and is intended to eventually replace that dual-maintenance — but that consolidation has not happened yet: all three variants are still maintained in parallel (see the parity rule below).
|
|
361
|
+
- **Parity-tested.** The Python ports are covered by tests — output-parity tests against the shell scripts where the contract is stdout-based, and direct unit tests elsewhere — so the stdout contract agents rely on stays stable.
|
|
362
|
+
|
|
363
|
+
### Defaults and availability
|
|
364
|
+
|
|
365
|
+
- `py` is available today for the core command templates (via their `scripts:` frontmatter). The bundled extensions (`agent-context`, `git`) ship Python script variants on disk, but their command templates still hard-code the Bash/PowerShell invocations, so `--script py` does not yet route those extension commands to Python — wiring `py` into the extension command templates is tracked separately.
|
|
366
|
+
- Selection is per project: interactive `specify init` prompts for the script type, while non-interactive runs default to a shell variant by OS (`sh` on Linux/macOS, `ps` on Windows). `py` is chosen at the prompt or via `--script py`.
|
|
367
|
+
- `sh` and `ps` remain fully supported. Nothing is removed, and `py` is not yet the default.
|
|
368
|
+
|
|
369
|
+
### Parity rule for contributors
|
|
370
|
+
|
|
371
|
+
All three script types are first-class: any change to a workflow script must update `sh`, `ps`, and `py` together and keep their tests (parity and unit) green. Making `py` the default and eventually retiring `sh`/`ps` is future work gated on adoption, tracked under the script-unification epic ([#3277](https://github.com/github/spec-kit/issues/3277)) — not something to act on from this doc.
|
|
372
|
+
|
|
334
373
|
## Special Processing Requirements
|
|
335
374
|
|
|
336
375
|
Some agents require custom processing beyond the standard template transformations:
|
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- insert new changelog below this comment -->
|
|
4
4
|
|
|
5
|
+
## [0.14.0] - 2026-07-23
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- docs: add spec-kit-copilot to community friends (#3675)
|
|
10
|
+
- fix(integrations): recompute invoke_separator from retained parsed_options (#3664)
|
|
11
|
+
- fix(workflows): preserve intra-overlay order for multiple insert_after edits (#3662)
|
|
12
|
+
- fix(bundler): reject falsy non-mapping requires/provides in manifest from_dict (#3661)
|
|
13
|
+
- fix(bundler): dump_yaml writes literal UTF-8 (allow_unicode=True) (#3660)
|
|
14
|
+
- fix(integrations): declare kiro-cli multi-install safe (#3477)
|
|
15
|
+
- fix(git-extension): trim trailing whitespace before stripping commit-message quotes (#3673)
|
|
16
|
+
- fix(bundler): order bundle members by canonical POSIX arcname (reproducible builds) (#3658)
|
|
17
|
+
- fix(integrations): Cline overrides post_process_command_content (correct hook name) (#3657)
|
|
18
|
+
- docs(workflows): gate step docstring lists the 'retry' on_reject behaviour (#3656)
|
|
19
|
+
- fix: harden bounded reads and redirect validation (#3671)
|
|
20
|
+
- fix(packaging): bundle scripts/python into the wheel core_pack (#3665) (#3670)
|
|
21
|
+
- fix: bundle scripts/python in wheel so --script py works (#3665) (#3668)
|
|
22
|
+
- docs(workflows): init step docstring lists the 'py' script type (#3655)
|
|
23
|
+
- fix(integrations): declare LingmaIntegration multi_install_safe (#3654)
|
|
24
|
+
- fix: guard constitution command against feature execution (#3646)
|
|
25
|
+
- Fix duplicate step numbering in specify command (#3647)
|
|
26
|
+
- docs(scripts): document the 'py' script type and sh/ps migration plan (#3284) (#3653)
|
|
27
|
+
- harden: bound HTTP reads and enforce strict redirects (#3140)
|
|
28
|
+
- chore: release 0.13.4, begin 0.13.5.dev0 development (#3649)
|
|
29
|
+
|
|
5
30
|
## [0.13.4] - 2026-07-22
|
|
6
31
|
|
|
7
32
|
### Changed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Community Friends
|
|
2
2
|
|
|
3
3
|
> [!NOTE]
|
|
4
|
-
> Community projects listed here are independently created and maintained by their respective authors.
|
|
4
|
+
> Community projects listed here are independently created and maintained by their respective authors. Unless explicitly marked as a **first-party GitHub project**, they are **not reviewed, nor endorsed, nor supported by GitHub**. Review their source code before installation and use at your own discretion.
|
|
5
5
|
|
|
6
6
|
Community projects that extend, visualize, or build on Spec Kit:
|
|
7
7
|
|
|
@@ -16,3 +16,5 @@ Community projects that extend, visualize, or build on Spec Kit:
|
|
|
16
16
|
- **[cc-spec-kit](https://github.com/speckit-community/cc-spec-kit)** — Community-maintained plugin for Claude Code and GitHub Copilot CLI that installs Spec Kit skills via the plugin marketplace.
|
|
17
17
|
|
|
18
18
|
- **[spectatui](https://github.com/tinesoft/spectatui)** — A terminal UI (TUI) dashboard for Spec Kit that lets you track features, manage specifications, integrations, presets, workflows, and extensions, and monitor AI agent workflows. Attach to existing AI sessions or launch new ones from your terminal. Keyboard and mouse support. Light/dark theme support. Customizable and performance-oriented. Requires the `specify` CLI in your PATH.
|
|
19
|
+
|
|
20
|
+
- **[spec-kit-copilot](https://github.com/github/spec-kit-copilot)** — _First-party GitHub project._ A GitHub Copilot **skills plugin** that exposes the Spec Kit `specify` CLI to the Copilot agent in both the Copilot CLI and the GitHub Copilot app. It provides a focused skill per `specify` command group — setup, init, check, extensions, presets, bundles, workflows, workflow steps, and self-upgrade — so you can navigate and drive the entire Spec Kit ecosystem through natural language, letting Copilot decide when and how to run the right `specify` commands on your behalf.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This guide shows how to iterate on the `specify` CLI locally without publishing a release or committing to `main` first.
|
|
4
4
|
|
|
5
|
-
> Scripts
|
|
5
|
+
> Scripts are available as Bash (`.sh`), PowerShell (`.ps1`), and Python (`.py`) variants. Interactive `specify init` prompts you to choose one; non-interactive runs default to a shell variant for your OS. Pass `--script sh|ps|py` to select explicitly.
|
|
6
6
|
|
|
7
7
|
## 1. Clone and Switch Branches
|
|
8
8
|
|
|
@@ -189,7 +189,7 @@ rm -rf .venv dist build *.egg-info
|
|
|
189
189
|
| `ModuleNotFoundError: typer` | Run `uv pip install -e .` |
|
|
190
190
|
| Scripts not executable (Linux) | Re-run init or `chmod +x scripts/*.sh` |
|
|
191
191
|
| Git commands unavailable | Install the git extension with `specify extension add git` |
|
|
192
|
-
| Wrong script type downloaded | Pass `--script sh
|
|
192
|
+
| Wrong script type downloaded | Pass `--script sh`, `--script ps`, or `--script py` explicitly |
|
|
193
193
|
| TLS errors on corporate network | Configure your environment's certificate store or proxy. The `--skip-tls` flag is deprecated and has no effect. |
|
|
194
194
|
|
|
195
195
|
## 14. Next Steps
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This guide will help you get started with Spec-Driven Development using Spec Kit. Throughout, we illustrate each step with a running example: **Taskify**, a small team productivity platform.
|
|
4
4
|
|
|
5
5
|
> [!NOTE]
|
|
6
|
-
> Automation scripts are provided as
|
|
6
|
+
> Automation scripts are provided as Bash (`.sh`), PowerShell (`.ps1`), and Python (`.py`) variants. Interactive `specify init` prompts you to choose one; non-interactive runs default to a shell variant for your OS. Pass `--script sh|ps|py` to select explicitly.
|
|
7
7
|
|
|
8
8
|
> [!NOTE]
|
|
9
9
|
> Commands are shown here in `/speckit.*` form, but the exact invocation depends on your agent. Some skills-based agents use `$speckit-*` (e.g. Codex, ZCode) or `/skill:speckit-*` (e.g. Kimi). Use whichever form your agent exposes — the steps are otherwise identical.
|
|
@@ -86,7 +86,7 @@ specify integration install <key>
|
|
|
86
86
|
|
|
87
87
|
| Option | Description |
|
|
88
88
|
| ------------------------ | ------------------------------------------------------------------------ |
|
|
89
|
-
| `--script sh\|ps`
|
|
89
|
+
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
|
90
90
|
| `--force` | Opt in to installing alongside integrations that are not declared multi-install safe |
|
|
91
91
|
| `--integration-options` | Integration-specific options (e.g. `--integration-options="--commands-dir .myagent/cmds"`) |
|
|
92
92
|
|
|
@@ -122,7 +122,7 @@ specify integration switch <key>
|
|
|
122
122
|
|
|
123
123
|
| Option | Description |
|
|
124
124
|
| ------------------------ | ------------------------------------------------------------------------ |
|
|
125
|
-
| `--script sh\|ps`
|
|
125
|
+
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
|
126
126
|
| `--force` | Force removal of modified files during uninstall; when the target is already installed, overwrite managed shared templates while changing the default |
|
|
127
127
|
| `--refresh-shared-infra` | Also overwrite shared infrastructure files even if you customized them (otherwise customizations are preserved) |
|
|
128
128
|
| `--integration-options` | Options for the target integration when it is not already installed |
|
|
@@ -150,7 +150,7 @@ specify integration upgrade [<key>]
|
|
|
150
150
|
| Option | Description |
|
|
151
151
|
| ------------------------ | ------------------------------------------------------------------------ |
|
|
152
152
|
| `--force` | Overwrite files even if they have been modified |
|
|
153
|
-
| `--script sh\|ps`
|
|
153
|
+
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
|
154
154
|
| `--integration-options` | Options for the integration |
|
|
155
155
|
|
|
156
156
|
Reinstalls an installed integration with updated templates and commands (e.g., after upgrading Spec Kit). Defaults to the default integration; if a key is provided, it must be one of the installed integrations. Detects locally modified files and blocks the upgrade unless `--force` is used. Stale files from the previous install that are no longer needed are removed automatically. Shared templates stay aligned with the default integration even when upgrading a non-default integration.
|
|
@@ -257,31 +257,30 @@ Spec Kit tracks one default integration in `.specify/integration.json` with `def
|
|
|
257
257
|
|
|
258
258
|
An integration is multi-install safe when it uses a static, unique agent root and command directory, stable command invocation settings, and a separate install manifest whose managed files do not overlap another safe integration. Registry tests enforce those path and manifest invariants. Shared Spec Kit templates remain aligned to the single default integration.
|
|
259
259
|
|
|
260
|
-
The
|
|
261
|
-
|
|
262
|
-
**Agent-context defaults are separate.** The optional agent-context extension maps each integration to a default context file in `extensions/agent-context/agent-context-defaults.json`. Those defaults are independent of multi-install safety: several agents may share a root file such as `AGENTS.md` when the extension is enabled. Multi-install safety does not require a unique context file per safe integration.
|
|
260
|
+
The Command directory column below lists the directory each integration installs its commands or skills into. Context-file targeting is a separate concern from integration multi-install safety: `multi_install_safe` is an integration declaration about command/skill paths, whereas the optional agent-context extension manages a per-agent context file (for example `AGENTS.md` or `CLAUDE.md`) and can even synchronize several anchors at once via its `context_files` setting. Multiple agents mapping to the same context file is expected there and does not affect whether an integration is multi-install safe; see the agent-context extension for details.
|
|
263
261
|
|
|
264
262
|
The currently declared multi-install safe integrations are:
|
|
265
263
|
|
|
266
|
-
| Key |
|
|
267
|
-
| --- |
|
|
268
|
-
| `auggie` | `.augment/commands
|
|
269
|
-
| `claude` | `.claude/skills
|
|
270
|
-
| `cline` | `.clinerules/workflows
|
|
271
|
-
| `codebuddy` | `.codebuddy/commands
|
|
272
|
-
| `codex` | `.agents/skills
|
|
273
|
-
| `cursor-agent` | `.cursor/skills
|
|
274
|
-
| `firebender` | `.firebender/commands
|
|
275
|
-
| `gemini` | `.gemini/commands
|
|
264
|
+
| Key | Command directory |
|
|
265
|
+
| --- | ----------------- |
|
|
266
|
+
| `auggie` | `.augment/commands` |
|
|
267
|
+
| `claude` | `.claude/skills` |
|
|
268
|
+
| `cline` | `.clinerules/workflows` |
|
|
269
|
+
| `codebuddy` | `.codebuddy/commands` |
|
|
270
|
+
| `codex` | `.agents/skills` |
|
|
271
|
+
| `cursor-agent` | `.cursor/skills` |
|
|
272
|
+
| `firebender` | `.firebender/commands` |
|
|
273
|
+
| `gemini` | `.gemini/commands` |
|
|
276
274
|
| `grok` | `.grok/skills` |
|
|
277
|
-
| `junie` | `.junie/commands
|
|
278
|
-
| `kilocode` | `.kilocode/workflows
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `
|
|
282
|
-
| `
|
|
283
|
-
| `
|
|
284
|
-
| `
|
|
275
|
+
| `junie` | `.junie/commands` |
|
|
276
|
+
| `kilocode` | `.kilocode/workflows` |
|
|
277
|
+
| `kiro-cli` | `.kiro/prompts` |
|
|
278
|
+
| `qodercli` | `.qoder/commands` |
|
|
279
|
+
| `qwen` | `.qwen/commands` |
|
|
280
|
+
| `shai` | `.shai/commands` |
|
|
281
|
+
| `tabnine` | `.tabnine/agent/commands` |
|
|
282
|
+
| `trae` | `.trae/skills` |
|
|
283
|
+
| `zcode` | `.zcode/skills` |
|
|
285
284
|
|
|
286
285
|
Integrations that share a command directory with another integration, require dynamic install paths such as `--commands-dir`, or merge shared tool settings are not declared safe by default. They can still be installed alongside another integration with `--force`.
|
|
287
286
|
|
|
@@ -94,7 +94,12 @@ if [ -f "$_config_file" ]; then
|
|
|
94
94
|
[ "$_val" = "false" ] && _enabled=false
|
|
95
95
|
fi
|
|
96
96
|
if echo "$_line" | grep -Eq '[[:space:]]+message:'; then
|
|
97
|
-
|
|
97
|
+
# Trim trailing whitespace before stripping the closing quote:
|
|
98
|
+
# a value like `message: "Done" ` (trailing spaces after the
|
|
99
|
+
# quote) would otherwise leave the quote dangling (`Done" `),
|
|
100
|
+
# since the closing-quote strip is anchored to end-of-string.
|
|
101
|
+
# The PowerShell twin .Trim()s first; match it for parity.
|
|
102
|
+
_commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/[[:space:]]*$//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
|
|
98
103
|
fi
|
|
99
104
|
fi
|
|
100
105
|
fi
|
|
@@ -33,7 +33,15 @@ def _value_after_colon(line: str) -> str:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def _strip_quotes(value: str) -> str:
|
|
36
|
-
"""Strip one leading quote and all trailing quotes
|
|
36
|
+
"""Strip surrounding whitespace, then one leading quote and all trailing quotes.
|
|
37
|
+
|
|
38
|
+
Trimming first matters when the YAML value has trailing whitespace after a
|
|
39
|
+
closing quote (``message: "Done" ``): stripping quotes anchored to the end
|
|
40
|
+
of string would leave the closing quote dangling (``Done" ``) because the
|
|
41
|
+
quote is no longer at the end. The PowerShell twin ``.Trim()``s before
|
|
42
|
+
stripping, so trim here too to keep all three script variants in parity.
|
|
43
|
+
"""
|
|
44
|
+
value = value.strip()
|
|
37
45
|
value = re.sub(r"^[\"']", "", value)
|
|
38
46
|
return re.sub(r"[\"']*$", "", value)
|
|
39
47
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create or update the project constitution.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## User Input
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
$ARGUMENTS
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Scope Guard
|
|
12
|
+
|
|
13
|
+
This command's own work is limited to creating or updating the project constitution and
|
|
14
|
+
propagating constitution-driven changes to dependent Spec Kit artifacts.
|
|
15
|
+
|
|
16
|
+
- Classify every part of the user input as constitution content or a separate non-governance
|
|
17
|
+
intent. Feature implementation, code generation, refactoring, build, and deployment requests
|
|
18
|
+
are examples of non-governance intents.
|
|
19
|
+
- You **MUST NOT** execute any non-governance intent. Defer each one to `Next Actions`.
|
|
20
|
+
- You **MUST NOT** create, modify, or delete application source files or other artifacts
|
|
21
|
+
unrelated to the constitution workflow.
|
|
22
|
+
- If an instruction could be either constitution content or a non-governance intent, ask for
|
|
23
|
+
clarification before making changes.
|
|
24
|
+
- After updating the constitution, list each deferred intent in a `Next Actions` section with an
|
|
25
|
+
appropriate follow-up Spec Kit command, such as `__SPECKIT_COMMAND_SPECIFY__`, but do not
|
|
26
|
+
invoke it.
|
|
27
|
+
- Omit `Next Actions` when there are no non-governance intents.
|
|
28
|
+
|
|
29
|
+
## Outline
|
|
30
|
+
|
|
31
|
+
1. Create or update the project constitution and store it in `.specify/memory/constitution.md`.
|
|
32
|
+
- Project name, guiding principles, non-negotiable rules
|
|
33
|
+
- Derive from user input and existing repo context (README, docs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "specify-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.14.0"
|
|
4
4
|
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -39,6 +39,7 @@ packages = ["src/specify_cli"]
|
|
|
39
39
|
"templates/commands" = "specify_cli/core_pack/commands"
|
|
40
40
|
"scripts/bash" = "specify_cli/core_pack/scripts/bash"
|
|
41
41
|
"scripts/powershell" = "specify_cli/core_pack/scripts/powershell"
|
|
42
|
+
"scripts/python" = "specify_cli/core_pack/scripts/python"
|
|
42
43
|
# Bundled extensions (installable via `specify extension add <name>`)
|
|
43
44
|
"extensions/git" = "specify_cli/core_pack/extensions/git"
|
|
44
45
|
"extensions/agent-context" = "specify_cli/core_pack/extensions/agent-context"
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Helpers for bounded HTTP downloads."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import socket
|
|
7
|
+
from ipaddress import IPv4Address, IPv6Address, ip_address
|
|
8
|
+
from typing import NoReturn, TypeVar
|
|
9
|
+
from urllib.parse import ParseResult, urlparse
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ErrorT = TypeVar("ErrorT", bound=Exception)
|
|
13
|
+
|
|
14
|
+
MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024
|
|
15
|
+
READ_CHUNK_SIZE = 64 * 1024
|
|
16
|
+
|
|
17
|
+
# Tighter ceiling for responses that are read fully into memory and parsed as
|
|
18
|
+
# JSON. The 50 MiB MAX_DOWNLOAD_BYTES default is sized for archive/payload
|
|
19
|
+
# downloads; JSON metadata responses are far smaller, so capping them close to
|
|
20
|
+
# their real size shrinks the memory-DoS surface and keeps the "too large"
|
|
21
|
+
# error reachable (rather than only triggering on tens of MiB). Pass it
|
|
22
|
+
# explicitly at each JSON call site so the intended bound is pinned there.
|
|
23
|
+
# METADATA covers fixed-shape single-object responses (an OAuth token, one
|
|
24
|
+
# release's metadata): a few KiB in practice, 1 MiB is already generous.
|
|
25
|
+
MAX_JSON_METADATA_BYTES = 1 * 1024 * 1024
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _ip_address_without_scope(
|
|
29
|
+
hostname: str,
|
|
30
|
+
) -> IPv4Address | IPv6Address | None:
|
|
31
|
+
"""Parse a canonical IP literal, validating an optional IPv6 zone ID."""
|
|
32
|
+
if "%" in hostname:
|
|
33
|
+
# Accept only the RFC 6874 ``%25<zone>`` spelling. Other escapes can
|
|
34
|
+
# alter the IPv6 address when urllib unquotes the authority.
|
|
35
|
+
address_text, separator, zone = hostname.partition("%25")
|
|
36
|
+
if (
|
|
37
|
+
not separator
|
|
38
|
+
or ":" not in address_text
|
|
39
|
+
or "%" in address_text
|
|
40
|
+
or "%" in zone
|
|
41
|
+
):
|
|
42
|
+
return None
|
|
43
|
+
if not zone or any(
|
|
44
|
+
not (character.isascii() and (character.isalnum() or character in "._~-"))
|
|
45
|
+
for character in zone
|
|
46
|
+
):
|
|
47
|
+
return None
|
|
48
|
+
else:
|
|
49
|
+
address_text = hostname
|
|
50
|
+
try:
|
|
51
|
+
address = ip_address(address_text)
|
|
52
|
+
except ValueError:
|
|
53
|
+
return None
|
|
54
|
+
if "%" in hostname and not isinstance(address, IPv6Address):
|
|
55
|
+
return None
|
|
56
|
+
return address
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _is_ip_loopback(address: IPv4Address | IPv6Address | None) -> bool:
|
|
60
|
+
if address is None:
|
|
61
|
+
return False
|
|
62
|
+
mapped = getattr(address, "ipv4_mapped", None)
|
|
63
|
+
return address.is_loopback or bool(mapped and mapped.is_loopback)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _is_ip_local_redirect_target(
|
|
67
|
+
address: IPv4Address | IPv6Address | None,
|
|
68
|
+
) -> bool:
|
|
69
|
+
"""Treat loopback and unspecified listener aliases as local targets."""
|
|
70
|
+
if address is None:
|
|
71
|
+
return False
|
|
72
|
+
mapped = getattr(address, "ipv4_mapped", None)
|
|
73
|
+
return _is_ip_loopback(address) or address.is_unspecified or bool(
|
|
74
|
+
mapped and mapped.is_unspecified
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _parse_url(url: str) -> ParseResult | None:
|
|
79
|
+
"""Parse *url*, rejecting missing hosts and malformed ports."""
|
|
80
|
+
try:
|
|
81
|
+
parsed = urlparse(url)
|
|
82
|
+
hostname = parsed.hostname
|
|
83
|
+
# Accessing ``port`` performs urllib's range and syntax validation.
|
|
84
|
+
parsed.port
|
|
85
|
+
except (TypeError, ValueError):
|
|
86
|
+
return None
|
|
87
|
+
if not hostname:
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
if "%" in hostname:
|
|
91
|
+
# urllib unquotes reg-name/IPv4 authorities before connecting. Reject
|
|
92
|
+
# them so encoded dots, characters, ports, or brackets cannot make the
|
|
93
|
+
# validated hostname differ from the effective target. The only safe
|
|
94
|
+
# percent form retained is a validated bracketed IPv6 zone ID.
|
|
95
|
+
if _ip_address_without_scope(hostname) is None:
|
|
96
|
+
return None
|
|
97
|
+
elif ":" not in hostname:
|
|
98
|
+
try:
|
|
99
|
+
hostname.encode("idna")
|
|
100
|
+
except UnicodeError:
|
|
101
|
+
return None
|
|
102
|
+
return parsed
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _is_definite_loopback_host(hostname: str) -> bool:
|
|
106
|
+
"""Recognize only unambiguous hosts that may safely authorize HTTP."""
|
|
107
|
+
if not hostname.isascii():
|
|
108
|
+
return False
|
|
109
|
+
if hostname == "localhost":
|
|
110
|
+
return True
|
|
111
|
+
return _is_ip_loopback(_ip_address_without_scope(hostname))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _is_potential_local_target_host(hostname: str) -> bool:
|
|
115
|
+
"""Conservatively classify aliases that could reach a local listener."""
|
|
116
|
+
if ":" in hostname:
|
|
117
|
+
return _is_ip_local_redirect_target(_ip_address_without_scope(hostname))
|
|
118
|
+
try:
|
|
119
|
+
host = hostname.encode("idna").decode("ascii").lower().removesuffix(".")
|
|
120
|
+
except UnicodeError:
|
|
121
|
+
return False
|
|
122
|
+
if host == "localhost" or host.endswith(".localhost"):
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
address = _ip_address_without_scope(host)
|
|
126
|
+
if address is None:
|
|
127
|
+
# Historical IPv4 spellings are resolver-dependent. They are never
|
|
128
|
+
# trusted to authorize HTTP, but treating them as potentially local
|
|
129
|
+
# prevents them from bypassing a remote-to-loopback redirect check.
|
|
130
|
+
try:
|
|
131
|
+
address = ip_address(socket.inet_aton(host))
|
|
132
|
+
except OSError:
|
|
133
|
+
return False
|
|
134
|
+
return _is_ip_local_redirect_target(address)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def is_loopback_url(url: str) -> bool:
|
|
138
|
+
"""Return whether *url* has an unambiguous loopback host."""
|
|
139
|
+
parsed = _parse_url(url)
|
|
140
|
+
return parsed is not None and _is_definite_loopback_host(parsed.hostname)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _is_potential_local_target_url(url: str) -> bool:
|
|
144
|
+
parsed = _parse_url(url)
|
|
145
|
+
return parsed is not None and _is_potential_local_target_host(parsed.hostname)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def is_https_or_localhost_http(url: str) -> bool:
|
|
149
|
+
"""Return True if *url* is HTTPS, or HTTP limited to loopback hosts.
|
|
150
|
+
|
|
151
|
+
Shared scheme-safety predicate used by the auth HTTP redirect handler and
|
|
152
|
+
direct URL validations in CLI download flows.
|
|
153
|
+
|
|
154
|
+
A hostname is always required: a URL without one (e.g. ``https:///x``)
|
|
155
|
+
has no real target and is rejected regardless of scheme.
|
|
156
|
+
|
|
157
|
+
The HTTP exception is deliberately limited to unambiguous ``localhost``
|
|
158
|
+
and canonical IPv4/IPv6 loopback literals. Ambiguous numeric, Unicode, and
|
|
159
|
+
unspecified-address aliases are classified defensively for redirects but
|
|
160
|
+
never authorize HTTP. No DNS lookup is performed; DNS and hosts-file
|
|
161
|
+
aliases require connection-level rebinding protection outside this helper.
|
|
162
|
+
"""
|
|
163
|
+
parsed = _parse_url(url)
|
|
164
|
+
if parsed is None:
|
|
165
|
+
return False
|
|
166
|
+
return parsed.scheme == "https" or (
|
|
167
|
+
parsed.scheme == "http" and _is_definite_loopback_host(parsed.hostname)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def is_safe_download_redirect(old_url: str, new_url: str) -> bool:
|
|
172
|
+
"""Return whether a redirect preserves the shared download URL policy."""
|
|
173
|
+
if not is_https_or_localhost_http(new_url):
|
|
174
|
+
return False
|
|
175
|
+
return not _is_potential_local_target_url(new_url) or is_loopback_url(old_url)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _raise(error_type: type[ErrorT], message: str) -> NoReturn:
|
|
179
|
+
raise error_type(message)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def read_response_limited(
|
|
183
|
+
response,
|
|
184
|
+
*,
|
|
185
|
+
max_bytes: int = MAX_DOWNLOAD_BYTES,
|
|
186
|
+
error_type: type[ErrorT] = ValueError,
|
|
187
|
+
label: str = "download",
|
|
188
|
+
) -> bytes:
|
|
189
|
+
"""Read at most *max_bytes* from a response object.
|
|
190
|
+
|
|
191
|
+
``response.read(n)`` is only guaranteed to return *up to* ``n`` bytes and may
|
|
192
|
+
return fewer even when more data is pending (e.g. chunked transfer encoding),
|
|
193
|
+
so a single ``read(max_bytes + 1)`` cannot enforce the bound on its own. Read
|
|
194
|
+
in a loop until EOF or until one byte past the limit has been accumulated.
|
|
195
|
+
|
|
196
|
+
*max_bytes* is keyword-only. It defaults to the module-wide
|
|
197
|
+
``MAX_DOWNLOAD_BYTES`` (50 MiB) ceiling for archive/payload downloads;
|
|
198
|
+
callers with a tighter budget (e.g. small JSON responses) should pass an
|
|
199
|
+
explicit value so the intended bound is pinned at the call site rather than
|
|
200
|
+
tracking changes to the shared default.
|
|
201
|
+
"""
|
|
202
|
+
if isinstance(max_bytes, bool) or not isinstance(max_bytes, int):
|
|
203
|
+
raise TypeError("max_bytes must be an integer")
|
|
204
|
+
if max_bytes < 0:
|
|
205
|
+
raise ValueError("max_bytes must be non-negative")
|
|
206
|
+
|
|
207
|
+
output = io.BytesIO()
|
|
208
|
+
total = 0
|
|
209
|
+
limit = max_bytes + 1
|
|
210
|
+
while total < limit:
|
|
211
|
+
chunk = response.read(min(READ_CHUNK_SIZE, limit - total))
|
|
212
|
+
if not chunk:
|
|
213
|
+
break
|
|
214
|
+
total += len(chunk)
|
|
215
|
+
if total > max_bytes:
|
|
216
|
+
_raise(error_type, f"{label} exceeds maximum size of {max_bytes} bytes")
|
|
217
|
+
output.write(chunk)
|
|
218
|
+
return output.getvalue()
|
|
@@ -100,6 +100,8 @@ def resolve_github_release_asset_api_url(
|
|
|
100
100
|
import json
|
|
101
101
|
import urllib.error
|
|
102
102
|
|
|
103
|
+
from specify_cli._download_security import read_response_limited
|
|
104
|
+
|
|
103
105
|
parsed = urlparse(download_url)
|
|
104
106
|
hostname = (parsed.hostname or "").lower()
|
|
105
107
|
parts = [unquote(part) for part in parsed.path.strip("/").split("/")]
|
|
@@ -158,10 +160,13 @@ def resolve_github_release_asset_api_url(
|
|
|
158
160
|
if redirect_validator is not None:
|
|
159
161
|
open_kwargs["redirect_validator"] = redirect_validator
|
|
160
162
|
with open_url_fn(release_url, **open_kwargs) as response:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
release_data = json.loads(
|
|
164
|
+
read_response_limited(
|
|
165
|
+
response,
|
|
166
|
+
max_bytes=max_metadata_bytes,
|
|
167
|
+
label=f"GitHub release metadata {release_url}",
|
|
168
|
+
)
|
|
169
|
+
)
|
|
165
170
|
except (
|
|
166
171
|
urllib.error.URLError,
|
|
167
172
|
json.JSONDecodeError,
|
|
@@ -4,8 +4,8 @@ Pure helpers for comparing PEP 440 versions and fetching the latest GitHub
|
|
|
4
4
|
release tag. The ``self_app`` Typer sub-command group is co-located here so
|
|
5
5
|
all version-related logic lives in one place.
|
|
6
6
|
|
|
7
|
-
Dependencies: stdlib + packaging + ._console
|
|
8
|
-
|
|
7
|
+
Dependencies: stdlib + packaging + ._console + ._download_security only
|
|
8
|
+
(keeping this layer thin and circular-import-safe).
|
|
9
9
|
"""
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
@@ -28,6 +28,7 @@ from pathlib import Path
|
|
|
28
28
|
import typer
|
|
29
29
|
from packaging.version import InvalidVersion, Version
|
|
30
30
|
|
|
31
|
+
from ._download_security import MAX_JSON_METADATA_BYTES, read_response_limited
|
|
31
32
|
from ._console import console
|
|
32
33
|
|
|
33
34
|
GITHUB_API_LATEST = "https://api.github.com/repos/github/spec-kit/releases/latest"
|
|
@@ -119,7 +120,13 @@ def _fetch_latest_release_tag() -> tuple[str | None, str | None]:
|
|
|
119
120
|
timeout=5,
|
|
120
121
|
extra_headers={"Accept": "application/vnd.github+json"},
|
|
121
122
|
) as resp:
|
|
122
|
-
payload = json.loads(
|
|
123
|
+
payload = json.loads(
|
|
124
|
+
read_response_limited(
|
|
125
|
+
resp,
|
|
126
|
+
max_bytes=MAX_JSON_METADATA_BYTES,
|
|
127
|
+
label="GitHub latest release",
|
|
128
|
+
).decode("utf-8")
|
|
129
|
+
)
|
|
123
130
|
tag = payload.get("tag_name")
|
|
124
131
|
if not isinstance(tag, str) or not tag:
|
|
125
132
|
raise ValueError("GitHub API response missing valid tag_name")
|