specify-cli 0.12.11__tar.gz → 0.12.12__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 (462) hide show
  1. {specify_cli-0.12.11 → specify_cli-0.12.12}/CHANGELOG.md +15 -0
  2. {specify_cli-0.12.11 → specify_cli-0.12.12}/PKG-INFO +1 -1
  3. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/community/extensions.md +1 -0
  4. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/bundles.md +5 -4
  5. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/integrations.md +2 -1
  6. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/catalog.community.json +35 -1
  7. {specify_cli-0.12.11 → specify_cli-0.12.12}/pyproject.toml +1 -1
  8. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/commands/bundle/__init__.py +37 -23
  9. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/extensions/__init__.py +20 -6
  10. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/extensions/_commands.py +8 -1
  11. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/presets/_commands.py +8 -1
  12. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/catalog.py +13 -5
  13. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/engine.py +10 -1
  14. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/contract/test_bundle_cli.py +29 -7
  15. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integration/test_bundler_local_install.py +59 -0
  16. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_extensions.py +89 -0
  17. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_presets.py +34 -0
  18. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_workflows.py +97 -0
  19. {specify_cli-0.12.11 → specify_cli-0.12.12}/.devcontainer/devcontainer.json +0 -0
  20. {specify_cli-0.12.11 → specify_cli-0.12.12}/.devcontainer/post-create.sh +0 -0
  21. {specify_cli-0.12.11 → specify_cli-0.12.12}/.editorconfig +0 -0
  22. {specify_cli-0.12.11 → specify_cli-0.12.12}/.gitattributes +0 -0
  23. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/CODEOWNERS +0 -0
  24. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/agent_request.yml +0 -0
  25. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  26. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/bundle_submission.yml +0 -0
  27. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  28. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/extension_submission.yml +0 -0
  29. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  30. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/ISSUE_TEMPLATE/preset_submission.yml +0 -0
  31. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  32. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/aw/actions-lock.json +0 -0
  33. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/dependabot.yml +0 -0
  34. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/skills/add-community-extension/SKILL.md +0 -0
  35. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/RELEASE-PROCESS.md +0 -0
  36. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/add-community-extension.lock.yml +0 -0
  37. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/add-community-extension.md +0 -0
  38. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/add-community-preset.lock.yml +0 -0
  39. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/add-community-preset.md +0 -0
  40. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/bug-assess.lock.yml +0 -0
  41. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/bug-assess.md +0 -0
  42. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/bug-fix.lock.yml +0 -0
  43. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/bug-fix.md +0 -0
  44. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/bug-test.lock.yml +0 -0
  45. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/bug-test.md +0 -0
  46. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/catalog-assign.yml +0 -0
  47. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/codeql.yml +0 -0
  48. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/docs.yml +0 -0
  49. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/lint.yml +0 -0
  50. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/publish-pypi.yml +0 -0
  51. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/release-trigger.yml +0 -0
  52. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/release.yml +0 -0
  53. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/stale.yml +0 -0
  54. {specify_cli-0.12.11 → specify_cli-0.12.12}/.github/workflows/test.yml +0 -0
  55. {specify_cli-0.12.11 → specify_cli-0.12.12}/.gitignore +0 -0
  56. {specify_cli-0.12.11 → specify_cli-0.12.12}/.markdownlint-cli2.jsonc +0 -0
  57. {specify_cli-0.12.11 → specify_cli-0.12.12}/.pre-commit-config.yaml +0 -0
  58. {specify_cli-0.12.11 → specify_cli-0.12.12}/.zenodo.json +0 -0
  59. {specify_cli-0.12.11 → specify_cli-0.12.12}/AGENTS.md +0 -0
  60. {specify_cli-0.12.11 → specify_cli-0.12.12}/CITATION.cff +0 -0
  61. {specify_cli-0.12.11 → specify_cli-0.12.12}/CODE_OF_CONDUCT.md +0 -0
  62. {specify_cli-0.12.11 → specify_cli-0.12.12}/CONTRIBUTING.md +0 -0
  63. {specify_cli-0.12.11 → specify_cli-0.12.12}/DEVELOPMENT.md +0 -0
  64. {specify_cli-0.12.11 → specify_cli-0.12.12}/LICENSE +0 -0
  65. {specify_cli-0.12.11 → specify_cli-0.12.12}/README.md +0 -0
  66. {specify_cli-0.12.11 → specify_cli-0.12.12}/SECURITY.md +0 -0
  67. {specify_cli-0.12.11 → specify_cli-0.12.12}/SUPPORT.md +0 -0
  68. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/.gitignore +0 -0
  69. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/README.md +0 -0
  70. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/community/bundles.md +0 -0
  71. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/community/friends.md +0 -0
  72. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/community/overview.md +0 -0
  73. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/community/presets.md +0 -0
  74. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/community/walkthroughs.md +0 -0
  75. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/concepts/complex-features.md +0 -0
  76. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/concepts/sdd.md +0 -0
  77. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/concepts/spec-persistence.md +0 -0
  78. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/docfx.json +0 -0
  79. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/guides/evolving-specs.md +0 -0
  80. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/guides/monorepo.md +0 -0
  81. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/index.md +0 -0
  82. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/install/air-gapped.md +0 -0
  83. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/install/one-time.md +0 -0
  84. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/install/pipx.md +0 -0
  85. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/install/uv.md +0 -0
  86. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/installation.md +0 -0
  87. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/local-development.md +0 -0
  88. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/quickstart.md +0 -0
  89. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/authentication.md +0 -0
  90. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/core.md +0 -0
  91. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/extensions.md +0 -0
  92. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/overview.md +0 -0
  93. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/presets.md +0 -0
  94. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/reference/workflows.md +0 -0
  95. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/template/public/main.css +0 -0
  96. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/toc.yml +0 -0
  97. {specify_cli-0.12.11 → specify_cli-0.12.12}/docs/upgrade.md +0 -0
  98. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/business-analyst/README.md +0 -0
  99. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/business-analyst/bundle.yml +0 -0
  100. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/developer/README.md +0 -0
  101. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/developer/bundle.yml +0 -0
  102. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/product-manager/README.md +0 -0
  103. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/product-manager/bundle.yml +0 -0
  104. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/security-researcher/README.md +0 -0
  105. {specify_cli-0.12.11 → specify_cli-0.12.12}/examples/bundles/security-researcher/bundle.yml +0 -0
  106. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/EXTENSION-API-REFERENCE.md +0 -0
  107. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/EXTENSION-DEVELOPMENT-GUIDE.md +0 -0
  108. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/EXTENSION-PUBLISHING-GUIDE.md +0 -0
  109. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/EXTENSION-USER-GUIDE.md +0 -0
  110. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/README.md +0 -0
  111. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/RFC-EXTENSION-SYSTEM.md +0 -0
  112. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/README.md +0 -0
  113. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/agent-context-config.yml +0 -0
  114. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/agent-context-defaults.json +0 -0
  115. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/commands/speckit.agent-context.update.md +0 -0
  116. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/extension.yml +0 -0
  117. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -0
  118. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -0
  119. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/agent-context/scripts/python/update_agent_context.py +0 -0
  120. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/bug/README.md +0 -0
  121. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/bug/commands/speckit.bug.assess.md +0 -0
  122. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/bug/commands/speckit.bug.fix.md +0 -0
  123. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/bug/commands/speckit.bug.test.md +0 -0
  124. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/bug/extension.yml +0 -0
  125. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/catalog.json +0 -0
  126. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/README.md +0 -0
  127. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/commands/speckit.git.commit.md +0 -0
  128. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/commands/speckit.git.feature.md +0 -0
  129. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/commands/speckit.git.initialize.md +0 -0
  130. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/commands/speckit.git.remote.md +0 -0
  131. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/commands/speckit.git.validate.md +0 -0
  132. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/config-template.yml +0 -0
  133. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/extension.yml +0 -0
  134. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/git-config.yml +0 -0
  135. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/bash/auto-commit.sh +0 -0
  136. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/bash/create-new-feature-branch.sh +0 -0
  137. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/bash/git-common.sh +0 -0
  138. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  139. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  140. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +0 -0
  141. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  142. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  143. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/selftest/commands/selftest.md +0 -0
  144. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/selftest/extension.yml +0 -0
  145. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/.gitignore +0 -0
  146. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/CHANGELOG.md +0 -0
  147. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/EXAMPLE-README.md +0 -0
  148. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/LICENSE +0 -0
  149. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/README.md +0 -0
  150. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/commands/example.md +0 -0
  151. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/config-template.yml +0 -0
  152. {specify_cli-0.12.11 → specify_cli-0.12.12}/extensions/template/extension.yml +0 -0
  153. {specify_cli-0.12.11 → specify_cli-0.12.12}/integrations/CONTRIBUTING.md +0 -0
  154. {specify_cli-0.12.11 → specify_cli-0.12.12}/integrations/README.md +0 -0
  155. {specify_cli-0.12.11 → specify_cli-0.12.12}/integrations/catalog.community.json +0 -0
  156. {specify_cli-0.12.11 → specify_cli-0.12.12}/integrations/catalog.json +0 -0
  157. {specify_cli-0.12.11 → specify_cli-0.12.12}/media/bootstrap-claude-code.gif +0 -0
  158. {specify_cli-0.12.11 → specify_cli-0.12.12}/media/logo_large.webp +0 -0
  159. {specify_cli-0.12.11 → specify_cli-0.12.12}/media/logo_small.webp +0 -0
  160. {specify_cli-0.12.11 → specify_cli-0.12.12}/media/spec-kit-video-header.jpg +0 -0
  161. {specify_cli-0.12.11 → specify_cli-0.12.12}/media/specify_cli.gif +0 -0
  162. {specify_cli-0.12.11 → specify_cli-0.12.12}/newsletters/2026-April.md +0 -0
  163. {specify_cli-0.12.11 → specify_cli-0.12.12}/newsletters/2026-February.md +0 -0
  164. {specify_cli-0.12.11 → specify_cli-0.12.12}/newsletters/2026-June.md +0 -0
  165. {specify_cli-0.12.11 → specify_cli-0.12.12}/newsletters/2026-March.md +0 -0
  166. {specify_cli-0.12.11 → specify_cli-0.12.12}/newsletters/2026-May.md +0 -0
  167. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/ARCHITECTURE.md +0 -0
  168. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/PUBLISHING.md +0 -0
  169. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/README.md +0 -0
  170. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/catalog.community.json +0 -0
  171. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/catalog.json +0 -0
  172. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/README.md +0 -0
  173. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/commands/speckit.constitution.md +0 -0
  174. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/commands/speckit.implement.md +0 -0
  175. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/commands/speckit.plan.md +0 -0
  176. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/commands/speckit.specify.md +0 -0
  177. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/commands/speckit.tasks.md +0 -0
  178. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/lean/preset.yml +0 -0
  179. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/scaffold/README.md +0 -0
  180. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/scaffold/commands/speckit.myext.myextcmd.md +0 -0
  181. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/scaffold/commands/speckit.specify.md +0 -0
  182. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/scaffold/preset.yml +0 -0
  183. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/scaffold/templates/myext-template.md +0 -0
  184. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/scaffold/templates/spec-template.md +0 -0
  185. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/commands/speckit.specify.md +0 -0
  186. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/commands/speckit.wrap-test.md +0 -0
  187. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/preset.yml +0 -0
  188. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/templates/agent-file-template.md +0 -0
  189. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/templates/checklist-template.md +0 -0
  190. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/templates/constitution-template.md +0 -0
  191. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/templates/plan-template.md +0 -0
  192. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/templates/spec-template.md +0 -0
  193. {specify_cli-0.12.11 → specify_cli-0.12.12}/presets/self-test/templates/tasks-template.md +0 -0
  194. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/bash/check-prerequisites.sh +0 -0
  195. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/bash/common.sh +0 -0
  196. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/bash/create-new-feature.sh +0 -0
  197. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/bash/setup-plan.sh +0 -0
  198. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/bash/setup-tasks.sh +0 -0
  199. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/powershell/check-prerequisites.ps1 +0 -0
  200. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/powershell/common.ps1 +0 -0
  201. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/powershell/create-new-feature.ps1 +0 -0
  202. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/powershell/setup-plan.ps1 +0 -0
  203. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/powershell/setup-tasks.ps1 +0 -0
  204. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/python/check_prerequisites.py +0 -0
  205. {specify_cli-0.12.11 → specify_cli-0.12.12}/scripts/python/common.py +0 -0
  206. {specify_cli-0.12.11 → specify_cli-0.12.12}/spec-driven.md +0 -0
  207. {specify_cli-0.12.11 → specify_cli-0.12.12}/spec-kit.code-workspace +0 -0
  208. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/__init__.py +0 -0
  209. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_agent_config.py +0 -0
  210. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_assets.py +0 -0
  211. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_console.py +0 -0
  212. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_github_http.py +0 -0
  213. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_init_options.py +0 -0
  214. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_invocation_style.py +0 -0
  215. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_project.py +0 -0
  216. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_toml_string.py +0 -0
  217. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_utils.py +0 -0
  218. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/_version.py +0 -0
  219. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/agents.py +0 -0
  220. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/authentication/__init__.py +0 -0
  221. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/authentication/azure_devops.py +0 -0
  222. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/authentication/base.py +0 -0
  223. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/authentication/config.py +0 -0
  224. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/authentication/github.py +0 -0
  225. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/authentication/http.py +0 -0
  226. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/__init__.py +0 -0
  227. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/commands_impl/__init__.py +0 -0
  228. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/commands_impl/catalog_config.py +0 -0
  229. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/lib/__init__.py +0 -0
  230. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/lib/project.py +0 -0
  231. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/lib/versioning.py +0 -0
  232. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/lib/yamlio.py +0 -0
  233. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/models/__init__.py +0 -0
  234. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/models/catalog.py +0 -0
  235. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/models/manifest.py +0 -0
  236. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/models/records.py +0 -0
  237. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/__init__.py +0 -0
  238. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/adapters.py +0 -0
  239. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/catalog_stack.py +0 -0
  240. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/conflict.py +0 -0
  241. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/installer.py +0 -0
  242. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/packager.py +0 -0
  243. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/primitives.py +0 -0
  244. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/references.py +0 -0
  245. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/resolver.py +0 -0
  246. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/bundler/services/validator.py +0 -0
  247. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/catalogs.py +0 -0
  248. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/commands/__init__.py +0 -0
  249. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/commands/init.py +0 -0
  250. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integration_runtime.py +0 -0
  251. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integration_scaffold.py +0 -0
  252. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integration_state.py +0 -0
  253. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integration_status.py +0 -0
  254. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/__init__.py +0 -0
  255. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/_commands.py +0 -0
  256. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/_helpers.py +0 -0
  257. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/_install_commands.py +0 -0
  258. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/_migrate_commands.py +0 -0
  259. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/_query_commands.py +0 -0
  260. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/_scaffold_commands.py +0 -0
  261. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/agy/__init__.py +0 -0
  262. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/amp/__init__.py +0 -0
  263. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/auggie/__init__.py +0 -0
  264. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/base.py +0 -0
  265. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/bob/__init__.py +0 -0
  266. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/catalog.py +0 -0
  267. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/claude/__init__.py +0 -0
  268. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/cline/__init__.py +0 -0
  269. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/codebuddy/__init__.py +0 -0
  270. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/codex/__init__.py +0 -0
  271. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/copilot/__init__.py +0 -0
  272. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/cursor_agent/__init__.py +0 -0
  273. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/devin/__init__.py +0 -0
  274. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/firebender/__init__.py +0 -0
  275. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/forge/__init__.py +0 -0
  276. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/gemini/__init__.py +0 -0
  277. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/generic/__init__.py +0 -0
  278. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/goose/__init__.py +0 -0
  279. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/hermes/__init__.py +0 -0
  280. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/junie/__init__.py +0 -0
  281. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/kilocode/__init__.py +0 -0
  282. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/kimi/__init__.py +0 -0
  283. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/kiro_cli/__init__.py +0 -0
  284. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/lingma/__init__.py +0 -0
  285. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/manifest.py +0 -0
  286. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/omp/__init__.py +0 -0
  287. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/opencode/__init__.py +0 -0
  288. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/pi/__init__.py +0 -0
  289. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/qodercli/__init__.py +0 -0
  290. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/qwen/__init__.py +0 -0
  291. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/rovodev/__init__.py +0 -0
  292. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/shai/__init__.py +0 -0
  293. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/tabnine/__init__.py +0 -0
  294. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/trae/__init__.py +0 -0
  295. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/vibe/__init__.py +0 -0
  296. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/zcode/__init__.py +0 -0
  297. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/integrations/zed/__init__.py +0 -0
  298. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/presets/__init__.py +0 -0
  299. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/shared_infra.py +0 -0
  300. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/__init__.py +0 -0
  301. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/_commands.py +0 -0
  302. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/base.py +0 -0
  303. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/expressions.py +0 -0
  304. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/__init__.py +0 -0
  305. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/command/__init__.py +0 -0
  306. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/do_while/__init__.py +0 -0
  307. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/fan_in/__init__.py +0 -0
  308. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/fan_out/__init__.py +0 -0
  309. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/gate/__init__.py +0 -0
  310. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/if_then/__init__.py +0 -0
  311. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/init/__init__.py +0 -0
  312. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/prompt/__init__.py +0 -0
  313. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/shell/__init__.py +0 -0
  314. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/switch/__init__.py +0 -0
  315. {specify_cli-0.12.11 → specify_cli-0.12.12}/src/specify_cli/workflows/steps/while_loop/__init__.py +0 -0
  316. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/checklist-template.md +0 -0
  317. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/analyze.md +0 -0
  318. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/checklist.md +0 -0
  319. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/clarify.md +0 -0
  320. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/constitution.md +0 -0
  321. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/converge.md +0 -0
  322. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/implement.md +0 -0
  323. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/plan.md +0 -0
  324. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/specify.md +0 -0
  325. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/tasks.md +0 -0
  326. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/commands/taskstoissues.md +0 -0
  327. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/constitution-template.md +0 -0
  328. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/plan-template.md +0 -0
  329. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/spec-template.md +0 -0
  330. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/tasks-template.md +0 -0
  331. {specify_cli-0.12.11 → specify_cli-0.12.12}/templates/vscode-settings.json +0 -0
  332. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/__init__.py +0 -0
  333. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/auth_helpers.py +0 -0
  334. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/bundler_helpers.py +0 -0
  335. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/conftest.py +0 -0
  336. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/contract/test_catalog_schema.py +0 -0
  337. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/contract/test_manifest_schema.py +0 -0
  338. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/__init__.py +0 -0
  339. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/bug/__init__.py +0 -0
  340. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/bug/test_bug_extension.py +0 -0
  341. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/git/__init__.py +0 -0
  342. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/git/test_git_extension.py +0 -0
  343. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/test_agent_context_cli_free.py +0 -0
  344. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/test_extension_agent_context.py +0 -0
  345. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/test_update_agent_context_feature_json.py +0 -0
  346. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/extensions/test_update_agent_context_python_parity.py +0 -0
  347. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/hooks/TESTING.md +0 -0
  348. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/hooks/plan.md +0 -0
  349. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/hooks/spec.md +0 -0
  350. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/hooks/tasks.md +0 -0
  351. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/http_helpers.py +0 -0
  352. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integration/test_bundler_catalog_stack.py +0 -0
  353. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integration/test_bundler_init_install.py +0 -0
  354. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integration/test_bundler_install_flow.py +0 -0
  355. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integration/test_bundler_offline.py +0 -0
  356. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integration/test_bundler_security_paths.py +0 -0
  357. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/__init__.py +0 -0
  358. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/conftest.py +0 -0
  359. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_base.py +0 -0
  360. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_cli.py +0 -0
  361. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_extra_args.py +0 -0
  362. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_home_isolation.py +0 -0
  363. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_agy.py +0 -0
  364. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_amp.py +0 -0
  365. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_auggie.py +0 -0
  366. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_base_markdown.py +0 -0
  367. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_base_skills.py +0 -0
  368. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_base_toml.py +0 -0
  369. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_base_yaml.py +0 -0
  370. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_bob.py +0 -0
  371. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_catalog.py +0 -0
  372. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_claude.py +0 -0
  373. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_cline.py +0 -0
  374. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_codebuddy.py +0 -0
  375. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_codex.py +0 -0
  376. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_copilot.py +0 -0
  377. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_cursor_agent.py +0 -0
  378. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_devin.py +0 -0
  379. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_firebender.py +0 -0
  380. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_forge.py +0 -0
  381. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_gemini.py +0 -0
  382. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_generic.py +0 -0
  383. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_goose.py +0 -0
  384. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_hermes.py +0 -0
  385. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_junie.py +0 -0
  386. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_kilocode.py +0 -0
  387. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_kimi.py +0 -0
  388. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_kiro_cli.py +0 -0
  389. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_lingma.py +0 -0
  390. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_omp.py +0 -0
  391. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_opencode.py +0 -0
  392. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_pi.py +0 -0
  393. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_qodercli.py +0 -0
  394. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_qwen.py +0 -0
  395. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_rovodev.py +0 -0
  396. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_scaffold.py +0 -0
  397. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_shai.py +0 -0
  398. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_state.py +0 -0
  399. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_subcommand.py +0 -0
  400. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_tabnine.py +0 -0
  401. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_trae.py +0 -0
  402. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_vibe.py +0 -0
  403. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_zcode.py +0 -0
  404. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_integration_zed.py +0 -0
  405. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_manifest.py +0 -0
  406. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_registry.py +0 -0
  407. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/integrations/test_skill_frontmatter_quoting.py +0 -0
  408. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/self_upgrade_helpers.py +0 -0
  409. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_agent_config_consistency.py +0 -0
  410. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_authentication.py +0 -0
  411. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_branch_numbering.py +0 -0
  412. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_check_prerequisites_paths_only.py +0 -0
  413. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_check_prerequisites_python_parity.py +0 -0
  414. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_check_tool.py +0 -0
  415. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_cli_version.py +0 -0
  416. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_command_template_py_scripts.py +0 -0
  417. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_commands_package.py +0 -0
  418. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_console_imports.py +0 -0
  419. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_extension_registration.py +0 -0
  420. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_extension_skills.py +0 -0
  421. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_extension_update_hardening.py +0 -0
  422. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_github_http.py +0 -0
  423. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_github_workflows.py +0 -0
  424. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_init_dir.py +0 -0
  425. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_init_dir_cli.py +0 -0
  426. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_live_transient_windows.py +0 -0
  427. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_merge.py +0 -0
  428. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_post_process.py +0 -0
  429. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_ps1_encoding.py +0 -0
  430. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_registrar_path_traversal.py +0 -0
  431. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_self_upgrade_detection.py +0 -0
  432. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_self_upgrade_execution.py +0 -0
  433. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_self_upgrade_guidance.py +0 -0
  434. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_self_upgrade_verification.py +0 -0
  435. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_setup_plan_feature_json.py +0 -0
  436. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_setup_plan_no_overwrite.py +0 -0
  437. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_setup_tasks.py +0 -0
  438. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_shared_infra_integrity.py +0 -0
  439. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_timestamp_branches.py +0 -0
  440. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_upgrade.py +0 -0
  441. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_utils.py +0 -0
  442. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_utils_assets_imports.py +0 -0
  443. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_version_imports.py +0 -0
  444. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/test_workflow_run_without_project.py +0 -0
  445. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_adapters.py +0 -0
  446. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_catalog_config.py +0 -0
  447. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_conflict.py +0 -0
  448. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_packager.py +0 -0
  449. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_primitives.py +0 -0
  450. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_records.py +0 -0
  451. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_references.py +0 -0
  452. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_resolver.py +0 -0
  453. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_validator.py +0 -0
  454. {specify_cli-0.12.11 → specify_cli-0.12.12}/tests/unit/test_bundler_versioning.py +0 -0
  455. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/ARCHITECTURE.md +0 -0
  456. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/PUBLISHING.md +0 -0
  457. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/README.md +0 -0
  458. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/catalog.community.json +0 -0
  459. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/catalog.json +0 -0
  460. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/speckit/workflow.yml +0 -0
  461. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/step-catalog.community.json +0 -0
  462. {specify_cli-0.12.11 → specify_cli-0.12.12}/workflows/step-catalog.json +0 -0
