fix(bmad-customize): address PR #2289 review findings

- SKILL.md preflight: load root config from _bmad/config.toml and
  config.user.toml (not .yaml) — the installer emits TOML; the YAML
  references would have made the skill silently miss real user config
- SKILL.md resolver fallback (Step 6.4): read all three merge layers
  when present (base / team / user) and describe the merge in
  base → team → user order; the prior wording could describe the wrong
  effective merge when the user wrote .user.toml on top of an existing
  team .toml
- SKILL.md: replace bare 'docs/how-to/customize-bmad.md' references
  (3 locations) with the public docs URL so users installing the skill
  aren't pointed at a path they don't have locally
- list_customizable_skills.py: catch UnicodeDecodeError in
  read_frontmatter_description so a non-UTF-8 SKILL.md can't abort
  the whole scan
- list_customizable_skills.py: clarify exit-code contract in the
  module docstring — errors[] is non-fatal by design, exit 2 is
  reserved for invocation errors
- customize-bmad.md: tighten the tip to scope bmad-customize to the
  per-skill surface; central-config is out of scope v1
- expand-bmad-for-your-org.md: same scoping — Recipes 1-4 can be
  applied by the skill; Recipe 5 (central config) stays hand-authored
This commit is contained in:
Brian Madison 2026-04-20 21:50:44 -05:00
parent 2d804fdad7
commit da8d5d6b6d
4 changed files with 14 additions and 9 deletions

View File

