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.
- {specify_cli-0.12.8 → specify_cli-0.12.9}/CHANGELOG.md +15 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/PKG-INFO +1 -1
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/extensions.md +1 -1
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/catalog.community.json +9 -6
- {specify_cli-0.12.8 → specify_cli-0.12.9}/pyproject.toml +1 -1
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/common.sh +66 -11
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/agents.py +6 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/primitives.py +49 -6
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/base.py +55 -15
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/hermes/__init__.py +9 -11
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/shared_infra.py +5 -2
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/expressions.py +91 -57
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/conftest.py +24 -4
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_base.py +72 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_cli.py +41 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_home_isolation.py +4 -0
- specify_cli-0.12.9/tests/integrations/test_integration_goose.py +85 -0
- specify_cli-0.12.9/tests/integrations/test_skill_frontmatter_quoting.py +111 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_setup_tasks.py +62 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_workflows.py +67 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_primitives.py +84 -0
- specify_cli-0.12.8/tests/integrations/test_integration_goose.py +0 -38
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.devcontainer/devcontainer.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.devcontainer/post-create.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.editorconfig +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.gitattributes +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/CODEOWNERS +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/agent_request.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/bundle_submission.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/extension_submission.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/ISSUE_TEMPLATE/preset_submission.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/aw/actions-lock.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/dependabot.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/skills/add-community-extension/SKILL.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/RELEASE-PROCESS.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-extension.lock.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-extension.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-preset.lock.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/add-community-preset.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-assess.lock.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-assess.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-fix.lock.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-fix.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-test.lock.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/bug-test.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/catalog-assign.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/codeql.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/docs.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/lint.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/publish-pypi.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/release-trigger.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/release.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/stale.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.github/workflows/test.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.gitignore +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.markdownlint-cli2.jsonc +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/.zenodo.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/AGENTS.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/CITATION.cff +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/CODE_OF_CONDUCT.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/CONTRIBUTING.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/DEVELOPMENT.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/LICENSE +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/SECURITY.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/SUPPORT.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/.gitignore +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/bundles.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/friends.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/overview.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/presets.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/community/walkthroughs.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/concepts/complex-features.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/concepts/sdd.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/concepts/spec-persistence.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/docfx.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/guides/evolving-specs.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/guides/monorepo.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/index.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/air-gapped.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/one-time.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/pipx.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/install/uv.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/installation.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/local-development.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/quickstart.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/authentication.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/bundles.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/core.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/extensions.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/integrations.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/overview.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/presets.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/reference/workflows.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/template/public/main.css +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/toc.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/docs/upgrade.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/business-analyst/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/business-analyst/bundle.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/developer/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/developer/bundle.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/product-manager/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/product-manager/bundle.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/security-researcher/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/examples/bundles/security-researcher/bundle.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-API-REFERENCE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-DEVELOPMENT-GUIDE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-PUBLISHING-GUIDE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/EXTENSION-USER-GUIDE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/RFC-EXTENSION-SYSTEM.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/agent-context-config.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/agent-context-defaults.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/commands/speckit.agent-context.update.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/extension.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/agent-context/scripts/python/update_agent_context.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/commands/speckit.bug.assess.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/commands/speckit.bug.fix.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/commands/speckit.bug.test.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/bug/extension.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/catalog.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.commit.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.feature.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.initialize.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.remote.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/commands/speckit.git.validate.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/config-template.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/extension.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/git-config.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/auto-commit.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/create-new-feature-branch.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/git-common.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/bash/initialize-repo.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/git-common.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/selftest/commands/selftest.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/selftest/extension.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/.gitignore +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/CHANGELOG.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/EXAMPLE-README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/LICENSE +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/commands/example.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/config-template.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/extensions/template/extension.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/CONTRIBUTING.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/catalog.community.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/integrations/catalog.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/media/bootstrap-claude-code.gif +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/media/logo_large.webp +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/media/logo_small.webp +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/media/spec-kit-video-header.jpg +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/media/specify_cli.gif +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-April.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-February.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-June.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-March.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/newsletters/2026-May.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/ARCHITECTURE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/PUBLISHING.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/catalog.community.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/catalog.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.constitution.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.implement.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.plan.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.specify.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/commands/speckit.tasks.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/lean/preset.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/commands/speckit.myext.myextcmd.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/commands/speckit.specify.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/preset.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/templates/myext-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/scaffold/templates/spec-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/commands/speckit.specify.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/commands/speckit.wrap-test.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/preset.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/agent-file-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/checklist-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/constitution-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/plan-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/spec-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/presets/self-test/templates/tasks-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/check-prerequisites.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/create-new-feature.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/setup-plan.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/bash/setup-tasks.sh +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/check-prerequisites.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/common.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/create-new-feature.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/setup-plan.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/powershell/setup-tasks.ps1 +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/python/check_prerequisites.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/scripts/python/common.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/spec-driven.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/spec-kit.code-workspace +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_agent_config.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_assets.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_console.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_github_http.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_init_options.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_invocation_style.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_project.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_toml_string.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_utils.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/_version.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/azure_devops.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/base.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/config.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/github.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/authentication/http.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/commands_impl/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/commands_impl/catalog_config.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/project.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/versioning.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/lib/yamlio.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/catalog.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/manifest.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/models/records.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/adapters.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/catalog_stack.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/conflict.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/installer.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/packager.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/references.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/resolver.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/bundler/services/validator.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/catalogs.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/commands/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/commands/bundle/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/commands/init.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/extensions/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/extensions/_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_runtime.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_scaffold.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_state.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integration_status.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_helpers.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_install_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_migrate_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_query_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/_scaffold_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/agy/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/amp/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/auggie/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/bob/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/catalog.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/claude/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/cline/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/codebuddy/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/codex/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/copilot/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/cursor_agent/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/devin/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/firebender/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/forge/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/gemini/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/generic/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/goose/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/junie/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/kilocode/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/kimi/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/kiro_cli/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/lingma/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/manifest.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/omp/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/opencode/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/pi/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/qodercli/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/qwen/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/rovodev/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/shai/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/tabnine/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/trae/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/vibe/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/zcode/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/integrations/zed/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/presets/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/presets/_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/_commands.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/base.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/catalog.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/engine.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/command/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/do_while/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/fan_in/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/fan_out/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/gate/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/if_then/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/init/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/prompt/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/shell/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/switch/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/src/specify_cli/workflows/steps/while_loop/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/checklist-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/analyze.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/checklist.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/clarify.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/constitution.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/converge.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/implement.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/plan.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/specify.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/tasks.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/commands/taskstoissues.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/constitution-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/plan-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/spec-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/tasks-template.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/templates/vscode-settings.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/auth_helpers.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/bundler_helpers.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/conftest.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/contract/test_bundle_cli.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/contract/test_catalog_schema.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/contract/test_manifest_schema.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/bug/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/bug/test_bug_extension.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/git/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/git/test_git_extension.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_agent_context_cli_free.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_extension_agent_context.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_update_agent_context_feature_json.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/extensions/test_update_agent_context_python_parity.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/TESTING.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/plan.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/spec.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/hooks/tasks.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/http_helpers.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_catalog_stack.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_init_install.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_install_flow.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_local_install.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_offline.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integration/test_bundler_security_paths.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/__init__.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_extra_args.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_agy.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_amp.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_auggie.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_markdown.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_skills.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_toml.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_base_yaml.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_bob.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_catalog.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_claude.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_cline.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_codebuddy.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_codex.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_copilot.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_cursor_agent.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_devin.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_firebender.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_forge.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_gemini.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_generic.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_hermes.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_junie.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_kilocode.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_kimi.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_kiro_cli.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_lingma.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_omp.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_opencode.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_pi.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_qodercli.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_qwen.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_rovodev.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_scaffold.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_shai.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_state.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_subcommand.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_tabnine.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_trae.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_vibe.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_zcode.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_integration_zed.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_manifest.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/integrations/test_registry.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/self_upgrade_helpers.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_agent_config_consistency.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_authentication.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_branch_numbering.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_check_prerequisites_paths_only.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_check_prerequisites_python_parity.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_check_tool.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_cli_version.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_commands_package.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_console_imports.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extension_registration.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extension_skills.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extension_update_hardening.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_extensions.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_github_http.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_github_workflows.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_init_dir.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_init_dir_cli.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_live_transient_windows.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_merge.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_post_process.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_presets.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_ps1_encoding.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_registrar_path_traversal.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_detection.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_execution.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_guidance.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_self_upgrade_verification.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_setup_plan_feature_json.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_setup_plan_no_overwrite.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_shared_infra_integrity.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_timestamp_branches.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_upgrade.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_utils.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_utils_assets_imports.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_version_imports.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/test_workflow_run_without_project.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_adapters.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_catalog_config.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_conflict.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_packager.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_records.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_references.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_resolver.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_validator.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/tests/unit/test_bundler_versioning.py +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/ARCHITECTURE.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/PUBLISHING.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/README.md +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/catalog.community.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/catalog.json +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/speckit/workflow.yml +0 -0
- {specify_cli-0.12.8 → specify_cli-0.12.9}/workflows/step-catalog.community.json +0 -0
- {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
|
|
@@ -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
|
|
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-
|
|
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
|
|
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.
|
|
1404
|
-
"download_url": "https://github.com/jasstt/spec-kit-golden-demo/archive/refs/tags/v0.
|
|
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":
|
|
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-
|
|
1432
|
+
"updated_at": "2026-07-07T00:00:00Z"
|
|
1430
1433
|
},
|
|
1431
1434
|
"harness": {
|
|
1432
1435
|
"name": "Research Harness",
|
|
@@ -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
|
|
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 [[ "$
|
|
261
|
-
|
|
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
|
-
|
|
285
|
+
sys.exit(1)
|
|
278
286
|
PY
|
|
279
287
|
); then
|
|
280
|
-
case "$
|
|
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
|
|
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
|
|
40
|
-
pin. When the
|
|
41
|
-
installation proceeds (the
|
|
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
|
|
58
|
-
"pinned version or the
|
|
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
|
-
|
|
596
|
-
|
|
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
|
|
1484
|
-
#
|
|
1485
|
-
#
|
|
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: {
|
|
1493
|
-
f"description: {
|
|
1494
|
-
f"compatibility: {
|
|
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: {
|
|
1497
|
-
f" source: {
|
|
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
|
-
|
|
158
|
-
|
|
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: {
|
|
164
|
-
f"description: {
|
|
161
|
+
f"name: {yaml_quote(skill_name)}\n"
|
|
162
|
+
f"description: {yaml_quote(description)}\n"
|
|
165
163
|
f"compatibility: "
|
|
166
|
-
f"{
|
|
164
|
+
f"{yaml_quote('Requires spec-kit project structure with .specify/ directory')}\n"
|
|
167
165
|
f"metadata:\n"
|
|
168
|
-
f" author: {
|
|
169
|
-
f" source: {
|
|
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
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|