specify-cli 0.16.0__tar.gz → 0.16.1__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.16.0 → specify_cli-0.16.1}/.github/workflows/codeql.yml +2 -2
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/lint.yml +1 -1
- {specify_cli-0.16.0 → specify_cli-0.16.1}/CHANGELOG.md +26 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/PKG-INFO +1 -1
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/core.md +2 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/scripts/python/update_agent_context.py +26 -11
- {specify_cli-0.16.0 → specify_cli-0.16.1}/pyproject.toml +1 -1
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/python/check_prerequisites.py +21 -4
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/python/setup_tasks.py +21 -4
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_download_security.py +21 -3
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_utils.py +2 -2
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/commands/bundle/__init__.py +11 -1
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/commands/init.py +64 -16
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/events.py +9 -1
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/extensions/__init__.py +11 -7
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/catalog.py +10 -1
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/hermes/__init__.py +37 -7
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/kimi/__init__.py +13 -3
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/manifest.py +1 -2
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/presets/__init__.py +20 -3
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/shared_infra.py +47 -2
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/_commands.py +6 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/test_update_agent_context_python_parity.py +69 -33
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integration/test_bundler_local_install.py +44 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_cli.py +93 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_events.py +47 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_base_markdown.py +1 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_base_skills.py +86 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_base_toml.py +1 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_base_yaml.py +1 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_catalog.py +202 -128
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_cline.py +1 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_copilot.py +3 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_generic.py +2 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_skill_frontmatter_quoting.py +63 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_check_prerequisites_python_parity.py +46 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_download_security.py +188 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_extensions.py +25 -0
- specify_cli-0.16.1/tests/test_init_output_markup.py +176 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_presets.py +107 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_setup_tasks_python_parity.py +25 -0
- specify_cli-0.16.1/tests/test_shared_infra_gitignore.py +103 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_workflows.py +51 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.devcontainer/devcontainer.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.devcontainer/post-create.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.editorconfig +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.gitattributes +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/CODEOWNERS +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/agent_request.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/bundle_submission.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/extension_submission.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/ISSUE_TEMPLATE/preset_submission.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/aw/actions-lock.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/dependabot.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/scripts/check_security_requirements.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/security-audit-requirements.txt +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/skills/add-community-extension/SKILL.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/RELEASE-PROCESS.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/add-community-bundle.lock.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/add-community-bundle.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/add-community-extension.lock.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/add-community-extension.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/add-community-preset.lock.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/add-community-preset.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/bug-assess.lock.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/bug-assess.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/bug-fix.lock.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/bug-fix.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/bug-test.lock.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/bug-test.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/catalog-assign.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/docs.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/publish-pypi.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/release-trigger.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/release.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/security.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/stale.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.github/workflows/test.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.gitignore +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.markdownlint-cli2.jsonc +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.pre-commit-config.yaml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/.zenodo.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/AGENTS.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/CITATION.cff +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/CODE_OF_CONDUCT.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/CONTRIBUTING.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/DEVELOPMENT.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/LICENSE +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/README.zh-CN.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/SECURITY.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/SUPPORT.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/bundles/catalog.community.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/.gitignore +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/community/bundles.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/community/extensions.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/community/friends.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/community/overview.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/community/presets.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/community/walkthroughs.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/concepts/complex-features.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/concepts/sdd.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/concepts/spec-of-specs.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/concepts/spec-persistence.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/docfx.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/guides/evolving-specs.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/guides/monorepo.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/index.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/install/air-gapped.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/install/one-time.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/install/pipx.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/install/pypi.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/install/uv.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/installation.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/local-development.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/quickstart.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/agentic-bugfix.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/agentic-sdd.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/authentication.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/bundles.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/extensions.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/integrations.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/overview.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/presets.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/reference/workflows.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/template/public/main.css +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/toc.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/docs/upgrade.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/business-analyst/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/business-analyst/bundle.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/developer/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/developer/bundle.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/product-manager/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/product-manager/bundle.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/security-researcher/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/examples/bundles/security-researcher/bundle.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/EXTENSION-API-REFERENCE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/EXTENSION-DEVELOPMENT-GUIDE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/EXTENSION-PUBLISHING-GUIDE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/EXTENSION-USER-GUIDE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/RFC-EXTENSION-SYSTEM.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/agent-context-config.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/agent-context-defaults.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/commands/speckit.agent-context.update.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/extension.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/commands/speckit.assess.decide.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/commands/speckit.assess.define.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/commands/speckit.assess.intake.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/commands/speckit.assess.research.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/commands/speckit.assess.shape.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/assess/extension.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/bug/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/bug/commands/speckit.bug.assess.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/bug/commands/speckit.bug.fix.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/bug/commands/speckit.bug.test.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/bug/extension.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/catalog.community.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/catalog.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/commands/speckit.git.commit.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/commands/speckit.git.feature.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/commands/speckit.git.initialize.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/commands/speckit.git.remote.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/commands/speckit.git.validate.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/config-template.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/extension.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/git-config.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/bash/auto-commit.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/bash/create-new-feature-branch.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/bash/git-common.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/bash/initialize-repo.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/powershell/git-common.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/python/auto_commit.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/python/create_new_feature_branch.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/python/git_common.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/git/scripts/python/initialize_repo.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/selftest/commands/selftest.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/selftest/extension.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/.gitignore +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/CHANGELOG.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/EXAMPLE-README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/LICENSE +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/commands/example.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/config-template.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/extensions/template/extension.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/integrations/CONTRIBUTING.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/integrations/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/integrations/catalog.community.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/integrations/catalog.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/media/bootstrap-claude-code.gif +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/media/logo_large.webp +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/media/logo_small.webp +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/media/spec-kit-video-header.jpg +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/media/specify_cli.gif +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/newsletters/2026-April.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/newsletters/2026-February.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/newsletters/2026-July.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/newsletters/2026-June.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/newsletters/2026-March.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/newsletters/2026-May.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/ARCHITECTURE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/PUBLISHING.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/catalog.community.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/catalog.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/constitution-sync/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/constitution-sync/commands/speckit.constitution.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/constitution-sync/preset.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/commands/speckit.constitution.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/commands/speckit.implement.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/commands/speckit.plan.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/commands/speckit.specify.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/commands/speckit.tasks.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/lean/preset.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/scaffold/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/scaffold/commands/speckit.myext.myextcmd.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/scaffold/commands/speckit.specify.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/scaffold/preset.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/scaffold/templates/myext-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/scaffold/templates/spec-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/commands/speckit.specify.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/commands/speckit.wrap-test.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/preset.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/templates/checklist-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/templates/constitution-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/templates/plan-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/templates/spec-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/presets/self-test/templates/tasks-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/bash/check-prerequisites.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/bash/common.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/bash/create-new-feature.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/bash/setup-plan.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/bash/setup-tasks.sh +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/powershell/check-prerequisites.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/powershell/common.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/powershell/create-new-feature.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/powershell/setup-plan.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/powershell/setup-tasks.ps1 +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/python/common.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/python/create_new_feature.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/scripts/python/setup_plan.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/spec-driven.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/spec-kit.code-workspace +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_agent_config.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_assets.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_console.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_github_http.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_init_options.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_invocation_style.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_project.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_toml_string.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/_version.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/agents.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/authentication/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/authentication/azure_devops.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/authentication/base.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/authentication/config.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/authentication/github.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/authentication/http.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/commands_impl/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/commands_impl/catalog_config.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/lib/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/lib/project.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/lib/versioning.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/lib/yamlio.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/models/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/models/catalog.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/models/manifest.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/models/records.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/adapters.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/catalog_stack.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/conflict.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/installer.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/packager.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/primitives.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/references.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/resolver.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/bundler/services/validator.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/catalogs.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/commands/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/commands/event.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/extensions/_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integration_runtime.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integration_scaffold.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integration_state.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integration_status.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/_helpers.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/_install_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/_migrate_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/_query_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/_scaffold_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/agy/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/alquimia/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/amp/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/auggie/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/base.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/bob/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/claude/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/cline/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/codebuddy/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/codex/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/copilot/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/cursor_agent/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/devin/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/droid/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/firebender/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/forge/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/gemini/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/generic/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/goose/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/grok/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/junie/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/kilocode/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/kiro_cli/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/lingma/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/omp/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/opencode/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/pi/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/qodercli/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/qwen/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/rovodev/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/shai/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/tabnine/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/trae/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/vibe/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/zcode/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/integrations/zed/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/presets/_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/base.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/catalog.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/engine.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/expressions.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/overlays/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/overlays/_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/overlays/composer.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/overlays/layer_sources.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/overlays/merge.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/overlays/schema.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/command/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/do_while/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/fan_in/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/fan_out/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/gate/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/if_then/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/init/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/prompt/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/shell/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/switch/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/src/specify_cli/workflows/steps/while_loop/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/checklist-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/analyze.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/checklist.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/clarify.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/constitution.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/converge.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/implement.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/plan.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/specify.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/tasks.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/commands/taskstoissues.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/constitution-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/plan-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/spec-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/tasks-template.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/templates/vscode-settings.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/auth_helpers.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/bundler_helpers.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/conftest.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/contract/test_bundle_cli.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/contract/test_catalog_schema.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/contract/test_manifest_schema.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/contract/test_wheel_bundled_presets.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/contract/test_wheel_core_pack_scripts.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/assess/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/assess/test_assess_extension.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/bug/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/bug/test_bug_extension.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/git/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/git/test_git_extension.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/git/test_git_extension_python_parity.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/test_agent_context_cli_free.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/test_extension_agent_context.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/extensions/test_update_agent_context_feature_json.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/hooks/TESTING.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/hooks/plan.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/hooks/spec.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/hooks/tasks.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/http_helpers.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integration/test_bundler_catalog_stack.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integration/test_bundler_init_install.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integration/test_bundler_install_flow.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integration/test_bundler_offline.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integration/test_bundler_security_paths.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/__init__.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/conftest.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_base.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_extra_args.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_home_isolation.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_agy.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_alquimia.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_amp.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_auggie.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_bob.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_claude.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_codebuddy.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_codex.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_cursor_agent.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_devin.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_droid.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_firebender.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_forge.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_gemini.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_goose.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_grok.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_hermes.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_junie.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_kilocode.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_kimi.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_kiro_cli.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_lingma.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_omp.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_opencode.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_pi.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_qodercli.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_qwen.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_rovodev.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_scaffold.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_shai.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_state.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_subcommand.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_tabnine.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_trae.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_vibe.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_zcode.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_integration_zed.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_manifest.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/integrations/test_registry.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/parity_helpers.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/self_upgrade_helpers.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_agent_config_consistency.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_authentication.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_branch_numbering.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_check_prerequisites_paths_only.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_check_tool.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_cli_version.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_command_template_py_scripts.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_commands_package.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_console_imports.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_create_new_feature_python_parity.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_extension_add_path_traversal.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_extension_registration.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_extension_skills.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_extension_update_hardening.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_github_http.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_github_workflows.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_init_dir.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_init_dir_cli.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_live_transient_windows.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_merge.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_post_process.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_ps1_encoding.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_registrar_path_traversal.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_security_workflow.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_self_upgrade_detection.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_self_upgrade_execution.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_self_upgrade_guidance.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_self_upgrade_verification.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_setup_plan_feature_json.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_setup_plan_no_overwrite.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_setup_plan_python_parity.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_setup_tasks.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_shared_infra_integrity.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_skill_placeholder_py.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_specify_template_numbering.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_timestamp_branches.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_upgrade.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_utils.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_utils_assets_imports.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_version_imports.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/test_workflow_run_without_project.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundle_download_url.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_adapters.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_catalog_config.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_conflict.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_packager.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_primitives.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_records.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_references.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_resolver.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_validator.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_versioning.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/unit/test_bundler_yamlio.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/conftest.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_overlay_commands.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_overlay_composer.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_overlay_layer_sources.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_overlay_merge.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_overlay_schema.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_overlay_security.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/tests/workflows/test_resolver_integration.py +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/ARCHITECTURE.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/PUBLISHING.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/README.md +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/catalog.community.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/catalog.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/speckit/workflow.yml +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/step-catalog.community.json +0 -0
- {specify_cli-0.16.0 → specify_cli-0.16.1}/workflows/step-catalog.json +0 -0
|
@@ -22,11 +22,11 @@ jobs:
|
|
|
22
22
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
23
|
|
|
24
24
|
- name: Initialize CodeQL
|
|
25
|
-
uses: github/codeql-action/init@
|
|
25
|
+
uses: github/codeql-action/init@d1ba80a13dd99fba24a470575428917156a28b43 # v4
|
|
26
26
|
with:
|
|
27
27
|
languages: ${{ matrix.language }}
|
|
28
28
|
|
|
29
29
|
- name: Perform CodeQL Analysis
|
|
30
|
-
uses: github/codeql-action/analyze@
|
|
30
|
+
uses: github/codeql-action/analyze@d1ba80a13dd99fba24a470575428917156a28b43 # v4
|
|
31
31
|
with:
|
|
32
32
|
category: "/language:${{ matrix.language }}"
|
|
@@ -37,7 +37,7 @@ jobs:
|
|
|
37
37
|
fi
|
|
38
38
|
|
|
39
39
|
- name: Run markdownlint-cli2
|
|
40
|
-
uses: DavidAnson/markdownlint-cli2-action@
|
|
40
|
+
uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
|
|
41
41
|
with:
|
|
42
42
|
globs: |
|
|
43
43
|
'**/*.md'
|
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- insert new changelog below this comment -->
|
|
4
4
|
|
|
5
|
+
## [0.16.1] - 2026-08-07
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- fix(integrations): wrap a non-UTF-8 catalog response (#4011)
|
|
10
|
+
- fix(events): skip an unreadable command template (#3956)
|
|
11
|
+
- fix(bundle): wrap malformed YAML in a local .zip bundle manifest (#4013)
|
|
12
|
+
- chore(deps): bump github/codeql-action/analyze from 4.37.3 to 4.37.5 (#4005)
|
|
13
|
+
- chore(deps): bump DavidAnson/markdownlint-cli2-action (#4006)
|
|
14
|
+
- fix(agent-context): recurse for nested plans in Python mtime fallback (#3757)
|
|
15
|
+
- fix: add utf-8 encoding to extension and preset registry file I/O (#3834)
|
|
16
|
+
- fix(init): escape user-supplied values in `specify init` output (#3787)
|
|
17
|
+
- fix: bound response read in integration catalog fetch (#3812)
|
|
18
|
+
- fix: use missing_ok for temp file cleanup to avoid masking errors (#3803)
|
|
19
|
+
- fix(workflows): handle an unreadable run state in `workflow status` (#3999)
|
|
20
|
+
- feat(init): scaffold managed .specify/.gitignore (#4000)
|
|
21
|
+
- fix(scripts): stop check-prerequisites text mode crashing on a legacy stdout code page (#3890)
|
|
22
|
+
- fix(presets): return None for an unreadable layer in resolve_content (#3959)
|
|
23
|
+
- fix(extensions): start fresh on a non-UTF-8 extension registry (#3998)
|
|
24
|
+
- Fix init-force-preset-desync: reapply presets/extensions on init --here --force (#3995)
|
|
25
|
+
- fix(skills): apply the line-anchored delimiter scan to hermes and kimi (#3739)
|
|
26
|
+
- fix(archives): wrap the bare EOFError a truncated tar.gz raises (#3938)
|
|
27
|
+
- test(integrations): guard multiline/control-char SKILL.md frontmatter escaping (#3392)
|
|
28
|
+
- fix(scripts): stop setup-tasks text mode crashing on a legacy code page (#3892)
|
|
29
|
+
- chore: release 0.16.0, begin 0.16.1.dev0 development (#3992)
|
|
30
|
+
|
|
5
31
|
## [0.16.0] - 2026-08-05
|
|
6
32
|
|
|
7
33
|
### Changed
|
|
@@ -57,6 +57,8 @@ specify init my-project --integration copilot --preset compliance
|
|
|
57
57
|
|
|
58
58
|
> **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature.
|
|
59
59
|
|
|
60
|
+
> **Version control.** `specify init` scaffolds a managed `.specify/.gitignore` that excludes machine-local state — `feature.json` (the current-feature pointer, rewritten on every feature switch) and per-machine extension `extensions/*/local-config.yml` overrides — while leaving everything else under `.specify/` (constitution, templates, scripts, extension config) shareable so teams stay aligned. Like the rest of `.specify/`'s shared scripts and templates, the file is tracked in the shared-infrastructure manifest: your edits are preserved on re-init and `specify init --here --force` restores the managed content. It is intentionally left in place by `specify integration uninstall`, which only removes the uninstalled agent's own files.
|
|
61
|
+
|
|
60
62
|
> **Symlinked project roots.** `SPECIFY_INIT_DIR` relocates *where* the project is, not *how* a command treats symlinks: each command keeps its existing cwd-path stance. Commands that traverse and write project files through broad input paths (`bundle`, `workflow run <file>`) refuse a symlinked `.specify/` to preserve write confinement. Other project-scoped commands keep their existing behavior when `SPECIFY_INIT_DIR` points at a project root, which may include following a symlinked `.specify/`.
|
|
61
63
|
|
|
62
64
|
## Check Installed Tools
|
|
@@ -11,8 +11,8 @@ Usage: update_agent_context.py [plan_path]
|
|
|
11
11
|
|
|
12
12
|
When ``plan_path`` is omitted, the script derives it from
|
|
13
13
|
``.specify/feature.json`` (written by /speckit-specify). Falls back to the most
|
|
14
|
-
recently modified ``plan.md`` anywhere under ``specs/``
|
|
15
|
-
|
|
14
|
+
recently modified ``plan.md`` found anywhere under ``specs/`` — scoped layouts
|
|
15
|
+
nest it as ``specs/<scope>/<feature>/plan.md`` — only when feature.json is
|
|
16
16
|
absent or its plan does not exist yet.
|
|
17
17
|
"""
|
|
18
18
|
|
|
@@ -173,16 +173,31 @@ def _resolve_plan_path(project_root: str) -> str:
|
|
|
173
173
|
|
|
174
174
|
if not plan_path:
|
|
175
175
|
root = Path(project_root).resolve()
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
specs = root / "specs"
|
|
177
|
+
|
|
178
|
+
def _resolved_rel(p: Path) -> Path | None:
|
|
179
|
+
# Resolve symlinks before checking containment: relative_to() is
|
|
180
|
+
# lexical and would otherwise accept a plan reached through a specs/
|
|
181
|
+
# symlink that points outside the project, emitting an
|
|
182
|
+
# in-project-looking path for an out-of-project file (or picking it
|
|
183
|
+
# as "most recent").
|
|
182
184
|
try:
|
|
183
|
-
|
|
184
|
-
except ValueError:
|
|
185
|
-
|
|
185
|
+
return p.resolve().relative_to(root)
|
|
186
|
+
except (OSError, ValueError):
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
# Recurse (rather than the old one-level specs/*/plan.md glob) so scoped
|
|
190
|
+
# layouts created via SPECIFY_FEATURE_DIRECTORY, e.g.
|
|
191
|
+
# specs/<scope>/<feature>/plan.md, are still discovered when
|
|
192
|
+
# feature.json is absent (#3024). Mirrors the bash and PowerShell twins.
|
|
193
|
+
candidates = []
|
|
194
|
+
for p in specs.rglob("plan.md"):
|
|
195
|
+
rel = _resolved_rel(p)
|
|
196
|
+
if rel is not None:
|
|
197
|
+
candidates.append((p, rel))
|
|
198
|
+
candidates.sort(key=lambda pr: pr[0].stat().st_mtime, reverse=True)
|
|
199
|
+
if candidates:
|
|
200
|
+
plan_path = candidates[0][1].as_posix()
|
|
186
201
|
return plan_path
|
|
187
202
|
|
|
188
203
|
|
|
@@ -130,14 +130,31 @@ def _print_paths_only(paths: FeaturePaths, json_mode: bool) -> None:
|
|
|
130
130
|
print(f"TASKS: {paths.tasks}")
|
|
131
131
|
|
|
132
132
|
|
|
133
|
+
def _status_marker(ok: bool) -> str:
|
|
134
|
+
"""Return the status glyph, downgraded to ASCII when stdout cannot encode it.
|
|
135
|
+
|
|
136
|
+
On Windows sys.stdout falls back to the ANSI code page whenever it is not a
|
|
137
|
+
console - a pipe or a file redirect, which is how agents and workflow steps
|
|
138
|
+
invoke these scripts - and U+2713 is unencodable in cp1252, so printing it
|
|
139
|
+
raised UnicodeEncodeError and aborted the report right after
|
|
140
|
+
"AVAILABLE_DOCS:". "[OK]"/"[FAIL]" is the ASCII rendering these markers
|
|
141
|
+
already have in-tree: see Test-FileExists in scripts/powershell/common.ps1
|
|
142
|
+
and normalize_status_text in tests/parity_helpers.py.
|
|
143
|
+
"""
|
|
144
|
+
glyph = "✓" if ok else "✗"
|
|
145
|
+
try:
|
|
146
|
+
glyph.encode(getattr(sys.stdout, "encoding", None) or "utf-8")
|
|
147
|
+
except (LookupError, UnicodeEncodeError):
|
|
148
|
+
return "[OK]" if ok else "[FAIL]"
|
|
149
|
+
return glyph
|
|
150
|
+
|
|
151
|
+
|
|
133
152
|
def _check_file(path: Path, description: str) -> None:
|
|
134
|
-
|
|
135
|
-
print(f" {marker} {description}")
|
|
153
|
+
print(f" {_status_marker(path.is_file())} {description}")
|
|
136
154
|
|
|
137
155
|
|
|
138
156
|
def _check_dir(path: Path, description: str) -> None:
|
|
139
|
-
|
|
140
|
-
print(f" {marker} {description}")
|
|
157
|
+
print(f" {_status_marker(_dir_has_entries(path))} {description}")
|
|
141
158
|
|
|
142
159
|
|
|
143
160
|
def _print_text_results(paths: FeaturePaths, include_tasks: bool) -> None:
|
|
@@ -55,14 +55,31 @@ def _available_docs(paths: FeaturePaths) -> list[str]:
|
|
|
55
55
|
return docs
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def _status_marker(ok: bool) -> str:
|
|
59
|
+
"""Return the status glyph, downgraded to ASCII when stdout cannot encode it.
|
|
60
|
+
|
|
61
|
+
On Windows sys.stdout falls back to the ANSI code page whenever it is not a
|
|
62
|
+
console - a pipe or a file redirect, which is how agents and workflow steps
|
|
63
|
+
invoke these scripts - and U+2713 is unencodable in cp1252, so printing it
|
|
64
|
+
raised UnicodeEncodeError and aborted the report mid-listing.
|
|
65
|
+
"[OK]"/"[FAIL]" is the ASCII rendering these markers already have in-tree:
|
|
66
|
+
see Test-FileExists in scripts/powershell/common.ps1 and
|
|
67
|
+
normalize_status_text in tests/parity_helpers.py.
|
|
68
|
+
"""
|
|
69
|
+
glyph = "✓" if ok else "✗"
|
|
70
|
+
try:
|
|
71
|
+
glyph.encode(getattr(sys.stdout, "encoding", None) or "utf-8")
|
|
72
|
+
except (LookupError, UnicodeEncodeError):
|
|
73
|
+
return "[OK]" if ok else "[FAIL]"
|
|
74
|
+
return glyph
|
|
75
|
+
|
|
76
|
+
|
|
58
77
|
def _check_file(path: Path, description: str) -> None:
|
|
59
|
-
|
|
60
|
-
print(f" {marker} {description}")
|
|
78
|
+
print(f" {_status_marker(path.is_file())} {description}")
|
|
61
79
|
|
|
62
80
|
|
|
63
81
|
def _check_dir(path: Path, description: str) -> None:
|
|
64
|
-
|
|
65
|
-
print(f" {marker} {description}")
|
|
82
|
+
print(f" {_status_marker(_dir_has_entries(path))} {description}")
|
|
66
83
|
|
|
67
84
|
|
|
68
85
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -10,6 +10,7 @@ import struct
|
|
|
10
10
|
import tarfile
|
|
11
11
|
import unicodedata
|
|
12
12
|
import zipfile
|
|
13
|
+
import zlib
|
|
13
14
|
from collections.abc import Iterator
|
|
14
15
|
from contextlib import ExitStack, contextmanager
|
|
15
16
|
from ipaddress import IPv4Address, IPv6Address, ip_address
|
|
@@ -69,6 +70,19 @@ _ZIP_MAX_COMMENT_BYTES = (1 << 16) - 1
|
|
|
69
70
|
_BOUNDED_ZIP_COMPRESSION_METHODS = frozenset(
|
|
70
71
|
(zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED)
|
|
71
72
|
)
|
|
73
|
+
#: Decompression failures a truncated or corrupt gzip stream raises from
|
|
74
|
+
#: ``tarfile``. Most are wrapped in ``TarError``, but two escape raw, and
|
|
75
|
+
#: neither derives from ``TarError`` or ``OSError``, so both bypass a
|
|
76
|
+
#: ``(TarError, OSError)`` handler:
|
|
77
|
+
#:
|
|
78
|
+
#: * ``EOFError`` -- from the gzip layer when the stream ends before its
|
|
79
|
+
#: end-of-stream marker, i.e. a truncated archive.
|
|
80
|
+
#: * ``zlib.error`` -- from a corrupt deflate block. ``tarfile`` converts this
|
|
81
|
+
#: to ``ReadError`` while reading a member *header*, but the forward seek it
|
|
82
|
+
#: performs to skip member *data* sits outside that conversion, so a corrupt
|
|
83
|
+
#: region past the first header escapes raw.
|
|
84
|
+
_TAR_DECOMPRESSION_ERRORS = (tarfile.TarError, EOFError, zlib.error)
|
|
85
|
+
|
|
72
86
|
_ARCHIVE_CONTENT_TYPES: dict[str, ArchiveFormat] = {
|
|
73
87
|
"application/gzip": "tar.gz",
|
|
74
88
|
"application/x-gzip": "tar.gz",
|
|
@@ -166,7 +180,11 @@ def detect_archive_format(
|
|
|
166
180
|
try:
|
|
167
181
|
with tarfile.open(fileobj=archive_file, mode="r:gz"):
|
|
168
182
|
is_tar_gz = True
|
|
169
|
-
except
|
|
183
|
+
except _TAR_DECOMPRESSION_ERRORS:
|
|
184
|
+
# A truncated gzip stream raises a bare EOFError here rather
|
|
185
|
+
# than a TarError, so catching only TarError let it escape
|
|
186
|
+
# this probe as a raw exception instead of leaving
|
|
187
|
+
# ``is_tar_gz`` False and reporting the format mismatch.
|
|
170
188
|
pass
|
|
171
189
|
archive_file.seek(0)
|
|
172
190
|
except OSError as exc:
|
|
@@ -1077,7 +1095,7 @@ def safe_extract_tar(
|
|
|
1077
1095
|
mode="r:gz",
|
|
1078
1096
|
fileobj=archive_file,
|
|
1079
1097
|
)
|
|
1080
|
-
except (
|
|
1098
|
+
except (*_TAR_DECOMPRESSION_ERRORS, OSError) as exc:
|
|
1081
1099
|
_raise_from(error_type, f"Invalid tar.gz archive: {archive_path}", exc)
|
|
1082
1100
|
|
|
1083
1101
|
with archive:
|
|
@@ -1149,7 +1167,7 @@ def safe_extract_tar(
|
|
|
1149
1167
|
f"of {max_total_bytes} bytes",
|
|
1150
1168
|
)
|
|
1151
1169
|
validated.append((member, normalized_name, is_dir))
|
|
1152
|
-
except (
|
|
1170
|
+
except (*_TAR_DECOMPRESSION_ERRORS, OSError) as exc:
|
|
1153
1171
|
_raise_from(
|
|
1154
1172
|
error_type,
|
|
1155
1173
|
f"Invalid tar.gz archive: {archive_path}",
|
|
@@ -192,8 +192,8 @@ def handle_vscode_settings(sub_item, dest_file, rel_path, verbose=False, tracker
|
|
|
192
192
|
|
|
193
193
|
os.replace(temp_path, target_file)
|
|
194
194
|
except Exception:
|
|
195
|
-
if temp_path
|
|
196
|
-
temp_path.unlink()
|
|
195
|
+
if temp_path:
|
|
196
|
+
temp_path.unlink(missing_ok=True)
|
|
197
197
|
raise
|
|
198
198
|
|
|
199
199
|
try:
|
|
@@ -771,7 +771,17 @@ def _local_manifest_source(arg: str):
|
|
|
771
771
|
error_type=BundlerError,
|
|
772
772
|
label="bundle manifest",
|
|
773
773
|
)
|
|
774
|
-
|
|
774
|
+
try:
|
|
775
|
+
data = _yaml.safe_load(io.BytesIO(raw))
|
|
776
|
+
except _yaml.YAMLError as exc:
|
|
777
|
+
# The sibling directory/bundle.yml branches reach YAML through
|
|
778
|
+
# load_yaml(), which turns a parse failure into a BundlerError. This
|
|
779
|
+
# branch parses inline, so without this it raises a raw YAMLError --
|
|
780
|
+
# neither a ValueError nor an OSError -- which escapes
|
|
781
|
+
# bundle_install()'s `except BundlerError` as a traceback.
|
|
782
|
+
raise BundlerError(
|
|
783
|
+
f"Invalid YAML in bundle.yml inside '{candidate}': {exc}"
|
|
784
|
+
) from exc
|
|
775
785
|
return BundleManifest.from_dict(data)
|
|
776
786
|
|
|
777
787
|
if candidate.name == "bundle.yml" or candidate.suffix in (".yml", ".yaml"):
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import shlex
|
|
6
7
|
import shutil
|
|
8
|
+
import subprocess
|
|
7
9
|
import sys
|
|
8
10
|
from pathlib import Path
|
|
9
11
|
from typing import Any
|
|
10
12
|
|
|
11
13
|
import typer
|
|
12
14
|
from rich.live import Live
|
|
15
|
+
from rich.markup import escape as _escape_markup
|
|
13
16
|
from rich.panel import Panel
|
|
14
17
|
|
|
15
18
|
from .._agent_config import (
|
|
@@ -169,6 +172,25 @@ def _install_extension_during_init(project_path: Path, ext_spec: str, speckit_ve
|
|
|
169
172
|
return f"{manifest.name} v{manifest.version} installed"
|
|
170
173
|
|
|
171
174
|
|
|
175
|
+
def _shell_quote_arg(value: str) -> str:
|
|
176
|
+
"""Quote *value* as one argument for the shells of the host OS.
|
|
177
|
+
|
|
178
|
+
The Next Steps ``cd`` line is copy-pasted into whichever shell ran
|
|
179
|
+
``specify init``, so it is quoted for the host the same way
|
|
180
|
+
``_version._render_argv`` renders its copy-pasteable installer command:
|
|
181
|
+
``list2cmdline`` on Windows, ``shlex.quote`` elsewhere. The Windows branch
|
|
182
|
+
must emit double quotes -- ``cd 'my project'`` is a path-not-found in
|
|
183
|
+
cmd.exe, while ``cd "my project"`` is accepted by cmd.exe, PowerShell and
|
|
184
|
+
Git Bash alike. A value needing no quoting is returned unchanged.
|
|
185
|
+
|
|
186
|
+
Whitespace only. PowerShell also glob-expands ``[``/``]`` and expands
|
|
187
|
+
``$``/backtick inside double quotes, so such a name still needs
|
|
188
|
+
``Set-Location -LiteralPath`` there -- syntax invalid in cmd.exe and sh, so
|
|
189
|
+
this shell-neutral line cannot cover it.
|
|
190
|
+
"""
|
|
191
|
+
return subprocess.list2cmdline([value]) if os.name == "nt" else shlex.quote(value)
|
|
192
|
+
|
|
193
|
+
|
|
172
194
|
def ensure_constitution_from_template(
|
|
173
195
|
project_path: Path, tracker: StepTracker | None = None
|
|
174
196
|
) -> None:
|
|
@@ -351,7 +373,10 @@ def register(app: typer.Typer) -> None:
|
|
|
351
373
|
if integration:
|
|
352
374
|
resolved_integration = get_integration(integration)
|
|
353
375
|
if not resolved_integration:
|
|
354
|
-
console.print(
|
|
376
|
+
console.print(
|
|
377
|
+
f"[red]Error:[/red] Unknown integration: "
|
|
378
|
+
f"'{_escape_markup(str(integration))}'"
|
|
379
|
+
)
|
|
355
380
|
available = ", ".join(sorted(INTEGRATION_REGISTRY))
|
|
356
381
|
console.print(f"[yellow]Available integrations:[/yellow] {available}")
|
|
357
382
|
raise typer.Exit(1)
|
|
@@ -428,26 +453,27 @@ def register(app: typer.Typer) -> None:
|
|
|
428
453
|
project_path = Path(project_name).resolve()
|
|
429
454
|
dir_existed_before = project_path.exists()
|
|
430
455
|
if project_path.exists():
|
|
456
|
+
safe_name = _escape_markup(str(project_name))
|
|
431
457
|
if not project_path.is_dir():
|
|
432
458
|
console.print(
|
|
433
|
-
f"[red]Error:[/red] '{
|
|
459
|
+
f"[red]Error:[/red] '{safe_name}' exists but is not a directory."
|
|
434
460
|
)
|
|
435
461
|
raise typer.Exit(1)
|
|
436
462
|
existing_items = list(project_path.iterdir())
|
|
437
463
|
if force:
|
|
438
464
|
if existing_items:
|
|
439
465
|
console.print(
|
|
440
|
-
f"[yellow]Warning:[/yellow] Directory '{
|
|
466
|
+
f"[yellow]Warning:[/yellow] Directory '{safe_name}' is not empty ({len(existing_items)} items)"
|
|
441
467
|
)
|
|
442
468
|
console.print(
|
|
443
469
|
"[yellow]Template files will be merged with existing content and may overwrite existing files[/yellow]"
|
|
444
470
|
)
|
|
445
471
|
console.print(
|
|
446
|
-
f"[cyan]--force supplied: merging into existing directory '[cyan]{
|
|
472
|
+
f"[cyan]--force supplied: merging into existing directory '[cyan]{safe_name}[/cyan]'[/cyan]"
|
|
447
473
|
)
|
|
448
474
|
else:
|
|
449
475
|
error_panel = Panel(
|
|
450
|
-
f"Directory already exists: '[cyan]{
|
|
476
|
+
f"Directory already exists: '[cyan]{safe_name}[/cyan]'\n"
|
|
451
477
|
"Please choose a different project name or remove the existing directory.\n"
|
|
452
478
|
"Use [bold]--force[/bold] to merge into the existing directory.",
|
|
453
479
|
title="[red]Directory Conflict[/red]",
|
|
@@ -461,7 +487,7 @@ def register(app: typer.Typer) -> None:
|
|
|
461
487
|
if integration:
|
|
462
488
|
if integration not in AGENT_CONFIG:
|
|
463
489
|
console.print(
|
|
464
|
-
f"[red]Error:[/red] Invalid integration '{integration}'. Choose from: {', '.join(AGENT_CONFIG.keys())}"
|
|
490
|
+
f"[red]Error:[/red] Invalid integration '{_escape_markup(str(integration))}'. Choose from: {', '.join(AGENT_CONFIG.keys())}"
|
|
465
491
|
)
|
|
466
492
|
raise typer.Exit(1)
|
|
467
493
|
selected_ai = integration
|
|
@@ -500,12 +526,14 @@ def register(app: typer.Typer) -> None:
|
|
|
500
526
|
setup_lines = [
|
|
501
527
|
"[cyan]Specify Project Setup[/cyan]",
|
|
502
528
|
"",
|
|
503
|
-
f"{'Project':<15} [green]{project_path.name}[/green]",
|
|
504
|
-
f"{'Working Path':<15} [dim]{current_dir}[/dim]",
|
|
529
|
+
f"{'Project':<15} [green]{_escape_markup(project_path.name)}[/green]",
|
|
530
|
+
f"{'Working Path':<15} [dim]{_escape_markup(str(current_dir))}[/dim]",
|
|
505
531
|
]
|
|
506
532
|
|
|
507
533
|
if not here:
|
|
508
|
-
setup_lines.append(
|
|
534
|
+
setup_lines.append(
|
|
535
|
+
f"{'Target Path':<15} [dim]{_escape_markup(str(project_path))}[/dim]"
|
|
536
|
+
)
|
|
509
537
|
|
|
510
538
|
console.print(
|
|
511
539
|
Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2))
|
|
@@ -532,7 +560,7 @@ def register(app: typer.Typer) -> None:
|
|
|
532
560
|
if script_type:
|
|
533
561
|
if script_type not in SCRIPT_TYPE_CHOICES:
|
|
534
562
|
console.print(
|
|
535
|
-
f"[red]Error:[/red] Invalid script type '{script_type}'. Choose from: {', '.join(SCRIPT_TYPE_CHOICES.keys())}"
|
|
563
|
+
f"[red]Error:[/red] Invalid script type '{_escape_markup(str(script_type))}'. Choose from: {', '.join(SCRIPT_TYPE_CHOICES.keys())}"
|
|
536
564
|
)
|
|
537
565
|
raise typer.Exit(1)
|
|
538
566
|
selected_script = script_type
|
|
@@ -571,8 +599,6 @@ def register(app: typer.Typer) -> None:
|
|
|
571
599
|
tracker.add(key, label)
|
|
572
600
|
|
|
573
601
|
if extensions:
|
|
574
|
-
from rich.markup import escape as _escape_markup
|
|
575
|
-
|
|
576
602
|
for i, ext_spec in enumerate(extensions):
|
|
577
603
|
tracker.add(
|
|
578
604
|
f"extension-{i}", f"Install extension: {_escape_markup(ext_spec)}"
|
|
@@ -635,6 +661,30 @@ def register(app: typer.Typer) -> None:
|
|
|
635
661
|
)
|
|
636
662
|
manifest.save()
|
|
637
663
|
|
|
664
|
+
if force:
|
|
665
|
+
from ..integrations._helpers import (
|
|
666
|
+
_register_extensions_for_agent,
|
|
667
|
+
_register_presets_for_agent,
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
_register_extensions_for_agent(
|
|
671
|
+
project_path,
|
|
672
|
+
resolved_integration.key,
|
|
673
|
+
force=True,
|
|
674
|
+
continuing=(
|
|
675
|
+
"The project was re-initialized, but installed extensions"
|
|
676
|
+
" may need re-registration."
|
|
677
|
+
),
|
|
678
|
+
)
|
|
679
|
+
_register_presets_for_agent(
|
|
680
|
+
project_path,
|
|
681
|
+
resolved_integration.key,
|
|
682
|
+
continuing=(
|
|
683
|
+
"The project was re-initialized, but installed presets"
|
|
684
|
+
" may need re-registration."
|
|
685
|
+
),
|
|
686
|
+
)
|
|
687
|
+
|
|
638
688
|
integration_settings = _with_integration_setting(
|
|
639
689
|
{},
|
|
640
690
|
resolved_integration.key,
|
|
@@ -803,8 +853,6 @@ def register(app: typer.Typer) -> None:
|
|
|
803
853
|
|
|
804
854
|
# Install extensions specified via --extension
|
|
805
855
|
if extensions:
|
|
806
|
-
from rich.markup import escape as _escape_markup
|
|
807
|
-
|
|
808
856
|
from ..extensions._commands import _refresh_events_and_warn
|
|
809
857
|
|
|
810
858
|
speckit_ver = get_speckit_version()
|
|
@@ -894,7 +942,7 @@ def register(app: typer.Typer) -> None:
|
|
|
894
942
|
if agent_folder:
|
|
895
943
|
security_notice = Panel(
|
|
896
944
|
f"Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project.\n"
|
|
897
|
-
f"Consider adding [cyan]{agent_folder}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.",
|
|
945
|
+
f"Consider adding [cyan]{_escape_markup(str(agent_folder))}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.",
|
|
898
946
|
title="[yellow]Agent Folder Security[/yellow]",
|
|
899
947
|
border_style="yellow",
|
|
900
948
|
padding=(1, 2),
|
|
@@ -905,7 +953,7 @@ def register(app: typer.Typer) -> None:
|
|
|
905
953
|
steps_lines = []
|
|
906
954
|
if not here:
|
|
907
955
|
steps_lines.append(
|
|
908
|
-
f"1. Go to the project folder: [cyan]cd {project_name}[/cyan]"
|
|
956
|
+
f"1. Go to the project folder: [cyan]cd {_escape_markup(_shell_quote_arg(str(project_name)))}[/cyan]"
|
|
909
957
|
)
|
|
910
958
|
step_num = 2
|
|
911
959
|
else:
|
|
@@ -548,7 +548,15 @@ def _resolve_event_command_argv(
|
|
|
548
548
|
"""
|
|
549
549
|
from .integrations.base import IntegrationBase
|
|
550
550
|
|
|
551
|
-
|
|
551
|
+
try:
|
|
552
|
+
content = template_path.read_text(encoding="utf-8")
|
|
553
|
+
except (OSError, UnicodeDecodeError):
|
|
554
|
+
# An unreadable or undecodable template cannot declare a runnable
|
|
555
|
+
# script. Degrade to "no argv" like every other failure in this
|
|
556
|
+
# resolver (missing frontmatter, malformed YAML, absent scripts)
|
|
557
|
+
# instead of leaking a raw traceback through
|
|
558
|
+
# resolve_and_run_event_command.
|
|
559
|
+
return None
|
|
552
560
|
m = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
|
|
553
561
|
if not m:
|
|
554
562
|
return None
|
|
@@ -660,8 +660,13 @@ class ExtensionRegistry:
|
|
|
660
660
|
if not isinstance(data.get("extensions"), dict):
|
|
661
661
|
data["extensions"] = {}
|
|
662
662
|
return data
|
|
663
|
-
except (json.JSONDecodeError, FileNotFoundError):
|
|
664
|
-
# Corrupted or missing registry, start fresh
|
|
663
|
+
except (json.JSONDecodeError, UnicodeDecodeError, FileNotFoundError):
|
|
664
|
+
# Corrupted or missing registry, start fresh. A registry whose
|
|
665
|
+
# bytes cannot be decoded as UTF-8 is the same corruption class as
|
|
666
|
+
# malformed JSON — only the exception type differs, and it is
|
|
667
|
+
# raised by the text-mode read before JSON parsing begins. OSError
|
|
668
|
+
# is deliberately not caught: the data may be intact on disk, and
|
|
669
|
+
# starting fresh would let a later _save() wipe it.
|
|
665
670
|
return {"schema_version": self.SCHEMA_VERSION, "extensions": {}}
|
|
666
671
|
|
|
667
672
|
def _save(self):
|
|
@@ -4310,11 +4315,10 @@ class ConfigManager:
|
|
|
4310
4315
|
Returns an empty list if the registry is missing or corrupted
|
|
4311
4316
|
(fresh project, ad-hoc test harness) so ``_get_env_config`` degrades
|
|
4312
4317
|
to its pre-fix behaviour rather than crashing. ``UnicodeError`` is
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
config read instead of degrading gracefully.
|
|
4318
|
+
kept alongside ``OSError`` as belt-and-braces: ``_load()`` now starts
|
|
4319
|
+
fresh on non-UTF-8 registry bytes itself, but catching it here too
|
|
4320
|
+
keeps this call site degrading gracefully rather than breaking *every*
|
|
4321
|
+
config read if that handling ever regresses.
|
|
4318
4322
|
|
|
4319
4323
|
Used by ``_get_env_config`` to detect env vars whose remainder claims
|
|
4320
4324
|
a longer, sibling-owned prefix (e.g. ``SPECKIT_GIT_HOOKS_URL`` is
|
|
@@ -207,7 +207,7 @@ class IntegrationCatalog(CatalogStackBase):
|
|
|
207
207
|
max_bytes=MAX_JSON_METADATA_BYTES,
|
|
208
208
|
error_type=IntegrationCatalogError,
|
|
209
209
|
label=f"catalog from {entry.url}",
|
|
210
|
-
)
|
|
210
|
+
).decode("utf-8")
|
|
211
211
|
)
|
|
212
212
|
|
|
213
213
|
shape_error = _catalog_shape_error(catalog_data)
|
|
@@ -237,6 +237,15 @@ class IntegrationCatalog(CatalogStackBase):
|
|
|
237
237
|
raise IntegrationCatalogError(
|
|
238
238
|
f"Failed to fetch catalog from {entry.url}: {exc}"
|
|
239
239
|
)
|
|
240
|
+
except UnicodeDecodeError as exc:
|
|
241
|
+
# A non-UTF-8 response body fails at .decode() before json.loads()
|
|
242
|
+
# ever runs, so JSONDecodeError below does not cover it (the two are
|
|
243
|
+
# sibling ValueError subclasses, not parent/child). Without this the
|
|
244
|
+
# raw UnicodeDecodeError escapes _get_merged_integrations()'s
|
|
245
|
+
# "warn and skip this catalog" handler and kills the whole command.
|
|
246
|
+
raise IntegrationCatalogError(
|
|
247
|
+
f"Catalog from {entry.url} is not valid UTF-8: {exc}"
|
|
248
|
+
)
|
|
240
249
|
except json.JSONDecodeError as exc:
|
|
241
250
|
raise IntegrationCatalogError(
|
|
242
251
|
f"Invalid JSON in catalog from {entry.url}: {exc}"
|
|
@@ -121,13 +121,27 @@ class HermesIntegration(SkillsIntegration):
|
|
|
121
121
|
command_name = src_file.stem # e.g. "plan"
|
|
122
122
|
skill_name = f"speckit-{command_name.replace('.', '-')}"
|
|
123
123
|
|
|
124
|
-
# Parse frontmatter for description
|
|
124
|
+
# Parse frontmatter for description. Locate the closing ``---`` on
|
|
125
|
+
# its own line rather than with ``raw.split("---", 2)`` — a bare
|
|
126
|
+
# substring split stops at the first ``---`` *anywhere*, including
|
|
127
|
+
# one inside a value such as ``description: Separate sections
|
|
128
|
+
# with ---``, which truncates the frontmatter and drops later keys.
|
|
129
|
+
# The block between the delimiters is parsed unstripped so trailing
|
|
130
|
+
# newlines in literal (``|``) block scalars survive.
|
|
125
131
|
frontmatter: dict[str, Any] = {}
|
|
126
132
|
if raw.startswith("---"):
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
fm_lines = raw.splitlines(keepends=True)
|
|
134
|
+
fm_close = next(
|
|
135
|
+
(
|
|
136
|
+
i
|
|
137
|
+
for i in range(1, len(fm_lines))
|
|
138
|
+
if fm_lines[i].rstrip() == "---"
|
|
139
|
+
),
|
|
140
|
+
None,
|
|
141
|
+
)
|
|
142
|
+
if fm_close is not None:
|
|
129
143
|
try:
|
|
130
|
-
fm = yaml.safe_load(
|
|
144
|
+
fm = yaml.safe_load("".join(fm_lines[1:fm_close]))
|
|
131
145
|
if isinstance(fm, dict):
|
|
132
146
|
frontmatter = fm
|
|
133
147
|
except yaml.YAMLError:
|
|
@@ -143,10 +157,26 @@ class HermesIntegration(SkillsIntegration):
|
|
|
143
157
|
project_root=project_root,
|
|
144
158
|
)
|
|
145
159
|
# Strip the processed frontmatter — we rebuild it for skills.
|
|
160
|
+
# Scan for the closing ``---`` on its own line rather than
|
|
161
|
+
# ``split("---", 2)`` so a ``---`` embedded in a value does not
|
|
162
|
+
# truncate the frontmatter and spill it into the body.
|
|
146
163
|
if processed_body.startswith("---"):
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
164
|
+
body_lines = processed_body.splitlines(keepends=True)
|
|
165
|
+
close_idx = next(
|
|
166
|
+
(
|
|
167
|
+
i
|
|
168
|
+
for i in range(1, len(body_lines))
|
|
169
|
+
if body_lines[i].rstrip() == "---"
|
|
170
|
+
),
|
|
171
|
+
None,
|
|
172
|
+
)
|
|
173
|
+
if close_idx is not None:
|
|
174
|
+
# Keep whatever trails the ``---`` marker on the closing
|
|
175
|
+
# line so the body stays byte-for-byte identical to
|
|
176
|
+
# ``split("---", 2)[2]`` for well-formed templates.
|
|
177
|
+
processed_body = body_lines[close_idx][3:] + "".join(
|
|
178
|
+
body_lines[close_idx + 1 :]
|
|
179
|
+
)
|
|
150
180
|
|
|
151
181
|
# Select description
|
|
152
182
|
description = frontmatter.get("description", "")
|