From bc95d0324a98aa835831995301bbb213c2c485f5 Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 00:50:30 +0200 Subject: [PATCH 01/13] fix: align manifest version with 1.0.0 release --- .github/vstack.json | 4 ++-- pyproject.toml | 2 +- src/vstack/constants.py | 44 ++++++++++++++++++++++++++++------ tests/vstack/test_constants.py | 10 +++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/.github/vstack.json b/.github/vstack.json index 33f5ce7..819945d 100644 --- a/.github/vstack.json +++ b/.github/vstack.json @@ -1,6 +1,6 @@ { - "vstack_version": "0.1.0", - "installed_at": "2026-04-16T22:29:03.096690+00:00", + "vstack_version": "1.0.0", + "installed_at": "2026-04-16T22:50:27.434468+00:00", "artifacts": { "skills": [ { diff --git a/pyproject.toml b/pyproject.toml index 248e852..1394bb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "vstack" -dynamic = ["version"] +version = "1.0.0" description = "VS Code-native AI engineering workflow system for microservices, libraries, and backend systems." authors = [] license = "MIT" diff --git a/src/vstack/constants.py b/src/vstack/constants.py index 0e9c379..94e8e96 100644 --- a/src/vstack/constants.py +++ b/src/vstack/constants.py @@ -2,6 +2,8 @@ from __future__ import annotations +import re +import subprocess from importlib.metadata import PackageNotFoundError from importlib.metadata import version as _pkg_version from importlib.resources import files @@ -12,12 +14,40 @@ TEMPLATES_ROOT = Path(str(_PACKAGE_ROOT / "_templates")) -try: - # Reads from installed package metadata, populated by poetry-dynamic-versioning - # at build time from the nearest reachable git tag (e.g. "1.2.3"). - VERSION = _pkg_version("vstack") -except PackageNotFoundError: - # Fallback: uninstalled source tree or shallow clone without any git tag. - VERSION = "0.0.0" +_SEMVER_TAG_RE = re.compile(r"^\d+\.\d+\.\d+$") + + +def _version_tuple(tag: str) -> tuple[int, int, int]: + major, minor, patch = tag.split(".") + return int(major), int(minor), int(patch) + + +def _head_semver_tag() -> str | None: + """Return the highest plain semver tag pointing at HEAD, if available.""" + try: + out = subprocess.check_output( + ["git", "tag", "--points-at", "HEAD"], + stderr=subprocess.DEVNULL, + text=True, + ) + except (FileNotFoundError, subprocess.CalledProcessError): + return None + + tags = [line.strip() for line in out.splitlines() if _SEMVER_TAG_RE.fullmatch(line.strip())] + if not tags: + return None + return max(tags, key=_version_tuple) + + +# Prefer an exact semver tag on HEAD for source checkouts. +VERSION = _head_semver_tag() or "" + +if not VERSION: + try: + # Reads from installed package metadata, populated by build-time versioning. + VERSION = _pkg_version("vstack") + except PackageNotFoundError: + # Fallback: uninstalled source tree or shallow clone without any git tag. + VERSION = "0.0.0" MANIFEST_FILENAME = "vstack.json" diff --git a/tests/vstack/test_constants.py b/tests/vstack/test_constants.py index 348378f..3b8c98b 100644 --- a/tests/vstack/test_constants.py +++ b/tests/vstack/test_constants.py @@ -4,6 +4,7 @@ import importlib import importlib.metadata as importlib_metadata +import subprocess from pathlib import Path import vstack.constants as constants_module @@ -27,17 +28,24 @@ def test_manifest_filename(self) -> None: assert MANIFEST_FILENAME == "vstack.json" def test_version_fallback_when_package_metadata_missing(self, monkeypatch) -> None: - """Test that version fallback when package metadata missing.""" + """Test that version fallback when git and package metadata are unavailable.""" original_version = importlib_metadata.version + original_check_output = subprocess.check_output + + def _no_git(*_args, **_kwargs) -> str: + """Internal helper to simulate missing git binary/context.""" + raise FileNotFoundError def _raise_not_found(_: str) -> str: """Internal helper to raise not found.""" raise importlib_metadata.PackageNotFoundError + monkeypatch.setattr(subprocess, "check_output", _no_git) monkeypatch.setattr(importlib_metadata, "version", _raise_not_found) fallback_loaded = importlib.reload(constants_module) assert fallback_loaded.VERSION == "0.0.0" + monkeypatch.setattr(subprocess, "check_output", original_check_output) monkeypatch.setattr(importlib_metadata, "version", original_version) restored = importlib.reload(constants_module) assert restored.VERSION From df3fe6ec824f3c71663587a995b7c37ee40d70a3 Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:18:08 +0200 Subject: [PATCH 02/13] chore: tighten python support and restore 100% coverage --- .github/vstack.json | 4 +-- .github/workflows/qa.yml | 37 ++++++++++++++++++--- .python-version | 4 +++ CHANGELOG.md | 20 +++++++++++ Makefile | 24 ++++++++++--- README.md | 32 ++++++++++++++++-- pyproject.toml | 40 ++++++++++++++++++++-- src/vstack/constants.py | 16 ++++++--- tests/vstack/test_constants.py | 61 +++++++++++++++++++++++++--------- tests/vstack/test_main.py | 31 +++++++++++++++++ 10 files changed, 232 insertions(+), 37 deletions(-) create mode 100644 .python-version diff --git a/.github/vstack.json b/.github/vstack.json index 819945d..6220c79 100644 --- a/.github/vstack.json +++ b/.github/vstack.json @@ -1,6 +1,6 @@ { - "vstack_version": "1.0.0", - "installed_at": "2026-04-16T22:50:27.434468+00:00", + "vstack_version": "0.0.0", + "installed_at": "2026-04-16T23:17:38.938061+00:00", "artifacts": { "skills": [ { diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index abb2a79..45ef095 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -1,5 +1,6 @@ # Quality gate for branch pushes (excluding main). -# Purpose: keep fast feedback for formatting, linting, and type checking. +# Purpose: keep fast feedback for formatting/lint/type checks and run tests across +# supported Python versions. name: QA on: @@ -8,12 +9,11 @@ on: branches-ignore: [main] env: - # Single Python version used across all QA checks. PYTHON_VERSION: "3.11" jobs: - qa: - # One consolidated job for style and static-analysis checks. + quality: + # Fast quality checks run once on the baseline Python version. name: Format Lint Typecheck runs-on: ubuntu-latest @@ -24,7 +24,6 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - # Cache Poetry downloads based on lockfile changes. python-version: ${{ env.PYTHON_VERSION }} cache: poetry cache-dependency-path: poetry.lock @@ -43,3 +42,31 @@ jobs: - name: Typecheck run: make typecheck + + test-matrix: + name: Tests (py${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12", "3.13", "3.14"] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: poetry + cache-dependency-path: poetry.lock + + - name: Install Poetry + run: pipx install poetry + + - name: Install dependencies + run: poetry install --no-interaction --no-ansi + + - name: Test + run: make test diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..ef3e413 --- /dev/null +++ b/.python-version @@ -0,0 +1,4 @@ +3.14.3 +3.13.12 +3.12.12 +3.11.14 diff --git a/CHANGELOG.md b/CHANGELOG.md index 03fd39c..c8b63e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ - No unreleased changes yet. +## 1.0.1 — 2026-04-17 + +Tooling and release hygiene update focused on making local and CI verification match. + +### Added in 1.0.1 + +- Repo-local `.python-version` for consistent pyenv interpreter resolution. +- Tox-based multi-version test execution across Python 3.11, 3.12, 3.13, and 3.14. + +### Changed in 1.0.1 + +- Python support metadata is now explicitly bounded to 3.11-3.14. +- QA workflow now runs a Python test matrix across all supported runtimes. +- Local developer workflow now documents pyenv as the standard multi-version setup. + +### Quality in 1.0.1 + +- Coverage gate raised back to 100%. +- `make test` now exercises every supported Python version when interpreters are installed. + ## 1.0.0 — 2026-04-01 Initial public baseline for the VS Code-native vstack system. diff --git a/Makefile b/Makefile index 307bfe4..88d7a3f 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,11 @@ TARGET ?= . PIP ?= $(VENV_PYTHON) -m pip PYTHON_CHECK_PATHS ?= src tests -DEV_PACKAGES := pip setuptools wheel pytest pytest-cov ruff mypy pre-commit +DEV_PACKAGES := pip setuptools wheel pytest pytest-cov ruff mypy pre-commit tox .PHONY: help help-all preflight bootstrap venv install build clean clean-deep format format-check lint lint-fix \ deps pre-commit-install setup all markdown-format markdown-format-check markdown-lint \ - typecheck test check ci vstack-validate vstack-install vstack-install-global vstack-verify + typecheck test test-local check ci tox tox-all vstack-validate vstack-install vstack-install-global vstack-verify preflight: @command -v $(POETRY) >/dev/null 2>&1 || { echo "Poetry not found. Install from https://python-poetry.org/docs/"; exit 1; } @@ -29,9 +29,11 @@ help: @echo " make setup - create .venv, install dev tools, install pre-commit hook" @echo " make format - format Python and Markdown" @echo " make lint - lint Python" - @echo " make test - run pytest" + @echo " make test - run tests on Python 3.11-3.14 via tox" + @echo " make test-local - run pytest on current interpreter only" @echo " make check - full local quality gate" @echo " make ci - check + vstack template validation" + @echo " make tox - run pytest across Python 3.11-3.14 (if interpreters are available)" @echo " make clean - remove build/cache artifacts" @echo " make vstack-install - install generated artifacts into target" @echo "" @@ -56,9 +58,12 @@ help-all: @echo " make markdown-format-check - run markdown formatting check hook (may rewrite files)" @echo " make markdown-lint - lint Markdown via pre-commit markdownlint hook" @echo " make typecheck - run mypy on src/ and tests/" - @echo " make test - run pytest" + @echo " make test - run pytest matrix via tox (py311, py312, py313, py314)" + @echo " make test-local - run pytest on current interpreter only" @echo " make check - run format-check + markdown checks + lint + typecheck + test" @echo " make ci - run check + vstack-validate" + @echo " make tox - run tox pytest matrix (py311, py312, py313, py314)" + @echo " make tox-all - run all tox envs (tests + lint + type)" @echo " make vstack-validate - run vstack validate" @echo " make vstack-install - run vstack install --target (default .)" @echo " make vstack-install-global - run vstack install --global" @@ -116,13 +121,22 @@ markdown-lint: typecheck: $(VENV_PYTHON) -m mypy $(PYTHON_CHECK_PATHS) -test: +test-local: $(VENV_PYTHON) -m pytest -q +test: + $(VENV_PYTHON) -m tox -e py311,py312,py313,py314 + check: format-check markdown-lint lint typecheck test ci: check vstack-validate +tox: + $(VENV_PYTHON) -m tox -e py311,py312,py313,py314 + +tox-all: + $(VENV_PYTHON) -m tox + vstack-validate: $(VENV_PYTHON) -m vstack validate diff --git a/README.md b/README.md index 8c42c59..bb66dd5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ______________________________________________________________________ ## install -Requires **Python 3.11+**. +Requires **Python 3.11-3.14**. ```bash pip install vstack @@ -128,7 +128,7 @@ ______________________________________________________________________ ## development -Requires **Poetry** and **Python 3.11+**. +Requires **Poetry** and **Python 3.11-3.14**. ```bash git clone https://github.com/your-org/vstack @@ -153,7 +153,33 @@ make ci poetry run vstack validate # validate templates without writing files poetry run vstack install # install all artifacts -> .github/ poetry run vstack verify # full validation suite -poetry run pytest # run test suite +make test-local # run pytest on current interpreter +make test # run tests on py311-py314 via tox +make tox # run tests on py311-py314 (when installed) +``` + +### local multi-python testing (pyenv + tox) + +This repository keeps its local interpreter order in `.python-version` so `tox` +can resolve the supported runtimes consistently. + +Install and activate the supported Python runtimes with `pyenv`: + +```bash +pyenv install 3.11.14 +pyenv install 3.12.12 +pyenv install 3.13.12 +pyenv install 3.14.3 +pyenv local 3.14.3 3.13.12 3.12.12 3.11.14 +``` + +That command writes the repo-local `.python-version` file. + +Run cross-version tests locally: + +```bash +make tox # pytest on py311, py312, py313, py314 +make tox-all # all tox envs: tests + lint + type ``` **Editing skills:** source of truth is `src/vstack/_templates/skills//{config.yaml,template.md}`. Never edit generated files. diff --git a/pyproject.toml b/pyproject.toml index 1394bb7..73507d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ include = [ vstack = "vstack:main" [tool.poetry.dependencies] -python = ">=3.11" +python = ">=3.11,<3.15" [tool.poetry.group.dev.dependencies] pytest = ">=9.0" @@ -22,6 +22,7 @@ pytest-cov = ">=6.0" ruff = ">=0.9" mypy = ">=1.0" pre-commit = ">=3.8" +tox = ">=4.15" # --------------------------------------------------------------------------- @@ -38,7 +39,7 @@ addopts = [ "--import-mode=importlib", "--cov=src/vstack", "--cov-report=term-missing", - "--cov-fail-under=95", + "--cov-fail-under=100", ] # --------------------------------------------------------------------------- @@ -98,3 +99,38 @@ fallback-version = "0.0.0" [tool.pyright] pythonVersion = "3.11" extraPaths = ["scripts"] + +[tool.tox] +legacy_tox_ini = """ +[tox] +min_version = 4.15 +env_list = py311, py312, py313, py314, lint, type +skip_missing_interpreters = true + +[testenv] +description = Run pytest for {env_name} +package = skip +deps = + pytest>=9.0 + pytest-cov>=6.0 +setenv = + PYTHONPATH = {tox_root}/src +commands = + python -m pytest -q + +[testenv:lint] +description = Run ruff lint checks +package = skip +deps = + ruff>=0.9 +commands = + ruff check src tests + +[testenv:type] +description = Run mypy type checks +package = skip +deps = + mypy>=1.0 +commands = + mypy src tests +""" diff --git a/src/vstack/constants.py b/src/vstack/constants.py index 94e8e96..24d265f 100644 --- a/src/vstack/constants.py +++ b/src/vstack/constants.py @@ -39,15 +39,21 @@ def _head_semver_tag() -> str | None: return max(tags, key=_version_tuple) -# Prefer an exact semver tag on HEAD for source checkouts. -VERSION = _head_semver_tag() or "" +def _resolve_version() -> str: + """Resolve the package version from git tags, package metadata, or fallback.""" + version = _head_semver_tag() or "" + if version: + return version -if not VERSION: try: # Reads from installed package metadata, populated by build-time versioning. - VERSION = _pkg_version("vstack") + return _pkg_version("vstack") except PackageNotFoundError: # Fallback: uninstalled source tree or shallow clone without any git tag. - VERSION = "0.0.0" + return "0.0.0" + + +# Prefer an exact semver tag on HEAD for source checkouts. +VERSION = _resolve_version() MANIFEST_FILENAME = "vstack.json" diff --git a/tests/vstack/test_constants.py b/tests/vstack/test_constants.py index 3b8c98b..3fdefce 100644 --- a/tests/vstack/test_constants.py +++ b/tests/vstack/test_constants.py @@ -2,7 +2,6 @@ from __future__ import annotations -import importlib import importlib.metadata as importlib_metadata import subprocess from pathlib import Path @@ -27,25 +26,57 @@ def test_manifest_filename(self) -> None: """Test that manifest filename.""" assert MANIFEST_FILENAME == "vstack.json" + def test_head_semver_tag_returns_none_when_head_has_no_semver_tags(self, monkeypatch) -> None: + """Test that non-semver tags on HEAD are ignored.""" + monkeypatch.setattr(subprocess, "check_output", lambda *_args, **_kwargs: "main\nv1.0.0\n") + + assert constants_module._head_semver_tag() is None + + def test_head_semver_tag_returns_none_when_git_lookup_fails(self, monkeypatch) -> None: + """Test that git lookup failures fall back cleanly.""" + + def _raise_called_process_error(*_args, **_kwargs) -> str: + """Internal helper to simulate git lookup failure.""" + raise subprocess.CalledProcessError(returncode=1, cmd=["git", "tag"]) + + monkeypatch.setattr(subprocess, "check_output", _raise_called_process_error) + + assert constants_module._head_semver_tag() is None + + def test_head_semver_tag_returns_highest_plain_semver(self, monkeypatch) -> None: + """Test that the highest semver tag on HEAD is selected.""" + monkeypatch.setattr( + subprocess, + "check_output", + lambda *_args, **_kwargs: "1.0.0\n1.0.2\n1.0.1\nrelease-candidate\n", + ) + + assert constants_module._head_semver_tag() == "1.0.2" + + def test_version_uses_installed_package_metadata_when_git_tag_missing( + self, monkeypatch + ) -> None: + """Test that installed metadata is used when no semver tag points at HEAD.""" + monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: None) + monkeypatch.setattr(constants_module, "_pkg_version", lambda _name: "1.0.1") + + assert constants_module._resolve_version() == "1.0.1" + + def test_version_prefers_head_semver_tag(self, monkeypatch) -> None: + """Test that a semver tag on HEAD wins over package metadata.""" + monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: "1.0.1") + monkeypatch.setattr(constants_module, "_pkg_version", lambda _name: "9.9.9") + + assert constants_module._resolve_version() == "1.0.1" + def test_version_fallback_when_package_metadata_missing(self, monkeypatch) -> None: """Test that version fallback when git and package metadata are unavailable.""" - original_version = importlib_metadata.version - original_check_output = subprocess.check_output - - def _no_git(*_args, **_kwargs) -> str: - """Internal helper to simulate missing git binary/context.""" - raise FileNotFoundError def _raise_not_found(_: str) -> str: """Internal helper to raise not found.""" raise importlib_metadata.PackageNotFoundError - monkeypatch.setattr(subprocess, "check_output", _no_git) - monkeypatch.setattr(importlib_metadata, "version", _raise_not_found) - fallback_loaded = importlib.reload(constants_module) - assert fallback_loaded.VERSION == "0.0.0" + monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: None) + monkeypatch.setattr(constants_module, "_pkg_version", _raise_not_found) - monkeypatch.setattr(subprocess, "check_output", original_check_output) - monkeypatch.setattr(importlib_metadata, "version", original_version) - restored = importlib.reload(constants_module) - assert restored.VERSION + assert constants_module._resolve_version() == "0.0.0" diff --git a/tests/vstack/test_main.py b/tests/vstack/test_main.py index a30d49b..ae7a3fa 100644 --- a/tests/vstack/test_main.py +++ b/tests/vstack/test_main.py @@ -65,6 +65,12 @@ def uninstall(self, *args, **kwargs): class TestMain: """Test cases for Main.""" + def test_resolve_only_for_scope_returns_requested_only_for_non_global(self) -> None: + """Test that non-global commands keep the explicit type filter.""" + args = _Args("install", only=["skill"], use_global=False) + + assert main_module._resolve_only_for_scope(args) == ["skill"] + def test_main_dispatch_validate(self, monkeypatch: pytest.MonkeyPatch) -> None: """Test that main dispatch validate.""" cli = _CLI() @@ -148,6 +154,12 @@ def test_main_global_install_defaults_to_supported_types( }, ) + def test_resolve_only_for_scope_returns_requested_only_for_global_allowed_types(self) -> None: + """Test that allowed global type filters are passed through unchanged.""" + args = _Args("install", only=["agent", "prompt"], use_global=True) + + assert main_module._resolve_only_for_scope(args) == ["agent", "prompt"] + def test_main_global_install_rejects_unknown_type( self, monkeypatch: pytest.MonkeyPatch, tmp_path ) -> None: @@ -198,3 +210,22 @@ def test_main_dispatch_verify_includes_only_filter( "only": ["agent", "instruction", "prompt", "skill"], }, ) + + def test_main_dispatch_uninstall(self, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: + """Test that uninstall resolves the target directory and dispatches correctly.""" + cli = _CLI() + args = _Args("uninstall") + parser = type("P", (), {"parse_args": lambda self: args})() + + monkeypatch.setattr(main_module, "build_parser", lambda: parser) + monkeypatch.setattr(main_module, "resolve_targets", lambda _args: tmp_path) + monkeypatch.setattr(main_module, "CommandLineInterface", lambda templates_root: cli) + monkeypatch.setattr( + main_module.sys, "exit", lambda code: (_ for _ in ()).throw(SystemExit(code)) + ) + + with pytest.raises(SystemExit) as exc: + main_module.main() + + assert exc.value.code == 10 + assert cli.called == ("uninstall", (tmp_path,), {}) From 64dfb518dcf91a3e96e3eadf9e8e2c02c24ec32c Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:37:17 +0200 Subject: [PATCH 03/13] chore: migrate to PEP 621, extend pre-commit hooks - Migrate pyproject.toml from [tool.poetry] to [project] (PEP 621) form - version = "0.0.0" placeholder; poetry-dynamic-versioning overwrites at build time - Add pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-toml, check-yaml, ruff, ruff-format - Regenerate poetry.lock after pyproject restructure - Fix trailing whitespace and missing newlines in LICENSE, verify.yml, security.yml --- .github/vstack.json | 54 ++-- .github/workflows/security.yml | 2 +- .github/workflows/verify.yml | 2 +- .pre-commit-config.yaml | 15 + LICENSE | 1 - docs/design/agents.md | 2 +- docs/design/design.md | 2 +- docs/design/skills.md | 2 +- poetry.lock | 290 +++++++++++++++++- pyproject.toml | 25 +- .../_templates/agents/architect/config.yaml | 2 +- .../_templates/agents/designer/config.yaml | 2 +- .../_templates/agents/engineer/config.yaml | 2 +- .../_templates/agents/product/config.yaml | 2 +- .../_templates/agents/release/config.yaml | 2 +- .../_templates/agents/tester/config.yaml | 2 +- src/vstack/_templates/skills/adr/config.yaml | 2 +- .../_templates/skills/analyse/config.yaml | 2 +- .../skills/architecture/config.yaml | 2 +- src/vstack/_templates/skills/cicd/config.yaml | 2 +- .../_templates/skills/code-review/config.yaml | 2 +- .../_templates/skills/consult/config.yaml | 2 +- .../_templates/skills/container/config.yaml | 2 +- .../_templates/skills/debug/config.yaml | 2 +- .../_templates/skills/design/config.yaml | 2 +- src/vstack/_templates/skills/docs/config.yaml | 2 +- .../_templates/skills/explore/config.yaml | 2 +- .../_templates/skills/guardrails/config.yaml | 2 +- .../_templates/skills/inspect/config.yaml | 2 +- .../_templates/skills/performance/config.yaml | 2 +- src/vstack/_templates/skills/pr/config.yaml | 2 +- .../skills/release-notes/config.yaml | 2 +- .../skills/requirements/config.yaml | 2 +- .../_templates/skills/security/config.yaml | 2 +- .../_templates/skills/verify/config.yaml | 2 +- .../_templates/skills/vision/config.yaml | 2 +- src/vstack/cli/manifest.py | 2 +- 37 files changed, 376 insertions(+), 73 deletions(-) diff --git a/.github/vstack.json b/.github/vstack.json index 6220c79..947c705 100644 --- a/.github/vstack.json +++ b/.github/vstack.json @@ -1,139 +1,139 @@ { "vstack_version": "0.0.0", - "installed_at": "2026-04-16T23:17:38.938061+00:00", + "installed_at": "2026-04-16T23:34:10.546914+00:00", "artifacts": { "skills": [ { "name": "adr", "file": "skills/adr/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "analyse", "file": "skills/analyse/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "architecture", "file": "skills/architecture/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "cicd", "file": "skills/cicd/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "code-review", "file": "skills/code-review/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "consult", "file": "skills/consult/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "container", "file": "skills/container/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "debug", "file": "skills/debug/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "design", "file": "skills/design/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "docs", "file": "skills/docs/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "explore", "file": "skills/explore/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "guardrails", "file": "skills/guardrails/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "inspect", "file": "skills/inspect/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "performance", "file": "skills/performance/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "pr", "file": "skills/pr/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "release-notes", "file": "skills/release-notes/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "requirements", "file": "skills/requirements/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "security", "file": "skills/security/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "verify", "file": "skills/verify/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "vision", "file": "skills/vision/SKILL.md", - "version": "1.0.0" + "version": "1.0.1" } ], "agents": [ { "name": "architect", "file": "agents/architect.agent.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "designer", "file": "agents/designer.agent.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "engineer", "file": "agents/engineer.agent.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "product", "file": "agents/product.agent.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "release", "file": "agents/release.agent.md", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "tester", "file": "agents/tester.agent.md", - "version": "1.0.0" + "version": "1.0.1" } ], "instructions": [ diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index bb24e7a..4ef0c2b 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -47,4 +47,4 @@ jobs: with: path: ./ base: ${{ github.event.pull_request.base.sha }} - head: ${{ github.event.pull_request.head.sha }} \ No newline at end of file + head: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 2e299d3..59f1fa0 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -67,4 +67,4 @@ jobs: run: poetry run vstack install --target "${TARGET_DIR}" - name: Verify source and installed output - run: poetry run vstack verify --target "${TARGET_DIR}" \ No newline at end of file + run: poetry run vstack verify --target "${TARGET_DIR}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83dd15a..a13c091 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,19 @@ repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-toml + - id: check-yaml + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.4 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + - repo: https://github.com/executablebooks/mdformat rev: 0.7.22 hooks: diff --git a/LICENSE b/LICENSE index 6c80fdb..012479c 100644 --- a/LICENSE +++ b/LICENSE @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/docs/design/agents.md b/docs/design/agents.md index 7da0288..a9be8f5 100644 --- a/docs/design/agents.md +++ b/docs/design/agents.md @@ -217,7 +217,7 @@ ______________________________________________________________________ ```yaml name: architect -version: 1.0.0 +version: 1.0.1 description: "Senior software architect. Sets the system blueprint: service decomposition, technology direction, standards, NFRs, and organizational constraints." argument-hint: "[design architecture | write ADR | review architecture | check implementation alignment]" tools: diff --git a/docs/design/design.md b/docs/design/design.md index 0598e9b..d72fd2e 100644 --- a/docs/design/design.md +++ b/docs/design/design.md @@ -64,7 +64,7 @@ Each skill is a directory containing two files: ```yaml name: architecture -version: 1.0.0 +version: 1.0.1 description: | Engineering-lead plan review. Lock in the execution plan — service boundaries, data models, API contracts, error handling, test strategy, ... diff --git a/docs/design/skills.md b/docs/design/skills.md index faf5e10..9015891 100644 --- a/docs/design/skills.md +++ b/docs/design/skills.md @@ -97,7 +97,7 @@ The generator reads `config.yaml` and emits recognised fields as frontmatter in ```yaml name: architecture -version: 1.0.0 +version: 1.0.1 description: | Engineering-lead plan review. Lock in the execution plan — service boundaries, data models, API contracts, error handling, test strategy, ... diff --git a/poetry.lock b/poetry.lock index ff8c0df..214bd8c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,29 @@ # This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +[[package]] +name = "cachetools" +version = "7.0.5" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114"}, + {file = "cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990"}, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0"}, + {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"}, +] + [[package]] name = "colorama" version = "0.4.6" @@ -7,7 +31,6 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -132,6 +155,45 @@ files = [ [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +[[package]] +name = "distlib" +version = "0.4.0" +description = "Distribution utilities" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, + {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, +] + +[[package]] +name = "filelock" +version = "3.28.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "filelock-3.28.0-py3-none-any.whl", hash = "sha256:de9af6712788e7171df1b28b15eba2446c69721433fa427a9bee07b17820a9db"}, + {file = "filelock-3.28.0.tar.gz", hash = "sha256:4ed1010aae813c4ee8d9c660e4792475ee60c4a0ba76073ceaf862bd317e3ca6"}, +] + +[[package]] +name = "identify" +version = "2.6.18" +description = "File identification library for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737"}, + {file = "identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "iniconfig" version = "2.3.0" @@ -318,6 +380,18 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"}, + {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, +] + [[package]] name = "packaging" version = "26.0" @@ -348,6 +422,18 @@ optional = ["typing-extensions (>=4)"] re2 = ["google-re2 (>=1.1)"] tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] +[[package]] +name = "platformdirs" +version = "4.9.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917"}, + {file = "platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a"}, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -364,6 +450,25 @@ files = [ dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] +[[package]] +name = "pre-commit" +version = "4.5.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77"}, + {file = "pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "pygments" version = "2.19.2" @@ -379,6 +484,25 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyproject-api" +version = "1.10.0" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pyproject_api-1.10.0-py3-none-any.whl", hash = "sha256:8757c41a79c0f4ab71b99abed52b97ecf66bd20b04fa59da43b5840bac105a09"}, + {file = "pyproject_api-1.10.0.tar.gz", hash = "sha256:40c6f2d82eebdc4afee61c773ed208c04c19db4c4a60d97f8d7be3ebc0bbb330"}, +] + +[package.dependencies] +packaging = ">=25" + +[package.extras] +docs = ["furo (>=2025.9.25)", "sphinx-autodoc-typehints (>=3.5.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)", "setuptools (>=80.9)"] + [[package]] name = "pytest" version = "9.0.2" @@ -421,6 +545,109 @@ pytest = ">=7" [package.extras] testing = ["process-tests", "pytest-xdist", "virtualenv"] +[[package]] +name = "python-discovery" +version = "1.2.2" +description = "Python interpreter discovery" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a"}, + {file = "python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb"}, +] + +[package.dependencies] +filelock = ">=3.15.4" +platformdirs = ">=4.3.6,<5" + +[package.extras] +docs = ["furo (>=2025.12.19)", "sphinx (>=9.1)", "sphinx-autodoc-typehints (>=3.6.3)", "sphinxcontrib-mermaid (>=2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.5.4)", "pytest (>=8.3.5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + [[package]] name = "ruff" version = "0.15.8" @@ -449,6 +676,45 @@ files = [ {file = "ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e"}, ] +[[package]] +name = "tomli-w" +version = "1.2.0" +description = "A lil' TOML writer" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90"}, + {file = "tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021"}, +] + +[[package]] +name = "tox" +version = "4.53.0" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "tox-4.53.0-py3-none-any.whl", hash = "sha256:cc4e716d18c4889aa179d785175c438fa60c35deef20ce689ec288d8fb656096"}, + {file = "tox-4.53.0.tar.gz", hash = "sha256:62c780e42f87d34ee60f2ea20342156253794fdcbd6885fd797d98ee05009f22"}, +] + +[package.dependencies] +cachetools = ">=7.0.3" +colorama = ">=0.4.6" +filelock = ">=3.25" +packaging = ">=26" +platformdirs = ">=4.9.4" +pluggy = ">=1.6" +pyproject-api = ">=1.10" +python-discovery = ">=1.2.2" +tomli-w = ">=1.2" +virtualenv = ">=21.1" + +[package.extras] +completion = ["argcomplete (>=3.6.3)"] + [[package]] name = "typing-extensions" version = "4.15.0" @@ -461,7 +727,25 @@ files = [ {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +[[package]] +name = "virtualenv" +version = "21.2.4" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac"}, + {file = "virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} +platformdirs = ">=3.9.1,<5" +python-discovery = ">=1.2.2" + [metadata] lock-version = "2.1" -python-versions = ">=3.11" -content-hash = "efe273ac3069ba9febe8e00cac9a4b486e1e37e33ff714eaf281b37a69b5121b" +python-versions = ">=3.11,<3.15" +content-hash = "2d7100af3a60d80e213f99733bf17ad81af3081850b6e9e143f1218ea9e2b474" diff --git a/pyproject.toml b/pyproject.toml index 73507d8..fab6c67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,26 @@ -[tool.poetry] +[project] name = "vstack" -version = "1.0.0" +version = "0.0.0" description = "VS Code-native AI engineering workflow system for microservices, libraries, and backend systems." -authors = [] license = "MIT" readme = "README.md" +requires-python = ">=3.11,<3.15" +dependencies = [] + +[project.scripts] +vstack = "vstack:main" + +# --------------------------------------------------------------------------- +# Poetry-specific packaging config (package layout, file inclusion, dev deps). +# [project] above covers PEP 621 metadata; Poetry reads both. +# --------------------------------------------------------------------------- + +[tool.poetry] packages = [{include = "vstack", from = "src"}] include = [ { path = "src/vstack/_templates", format = ["sdist", "wheel"] }, ] -[tool.poetry.scripts] -vstack = "vstack:main" - -[tool.poetry.dependencies] -python = ">=3.11,<3.15" - [tool.poetry.group.dev.dependencies] pytest = ">=9.0" pytest-cov = ">=6.0" @@ -93,7 +98,7 @@ fallback-version = "0.0.0" # --------------------------------------------------------------------------- # pyright is used for type checking in VS Code, by Pylance. # The "extraPaths" setting is used to find the "scripts" directory, -# which contains the main code for vstack, but is not a project dependency. +# which contains the main code for vstack, but is not a project dependency. # --------------------------------------------------------------------------- [tool.pyright] diff --git a/src/vstack/_templates/agents/architect/config.yaml b/src/vstack/_templates/agents/architect/config.yaml index 7e22fc7..7e76404 100644 --- a/src/vstack/_templates/agents/architect/config.yaml +++ b/src/vstack/_templates/agents/architect/config.yaml @@ -1,5 +1,5 @@ name: architect -version: 1.0.0 +version: 1.0.1 description: > Senior software architect. Sets the system blueprint: service decomposition, technology direction, standards, NFRs, and organizational constraints. diff --git a/src/vstack/_templates/agents/designer/config.yaml b/src/vstack/_templates/agents/designer/config.yaml index 1310972..d2417e3 100644 --- a/src/vstack/_templates/agents/designer/config.yaml +++ b/src/vstack/_templates/agents/designer/config.yaml @@ -1,5 +1,5 @@ name: designer -version: 1.0.0 +version: 1.0.1 description: > Senior interaction designer. Translates architecture blueprint into developer-ready specifications: API contracts, event schemas, data flows, diff --git a/src/vstack/_templates/agents/engineer/config.yaml b/src/vstack/_templates/agents/engineer/config.yaml index 1db8535..c546092 100644 --- a/src/vstack/_templates/agents/engineer/config.yaml +++ b/src/vstack/_templates/agents/engineer/config.yaml @@ -1,5 +1,5 @@ name: engineer -version: 1.0.0 +version: 1.0.1 description: > Senior software engineer. Implements features, bug fixes, and unit tests based on docs/design/design.md, docs/architecture/architecture.md, and ADRs. diff --git a/src/vstack/_templates/agents/product/config.yaml b/src/vstack/_templates/agents/product/config.yaml index 1939515..60fe874 100644 --- a/src/vstack/_templates/agents/product/config.yaml +++ b/src/vstack/_templates/agents/product/config.yaml @@ -1,5 +1,5 @@ name: product -version: 1.0.0 +version: 1.0.1 description: > Senior product manager. Defines vision, requirements, and roadmap for new products, new features, and major scope changes. Baseline-first on branch: diff --git a/src/vstack/_templates/agents/release/config.yaml b/src/vstack/_templates/agents/release/config.yaml index b42f925..149b826 100644 --- a/src/vstack/_templates/agents/release/config.yaml +++ b/src/vstack/_templates/agents/release/config.yaml @@ -1,5 +1,5 @@ name: release -version: 1.0.0 +version: 1.0.1 description: > Senior platform and release engineer. Acts as release gatekeeper: verifies baseline artifacts are complete (docs/product, docs/architecture, diff --git a/src/vstack/_templates/agents/tester/config.yaml b/src/vstack/_templates/agents/tester/config.yaml index d0c943c..a614758 100644 --- a/src/vstack/_templates/agents/tester/config.yaml +++ b/src/vstack/_templates/agents/tester/config.yaml @@ -1,5 +1,5 @@ name: tester -version: 1.0.0 +version: 1.0.1 description: > Senior QA, security, and reliability engineer. Runs functional, security, and performance tests. Produces docs/test-report.md, docs/security-report.md, diff --git a/src/vstack/_templates/skills/adr/config.yaml b/src/vstack/_templates/skills/adr/config.yaml index 6c93a73..54644dd 100644 --- a/src/vstack/_templates/skills/adr/config.yaml +++ b/src/vstack/_templates/skills/adr/config.yaml @@ -1,5 +1,5 @@ name: adr -version: 1.0.0 +version: 1.0.1 description: | Architecture Decision Record writing. Documents a significant architectural decision with context, alternatives considered, rationale, and impact. diff --git a/src/vstack/_templates/skills/analyse/config.yaml b/src/vstack/_templates/skills/analyse/config.yaml index 1c41f42..b1bedca 100644 --- a/src/vstack/_templates/skills/analyse/config.yaml +++ b/src/vstack/_templates/skills/analyse/config.yaml @@ -1,5 +1,5 @@ name: analyse -version: 1.0.0 +version: 1.0.1 description: | Cross-cutting technical analysis. Investigates impact, tradeoffs, root causes, or feasibility without implementing changes. Use when asked to "analyse this", diff --git a/src/vstack/_templates/skills/architecture/config.yaml b/src/vstack/_templates/skills/architecture/config.yaml index caa21a5..233d61b 100644 --- a/src/vstack/_templates/skills/architecture/config.yaml +++ b/src/vstack/_templates/skills/architecture/config.yaml @@ -1,5 +1,5 @@ name: architecture -version: 1.0.0 +version: 1.0.1 description: | Engineering-lead plan review. Lock in the execution plan — service boundaries, data models, API contracts, error handling, test strategy, diff --git a/src/vstack/_templates/skills/cicd/config.yaml b/src/vstack/_templates/skills/cicd/config.yaml index 9a55e93..bbab73d 100644 --- a/src/vstack/_templates/skills/cicd/config.yaml +++ b/src/vstack/_templates/skills/cicd/config.yaml @@ -1,5 +1,5 @@ name: cicd -version: 1.0.0 +version: 1.0.1 description: | Write GitHub Actions CI/CD workflow configuration. Covers build, test, lint, security scan, container publish, and deployment trigger workflows. diff --git a/src/vstack/_templates/skills/code-review/config.yaml b/src/vstack/_templates/skills/code-review/config.yaml index 7456933..a8f2c8c 100644 --- a/src/vstack/_templates/skills/code-review/config.yaml +++ b/src/vstack/_templates/skills/code-review/config.yaml @@ -1,5 +1,5 @@ name: code-review -version: 1.0.0 +version: 1.0.1 description: | Pre-landing code review. Finds bugs that pass CI but break in production — race conditions, missing error handling, API contract violations, observability diff --git a/src/vstack/_templates/skills/consult/config.yaml b/src/vstack/_templates/skills/consult/config.yaml index f23e563..5b78fe8 100644 --- a/src/vstack/_templates/skills/consult/config.yaml +++ b/src/vstack/_templates/skills/consult/config.yaml @@ -1,5 +1,5 @@ name: consult -version: 1.0.0 +version: 1.0.1 description: | DX triage and focused review. First classifies whether the request is API DX, CLI/tool DX, or developer workflow DX, then runs exactly one review path with diff --git a/src/vstack/_templates/skills/container/config.yaml b/src/vstack/_templates/skills/container/config.yaml index fb1d7c0..2def729 100644 --- a/src/vstack/_templates/skills/container/config.yaml +++ b/src/vstack/_templates/skills/container/config.yaml @@ -1,5 +1,5 @@ name: container -version: 1.0.0 +version: 1.0.1 description: | Write and review Dockerfile, docker-compose, and container configuration. Covers multi-stage builds, image hardening, non-root users, minimal base images, diff --git a/src/vstack/_templates/skills/debug/config.yaml b/src/vstack/_templates/skills/debug/config.yaml index 4d115eb..d95b682 100644 --- a/src/vstack/_templates/skills/debug/config.yaml +++ b/src/vstack/_templates/skills/debug/config.yaml @@ -1,5 +1,5 @@ name: debug -version: 1.0.0 +version: 1.0.1 description: | Systematic root-cause debugging for backend services, APIs, and libraries. No fixes without investigation. Follows the scientific method: observe → diff --git a/src/vstack/_templates/skills/design/config.yaml b/src/vstack/_templates/skills/design/config.yaml index aa03b33..562d863 100644 --- a/src/vstack/_templates/skills/design/config.yaml +++ b/src/vstack/_templates/skills/design/config.yaml @@ -1,5 +1,5 @@ name: design -version: 1.0.0 +version: 1.0.1 description: | Build a complete API design or service design from scratch. Produces OpenAPI specs, error conventions, naming standards, pagination patterns, and versioning diff --git a/src/vstack/_templates/skills/docs/config.yaml b/src/vstack/_templates/skills/docs/config.yaml index 7782e0f..1ec26d9 100644 --- a/src/vstack/_templates/skills/docs/config.yaml +++ b/src/vstack/_templates/skills/docs/config.yaml @@ -1,5 +1,5 @@ name: docs -version: 1.0.0 +version: 1.0.1 description: | Post-release documentation alignment. Updates README, API docs, migration guides, and related docs to match shipped behavior. Does not own release-note diff --git a/src/vstack/_templates/skills/explore/config.yaml b/src/vstack/_templates/skills/explore/config.yaml index 2f1eec2..09abef1 100644 --- a/src/vstack/_templates/skills/explore/config.yaml +++ b/src/vstack/_templates/skills/explore/config.yaml @@ -1,5 +1,5 @@ name: explore -version: 1.0.0 +version: 1.0.1 description: | Repository and system discovery. Maps the architecture, understands the codebase, identifies technical debt, and produces a structured onboarding diff --git a/src/vstack/_templates/skills/guardrails/config.yaml b/src/vstack/_templates/skills/guardrails/config.yaml index 93486b6..1734bc7 100644 --- a/src/vstack/_templates/skills/guardrails/config.yaml +++ b/src/vstack/_templates/skills/guardrails/config.yaml @@ -1,5 +1,5 @@ name: guardrails -version: 1.0.0 +version: 1.0.1 description: | Activate safety guardrails for the current session. Before any destructive command (rm -rf, DROP TABLE, git push --force, git reset --hard, kubectl delete, diff --git a/src/vstack/_templates/skills/inspect/config.yaml b/src/vstack/_templates/skills/inspect/config.yaml index 1a9f3fb..f0ed115 100644 --- a/src/vstack/_templates/skills/inspect/config.yaml +++ b/src/vstack/_templates/skills/inspect/config.yaml @@ -1,5 +1,5 @@ name: inspect -version: 1.0.0 +version: 1.0.1 description: | Read-only verification audit. Runs baseline plus optional extended checks, produces severity-ranked findings, and makes no code or commit changes. diff --git a/src/vstack/_templates/skills/performance/config.yaml b/src/vstack/_templates/skills/performance/config.yaml index fad8afb..62453c2 100644 --- a/src/vstack/_templates/skills/performance/config.yaml +++ b/src/vstack/_templates/skills/performance/config.yaml @@ -1,5 +1,5 @@ name: performance -version: 1.0.0 +version: 1.0.1 description: | Performance profiling and regression detection. Establishes baselines, detects regressions, profiles bottlenecks, and recommends optimizations. Use when asked diff --git a/src/vstack/_templates/skills/pr/config.yaml b/src/vstack/_templates/skills/pr/config.yaml index b6f06f5..9a43d1e 100644 --- a/src/vstack/_templates/skills/pr/config.yaml +++ b/src/vstack/_templates/skills/pr/config.yaml @@ -1,5 +1,5 @@ name: pr -version: 1.0.0 +version: 1.0.1 description: | Commit, push, and open a pull request from the current branch to main. Uses the release notes from docs/releases/{date}.md as the PR body. diff --git a/src/vstack/_templates/skills/release-notes/config.yaml b/src/vstack/_templates/skills/release-notes/config.yaml index 702af79..92d3aa5 100644 --- a/src/vstack/_templates/skills/release-notes/config.yaml +++ b/src/vstack/_templates/skills/release-notes/config.yaml @@ -1,5 +1,5 @@ name: release-notes -version: 1.0.0 +version: 1.0.1 description: | Prepare release artifacts: verify all docs are present, write release notes, own CHANGELOG.md updates, and produce docs/releases/{date}.md. diff --git a/src/vstack/_templates/skills/requirements/config.yaml b/src/vstack/_templates/skills/requirements/config.yaml index 259a79f..7b461bf 100644 --- a/src/vstack/_templates/skills/requirements/config.yaml +++ b/src/vstack/_templates/skills/requirements/config.yaml @@ -1,5 +1,5 @@ name: requirements -version: 1.0.0 +version: 1.0.1 description: | Collaborative requirements gathering and documentation. Clarifies what must be built, defines success criteria, constraints, and non-functional requirements. diff --git a/src/vstack/_templates/skills/security/config.yaml b/src/vstack/_templates/skills/security/config.yaml index 75a0e69..6c5efe4 100644 --- a/src/vstack/_templates/skills/security/config.yaml +++ b/src/vstack/_templates/skills/security/config.yaml @@ -1,5 +1,5 @@ name: security -version: 1.0.0 +version: 1.0.1 description: | OWASP Top 10 + STRIDE security audit for APIs, services, and libraries. Finds authentication bypasses, injection vulnerabilities, insecure configurations, diff --git a/src/vstack/_templates/skills/verify/config.yaml b/src/vstack/_templates/skills/verify/config.yaml index a31b510..adf6bb9 100644 --- a/src/vstack/_templates/skills/verify/config.yaml +++ b/src/vstack/_templates/skills/verify/config.yaml @@ -1,5 +1,5 @@ name: verify -version: 1.0.0 +version: 1.0.1 description: | Verification fix-loop skill. Routes by mode (quick/standard/exhaustive), runs targeted checks, fixes findings by severity, and re-verifies impacted paths. diff --git a/src/vstack/_templates/skills/vision/config.yaml b/src/vstack/_templates/skills/vision/config.yaml index af218f5..c58ccca 100644 --- a/src/vstack/_templates/skills/vision/config.yaml +++ b/src/vstack/_templates/skills/vision/config.yaml @@ -1,5 +1,5 @@ name: vision -version: 1.0.0 +version: 1.0.1 description: | CEO/founder-mode plan review. Rethink the problem from first principles, validate ambition and scope, challenge premises, find the 10x solution. Four modes: diff --git a/src/vstack/cli/manifest.py b/src/vstack/cli/manifest.py index 33d2572..88e6039 100644 --- a/src/vstack/cli/manifest.py +++ b/src/vstack/cli/manifest.py @@ -10,7 +10,7 @@ "vstack_version": "…", "installed_at": "…", "artifacts": { - "skills": [{"name": "vision", "version": "1.0.0", "file": "skills/vision/SKILL.md"}], + "skills": [{"name": "vision", "version": "1.0.1", "file": "skills/vision/SKILL.md"}], "agents": [{"name": "engineer", "file": "agents/engineer.agent.md"}] } } From bb4f437192354cec5bc318815b526afcc49a12de Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:39:09 +0200 Subject: [PATCH 04/13] docs: update CHANGELOG 1.0.1 with PEP 621 and pre-commit entries --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8b63e4..d37aa87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ Tooling and release hygiene update focused on making local and CI verification m - Repo-local `.python-version` for consistent pyenv interpreter resolution. - Tox-based multi-version test execution across Python 3.11, 3.12, 3.13, and 3.14. +- Pre-commit hooks expanded: `trailing-whitespace`, `end-of-file-fixer`, `check-toml`, `check-yaml`, `ruff`, `ruff-format`. ### Changed in 1.0.1 -- Python support metadata is now explicitly bounded to 3.11-3.14. +- `pyproject.toml` migrated from `[tool.poetry]` to PEP 621 `[project]` form; `version = "0.0.0"` is a build-time placeholder overwritten by `poetry-dynamic-versioning`. +- Python support metadata is now explicitly bounded to 3.11–3.14. - QA workflow now runs a Python test matrix across all supported runtimes. - Local developer workflow now documents pyenv as the standard multi-version setup. From 3cdca6c88148ac163c8611e19356a930c4f32f7b Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:41:31 +0200 Subject: [PATCH 05/13] fix: install poetry before setup-python to fix cache: poetry lookup --- .github/workflows/qa.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 45ef095..16d3729 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -21,6 +21,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Install Poetry + run: pipx install poetry + - name: Setup Python uses: actions/setup-python@v5 with: @@ -28,9 +31,6 @@ jobs: cache: poetry cache-dependency-path: poetry.lock - - name: Install Poetry - run: pipx install poetry - - name: Install dependencies run: poetry install --no-interaction --no-ansi @@ -55,6 +55,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Install Poetry + run: pipx install poetry + - name: Setup Python uses: actions/setup-python@v5 with: @@ -62,9 +65,6 @@ jobs: cache: poetry cache-dependency-path: poetry.lock - - name: Install Poetry - run: pipx install poetry - - name: Install dependencies run: poetry install --no-interaction --no-ansi From 89cb1eb36fc416cb6817e6ce85dd44acdbb26a43 Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:43:46 +0200 Subject: [PATCH 06/13] fix: move poetry install before setup-python, bump actions to v5/v6 - verify.yml, security.yml: install poetry before setup-python (fixes 'poetry not found' in cache lookup) - all workflows: checkout@v4 -> v5, setup-python@v5 -> v6 (fixes Node.js 20 deprecation warnings) --- .github/workflows/qa.yml | 8 ++++---- .github/workflows/release.yml | 6 +++--- .github/workflows/security.yml | 10 +++++----- .github/workflows/verify.yml | 20 ++++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 16d3729..a25ca03 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -19,13 +19,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Poetry run: pipx install poetry - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} cache: poetry @@ -53,13 +53,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Poetry run: pipx install poetry - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: poetry diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b4c244..6d49b83 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: # Full history is required by semantic-version to inspect commit history. fetch-depth: 0 @@ -96,12 +96,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.merge_commit_sha }} - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} cache: poetry diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 4ef0c2b..072349b 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -19,18 +19,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 + + - name: Install Poetry + run: pipx install poetry - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} cache: poetry cache-dependency-path: poetry.lock - - name: Install Poetry - run: pipx install poetry - - name: Install pip-audit run: pipx install pip-audit diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 59f1fa0..6d86864 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -19,18 +19,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 + + - name: Install Poetry + run: pipx install poetry - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} cache: poetry cache-dependency-path: poetry.lock - - name: Install Poetry - run: pipx install poetry - - name: Install dependencies run: poetry install --no-interaction --no-ansi @@ -44,18 +44,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 + + - name: Install Poetry + run: pipx install poetry - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} cache: poetry cache-dependency-path: poetry.lock - - name: Install Poetry - run: pipx install poetry - - name: Install dependencies run: poetry install --no-interaction --no-ansi From 892701641ca53b8311a6ac4d9ede130012d8807e Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:45:05 +0200 Subject: [PATCH 07/13] docs: add CI workflow fixes to CHANGELOG 1.0.1 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d37aa87..a902763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ Tooling and release hygiene update focused on making local and CI verification m - QA workflow now runs a Python test matrix across all supported runtimes. - Local developer workflow now documents pyenv as the standard multi-version setup. +### Fixed in 1.0.1 + +- CI workflows: `pipx install poetry` now runs before `actions/setup-python` so the `cache: poetry` lookup always succeeds. +- CI workflows: bumped `actions/checkout@v4` → `v5` and `actions/setup-python@v5` → `v6` to resolve Node.js 20 deprecation warnings. + ### Quality in 1.0.1 - Coverage gate raised back to 100%. From f989c21c2663315d61f237d3f3488fcc617dbc2f Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:46:45 +0200 Subject: [PATCH 08/13] fix: use python -m tox in make test/tox targets Avoids coupling to .venv/bin/python. Uses the active interpreter, which is the Poetry-managed env in CI and .venv locally. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 88d7a3f..6030b93 100644 --- a/Makefile +++ b/Makefile @@ -125,17 +125,17 @@ test-local: $(VENV_PYTHON) -m pytest -q test: - $(VENV_PYTHON) -m tox -e py311,py312,py313,py314 + python -m tox -e py311,py312,py313,py314 check: format-check markdown-lint lint typecheck test ci: check vstack-validate tox: - $(VENV_PYTHON) -m tox -e py311,py312,py313,py314 + python -m tox -e py311,py312,py313,py314 tox-all: - $(VENV_PYTHON) -m tox + python -m tox vstack-validate: $(VENV_PYTHON) -m vstack validate From 42fbb7deb184b9387ba6c4c90259f116066c31d3 Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:47:36 +0200 Subject: [PATCH 09/13] fix: set POETRY_VIRTUALENVS_IN_PROJECT=true in CI workflows Ensures poetry install creates .venv in-project so all make targets that reference $(VENV_PYTHON) work without explicit activation. --- .github/workflows/qa.yml | 1 + .github/workflows/verify.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index a25ca03..6776d4b 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -10,6 +10,7 @@ on: env: PYTHON_VERSION: "3.11" + POETRY_VIRTUALENVS_IN_PROJECT: "true" jobs: quality: diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 6d86864..68fcd54 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -10,6 +10,7 @@ name: Verify env: # Shared interpreter version for reproducible CI behavior. PYTHON_VERSION: "3.11" + POETRY_VIRTUALENVS_IN_PROJECT: "true" jobs: unit-tests: From fa683322f75fa0de0af5efd0d2d90e4d5335558f Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:50:26 +0200 Subject: [PATCH 10/13] fix: restrict git tag lookup to vstack source repo _head_semver_tag() now only runs git when inside the vstack source checkout (detected via _vstack_repo_root()). Prevents VERSION from incorrectly resolving to a HEAD tag from the user's working repo when vstack is installed as a tool and invoked from another git project. --- src/vstack/constants.py | 29 +++++++++++++++++++++++++++-- tests/vstack/test_constants.py | 22 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/vstack/constants.py b/src/vstack/constants.py index 24d265f..6cf96bd 100644 --- a/src/vstack/constants.py +++ b/src/vstack/constants.py @@ -22,11 +22,36 @@ def _version_tuple(tag: str) -> tuple[int, int, int]: return int(major), int(minor), int(patch) +def _vstack_repo_root() -> Path | None: + """Return the root of the vstack source checkout, or None if not in one. + + Walks up from the package directory looking for a .git directory. + Returns None when invoked outside the vstack source tree, preventing + accidental version reads from the user's working directory. + """ + candidate = Path(str(_PACKAGE_ROOT)).resolve() + for parent in [candidate, *candidate.parents]: + if (parent / ".git").exists(): + # Confirm this is the vstack repo, not an arbitrary repo that + # happens to contain the installed package somewhere inside it. + if (parent / "src" / "vstack").exists(): + return parent + break + return None + + def _head_semver_tag() -> str | None: - """Return the highest plain semver tag pointing at HEAD, if available.""" + """Return the highest plain semver tag pointing at HEAD, if available. + + Only runs git inside the vstack source checkout to prevent picking up + tags from whatever repository the user is working in. + """ + repo_root = _vstack_repo_root() + if repo_root is None: + return None try: out = subprocess.check_output( - ["git", "tag", "--points-at", "HEAD"], + ["git", "-C", str(repo_root), "tag", "--points-at", "HEAD"], stderr=subprocess.DEVNULL, text=True, ) diff --git a/tests/vstack/test_constants.py b/tests/vstack/test_constants.py index 3fdefce..e4fb685 100644 --- a/tests/vstack/test_constants.py +++ b/tests/vstack/test_constants.py @@ -28,12 +28,14 @@ def test_manifest_filename(self) -> None: def test_head_semver_tag_returns_none_when_head_has_no_semver_tags(self, monkeypatch) -> None: """Test that non-semver tags on HEAD are ignored.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: Path("/fake/root")) monkeypatch.setattr(subprocess, "check_output", lambda *_args, **_kwargs: "main\nv1.0.0\n") assert constants_module._head_semver_tag() is None def test_head_semver_tag_returns_none_when_git_lookup_fails(self, monkeypatch) -> None: """Test that git lookup failures fall back cleanly.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: Path("/fake/root")) def _raise_called_process_error(*_args, **_kwargs) -> str: """Internal helper to simulate git lookup failure.""" @@ -43,8 +45,28 @@ def _raise_called_process_error(*_args, **_kwargs) -> str: assert constants_module._head_semver_tag() is None + def test_head_semver_tag_returns_none_outside_vstack_repo(self, monkeypatch) -> None: + """Test that git is not consulted when not inside the vstack source tree.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: None) + called = [] + monkeypatch.setattr(subprocess, "check_output", lambda *_a, **_kw: called.append(1) or "") + + assert constants_module._head_semver_tag() is None + assert not called, "git must not be invoked outside the vstack repo" + + def test_vstack_repo_root_returns_none_for_unrelated_git_repo( + self, monkeypatch, tmp_path + ) -> None: + """Test that a .git dir without src/vstack does not match as vstack repo.""" + fake_git = tmp_path / ".git" + fake_git.mkdir() + monkeypatch.setattr(constants_module, "_PACKAGE_ROOT", tmp_path) # type: ignore[attr-defined] + + assert constants_module._vstack_repo_root() is None + def test_head_semver_tag_returns_highest_plain_semver(self, monkeypatch) -> None: """Test that the highest semver tag on HEAD is selected.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: Path("/fake/root")) monkeypatch.setattr( subprocess, "check_output", From 4a3f33439e1c4917782cb31782d710aa88d9bfe9 Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:54:05 +0200 Subject: [PATCH 11/13] fix: make test/tox use project or poetry python automatically Prefer .venv/bin/python when present; otherwise use poetry run python and finally PYTHON. Prevents CI failures when tox is not on runner python. --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6030b93..4fd50a2 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ VENV ?= .venv VENV_PYTHON ?= $(VENV)/bin/python TARGET ?= . PIP ?= $(VENV_PYTHON) -m pip +TOX_PYTHON ?= $(if $(wildcard $(VENV_PYTHON)),$(VENV_PYTHON),$(if $(shell command -v $(POETRY) 2>/dev/null),$(POETRY) run python,$(PYTHON))) PYTHON_CHECK_PATHS ?= src tests DEV_PACKAGES := pip setuptools wheel pytest pytest-cov ruff mypy pre-commit tox @@ -125,17 +126,17 @@ test-local: $(VENV_PYTHON) -m pytest -q test: - python -m tox -e py311,py312,py313,py314 + $(TOX_PYTHON) -m tox -e py311,py312,py313,py314 check: format-check markdown-lint lint typecheck test ci: check vstack-validate tox: - python -m tox -e py311,py312,py313,py314 + $(TOX_PYTHON) -m tox -e py311,py312,py313,py314 tox-all: - python -m tox + $(TOX_PYTHON) -m tox vstack-validate: $(VENV_PYTHON) -m vstack validate From ee9b5f5f78cf846ed0bf217f94fe92c633f06a8b Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:57:04 +0200 Subject: [PATCH 12/13] fix: resolve mypy issues in constants tests --- tests/vstack/test_constants.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/vstack/test_constants.py b/tests/vstack/test_constants.py index e4fb685..856c814 100644 --- a/tests/vstack/test_constants.py +++ b/tests/vstack/test_constants.py @@ -49,7 +49,12 @@ def test_head_semver_tag_returns_none_outside_vstack_repo(self, monkeypatch) -> """Test that git is not consulted when not inside the vstack source tree.""" monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: None) called = [] - monkeypatch.setattr(subprocess, "check_output", lambda *_a, **_kw: called.append(1) or "") + + def _record_call(*_args, **_kwargs) -> str: + called.append(1) + return "" + + monkeypatch.setattr(subprocess, "check_output", _record_call) assert constants_module._head_semver_tag() is None assert not called, "git must not be invoked outside the vstack repo" @@ -60,7 +65,7 @@ def test_vstack_repo_root_returns_none_for_unrelated_git_repo( """Test that a .git dir without src/vstack does not match as vstack repo.""" fake_git = tmp_path / ".git" fake_git.mkdir() - monkeypatch.setattr(constants_module, "_PACKAGE_ROOT", tmp_path) # type: ignore[attr-defined] + monkeypatch.setattr(constants_module, "_PACKAGE_ROOT", tmp_path) assert constants_module._vstack_repo_root() is None From 90273c1de7b9b9542bd63559d453fd1db06e7089 Mon Sep 17 00:00:00 2001 From: Erik Schaareman Date: Fri, 17 Apr 2026 01:58:04 +0200 Subject: [PATCH 13/13] fix: create verify target dir before artifact install --- .github/workflows/verify.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 68fcd54..f9a0594 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -64,6 +64,9 @@ jobs: # Avoid cross-run collisions on shared/self-hosted runners. run: echo "TARGET_DIR=${RUNNER_TEMP}/vstack-pr-target-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_ENV" + - name: Create temp target directory + run: mkdir -p "${TARGET_DIR}" + - name: Install artifacts into temp target run: poetry run vstack install --target "${TARGET_DIR}"