@ -8,7 +8,7 @@ sidebar:
Tailor agent personas, inject domain context, add capabilities, and configure workflow behavior -- all without modifying installed files. Your customizations survive every update.
:::tip[Don't want to hand-author TOML? Use `bmad-customize`]
The `bmad-customize` skill is a guided authoring helper for the customization surface described in this doc. It scans what's customizable in your installation, helps you choose the right surface (agent vs workflow) for your intent, writes the override file for you, and verifies the merge landed. Run it whenever you want to make a change; this doc is the reference for *what* the surface exposes and how merging works.
The `bmad-customize` skill is a guided authoring helper for the **per-skill agent/workflow override surface** described in this doc. It scans what's customizable in your installation, helps you choose the right surface (agent vs workflow) for your intent, writes the override file for you, and verifies the merge landed. Central-config overrides (`_bmad/custom/config.toml`) are out of scope for v1 — hand-author those per the Central Configuration section below. Run the skill whenever you want to make a per-skill change; this doc is the reference for *what* each surface exposes and how merging works.
:::
## When to Use This

View File

@ -15,7 +15,7 @@ BMad's customization surface lets an organization reshape behavior without editi
:::
:::tip[Applying these recipes]
Every recipe below can be applied by running the `bmad-customize` skill and describing the intent — it will pick the right surface, author the override file, and verify the merge. The recipes here are the source of truth for *what* to override; `bmad-customize` handles the *how*. Hand-authoring still works the same way if you prefer it or need a pattern the skill doesn't cover yet (currently: central-config overrides).
The **per-skill recipes** below (Recipes 14) can be applied by running the `bmad-customize` skill and describing the intent — it will pick the right surface, author the override file, and verify the merge. Recipe 5 (central-config overrides to the agent roster) is out of scope for v1 of the skill and remains hand-authored. The recipes here are the source of truth for *what* to override; `bmad-customize` handles the *how* for the agent/workflow surface.
:::
## The Three-Layer Mental Model

View File

@ -11,7 +11,7 @@ Translate a user's intent ("I want X to behave differently") into a correctly-pl
**What customization means in BMad.** Every customizable skill ships a `customize.toml` that declares the knobs it exposes — scalars, arrays, and keyed tables under `[agent]` or `[workflow]`. Users never edit that file. Instead, they write sparse override files to `{project-root}/_bmad/custom/`, and the resolver merges base → team → user at activation. This skill's job is to help users author those override files correctly. Users typically arrive either with a specific skill and change in mind ("make bmad-create-prd require a brief first") or with a broader want ("the PM agent should speak more formally"); you handle both.
Scope for this version: per-skill **agent** overrides (`bmad-agent-<role>.toml` / `.user.toml`) and per-skill **workflow** overrides (`bmad-<workflow>.toml` / `.user.toml`). Central config (`{project-root}/_bmad/custom/config.toml`) is out of scope — flag it and point the user at `docs/how-to/customize-bmad.md` if their ask lives there.
Scope for this version: per-skill **agent** overrides (`bmad-agent-<role>.toml` / `.user.toml`) and per-skill **workflow** overrides (`bmad-<workflow>.toml` / `.user.toml`). Central config (`{project-root}/_bmad/custom/config.toml`) is out of scope — flag it and point the user at the **How to Customize BMad** guide (https://docs.bmad-method.org/how-to/customize-bmad/) if their ask lives there.
## Desired Outcomes
@ -38,7 +38,7 @@ Before any other work, verify the project environment supports this skill:
### Config and greet
Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root level). Defaults if missing: `user_name` (BMad), `communication_language` (English). Greet the user and acknowledge the topic.
Load available config from `{project-root}/_bmad/config.toml` and `{project-root}/_bmad/config.user.toml` (root level, installer-owned). Defaults if missing: `user_name` (BMad), `communication_language` (English). Greet the user and acknowledge the topic.
Treat the user's invoking message as initial intent. Skip discovery if they already named a target skill AND a specific change — go straight to Step 3.
@ -144,7 +144,7 @@ Default the choice based on the change's character (policy → team, personal
Display the merged output and point out the fields that changed so the user sees their override took effect.
**If the resolver is missing or fails**, fall back: read the three files (`<install-path>/customize.toml`, the written override, and any sibling `.user.toml`) directly, describe how the merge resolves for the fields the user just changed, and tell them the normal verify path is unavailable in this environment.
**If the resolver is missing or fails**, fall back: read whichever of the three merge layers exist — `<install-path>/customize.toml` (base), `{project-root}/_bmad/custom/{skill-name}.toml` (team), and `{project-root}/_bmad/custom/{skill-name}.user.toml` (user). Apply base → team → user in order using the same merge rules as the resolver (scalars override, tables deep-merge, `code`/`id`-keyed arrays of tables merge by key, all other arrays append). Describe how the fields the user just changed resolve, and tell them the normal verify path is unavailable in this environment.
**If verification shows the override did not take effect** (field unchanged, resolver reports merge conflict, override file not picked up), do not declare success. Explain what the resolver showed, re-enter Step 4 with the verify output as new context — usually the fix is a field name, merge-mode mismatch (e.g. wrote a scalar where the base expects an array), or wrong placement scope.
@ -164,7 +164,7 @@ If any of these is missing, the skill is not done — either finish the remainin
Say so clearly:
- **Central config** (`{project-root}/_bmad/custom/config.toml` — agent roster, install answers) is not covered by this version. Point the user at `docs/how-to/customize-bmad.md`.
- **Central config** (`{project-root}/_bmad/custom/config.toml` — agent roster, install answers) is not covered by this version. Point the user at the **How to Customize BMad** guide (https://docs.bmad-method.org/how-to/customize-bmad/).
- **Changes to step logic, step ordering, or behavior not exposed in `customize.toml`** require a customization feature request or using bmad builder to create a custom skill. Offer to help with either path.
- **Skills without a `customize.toml`** are not customizable — fork is the only path.
@ -172,4 +172,4 @@ Say so clearly:
- Override files are sparse. Everything omitted inherits from the layer below (base → team → user).
- The scanner does not hardcode IDE paths. It scans whichever directory this skill itself was loaded from — that's the same place the user's other skills live in this session. For mixed project-local + user-global setups, use `--extra-root`.
- Full reference on the customization surface, merge rules, and central config lives in `docs/how-to/customize-bmad.md`.
- Full reference on the customization surface, merge rules, and central config lives in the **How to Customize BMad** guide: https://docs.bmad-method.org/how-to/customize-bmad/.

View File

@ -18,7 +18,12 @@ running skill's own location is the source of truth for sibling discovery.
`--extra-root` is available for the rare case where skills live in multiple
locations on the same machine.
Output: JSON to stdout. Exit 0 on success (including empty result), 2 on error.
Output: JSON to stdout. Non-empty `errors[]` in the payload is non-fatal
by contract the scanner surfaces malformed TOML, missing roots, and
skills with no customization block as data for the caller to display,
and still exits 0. Exit 2 is reserved for invocation errors (e.g.
missing or unreadable `--project-root`) where no useful payload can be
produced.
"""
from __future__ import annotations
@ -56,7 +61,7 @@ def read_frontmatter_description(skill_md: Path) -> str:
return ""
try:
text = skill_md.read_text(encoding="utf-8")
except OSError:
except (OSError, UnicodeDecodeError):
return ""
m = FRONTMATTER_RE.match(text)
if not m: