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.
Files changed (521) hide show
  1. {specify_cli-0.13.4 → specify_cli-0.14.0}/.devcontainer/devcontainer.json +2 -1
  2. {specify_cli-0.13.4 → specify_cli-0.14.0}/AGENTS.md +41 -2
  3. {specify_cli-0.13.4 → specify_cli-0.14.0}/CHANGELOG.md +25 -0
  4. {specify_cli-0.13.4 → specify_cli-0.14.0}/PKG-INFO +1 -1
  5. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/friends.md +3 -1
  6. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/local-development.md +2 -2
  7. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/quickstart.md +1 -1
  8. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/integrations.md +23 -24
  9. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/auto-commit.sh +6 -1
  10. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/auto_commit.py +9 -1
  11. specify_cli-0.14.0/presets/lean/commands/speckit.constitution.md +33 -0
  12. {specify_cli-0.13.4 → specify_cli-0.14.0}/pyproject.toml +2 -1
  13. specify_cli-0.14.0/src/specify_cli/_download_security.py +218 -0
  14. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_github_http.py +9 -4
  15. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_version.py +10 -3
  16. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/azure_devops.py +49 -7
  17. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/http.py +30 -5
  18. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/yamlio.py +7 -1
  19. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/manifest.py +8 -4
  20. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/packager.py +7 -1
  21. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_runtime.py +8 -1
  22. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/cline/__init__.py +9 -3
  23. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/kiro_cli/__init__.py +7 -7
  24. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/lingma/__init__.py +1 -0
  25. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/presets/_commands.py +10 -19
  26. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/_commands.py +15 -74
  27. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/merge.py +15 -3
  28. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/gate/__init__.py +1 -1
  29. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/init/__init__.py +1 -1
  30. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/constitution.md +20 -0
  31. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/specify.md +2 -2
  32. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/contract/test_manifest_schema.py +31 -0
  33. specify_cli-0.14.0/tests/contract/test_wheel_core_pack_scripts.py +40 -0
  34. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/git/test_git_extension_python_parity.py +26 -0
  35. specify_cli-0.14.0/tests/http_helpers.py +46 -0
  36. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_catalog.py +2 -0
  37. specify_cli-0.14.0/tests/integrations/test_integration_lingma.py +18 -0
  38. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_state.py +28 -0
  39. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/self_upgrade_helpers.py +2 -1
  40. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_authentication.py +342 -28
  41. specify_cli-0.14.0/tests/test_download_security.py +227 -0
  42. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extensions.py +7 -5
  43. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_github_http.py +52 -8
  44. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_post_process.py +31 -0
  45. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_presets.py +33 -5
  46. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_detection.py +1 -0
  47. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_execution.py +1 -0
  48. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_guidance.py +1 -0
  49. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_self_upgrade_verification.py +1 -0
  50. specify_cli-0.14.0/tests/test_specify_template_numbering.py +39 -0
  51. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_upgrade.py +51 -6
  52. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_workflows.py +59 -50
  53. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_packager.py +17 -0
  54. specify_cli-0.14.0/tests/unit/test_bundler_yamlio.py +26 -0
  55. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_merge.py +17 -0
  56. specify_cli-0.13.4/presets/lean/commands/speckit.constitution.md +0 -15
  57. specify_cli-0.13.4/tests/http_helpers.py +0 -15
  58. specify_cli-0.13.4/tests/integrations/test_integration_lingma.py +0 -10
  59. {specify_cli-0.13.4 → specify_cli-0.14.0}/.devcontainer/post-create.sh +0 -0
  60. {specify_cli-0.13.4 → specify_cli-0.14.0}/.editorconfig +0 -0
  61. {specify_cli-0.13.4 → specify_cli-0.14.0}/.gitattributes +0 -0
  62. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/CODEOWNERS +0 -0
  63. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/agent_request.yml +0 -0
  64. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  65. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/bundle_submission.yml +0 -0
  66. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  67. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/extension_submission.yml +0 -0
  68. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  69. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/ISSUE_TEMPLATE/preset_submission.yml +0 -0
  70. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  71. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/aw/actions-lock.json +0 -0
  72. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/dependabot.yml +0 -0
  73. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/scripts/check_security_requirements.py +0 -0
  74. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/security-audit-requirements.txt +0 -0
  75. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/skills/add-community-extension/SKILL.md +0 -0
  76. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/RELEASE-PROCESS.md +0 -0
  77. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-bundle.lock.yml +0 -0
  78. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-bundle.md +0 -0
  79. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-extension.lock.yml +0 -0
  80. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-extension.md +0 -0
  81. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-preset.lock.yml +0 -0
  82. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/add-community-preset.md +0 -0
  83. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-assess.lock.yml +0 -0
  84. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-assess.md +0 -0
  85. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-fix.lock.yml +0 -0
  86. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-fix.md +0 -0
  87. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-test.lock.yml +0 -0
  88. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/bug-test.md +0 -0
  89. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/catalog-assign.yml +0 -0
  90. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/codeql.yml +0 -0
  91. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/docs.yml +0 -0
  92. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/lint.yml +0 -0
  93. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/publish-pypi.yml +0 -0
  94. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/release-trigger.yml +0 -0
  95. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/release.yml +0 -0
  96. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/security.yml +0 -0
  97. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/stale.yml +0 -0
  98. {specify_cli-0.13.4 → specify_cli-0.14.0}/.github/workflows/test.yml +0 -0
  99. {specify_cli-0.13.4 → specify_cli-0.14.0}/.gitignore +0 -0
  100. {specify_cli-0.13.4 → specify_cli-0.14.0}/.markdownlint-cli2.jsonc +0 -0
  101. {specify_cli-0.13.4 → specify_cli-0.14.0}/.pre-commit-config.yaml +0 -0
  102. {specify_cli-0.13.4 → specify_cli-0.14.0}/.zenodo.json +0 -0
  103. {specify_cli-0.13.4 → specify_cli-0.14.0}/CITATION.cff +0 -0
  104. {specify_cli-0.13.4 → specify_cli-0.14.0}/CODE_OF_CONDUCT.md +0 -0
  105. {specify_cli-0.13.4 → specify_cli-0.14.0}/CONTRIBUTING.md +0 -0
  106. {specify_cli-0.13.4 → specify_cli-0.14.0}/DEVELOPMENT.md +0 -0
  107. {specify_cli-0.13.4 → specify_cli-0.14.0}/LICENSE +0 -0
  108. {specify_cli-0.13.4 → specify_cli-0.14.0}/README.md +0 -0
  109. {specify_cli-0.13.4 → specify_cli-0.14.0}/SECURITY.md +0 -0
  110. {specify_cli-0.13.4 → specify_cli-0.14.0}/SUPPORT.md +0 -0
  111. {specify_cli-0.13.4 → specify_cli-0.14.0}/bundles/catalog.community.json +0 -0
  112. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/.gitignore +0 -0
  113. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/README.md +0 -0
  114. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/bundles.md +0 -0
  115. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/extensions.md +0 -0
  116. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/overview.md +0 -0
  117. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/presets.md +0 -0
  118. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/community/walkthroughs.md +0 -0
  119. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/complex-features.md +0 -0
  120. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/sdd.md +0 -0
  121. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/spec-of-specs.md +0 -0
  122. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/concepts/spec-persistence.md +0 -0
  123. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/docfx.json +0 -0
  124. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/guides/evolving-specs.md +0 -0
  125. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/guides/monorepo.md +0 -0
  126. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/index.md +0 -0
  127. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/air-gapped.md +0 -0
  128. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/one-time.md +0 -0
  129. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/pipx.md +0 -0
  130. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/pypi.md +0 -0
  131. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/install/uv.md +0 -0
  132. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/installation.md +0 -0
  133. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/agentic-bugfix.md +0 -0
  134. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/agentic-sdd.md +0 -0
  135. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/authentication.md +0 -0
  136. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/bundles.md +0 -0
  137. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/core.md +0 -0
  138. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/extensions.md +0 -0
  139. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/overview.md +0 -0
  140. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/presets.md +0 -0
  141. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/reference/workflows.md +0 -0
  142. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/template/public/main.css +0 -0
  143. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/toc.yml +0 -0
  144. {specify_cli-0.13.4 → specify_cli-0.14.0}/docs/upgrade.md +0 -0
  145. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/business-analyst/README.md +0 -0
  146. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/business-analyst/bundle.yml +0 -0
  147. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/developer/README.md +0 -0
  148. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/developer/bundle.yml +0 -0
  149. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/product-manager/README.md +0 -0
  150. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/product-manager/bundle.yml +0 -0
  151. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/security-researcher/README.md +0 -0
  152. {specify_cli-0.13.4 → specify_cli-0.14.0}/examples/bundles/security-researcher/bundle.yml +0 -0
  153. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-API-REFERENCE.md +0 -0
  154. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-DEVELOPMENT-GUIDE.md +0 -0
  155. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-PUBLISHING-GUIDE.md +0 -0
  156. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/EXTENSION-USER-GUIDE.md +0 -0
  157. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/README.md +0 -0
  158. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/RFC-EXTENSION-SYSTEM.md +0 -0
  159. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/README.md +0 -0
  160. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/agent-context-config.yml +0 -0
  161. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/agent-context-defaults.json +0 -0
  162. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/commands/speckit.agent-context.update.md +0 -0
  163. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/extension.yml +0 -0
  164. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -0
  165. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -0
  166. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/agent-context/scripts/python/update_agent_context.py +0 -0
  167. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/README.md +0 -0
  168. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.decide.md +0 -0
  169. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.define.md +0 -0
  170. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.intake.md +0 -0
  171. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.research.md +0 -0
  172. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/commands/speckit.assess.shape.md +0 -0
  173. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/assess/extension.yml +0 -0
  174. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/README.md +0 -0
  175. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/commands/speckit.bug.assess.md +0 -0
  176. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/commands/speckit.bug.fix.md +0 -0
  177. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/commands/speckit.bug.test.md +0 -0
  178. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/bug/extension.yml +0 -0
  179. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/catalog.community.json +0 -0
  180. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/catalog.json +0 -0
  181. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/README.md +0 -0
  182. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.commit.md +0 -0
  183. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.feature.md +0 -0
  184. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.initialize.md +0 -0
  185. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.remote.md +0 -0
  186. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/commands/speckit.git.validate.md +0 -0
  187. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/config-template.yml +0 -0
  188. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/extension.yml +0 -0
  189. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/git-config.yml +0 -0
  190. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/create-new-feature-branch.sh +0 -0
  191. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/git-common.sh +0 -0
  192. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  193. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  194. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +0 -0
  195. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  196. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  197. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/create_new_feature_branch.py +0 -0
  198. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/git_common.py +0 -0
  199. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/git/scripts/python/initialize_repo.py +0 -0
  200. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/selftest/commands/selftest.md +0 -0
  201. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/selftest/extension.yml +0 -0
  202. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/.gitignore +0 -0
  203. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/CHANGELOG.md +0 -0
  204. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/EXAMPLE-README.md +0 -0
  205. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/LICENSE +0 -0
  206. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/README.md +0 -0
  207. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/commands/example.md +0 -0
  208. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/config-template.yml +0 -0
  209. {specify_cli-0.13.4 → specify_cli-0.14.0}/extensions/template/extension.yml +0 -0
  210. {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/CONTRIBUTING.md +0 -0
  211. {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/README.md +0 -0
  212. {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/catalog.community.json +0 -0
  213. {specify_cli-0.13.4 → specify_cli-0.14.0}/integrations/catalog.json +0 -0
  214. {specify_cli-0.13.4 → specify_cli-0.14.0}/media/bootstrap-claude-code.gif +0 -0
  215. {specify_cli-0.13.4 → specify_cli-0.14.0}/media/logo_large.webp +0 -0
  216. {specify_cli-0.13.4 → specify_cli-0.14.0}/media/logo_small.webp +0 -0
  217. {specify_cli-0.13.4 → specify_cli-0.14.0}/media/spec-kit-video-header.jpg +0 -0
  218. {specify_cli-0.13.4 → specify_cli-0.14.0}/media/specify_cli.gif +0 -0
  219. {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-April.md +0 -0
  220. {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-February.md +0 -0
  221. {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-June.md +0 -0
  222. {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-March.md +0 -0
  223. {specify_cli-0.13.4 → specify_cli-0.14.0}/newsletters/2026-May.md +0 -0
  224. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/ARCHITECTURE.md +0 -0
  225. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/PUBLISHING.md +0 -0
  226. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/README.md +0 -0
  227. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/catalog.community.json +0 -0
  228. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/catalog.json +0 -0
  229. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/README.md +0 -0
  230. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.implement.md +0 -0
  231. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.plan.md +0 -0
  232. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.specify.md +0 -0
  233. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/commands/speckit.tasks.md +0 -0
  234. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/lean/preset.yml +0 -0
  235. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/README.md +0 -0
  236. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/commands/speckit.myext.myextcmd.md +0 -0
  237. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/commands/speckit.specify.md +0 -0
  238. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/preset.yml +0 -0
  239. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/templates/myext-template.md +0 -0
  240. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/scaffold/templates/spec-template.md +0 -0
  241. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/commands/speckit.specify.md +0 -0
  242. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/commands/speckit.wrap-test.md +0 -0
  243. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/preset.yml +0 -0
  244. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/checklist-template.md +0 -0
  245. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/constitution-template.md +0 -0
  246. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/plan-template.md +0 -0
  247. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/spec-template.md +0 -0
  248. {specify_cli-0.13.4 → specify_cli-0.14.0}/presets/self-test/templates/tasks-template.md +0 -0
  249. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/check-prerequisites.sh +0 -0
  250. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/common.sh +0 -0
  251. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/create-new-feature.sh +0 -0
  252. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/setup-plan.sh +0 -0
  253. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/bash/setup-tasks.sh +0 -0
  254. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/check-prerequisites.ps1 +0 -0
  255. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/common.ps1 +0 -0
  256. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/create-new-feature.ps1 +0 -0
  257. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/setup-plan.ps1 +0 -0
  258. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/powershell/setup-tasks.ps1 +0 -0
  259. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/check_prerequisites.py +0 -0
  260. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/common.py +0 -0
  261. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/create_new_feature.py +0 -0
  262. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/setup_plan.py +0 -0
  263. {specify_cli-0.13.4 → specify_cli-0.14.0}/scripts/python/setup_tasks.py +0 -0
  264. {specify_cli-0.13.4 → specify_cli-0.14.0}/spec-driven.md +0 -0
  265. {specify_cli-0.13.4 → specify_cli-0.14.0}/spec-kit.code-workspace +0 -0
  266. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/__init__.py +0 -0
  267. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_agent_config.py +0 -0
  268. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_assets.py +0 -0
  269. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_console.py +0 -0
  270. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_init_options.py +0 -0
  271. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_invocation_style.py +0 -0
  272. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_project.py +0 -0
  273. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_toml_string.py +0 -0
  274. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/_utils.py +0 -0
  275. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/agents.py +0 -0
  276. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/__init__.py +0 -0
  277. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/base.py +0 -0
  278. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/config.py +0 -0
  279. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/authentication/github.py +0 -0
  280. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/__init__.py +0 -0
  281. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/commands_impl/__init__.py +0 -0
  282. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/commands_impl/catalog_config.py +0 -0
  283. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/__init__.py +0 -0
  284. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/project.py +0 -0
  285. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/lib/versioning.py +0 -0
  286. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/__init__.py +0 -0
  287. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/catalog.py +0 -0
  288. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/models/records.py +0 -0
  289. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/__init__.py +0 -0
  290. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/adapters.py +0 -0
  291. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/catalog_stack.py +0 -0
  292. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/conflict.py +0 -0
  293. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/installer.py +0 -0
  294. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/primitives.py +0 -0
  295. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/references.py +0 -0
  296. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/resolver.py +0 -0
  297. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/bundler/services/validator.py +0 -0
  298. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/catalogs.py +0 -0
  299. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/commands/__init__.py +0 -0
  300. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/commands/bundle/__init__.py +0 -0
  301. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/commands/init.py +0 -0
  302. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/extensions/__init__.py +0 -0
  303. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/extensions/_commands.py +0 -0
  304. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_scaffold.py +0 -0
  305. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_state.py +0 -0
  306. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integration_status.py +0 -0
  307. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/__init__.py +0 -0
  308. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_commands.py +0 -0
  309. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_helpers.py +0 -0
  310. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_install_commands.py +0 -0
  311. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_migrate_commands.py +0 -0
  312. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_query_commands.py +0 -0
  313. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/_scaffold_commands.py +0 -0
  314. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/agy/__init__.py +0 -0
  315. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/amp/__init__.py +0 -0
  316. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/auggie/__init__.py +0 -0
  317. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/base.py +0 -0
  318. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/bob/__init__.py +0 -0
  319. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/catalog.py +0 -0
  320. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/claude/__init__.py +0 -0
  321. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/codebuddy/__init__.py +0 -0
  322. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/codex/__init__.py +0 -0
  323. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/copilot/__init__.py +0 -0
  324. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/cursor_agent/__init__.py +0 -0
  325. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/devin/__init__.py +0 -0
  326. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/droid/__init__.py +0 -0
  327. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/firebender/__init__.py +0 -0
  328. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/forge/__init__.py +0 -0
  329. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/gemini/__init__.py +0 -0
  330. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/generic/__init__.py +0 -0
  331. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/goose/__init__.py +0 -0
  332. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/grok/__init__.py +0 -0
  333. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/hermes/__init__.py +0 -0
  334. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/junie/__init__.py +0 -0
  335. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/kilocode/__init__.py +0 -0
  336. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/kimi/__init__.py +0 -0
  337. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/manifest.py +0 -0
  338. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/omp/__init__.py +0 -0
  339. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/opencode/__init__.py +0 -0
  340. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/pi/__init__.py +0 -0
  341. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/qodercli/__init__.py +0 -0
  342. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/qwen/__init__.py +0 -0
  343. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/rovodev/__init__.py +0 -0
  344. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/shai/__init__.py +0 -0
  345. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/tabnine/__init__.py +0 -0
  346. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/trae/__init__.py +0 -0
  347. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/vibe/__init__.py +0 -0
  348. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/zcode/__init__.py +0 -0
  349. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/integrations/zed/__init__.py +0 -0
  350. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/presets/__init__.py +0 -0
  351. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/shared_infra.py +0 -0
  352. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/__init__.py +0 -0
  353. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/base.py +0 -0
  354. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/catalog.py +0 -0
  355. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/engine.py +0 -0
  356. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/expressions.py +0 -0
  357. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/__init__.py +0 -0
  358. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/_commands.py +0 -0
  359. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/composer.py +0 -0
  360. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/layer_sources.py +0 -0
  361. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/overlays/schema.py +0 -0
  362. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/__init__.py +0 -0
  363. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/command/__init__.py +0 -0
  364. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/do_while/__init__.py +0 -0
  365. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/fan_in/__init__.py +0 -0
  366. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/fan_out/__init__.py +0 -0
  367. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/if_then/__init__.py +0 -0
  368. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/prompt/__init__.py +0 -0
  369. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/shell/__init__.py +0 -0
  370. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/switch/__init__.py +0 -0
  371. {specify_cli-0.13.4 → specify_cli-0.14.0}/src/specify_cli/workflows/steps/while_loop/__init__.py +0 -0
  372. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/checklist-template.md +0 -0
  373. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/analyze.md +0 -0
  374. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/checklist.md +0 -0
  375. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/clarify.md +0 -0
  376. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/converge.md +0 -0
  377. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/implement.md +0 -0
  378. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/plan.md +0 -0
  379. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/tasks.md +0 -0
  380. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/commands/taskstoissues.md +0 -0
  381. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/constitution-template.md +0 -0
  382. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/plan-template.md +0 -0
  383. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/spec-template.md +0 -0
  384. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/tasks-template.md +0 -0
  385. {specify_cli-0.13.4 → specify_cli-0.14.0}/templates/vscode-settings.json +0 -0
  386. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/__init__.py +0 -0
  387. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/auth_helpers.py +0 -0
  388. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/bundler_helpers.py +0 -0
  389. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/conftest.py +0 -0
  390. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/contract/test_bundle_cli.py +0 -0
  391. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/contract/test_catalog_schema.py +0 -0
  392. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/__init__.py +0 -0
  393. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/assess/__init__.py +0 -0
  394. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/assess/test_assess_extension.py +0 -0
  395. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/bug/__init__.py +0 -0
  396. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/bug/test_bug_extension.py +0 -0
  397. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/git/__init__.py +0 -0
  398. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/git/test_git_extension.py +0 -0
  399. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_agent_context_cli_free.py +0 -0
  400. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_extension_agent_context.py +0 -0
  401. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_update_agent_context_feature_json.py +0 -0
  402. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/extensions/test_update_agent_context_python_parity.py +0 -0
  403. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/TESTING.md +0 -0
  404. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/plan.md +0 -0
  405. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/spec.md +0 -0
  406. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/hooks/tasks.md +0 -0
  407. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_catalog_stack.py +0 -0
  408. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_init_install.py +0 -0
  409. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_install_flow.py +0 -0
  410. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_local_install.py +0 -0
  411. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_offline.py +0 -0
  412. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integration/test_bundler_security_paths.py +0 -0
  413. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/__init__.py +0 -0
  414. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/conftest.py +0 -0
  415. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_base.py +0 -0
  416. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_cli.py +0 -0
  417. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_extra_args.py +0 -0
  418. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_home_isolation.py +0 -0
  419. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_agy.py +0 -0
  420. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_amp.py +0 -0
  421. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_auggie.py +0 -0
  422. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_markdown.py +0 -0
  423. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_skills.py +0 -0
  424. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_toml.py +0 -0
  425. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_base_yaml.py +0 -0
  426. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_bob.py +0 -0
  427. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_claude.py +0 -0
  428. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_cline.py +0 -0
  429. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_codebuddy.py +0 -0
  430. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_codex.py +0 -0
  431. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_copilot.py +0 -0
  432. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_cursor_agent.py +0 -0
  433. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_devin.py +0 -0
  434. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_droid.py +0 -0
  435. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_firebender.py +0 -0
  436. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_forge.py +0 -0
  437. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_gemini.py +0 -0
  438. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_generic.py +0 -0
  439. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_goose.py +0 -0
  440. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_grok.py +0 -0
  441. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_hermes.py +0 -0
  442. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_junie.py +0 -0
  443. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_kilocode.py +0 -0
  444. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_kimi.py +0 -0
  445. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_kiro_cli.py +0 -0
  446. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_omp.py +0 -0
  447. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_opencode.py +0 -0
  448. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_pi.py +0 -0
  449. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_qodercli.py +0 -0
  450. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_qwen.py +0 -0
  451. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_rovodev.py +0 -0
  452. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_scaffold.py +0 -0
  453. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_shai.py +0 -0
  454. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_subcommand.py +0 -0
  455. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_tabnine.py +0 -0
  456. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_trae.py +0 -0
  457. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_vibe.py +0 -0
  458. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_zcode.py +0 -0
  459. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_integration_zed.py +0 -0
  460. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_manifest.py +0 -0
  461. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_registry.py +0 -0
  462. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/integrations/test_skill_frontmatter_quoting.py +0 -0
  463. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/parity_helpers.py +0 -0
  464. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_agent_config_consistency.py +0 -0
  465. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_branch_numbering.py +0 -0
  466. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_check_prerequisites_paths_only.py +0 -0
  467. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_check_prerequisites_python_parity.py +0 -0
  468. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_check_tool.py +0 -0
  469. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_cli_version.py +0 -0
  470. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_command_template_py_scripts.py +0 -0
  471. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_commands_package.py +0 -0
  472. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_console_imports.py +0 -0
  473. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_create_new_feature_python_parity.py +0 -0
  474. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extension_registration.py +0 -0
  475. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extension_skills.py +0 -0
  476. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_extension_update_hardening.py +0 -0
  477. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_github_workflows.py +0 -0
  478. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_init_dir.py +0 -0
  479. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_init_dir_cli.py +0 -0
  480. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_live_transient_windows.py +0 -0
  481. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_merge.py +0 -0
  482. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_ps1_encoding.py +0 -0
  483. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_registrar_path_traversal.py +0 -0
  484. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_security_workflow.py +0 -0
  485. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_plan_feature_json.py +0 -0
  486. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_plan_no_overwrite.py +0 -0
  487. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_plan_python_parity.py +0 -0
  488. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_tasks.py +0 -0
  489. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_setup_tasks_python_parity.py +0 -0
  490. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_shared_infra_integrity.py +0 -0
  491. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_skill_placeholder_py.py +0 -0
  492. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_timestamp_branches.py +0 -0
  493. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_utils.py +0 -0
  494. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_utils_assets_imports.py +0 -0
  495. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_version_imports.py +0 -0
  496. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/test_workflow_run_without_project.py +0 -0
  497. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundle_download_url.py +0 -0
  498. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_adapters.py +0 -0
  499. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_catalog_config.py +0 -0
  500. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_conflict.py +0 -0
  501. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_primitives.py +0 -0
  502. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_records.py +0 -0
  503. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_references.py +0 -0
  504. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_resolver.py +0 -0
  505. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_validator.py +0 -0
  506. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/unit/test_bundler_versioning.py +0 -0
  507. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/conftest.py +0 -0
  508. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_commands.py +0 -0
  509. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_composer.py +0 -0
  510. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_layer_sources.py +0 -0
  511. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_schema.py +0 -0
  512. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_overlay_security.py +0 -0
  513. {specify_cli-0.13.4 → specify_cli-0.14.0}/tests/workflows/test_resolver_integration.py +0 -0
  514. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/ARCHITECTURE.md +0 -0
  515. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/PUBLISHING.md +0 -0
  516. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/README.md +0 -0
  517. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/catalog.community.json +0 -0
  518. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/catalog.json +0 -0
  519. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/speckit/workflow.yml +0 -0
  520. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/step-catalog.community.json +0 -0
  521. {specify_cli-0.13.4 → specify_cli-0.14.0}/workflows/step-catalog.json +0 -0
@@ -65,7 +65,8 @@
65
65
  },
66
66
  "chat.tools.terminal.autoApprove": {
67
67
  ".specify/scripts/bash/": true,
68
- ".specify/scripts/powershell/": true
68
+ ".specify/scripts/powershell/": true,
69
+ ".specify/scripts/python/": true
69
70
  }
70
71
  }
71
72
  }
@@ -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` and `.ps1`). The CLI registry is never consulted — all agent→context-file knowledge lives inside the extension.
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 actual script path)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specify-cli
3
- Version: 0.13.4
3
+ Version: 0.14.0
4
4
  Summary: Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD).
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -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. They are **not reviewed, nor endorsed, nor supported by GitHub**. Review their source code before installation and use at your own discretion.
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 now have both Bash (`.sh`) and PowerShell (`.ps1`) variants. The CLI auto-selects based on OS unless you pass `--script sh|ps`.
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` or `--script ps` explicitly |
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 both Bash (`.sh`) and PowerShell (`.ps1`) variants. The `specify` CLI auto-selects based on your OS unless you pass `--script sh|ps`.
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` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
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` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
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` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
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 Isolation column below lists paths Spec Kit manages for that integration (skills/commands roots and any integration-owned rule files). It is not a full inventory of every file an agent may read.
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 | Isolation |
267
- | --- | --------- |
268
- | `auggie` | `.augment/commands`, `.augment/rules/specify-rules.md` |
269
- | `claude` | `.claude/skills`, `CLAUDE.md` |
270
- | `cline` | `.clinerules/workflows`, `.clinerules/specify-rules.md` |
271
- | `codebuddy` | `.codebuddy/commands`, `CODEBUDDY.md` |
272
- | `codex` | `.agents/skills`, `AGENTS.md` |
273
- | `cursor-agent` | `.cursor/skills`, `.cursor/rules/specify-rules.mdc` |
274
- | `firebender` | `.firebender/commands`, `.firebender/rules/specify-rules.mdc` |
275
- | `gemini` | `.gemini/commands`, `GEMINI.md` |
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`, `.junie/AGENTS.md` |
278
- | `kilocode` | `.kilocode/workflows`, `.kilocode/rules/specify-rules.md` |
279
- | `qodercli` | `.qoder/commands`, `QODER.md` |
280
- | `qwen` | `.qwen/commands`, `QWEN.md` |
281
- | `shai` | `.shai/commands`, `SHAI.md` |
282
- | `tabnine` | `.tabnine/agent/commands`, `TABNINE.md` |
283
- | `trae` | `.trae/skills`, `.trae/rules/project_rules.md` |
284
- | `zcode` | `.zcode/skills`, `ZCODE.md` |
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
- _commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
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, mirroring the bash sed."""
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.13.4"
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
- raw_release_data = response.read(max_metadata_bytes + 1)
162
- if len(raw_release_data) > max_metadata_bytes:
163
- raise ValueError("GitHub release metadata exceeds size limit")
164
- release_data = json.loads(raw_release_data)
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 only (no other internal imports
8
- at module level, keeping this layer thin and circular-import-safe).
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(resp.read().decode("utf-8"))
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")