refactor(skills): use PEP 723 inline deps + uv run for resolver

BMB standard and Anthropic's Agent Skills spec both reference the
PEP 723 + uv pattern: declare dependencies in an inline script
header, invoke via `uv run` so deps auto-install into a cached
isolated environment on first run.

- resolve_customization.py: add PEP 723 header declaring pyyaml>=6.0
  and requires-python>=3.10.
- Remove src/scripts/requirements.txt (superseded by inline metadata).
- Update 6 agent SKILL.md files to invoke `uv run` instead of `python3`.
- Update docs/how-to/customize-bmad.md to explain the uv requirement
  and the PEP 723 pattern, with a plain-python3 fallback note.
- Refresh the script's ImportError message to point at uv run first.
This commit is contained in:
Brian Madison 2026-04-18 20:14:58 -05:00
parent da5016d34a
commit 6a5b814881
9 changed files with 28 additions and 19 deletions

View File

@ -183,31 +183,35 @@ agent:
## How Resolution Works
On activation, the agent's SKILL.md runs a shared Python script that does the three-layer merge and returns the resolved `agent` block as JSON. The script requires Python 3.8+ and PyYAML (`pip install PyYAML`).
On activation, the agent's SKILL.md runs a shared Python script that does the three-layer merge and returns the resolved `agent` block as JSON. The script uses [PEP 723 inline script metadata](https://peps.python.org/pep-0723/) to declare its dependency on PyYAML, and is designed to be invoked via [`uv`](https://docs.astral.sh/uv/):
```bash
python3 {project-root}/_bmad/scripts/resolve_customization.py \
uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill {skill-root} \
--key agent
```
`uv run` reads the inline metadata, creates a cached isolated environment with PyYAML installed, and runs the script. First run takes a few seconds while the env is built; subsequent runs reuse the cache and are instant.
**Requirements**: Python 3.10+ and `uv` (install via `brew install uv`, `pip install uv`, or [the official installer](https://docs.astral.sh/uv/getting-started/installation/)). If `uv` isn't available, the script can be run with plain `python3` provided PyYAML is already installed (`pip install PyYAML`).
`--skill` points at the skill's installed directory (where `customize.yaml` lives). The skill name is derived from the directory's basename, and the script looks up `_bmad/custom/{skill-name}.yaml` and `{skill-name}.user.yaml` automatically.
Useful invocations:
```bash
# Resolve the full agent block
python3 {project-root}/_bmad/scripts/resolve_customization.py \
uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /abs/path/to/bmad-agent-pm \
--key agent
# Resolve a single field
python3 {project-root}/_bmad/scripts/resolve_customization.py \
uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /abs/path/to/bmad-agent-pm \
--key agent.metadata.name
# Full dump (everything under agent plus any other top-level keys)
python3 {project-root}/_bmad/scripts/resolve_customization.py \
uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /abs/path/to/bmad-agent-pm
```

View File

@ -13,7 +13,7 @@ description: Strategic business analyst and requirements expert. Use when the us
### Step 1: Resolve the Agent Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
**If the script fails**, resolve the `agent` block yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).

View File

@ -13,7 +13,7 @@ description: Technical documentation specialist and knowledge curator. Use when
### Step 1: Resolve the Agent Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
**If the script fails**, resolve the `agent` block yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).

View File

@ -13,7 +13,7 @@ description: Product manager for PRD creation and requirements discovery. Use wh
### Step 1: Resolve the Agent Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
**If the script fails**, resolve the `agent` block yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).

View File

@ -13,7 +13,7 @@ description: UX designer and UI specialist. Use when the user asks to talk to Sa
### Step 1: Resolve the Agent Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
**If the script fails**, resolve the `agent` block yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).

View File

@ -13,7 +13,7 @@ description: System architect and technical design leader. Use when the user ask
### Step 1: Resolve the Agent Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
**If the script fails**, resolve the `agent` block yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).

View File

@ -13,7 +13,7 @@ description: Senior software engineer for story execution and code implementatio
### Step 1: Resolve the Agent Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key agent`
**If the script fails**, resolve the `agent` block yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).

View File

@ -1 +0,0 @@
PyYAML>=6.0

View File

@ -1,4 +1,8 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = ["pyyaml>=6.0"]
# ///
"""
Resolve customization for a BMad skill using three-layer YAML merge.
@ -11,10 +15,12 @@ Skill name is derived from the basename of the skill directory.
Outputs merged JSON to stdout. Errors go to stderr.
Usage:
python3 resolve_customization.py --skill /abs/path/to/skill-dir
python3 resolve_customization.py --skill ... --key agent
python3 resolve_customization.py --skill ... --key agent --key agent.menu
Dependencies declared inline via PEP 723. Invoke with `uv run` to
auto-install PyYAML into an isolated, cached environment:
uv run resolve_customization.py --skill /abs/path/to/skill-dir
uv run resolve_customization.py --skill ... --key agent
uv run resolve_customization.py --skill ... --key agent --key agent.menu
Merge rules (matches BMad v6.1 semantics where applicable):
- metadata: shallow merge (scalar fields override)
@ -25,8 +31,6 @@ Merge rules (matches BMad v6.1 semantics where applicable):
- other tables: deep merge
- other arrays: atomic replace
- scalars: override wins
Requires PyYAML. Install with: pip install PyYAML
"""
import argparse
@ -39,7 +43,9 @@ try:
except ImportError:
sys.stderr.write(
"error: PyYAML is required to run this script.\n"
"Install it with: pip install PyYAML\n"
"Invoke via `uv run resolve_customization.py ...` so dependencies\n"
"declared in the PEP 723 header are auto-installed, or run\n"
"`pip install PyYAML` if invoking with plain `python3`.\n"
)
sys.exit(3)