From 19e4d64a713a61adafe0f6ca17eb514fae4408a6 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sun, 9 Aug 2026 16:43:15 -0500 Subject: [PATCH 1/2] fix: invoke Python scripts with uv run across skills, samples, and docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #98 standardized on `uv run` for bmad-agent-builder and bmad-workflow-builder, but bmad-bmb-setup, bmad-module-builder, and bmad-eval-runner were missed. This finishes it: 57 invocations across skills/, samples/, and docs/how-to/. Why it matters beyond consistency: a bare `python3` is not guaranteed to be 3.11+, which BMad's shared resolve_customization.py requires for tomllib. `uv run` reads a script's own requires-python and provisions a matching interpreter. Five of the fixes are in assets/setup-skill-template/ and assets/standalone-module-template/ — the templates bmad-module-builder emits — so every module scaffolded from bmb stops inheriting the defect. pytest lines become `uv run --with pytest -m pytest`, matching the convention in bmad-method: those test files carry no PEP 723 block, so uv has nothing else to resolve pytest from. Left deliberately: the two anti-pattern cells in docs/explanation/ (labelled Fragile and "do not modify"), which teach what not to do; the "runs under a bare python3" prose, which describes a script's lack of dependencies rather than invoking anything; and the per-script requires-python floors, which are correct as written. Also corrects the README badge, which advertised >=3.10 — a floor that cannot run the shared resolver. --- README.md | 2 +- docs/how-to/install-docker-for-evals.md | 2 +- docs/how-to/make-a-skill-customizable.md | 2 +- samples/bmad-agent-code-coach/scripts/init-sanctum.py | 6 +++--- samples/bmad-agent-code-coach/scripts/wake.py | 2 +- samples/bmad-agent-creative-muse/scripts/init-sanctum.py | 6 +++--- samples/bmad-agent-creative-muse/scripts/wake.py | 2 +- samples/bmad-agent-dream-weaver/assets/module-setup.md | 4 ++-- samples/bmad-agent-sentinel/scripts/init-sanctum.py | 6 +++--- samples/bmad-agent-sentinel/scripts/wake.py | 2 +- samples/bmad-excalidraw/references/diagram-generation.md | 6 +++--- samples/bmad-excalidraw/scripts/generate_excalidraw.py | 6 +++--- samples/bmad-excalidraw/scripts/validate_excalidraw.py | 4 ++-- samples/sample-module-setup/SKILL.md | 6 +++--- skills/bmad-bmb-setup/SKILL.md | 6 +++--- skills/bmad-eval-runner/SKILL.md | 6 +++--- skills/bmad-eval-runner/scripts/aggregate_benchmark.py | 8 ++++---- skills/bmad-eval-runner/scripts/run_evals.py | 2 +- skills/bmad-eval-runner/scripts/run_triggers.py | 2 +- .../bmad-eval-runner/scripts/tests/test_env_isolation.py | 4 ++-- .../scripts/tests/test_trigger_detection.py | 4 ++-- .../assets/setup-skill-template/SKILL.md | 6 +++--- .../assets/standalone-module-template/module-setup.md | 4 ++-- skills/bmad-module-builder/references/create-module.md | 4 ++-- skills/bmad-module-builder/references/validate-module.md | 2 +- .../scripts/tests/test_canon_sync.py | 4 ++-- .../scripts/tests/test_count_tokens.py | 4 ++-- .../scripts/tests/test_render_report.py | 4 ++-- 28 files changed, 58 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 7f5383e..da02ee8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Version](https://img.shields.io/npm/v/bmad-builder?color=blue&label=version)](https://www.npmjs.com/package/bmad-builder) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Python Version](https://img.shields.io/badge/python-%3E%3D3.10-blue?logo=python&logoColor=white)](https://www.python.org) +[![Python Version](https://img.shields.io/badge/python-%3E%3D3.11-blue?logo=python&logoColor=white)](https://www.python.org) [![uv](https://img.shields.io/badge/uv-package%20manager-blueviolet?logo=uv)](https://docs.astral.sh/uv/) [![Discord](https://img.shields.io/badge/Discord-Join%20Community-7289da?logo=discord&logoColor=white)](https://discord.gg/gk8jAdXWmj) diff --git a/docs/how-to/install-docker-for-evals.md b/docs/how-to/install-docker-for-evals.md index 95354e5..1ebf670 100644 --- a/docs/how-to/install-docker-for-evals.md +++ b/docs/how-to/install-docker-for-evals.md @@ -78,7 +78,7 @@ The Dockerfile contains no tokens, API keys, or credentials. Your authentication ## Tips -- Rebuild the image with `python3 scripts/docker_setup.py --rebuild` if you ever need to reset it +- Rebuild the image with `uv run scripts/docker_setup.py --rebuild` if you ever need to reset it - Per-eval container resource use is small (a few hundred MB). Parallel workers each spin up their own container. - If `docker info` works in one terminal but not in your editor's integrated terminal, your shell PATH probably differs. Open a fresh terminal session. diff --git a/docs/how-to/make-a-skill-customizable.md b/docs/how-to/make-a-skill-customizable.md index 40974ae..5f9f26f 100644 --- a/docs/how-to/make-a-skill-customizable.md +++ b/docs/how-to/make-a-skill-customizable.md @@ -110,7 +110,7 @@ EOF Run the resolver directly to confirm your override takes effect: ```bash -python3 _bmad/scripts/resolve_customization.py \ +uv run _bmad/scripts/resolve_customization.py \ --skill /path/to/built/skill \ --key workflow.on_complete ``` diff --git a/samples/bmad-agent-code-coach/scripts/init-sanctum.py b/samples/bmad-agent-code-coach/scripts/init-sanctum.py index 6b86225..65e2ae2 100755 --- a/samples/bmad-agent-code-coach/scripts/init-sanctum.py +++ b/samples/bmad-agent-code-coach/scripts/init-sanctum.py @@ -11,13 +11,13 @@ not depend on the skill bundle location for normal operation. Usage: - python3 init-sanctum.py + uv run init-sanctum.py project-root: The root of the project (where _bmad/ lives) skill-path: Path to the skill directory (where SKILL.md, references/, assets/ live) Example: - python3 scripts/init-sanctum.py /Users/me/myproject /path/to/bmad-agent-code-coach + uv run scripts/init-sanctum.py /Users/me/myproject /path/to/bmad-agent-code-coach """ import sys @@ -192,7 +192,7 @@ def substitute_vars(content: str, variables: dict) -> str: def main(): if len(sys.argv) < 3: - print("Usage: python3 init-sanctum.py ") + print("Usage: uv run init-sanctum.py ") sys.exit(1) project_root = Path(sys.argv[1]).resolve() diff --git a/samples/bmad-agent-code-coach/scripts/wake.py b/samples/bmad-agent-code-coach/scripts/wake.py index 7fc53b2..e7f052c 100644 --- a/samples/bmad-agent-code-coach/scripts/wake.py +++ b/samples/bmad-agent-code-coach/scripts/wake.py @@ -14,7 +14,7 @@ This loads runtime memory only. It never reads or writes config or customize.toml. Usage: - python3 wake.py [--pulse] + uv run wake.py [--pulse] project-root: The root of the project (where _bmad/ lives) """ diff --git a/samples/bmad-agent-creative-muse/scripts/init-sanctum.py b/samples/bmad-agent-creative-muse/scripts/init-sanctum.py index e7853e7..70959d0 100644 --- a/samples/bmad-agent-creative-muse/scripts/init-sanctum.py +++ b/samples/bmad-agent-creative-muse/scripts/init-sanctum.py @@ -11,13 +11,13 @@ not depend on the skill bundle location for normal operation. Usage: - python3 init-sanctum.py + uv run init-sanctum.py project-root: The root of the project (where _bmad/ lives) skill-path: Path to the skill directory (where SKILL.md, references/, assets/ live) Example: - python3 scripts/init-sanctum.py /Users/me/myproject /path/to/bmad-agent-creative-muse + uv run scripts/init-sanctum.py /Users/me/myproject /path/to/bmad-agent-creative-muse """ import sys @@ -178,7 +178,7 @@ def substitute_vars(content: str, variables: dict) -> str: def main(): if len(sys.argv) < 3: - print("Usage: python3 init-sanctum.py ") + print("Usage: uv run init-sanctum.py ") sys.exit(1) project_root = Path(sys.argv[1]).resolve() diff --git a/samples/bmad-agent-creative-muse/scripts/wake.py b/samples/bmad-agent-creative-muse/scripts/wake.py index 85cdd2b..4caa8ab 100644 --- a/samples/bmad-agent-creative-muse/scripts/wake.py +++ b/samples/bmad-agent-creative-muse/scripts/wake.py @@ -14,7 +14,7 @@ This loads runtime memory only. It never reads or writes config or customize.toml. Usage: - python3 wake.py [--pulse] + uv run wake.py [--pulse] project-root: The root of the project (where _bmad/ lives) """ diff --git a/samples/bmad-agent-dream-weaver/assets/module-setup.md b/samples/bmad-agent-dream-weaver/assets/module-setup.md index 67d9a15..90ffb2f 100644 --- a/samples/bmad-agent-dream-weaver/assets/module-setup.md +++ b/samples/bmad-agent-dream-weaver/assets/module-setup.md @@ -53,8 +53,8 @@ Write a temp JSON file with the collected answers structured as `{"core": {...}, In the commands below, replace `{project-root}` in every path argument with the actual project root (e.g. `/home/me/myapp`) before running — these are filesystem paths, not config values. ```bash -python3 ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} -python3 ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --module-code {module-code} +uv run ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} +uv run ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --module-code {module-code} ``` Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. diff --git a/samples/bmad-agent-sentinel/scripts/init-sanctum.py b/samples/bmad-agent-sentinel/scripts/init-sanctum.py index 507ad8a..06e0b37 100644 --- a/samples/bmad-agent-sentinel/scripts/init-sanctum.py +++ b/samples/bmad-agent-sentinel/scripts/init-sanctum.py @@ -11,13 +11,13 @@ not depend on the skill bundle location for normal operation. Usage: - python3 init-sanctum.py + uv run init-sanctum.py project-root: The root of the project (where _bmad/ lives) skill-path: Path to the skill directory (where SKILL.md, references/, assets/ live) Example: - python3 scripts/init-sanctum.py /Users/me/myproject /path/to/bmad-agent-sentinel + uv run scripts/init-sanctum.py /Users/me/myproject /path/to/bmad-agent-sentinel """ import sys @@ -189,7 +189,7 @@ def substitute_vars(content: str, variables: dict) -> str: def main(): if len(sys.argv) < 3: - print("Usage: python3 init-sanctum.py ") + print("Usage: uv run init-sanctum.py ") sys.exit(1) project_root = Path(sys.argv[1]).resolve() diff --git a/samples/bmad-agent-sentinel/scripts/wake.py b/samples/bmad-agent-sentinel/scripts/wake.py index d54252c..a7a561b 100644 --- a/samples/bmad-agent-sentinel/scripts/wake.py +++ b/samples/bmad-agent-sentinel/scripts/wake.py @@ -14,7 +14,7 @@ This loads runtime memory only. It never reads or writes config or customize.toml. Usage: - python3 wake.py [--pulse] + uv run wake.py [--pulse] project-root: The root of the project (where _bmad/ lives) """ diff --git a/samples/bmad-excalidraw/references/diagram-generation.md b/samples/bmad-excalidraw/references/diagram-generation.md index 8a489e9..4e515ec 100644 --- a/samples/bmad-excalidraw/references/diagram-generation.md +++ b/samples/bmad-excalidraw/references/diagram-generation.md @@ -49,13 +49,13 @@ The specification format: Run the generation script: ```bash -python3 ../scripts/generate_excalidraw.py --spec '' --output '{output_folder}/diagrams/{filename}.excalidraw' +uv run ../scripts/generate_excalidraw.py --spec '' --output '{output_folder}/diagrams/{filename}.excalidraw' ``` Or pipe the spec via stdin: ```bash -echo '' | python3 ../scripts/generate_excalidraw.py --output '{output_folder}/diagrams/{filename}.excalidraw' +echo '' | uv run ../scripts/generate_excalidraw.py --output '{output_folder}/diagrams/{filename}.excalidraw' ``` The script handles: @@ -72,7 +72,7 @@ The script handles: Run validation: ```bash -python3 ../scripts/validate_excalidraw.py '{output_folder}/diagrams/{filename}.excalidraw' +uv run ../scripts/validate_excalidraw.py '{output_folder}/diagrams/{filename}.excalidraw' ``` Fix any critical issues before delivering. diff --git a/samples/bmad-excalidraw/scripts/generate_excalidraw.py b/samples/bmad-excalidraw/scripts/generate_excalidraw.py index ba4e3fd..d44c975 100644 --- a/samples/bmad-excalidraw/scripts/generate_excalidraw.py +++ b/samples/bmad-excalidraw/scripts/generate_excalidraw.py @@ -8,9 +8,9 @@ with auto-layout positioning. Usage: - python generate_excalidraw.py --spec '{"title":"My Diagram",...}' --output diagram.excalidraw - echo '{"title":"My Diagram",...}' | python generate_excalidraw.py --output diagram.excalidraw - python generate_excalidraw.py --spec-file spec.json --output diagram.excalidraw + uv run generate_excalidraw.py --spec '{"title":"My Diagram",...}' --output diagram.excalidraw + echo '{"title":"My Diagram",...}' | uv run generate_excalidraw.py --output diagram.excalidraw + uv run generate_excalidraw.py --spec-file spec.json --output diagram.excalidraw Spec format: { diff --git a/samples/bmad-excalidraw/scripts/validate_excalidraw.py b/samples/bmad-excalidraw/scripts/validate_excalidraw.py index 64cb5df..d0b11f1 100644 --- a/samples/bmad-excalidraw/scripts/validate_excalidraw.py +++ b/samples/bmad-excalidraw/scripts/validate_excalidraw.py @@ -7,8 +7,8 @@ Validates .excalidraw files for structural correctness. Usage: - python validate_excalidraw.py path/to/diagram.excalidraw - python validate_excalidraw.py path/to/diagram.excalidraw -o report.json + uv run validate_excalidraw.py path/to/diagram.excalidraw + uv run validate_excalidraw.py path/to/diagram.excalidraw -o report.json Exit codes: 0=pass, 1=fail, 2=error """ diff --git a/samples/sample-module-setup/SKILL.md b/samples/sample-module-setup/SKILL.md index 147375d..6c08558 100644 --- a/samples/sample-module-setup/SKILL.md +++ b/samples/sample-module-setup/SKILL.md @@ -45,8 +45,8 @@ Write a temp JSON file with the collected answers structured as `{"core": {...}, In the commands below, replace `{project-root}` in every path argument with the actual project root (e.g. `/home/me/myapp`) before running — these are filesystem paths, not config values. ```bash -python3 ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" -python3 ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code sam +uv run ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" +uv run ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code sam ``` Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. The scripts automatically read legacy config values as fallback defaults, then delete the legacy files after a successful merge. Check `legacy_configs_deleted` and `legacy_csvs_deleted` in the output to confirm cleanup. @@ -64,7 +64,7 @@ After both merge scripts complete successfully, remove the installer's package d As with the merge scripts, replace `{project-root}` in the `--bmad-dir` and `--skills-dir` path arguments with the actual project root before running. ```bash -python3 ./scripts/cleanup-legacy.py --bmad-dir "{project-root}/_bmad" --module-code sam --also-remove _config --skills-dir "{project-root}/.claude/skills" +uv run ./scripts/cleanup-legacy.py --bmad-dir "{project-root}/_bmad" --module-code sam --also-remove _config --skills-dir "{project-root}/.claude/skills" ``` The script verifies that every skill in the legacy directories exists at `.claude/skills/` before removing anything. Directories without skills (like `_config/`) are removed directly. If the script exits non-zero, surface the error and stop. Missing directories (already cleaned by a prior run) are not errors — the script is idempotent. diff --git a/skills/bmad-bmb-setup/SKILL.md b/skills/bmad-bmb-setup/SKILL.md index 50fb6bf..b2b3fad 100644 --- a/skills/bmad-bmb-setup/SKILL.md +++ b/skills/bmad-bmb-setup/SKILL.md @@ -45,8 +45,8 @@ Write a temp JSON file with the collected answers structured as `{"core": {...}, In the commands below, replace `{project-root}` in every path argument with the actual project root (e.g. `/home/me/myapp`) before running — these are filesystem paths, not config values. Leave `{temp-file}` and `bmb` as-is. ```bash -python3 ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" -python3 ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code bmb +uv run ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" +uv run ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code bmb ``` Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. The scripts automatically read legacy config values as fallback defaults, then delete the legacy files after a successful merge. Check `legacy_configs_deleted` and `legacy_csvs_deleted` in the output to confirm cleanup. @@ -64,7 +64,7 @@ After both merge scripts complete successfully, remove the installer's package d As with the merge scripts, replace `{project-root}` in the `--bmad-dir` and `--skills-dir` path arguments with the actual project root before running. ```bash -python3 ./scripts/cleanup-legacy.py --bmad-dir "{project-root}/_bmad" --module-code bmb --also-remove _config --skills-dir "{project-root}/.claude/skills" +uv run ./scripts/cleanup-legacy.py --bmad-dir "{project-root}/_bmad" --module-code bmb --also-remove _config --skills-dir "{project-root}/.claude/skills" ``` The script verifies that every skill in the legacy directories exists at `.claude/skills/` before removing anything. Directories without skills (like `_config/`) are removed directly. If the script exits non-zero, surface the error and stop. Missing directories (already cleaned by a prior run) are not errors — the script is idempotent. diff --git a/skills/bmad-eval-runner/SKILL.md b/skills/bmad-eval-runner/SKILL.md index 171b693..f5247e2 100644 --- a/skills/bmad-eval-runner/SKILL.md +++ b/skills/bmad-eval-runner/SKILL.md @@ -60,7 +60,7 @@ Each case runs in a clean working directory with the skill under test staged int For baseline, variant, and quality modes: ``` -python3 {skill-root}/scripts/run_evals.py \ +uv run {skill-root}/scripts/run_evals.py \ --cases --skill-path --output-dir \ --mode quality|baseline|variant [--variant-path ] \ [--adapter ] [--runs N] @@ -71,7 +71,7 @@ The script stages the skill and any case fixtures, applies any `state_prefix` to For trigger mode: ``` -python3 {skill-root}/scripts/run_triggers.py \ +uv run {skill-root}/scripts/run_triggers.py \ --skill-path --queries --output-dir \ [--adapter ] [--runs-per-query N] ``` @@ -80,7 +80,7 @@ It stages a synthetic skill where the runtime discovers skills, sends each query For quality mode, spawn the grader described in `references/grader.md` per case, passing the case's rubric, transcript path, artifacts dir (the case's `cwd/`), and a `grading_path` of `/grading.json`. The grader writes that file, gives no partial credit, and flags weak or non-discriminating assertions; relay that feedback. If a grader subagent errors, mark that case `grading_error` — never substitute a default verdict. -When `--runs` is greater than one, call `python3 {skill-root}/scripts/aggregate_benchmark.py --baseline / --variant /` to produce the mean, sample standard deviation, min, max, and the delta between configs (`--runs /` for a single config's spread). +When `--runs` is greater than one, call `uv run {skill-root}/scripts/aggregate_benchmark.py --baseline / --variant /` to produce the mean, sample standard deviation, min, max, and the delta between configs (`--runs /` for a single config's spread). When a run fails or comes back weak and the user wants the skill improved from the results, follow `references/self-improvement.md`. diff --git a/skills/bmad-eval-runner/scripts/aggregate_benchmark.py b/skills/bmad-eval-runner/scripts/aggregate_benchmark.py index e4cf019..2746dd9 100644 --- a/skills/bmad-eval-runner/scripts/aggregate_benchmark.py +++ b/skills/bmad-eval-runner/scripts/aggregate_benchmark.py @@ -21,14 +21,14 @@ Usage: Summarize one config across its runs: - python3 aggregate_benchmark.py --runs CONFIG_A.json - python3 aggregate_benchmark.py --runs RUN_DIR/ (reads timing.json files) + uv run aggregate_benchmark.py --runs CONFIG_A.json + uv run aggregate_benchmark.py --runs RUN_DIR/ (reads timing.json files) Compare two configs (each summarized, then delta = B - A): - python3 aggregate_benchmark.py --baseline A.json --variant B.json + uv run aggregate_benchmark.py --baseline A.json --variant B.json Self-test on a known fixture (no external input needed): - python3 aggregate_benchmark.py --self-test + uv run aggregate_benchmark.py --self-test Output is one JSON object on stdout. """ diff --git a/skills/bmad-eval-runner/scripts/run_evals.py b/skills/bmad-eval-runner/scripts/run_evals.py index c518c93..7e9d162 100644 --- a/skills/bmad-eval-runner/scripts/run_evals.py +++ b/skills/bmad-eval-runner/scripts/run_evals.py @@ -66,7 +66,7 @@ recorded so the grader sees exactly what ran. Usage: - python3 run_evals.py \\ + uv run run_evals.py \\ --cases CASES.json \\ --skill-path SKILL_DIR \\ --output-dir DIR \\ diff --git a/skills/bmad-eval-runner/scripts/run_triggers.py b/skills/bmad-eval-runner/scripts/run_triggers.py index a406b27..c566346 100644 --- a/skills/bmad-eval-runner/scripts/run_triggers.py +++ b/skills/bmad-eval-runner/scripts/run_triggers.py @@ -38,7 +38,7 @@ and records "skipped: no runtime adapter configured" rather than crashing. Usage: - python3 run_triggers.py \\ + uv run run_triggers.py \\ --skill-path SKILL_DIR \\ --queries QUERIES.json \\ --output-dir DIR \\ diff --git a/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py b/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py index 7138412..205a5a0 100644 --- a/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py +++ b/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py @@ -6,8 +6,8 @@ deliberately self-contained); this test pins the contract on both copies: exactly PATH + fresh HOME + CLAUDE_CONFIG_DIR + auth-var-only-when-set + declared passthrough keys, nothing else. -Run with: python3 -m pytest test_env_isolation.py -(or plain `python3 test_env_isolation.py` for a lightweight self-check). +Run with: uv run --with pytest -m pytest test_env_isolation.py +(or plain `uv run test_env_isolation.py` for a lightweight self-check). """ import sys from pathlib import Path diff --git a/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py b/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py index 4c3e5fa..6e70f64 100644 --- a/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py +++ b/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py @@ -6,8 +6,8 @@ rate. These tests pin the rule: only tool_use events (a Skill call naming the synthetic skill, or a Read inside its directory) count as a load, and substring-style load signals are rejected outright. -Run with: python3 -m pytest test_trigger_detection.py -(or plain `python3 test_trigger_detection.py` for a lightweight self-check). +Run with: uv run --with pytest -m pytest test_trigger_detection.py +(or plain `uv run test_trigger_detection.py` for a lightweight self-check). """ import json import sys diff --git a/skills/bmad-module-builder/assets/setup-skill-template/SKILL.md b/skills/bmad-module-builder/assets/setup-skill-template/SKILL.md index 1712675..82c20ae 100644 --- a/skills/bmad-module-builder/assets/setup-skill-template/SKILL.md +++ b/skills/bmad-module-builder/assets/setup-skill-template/SKILL.md @@ -45,8 +45,8 @@ Write a temp JSON file with the collected answers structured as `{"core": {...}, In the commands below, replace `{project-root}` in every path argument with the actual project root (e.g. `/home/me/myapp`) before running — these are filesystem paths, not config values. ```bash -python3 ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" -python3 ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code {module-code} +uv run ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" +uv run ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code {module-code} ``` Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. The scripts automatically read legacy config values as fallback defaults, then delete the legacy files after a successful merge. Check `legacy_configs_deleted` and `legacy_csvs_deleted` in the output to confirm cleanup. @@ -64,7 +64,7 @@ After both merge scripts complete successfully, remove the installer's package d As with the merge scripts, replace `{project-root}` in the `--bmad-dir` and `--skills-dir` path arguments with the actual project root before running. ```bash -python3 ./scripts/cleanup-legacy.py --bmad-dir "{project-root}/_bmad" --module-code {module-code} --also-remove _config --skills-dir "{project-root}/.claude/skills" +uv run ./scripts/cleanup-legacy.py --bmad-dir "{project-root}/_bmad" --module-code {module-code} --also-remove _config --skills-dir "{project-root}/.claude/skills" ``` The script verifies that every skill in the legacy directories exists at `.claude/skills/` before removing anything. Directories without skills (like `_config/`) are removed directly. If the script exits non-zero, surface the error and stop. Missing directories (already cleaned by a prior run) are not errors — the script is idempotent. diff --git a/skills/bmad-module-builder/assets/standalone-module-template/module-setup.md b/skills/bmad-module-builder/assets/standalone-module-template/module-setup.md index f6f8508..380b9ff 100644 --- a/skills/bmad-module-builder/assets/standalone-module-template/module-setup.md +++ b/skills/bmad-module-builder/assets/standalone-module-template/module-setup.md @@ -56,8 +56,8 @@ Write a temp JSON file with the collected answers structured as `{"core": {...}, In the commands below, replace `{project-root}` in every path argument with the actual project root (e.g. `/home/me/myapp`) before running — these are filesystem paths, not config values. ```bash -python3 ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} -python3 ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --module-code {module-code} +uv run ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml ./assets/module.yaml --answers {temp-file} +uv run ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source ./assets/module-help.csv --module-code {module-code} ``` Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. diff --git a/skills/bmad-module-builder/references/create-module.md b/skills/bmad-module-builder/references/create-module.md index 0905caf..ffb6fc4 100644 --- a/skills/bmad-module-builder/references/create-module.md +++ b/skills/bmad-module-builder/references/create-module.md @@ -170,7 +170,7 @@ Iterate until the user confirms everything is correct. Write the confirmed module.yaml and module-help.csv content to temporary files at `{bmad_builder_reports}/{module-code}-temp-module.yaml` and `{bmad_builder_reports}/{module-code}-temp-help.csv`. Run the scaffold script: ```bash -python3 ./scripts/scaffold-setup-skill.py \ +uv run ./scripts/scaffold-setup-skill.py \ --target-dir "{skills-folder}" \ --module-code "{code}" \ --module-name "{name}" \ @@ -190,7 +190,7 @@ This creates `{code}-setup/` in the user's skills folder containing: Write the confirmed module.yaml and module-help.csv directly to the skill's `assets/` folder (create the folder if needed). Then run the standalone scaffold script to copy the template infrastructure: ```bash -python3 ./scripts/scaffold-standalone-module.py \ +uv run ./scripts/scaffold-standalone-module.py \ --skill-dir "{skill-folder}" \ --module-code "{code}" \ --module-name "{name}" diff --git a/skills/bmad-module-builder/references/validate-module.md b/skills/bmad-module-builder/references/validate-module.md index d1b3459..0b21106 100644 --- a/skills/bmad-module-builder/references/validate-module.md +++ b/skills/bmad-module-builder/references/validate-module.md @@ -20,7 +20,7 @@ Ask the user for the path to their module's skills folder (or a single skill fol Run the validation script for deterministic checks: ```bash -python3 ./scripts/validate-module.py "{module-skills-folder}" +uv run ./scripts/validate-module.py "{module-skills-folder}" ``` This checks: module structure (setup skill or standalone), module.yaml completeness, CSV integrity (missing entries, orphans, duplicate menu codes, broken before/after references, missing required fields). For standalone modules, it also verifies the presence of module-setup.md and merge scripts. diff --git a/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py b/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py index 6b62b37..232bb5d 100644 --- a/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py +++ b/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py @@ -6,8 +6,8 @@ agents — with no in-file sync note, because the loaded files are LLM-facing and a maintenance comment there is paid on every load. This test is the sync mechanism instead: all three copies must be byte-identical. -Run with: python3 -m pytest test_canon_sync.py -(or plain `python3 test_canon_sync.py` for a lightweight self-check). +Run with: uv run --with pytest -m pytest test_canon_sync.py +(or plain `uv run test_canon_sync.py` for a lightweight self-check). """ import sys from pathlib import Path diff --git a/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py b/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py index 2bb4b6a..3871ad4 100644 --- a/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py +++ b/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py @@ -3,8 +3,8 @@ Covers the output schema, the tiktoken path and the forced-fallback path agreeing within tolerance, the CLI over a file and over stdin, and argument -guards. Run with: python3 -m pytest test_count_tokens.py -(or plain `python3 test_count_tokens.py` to run a lightweight self-check). +guards. Run with: uv run --with pytest -m pytest test_count_tokens.py +(or plain `uv run test_count_tokens.py` to run a lightweight self-check). """ import builtins import importlib.util diff --git a/skills/bmad-workflow-builder/scripts/tests/test_render_report.py b/skills/bmad-workflow-builder/scripts/tests/test_render_report.py index 156a265..7d49200 100644 --- a/skills/bmad-workflow-builder/scripts/tests/test_render_report.py +++ b/skills/bmad-workflow-builder/scripts/tests/test_render_report.py @@ -4,8 +4,8 @@ Covers: valid island injection, refusal on malformed JSON, refusal on the placeholder subject, the --md archival rendering, and that both shipped shells carry a parseable placeholder island. -Run with: python3 -m pytest test_render_report.py -(or plain `python3 test_render_report.py` for a lightweight self-check). +Run with: uv run --with pytest -m pytest test_render_report.py +(or plain `uv run test_render_report.py` for a lightweight self-check). """ import json import re From 6759001cb614c7b1e2885af8985bf1fe0bdd4c24 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sun, 9 Aug 2026 23:44:28 -0500 Subject: [PATCH 2/2] docs(changelog): add 2.2.0 entry and bump marketplace plugin versions Documents the uv run conversion in this branch, and notes plainly that the 2.1.0 entry's claim about #98 covered only two of the five skills. All three marketplace plugins go 2.1.0 -> 2.2.0. --- .claude-plugin/marketplace.json | 6 +- CHANGELOG.md | 208 +++++++++++++++++--------------- 2 files changed, 117 insertions(+), 97 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 43d5471..b60221c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,7 +12,7 @@ "name": "bmad-builder", "source": "./", "description": "Build AI agents, workflows, and modules from a conversation. Four skills — Agent Builder, Workflow Builder, Module Builder, and Setup — guide you from idea to production-ready skill structure with built-in quality optimization. Part of the BMad Method ecosystem.", - "version": "2.1.0", + "version": "2.2.0", "author": { "name": "Brian (BMad) Madison" }, @@ -22,7 +22,7 @@ "name": "sample-plugins", "source": "./", "description": "Sample plugins demonstrating how to build BMad agents and skills. Includes a code coach, creative muse, diagram reviewer, dream weaver, sentinel, and excalidraw generator.", - "version": "2.1.0", + "version": "2.2.0", "author": { "name": "Brian (BMad) Madison" }, @@ -40,7 +40,7 @@ "name": "bmad-dream-weaver-agent", "source": "./", "description": "Dream journaling and interpretation agent with lucid dreaming coaching, pattern discovery, symbol analysis, and recall training.", - "version": "2.1.0", + "version": "2.2.0", "author": { "name": "Brian (BMad) Madison" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index cecadd6..5745e39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,36 @@ # Changelog +## [2.2.0] - 2026-08-09 + +### 🐛 Fixes + +- **`uv run` conversion finished across the remaining skills** — v2.1.0 claimed `uv run` was standardized across builder scripts, but #98 reached only `bmad-agent-builder` and `bmad-workflow-builder`. `bmad-bmb-setup`, `bmad-module-builder`, and `bmad-eval-runner` were missed. This converts the remaining 57 invocations across `skills/`, `samples/`, and `docs/how-to/`. It matters beyond consistency: a bare `python3` is not guaranteed to be 3.11+, which BMad's shared `resolve_customization.py` requires for `tomllib`, and `uv run` reads a script's own `requires-python` and provisions a matching interpreter (#105). + +- **Emitted templates no longer pass the defect on** — five of the fixes are in `assets/setup-skill-template/` and `assets/standalone-module-template/`, so every module scaffolded by `bmad-module-builder` stops inheriting bare-`python3` invocations. This also closes a visible seam: `init-sanctum-template.py` had been converted in #98, but the three `samples/*/scripts/init-sanctum.py` copies generated from it still said `python3` (#105). + +- **pytest docstrings call `uv run --with pytest -m pytest`** — the eval-runner and workflow-builder test files carry no PEP 723 block, so `uv run -m pytest` alone has nothing to resolve pytest from. Matches the convention in BMAD-METHOD (#105). + +- **README Python badge corrected to `>=3.11`** — it advertised `>=3.10`, a floor that cannot run the shared resolver (#105). + +### 🔧 Maintenance + +- **Marketplace plugin versions synced to 2.2.0** — nothing in the release workflow touches `.claude-plugin/marketplace.json`, so its versions drift from `package.json` unless bumped by hand. + +### Notes + +- Deliberately unconverted: the two anti-pattern cells in `docs/explanation/` (`scripts-in-skills.md`, labelled **Fragile**, and `skill-authoring-best-practices.md`, labelled "do not modify") — they teach what not to do; the "runs under a bare python3" prose in `count_tokens.py` and `prepass.py`, which describes a script's lack of third-party dependencies rather than invoking anything; and the per-script `requires-python` floors, which are correct as written. + ## [2.1.0] - 2026-06-22 ### 🐛 Fixes -* **Standalone module validation hardening** — `validate-module.py` no longer emits false-positive findings for correctly-structured standalone single-skill modules. It now accepts either `merge-config.py`/`merge_config.py` naming (dash or importable underscore form), skips colon-less `preceded-by`/`followed-by` cross-module refs that are unresolvable in isolation while still validating intra-module `skill:action` refs, and recognizes a module when handed its own skill directory directly (#97). +- **Standalone module validation hardening** — `validate-module.py` no longer emits false-positive findings for correctly-structured standalone single-skill modules. It now accepts either `merge-config.py`/`merge_config.py` naming (dash or importable underscore form), skips colon-less `preceded-by`/`followed-by` cross-module refs that are unresolvable in isolation while still validating intra-module `skill:action` refs, and recognizes a module when handed its own skill directory directly (#97). ### 🔧 Maintenance -* **Builders use runtime-installed memlog** — Workflow Builder and Agent Builder now point at the shared runtime memlog CLI at `{project-root}/_bmad/scripts/memlog.py` instead of each bundling its own copy. Removes drift between copies; `{project-root}` resolves at runtime so the call works from any skill root. Bundled `scripts/memlog.py` copies (and the workflow-builder memlog test) were deleted, and the obsolete "copy the CLI into each built skill" guidance was rewritten (#98). +- **Builders use runtime-installed memlog** — Workflow Builder and Agent Builder now point at the shared runtime memlog CLI at `{project-root}/_bmad/scripts/memlog.py` instead of each bundling its own copy. Removes drift between copies; `{project-root}` resolves at runtime so the call works from any skill root. Bundled `scripts/memlog.py` copies (and the workflow-builder memlog test) were deleted, and the obsolete "copy the CLI into each built skill" guidance was rewritten (#98). -* **`uv run` standardized across builder scripts** — Prompt-facing script invocations, usage strings, and emitted init-sanctum/wake templates now call `uv run