specify-cli 0.12.8__tar.gz → 0.12.9__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 (461) hide show
  1. {specify_cli-0.12.8 → specify_cli-0.12.9}/CHANGELOG.md +15 -0
  2. {specify_cli-0.12.8 → specify_cli-0.12.9}/PKG-INFO +1 -1
  3. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/extensions.md +1 -1
  4. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/catalog.community.json +9 -6
  5. {specify_cli-0.12.8 → specify_cli-0.12.9}/pyproject.toml +1 -1
  6. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/common.sh +66 -11
  7. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/agents.py +6 -0
  8. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/primitives.py +49 -6
  9. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/base.py +55 -15
  10. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/hermes/__init__.py +9 -11
  11. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/shared_infra.py +5 -2
  12. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/expressions.py +91 -57
  13. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/conftest.py +24 -4
  14. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_base.py +72 -0
  15. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_cli.py +41 -0
  16. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_home_isolation.py +4 -0
  17. specify_cli-0.12.9/tests/integrations/test_integration_goose.py +85 -0
  18. specify_cli-0.12.9/tests/integrations/test_skill_frontmatter_quoting.py +111 -0
  19. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_setup_tasks.py +62 -0
  20. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_workflows.py +67 -0
  21. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_primitives.py +84 -0
  22. specify_cli-0.12.8/tests/integrations/test_integration_goose.py +0 -38
  23. {specify_cli-0.12.8 → specify_cli-0.12.9}/.devcontainer/devcontainer.json +0 -0
  24. {specify_cli-0.12.8 → specify_cli-0.12.9}/.devcontainer/post-create.sh +0 -0
  25. {specify_cli-0.12.8 → specify_cli-0.12.9}/.editorconfig +0 -0
  26. {specify_cli-0.12.8 → specify_cli-0.12.9}/.gitattributes +0 -0
  27. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/CODEOWNERS +0 -0
  28. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/agent_request.yml +0 -0
  29. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  30. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/bundle_submission.yml +0 -0
  31. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  32. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/extension_submission.yml +0 -0
  33. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  34. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/preset_submission.yml +0 -0
  35. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  36. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/aw/actions-lock.json +0 -0
  37. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/dependabot.yml +0 -0
  38. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/skills/add-community-extension/SKILL.md +0 -0
  39. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/RELEASE-PROCESS.md +0 -0
  40. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-extension.lock.yml +0 -0
  41. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-extension.md +0 -0
  42. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-preset.lock.yml +0 -0
  43. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-preset.md +0 -0
  44. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-assess.lock.yml +0 -0
  45. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-assess.md +0 -0
  46. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-fix.lock.yml +0 -0
  47. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-fix.md +0 -0
  48. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-test.lock.yml +0 -0
  49. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-test.md +0 -0
  50. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/catalog-assign.yml +0 -0
  51. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/codeql.yml +0 -0
  52. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/docs.yml +0 -0
  53. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/lint.yml +0 -0
  54. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/publish-pypi.yml +0 -0
  55. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/release-trigger.yml +0 -0
  56. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/release.yml +0 -0
  57. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/stale.yml +0 -0
  58. {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/test.yml +0 -0
  59. {specify_cli-0.12.8 → specify_cli-0.12.9}/.gitignore +0 -0
  60. {specify_cli-0.12.8 → specify_cli-0.12.9}/.markdownlint-cli2.jsonc +0 -0
  61. {specify_cli-0.12.8 → specify_cli-0.12.9}/.zenodo.json +0 -0
  62. {specify_cli-0.12.8 → specify_cli-0.12.9}/AGENTS.md +0 -0
  63. {specify_cli-0.12.8 → specify_cli-0.12.9}/CITATION.cff +0 -0
  64. {specify_cli-0.12.8 → specify_cli-0.12.9}/CODE_OF_CONDUCT.md +0 -0
  65. {specify_cli-0.12.8 → specify_cli-0.12.9}/CONTRIBUTING.md +0 -0
  66. {specify_cli-0.12.8 → specify_cli-0.12.9}/DEVELOPMENT.md +0 -0
  67. {specify_cli-0.12.8 → specify_cli-0.12.9}/LICENSE +0 -0
  68. {specify_cli-0.12.8 → specify_cli-0.12.9}/README.md +0 -0
  69. {specify_cli-0.12.8 → specify_cli-0.12.9}/SECURITY.md +0 -0
  70. {specify_cli-0.12.8 → specify_cli-0.12.9}/SUPPORT.md +0 -0
  71. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/.gitignore +0 -0
  72. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/README.md +0 -0
  73. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/bundles.md +0 -0
  74. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/friends.md +0 -0
  75. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/overview.md +0 -0
  76. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/presets.md +0 -0
  77. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/walkthroughs.md +0 -0
  78. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/concepts/complex-features.md +0 -0
  79. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/concepts/sdd.md +0 -0
  80. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/concepts/spec-persistence.md +0 -0
  81. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/docfx.json +0 -0
  82. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/guides/evolving-specs.md +0 -0
  83. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/guides/monorepo.md +0 -0
  84. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/index.md +0 -0
  85. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/air-gapped.md +0 -0
  86. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/one-time.md +0 -0
  87. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/pipx.md +0 -0
  88. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/uv.md +0 -0
  89. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/installation.md +0 -0
  90. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/local-development.md +0 -0
  91. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/quickstart.md +0 -0
  92. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/authentication.md +0 -0
  93. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/bundles.md +0 -0
  94. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/core.md +0 -0
  95. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/extensions.md +0 -0
  96. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/integrations.md +0 -0
  97. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/overview.md +0 -0
  98. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/presets.md +0 -0
  99. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/workflows.md +0 -0
  100. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/template/public/main.css +0 -0
  101. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/toc.yml +0 -0
  102. {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/upgrade.md +0 -0
  103. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/business-analyst/README.md +0 -0
  104. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/business-analyst/bundle.yml +0 -0
  105. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/developer/README.md +0 -0
  106. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/developer/bundle.yml +0 -0
  107. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/product-manager/README.md +0 -0
  108. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/product-manager/bundle.yml +0 -0
  109. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/security-researcher/README.md +0 -0
  110. {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/security-researcher/bundle.yml +0 -0
  111. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-API-REFERENCE.md +0 -0
  112. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-DEVELOPMENT-GUIDE.md +0 -0
  113. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-PUBLISHING-GUIDE.md +0 -0
  114. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-USER-GUIDE.md +0 -0
  115. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/README.md +0 -0
  116. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/RFC-EXTENSION-SYSTEM.md +0 -0
  117. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/README.md +0 -0
  118. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/agent-context-config.yml +0 -0
  119. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/agent-context-defaults.json +0 -0
  120. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/commands/speckit.agent-context.update.md +0 -0
  121. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/extension.yml +0 -0
  122. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -0
  123. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -0
  124. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/scripts/python/update_agent_context.py +0 -0
  125. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/README.md +0 -0
  126. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/commands/speckit.bug.assess.md +0 -0
  127. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/commands/speckit.bug.fix.md +0 -0
  128. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/commands/speckit.bug.test.md +0 -0
  129. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/extension.yml +0 -0
  130. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/catalog.json +0 -0
  131. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/README.md +0 -0
  132. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.commit.md +0 -0
  133. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.feature.md +0 -0
  134. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.initialize.md +0 -0
  135. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.remote.md +0 -0
  136. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.validate.md +0 -0
  137. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/config-template.yml +0 -0
  138. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/extension.yml +0 -0
  139. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/git-config.yml +0 -0
  140. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/auto-commit.sh +0 -0
  141. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/create-new-feature-branch.sh +0 -0
  142. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/git-common.sh +0 -0
  143. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  144. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  145. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +0 -0
  146. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  147. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  148. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/selftest/commands/selftest.md +0 -0
  149. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/selftest/extension.yml +0 -0
  150. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/.gitignore +0 -0
  151. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/CHANGELOG.md +0 -0
  152. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/EXAMPLE-README.md +0 -0
  153. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/LICENSE +0 -0
  154. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/README.md +0 -0
  155. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/commands/example.md +0 -0
  156. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/config-template.yml +0 -0
  157. {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/extension.yml +0 -0
  158. {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/CONTRIBUTING.md +0 -0
  159. {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/README.md +0 -0
  160. {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/catalog.community.json +0 -0
  161. {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/catalog.json +0 -0
  162. {specify_cli-0.12.8 → specify_cli-0.12.9}/media/bootstrap-claude-code.gif +0 -0
  163. {specify_cli-0.12.8 → specify_cli-0.12.9}/media/logo_large.webp +0 -0
  164. {specify_cli-0.12.8 → specify_cli-0.12.9}/media/logo_small.webp +0 -0
  165. {specify_cli-0.12.8 → specify_cli-0.12.9}/media/spec-kit-video-header.jpg +0 -0
  166. {specify_cli-0.12.8 → specify_cli-0.12.9}/media/specify_cli.gif +0 -0
  167. {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-April.md +0 -0
  168. {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-February.md +0 -0
  169. {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-June.md +0 -0
  170. {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-March.md +0 -0
  171. {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-May.md +0 -0
  172. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/ARCHITECTURE.md +0 -0
  173. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/PUBLISHING.md +0 -0
  174. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/README.md +0 -0
  175. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/catalog.community.json +0 -0
  176. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/catalog.json +0 -0
  177. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/README.md +0 -0
  178. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.constitution.md +0 -0
  179. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.implement.md +0 -0
  180. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.plan.md +0 -0
  181. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.specify.md +0 -0
  182. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.tasks.md +0 -0
  183. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/preset.yml +0 -0
  184. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/README.md +0 -0
  185. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/commands/speckit.myext.myextcmd.md +0 -0
  186. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/commands/speckit.specify.md +0 -0
  187. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/preset.yml +0 -0
  188. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/templates/myext-template.md +0 -0
  189. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/templates/spec-template.md +0 -0
  190. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/commands/speckit.specify.md +0 -0
  191. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/commands/speckit.wrap-test.md +0 -0
  192. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/preset.yml +0 -0
  193. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/agent-file-template.md +0 -0
  194. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/checklist-template.md +0 -0
  195. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/constitution-template.md +0 -0
  196. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/plan-template.md +0 -0
  197. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/spec-template.md +0 -0
  198. {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/tasks-template.md +0 -0
  199. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/check-prerequisites.sh +0 -0
  200. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/create-new-feature.sh +0 -0
  201. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/setup-plan.sh +0 -0
  202. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/setup-tasks.sh +0 -0
  203. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/check-prerequisites.ps1 +0 -0
  204. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/common.ps1 +0 -0
  205. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/create-new-feature.ps1 +0 -0
  206. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/setup-plan.ps1 +0 -0
  207. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/setup-tasks.ps1 +0 -0
  208. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/python/check_prerequisites.py +0 -0
  209. {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/python/common.py +0 -0
  210. {specify_cli-0.12.8 → specify_cli-0.12.9}/spec-driven.md +0 -0
  211. {specify_cli-0.12.8 → specify_cli-0.12.9}/spec-kit.code-workspace +0 -0
  212. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/__init__.py +0 -0
  213. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_agent_config.py +0 -0
  214. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_assets.py +0 -0
  215. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_console.py +0 -0
  216. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_github_http.py +0 -0
  217. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_init_options.py +0 -0
  218. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_invocation_style.py +0 -0
  219. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_project.py +0 -0
  220. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_toml_string.py +0 -0
  221. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_utils.py +0 -0
  222. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_version.py +0 -0
  223. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/__init__.py +0 -0
  224. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/azure_devops.py +0 -0
  225. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/base.py +0 -0
  226. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/config.py +0 -0
  227. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/github.py +0 -0
  228. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/http.py +0 -0
  229. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/__init__.py +0 -0
  230. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/commands_impl/__init__.py +0 -0
  231. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/commands_impl/catalog_config.py +0 -0
  232. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/__init__.py +0 -0
  233. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/project.py +0 -0
  234. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/versioning.py +0 -0
  235. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/yamlio.py +0 -0
  236. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/__init__.py +0 -0
  237. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/catalog.py +0 -0
  238. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/manifest.py +0 -0
  239. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/records.py +0 -0
  240. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/__init__.py +0 -0
  241. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/adapters.py +0 -0
  242. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/catalog_stack.py +0 -0
  243. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/conflict.py +0 -0
  244. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/installer.py +0 -0
  245. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/packager.py +0 -0
  246. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/references.py +0 -0
  247. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/resolver.py +0 -0
  248. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/validator.py +0 -0
  249. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/catalogs.py +0 -0
  250. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/commands/__init__.py +0 -0
  251. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/commands/bundle/__init__.py +0 -0
  252. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/commands/init.py +0 -0
  253. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/extensions/__init__.py +0 -0
  254. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/extensions/_commands.py +0 -0
  255. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_runtime.py +0 -0
  256. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_scaffold.py +0 -0
  257. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_state.py +0 -0
  258. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_status.py +0 -0
  259. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/__init__.py +0 -0
  260. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_commands.py +0 -0
  261. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_helpers.py +0 -0
  262. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_install_commands.py +0 -0
  263. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_migrate_commands.py +0 -0
  264. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_query_commands.py +0 -0
  265. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_scaffold_commands.py +0 -0
  266. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/agy/__init__.py +0 -0
  267. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/amp/__init__.py +0 -0
  268. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/auggie/__init__.py +0 -0
  269. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/bob/__init__.py +0 -0
  270. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/catalog.py +0 -0
  271. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/claude/__init__.py +0 -0
  272. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/cline/__init__.py +0 -0
  273. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/codebuddy/__init__.py +0 -0
  274. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/codex/__init__.py +0 -0
  275. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/copilot/__init__.py +0 -0
  276. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/cursor_agent/__init__.py +0 -0
  277. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/devin/__init__.py +0 -0
  278. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/firebender/__init__.py +0 -0
  279. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/forge/__init__.py +0 -0
  280. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/gemini/__init__.py +0 -0
  281. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/generic/__init__.py +0 -0
  282. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/goose/__init__.py +0 -0
  283. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/junie/__init__.py +0 -0
  284. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/kilocode/__init__.py +0 -0
  285. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/kimi/__init__.py +0 -0
  286. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/kiro_cli/__init__.py +0 -0
  287. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/lingma/__init__.py +0 -0
  288. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/manifest.py +0 -0
  289. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/omp/__init__.py +0 -0
  290. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/opencode/__init__.py +0 -0
  291. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/pi/__init__.py +0 -0
  292. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/qodercli/__init__.py +0 -0
  293. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/qwen/__init__.py +0 -0
  294. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/rovodev/__init__.py +0 -0
  295. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/shai/__init__.py +0 -0
  296. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/tabnine/__init__.py +0 -0
  297. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/trae/__init__.py +0 -0
  298. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/vibe/__init__.py +0 -0
  299. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/zcode/__init__.py +0 -0
  300. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/zed/__init__.py +0 -0
  301. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/presets/__init__.py +0 -0
  302. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/presets/_commands.py +0 -0
  303. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/__init__.py +0 -0
  304. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/_commands.py +0 -0
  305. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/base.py +0 -0
  306. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/catalog.py +0 -0
  307. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/engine.py +0 -0
  308. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/__init__.py +0 -0
  309. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/command/__init__.py +0 -0
  310. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/do_while/__init__.py +0 -0
  311. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/fan_in/__init__.py +0 -0
  312. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/fan_out/__init__.py +0 -0
  313. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/gate/__init__.py +0 -0
  314. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/if_then/__init__.py +0 -0
  315. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/init/__init__.py +0 -0
  316. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/prompt/__init__.py +0 -0
  317. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/shell/__init__.py +0 -0
  318. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/switch/__init__.py +0 -0
  319. {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/while_loop/__init__.py +0 -0
  320. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/checklist-template.md +0 -0
  321. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/analyze.md +0 -0
  322. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/checklist.md +0 -0
  323. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/clarify.md +0 -0
  324. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/constitution.md +0 -0
  325. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/converge.md +0 -0
  326. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/implement.md +0 -0
  327. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/plan.md +0 -0
  328. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/specify.md +0 -0
  329. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/tasks.md +0 -0
  330. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/taskstoissues.md +0 -0
  331. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/constitution-template.md +0 -0
  332. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/plan-template.md +0 -0
  333. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/spec-template.md +0 -0
  334. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/tasks-template.md +0 -0
  335. {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/vscode-settings.json +0 -0
  336. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/__init__.py +0 -0
  337. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/auth_helpers.py +0 -0
  338. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/bundler_helpers.py +0 -0
  339. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/conftest.py +0 -0
  340. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/contract/test_bundle_cli.py +0 -0
  341. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/contract/test_catalog_schema.py +0 -0
  342. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/contract/test_manifest_schema.py +0 -0
  343. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/__init__.py +0 -0
  344. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/bug/__init__.py +0 -0
  345. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/bug/test_bug_extension.py +0 -0
  346. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/git/__init__.py +0 -0
  347. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/git/test_git_extension.py +0 -0
  348. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_agent_context_cli_free.py +0 -0
  349. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_extension_agent_context.py +0 -0
  350. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_update_agent_context_feature_json.py +0 -0
  351. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_update_agent_context_python_parity.py +0 -0
  352. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/TESTING.md +0 -0
  353. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/plan.md +0 -0
  354. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/spec.md +0 -0
  355. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/tasks.md +0 -0
  356. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/http_helpers.py +0 -0
  357. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_catalog_stack.py +0 -0
  358. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_init_install.py +0 -0
  359. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_install_flow.py +0 -0
  360. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_local_install.py +0 -0
  361. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_offline.py +0 -0
  362. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_security_paths.py +0 -0
  363. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/__init__.py +0 -0
  364. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_extra_args.py +0 -0
  365. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_agy.py +0 -0
  366. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_amp.py +0 -0
  367. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_auggie.py +0 -0
  368. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_markdown.py +0 -0
  369. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_skills.py +0 -0
  370. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_toml.py +0 -0
  371. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_yaml.py +0 -0
  372. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_bob.py +0 -0
  373. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_catalog.py +0 -0
  374. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_claude.py +0 -0
  375. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_cline.py +0 -0
  376. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_codebuddy.py +0 -0
  377. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_codex.py +0 -0
  378. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_copilot.py +0 -0
  379. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_cursor_agent.py +0 -0
  380. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_devin.py +0 -0
  381. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_firebender.py +0 -0
  382. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_forge.py +0 -0
  383. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_gemini.py +0 -0
  384. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_generic.py +0 -0
  385. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_hermes.py +0 -0
  386. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_junie.py +0 -0
  387. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_kilocode.py +0 -0
  388. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_kimi.py +0 -0
  389. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_kiro_cli.py +0 -0
  390. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_lingma.py +0 -0
  391. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_omp.py +0 -0
  392. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_opencode.py +0 -0
  393. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_pi.py +0 -0
  394. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_qodercli.py +0 -0
  395. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_qwen.py +0 -0
  396. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_rovodev.py +0 -0
  397. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_scaffold.py +0 -0
  398. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_shai.py +0 -0
  399. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_state.py +0 -0
  400. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_subcommand.py +0 -0
  401. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_tabnine.py +0 -0
  402. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_trae.py +0 -0
  403. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_vibe.py +0 -0
  404. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_zcode.py +0 -0
  405. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_zed.py +0 -0
  406. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_manifest.py +0 -0
  407. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_registry.py +0 -0
  408. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/self_upgrade_helpers.py +0 -0
  409. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_agent_config_consistency.py +0 -0
  410. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_authentication.py +0 -0
  411. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_branch_numbering.py +0 -0
  412. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_check_prerequisites_paths_only.py +0 -0
  413. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_check_prerequisites_python_parity.py +0 -0
  414. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_check_tool.py +0 -0
  415. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_cli_version.py +0 -0
  416. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_commands_package.py +0 -0
  417. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_console_imports.py +0 -0
  418. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extension_registration.py +0 -0
  419. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extension_skills.py +0 -0
  420. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extension_update_hardening.py +0 -0
  421. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extensions.py +0 -0
  422. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_github_http.py +0 -0
  423. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_github_workflows.py +0 -0
  424. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_init_dir.py +0 -0
  425. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_init_dir_cli.py +0 -0
  426. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_live_transient_windows.py +0 -0
  427. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_merge.py +0 -0
  428. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_post_process.py +0 -0
  429. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_presets.py +0 -0
  430. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_ps1_encoding.py +0 -0
  431. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_registrar_path_traversal.py +0 -0
  432. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_detection.py +0 -0
  433. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_execution.py +0 -0
  434. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_guidance.py +0 -0
  435. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_verification.py +0 -0
  436. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_setup_plan_feature_json.py +0 -0
  437. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_setup_plan_no_overwrite.py +0 -0
  438. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_shared_infra_integrity.py +0 -0
  439. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_timestamp_branches.py +0 -0
  440. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_upgrade.py +0 -0
  441. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_utils.py +0 -0
  442. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_utils_assets_imports.py +0 -0
  443. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_version_imports.py +0 -0
  444. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_workflow_run_without_project.py +0 -0
  445. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_adapters.py +0 -0
  446. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_catalog_config.py +0 -0
  447. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_conflict.py +0 -0
  448. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_packager.py +0 -0
  449. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_records.py +0 -0
  450. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_references.py +0 -0
  451. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_resolver.py +0 -0
  452. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_validator.py +0 -0
  453. {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_versioning.py +0 -0
  454. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/ARCHITECTURE.md +0 -0
  455. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/PUBLISHING.md +0 -0
  456. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/README.md +0 -0
  457. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/catalog.community.json +0 -0
  458. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/catalog.json +0 -0
  459. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/speckit/workflow.yml +0 -0
  460. {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/step-catalog.community.json +0 -0
  461. {specify_cli-0.12.8 → specify_cli-0.12.9}/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.9] - 2026-07-09
6
+
7
+ ### Changed
8
+
9
+ - fix(integrations): skip Windows Store python3 alias stub in resolve_python_interpreter (#3385)
10
+ - fix(integrations): escape control characters in SKILL.md frontmatter (#3399)
11
+ - fix(workflows): apply chained expression filters left-to-right (#3339)
12
+ - fix(scripts): resolve invoke_separator by parse success, not python3 availability (#3304) (#3320)
13
+ - fix(shared-infra): refresh_shared_templates preserves recovered user files (#3378)
14
+ - fix(agents): resolve skill placeholders in Goose (yaml) command output (#3374)
15
+ - fix(bundler): enforce version pin on bundled preset/extension installs (#3377)
16
+ - Update Golden Demo extension to v0.3.0 (#3394)
17
+ - test: isolate integration test home (#3144)
18
+ - chore: release 0.12.8, begin 0.12.9.dev0 development (#3410)
19
+
5
20
  ## [0.12.8] - 2026-07-08
6
21
 
7
22
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specify-cli
3
- Version: 0.12.8
3
+ Version: 0.12.9
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
@@ -58,7 +58,7 @@ The following community-contributed extensions are available in [`catalog.commun
58
58
  | Fleet Orchestrator | Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases | `process` | Read+Write | [spec-kit-fleet](https://github.com/sharathsatish/spec-kit-fleet) |
59
59
  | GitHub Issues Integration 1 | Generate spec artifacts from GitHub Issues - import issues, sync updates, and maintain bidirectional traceability | `integration` | Read+Write | [spec-kit-github-issues](https://github.com/Fatima367/spec-kit-github-issues) |
60
60
  | GitHub Issues Integration 2 | Creates and syncs local specs from an existing GitHub issue | `integration` | Read+Write | [spec-kit-issue](https://github.com/aaronrsun/spec-kit-issue) |
61
- | Golden Demo | Extracts acceptance criteria from specs, builds test vectors, and produces a behavioral drift report complementary to Architecture Guard and CDD | `docs` | Read+Write | [spec-kit-golden-demo](https://github.com/jasstt/spec-kit-golden-demo) |
61
+ | Golden Demo | Deterministic behavioral drift oracle. Extracts acceptance criteria, generates fuzz test vectors (seed=42), compares golden Python implementations against real code in any language. CI/CD gatekeeper with warn/strict modes. | `docs` | Read+Write | [spec-kit-golden-demo](https://github.com/jasstt/spec-kit-golden-demo) |
62
62
  | Improve Extension | Audits any codebase as a senior advisor and writes prioritized, self-contained spec prompts under specs/ that the spec-kit lifecycle can process | `process` | Read+Write | [spec-kit-improve](https://github.com/d0whc3r/spec-kit-improve) |
63
63
  | Intake | Normalize PRD, design, HTML SSOT, and test-case evidence into SDD-ready intake artifacts. | `docs` | Read+Write | [spec-kit-intake](https://github.com/bigsmartben/spec-kit-intake) |
64
64
  | Intelligent Agent Orchestrator | Cross-catalog agent discovery and intelligent prompt-to-command routing | `process` | Read+Write | [spec-kit-orchestrator](https://github.com/pragya247/spec-kit-orchestrator) |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": "1.0",
3
- "updated_at": "2026-07-06T00:00:00Z",
3
+ "updated_at": "2026-07-07T00:00:00Z",
4
4
  "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
5
5
  "extensions": {
6
6
  "aide": {
@@ -1398,10 +1398,10 @@
1398
1398
  "golden-demo": {
1399
1399
  "name": "Golden Demo",
1400
1400
  "id": "golden-demo",
1401
- "description": "Extracts acceptance criteria from specs, builds test vectors, and produces a behavioral drift report complementary to Architecture Guard and CDD.",
1401
+ "description": "Deterministic behavioral drift oracle. Extracts acceptance criteria, generates fuzz test vectors (seed=42), compares golden Python implementations against real code in any language. CI/CD gatekeeper with warn/strict modes.",
1402
1402
  "author": "jasstt",
1403
- "version": "0.1.1",
1404
- "download_url": "https://github.com/jasstt/spec-kit-golden-demo/archive/refs/tags/v0.1.1.zip",
1403
+ "version": "0.3.0",
1404
+ "download_url": "https://github.com/jasstt/spec-kit-golden-demo/archive/refs/tags/v0.3.0.zip",
1405
1405
  "repository": "https://github.com/jasstt/spec-kit-golden-demo",
1406
1406
  "homepage": "https://github.com/jasstt/spec-kit-golden-demo",
1407
1407
  "documentation": "https://github.com/jasstt/spec-kit-golden-demo",
@@ -1412,13 +1412,16 @@
1412
1412
  "speckit_version": ">=0.1.0"
1413
1413
  },
1414
1414
  "provides": {
1415
- "commands": 2,
1415
+ "commands": 3,
1416
1416
  "hooks": 2
1417
1417
  },
1418
1418
  "tags": [
1419
1419
  "testing",
1420
1420
  "drift-detection",
1421
1421
  "behavioral-oracle",
1422
+ "fuzzing",
1423
+ "ci-cd",
1424
+ "cross-language",
1422
1425
  "tdd",
1423
1426
  "quality"
1424
1427
  ],
@@ -1426,7 +1429,7 @@
1426
1429
  "downloads": 0,
1427
1430
  "stars": 0,
1428
1431
  "created_at": "2026-06-24T00:00:00Z",
1429
- "updated_at": "2026-06-24T00:00:00Z"
1432
+ "updated_at": "2026-07-07T00:00:00Z"
1430
1433
  },
1431
1434
  "harness": {
1432
1435
  "name": "Research Harness",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "specify-cli"
3
- version = "0.12.8"
3
+ version = "0.12.9"
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"
@@ -244,21 +244,29 @@ get_invoke_separator() {
244
244
 
245
245
  local integration_json="$repo_root/.specify/integration.json"
246
246
  local separator="."
247
- local parsed_with_jq=0
247
+ local parsed=0
248
248
 
249
249
  if [[ -f "$integration_json" ]]; then
250
+ # Try parsers in order (jq -> python3 -> awk), falling through on
251
+ # failure. Selection is by *parse success*, not mere availability: on
252
+ # Windows `python3` commonly resolves to the Microsoft Store App
253
+ # Execution Alias stub, which passes `command -v` but fails at runtime
254
+ # (exit 49). An availability-gated branch would pick python3, swallow
255
+ # its failure, and — because this function historically had no text
256
+ # fallback — silently return "." even for `-`-separator integrations
257
+ # (e.g. forge, cline), yielding wrong command hints (issue #3304).
250
258
  if command -v jq >/dev/null 2>&1; then
251
259
  local jq_separator
252
260
  if jq_separator=$(jq -r '(.default_integration // .integration // "") as $k | if $k == "" then "." else (.integration_settings[$k].invoke_separator // ".") end' "$integration_json" 2>/dev/null); then
253
- parsed_with_jq=1
254
261
  case "$jq_separator" in
255
- "."|"-") separator="$jq_separator" ;;
262
+ "."|"-") separator="$jq_separator"; parsed=1 ;;
256
263
  esac
257
264
  fi
258
265
  fi
259
266
 
260
- if [[ "$parsed_with_jq" -eq 0 ]] && command -v python3 >/dev/null 2>&1; then
261
- if separator=$(python3 - "$integration_json" <<'PY' 2>/dev/null
267
+ if [[ "$parsed" -eq 0 ]] && command -v python3 >/dev/null 2>&1; then
268
+ local py_separator
269
+ if py_separator=$(python3 - "$integration_json" <<'PY' 2>/dev/null
262
270
  import json
263
271
  import sys
264
272
 
@@ -274,17 +282,64 @@ try:
274
282
  separator = entry["invoke_separator"]
275
283
  print(separator)
276
284
  except Exception:
277
- print(".")
285
+ sys.exit(1)
278
286
  PY
279
287
  ); then
280
- case "$separator" in
281
- "."|"-") ;;
282
- *) separator="." ;;
288
+ case "$py_separator" in
289
+ "."|"-") separator="$py_separator"; parsed=1 ;;
283
290
  esac
284
- else
285
- separator="."
286
291
  fi
287
292
  fi
293
+
294
+ if [[ "$parsed" -eq 0 ]]; then
295
+ # Last-resort text fallback for environments with neither jq nor a
296
+ # working python3 (e.g. stock Windows + Git Bash). Reads the active
297
+ # integration key (default_integration, else integration) and its
298
+ # invoke_separator from within the integration_settings object.
299
+ # Handles both pretty-printed (the written form) and compact JSON.
300
+ # Accumulate all lines into one buffer in END rather than using
301
+ # gawk-only whole-file slurp (RS="^$"), so this stays portable to
302
+ # the BSD awk on macOS.
303
+ local awk_separator
304
+ awk_separator=$(awk '
305
+ function keyval(d, name, v) {
306
+ if (match(d, "\"" name "\"[ \t\r\n]*:[ \t\r\n]*\"[^\"]*\"")) {
307
+ v=substr(d,RSTART,RLENGTH); sub(/^.*:[ \t\r\n]*"/,"",v); sub(/"$/,"",v); return v
308
+ }
309
+ return ""
310
+ }
311
+ { doc = doc $0 "\n" }
312
+ END {
313
+ key=keyval(doc,"default_integration"); if (key=="") key=keyval(doc,"integration")
314
+ sep="."
315
+ if (key!="") {
316
+ settings=doc
317
+ if (match(doc, /"integration_settings"[ \t\r\n]*:[ \t\r\n]*[{]/)) {
318
+ settings=substr(doc, RSTART+RLENGTH-1)
319
+ }
320
+ if (match(settings, "\"" key "\"[ \t\r\n]*:[ \t\r\n]*[{]")) {
321
+ start=RSTART+RLENGTH-1
322
+ depth=0
323
+ obj=""
324
+ for (i=start; i<=length(settings); i++) {
325
+ c=substr(settings,i,1)
326
+ obj=obj c
327
+ if (c=="{") depth++
328
+ else if (c=="}") { depth--; if (depth==0) break }
329
+ }
330
+ if (match(obj, /"invoke_separator"[ \t\r\n]*:[ \t\r\n]*"[-.]"/)) {
331
+ tok=substr(obj,RSTART,RLENGTH); s=substr(tok,length(tok)-1,1)
332
+ if (s=="." || s=="-") sep=s
333
+ }
334
+ }
335
+ }
336
+ print sep
337
+ }
338
+ ' "$integration_json" 2>/dev/null)
339
+ case "$awk_separator" in
340
+ "."|"-") separator="$awk_separator" ;;
341
+ esac
342
+ fi
288
343
  fi
289
344
 
290
345
  _SPECIFY_INVOKE_SEPARATOR_CACHE_REPO_ROOT="$repo_root"
@@ -701,6 +701,12 @@ class CommandRegistrar:
701
701
  )
702
702
  output = self.render_toml_command(frontmatter, body, source_id)
703
703
  elif agent_config["format"] == "yaml":
704
+ body = self.resolve_skill_placeholders(
705
+ agent_name, frontmatter, body, project_root
706
+ )
707
+ body = self._convert_argument_placeholder(
708
+ body, "$ARGUMENTS", agent_config["args"]
709
+ )
704
710
  output = self.render_yaml_command(
705
711
  frontmatter, body, source_id, cmd_name
706
712
  )
@@ -33,12 +33,13 @@ DEFAULT_PRIORITY = 10
33
33
  def _assert_pinned_version(
34
34
  kind: str, component_id: str, pinned: str | None, advertised: object
35
35
  ) -> None:
36
- """Refuse to install when the catalog version differs from the manifest pin.
36
+ """Refuse to install when the resolved version differs from the manifest pin.
37
37
 
38
38
  Bundle manifests pin component versions for reproducibility; installing
39
- whatever the active catalog currently serves would silently violate the
40
- pin. When the catalog advertises no version we cannot enforce the pin, so
41
- installation proceeds (the catalog, not the bundler, owns that gap).
39
+ whatever the resolved source (catalog *or* bundled asset) provides would
40
+ silently violate the pin. When the source advertises no version we cannot
41
+ enforce the pin, so installation proceeds (the source, not the bundler,
42
+ owns that gap).
42
43
  """
43
44
  if not pinned or advertised is None:
44
45
  return
@@ -54,11 +55,35 @@ def _assert_pinned_version(
54
55
  if not matches:
55
56
  raise BundlerError(
56
57
  f"{kind} '{component_id}' is pinned to version {pinned} in the bundle "
57
- f"manifest, but the active catalog serves {actual}. Update the bundle's "
58
- "pinned version or the catalog before installing."
58
+ f"manifest, but the resolved version is {actual}. Update the bundle's "
59
+ "pinned version or the source before installing."
59
60
  )
60
61
 
61
62
 
63
+ def _bundled_manifest_version(manifest_path: Path, root_key: str) -> str | None:
64
+ """Best-effort read of a bundled asset's declared version from its manifest.
65
+
66
+ Returns ``None`` when the manifest is missing/unreadable/invalid, which
67
+ ``_assert_pinned_version`` treats as "cannot enforce" (proceed) — matching
68
+ the catalog "advertises no version" escape hatch.
69
+ """
70
+ try:
71
+ import yaml
72
+
73
+ data = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
74
+ if isinstance(data, dict):
75
+ section = data.get(root_key)
76
+ if isinstance(section, dict):
77
+ version = section.get("version")
78
+ # Only a non-empty string is a usable version; anything else
79
+ # (missing / non-string / whitespace) means "cannot enforce".
80
+ if isinstance(version, str) and version.strip():
81
+ return version
82
+ except Exception: # noqa: BLE001 - unreadable/invalid manifest: skip pin
83
+ return None
84
+ return None
85
+
86
+
62
87
  class _KindManager(Protocol):
63
88
  def is_installed(self, component: ComponentRef) -> bool: ...
64
89
 
@@ -134,6 +159,15 @@ class _PresetKindManager:
134
159
 
135
160
  bundled = _locate_bundled_preset(component.id)
136
161
  if bundled is not None:
162
+ # Enforce the manifest pin against the bundled asset's own version,
163
+ # mirroring the catalog path below (the bundled path previously
164
+ # skipped the pin entirely).
165
+ _assert_pinned_version(
166
+ "Preset",
167
+ component.id,
168
+ component.version,
169
+ _bundled_manifest_version(bundled / "preset.yml", "preset"),
170
+ )
137
171
  self._manager.install_from_directory(bundled, speckit_version, priority)
138
172
  return
139
173
 
@@ -198,6 +232,15 @@ class _ExtensionKindManager:
198
232
 
199
233
  bundled = _locate_bundled_extension(component.id)
200
234
  if bundled is not None:
235
+ # Enforce the manifest pin against the bundled asset's own version,
236
+ # mirroring the catalog path below (the bundled path previously
237
+ # skipped the pin entirely).
238
+ _assert_pinned_version(
239
+ "Extension",
240
+ component.id,
241
+ component.version,
242
+ _bundled_manifest_version(bundled / "extension.yml", "extension"),
243
+ )
201
244
  self._manager.install_from_directory(
202
245
  bundled, speckit_version, priority=priority
203
246
  )
@@ -17,6 +17,7 @@ import os
17
17
  import re
18
18
  import shlex
19
19
  import shutil
20
+ import subprocess
20
21
  import sys
21
22
  from abc import ABC
22
23
  from dataclasses import dataclass
@@ -54,6 +55,18 @@ _CORE_COMMAND_TEMPLATE_RANK = {
54
55
  }
55
56
 
56
57
 
58
+ def yaml_quote(value: str) -> str:
59
+ """Emit *value* as a double-quoted YAML scalar on a single line.
60
+
61
+ A hand-rolled quote cannot carry raw newlines (YAML folds them to
62
+ spaces) or control characters (the reader rejects them), so let the
63
+ YAML emitter produce the escapes.
64
+ """
65
+ return yaml.safe_dump(
66
+ str(value), default_style='"', allow_unicode=True, width=sys.maxsize
67
+ ).strip()
68
+
69
+
57
70
  # ---------------------------------------------------------------------------
58
71
  # IntegrationOption
59
72
  # ---------------------------------------------------------------------------
@@ -592,10 +605,42 @@ class IntegrationBase(ABC):
592
605
  if candidate.exists():
593
606
  return relative
594
607
  for name in ("python3", "python"):
595
- if shutil.which(name):
596
- return name
608
+ found = shutil.which(name)
609
+ if not found:
610
+ continue
611
+ # On Windows, python3/python on PATH may be the Microsoft
612
+ # Store App Execution Alias stub: it exists but only prints
613
+ # an installer hint and exits non-zero, so existence is not
614
+ # enough (see #3304 for the same defect in the sh scripts).
615
+ if sys.platform == "win32" and not IntegrationBase._interpreter_runs(
616
+ found
617
+ ):
618
+ continue
619
+ return name
597
620
  return sys.executable or "python3"
598
621
 
622
+ @staticmethod
623
+ def _interpreter_runs(path: str) -> bool:
624
+ """Return True when *path* executes as a Python interpreter.
625
+
626
+ Runs isolated (``-I``) without ``site`` (``-S``) and discards
627
+ I/O so the probe is a fast liveness check that cannot trigger
628
+ ``sitecustomize``/user startup hooks.
629
+ """
630
+ try:
631
+ return (
632
+ subprocess.run(
633
+ [path, "-I", "-S", "-c", ""],
634
+ stdin=subprocess.DEVNULL,
635
+ stdout=subprocess.DEVNULL,
636
+ stderr=subprocess.DEVNULL,
637
+ timeout=15,
638
+ ).returncode
639
+ == 0
640
+ )
641
+ except (OSError, subprocess.SubprocessError):
642
+ return False
643
+
599
644
  @staticmethod
600
645
  def process_template(
601
646
  content: str,
@@ -1077,7 +1122,6 @@ class TomlIntegration(IntegrationBase):
1077
1122
  # YamlIntegration — YAML-format agents (Goose)
1078
1123
  # ---------------------------------------------------------------------------
1079
1124
 
1080
-
1081
1125
  class YamlIntegration(IntegrationBase):
1082
1126
  """Concrete base for integrations that use YAML recipe format.
1083
1127
 
@@ -1480,21 +1524,17 @@ class SkillsIntegration(IntegrationBase):
1480
1524
  if not description:
1481
1525
  description = f"Spec Kit: {command_name} workflow"
1482
1526
 
1483
- # Build SKILL.md with manually formatted frontmatter to match
1484
- # the release packaging script output exactly (double-quoted
1485
- # values, no yaml.safe_dump quoting differences).
1486
- def _quote(v: str) -> str:
1487
- escaped = v.replace("\\", "\\\\").replace('"', '\\"')
1488
- return f'"{escaped}"'
1489
-
1527
+ # Build SKILL.md with manually formatted frontmatter (stable
1528
+ # double-quoted values). yaml_quote escapes newlines and control
1529
+ # characters that a plain quoted f-string cannot carry.
1490
1530
  skill_content = (
1491
1531
  f"---\n"
1492
- f"name: {_quote(skill_name)}\n"
1493
- f"description: {_quote(description)}\n"
1494
- f"compatibility: {_quote('Requires spec-kit project structure with .specify/ directory')}\n"
1532
+ f"name: {yaml_quote(skill_name)}\n"
1533
+ f"description: {yaml_quote(description)}\n"
1534
+ f"compatibility: {yaml_quote('Requires spec-kit project structure with .specify/ directory')}\n"
1495
1535
  f"metadata:\n"
1496
- f" author: {_quote('github-spec-kit')}\n"
1497
- f" source: {_quote('templates/commands/' + src_file.name)}\n"
1536
+ f" author: {yaml_quote('github-spec-kit')}\n"
1537
+ f" source: {yaml_quote('templates/commands/' + src_file.name)}\n"
1498
1538
  f"---\n"
1499
1539
  f"{processed_body}"
1500
1540
  )
@@ -18,7 +18,7 @@ from typing import Any
18
18
 
19
19
  import yaml
20
20
 
21
- from ..base import IntegrationOption, SkillsIntegration
21
+ from ..base import IntegrationOption, SkillsIntegration, yaml_quote
22
22
  from ..manifest import IntegrationManifest
23
23
 
24
24
 
@@ -153,20 +153,18 @@ class HermesIntegration(SkillsIntegration):
153
153
  if not description:
154
154
  description = f"Spec Kit: {command_name} workflow"
155
155
 
156
- # Build SKILL.md with manually formatted frontmatter
157
- def _quote(v: str) -> str:
158
- escaped = v.replace("\\", "\\\\").replace('"', '\\"')
159
- return f'"{escaped}"'
160
-
156
+ # Build SKILL.md with manually formatted frontmatter. yaml_quote
157
+ # escapes newlines and control characters that a plain quoted
158
+ # f-string cannot carry.
161
159
  skill_content = (
162
160
  f"---\n"
163
- f"name: {_quote(skill_name)}\n"
164
- f"description: {_quote(description)}\n"
161
+ f"name: {yaml_quote(skill_name)}\n"
162
+ f"description: {yaml_quote(description)}\n"
165
163
  f"compatibility: "
166
- f"{_quote('Requires spec-kit project structure with .specify/ directory')}\n"
164
+ f"{yaml_quote('Requires spec-kit project structure with .specify/ directory')}\n"
167
165
  f"metadata:\n"
168
- f" author: {_quote('github-spec-kit')}\n"
169
- f" source: {_quote('templates/commands/' + src_file.name)}\n"
166
+ f" author: {yaml_quote('github-spec-kit')}\n"
167
+ f" source: {yaml_quote('templates/commands/' + src_file.name)}\n"
170
168
  f"---\n"
171
169
  f"{processed_body}"
172
170
  )
@@ -328,7 +328,10 @@ def refresh_shared_templates(
328
328
  _ensure_safe_shared_destination(project_path, dst)
329
329
  rel = dst.relative_to(project_path).as_posix()
330
330
  if dst.exists() and not force:
331
- if rel not in tracked_files or rel in modified:
331
+ if rel not in tracked_files or rel in modified or manifest.is_recovered(rel):
332
+ # Never overwrite a recovered (pre-existing user) file without
333
+ # --force, matching install_shared_infra's is_recovered gate
334
+ # (#2918). Without this, refresh clobbers user content.
332
335
  skipped_files.append(rel)
333
336
  continue
334
337
 
@@ -344,7 +347,7 @@ def refresh_shared_templates(
344
347
 
345
348
  if skipped_files:
346
349
  console.print(
347
- f"[yellow]⚠[/yellow] {len(skipped_files)} modified or untracked shared template file(s) were not updated:"
350
+ f"[yellow]⚠[/yellow] {len(skipped_files)} modified, untracked, or preserved (recovered) shared template file(s) were not updated:"
348
351
  )
349
352
  for rel in skipped_files:
350
353
  console.print(f" {rel}")
@@ -242,6 +242,26 @@ def _interpolate_expressions(template: str, namespace: dict[str, Any]) -> str:
242
242
  return "".join(out)
243
243
 
244
244
 
245
+ def _split_top_level(text: str, sep: str) -> list[str]:
246
+ """Split *text* on each occurrence of *sep* that lies outside any quoted
247
+ string or nested brackets.
248
+
249
+ Used to break a filter chain (``a | map('x') | join(',')``) into its
250
+ individual filter segments without splitting on a ``|`` that appears inside
251
+ a quoted argument. Each returned segment is a slice at a top-level
252
+ boundary, so the quote/bracket scan restarts cleanly on the remainder.
253
+ """
254
+ parts: list[str] = []
255
+ start = 0
256
+ while True:
257
+ idx = _find_top_level(text[start:], sep)
258
+ if idx == -1:
259
+ parts.append(text[start:])
260
+ return parts
261
+ parts.append(text[start:start + idx])
262
+ start += idx + len(sep)
263
+
264
+
245
265
  def _split_top_level_commas(text: str) -> list[str]:
246
266
  """Split *text* on commas that are not inside quotes or nested brackets.
247
267
 
@@ -305,6 +325,68 @@ def _find_top_level(text: str, token: str) -> int:
305
325
  return -1
306
326
 
307
327
 
328
+ def _apply_filter(value: Any, filter_expr: str, namespace: dict[str, Any]) -> Any:
329
+ """Apply a single pipe filter segment to *value*.
330
+
331
+ *filter_expr* is one link of a filter chain — the text between two
332
+ top-level ``|`` separators, already stripped (e.g. ``map('name')``,
333
+ ``default('x')``, ``from_json``). Returns the filtered value so the caller
334
+ can feed it into the next link.
335
+
336
+ Raises ``ValueError`` on any mis-wired or unknown filter rather than
337
+ silently returning *value* unchanged: a passthrough would turn a mistyped
338
+ or unsupported filter into a wrong result with no signal.
339
+ """
340
+ # `from_json` is strict: it takes no arguments and tolerates no trailing
341
+ # tokens. Match on the leading filter name and require the whole filter to
342
+ # be exactly `from_json`, so every mis-wired form (`from_json()`,
343
+ # `from_json('x')`, `from_json)`, `from_json extra`) fails loudly instead of
344
+ # silently falling through to the unknown-filter path.
345
+ leading = re.match(r"\w+", filter_expr)
346
+ if leading and leading.group(0) == "from_json":
347
+ if filter_expr != "from_json":
348
+ raise ValueError(
349
+ "from_json: expected '| from_json' with no arguments or "
350
+ f"trailing tokens, got '| {filter_expr}'"
351
+ )
352
+ return _filter_from_json(value)
353
+
354
+ # Parse filter name and argument
355
+ filter_match = re.match(r"(\w+)\((.+)\)", filter_expr)
356
+ if filter_match:
357
+ fname = filter_match.group(1)
358
+ farg = _evaluate_simple_expression(filter_match.group(2).strip(), namespace)
359
+ if fname == "default":
360
+ return _filter_default(value, farg)
361
+ if fname == "join":
362
+ return _filter_join(value, farg)
363
+ if fname == "map":
364
+ return _filter_map(value, farg)
365
+ if fname == "contains":
366
+ return _filter_contains(value, farg)
367
+ # Filter without args
368
+ if filter_expr == "default":
369
+ return _filter_default(value)
370
+ # No recognized filter matched. Fail loudly rather than silently returning
371
+ # the unfiltered value. Distinguish a *registered* filter used in an
372
+ # unsupported form (e.g. `| join` or `| map` with no argument) from a
373
+ # genuinely unknown filter name, so the message names the real problem
374
+ # instead of calling a known filter "unknown".
375
+ name = leading.group(0) if leading else filter_expr
376
+ expected = (
377
+ "expected one of default or default('x'), join('sep'), "
378
+ "map('attr'), contains('s'), or from_json"
379
+ )
380
+ if name in _REGISTERED_FILTERS:
381
+ raise ValueError(
382
+ f"filter '{name}' used in an unsupported form (got "
383
+ f"'| {filter_expr}'): {expected}"
384
+ )
385
+ raise ValueError(
386
+ f"unknown filter '{name}': {expected} (got '| {filter_expr}')"
387
+ )
388
+
389
+
308
390
  def _evaluate_simple_expression(expr: str, namespace: dict[str, Any]) -> Any:
309
391
  """Evaluate a simple expression against the namespace.
310
392
 
@@ -329,65 +411,17 @@ def _evaluate_simple_expression(expr: str, namespace: dict[str, Any]) -> Any:
329
411
  # Handle pipe filters. Detect the pipe at the top level only, so a literal
330
412
  # '|' inside a quoted operand (e.g. `inputs.x == 'a|b'`) or nested brackets is
331
413
  # not mistaken for a filter separator — mirroring the operator parsing below.
414
+ # Filters chain left-to-right: `list | map('name') | join(', ')` feeds each
415
+ # filter's result into the next, so `map` (which yields a list) can be
416
+ # rendered by `join`. Splitting only at the first pipe would hand the whole
417
+ # tail to one filter and mangle any later `|`.
332
418
  pipe_idx = _find_top_level(expr, "|")
333
419
  if pipe_idx != -1:
334
- value = _evaluate_simple_expression(expr[:pipe_idx].strip(), namespace)
335
- filter_expr = expr[pipe_idx + 1:].strip()
336
-
337
- # `from_json` is strict: it takes no arguments and tolerates no
338
- # trailing tokens. Match on the leading filter name and require the
339
- # whole filter to be exactly `from_json`, so every mis-wired form
340
- # (`from_json()`, `from_json('x')`, `from_json)`, `from_json extra`)
341
- # fails loudly instead of silently falling through to the
342
- # unknown-filter path and returning the unparsed value. (filter_expr
343
- # is already stripped above.)
344
- leading = re.match(r"\w+", filter_expr)
345
- if leading and leading.group(0) == "from_json":
346
- if filter_expr != "from_json":
347
- raise ValueError(
348
- "from_json: expected '| from_json' with no arguments or "
349
- f"trailing tokens, got '| {filter_expr}'"
350
- )
351
- return _filter_from_json(value)
352
-
353
- # Parse filter name and argument
354
- filter_match = re.match(r"(\w+)\((.+)\)", filter_expr)
355
- if filter_match:
356
- fname = filter_match.group(1)
357
- farg = _evaluate_simple_expression(filter_match.group(2).strip(), namespace)
358
- if fname == "default":
359
- return _filter_default(value, farg)
360
- if fname == "join":
361
- return _filter_join(value, farg)
362
- if fname == "map":
363
- return _filter_map(value, farg)
364
- if fname == "contains":
365
- return _filter_contains(value, farg)
366
- # Filter without args
367
- filter_name = filter_expr.strip()
368
- if filter_name == "default":
369
- return _filter_default(value)
370
- # No recognized filter matched. Fail loudly rather than silently
371
- # returning the unfiltered value: a passthrough turns a mis-typed or
372
- # unsupported filter into a wrong result with no signal. Mirrors the
373
- # strict `from_json` handling above. Distinguish a *registered* filter
374
- # used in an unsupported form (e.g. `| join` or `| map` with no
375
- # argument) from a genuinely unknown filter name, so the message names
376
- # the real problem instead of calling a known filter "unknown".
377
- leading_name = re.match(r"\w+", filter_expr)
378
- name = leading_name.group(0) if leading_name else filter_expr
379
- expected = (
380
- "expected one of default or default('x'), join('sep'), "
381
- "map('attr'), contains('s'), or from_json"
382
- )
383
- if name in _REGISTERED_FILTERS:
384
- raise ValueError(
385
- f"filter '{name}' used in an unsupported form (got "
386
- f"'| {filter_expr}'): {expected}"
387
- )
388
- raise ValueError(
389
- f"unknown filter '{name}': {expected} (got '| {filter_expr}')"
390
- )
420
+ segments = _split_top_level(expr, "|")
421
+ value = _evaluate_simple_expression(segments[0].strip(), namespace)
422
+ for segment in segments[1:]:
423
+ value = _apply_filter(value, segment.strip(), namespace)
424
+ return value
391
425
 
392
426
  # Boolean operators — parse 'or' first (lower precedence) so that
393
427
  # 'a or b and c' is evaluated as 'a or (b and c)'. Splits are quote/bracket