@@ -2,6 +2,21 @@
2
2
 
3
3
  <!-- insert new changelog below this comment -->
4
4
 
5
+ ## [0.12.12] - 2026-07-13
6
+
7
+ ### Changed
8
+
9
+ - fix(extensions): set-priority repairs corrupted boolean priority (#3268)
10
+ - fix(presets): set-priority repairs corrupted boolean priority (#3269)
11
+ - fix(workflows): engine loop cap ignores bool max_iterations (#3270)
12
+ - docs(bundles): document --integration on 'bundle update' (#3271)
13
+ - fix(workflows): harden catalog.py against mis-shaped registry & non-string fields (#3375)
14
+ - Add Verify Review Ship extension to community catalog (#3450)
15
+ - fix(bundle): reject file:// / local download_url — catalog URLs are HTTPS-only (#3344)
16
+ - fix(extensions): handle prefix-colliding env vars in _get_env_config (#3350)
17
+ - docs: document copilot skills mode (--skills) and markdown deprecation (#3313)
18
+ - chore: release 0.12.11, begin 0.12.12.dev0 development (#3460)
19
+
5
20
  ## [0.12.11] - 2026-07-10
6
21
 
7
22
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specify-cli
3
- Version: 0.12.11
3
+ Version: 0.12.12
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
@@ -148,6 +148,7 @@ The following community-contributed extensions are available in [`catalog.commun
148
148
  | Token Economy | Token routing, measured savings, and context audit workflows | `process` | Read+Write | [spec-kit-token-economy](https://github.com/formin/spec-kit-token-economy) |
149
149
  | V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | `docs` | Read+Write | [spec-kit-v-model](https://github.com/leocamello/spec-kit-v-model) |
150
150
  | Verify Extension | Post-implementation quality gate that validates implemented code against specification artifacts | `code` | Read-only | [spec-kit-verify](https://github.com/ismaelJimenez/spec-kit-verify) |
151
+ | Verify Review Ship | Adds post-implementation verify, review, and ship readiness gates to Spec Kit workflows | `process` | Read-only | [spec-kit-verify-review-ship](https://github.com/cadugevaerd/spec-kit-verify-review-ship) |
151
152
  | Verify Tasks Extension | Detect phantom completions: tasks marked [X] in tasks.md with no real implementation | `code` | Read-only | [spec-kit-verify-tasks](https://github.com/datastone-inc/spec-kit-verify-tasks) |
152
153
  | Version Guard | Verify tech stack versions against live npm registries before planning and implementation | `process` | Read-only | [spec-kit-version-guard](https://github.com/KevinBrown5280/spec-kit-version-guard) |
153
154
  | What-if Analysis | Preview the downstream impact (complexity, effort, tasks, risks) of requirement changes before committing to them | `visibility` | Read-only | [spec-kit-whatif](https://github.com/DevAbdullah90/spec-kit-whatif) |
@@ -51,10 +51,11 @@ If the current directory is not yet a Spec Kit project, `install` initializes on
51
51
  specify bundle update [<bundle_id>]
52
52
  ```
53
53
 
54
- | Option | Description |
55
- | ------------ | ------------------------------------ |
56
- | `--all` | Update every installed bundle |
57
- | `--offline` | Do not access the network |
54
+ | Option | Description |
55
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
56
+ | `--all` | Update every installed bundle |
57
+ | `--integration` | Override the integration used when refreshing components; applied only when the project's active integration can't be determined |
58
+ | `--offline` | Do not access the network |
58
59
 
59
60
  Re-resolves a bundle and **refreshes** its components through each primitive's update path, bringing already-installed components up to the bundle's newly pinned versions while preserving primitive-level overrides (such as preset priority). Provide a bundle id, or use `--all` to update everything installed.
60
61
 
@@ -18,7 +18,7 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
18
18
  | [Firebender](https://firebender.com/) | `firebender` | IDE-based agent for Android Studio / IntelliJ |
19
19
  | [Forge](https://forgecode.dev/) | `forge` | |
20
20
  | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | |
21
- | [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | |
21
+ | [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | Defaults to legacy markdown mode: `.agent.md` command files under `.github/agents/`, companion `.prompt.md` files under `.github/prompts/`, and a `.vscode/settings.json` merge. Pass `--integration-options="--skills"` to scaffold skills as `speckit-<command>/SKILL.md` under `.github/skills/` instead. Legacy markdown mode is deprecated and will stop being the default in a future release. |
22
22
  | [Goose](https://goose-docs.ai/) | `goose` | Uses YAML recipe format in `.goose/recipes/` |
23
23
  | [Hermes](https://github.com/NousResearch/hermes-agent) | `hermes` | Skills-based integration; installs skills globally into `~/.hermes/skills/` |
24
24
  | [IBM Bob](https://www.ibm.com/products/bob) | `bob` | IDE-based agent |
@@ -219,6 +219,7 @@ Some integrations accept additional options via `--integration-options`:
219
219
  | ----------- | ------------------- | -------------------------------------------------------------- |
220
220
  | `generic` | `--commands-dir` | Required. Directory for command files |
221
221
  | `kimi` | `--migrate-legacy` | Migrate legacy `.kimi/skills/` installs to `.kimi-code/skills/` (including dotted→hyphenated skill naming, e.g. `speckit.xxx` → `speckit-xxx`) |
222
+ | `copilot` | `--skills` | Scaffold commands as agent skills (`speckit-<command>/SKILL.md` under `.github/skills/`, invoked as `/speckit-<command>`) instead of the default legacy markdown mode (`.github/agents/*.agent.md` plus `.github/prompts/*.prompt.md` and a `.vscode/settings.json` merge). Without this flag, install warns that legacy markdown mode is deprecated. |
222
223
 
223
224
  Example:
224
225
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": "1.0",
3
- "updated_at": "2026-07-08T00:00:00Z",
3
+ "updated_at": "2026-07-10T00:00:00Z",
4
4
  "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
5
5
  "extensions": {
6
6
  "aide": {
@@ -4356,6 +4356,40 @@
4356
4356
  "created_at": "2026-03-03T00:00:00Z",
4357
4357
  "updated_at": "2026-04-09T00:00:00Z"
4358
4358
  },
4359
+ "verify-review-ship": {
4360
+ "name": "Verify Review Ship",
4361
+ "id": "verify-review-ship",
4362
+ "description": "Adds post-implementation verify, review, and ship readiness gates to Spec Kit workflows.",
4363
+ "author": "Carlos Eduardo Gevaerd Araujo",
4364
+ "version": "0.1.0",
4365
+ "download_url": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/archive/refs/tags/v0.1.0.zip",
4366
+ "repository": "https://github.com/cadugevaerd/spec-kit-verify-review-ship",
4367
+ "homepage": "https://github.com/cadugevaerd/spec-kit-verify-review-ship",
4368
+ "documentation": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/blob/main/README.md",
4369
+ "changelog": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/blob/main/CHANGELOG.md",
4370
+ "license": "MIT",
4371
+ "category": "process",
4372
+ "effect": "read-only",
4373
+ "requires": {
4374
+ "speckit_version": ">=0.1.0"
4375
+ },
4376
+ "provides": {
4377
+ "commands": 3,
4378
+ "hooks": 1
4379
+ },
4380
+ "tags": [
4381
+ "quality",
4382
+ "review",
4383
+ "shipping",
4384
+ "workflow",
4385
+ "testing"
4386
+ ],
4387
+ "verified": false,
4388
+ "downloads": 0,
4389
+ "stars": 0,
4390
+ "created_at": "2026-07-10T00:00:00Z",
4391
+ "updated_at": "2026-07-10T00:00:00Z"
4392
+ },
4359
4393
  "verify-tasks": {
4360
4394
  "name": "Verify Tasks Extension",
4361
4395
  "id": "verify-tasks",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "specify-cli"
3
- version = "0.12.11"
3
+ version = "0.12.12"
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"
@@ -746,11 +746,16 @@ def _resolve_manifest_path(path: Path | None) -> Path:
746
746
  def _download_manifest(resolved, *, offline: bool):
747
747
  """Resolve a bundle's manifest from its catalog ``download_url``.
748
748
 
749
- Local/``file://`` URLs always work offline and may point at a ``.zip``
750
- artifact, a bundle directory, or a ``bundle.yml`` (handled by
751
- :func:`_local_manifest_source`). Remote ``https://`` URLs are fetched with
752
- the shared authenticated, redirect-validated HTTP client, and only when not
753
- ``--offline``.
749
+ Catalog ``download_url``s are HTTPS-only (``http`` allowed for localhost),
750
+ matching the extensions/presets/workflows catalog systems. Remote URLs are
751
+ fetched with the shared authenticated, redirect-validated HTTP client, and
752
+ only when not ``--offline``.
753
+
754
+ Local and ``file://`` sources are intentionally not resolved here: to
755
+ install a bundle from disk, pass the path positionally
756
+ (``specify bundle install ./path/to/bundle.yml`` — a bundle directory or a
757
+ ``.zip`` artifact also works), which :func:`_local_manifest_source` handles
758
+ before catalog resolution and which never touches ``download_url``.
754
759
  """
755
760
  from urllib.parse import urlparse
756
761
 
@@ -763,26 +768,35 @@ def _download_manifest(resolved, *, offline: bool):
763
768
  parsed = urlparse(url)
764
769
  scheme = parsed.scheme.lower()
765
770
 
766
- # On Windows an absolute path like ``C:\bundle.yml`` parses with a
767
- # single-letter ``scheme``; treat it as a local file, not a URL scheme.
771
+ # ``file://`` URLs and bare filesystem paths (including Windows drive paths
772
+ # like ``C:\bundle.yml``, which urlparse reads as a single-letter scheme)
773
+ # are not valid catalog download URLs. Catalog URLs are HTTPS-only across
774
+ # every catalog system; installing from disk is done by passing the path
775
+ # positionally, which never reaches URL resolution. Give an actionable
776
+ # error rather than accepting a scheme the rest of the codebase rejects.
768
777
  if scheme in ("", "file") or re.match(r"^[A-Za-z]:[\\/]", url):
769
- local = Path(parsed.path if scheme == "file" else url)
770
- manifest = _local_manifest_source(str(local))
771
- if manifest is None:
772
- raise BundlerError(f"Bundle manifest not found: {local}")
773
- return manifest
774
-
775
- if scheme in ("http", "https"):
776
- if offline:
777
- raise BundlerError(
778
- f"Network access disabled; cannot download bundle '{resolved.entry.id}' "
779
- f"from {url}."
780
- )
781
- return _download_remote_manifest(resolved.entry.id, url)
778
+ raise BundlerError(
779
+ f"Catalog entry '{resolved.entry.id}' has a non-HTTP(S) download_url "
780
+ f"({url}); catalog download URLs must be HTTPS (http for localhost) — "
781
+ "a file:// URL, a local filesystem path, or a scheme-less value "
782
+ "(e.g. 'example.com/bundle.zip') is not accepted. "
783
+ "To install a bundle from disk, pass the path directly: "
784
+ "'specify bundle install <path-to-bundle.yml | bundle-dir | .zip>'."
785
+ )
782
786
 
783
- raise BundlerError(
784
- f"Unsupported download_url scheme for bundle '{resolved.entry.id}': {url}"
785
- )
787
+ # Validate the scheme/host *before* the offline gate so an invalid or
788
+ # non-HTTPS download_url reports the real problem in every mode, rather
789
+ # than a misleading "Network access disabled" under --offline.
790
+ # (_download_remote_manifest re-checks this, but only once network access
791
+ # is permitted.) HTTPS-only, http allowed for localhost.
792
+ _require_https(f"bundle '{resolved.entry.id}'", url)
793
+
794
+ if offline:
795
+ raise BundlerError(
796
+ f"Network access disabled; cannot download bundle '{resolved.entry.id}' "
797
+ f"from {url}."
798
+ )
799
+ return _download_remote_manifest(resolved.entry.id, url)
786
800
 
787
801
 
788
802
  def _require_https(label: str, url: str) -> None:
@@ -2755,18 +2755,32 @@ class ConfigManager:
2755
2755
  if not key.startswith(prefix):
2756
2756
  continue
2757
2757
 
2758
- # Remove prefix and split into parts
2759
- config_path = key[len(prefix) :].lower().split("_")
2758
+ # Remove prefix and split into parts. Drop empty components from a
2759
+ # malformed name (e.g. ``SPECKIT_<EXT>_`` with no key, or
2760
+ # consecutive underscores ``SPECKIT_X__Y``) so we never create an
2761
+ # entry under an empty key.
2762
+ config_path = [p for p in key[len(prefix) :].lower().split("_") if p]
2763
+ if not config_path:
2764
+ continue
2760
2765
 
2761
- # Build nested dict
2766
+ # Build nested dict. Two env vars can collide on a prefix, e.g.
2767
+ # SPECKIT_X_CONNECTION=a and SPECKIT_X_CONNECTION_URL=b. Guard the
2768
+ # walk so a colliding scalar is replaced by a dict (deeper/more
2769
+ # specific vars win) instead of being indexed into — which raised
2770
+ # TypeError ('str' object does not support item assignment) — and
2771
+ # guard the leaf so a scalar processed after the nested var does
2772
+ # not clobber the nested dict. Order-independent: both insertion
2773
+ # orders yield {'connection': {'url': ...}}. Nested-wins mirrors
2774
+ # _merge_configs' dict-preserving semantics.
2762
2775
  current = env_config
2763
2776
  for part in config_path[:-1]:
2764
- if part not in current:
2777
+ if not isinstance(current.get(part), dict):
2765
2778
  current[part] = {}
2766
2779
  current = current[part]
2767
2780
 
2768
- # Set the final value
2769
- current[config_path[-1]] = value
2781
+ # Set the final value, unless a nested dict already occupies it.
2782
+ if not isinstance(current.get(config_path[-1]), dict):
2783
+ current[config_path[-1]] = value
2770
2784
 
2771
2785
  return env_config
2772
2786
 
@@ -1566,7 +1566,14 @@ def extension_set_priority(
1566
1566
  raw_priority = metadata.get("priority")
1567
1567
  # Only skip if the stored value is already a valid int equal to requested priority
1568
1568
  # This ensures corrupted values (e.g., "high") get repaired even when setting to default (10)
1569
- if isinstance(raw_priority, int) and raw_priority == priority:
1569
+ # A bool is an int in Python (isinstance(True, int) is True), so exclude it explicitly —
1570
+ # mirroring normalize_priority's bool guard — otherwise a corrupted True/False priority
1571
+ # equals 1/0 here and is never repaired.
1572
+ if (
1573
+ isinstance(raw_priority, int)
1574
+ and not isinstance(raw_priority, bool)
1575
+ and raw_priority == priority
1576
+ ):
1570
1577
  console.print(f"[yellow]Extension '{_escape_markup(str(display_name))}' already has priority {priority}[/yellow]")
1571
1578
  raise typer.Exit(0)
1572
1579
 
@@ -469,7 +469,14 @@ def preset_set_priority(
469
469
  raw_priority = metadata.get("priority")
470
470
  # Only skip if the stored value is already a valid int equal to requested priority
471
471
  # This ensures corrupted values (e.g., "high") get repaired even when setting to default (10)
472
- if isinstance(raw_priority, int) and raw_priority == priority:
472
+ # A bool is an int in Python (isinstance(True, int) is True), so exclude it explicitly —
473
+ # mirroring normalize_priority's bool guard — otherwise a corrupted True/False priority
474
+ # equals 1/0 here and is never repaired.
475
+ if (
476
+ isinstance(raw_priority, int)
477
+ and not isinstance(raw_priority, bool)
478
+ and raw_priority == priority
479
+ ):
473
480
  console.print(f"[yellow]Preset '{preset_id}' already has priority {priority}[/yellow]")
474
481
  raise typer.Exit(0)
475
482
 
@@ -76,8 +76,16 @@ class WorkflowRegistry:
76
76
  if self.registry_path.exists():
77
77
  try:
78
78
  with open(self.registry_path, encoding="utf-8") as f:
79
- return json.load(f)
80
- except (json.JSONDecodeError, ValueError):
79
+ data = json.load(f)
80
+ # Validate shape: must be a dict with a dict "workflows" field,
81
+ # otherwise every method that indexes data["workflows"] crashes.
82
+ # Mirrors StepRegistry._load.
83
+ if not isinstance(data, dict):
84
+ return {"schema_version": self.SCHEMA_VERSION, "workflows": {}}
85
+ if not isinstance(data.get("workflows"), dict):
86
+ data["workflows"] = {}
87
+ return data
88
+ except (json.JSONDecodeError, ValueError, OSError, UnicodeError):
81
89
  # Corrupted registry file — reset to default
82
90
  return {"schema_version": self.SCHEMA_VERSION, "workflows": {}}
83
91
  return {"schema_version": self.SCHEMA_VERSION, "workflows": {}}
@@ -438,9 +446,9 @@ class WorkflowCatalog:
438
446
  q = query.lower()
439
447
  searchable = " ".join(
440
448
  [
441
- wf_data.get("name", ""),
442
- wf_data.get("description", ""),
443
- wf_data.get("id", ""),
449
+ str(wf_data.get("name") or ""),
450
+ str(wf_data.get("description") or ""),
451
+ str(wf_data.get("id") or ""),
444
452
  ]
445
453
  ).lower()
446
454
  if q not in searchable:
@@ -982,7 +982,16 @@ class WorkflowEngine:
982
982
  from .expressions import evaluate_condition
983
983
 
984
984
  max_iters = step_config.get("max_iterations")
985
- if not isinstance(max_iters, int) or max_iters < 1:
985
+ # A bool is an int in Python (isinstance(True, int) is True
986
+ # and True == 1), so a bool max_iterations would slip past
987
+ # the int check and cap the loop at range(0)==1 iteration
988
+ # instead of the default. Exclude bools, mirroring the
989
+ # while/do-while validators and the continue_on_error guard.
990
+ if (
991
+ isinstance(max_iters, bool)
992
+ or not isinstance(max_iters, int)
993
+ or max_iters < 1
994
+ ):
986
995
  max_iters = 10
987
996
  condition = step_config.get("condition", False)
988
997
  for _loop_iter in range(max_iters - 1):
@@ -175,7 +175,23 @@ def test_build_produces_artifact(project: Path):
175
175
  assert len(artifacts) == 1
176
176
 
177
177
 
178
- def test_info_expands_full_component_set(project: Path):
178
+ def _mock_manifest_download(monkeypatch, source_path: Path) -> None:
179
+ """Mock the HTTPS manifest fetch to return a locally-authored manifest.
180
+
181
+ Catalog ``download_url``s are HTTPS-only, so ``info`` tests can no longer
182
+ point one at a local file. Patch ``_download_manifest`` to return the
183
+ manifest parsed from *source_path* (a bundle.yml or a .zip artifact),
184
+ exercising ``info``'s expansion without a network call.
185
+ """
186
+ from specify_cli.commands.bundle import _local_manifest_source
187
+
188
+ monkeypatch.setattr(
189
+ "specify_cli.commands.bundle._download_manifest",
190
+ lambda resolved, *, offline: _local_manifest_source(str(source_path)),
191
+ )
192
+
193
+
194
+ def test_info_expands_full_component_set(project: Path, monkeypatch):
179
195
  bundle_dir = project / "src-bundle"
180
196
  bundle_dir.mkdir()
181
197
  (bundle_dir / "bundle.yml").write_text(
@@ -183,13 +199,14 @@ def test_info_expands_full_component_set(project: Path):
183
199
  )
184
200
  catalog = project / "local-catalog.json"
185
201
  entry = catalog_entry_dict(
186
- "demo-bundle", download_url=str(bundle_dir / "bundle.yml")
202
+ "demo-bundle", download_url="https://example.com/demo-bundle.zip"
187
203
  )
188
204
  write_catalog_file(catalog, {"demo-bundle": entry})
189
205
  added = runner.invoke(
190
206
  app, ["bundle", "catalog", "add", str(catalog), "--id", "local"]
191
207
  )
192
208
  assert added.exit_code == 0, added.output
209
+ _mock_manifest_download(monkeypatch, bundle_dir / "bundle.yml")
193
210
 
194
211
  result = runner.invoke(app, ["bundle", "info", "demo-bundle", "--json", "--offline"])
195
212
  assert result.exit_code == 0, result.output
@@ -207,7 +224,7 @@ def test_info_expands_full_component_set(project: Path):
207
224
  assert "Trust" in text.output
208
225
 
209
226
 
210
- def test_info_expands_discovery_only_bundle(project: Path):
227
+ def test_info_expands_discovery_only_bundle(project: Path, monkeypatch):
211
228
  # Discovery-only bundles must still be fully inspectable via `info`;
212
229
  # only `install` is refused for them.
213
230
  bundle_dir = project / "disc-bundle"
@@ -217,7 +234,7 @@ def test_info_expands_discovery_only_bundle(project: Path):
217
234
  )
218
235
  catalog = project / "disc-catalog.json"
219
236
  entry = catalog_entry_dict(
220
- "demo-bundle", download_url=str(bundle_dir / "bundle.yml")
237
+ "demo-bundle", download_url="https://example.com/demo-bundle.zip"
221
238
  )
222
239
  write_catalog_file(catalog, {"demo-bundle": entry})
223
240
  config = {
@@ -230,6 +247,7 @@ def test_info_expands_discovery_only_bundle(project: Path):
230
247
  (project / ".specify" / "bundle-catalogs.yml").write_text(
231
248
  yaml.safe_dump(config), encoding="utf-8"
232
249
  )
250
+ _mock_manifest_download(monkeypatch, bundle_dir / "bundle.yml")
233
251
  result = runner.invoke(app, ["bundle", "info", "demo-bundle", "--json", "--offline"])
234
252
  assert result.exit_code == 0, result.output
235
253
  payload = json.loads(result.output)
@@ -237,8 +255,9 @@ def test_info_expands_discovery_only_bundle(project: Path):
237
255
  assert ("extensions", "ext-a") in components
238
256
 
239
257
 
240
- def test_info_resolves_local_zip_download_url(project: Path):
241
- # A local .zip artifact as download_url is extracted to read bundle.yml.
258
+ def test_info_expands_zip_sourced_bundle(project: Path, monkeypatch):
259
+ # A .zip artifact is extracted to read bundle.yml; info expands it. (The
260
+ # download itself is HTTPS-only now and mocked here — see contract note.)
242
261
  bundle_dir = project / "zip-src"
243
262
  bundle_dir.mkdir()
244
263
  (bundle_dir / "bundle.yml").write_text(
@@ -249,12 +268,15 @@ def test_info_resolves_local_zip_download_url(project: Path):
249
268
  catalog = project / "zip-catalog.json"
250
269
  write_catalog_file(
251
270
  catalog,
252
- {"demo-bundle": catalog_entry_dict("demo-bundle", download_url=str(artifact))},
271
+ {"demo-bundle": catalog_entry_dict(
272
+ "demo-bundle", download_url="https://example.com/demo-bundle.zip"
273
+ )},
253
274
  )
254
275
  added = runner.invoke(
255
276
  app, ["bundle", "catalog", "add", str(catalog), "--id", "local"]
256
277
  )
257
278
  assert added.exit_code == 0, added.output
279
+ _mock_manifest_download(monkeypatch, artifact)
258
280
  result = runner.invoke(app, ["bundle", "info", "demo-bundle", "--json", "--offline"])
259
281
  assert result.exit_code == 0, result.output
260
282
  payload = json.loads(result.output)
@@ -112,3 +112,62 @@ def test_install_bundled_extension_from_zip_offline(tmp_path: Path):
112
112
  assert not ExtensionManager(project).registry.is_installed("agent-context")
113
113
  finally:
114
114
  os.chdir(previous)
115
+
116
+
117
+ def test_download_manifest_rejects_file_url(tmp_path: Path):
118
+ """A catalog ``file://`` download_url is rejected — catalog URLs are
119
+ HTTPS-only, matching extensions/presets/workflows. Disk installs go through
120
+ the positional path (see the local-source tests above), not download_url.
121
+ """
122
+ from types import SimpleNamespace
123
+
124
+ from specify_cli.commands.bundle import _download_manifest
125
+
126
+ manifest_path = write_manifest(tmp_path / "my bundles")
127
+ resolved = SimpleNamespace(
128
+ entry=SimpleNamespace(id="demo-bundle", download_url=manifest_path.as_uri())
129
+ )
130
+
131
+ with pytest.raises(BundlerError, match="bundle install"):
132
+ _download_manifest(resolved, offline=True)
133
+
134
+
135
+ def test_download_manifest_rejects_bare_path(tmp_path: Path):
136
+ """A bare filesystem path download_url is likewise rejected."""
137
+ from types import SimpleNamespace
138
+
139
+ from specify_cli.commands.bundle import _download_manifest
140
+
141
+ manifest_path = write_manifest(tmp_path / "plain")
142
+ resolved = SimpleNamespace(
143
+ entry=SimpleNamespace(id="demo-bundle", download_url=str(manifest_path))
144
+ )
145
+
146
+ with pytest.raises(BundlerError, match="bundle install"):
147
+ _download_manifest(resolved, offline=True)
148
+
149
+
150
+ def test_local_install_still_resolves_via_positional_path(tmp_path: Path):
151
+ """The supported local route — a positional path, not a download_url —
152
+ still resolves the manifest via _local_manifest_source."""
153
+ manifest_path = write_manifest(tmp_path / "my bundles")
154
+ manifest = _local_manifest_source(str(manifest_path))
155
+ assert manifest is not None
156
+ assert manifest.bundle.id == "demo-bundle"
157
+
158
+
159
+ def test_download_manifest_rejects_non_https_url_even_offline(tmp_path: Path):
160
+ """A non-HTTPS download_url must report the HTTPS problem, not a misleading
161
+ 'Network access disabled', even under --offline (scheme is validated before
162
+ the offline gate)."""
163
+ from types import SimpleNamespace
164
+
165
+ from specify_cli.commands.bundle import _download_manifest
166
+
167
+ resolved = SimpleNamespace(
168
+ entry=SimpleNamespace(
169
+ id="demo-bundle", download_url="http://example.com/bundle.zip"
170
+ )
171
+ )
172
+ with pytest.raises(BundlerError, match="HTTPS"):
173
+ _download_manifest(resolved, offline=True)
@@ -6424,6 +6424,42 @@ class TestExtensionPriorityCLI:
6424
6424
  plain = strip_ansi(result.output)
6425
6425
  assert "already has priority 5" in plain
6426
6426
 
6427
+ def test_set_priority_repairs_corrupted_bool(self, extension_dir, project_dir):
6428
+ """A corrupted boolean priority must be repaired, not skipped.
6429
+
6430
+ ``isinstance(True, int)`` is True and ``True == 1`` in Python, so a
6431
+ stored ``True`` priority would short-circuit the ``already has
6432
+ priority 1`` skip path and never get rewritten to a real int —
6433
+ contradicting the comment that promises corrupted values are
6434
+ repaired. The guard must exclude bools (like normalize_priority).
6435
+ """
6436
+ from typer.testing import CliRunner
6437
+ from unittest.mock import patch
6438
+ from specify_cli import app
6439
+
6440
+ runner = CliRunner()
6441
+
6442
+ manager = ExtensionManager(project_dir)
6443
+ manager.install_from_directory(
6444
+ extension_dir, "0.1.0", register_commands=False, priority=5
6445
+ )
6446
+ # Inject a corrupted boolean priority (True == 1).
6447
+ manager.registry.update("test-ext", {"priority": True})
6448
+
6449
+ with patch.object(Path, "cwd", return_value=project_dir):
6450
+ result = runner.invoke(app, ["extension", "set-priority", "test-ext", "1"])
6451
+
6452
+ assert result.exit_code == 0, result.output
6453
+ plain = strip_ansi(result.output)
6454
+ # The corrupted bool must be repaired, not reported as already-set.
6455
+ assert "already has priority" not in plain
6456
+ assert "priority changed" in plain
6457
+
6458
+ # The stored value is now a real int, not a bool.
6459
+ reloaded = ExtensionManager(project_dir).registry.get("test-ext")
6460
+ assert reloaded["priority"] == 1
6461
+ assert not isinstance(reloaded["priority"], bool)
6462
+
6427
6463
  def test_set_priority_invalid_value(self, extension_dir, project_dir):
6428
6464
  """Test set-priority rejects invalid priority values."""
6429
6465
  from typer.testing import CliRunner
@@ -7628,3 +7664,56 @@ class TestConfigManagerNonMappingYaml:
7628
7664
  (ext_dir / "jira-config.yml").write_text("just a string\n", encoding="utf-8")
7629
7665
  executor = HookExecutor(tmp_path)
7630
7666
  assert executor._evaluate_condition("config.x is set", "jira") is False
7667
+
7668
+
7669
+ class TestConfigManagerEnvPrefixCollision:
7670
+ """Prefix-colliding env vars must not crash or clobber nested config."""
7671
+
7672
+ def test_scalar_then_nested_yields_nested(self, tmp_path, monkeypatch):
7673
+ """SPECKIT_X_CONNECTION=x then SPECKIT_X_CONNECTION_URL=y.
7674
+
7675
+ The scalar-first order previously raised TypeError ('str' object
7676
+ does not support item assignment) when the walk indexed into 'x'.
7677
+ """
7678
+ monkeypatch.setenv("SPECKIT_TESTEXT_CONNECTION", "x")
7679
+ monkeypatch.setenv("SPECKIT_TESTEXT_CONNECTION_URL", "y")
7680
+ cm = ConfigManager(tmp_path, "testext")
7681
+ assert cm._get_env_config() == {"connection": {"url": "y"}}
7682
+
7683
+ def test_nested_then_scalar_does_not_clobber(self, tmp_path, monkeypatch):
7684
+ """Reverse order previously returned {'connection': 'x'}, losing url."""
7685
+ monkeypatch.setenv("SPECKIT_TESTEXT_CONNECTION_URL", "y")
7686
+ monkeypatch.setenv("SPECKIT_TESTEXT_CONNECTION", "x")
7687
+ cm = ConfigManager(tmp_path, "testext")
7688
+ assert cm._get_env_config() == {"connection": {"url": "y"}}
7689
+
7690
+ def test_colliding_env_does_not_disable_hook_condition(self, tmp_path, monkeypatch):
7691
+ """`config.connection.url is set` must stay True under colliding env.
7692
+
7693
+ Before the fix the TypeError propagated into should_execute_hook's
7694
+ blanket `except Exception: return False`, silently disabling the hook.
7695
+ """
7696
+ ext_dir = tmp_path / ".specify" / "extensions" / "testext"
7697
+ ext_dir.mkdir(parents=True)
7698
+ (ext_dir / "testext-config.yml").write_text(
7699
+ "connection:\n url: https://example.com\n", encoding="utf-8"
7700
+ )
7701
+ monkeypatch.setenv("SPECKIT_TESTEXT_CONNECTION", "x")
7702
+ monkeypatch.setenv("SPECKIT_TESTEXT_CONNECTION_URL", "y")
7703
+ executor = HookExecutor(tmp_path)
7704
+ # Exercise the public API: before the fix the TypeError was swallowed
7705
+ # by should_execute_hook's `except Exception: return False`, so the
7706
+ # hook was silently disabled (False); after the fix it returns True.
7707
+ assert executor.should_execute_hook(
7708
+ {"condition": "config.connection.url is set", "extension": "testext"}
7709
+ ) is True
7710
+
7711
+ def test_malformed_env_names_ignored(self, tmp_path, monkeypatch):
7712
+ """A name with no key (SPECKIT_X_) or empty parts (consecutive
7713
+ underscores) must not create an entry under an empty key."""
7714
+ monkeypatch.setenv("SPECKIT_TESTEXT_", "orphan") # no key at all
7715
+ monkeypatch.setenv("SPECKIT_TESTEXT_A__B", "z") # empty middle part
7716
+ cm = ConfigManager(tmp_path, "testext")
7717
+ cfg = cm._get_env_config()
7718
+ assert "" not in cfg
7719
+ assert cfg == {"a": {"b": "z"}}
@@ -4060,6 +4060,40 @@ class TestPresetSetPriority:
4060
4060
  plain = strip_ansi(result.output)
4061
4061
  assert "already has priority 5" in plain
4062
4062
 
4063
+ def test_set_priority_repairs_corrupted_bool(self, project_dir, pack_dir):
4064
+ """A corrupted boolean priority must be repaired, not skipped.
4065
+
4066
+ ``isinstance(True, int)`` is True and ``True == 1`` in Python, so a
4067
+ stored ``True`` priority would short-circuit the ``already has
4068
+ priority 1`` skip path and never get rewritten to a real int —
4069
+ contradicting the comment that promises corrupted values are
4070
+ repaired. The guard must exclude bools (like normalize_priority).
4071
+ """
4072
+ from typer.testing import CliRunner
4073
+ from unittest.mock import patch
4074
+ from specify_cli import app
4075
+
4076
+ runner = CliRunner()
4077
+
4078
+ manager = PresetManager(project_dir)
4079
+ manager.install_from_directory(pack_dir, "0.1.5", priority=5)
4080
+ # Inject a corrupted boolean priority (True == 1).
4081
+ manager.registry.update("test-pack", {"priority": True})
4082
+
4083
+ with patch.object(Path, "cwd", return_value=project_dir):
4084
+ result = runner.invoke(app, ["preset", "set-priority", "test-pack", "1"])
4085
+
4086
+ assert result.exit_code == 0, result.output
4087
+ plain = strip_ansi(result.output)
4088
+ # The corrupted bool must be repaired, not reported as already-set.
4089
+ assert "already has priority" not in plain
4090
+ assert "priority changed" in plain
4091
+
4092
+ # The stored value is now a real int, not a bool.
4093
+ reloaded = PresetManager(project_dir).registry.get("test-pack")
4094
+ assert reloaded["priority"] == 1
4095
+ assert not isinstance(reloaded["priority"], bool)
4096
+
4063
4097
  def test_set_priority_invalid_value(self, project_dir, pack_dir):
4064
4098
  """Test set-priority rejects invalid priority values."""
4065
4099
  from typer.testing import CliRunner