Compare commits

...

8 Commits

Author SHA1 Message Date
Zied Jlassi 8b1a47ff53
Merge aba48f90f1 into b483e66c3b 2026-06-23 10:51:14 -07:00
Alex Verkhovsky b483e66c3b
fix(quick-dev): backport dev auto prompt fixes (#2501)
* fix(quick-dev): tighten ready standard

Add sufficiency and coherence criteria so approval does not mask unresolved gaps, ambiguities, or contradictions in generated specs.

* fix(quick-dev): verify acceptance before review

Make the active session verify both tasks and acceptance criteria before review, and remove the redundant acceptance auditor review layer.

* fix(quick-dev): persist review loop counter

Store the review loop counter in spec frontmatter so loopbacks cannot reset it by re-reading the review step file.

* style(quick-dev): normalize subagent spelling

Use subagent and subagents consistently across Quick Dev step files.

* docs(quick-dev): clarify customization comments

Replace internal merge jargon and stale workflow comments with plain descriptions of the Quick Dev customization file behavior.

* fix(quick-dev): structure deferred work entries

Make Quick Dev append deferred work as source_spec, summary, and evidence entries without modifying existing entries or deduplicating during the run.
2026-06-23 08:44:57 -07:00
Alex Verkhovsky 20f7fa37e2
Add bmad-dev-auto unattended workflow (#2500)
* feat: add bmad dev auto skill

* fix: replace dev auto approval gate

* fix: remove dev auto one-shot route

* fix: tighten dev auto review step

* fix: inline dev auto finalization

* fix: clarify dev auto sprint sync

* fix: clarify dev auto epic context output

* fix: structure dev auto deferred work entries

* fix: remove dev auto sprint status coupling

* fix: run dev auto completion hook on blocked exits

* fix: persist dev auto review loop counter

* fix: remove dev auto approval-era intent marker

* fix: simplify dev auto final result handoff

* fix: tighten dev auto workflow contract

* fix: clarify dev auto verification owner

* fix: strengthen dev auto verification gates

Require the auto workflow to re-read its READY standard before judging the spec, and make implementation verification cover both tasks and acceptance criteria with a blocked HALT when missing work cannot be completed.

* fix: tighten dev auto prompt wording

Remove remaining human-interaction atavisms, make subagent wording consistent, and clarify blocked verification handling in the unattended development workflow.

* docs: clarify dev auto customization comments

Replace internal merge jargon and stale workflow comments with plain descriptions of the customization file behavior.
2026-06-23 04:53:11 -07:00
github-actions[bot] 6ac4c26b69 chore(release): v6.9.0 [skip ci] 2026-06-22 05:15:11 +00:00
Brian d570e70caf
docs: draft v6.9.0 changelog (#2496) 2026-06-22 00:12:33 -05:00
Brian 02739932bc
feat(installer): check for uv and standardize messaging on uv run (#2495)
Replace the standalone python3 environment check with a uv check, since
uv is becoming the de facto standard for running BMad's Python scripts
(`uv run <script>`) and uv provisions the interpreter itself.

Installer:
- Remove tools/installer/core/python-check.js and its wiring in ui.js
- Add tools/installer/core/uv-check.js: warn-don't-block, no ack prompt
  (the migration is in progress, so a missing uv never blocks install).
  Missing uv warns and points the user at setup, preferring "ask your
  agent to set up uv"
- Add a uv heads-up to the install intro (install-messages.yaml) and a
  uv tip line to the final "BMAD is ready" summary box
- Swap test Suite 46 from python-check to uv-check coverage

Docs and script comments (no functional skill invocations changed):
- resolve_config.py / resolve_customization.py docstrings: drop the
  "No uv ... plain python3 is sufficient" claims; frame uv run as the
  standard, python3 as the transition fallback; examples use uv run
- customize-bmad.md (en/fr/vi-vn): same reframing; example commands use
  uv run
- Update uv run hints in brain.py / list_customizable test comments
2026-06-22 00:03:03 -05:00
Zied Jlassi aba48f90f1 test(validate-skills): document test helpers with JSDoc
Added JSDoc to the test/assert/hasTriggerFinding helpers so the new
test file documents its own helpers. No behaviour change — the suite
still passes 3/3.
2026-06-20 14:38:38 +02:00
Zied Jlassi 05c1b20458 fix(validate-skills): exempt deprecated skills from SKILL-06 trigger
SKILL-06 flagged deprecated compatibility shims for missing a "Use when"
trigger phrase. Deprecated skills omit it on purpose so users are steered
to their replacement, so exempt them from that specific check.

- detect deprecation via description starting with "DEPRECATED"
- keep the other SKILL-06 check (length) and all other rules applying
- add test/test-validate-skills.js + fixtures, wired into npm test
2026-06-20 11:38:33 +02:00
39 changed files with 911 additions and 394 deletions

View File

@ -1,5 +1,46 @@
# Changelog # Changelog
## v6.9.0 - 2026-06-21
### ✨ Headline
**Reasoning skills get sharper and orchestration gets a memory.**
**bmad-forge-idea** is a new core skill that takes a half-formed idea and pressure-tests it one Socratic question at a time — with an adversarial attack mode and optional persona rooms — until the idea hardens, proves out, or dies cheaply.
**bmad-architecture** lands as a ground-up rewrite of the old multi-step create-architecture flow: a lean spine (`ARCHITECTURE-SPINE.md`) that is the source of truth, intent-based routing (Create/Update/Validate), a breadth-coverage rubric so no dimension is silently skipped, and an opt-in reviewer gate.
**party-mode** is reborn with creatable, savable custom parties, optional party memory, and many pacing and dynamics improvements.
**Under the hood:** a canonical shared **memlog** (`_bmad/scripts/memlog.py`) replaces per-skill decision logs and is now the standard working-memory primitive across the suite. The installer now checks for **uv** and reframes it as the standard way to run BMAD's Python scripts (`uv run`). Plus an **Astro 6** security upgrade clearing 8+ Dependabot advisories and two new platform targets.
### ⚠️ Upcoming Breaking Change (in v7) — standardizing on `uv`
The industry is converging on [**uv**](https://docs.astral.sh/uv/) for running Python, and BMAD is following. Today our skills use a **mix** of `uv run` and direct `python3` invocation. In the **v7 release, every skill that runs a Python script will standardize on `uv run`** instead of calling `python3` directly — `uv` provisions the interpreter and manages dependencies, so scripts run consistently regardless of what's on your PATH.
**What to do now:** install and set up `uv` ([docs](https://docs.astral.sh/uv/)) — or just ask your AI agent to "install and set up uv for me." Starting this release the installer checks for it and points you to setup if it's missing. `uv` is **not yet required** but without it some skills may have degraded performance or a shim AGENTS.md (or similar) or rule will need to be added to your environment to tell the agent when it sees uv run to use python3 instead. The best course of action though at this time is to install uv. A missing `uv` still warns rather than blocks, but it will be the assumed default in v7. Custom skills and overrides that shell out to `python3` should plan to migrate to `uv run`.
### 🎁 Features
* **bmad-forge-idea — new core skill** (#2492). Domain-agnostic idea pressure-testing for the analysis phase: Socratic, one-question-at-a-time interrogation with an adversarial attack mode and optional persona rooms resolved from the installed roster. Hardens or kills an idea cheaply; emits memlog residue and an optional brief that feeds bmad-spec or bmad-quick-dev. Interactive only (menu code FI).
* **bmad-architecture — lean spine rewrite** (#2467, #2475). Replaces the fixed-step `bmad-create-architecture` (retained as a forwarding shim, removed in v7) with intent-based routing across five entry shapes (raw idea, large doc, codebase, feature slice, existing spine). The spine (`ARCHITECTURE-SPINE.md`) is the source of truth and SPEC.md is derived from it. Adds a breadth-coverage rubric (every altitude-owned dimension decided/deferred/open), an opt-in reviewer gate that scales lenses to rigor, and a full non-interactive headless mode. `lint_spine.py` hardened with fence-blanking, robust column detection, and 28 regression tests.
* **party-mode: configurable parties + persistent memory** (#2479, #2484). Custom personas (`party_members`) and named rooms (`party_groups`, with optional scenes), four run modes (auto/session/subagent/agent-team), and a preloaded "Code Review Crew" of five adversarial lenses. Each party keeps append-only session memory under `{memory_dir}/<party_id>/` so sessions resume with prior context; ad-hoc casts stay ephemeral.
* **bmad-brainstorming: facilitation modes + visual composer** (#2445). Three modes (Facilitator / Creative Partner / Ideate for me), append-only memlog with optional `--by` authorship attribution, and a self-contained `brain-selector.html` composer (technique strategy, category chips, filter, copy-to-clipboard, dark mode). Catalog grows to 108 techniques (8 new classics: HMW, JTBD, Empathy Map, Backcasting, TRIZ, Fishbone, Build on What Works, Scenario Cross) plus a convergence phase.
* **Canonical shared memlog script** (#2462). New `src/scripts/memlog.py` — append-only chronological working memory with init/append/set ops, no lifecycle-status design, Python 3.8+ support, 30 tests. Any skill can call it at runtime.
* **Retrospective action items tracked in sprint-status** (#2465). The retrospective step appends an `action_items` section to `sprint-status.yaml`; sprint-status validates and surfaces open items, and sprint-planning preserves them on regenerate.
* **Installer checks for `uv` and reframes it as the standard** (#2495). Replaces the old python3 probe with a `uv` check, adds a heads-up to the install intro and a tip to the "BMAD is ready" summary, and updates docs/script docstrings (en/fr/vi-vn) to frame `uv run` as the standard and `python3` as the transition fallback. Migration-friendly: a missing `uv` warns and points you to setup, never blocks. See the Upcoming Breaking Change note above.
* **New installer platform targets: hermes-agent and CodeWhale** (#2489, #2459). hermes-agent added as a tool target; CodeWhale uses `.codewhale/skills/` (project) and `~/.codewhale/skills/` (global), both with test coverage.
### 🐛 Fixes
* **Astro 6 security upgrade clears Dependabot alerts** (#2493). Astro 5.18.1 → 6.4.6 and Starlight 0.37.5 → 0.40.0 (8 XSS/SSRF advisories), esbuild pinned to 0.28.1 (Windows dev-server file read), markdown-it 14.2.0 (smartquotes ReDoS), brace-expansion 5.0.6 (range DoS). Docs content config migrated to `src/content.config.ts`; page output verified identical to baseline.
* **Guard WSL installs from Windows Node** (#2470). Detects and prevents a Windows `node.exe` being used inside WSL, where it would silently fail.
* **Remove empty skill-group dirs after install** (#2461). Prunes empty parent dirs (e.g. `_bmad/bmm/1-analysis`) left after skill cleanup, with a path-boundary check to avoid sibling-dir collisions.
* **bmad-create-epics-and-stories discovers bmad-ux spine outputs** (#2446). Prerequisites now recognize `DESIGN.md` / `EXPERIENCE.md` alongside the legacy `ux-spec.md`.
* **Pass diff inline to the blind-hunter reviewer** (#2463). Diff output is passed inline in the subagent prompt rather than via a file the reviewer can't read, preventing context-starved hallucination.
* **Website: nav height for dual announcement banners** (#2473). Fixes layout crowding when two banners show at once.
* **Workflow clarity & numbering** — clarify quick-dev subagent use across code-review/create-story/quick-dev (#2450), renumber retrospective steps (#2448).
## v6.8.0 - 2026-05-25 ## v6.8.0 - 2026-05-25
### ✨ Headline ### ✨ Headline

View File

@ -22,7 +22,7 @@ Le skill `bmad-customize` est un assistant de rédaction guidée pour les **opti
:::note[Prérequis] :::note[Prérequis]
- BMad installé dans votre projet (voir [Comment installer BMad](./install-bmad.md)) - BMad installé dans votre projet (voir [Comment installer BMad](./install-bmad.md))
- Python 3.11+ sur votre PATH (pour le script de résolution — utilise `tomllib` de la bibliothèque standard, pas de `pip install`, pas de `uv`, pas de virtualenv) - Un moyen d’exécuter le script de résolution — BMad adopte `uv` comme standard (`uv run`, qui provisionne Python pour vous) ; un simple `python3` 3.11+ sur votre PATH fonctionne toujours pendant la transition. Le script n’utilise que `tomllib` de la bibliothèque standard, il n’y a donc rien à `pip install`.
- Un éditeur de texte pour les fichiers TOML - Un éditeur de texte pour les fichiers TOML
::: :::
@ -201,15 +201,15 @@ persistent_facts = [
## Comment fonctionne la résolution ## Comment fonctionne la résolution
À l’activation, le SKILL.md de l’agent exécute un script Python partagé qui effectue la fusion à trois couches et renvoie le bloc résolu en JSON. Le script utilise le module `tomllib` de la bibliothèque standard Python (aucune dépendance externe), donc `python3` suffit : À l’activation, le SKILL.md de l’agent exécute un script Python partagé qui effectue la fusion à trois couches et renvoie le bloc résolu en JSON. Le script utilise uniquement le module `tomllib` de la bibliothèque standard Python (aucune dépendance externe). BMad adopte `uv run` comme standard pour exécuter ces scripts (uv provisionne un Python adapté pour vous) ; un simple `python3` fonctionne toujours pendant la transition :
```bash ```bash
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill {skill-root} \ --skill {skill-root} \
--key agent --key agent
``` ```
**Prérequis** : Python 3.11+ (les versions antérieures n’incluent pas `tomllib`). Pas de `pip install`, pas de `uv`, pas de virtualenv. Vérifiez avec `python3 --version`. Certaines plateformes (macOS sans Homebrew, Ubuntu 22.04) ont `python3` par défaut en 3.10 ou antérieur, vous devrez peut-être installer 3.11+ séparément. **Prérequis** : Python 3.11+ (les versions antérieures n’incluent pas `tomllib`). Rien à `pip install`. L’exécution via `uv run` est le standard à venir — uv résout un interpréteur adapté pour vous. Si vous l’exécutez directement avec `python3` pendant la transition, vérifiez votre version avec `python3 --version` ; certaines plateformes (macOS sans Homebrew, Ubuntu 22.04) ont `python3` par défaut en 3.10 ou antérieur, vous devrez peut-être installer 3.11+ séparément.
`--skill` pointe vers le répertoire installé du skill (où se trouve `customize.toml`). Le nom du skill est déduit du basename du répertoire, et le script cherche automatiquement `_bmad/custom/{skill-name}.toml` et `{skill-name}.user.toml`. `--skill` pointe vers le répertoire installé du skill (où se trouve `customize.toml`). Le nom du skill est déduit du basename du répertoire, et le script cherche automatiquement `_bmad/custom/{skill-name}.toml` et `{skill-name}.user.toml`.
@ -217,17 +217,17 @@ Exemples d’utilisation :
```bash ```bash
# Résoudre le bloc agent complet # Résoudre le bloc agent complet
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /chemin/absolu/vers/bmad-agent-pm \ --skill /chemin/absolu/vers/bmad-agent-pm \
--key agent --key agent
# Résoudre un seul champ # Résoudre un seul champ
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /chemin/absolu/vers/bmad-agent-pm \ --skill /chemin/absolu/vers/bmad-agent-pm \
--key agent.icon --key agent.icon
# Dump complet # Dump complet
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /chemin/absolu/vers/bmad-agent-pm --skill /chemin/absolu/vers/bmad-agent-pm
``` ```

View File

@ -22,7 +22,7 @@ The `bmad-customize` skill is a guided authoring helper for the **per-skill agen
:::note[Prerequisites] :::note[Prerequisites]
- BMad installed in your project (see [How to Install BMad](./install-bmad.md)) - BMad installed in your project (see [How to Install BMad](./install-bmad.md))
- Python 3.11+ on your PATH (for the resolver script -- uses stdlib `tomllib`, no `pip install`, no `uv`, no virtualenv) - A way to run the resolver script — BMad is standardizing on `uv` (`uv run`, which provisions Python for you); a plain `python3` 3.11+ on your PATH still works during the transition. The script uses only stdlib `tomllib`, so there's nothing to `pip install`.
- A text editor for TOML files - A text editor for TOML files
::: :::
@ -201,15 +201,15 @@ persistent_facts = [
## How Resolution Works ## 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 block as JSON. The script uses the Python standard library's `tomllib` module (no external dependencies), so plain `python3` is enough: On activation, the agent's SKILL.md runs a shared Python script that does the three-layer merge and returns the resolved block as JSON. The script uses only the Python standard library's `tomllib` module (no external dependencies). BMad is standardizing on `uv run` to invoke these scripts (uv provisions a suitable Python for you); a plain `python3` still works during the transition:
```bash ```bash
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill {skill-root} \ --skill {skill-root} \
--key agent --key agent
``` ```
**Requirements**: Python 3.11+ (earlier versions don't include `tomllib`). No `pip install`, no `uv`, no virtualenv. Check with `python3 --version`. Some platforms (macOS without Homebrew, Ubuntu 22.04) default `python3` to 3.10 or earlier, so you may need to install 3.11+ separately. **Requirements**: Python 3.11+ (earlier versions don't include `tomllib`); nothing to `pip install`. Running via `uv run` is the going-forward standard — uv resolves a suitable interpreter for you. If you run it with `python3` directly during the transition, check your version with `python3 --version`: some platforms (macOS without Homebrew, Ubuntu 22.04) default `python3` to 3.10 or earlier, so you may need to install 3.11+ separately.
`--skill` points at the skill's installed directory (where `customize.toml` lives). The skill name is derived from the directory's basename, and the script looks up `_bmad/custom/{skill-name}.toml` and `{skill-name}.user.toml` automatically. `--skill` points at the skill's installed directory (where `customize.toml` lives). The skill name is derived from the directory's basename, and the script looks up `_bmad/custom/{skill-name}.toml` and `{skill-name}.user.toml` automatically.
@ -217,17 +217,17 @@ Useful invocations:
```bash ```bash
# Resolve the full agent block # 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 \ --skill /abs/path/to/bmad-agent-pm \
--key agent --key agent
# Resolve a single field # 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 \ --skill /abs/path/to/bmad-agent-pm \
--key agent.icon --key agent.icon
# Full dump # Full dump
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /abs/path/to/bmad-agent-pm --skill /abs/path/to/bmad-agent-pm
``` ```

View File

@ -99,7 +99,8 @@ Workflow skills run a structured, multi-step process without loading an agent pe
| `bmad-create-epics-and-stories` | Create epics and stories | | `bmad-create-epics-and-stories` | Create epics and stories |
| `bmad-dev-story` | Implement a story | | `bmad-dev-story` | Implement a story |
| `bmad-code-review` | Run a code review | | `bmad-code-review` | Run a code review |
| `bmad-quick-dev` | Unified quick flow — clarify intent, plan, implement, review, present | | `bmad-quick-dev` | Implement a story or any other small intent — clarify, plan, implement, review, present |
| `bmad-dev-auto` | One unattended development-loop iteration — small intent in, code out, no human interaction |
See [Workflow Map](./workflow-map.md) for the complete workflow reference organized by phase. See [Workflow Map](./workflow-map.md) for the complete workflow reference organized by phase.

View File

@ -94,6 +94,7 @@ Skip phases 1-3 for small, well-understood work.
| Workflow | Purpose | Produces | | Workflow | Purpose | Produces |
|------------------|---------------------------------------------------------------------------|--------------------| |------------------|---------------------------------------------------------------------------|--------------------|
| `bmad-quick-dev` | Unified quick flow — clarify intent, plan, implement, review, and present | `spec-*.md` + code | | `bmad-quick-dev` | Unified quick flow — clarify intent, plan, implement, review, and present | `spec-*.md` + code |
| `bmad-dev-auto` | One unattended development-loop iteration — small intent in, code out | `spec-*.md` + code |
## Context Management ## Context Management

View File

@ -22,7 +22,7 @@ Skill `bmad-customize` là trợ lý tạo cấu hình có hướng dẫn cho **
:::note[Điều kiện tiên quyết] :::note[Điều kiện tiên quyết]
- BMad đã được cài trong dự án của bạn (xem [Cách cài đặt BMad](./install-bmad.md)) - BMad đã được cài trong dự án của bạn (xem [Cách cài đặt BMad](./install-bmad.md))
- Python 3.11+ có trên PATH của bạn (để chạy resolver; dùng stdlib `tomllib`, không cần `pip install`, `uv` hay virtualenv) - Một cách để chạy resolver script — BMad đang chuẩn hóa sang `uv` (`uv run`, tự cấp Python cho bạn); một `python3` 3.11+ thuần trên PATH vẫn dùng được trong giai đoạn chuyển đổi. Script chỉ dùng stdlib `tomllib`, nên không cần `pip install` gì cả.
- Một trình soạn thảo văn bản cho file TOML - Một trình soạn thảo văn bản cho file TOML
::: :::
@ -201,15 +201,15 @@ persistent_facts = [
## Cách quá trình resolve diễn ra ## Cách quá trình resolve diễn ra
Khi agent được kích hoạt, `SKILL.md` của nó sẽ gọi một shared Python script để merge ba lớp nói trên và trả về block kết quả ở dạng JSON. Script này dùng `tomllib` của Python stdlib, nên `python3` thuần là đủ: Khi agent được kích hoạt, `SKILL.md` của nó sẽ gọi một shared Python script để merge ba lớp nói trên và trả về block kết quả ở dạng JSON. Script này chỉ dùng `tomllib` của Python stdlib (không có dependency ngoài). BMad đang chuẩn hóa sang `uv run` để chạy các script này (uv tự cấp một bản Python phù hợp cho bạn); một `python3` thuần vẫn dùng được trong giai đoạn chuyển đổi:
```bash ```bash
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill {skill-root} \ --skill {skill-root} \
--key agent --key agent
``` ```
**Yêu cầu**: Python 3.11+ vì các phiên bản cũ hơn không có `tomllib`. Không cần `pip install`, không cần `uv`, không cần virtualenv. Bạn có thể kiểm tra bằng `python3 --version`. Trên một số nền tảng, `python3` mặc định vẫn là 3.10 hoặc thấp hơn, nên có thể bạn sẽ phải cài 3.11+ riêng. **Yêu cầu**: Python 3.11+ vì các phiên bản cũ hơn không có `tomllib`; không cần `pip install` gì. Chạy qua `uv run` là chuẩn về sau — uv tự tìm một bản interpreter phù hợp cho bạn. Nếu bạn chạy trực tiếp bằng `python3` trong giai đoạn chuyển đổi, hãy kiểm tra phiên bản bằng `python3 --version`: trên một số nền tảng, `python3` mặc định vẫn là 3.10 hoặc thấp hơn, nên có thể bạn sẽ phải cài 3.11+ riêng.
`--skill` trỏ vào thư mục skill đã cài, nơi có file `customize.toml`. Tên skill được lấy từ basename của thư mục, sau đó script sẽ tự tìm `_bmad/custom/{skill-name}.toml` và `{skill-name}.user.toml`. `--skill` trỏ vào thư mục skill đã cài, nơi có file `customize.toml`. Tên skill được lấy từ basename của thư mục, sau đó script sẽ tự tìm `_bmad/custom/{skill-name}.toml` và `{skill-name}.user.toml`.
@ -217,17 +217,17 @@ Một số lệnh hữu ích:
```bash ```bash
# Resolve toàn bộ block agent # Resolve toàn bộ block agent
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /duong-dan/tuyet-doi/toi/bmad-agent-pm \ --skill /duong-dan/tuyet-doi/toi/bmad-agent-pm \
--key agent --key agent
# Resolve một trường cụ thể # Resolve một trường cụ thể
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /duong-dan/tuyet-doi/toi/bmad-agent-pm \ --skill /duong-dan/tuyet-doi/toi/bmad-agent-pm \
--key agent.icon --key agent.icon
# Dump toàn bộ # Dump toàn bộ
python3 {project-root}/_bmad/scripts/resolve_customization.py \ uv run {project-root}/_bmad/scripts/resolve_customization.py \
--skill /duong-dan/tuyet-doi/toi/bmad-agent-pm --skill /duong-dan/tuyet-doi/toi/bmad-agent-pm
``` ```

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "bmad-method", "name": "bmad-method",
"version": "6.8.0", "version": "6.9.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "bmad-method", "name": "bmad-method",
"version": "6.8.0", "version": "6.9.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@clack/core": "^1.3.1", "@clack/core": "^1.3.1",

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "bmad-method", "name": "bmad-method",
"version": "6.8.0", "version": "6.9.0",
"description": "Breakthrough Method of Agile AI-driven Development", "description": "Breakthrough Method of Agile AI-driven Development",
"keywords": [ "keywords": [
"agile", "agile",
@ -42,10 +42,11 @@
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0", "prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar", "quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar",
"rebundle": "node tools/installer/bundlers/bundle-web.js rebundle", "rebundle": "node tools/installer/bundlers/bundle-web.js rebundle",
"test": "npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run lint && npm run lint:md && npm run format:check", "test": "npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run test:skills && npm run lint && npm run lint:md && npm run format:check",
"test:channels": "node test/test-installer-channels.js", "test:channels": "node test/test-installer-channels.js",
"test:install": "node test/test-installation-components.js", "test:install": "node test/test-installation-components.js",
"test:refs": "node test/test-file-refs-csv.js", "test:refs": "node test/test-file-refs-csv.js",
"test:skills": "node test/test-validate-skills.js",
"test:urls": "node test/test-parse-source-urls.js", "test:urls": "node test/test-parse-source-urls.js",
"validate:refs": "node tools/validate-file-refs.js --strict", "validate:refs": "node tools/validate-file-refs.js --strict",
"validate:skills": "node tools/validate-skills.js --strict" "validate:skills": "node tools/validate-skills.js --strict"

View File

@ -0,0 +1,101 @@
---
name: bmad-dev-auto
description: 'One iteration of an unattended development loop. Use when invoked by name.'
---
# Dev Auto Workflow
**Goal:** Turn intent into a hardened, reviewable artifact, without human interaction.
**CRITICAL:** If a step says "read fully and follow step-XX", you read and follow step-XX. No exceptions.
## HALT
To HALT with a final status and optional blocking condition:
1. If `{spec_file}` is known and exists, update `status` in frontmatter and append missing result details under `## Auto Run Result`.
2. If `{spec_file}` is unknown or missing, create `{implementation_artifacts}/bmad-dev-auto-result-<slug-or-timestamp>.md` with:
```markdown
---
status: <final status>
---
# BMad Dev Auto Result
Status: <final status>
Blocking condition: <blocking condition, if any>
```
3. Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
4. If the resolved `workflow.on_complete` is non-empty, follow it as the final instruction before exiting.
5. Stop the workflow.
## Subagents
Using subagents when instructed is mandatory. If you cannot, HALT with status `blocked` and blocking condition `no subagents`.
## READY FOR DEVELOPMENT STANDARD
A specification is "Ready for Development" when:
- **Actionable**: Every task has a file path and specific action.
- **Logical**: Tasks ordered by dependency.
- **Testable**: All ACs use Given/When/Then.
- **Complete**: No placeholders or TBDs.
- **Sufficient**: No known requirement, acceptance, dependency, or implementation gaps remain unresolved.
- **Coherent**: No unresolved ambiguities or internal contradictions.
## Conventions
- Bare paths (e.g. `step-01-clarify-and-route.md`) resolve from the skill root.
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
- `{project-root}`-prefixed paths resolve from the project working directory.
- `{skill-name}` resolves to the skill directory's basename.
## On Activation
### Step 1: Resolve the Workflow Block
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`
**If the script fails**, resolve the `workflow` block yourself by reading these three files in base → team → user order and applying the same structural merge rules as the resolver:
1. `{skill-root}/customize.toml` — defaults
2. `{project-root}/_bmad/custom/{skill-name}.toml` — team overrides
3. `{project-root}/_bmad/custom/{skill-name}.user.toml` — personal overrides
Any missing file is skipped. Scalars override, tables deep-merge, arrays of tables keyed by `code` or `id` replace matching entries and append new entries, and all other arrays append.
### Step 2: Execute Prepend Steps
Execute each entry in `{workflow.activation_steps_prepend}` in order before proceeding.
### Step 3: Load Persistent Facts
Treat every entry in `{workflow.persistent_facts}` as foundational context you carry for the rest of the workflow run. Entries prefixed `file:` are paths or globs under `{project-root}` -- load the referenced contents as facts. All other entries are facts verbatim.
### Step 4: Load Config
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
- `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`
- `date` as system-generated current datetime
- `project_context` = `**/project-context.md` (load if exists)
- CLAUDE.md / memory files (load if exist)
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
- Language MUST be tailored to `{user_skill_level}`
- Generate all documents in `{document_output_language}`
### Step 5: Execute Append Steps
Execute each entry in `{workflow.activation_steps_append}` in order.
Activation is complete after all activation steps have run.
## Workflow Execution
Follow the step files in order. Read one step fully, execute it, then load the next step only when directed. Do not skip, reorder, or pre-load steps.
## First workflow step
Read fully and follow: `./step-01-clarify-and-route.md` to begin the workflow.

View File

@ -0,0 +1,62 @@
# Compile Epic Context
**Task**
Given an epic number, the epics file, the planning artifacts directory, and a desired output path, compile a clean, focused, developer-ready context file (`epic-<N>-context.md`).
**Steps**
1. Read the epics file and extract the target epic's title, goal, and list of stories.
2. Scan the planning artifacts directory for the standard files (PRD, architecture, UX/design, product brief).
3. Pull only the information relevant to this epic.
4. Write the compiled context to the exact output path using the format below.
## Exact Output Format
Use these headings:
```markdown
# Epic {N} Context: {Epic Title}
<!-- Generated from planning artifacts. Regenerate with compile-epic-context if planning docs change. -->
## Goal
{One clear paragraph: what this epic achieves and why it matters.}
## Stories
- Story X.Y: Brief title only
- ...
## Requirements & Constraints
{Relevant functional/non-functional requirements and success criteria for this epic (describe by purpose, not source).}
## Technical Decisions
{Key architecture decisions, constraints, patterns, data models, and conventions relevant to this epic.}
## UX & Interaction Patterns
{Relevant UX flows, interaction patterns, and design constraints (omit section entirely if nothing relevant).}
## Cross-Story Dependencies
{Dependencies between stories in this epic or with other epics/systems (omit if none).}
```
## Rules
- **Scope aggressively.** Include only what a developer working on any story in this epic actually needs. When in doubt, leave it out — the developer can always read the full planning doc.
- **Describe by purpose, not by source.** Write "API responses must include pagination metadata" not "Per PRD section 3.2.1, pagination is required." Planning doc internals will change; the constraint won't.
- **No full copies.** Never quote source documents, section numbers, or paste large blocks verbatim. Always distill.
- **No story-level details.** The story list is for orientation only. Individual story specs handle the details.
- **Nothing derivable from the codebase.** Don't document what a developer can learn by reading the code.
- **Be concise and actionable.** Target 800–1500 tokens total. This file loads into bmad-dev-auto's context alongside other material.
- **Never hallucinate content.** If source material doesn't say something, don't invent it.
- **Omit empty sections entirely**, except Goal and Stories, which are always required.
## Error handling
- **If the epics file is missing or the target epic is not found:** write nothing and report the problem to the calling agent. Goal and Stories cannot be populated without a usable epics file.
- **If planning artifacts are missing or empty:** still produce the file with Goal and Stories populated from the epics file. Under Requirements & Constraints, write: "Planning artifacts were unavailable; only epics-file context was used." Never hallucinate content to fill missing sections.

View File

@ -0,0 +1,33 @@
# DO NOT EDIT -- overwritten on every update.
#
# Default customization values for bmad-dev-auto.
# Override in _bmad/custom/bmad-dev-auto.toml or
# _bmad/custom/bmad-dev-auto.user.toml.
#
# Merge rules:
# - Strings replace the default.
# - Lists append to the default list.
# - Tables merge key by key.
[workflow]
# Extra instructions to run before config is loaded.
activation_steps_prepend = []
# Extra instructions to run after config is loaded and before step 01.
activation_steps_append = []
# Facts kept in context for the whole run.
# Entries are literal text or file references prefixed with "file:".
# File entries may use globs and are loaded during activation.
persistent_facts = [
"file:{project-root}/**/project-context.md",
]
# Instruction run by HALT after writing the terminal result.
# Empty means no extra terminal behavior.
on_complete = ""

View File

@ -0,0 +1,90 @@
---
title: '{title}'
type: 'feature' # feature | bugfix | refactor | chore
created: '{date}'
status: 'draft' # draft | ready-for-dev | in-progress | in-review | done | blocked
review_loop_iteration: 0 # incremented by step-04 before each review loopback
context: [] # optional: `{project-root}/`-prefixed paths to project-wide standards/docs the implementation agent should load. Keep short — only what isn't already distilled into the spec body.
warnings: [] # optional: machine-readable warnings for orchestration, e.g. oversized, multiple-goals
---
<!-- Aim for 900–1600 tokens. If larger, add `oversized` to frontmatter `warnings` and continue.
Never over-specify "how" — use boundaries + examples instead.
Cohesive cross-layer stories (DB+BE+UI) stay in ONE file.
IMPORTANT: Remove all HTML comments when filling this template. -->
<intent-contract>
## Intent
<!-- What is broken or missing, and why it matters. Then the high-level approach — the "what", not the "how". -->
**Problem:** ONE_TO_TWO_SENTENCES
**Approach:** ONE_TO_TWO_SENTENCES
## Boundaries & Constraints
<!-- Three tiers: Always = invariant rules. Block If = decisions that cannot be made unattended. Never = out of scope + forbidden approaches. -->
**Always:** INVARIANT_RULES
**Block If:** DECISIONS_REQUIRING_HUMAN_INPUT
<!-- Agent: if any of these trigger during execution, HALT with status blocked and the blocking condition. -->
**Never:** NON_GOALS_AND_FORBIDDEN_APPROACHES
## I/O & Edge-Case Matrix
<!-- If no meaningful I/O scenarios exist, DELETE THIS ENTIRE SECTION. Do not write "N/A" or "None". -->
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| HAPPY_PATH | INPUT | OUTCOME | No error expected |
| ERROR_CASE | INPUT | OUTCOME | ERROR_HANDLING |
</intent-contract>
## Code Map
<!-- Agent-populated during planning. Annotated paths prevent blind codebase searching. -->
- `FILE` -- ROLE_OR_RELEVANCE
- `FILE` -- ROLE_OR_RELEVANCE
## Tasks & Acceptance
<!-- Tasks: backtick-quoted file path -- action -- rationale. Prefer one task per file; group tightly-coupled changes when splitting would be artificial. -->
<!-- If an I/O Matrix is present, include a task to unit-test its edge cases. -->
<!-- AC covers system-level behaviors not captured by the I/O Matrix. Do not duplicate I/O scenarios here. -->
**Execution:**
- [ ] `FILE` -- ACTION -- RATIONALE
**Acceptance Criteria:**
- Given PRECONDITION, when ACTION, then EXPECTED_RESULT
## Spec Change Log
<!-- Append-only. Populated by step-04 during review loops. Do not modify or delete existing entries.
Each entry records: what finding triggered the change, what was amended, what known-bad state
the amendment avoids, and any KEEP instructions (what worked well and must survive re-derivation).
Empty until the first bad_spec loopback. -->
## Design Notes
<!-- If the approach is straightforward, DELETE THIS ENTIRE SECTION. Do not write "N/A" or "None". -->
<!-- Design rationale and golden examples only when non-obvious. Keep examples to 5–10 lines. -->
DESIGN_RATIONALE_AND_EXAMPLES
## Verification
<!-- If no build, test, or lint commands apply, DELETE THIS ENTIRE SECTION. Do not write "N/A" or "None". -->
<!-- How the agent confirms its own work. Prefer CLI commands. When no CLI check applies, state what to inspect manually. -->
**Commands:**
- `COMMAND` -- expected: SUCCESS_CRITERIA
**Manual checks (if no CLI):**
- WHAT_TO_INSPECT_AND_EXPECTED_STATE

View File

@ -0,0 +1,66 @@
---
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
spec_file: '' # set at runtime for both routes before leaving this step
---
# Step 1: Clarify and Route
## RULES
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
- Treat the invocation intent as workflow input, not as a substitute for step-02 investigation and spec generation.
- **EARLY EXIT** means: stop this step immediately, then read and follow the target file. Return here only if a later step explicitly says to loop back.
## Intent check (do this first)
Use the invocation prompt as the intent.
If the invocation prompt explicitly points to an existing spec file with recognized `status` frontmatter, set `spec_file`, then **EARLY EXIT** to the appropriate step:
- `draft` → `./step-02-plan.md`
- `ready-for-dev` or `in-progress` → `./step-03-implement.md`
- `in-review` → `./step-04-review.md`
- `blocked` → HALT with status `blocked` and blocking condition `blocked spec supplied`.
- `done` → ingest as context and proceed to INSTRUCTIONS — do not resume.
Otherwise, treat the invocation prompt as starting intent. This may be a story ID, ticket ID, file path, short description, or longer free-form intent. Do not infer workflow state from non-spec files.
If the invocation prompt does not contain enough intent to identify what to implement, HALT with status `blocked` and blocking condition `unclear intent`.
## INSTRUCTIONS
1. Load context.
- List files in `{planning_artifacts}` and `{implementation_artifacts}`.
- If the invocation prompt points to an unformatted spec or intent file, ingest that file. Do not scan for unrelated intent files.
- **Determine context strategy.** Using the intent and the artifact listing, infer whether the current work is a story from an epic. Do not rely on filename patterns or regex — reason about the intent, the listing, and any epics file content together.
**A) Epic story path** — if the intent is clearly an epic story:
1. Identify the epic number `{epic_num}` and (if present) the story number `{story_num}`. If you can't identify an epic number, use path B.
2. **Check for a valid cached epic context.** Look for `{implementation_artifacts}/epic-<N>-context.md` (where `<N>` is the epic number). A file is **valid** when it exists, is non-empty, starts with `# Epic <N> Context:` (with the correct epic number), and no file in `{planning_artifacts}` is newer.
- **If valid:** load it as the primary planning context. Do not load raw planning docs (PRD, architecture, UX, etc.).
- **If missing, empty, or invalid:** compile it in the next bullet.
3. **Compile epic context if needed.** If no valid cached epic context was loaded, produce `{implementation_artifacts}/epic-<N>-context.md` by spawning a subagent with `./compile-epic-context.md` as its prompt. Pass it the epic number, the epics file path, the `{planning_artifacts}` directory, and the output path `{implementation_artifacts}/epic-<N>-context.md`.
4. **Verify if compiled.** If epic context was compiled, verify the output file exists, is non-empty, and starts with `# Epic <N> Context:`. If valid, load it. If verification fails, HALT with status `blocked` and blocking condition `context compilation verification failed`.
5. **Previous story continuity.** Regardless of which context source succeeded above, scan `{implementation_artifacts}` for specs from the same epic with `status: done` and a lower story number. Load the most recent one (highest story number below current). Extract its **Code Map**, **Design Notes**, **Spec Change Log**, and **task list** as continuity context for step-02 planning. If no `done` spec is found but an `in-review` spec exists for the same epic with a lower story number, HALT with status `blocked` and blocking condition `missing previous-story continuity decision`.
**B) Freeform path** — if the intent is not an epic story:
- Planning artifacts are the output of BMAD phases 1-3. Typical files include:
- **PRD** (`*prd*`) — product requirements and success criteria
- **Architecture** (`*architecture*`) — technical design decisions and constraints
- **UX/Design** (`*ux*`) — user experience and interaction design
- **Epics** (`*epic*`) — feature breakdown into implementable stories
- **Product Brief** (`*brief*`) — project vision and scope
- Scan the listing for files matching these patterns. If any look relevant to the current intent, load them selectively — you don't need all of them, but you need the right constraints and requirements rather than guessing from code alone.
2. Resolve intent from the invocation prompt and loaded artifacts. Do not fantasize or leave open questions. If the intent cannot be resolved, HALT with status `blocked` and the unresolved questions as blocking condition.
3. Version control sanity check. Is the working tree clean? Does the current branch make sense for this intent — considering its name and recent history? If the tree is dirty or the branch is an obvious mismatch, HALT with status `blocked` and that condition as blocking condition. If version control is unavailable, skip this check.
4. Multi-goal warning. If the intent appears to contain multiple independently shippable goals, carry `multiple-goals` forward so step-02 can add it to `{spec_file}` frontmatter `warnings`. Do not split or block.
5. Route:
Derive a valid kebab-case slug from the clarified intent. If the intent references a tracking identifier (story number, issue number, ticket ID), lead the slug with it (e.g. `3-2-digest-delivery`, `gh-47-fix-auth`). If `{implementation_artifacts}/spec-{slug}.md` already exists: if its status is `draft`, treat it as the same work and resume it (set `spec_file` to that path, **EARLY EXIT** → `./step-02-plan.md`); otherwise append `-2`, `-3`, etc. Set `spec_file` = `{implementation_artifacts}/spec-{slug}.md`.
## NEXT
Read fully and follow `./step-02-plan.md`

View File

@ -0,0 +1,32 @@
---
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
---
# Step 2: Plan
## RULES
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
- No human interaction: do not ask questions or wait for approval in this step.
## INSTRUCTIONS
1. Draft resume check. If `{spec_file}` exists with `status: draft`, read it and capture the verbatim `<intent-contract>...</intent-contract>` block as `preserved_intent_contract`. Otherwise `preserved_intent_contract` is empty.
2. Investigate codebase. _Use subagents for deep exploration. To prevent context snowballing, instruct subagents to give you distilled summaries only._
3. Read `./spec-template.md` fully. Fill it out based on the intent and investigation. If `{preserved_intent_contract}` is non-empty, substitute it for the `<intent-contract>` block in your filled spec before writing. Write the result to `{spec_file}`.
4. Self-review against READY FOR DEVELOPMENT standard.
5. If intent gaps exist, do not fantasize and do not leave open questions. HALT with status `blocked`, blocking condition `intent gaps`, and include the unanswered questions and evidence gathered.
6. Warning check. If step-01 carried `multiple-goals`, add it to `{spec_file}` frontmatter `warnings`. If `{spec_file}` exceeds 1600 tokens, add `oversized` to frontmatter `warnings`. Continue either way.
### READY-FOR-DEVELOPMENT GATE
Re-read `./SKILL.md`, then re-read `{spec_file}` from disk and verify the spec meets the READY FOR DEVELOPMENT standard.
- **If the file is missing:** HALT with status `blocked` and blocking condition `planned spec file disappeared before implementation`.
- **If the spec meets the standard:** set `{spec_file}` frontmatter status to `ready-for-dev`, then continue to step 3.
- **If the spec does not meet the standard:** repair it once, then re-read it from disk and verify again. If it still does not meet the standard, HALT with status `blocked`, blocking condition `spec failed ready-for-development standard`, and include the failing criteria and evidence gathered.
## NEXT
Read fully and follow `./step-03-implement.md`

View File

@ -0,0 +1,38 @@
---
---
# Step 3: Implement
## RULES
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
- No human interaction: do not ask questions or wait for approval in this step.
- Content inside `<intent-contract>` in `{spec_file}` is read-only. Do not modify.
## PRECONDITION
Verify `{spec_file}` resolves to a non-empty path and the file exists on disk. If empty or missing, HALT with status `blocked` and blocking condition `missing spec_file before implementation`.
## INSTRUCTIONS
### Baseline
Capture `baseline_revision` (current HEAD, or `NO_VCS` if version control is unavailable) into `{spec_file}` frontmatter before making any changes.
### Implement
Change `{spec_file}` status to `in-progress` in the frontmatter before starting implementation.
If `{spec_file}` has a non-empty `context:` list in its frontmatter, load those files before implementation begins. When handing to a subagent, include them in the subagent prompt so it has access to the referenced context.
Hand `{spec_file}` to an implementation subagent.
**Path formatting rule:** Any markdown links written into `{spec_file}` must use paths relative to `{spec_file}`'s directory so they are clickable in VS Code. Any file paths displayed in terminal/conversation output must use CWD-relative format with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/` in either case.
### Tasks & Acceptance Verification
After the implementation subagent returns, verify every task in the `## Tasks & Acceptance` section of `{spec_file}` is complete and every acceptance criterion is satisfied. Mark each finished task `[x]`. If any task is not done or any acceptance criterion is not satisfied, finish the missing work before proceeding. If the missing work cannot be completed, HALT with status `blocked`, blocking condition `implementation verification failed`, and include the unfinished task or failing acceptance criterion and reason.
## NEXT
Read fully and follow `./step-04-review.md`

View File

@ -0,0 +1,61 @@
---
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
---
# Step 4: Review
## RULES
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
- No human interaction: do not ask questions or wait for approval in this step.
- Review subagents get no prior session context.
- All review subagents must run at the same model capability as the current session.
## INSTRUCTIONS
Change `{spec_file}` status to `in-review` in the frontmatter before continuing.
### Construct Diff
Read `{baseline_revision}` from `{spec_file}` frontmatter. If `{baseline_revision}` is missing or `NO_VCS`, use best effort to determine what changed. Otherwise, construct `{diff_output}` covering all changes — tracked and untracked — since `{baseline_revision}`.
Do NOT `git add` anything — this is read-only inspection.
### Review
Launch two subagents without prior session context.
- **Blind hunter** — receives inline `{diff_output}` only. No spec, no context docs, no project access. Invoke via the `bmad-review-adversarial-general` skill.
- **Edge case hunter** — receives `{diff_output}` and read access to the project. Invoke via the `bmad-review-edge-case-hunter` skill.
### Classify
1. Deduplicate all review findings.
2. Classify each finding. The first three categories are **this story's problem** — caused or exposed by the current change. The last two are **not this story's problem**.
- **intent_gap** — caused by the change; cannot be resolved from the spec because the captured intent is incomplete. Do not infer intent unless there is exactly one possible reading.
- **bad_spec** — caused by the change, including direct deviations from spec. The spec should have been clear enough to prevent it. When in doubt between bad_spec and patch, prefer bad_spec — a spec-level fix is more likely to produce coherent code.
- **patch** — caused by the change; trivially fixable without human input. Just part of the diff.
- **defer** — pre-existing issue not caused by this story, surfaced incidentally by the review. Collect for later focused attention.
- **reject** — noise. Drop silently. When unsure between defer and reject, prefer reject — only defer findings you are confident are real.
3. Process findings in cascading order. If intent_gap exists, lower findings are moot; follow the intent_gap branch below. If bad_spec exists, lower findings are moot since code will be re-derived. If neither exists, process patch and defer normally. Before each bad_spec loopback, read `{spec_file}` frontmatter `review_loop_iteration` (missing means `0`), increment it by 1, and write it back. If it exceeds 5, HALT with status `blocked` and blocking condition `review repair loop exceeded 5 iterations`.
- **intent_gap** — Root cause is inside `<intent-contract>`. Revert code changes. HALT with status `blocked`, blocking condition `intent gap in intent contract`, and include the intent-gap findings.
- **bad_spec** — Root cause is outside `<intent-contract>`. Do not modify content inside `<intent-contract>`. Before reverting code: extract KEEP instructions for positive preservation (what worked well and must survive re-derivation). Revert code changes. Read the `## Spec Change Log` in `{spec_file}` and strictly respect all logged constraints when amending the sections outside `<intent-contract>` that contain the root cause. Append a new change-log entry recording: the triggering finding, what was amended, the known-bad state avoided, and the KEEP instructions. Read fully and follow `./step-03-implement.md` to re-derive the code, then this step will run again.
- **patch** — Auto-fix. These are the only findings that survive loopbacks.
- **defer** — Append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates.
```markdown
- source_spec: `{spec_file}`
summary: <one sentence>
evidence: <why this is real>
```
- **reject** — Drop silently.
## Finalize
Prepare `Auto Run Result` details:
- Summary of implemented change
- Files changed with one-line descriptions
- Review findings breakdown: patches applied, items deferred, items rejected
- Verification performed, including command outcomes or manual inspection notes
- Any residual risks
HALT with status `done`.

View File

@ -20,6 +20,8 @@ A specification is "Ready for Development" when:
- **Logical**: Tasks ordered by dependency. - **Logical**: Tasks ordered by dependency.
- **Testable**: All ACs use Given/When/Then. - **Testable**: All ACs use Given/When/Then.
- **Complete**: No placeholders or TBDs. - **Complete**: No placeholders or TBDs.
- **Sufficient**: No known requirement, acceptance, dependency, or implementation gaps remain unresolved.
- **Coherent**: No unresolved ambiguities or internal contradictions.
## SCOPE STANDARD ## SCOPE STANDARD

View File

@ -1,41 +1,33 @@
# DO NOT EDIT -- overwritten on every update. # DO NOT EDIT -- overwritten on every update.
# #
# Workflow customization surface for bmad-quick-dev. Mirrors the # Default customization values for bmad-quick-dev.
# agent customization shape under the [workflow] namespace. # Override in _bmad/custom/bmad-quick-dev.toml or
# _bmad/custom/bmad-quick-dev.user.toml.
#
# Merge rules:
# - Strings replace the default.
# - Lists append to the default list.
# - Tables merge key by key.
[workflow] [workflow]
# --- Configurable below. Overrides merge per BMad structural rules: --- # Extra instructions to run before config is loaded and before the user is greeted.
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
# Steps to run before the standard activation (config load, greet).
# Overrides append. Use for pre-flight loads, compliance checks, etc.
activation_steps_prepend = [] activation_steps_prepend = []
# Steps to run after greet but before the workflow begins. # Extra instructions to run after the greeting and before step 01.
# Overrides append. Use for context-heavy setup that should happen
# once the user has been acknowledged.
activation_steps_append = [] activation_steps_append = []
# Persistent facts the workflow keeps in mind for the whole run # Facts kept in context for the whole run.
# (standards, compliance constraints, stylistic guardrails). # Entries are literal text or file references prefixed with "file:".
# Distinct from the runtime memory sidecar — these are static context # File entries may use globs and are loaded during activation.
# loaded on activation. Overrides append.
#
# Each entry is either:
# - a literal sentence, e.g. "All stories must include testable acceptance criteria."
# - a file reference prefixed with `file:`, e.g. "file:{project-root}/docs/standards.md"
# (glob patterns are supported; the file's contents are loaded and treated as facts).
persistent_facts = [ persistent_facts = [
"file:{project-root}/**/project-context.md", "file:{project-root}/**/project-context.md",
] ]
# Scalar: executed when the workflow reaches its final step, # Instruction run after Quick Dev completes.
# after implementation is complete and explanations are provided. Override wins. # Empty means no extra completion behavior.
# Leave empty for no custom post-completion behavior.
on_complete = "" on_complete = ""

View File

@ -3,6 +3,7 @@ title: '{title}'
type: 'feature' # feature | bugfix | refactor | chore type: 'feature' # feature | bugfix | refactor | chore
created: '{date}' created: '{date}'
status: 'draft' # draft | ready-for-dev | in-progress | in-review | done status: 'draft' # draft | ready-for-dev | in-progress | in-review | done
review_loop_iteration: 0 # incremented by step-04 before each review loopback
context: [] # optional: `{project-root}/`-prefixed paths to project-wide standards/docs the implementation agent should load. Keep short — only what isn't already distilled into the spec body. context: [] # optional: `{project-root}/`-prefixed paths to project-wide standards/docs the implementation agent should load. Keep short — only what isn't already distilled into the spec body.
--- ---

View File

@ -59,8 +59,8 @@ If the spec is an epic story and `{sprint_status}` exists: find the `development
- **If missing, empty, or invalid:** continue to step 3. - **If missing, empty, or invalid:** continue to step 3.
3. **Compile epic context.** Produce `{implementation_artifacts}/epic-<N>-context.md` by following `./compile-epic-context.md`, in order of preference: 3. **Compile epic context.** Produce `{implementation_artifacts}/epic-<N>-context.md` by following `./compile-epic-context.md`, in order of preference:
- **Preferred — sub-agent:** spawn a sub-agent with `./compile-epic-context.md` as its prompt. Pass it the epic number, the epics file path, the `{planning_artifacts}` directory, and the output path `{implementation_artifacts}/epic-<N>-context.md`. - **Preferred — subagent:** spawn a subagent with `./compile-epic-context.md` as its prompt. Pass it the epic number, the epics file path, the `{planning_artifacts}` directory, and the output path `{implementation_artifacts}/epic-<N>-context.md`.
- **Fallback — inline** (for runtimes without sub-agent support, e.g. Copilot, Codex, local Ollama, older Claude): if your runtime cannot spawn sub-agents, or the spawn fails/times out, read `./compile-epic-context.md` yourself and follow its instructions to produce the same output file. - **Fallback — inline** (for runtimes without subagent support, e.g. Copilot, Codex, local Ollama, older Claude): if your runtime cannot spawn subagents, or the spawn fails/times out, read `./compile-epic-context.md` yourself and follow its instructions to produce the same output file.
4. **Verify.** After compilation, verify the output file exists, is non-empty, and starts with `# Epic <N> Context:`. If valid, load it. If verification fails, HALT and report the failure. 4. **Verify.** After compilation, verify the output file exists, is non-empty, and starts with `# Epic <N> Context:`. If valid, load it. If verification fails, HALT and report the failure.
@ -82,7 +82,12 @@ If the spec is an epic story and `{sprint_status}` exists: find the `development
- Present detected distinct goals as a bullet list. - Present detected distinct goals as a bullet list.
- Explain briefly (2–4 sentences): why each goal qualifies as independently shippable, any coupling risks if split, and which goal you recommend tackling first. - Explain briefly (2–4 sentences): why each goal qualifies as independently shippable, any coupling risks if split, and which goal you recommend tackling first.
- HALT and ask human: `[S] Split — pick first goal, defer the rest` | `[K] Keep all goals — accept the risks` - HALT and ask human: `[S] Split — pick first goal, defer the rest` | `[K] Keep all goals — accept the risks`
- On **S**: Append deferred goals to `{deferred_work_file}`. Narrow scope to the first-mentioned goal. Continue routing. - On **S**: For each deferred goal, append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates. Narrow scope to the first-mentioned goal. Continue routing.
```markdown
- source_spec: none
summary: <one sentence naming the deferred goal>
evidence: <why this was split from the current intent>
```
- On **K**: Proceed as-is. - On **K**: Proceed as-is.
5. Route — choose exactly one: 5. Route — choose exactly one:

View File

@ -12,14 +12,19 @@ deferred_work_file: '{implementation_artifacts}/deferred-work.md'
## INSTRUCTIONS ## INSTRUCTIONS
1. Draft resume check. If `{spec_file}` exists with `status: draft`, read it and capture the verbatim `<frozen-after-approval>...</frozen-after-approval>` block as `preserved_intent`. Otherwise `preserved_intent` is empty. 1. Draft resume check. If `{spec_file}` exists with `status: draft`, read it and capture the verbatim `<frozen-after-approval>...</frozen-after-approval>` block as `preserved_intent`. Otherwise `preserved_intent` is empty.
2. Investigate codebase. _Isolate deep exploration in sub-agents/tasks where available. To prevent context snowballing, instruct subagents to give you distilled summaries only._ 2. Investigate codebase. _Isolate deep exploration in subagents/tasks where available. To prevent context snowballing, instruct subagents to give you distilled summaries only._
3. Read `./spec-template.md` fully. Fill it out based on the intent and investigation. If `{preserved_intent}` is non-empty, substitute it for the `<frozen-after-approval>` block in your filled spec before writing. Write the result to `{spec_file}`. 3. Read `./spec-template.md` fully. Fill it out based on the intent and investigation. If `{preserved_intent}` is non-empty, substitute it for the `<frozen-after-approval>` block in your filled spec before writing. Write the result to `{spec_file}`.
4. Self-review against READY FOR DEVELOPMENT standard. 4. Self-review against READY FOR DEVELOPMENT standard.
5. If intent gaps exist, do not fantasize, do not leave open questions, HALT and ask the human. 5. If intent gaps exist, do not fantasize, do not leave open questions, HALT and ask the human.
6. Token count check (see SCOPE STANDARD). If spec exceeds 1600 tokens: 6. Token count check (see SCOPE STANDARD). If spec exceeds 1600 tokens:
- Show user the token count. - Show user the token count.
- HALT and ask human: `[S] Split — carve off secondary goals` | `[K] Keep full spec — accept the risks` - HALT and ask human: `[S] Split — carve off secondary goals` | `[K] Keep full spec — accept the risks`
- On **S**: Propose the split — name each secondary goal. Append deferred goals to `{deferred_work_file}`. Rewrite the current spec to cover only the main goal — do not surgically carve sections out; regenerate the spec for the narrowed scope. Continue to checkpoint. - On **S**: Propose the split — name each secondary goal. For each deferred goal, append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates. Rewrite the current spec to cover only the main goal — do not surgically carve sections out; regenerate the spec for the narrowed scope. Continue to checkpoint.
```markdown
- source_spec: `{spec_file}`
summary: <one sentence naming the deferred goal>
evidence: <why this was split from the current spec>
```
- On **K**: Continue to checkpoint with full spec. - On **K**: Continue to checkpoint with full spec.
### CHECKPOINT 1 ### CHECKPOINT 1

View File

@ -26,15 +26,15 @@ Change `{spec_file}` status to `in-progress` in the frontmatter before starting
Follow `./sync-sprint-status.md` with `{target_status}` = `in-progress`. Follow `./sync-sprint-status.md` with `{target_status}` = `in-progress`.
If `{spec_file}` has a non-empty `context:` list in its frontmatter, load those files before implementation begins. When handing to a sub-agent, include them in the sub-agent prompt so it has access to the referenced context. If `{spec_file}` has a non-empty `context:` list in its frontmatter, load those files before implementation begins. When handing to a subagent, include them in the subagent prompt so it has access to the referenced context.
Hand `{spec_file}` to a sub-agent/task and let it implement. If no sub-agents are available, implement directly. Hand `{spec_file}` to a subagent/task and let it implement. If no subagents are available, implement directly.
**Path formatting rule:** Any markdown links written into `{spec_file}` must use paths relative to `{spec_file}`'s directory so they are clickable in VS Code. Any file paths displayed in terminal/conversation output must use CWD-relative format with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/` in either case. **Path formatting rule:** Any markdown links written into `{spec_file}` must use paths relative to `{spec_file}`'s directory so they are clickable in VS Code. Any file paths displayed in terminal/conversation output must use CWD-relative format with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/` in either case.
### Self-Check ### Tasks & Acceptance Verification
Before leaving this step, verify every task in the `## Tasks & Acceptance` section of `{spec_file}` is complete. Mark each finished task `[x]`. If any task is not done, finish it before proceeding. Before leaving this step, verify every task in the `## Tasks & Acceptance` section of `{spec_file}` is complete and every acceptance criterion is satisfied. Mark each finished task `[x]`. If any task is not done or any acceptance criterion is not satisfied, finish the missing work before proceeding.
## NEXT ## NEXT

View File

@ -1,6 +1,5 @@
--- ---
deferred_work_file: '{implementation_artifacts}/deferred-work.md' deferred_work_file: '{implementation_artifacts}/deferred-work.md'
specLoopIteration: 1
--- ---
# Step 4: Review # Step 4: Review
@ -23,11 +22,10 @@ Do NOT `git add` anything — this is read-only inspection.
### Review ### Review
Launch three subagents without conversation context. If no sub-agents are available, generate three review prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the human to run each in a separate session (ideally a different LLM) and paste back the findings. Launch two subagents without conversation context. If no subagents are available, generate two review prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the human to run each in a separate session (ideally a different LLM) and paste back the findings.
- **Blind hunter** — receives inline `{diff_output}` only. No spec, no context docs, no project access. Invoke via the `bmad-review-adversarial-general` skill. - **Blind hunter** — receives inline `{diff_output}` only. No spec, no context docs, no project access. Invoke via the `bmad-review-adversarial-general` skill.
- **Edge case hunter** — receives `{diff_output}` and read access to the project. Invoke via the `bmad-review-edge-case-hunter` skill. - **Edge case hunter** — receives `{diff_output}` and read access to the project. Invoke via the `bmad-review-edge-case-hunter` skill.
- **Acceptance auditor** — receives `{diff_output}`, `{spec_file}`, and read access to the project. Must also read the docs listed in `{spec_file}` frontmatter `context`. Checks for violations of acceptance criteria, rules, and principles from the spec and context docs.
### Classify ### Classify
@ -38,11 +36,16 @@ Launch three subagents without conversation context. If no sub-agents are availa
- **patch** — caused by the change; trivially fixable without human input. Just part of the diff. - **patch** — caused by the change; trivially fixable without human input. Just part of the diff.
- **defer** — pre-existing issue not caused by this story, surfaced incidentally by the review. Collect for later focused attention. - **defer** — pre-existing issue not caused by this story, surfaced incidentally by the review. Collect for later focused attention.
- **reject** — noise. Drop silently. When unsure between defer and reject, prefer reject — only defer findings you are confident are real. - **reject** — noise. Drop silently. When unsure between defer and reject, prefer reject — only defer findings you are confident are real.
3. Process findings in cascading order. If intent_gap or bad_spec findings exist, they trigger a loopback — lower findings are moot since code will be re-derived. If neither exists, process patch and defer normally. Increment `{specLoopIteration}` on each loopback. If it exceeds 5, HALT and escalate to the human. 3. Process findings in cascading order. If intent_gap or bad_spec findings exist, they trigger a loopback — lower findings are moot since code will be re-derived. If neither exists, process patch and defer normally. Before each loopback, read `{spec_file}` frontmatter `review_loop_iteration` (missing means `0`), increment it by 1, and write it back. If it exceeds 5, HALT and escalate to the human.
- **intent_gap** — Root cause is inside `<frozen-after-approval>`. Revert code changes. Loop back to the human to resolve. Once resolved, read fully and follow `./step-02-plan.md` to re-run steps 2–4. - **intent_gap** — Root cause is inside `<frozen-after-approval>`. Revert code changes. Loop back to the human to resolve. Once resolved, read fully and follow `./step-02-plan.md` to re-run steps 2–4.
- **bad_spec** — Root cause is outside `<frozen-after-approval>`. Before reverting code: extract KEEP instructions for positive preservation (what worked well and must survive re-derivation). Revert code changes. Read the `## Spec Change Log` in `{spec_file}` and strictly respect all logged constraints when amending the non-frozen sections that contain the root cause. Append a new change-log entry recording: the triggering finding, what was amended, the known-bad state avoided, and the KEEP instructions. Read fully and follow `./step-03-implement.md` to re-derive the code, then this step will run again. - **bad_spec** — Root cause is outside `<frozen-after-approval>`. Before reverting code: extract KEEP instructions for positive preservation (what worked well and must survive re-derivation). Revert code changes. Read the `## Spec Change Log` in `{spec_file}` and strictly respect all logged constraints when amending the non-frozen sections that contain the root cause. Append a new change-log entry recording: the triggering finding, what was amended, the known-bad state avoided, and the KEEP instructions. Read fully and follow `./step-03-implement.md` to re-derive the code, then this step will run again.
- **patch** — Auto-fix. These are the only findings that survive loopbacks. - **patch** — Auto-fix. These are the only findings that survive loopbacks.
- **defer** — Append to `{deferred_work_file}`. - **defer** — Append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates.
```markdown
- source_spec: `{spec_file}`
summary: <one sentence>
evidence: <why this is real>
```
- **reject** — Drop silently. - **reject** — Drop silently.
## NEXT ## NEXT

View File

@ -19,14 +19,19 @@ Implement the clarified intent directly.
### Review ### Review
Invoke the `bmad-review-adversarial-general` skill in a subagent with the changed files. The subagent gets NO conversation context — to avoid anchoring bias. Launch at the same model capability as the current session. If no sub-agents are available, write the changed files to a review prompt file in `{implementation_artifacts}` and HALT. Ask the human to run the review in a separate session and paste back the findings. Invoke the `bmad-review-adversarial-general` skill in a subagent with the changed files. The subagent gets NO conversation context — to avoid anchoring bias. Launch at the same model capability as the current session. If no subagents are available, write the changed files to a review prompt file in `{implementation_artifacts}` and HALT. Ask the human to run the review in a separate session and paste back the findings.
### Classify ### Classify
Deduplicate all review findings. Three categories only: Deduplicate all review findings. Three categories only:
- **patch** — trivially fixable. Auto-fix immediately. - **patch** — trivially fixable. Auto-fix immediately.
- **defer** — pre-existing issue not caused by this change. Append to `{deferred_work_file}`. - **defer** — pre-existing issue not caused by this change. Append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates.
```markdown
- source_spec: `{spec_file}`
summary: <one sentence>
evidence: <why this is real>
```
- **reject** — noise. Drop silently. - **reject** — noise. Drop silently.
If a finding is caused by this change but too significant for a trivial patch, HALT and present it to the human for decision before proceeding. If a finding is caused by this change but too significant for a trivial patch, HALT and present it to the human for decision before proceeding.

View File

@ -207,11 +207,11 @@ def test_unknown_category_style_uses_fallback_glyph():
def test_shipped_selector_is_in_sync_with_catalog(): def test_shipped_selector_is_in_sync_with_catalog():
# foolproofing: if someone edits brain-methods.csv they must regenerate the page. # foolproofing: if someone edits brain-methods.csv they must regenerate the page.
# Regenerate with: python3 brain.py html --out assets/brain-selector.html # Regenerate with: uv run brain.py html --out assets/brain-selector.html
asset = brain.DEFAULT_FILE.parent / "brain-selector.html" asset = brain.DEFAULT_FILE.parent / "brain-selector.html"
assert asset.is_file(), "missing assets/brain-selector.html — generate it" assert asset.is_file(), "missing assets/brain-selector.html — generate it"
expected = brain.html_doc(brain.load(brain.DEFAULT_FILE)) expected = brain.html_doc(brain.load(brain.DEFAULT_FILE))
assert asset.read_text(encoding="utf-8") == expected, ( assert asset.read_text(encoding="utf-8") == expected, (
"assets/brain-selector.html is stale; regenerate: " "assets/brain-selector.html is stale; regenerate: "
"python3 brain.py html --out assets/brain-selector.html" "uv run brain.py html --out assets/brain-selector.html"
) )

View File

@ -13,7 +13,7 @@ Exercises the scanner against a synthesized install tree:
- malformed TOML (surfaces as an error without aborting) - malformed TOML (surfaces as an error without aborting)
- multiple skills roots (e.g. project-local + user-global mix) - multiple skills roots (e.g. project-local + user-global mix)
Run: python3 scripts/tests/test_list_customizable_skills.py Run: uv run scripts/tests/test_list_customizable_skills.py
""" """
from __future__ import annotations from __future__ import annotations

View File

@ -10,12 +10,14 @@ Reads from four layers (highest priority last):
Outputs merged JSON to stdout. Errors go to stderr. Outputs merged JSON to stdout. Errors go to stderr.
Requires Python 3.11+ (uses stdlib `tomllib`). No `uv`, no `pip install`, Uses only the Python stdlib (`tomllib`) — no third-party dependencies.
no virtualenv — plain `python3` is sufficient. BMad is standardizing on `uv run` to invoke scripts (uv provisions a suitable
interpreter for you); a plain `python3` on PATH still works during the
transition. Either runner needs Python 3.11+ for `tomllib`.
python3 resolve_config.py --project-root /abs/path/to/project uv run resolve_config.py --project-root /abs/path/to/project
python3 resolve_config.py --project-root ... --key core uv run resolve_config.py --project-root ... --key core
python3 resolve_config.py --project-root ... --key agents uv run resolve_config.py --project-root ... --key agents
Merge rules (same as resolve_customization.py): Merge rules (same as resolve_customization.py):
- Scalars: override wins - Scalars: override wins

View File

@ -11,12 +11,14 @@ Skill name is derived from the basename of the skill directory.
Outputs merged JSON to stdout. Errors go to stderr. Outputs merged JSON to stdout. Errors go to stderr.
Requires Python 3.11+ (uses stdlib `tomllib`). No `uv`, no `pip install`, Uses only the Python stdlib (`tomllib`) — no third-party dependencies.
no virtualenv — plain `python3` is sufficient. BMad is standardizing on `uv run` to invoke scripts (uv provisions a suitable
interpreter for you); a plain `python3` on PATH still works during the
transition. Either runner needs Python 3.11+ for `tomllib`.
python3 resolve_customization.py --skill /abs/path/to/skill-dir uv run resolve_customization.py --skill /abs/path/to/skill-dir
python3 resolve_customization.py --skill ... --key agent uv run resolve_customization.py --skill ... --key agent
python3 resolve_customization.py --skill ... --key agent.menu uv run resolve_customization.py --skill ... --key agent.menu
Merge rules (purely structural — no field-name special-casing): Merge rules (purely structural — no field-name special-casing):
- Scalars (string, int, bool, float): override wins - Scalars (string, int, bool, float): override wins

View File

@ -0,0 +1,9 @@
---
name: deprecated-shim
description: 'DEPRECATED — consolidated into bmad-foo; this skill will be removed in v7 in favor of `bmad-foo`.'
---
# DEPRECATED — forwards to bmad-foo
This skill was consolidated into `bmad-foo` and is retained as a thin compatibility
shim so existing invocations keep working. New work should invoke `bmad-foo` directly.

View File

@ -0,0 +1,9 @@
---
name: missing-trigger
description: 'Generates a thing and writes it to disk for the user.'
---
# Missing Trigger
An active (non-deprecated) skill whose description omits a "Use when" trigger phrase.
This fixture guards against regressions: SKILL-06 must still flag it.

View File

@ -0,0 +1,8 @@
---
name: with-trigger
description: 'Generates a thing and writes it to disk. Use when the user asks to scaffold a thing.'
---
# With Trigger
An active skill whose description includes a "Use when" trigger phrase.

View File

@ -3319,134 +3319,63 @@ async function runTests() {
console.log(''); console.log('');
// ============================================================ // ============================================================
// Test Suite 46: Python environment check (version parsing + classification) // Test Suite 46: uv environment check (version parsing + messaging)
// ============================================================ // ============================================================
console.log(`${colors.yellow}Test Suite 46: python-check version parsing and classification${colors.reset}\n`); console.log(`${colors.yellow}Test Suite 46: uv-check version parsing and messaging${colors.reset}\n`);
try { try {
const { parsePythonVersion, classifyPython, detectPython } = require('../tools/installer/core/python-check'); const { parseUvVersion, detectUv } = require('../tools/installer/core/uv-check');
// Version parsing // Version parsing
const v312 = parsePythonVersion('Python 3.12.1'); const plain = parseUvVersion('uv 0.5.31');
assert(v312 && v312.major === 3 && v312.minor === 12 && v312.patch === 1, 'parses "Python 3.12.1"'); assert(plain && plain.major === 0 && plain.minor === 5 && plain.patch === 31, 'parses "uv 0.5.31"');
const v311 = parsePythonVersion('Python 3.11.0\n'); const brew = parseUvVersion('uv 0.5.31 (Homebrew 2025-02-12)');
assert(v311 && v311.raw === '3.11.0', 'parses with trailing newline'); assert(brew && brew.raw === '0.5.31', 'parses uv version with build suffix');
const v2 = parsePythonVersion('\nPython 2.7.18'); const noPatch = parseUvVersion('uv 1.2');
assert(v2 && v2.major === 2, 'parses Python 2 output (stderr-style)');
const noPatch = parsePythonVersion('Python 3.13');
assert(noPatch && noPatch.patch === 0, 'missing patch defaults to 0'); assert(noPatch && noPatch.patch === 0, 'missing patch defaults to 0');
assert(parsePythonVersion('') === null, 'empty output returns null'); assert(parseUvVersion('') === null, 'empty output returns null');
assert(parsePythonVersion('command not found: python3') === null, 'non-version output returns null'); assert(parseUvVersion('command not found: uv') === null, 'non-version output returns null');
assert(parsePythonVersion(null) === null, 'null output returns null'); assert(parseUvVersion(null) === null, 'null output returns null');
// Classification against feature requirements // Detection smoke test — must not throw; result is null or well-formed.
assert(classifyPython({ major: 3, minor: 11 }) === 'full', '3.11 is full support (tomllib floor)'); const detectedUv = detectUv();
assert(classifyPython({ major: 3, minor: 13 }) === 'full', '3.13 is full support'); assert(detectedUv === null || typeof detectedUv.version.raw === 'string', 'detectUv returns null or a well-formed result');
assert(classifyPython({ major: 4, minor: 0 }) === 'full', 'hypothetical 4.0 is full support');
assert(classifyPython({ major: 3, minor: 10 }) === 'partial', '3.10 is partial (memlog yes, tomllib no)');
assert(classifyPython({ major: 3, minor: 8 }) === 'partial', '3.8 is partial (memlog floor)');
assert(classifyPython({ major: 3, minor: 7 }) === 'unsupported', '3.7 is unsupported');
assert(classifyPython({ major: 2, minor: 7 }) === 'unsupported', '2.7 is unsupported');
assert(classifyPython(null) === 'none', 'no python is none');
// Detection smoke test — must not throw, and if it finds a Python the // checkUvEnvironment branch coverage — stub detection + prompts so the
// result must be well-formed. (CI machines may or may not have Python.) // assertions are deterministic regardless of whether uv is installed.
const detected = detectPython(); const uvCheck = require('../tools/installer/core/uv-check');
assert(
detected === null ||
(typeof detected.command === 'string' &&
typeof detected.version.raw === 'string' &&
typeof detected.isRuntimeCommand === 'boolean'),
'detectPython returns null or a well-formed result',
);
// checkPythonEnvironment branch coverage — stub detection, prompts, and
// process.exit so the assertions are deterministic regardless of the
// machine's Python. python-check resolves detectPython via module.exports
// and prompts via the shared module object, so swapping properties works.
const pythonCheck = require('../tools/installer/core/python-check');
const promptsModule = require('../tools/installer/prompts'); const promptsModule = require('../tools/installer/prompts');
const real = { const realUv = { detectUv: uvCheck.detectUv, log: promptsModule.log, note: promptsModule.note };
detectPython: pythonCheck.detectPython, const stubUv = (detectResult) => {
log: promptsModule.log, const seen = { success: [], warn: [], note: [] };
note: promptsModule.note, uvCheck.detectUv = () => detectResult;
select: promptsModule.select,
cancel: promptsModule.cancel,
exit: process.exit,
};
const stub = (detectResult, selectAnswer) => {
const seen = { success: [], warn: [], info: [], note: [], select: [], cancel: [], exit: [] };
pythonCheck.detectPython = () => detectResult;
promptsModule.log = { promptsModule.log = {
success: async (m) => void seen.success.push(m), success: async (m) => void seen.success.push(m),
warn: async (m) => void seen.warn.push(m), warn: async (m) => void seen.warn.push(m),
info: async (m) => void seen.info.push(m), info: async () => {},
error: async () => {}, error: async () => {},
}; };
promptsModule.note = async (m, t) => void seen.note.push(t || m); promptsModule.note = async (m, t) => void seen.note.push(t || m);
promptsModule.select = async (opts) => {
seen.select.push(opts.message);
return selectAnswer;
};
promptsModule.cancel = async (m) => void seen.cancel.push(m);
process.exit = (code) => {
seen.exit.push(code);
throw new Error('__stub_exit__');
};
return seen; return seen;
}; };
try { try {
const v = (major, minor, patch) => ({ major, minor, patch, raw: `${major}.${minor}.${patch}` }); // Branch: uv present — success, no warning.
let seen = stubUv({ version: { major: 0, minor: 5, patch: 31, raw: '0.5.31' } });
let result = await uvCheck.checkUvEnvironment();
assert(result.status === 'found' && seen.success.length === 1, 'uv present logs success');
assert(seen.success[0].includes('uv run') && seen.warn.length === 0, 'uv present mentions uv run, no warning');
// Branch: full support via the runtime command — success, no prompt. // Branch: uv missing — warn + setup note, never blocks (no prompt).
let seen = stub({ command: 'python3', version: v(3, 12, 1), isRuntimeCommand: true }, 'continue'); seen = stubUv(null);
let result = await pythonCheck.checkPythonEnvironment(); result = await uvCheck.checkUvEnvironment();
assert(result.status === 'full' && seen.success.length === 1, 'full support via python3 logs success'); assert(result.status === 'missing' && seen.warn.length === 1, 'uv missing warns');
assert(seen.select.length === 0 && seen.warn.length === 0, 'full support via python3 skips warning and ack prompt'); assert(seen.warn[0].includes('de facto standard'), 'uv-missing warning frames uv as the de facto standard');
assert(seen.note.length === 1 && seen.note[0].includes('uv'), 'uv missing shows a setup note');
// Branch: modern Python found, but not as `python3` — runtime mismatch.
seen = stub({ command: 'py -3', version: v(3, 12, 0), isRuntimeCommand: false }, 'continue');
result = await pythonCheck.checkPythonEnvironment();
assert(seen.success.length === 0, 'python3-mismatch never reports full support');
assert(
seen.warn.length === 1 && seen.warn[0].includes('python3') && seen.warn[0].includes('py -3'),
'python3-mismatch warns that scripts invoke python3',
);
assert(seen.select.length === 1 && result.status === 'full', 'python3-mismatch still requires the ack prompt');
// Branch: partial support (3.8–3.10) — warn + ack, continue returns.
seen = stub({ command: 'python3', version: v(3, 9, 5), isRuntimeCommand: true }, 'continue');
result = await pythonCheck.checkPythonEnvironment();
assert(
result.status === 'partial' && seen.warn.length === 1 && seen.warn[0].includes('3.11+'),
'partial support warns about tomllib floor',
);
assert(seen.select.length === 1 && seen.exit.length === 0, 'partial support prompts and continue proceeds');
// Branch: no Python, non-interactive — warn + info, never prompts.
seen = stub(null, 'continue');
result = await pythonCheck.checkPythonEnvironment({ nonInteractive: true });
assert(result.status === 'none' && seen.warn[0].includes('No Python found'), 'non-interactive with no Python warns');
assert(seen.select.length === 0 && seen.info.length === 1, 'non-interactive skips the ack prompt and logs continuation');
// Branch: no Python, interactive, user quits — cancel message + exit 0.
seen = stub(null, 'quit');
let threw = false;
try {
await pythonCheck.checkPythonEnvironment();
} catch (error) {
threw = error.message === '__stub_exit__';
}
assert(threw && seen.exit.length === 1 && seen.exit[0] === 0, 'quit choice exits 0 (user-cancel convention)');
assert(seen.cancel.length === 1, 'quit choice shows the cancel guidance');
} finally { } finally {
pythonCheck.detectPython = real.detectPython; uvCheck.detectUv = realUv.detectUv;
promptsModule.log = real.log; promptsModule.log = realUv.log;
promptsModule.note = real.note; promptsModule.note = realUv.note;
promptsModule.select = real.select;
promptsModule.cancel = real.cancel;
process.exit = real.exit;
} }
} catch (error) { } catch (error) {
console.log(`${colors.red}Test Suite 46 setup failed: ${error.message}${colors.reset}`); console.log(`${colors.red}Test Suite 46 setup failed: ${error.message}${colors.reset}`);

View File

@ -0,0 +1,107 @@
/**
* Skill Validation Test Runner
*
* Tests validateSkill() from validate-skills.js against fixtures, focused on
* SKILL-06 (description quality) and its deprecated-skill exemption.
*
* Usage: node test/test-validate-skills.js
* Exit codes: 0 = all tests pass, 1 = test failures
*/
const path = require('node:path');
const { validateSkill } = require('../tools/validate-skills.js');
// ANSI color codes
const colors = {
reset: '\u001B[0m',
green: '\u001B[32m',
red: '\u001B[31m',
cyan: '\u001B[36m',
dim: '\u001B[2m',
};
const FIXTURES = path.join(__dirname, 'fixtures/validate-skills');
let totalTests = 0;
let passedTests = 0;
const failures = [];
/**
* Run a single named test case, recording the result and printing a status line.
* @param {string} name - Human-readable test description.
* @param {Function} fn - Test body; throw to signal failure.
*/
function test(name, fn) {
totalTests++;
try {
fn();
passedTests++;
console.log(` ${colors.green}✓${colors.reset} ${name}`);
} catch (error) {
console.log(` ${colors.red}✗${colors.reset} ${name} ${colors.red}${error.message}${colors.reset}`);
failures.push({ name, message: error.message });
}
}
/**
* Throw an Error with `message` when `condition` is falsy.
* @param {boolean} condition - Expression that must hold.
* @param {string} message - Failure message.
*/
function assert(condition, message) {
if (!condition) throw new Error(message);
}
/**
* Whether validateSkill emitted the SKILL-06 "Use when/Use if" trigger finding
* for the given fixture skill directory.
* @param {string} skillName - Fixture subdirectory name under FIXTURES.
* @returns {boolean} True if the trigger-phrase finding was reported.
*/
function hasTriggerFinding(skillName) {
const findings = validateSkill(path.join(FIXTURES, skillName));
return findings.some((f) => f.rule === 'SKILL-06' && /trigger phrase/i.test(f.detail));
}
console.log(`\n${colors.cyan}Skill Validation — SKILL-06 trigger phrase${colors.reset}\n`);
test('deprecated skill is exempt from the trigger-phrase requirement', () => {
assert(
hasTriggerFinding('deprecated-shim') === false,
'Expected no SKILL-06 trigger finding for a DEPRECATED skill',
);
});
test('active skill missing a trigger phrase is still flagged', () => {
assert(
hasTriggerFinding('missing-trigger') === true,
'Expected a SKILL-06 trigger finding for a non-deprecated skill without "Use when"',
);
});
test('active skill with a "Use when" trigger is not flagged', () => {
assert(
hasTriggerFinding('with-trigger') === false,
'Expected no SKILL-06 trigger finding when description contains "Use when"',
);
});
// --- Summary ---
console.log(`\n${colors.cyan}${'═'.repeat(55)}${colors.reset}`);
console.log(`${colors.cyan}Test Results:${colors.reset}`);
console.log(` Total: ${totalTests}`);
console.log(` Passed: ${colors.green}${passedTests}${colors.reset}`);
console.log(` Failed: ${passedTests === totalTests ? colors.green : colors.red}${totalTests - passedTests}${colors.reset}`);
console.log(`${colors.cyan}${'═'.repeat(55)}${colors.reset}\n`);
if (failures.length > 0) {
console.log(`${colors.red}FAILED TESTS:${colors.reset}\n`);
for (const failure of failures) {
console.log(`${colors.red}✗${colors.reset} ${failure.name}`);
console.log(` ${failure.message}\n`);
}
process.exit(1);
}
console.log(`${colors.green}All tests passed!${colors.reset}\n`);
process.exit(0);

View File

@ -1233,6 +1233,9 @@ class Installer {
` 1. Launch your AI agent from your project folder`, ` 1. Launch your AI agent from your project folder`,
` 2. Not sure what to do? Invoke the ${color.cyan('bmad-help')} skill and ask it what to do!`, ` 2. Not sure what to do? Invoke the ${color.cyan('bmad-help')} skill and ask it what to do!`,
'', '',
` ${color.cyan('Tip:')} BMAD workflows increasingly run Python scripts via ${color.cyan('uv run')} — uv is`,
` becoming the de facto standard. If you don't have it yet, ask your agent to set it up.`,
'',
` Blog, Docs and Guides: ${color.blue('https://bmadcode.com/')}`, ` Blog, Docs and Guides: ${color.blue('https://bmadcode.com/')}`,
` Community: ${color.blue('https://discord.gg/gk8jAdXWmj')}`, ` Community: ${color.blue('https://discord.gg/gk8jAdXWmj')}`,
); );

View File

@ -1,199 +0,0 @@
const { spawnSync } = require('node:child_process');
const prompts = require('../prompts');
// Python 3.11 added stdlib `tomllib` (PEP 680), which the shared scripts in
// src/scripts/ (resolve_config.py, resolve_customization.py) require to read
// BMAD's TOML config files. memlog.py is more lenient and runs on 3.8+.
const PYTHON_FULL_SUPPORT = { major: 3, minor: 11 };
const PYTHON_PARTIAL_SUPPORT = { major: 3, minor: 8 };
// Every runtime call site (skill steps, on_complete hooks) invokes a literal
// `python3`, so only that command's version vouches for BMAD features. The
// fallback probes exist to tell the user "Python is installed, but not under
// the name BMAD uses" instead of a misleading "No Python found".
const RUNTIME_COMMAND = 'python3';
const PROBE_CANDIDATES =
process.platform === 'win32'
? [
{ command: 'python3', args: ['--version'] },
{ command: 'py', args: ['-3', '--version'] },
{ command: 'python', args: ['--version'] },
]
: [
{ command: 'python3', args: ['--version'] },
{ command: 'python', args: ['--version'] },
];
/**
* Parse a `python --version` output line into version parts.
* Python 3 prints to stdout; Python 2 printed to stderr — callers pass both.
* @param {string} output - Combined stdout/stderr from `python --version`
* @returns {{major: number, minor: number, patch: number, raw: string}|null}
*/
function parsePythonVersion(output) {
if (!output) return null;
const match = output.match(/Python\s+(\d+)\.(\d+)(?:\.(\d+))?/);
if (!match) return null;
return {
major: Number(match[1]),
minor: Number(match[2]),
patch: Number(match[3] || 0),
raw: `${match[1]}.${match[2]}.${match[3] || 0}`,
};
}
/**
* Classify a detected Python version against BMAD's feature requirements.
* @param {{major: number, minor: number}|null} version
* @returns {'full'|'partial'|'unsupported'|'none'}
*/
function classifyPython(version) {
if (!version) return 'none';
const { major, minor } = version;
if (major > PYTHON_FULL_SUPPORT.major || (major === PYTHON_FULL_SUPPORT.major && minor >= PYTHON_FULL_SUPPORT.minor)) {
return 'full';
}
if (major === PYTHON_PARTIAL_SUPPORT.major && minor >= PYTHON_PARTIAL_SUPPORT.minor) {
return 'partial';
}
return 'unsupported';
}
/**
* Run one probe candidate and return its parsed version, or null.
* @param {{command: string, args: string[]}} candidate
* @returns {{major: number, minor: number, patch: number, raw: string}|null}
*/
function probeVersion(candidate) {
const run = (extra = {}) =>
spawnSync(candidate.command, candidate.args, {
encoding: 'utf8',
timeout: 5000,
windowsHide: true,
...extra,
});
let result = run();
// Node >=18.20/20.12 refuses to spawn .bat/.cmd without a shell
// (CVE-2024-27980 hardening) and reports EINVAL — pyenv-win ships its
// python shims as .bat. Args here are static literals, so a shell retry
// is injection-safe.
if (result.error && result.error.code === 'EINVAL' && process.platform === 'win32') {
result = run({ shell: true });
}
if (result.error) return null;
return parsePythonVersion(`${result.stdout || ''}\n${result.stderr || ''}`);
}
/**
* Probe the local environment for a Python interpreter.
* Tries each candidate command and returns the first that reports a version.
* `isRuntimeCommand` is true only when the match is `python3` — the command
* BMAD scripts actually invoke.
* @returns {{command: string, version: {major: number, minor: number, patch: number, raw: string}, isRuntimeCommand: boolean}|null}
*/
function detectPython() {
for (const candidate of PROBE_CANDIDATES) {
try {
const version = probeVersion(candidate);
if (version) {
const display = candidate.args.length > 1 ? `${candidate.command} ${candidate.args.slice(0, -1).join(' ')}` : candidate.command;
return { command: display, version, isRuntimeCommand: candidate.command === RUNTIME_COMMAND };
}
} catch {
// Candidate not runnable — try the next one.
}
}
return null;
}
function upgradeHints() {
return [
'How to get Python 3.11+ (as `python3`):',
' macOS: brew install python3',
' Windows: winget install Python.Python.3.12 (then ensure `python3` resolves, e.g. enable the python3 alias)',
' Linux/WSL: sudo apt install python3 (Ubuntu 24.04+ ships 3.12; older distros: use pyenv or deadsnakes)',
' Docker: add python3 to your image (e.g. apk add python3 / apt-get install -y python3)',
].join('\n');
}
/**
* Check the local Python environment and warn about degraded BMAD features.
*
* Warn-don't-block: most of BMAD works without Python, so the install always
* may proceed — but the user must explicitly acknowledge the warning so it
* can't scroll past unseen. In non-interactive runs (--yes, or stdin is not
* a TTY) the warning is logged and the install continues without a prompt.
*
* @param {Object} [options]
* @param {boolean} [options.nonInteractive=false] - Skip the ack prompt (--yes, or no TTY)
* @returns {Promise<{status: string, detected: Object|null}>}
*/
async function checkPythonEnvironment({ nonInteractive = false } = {}) {
// Called via module.exports so tests can stub detection.
const detected = module.exports.detectPython();
const status = classifyPython(detected ? detected.version : null);
if (status === 'full' && detected.isRuntimeCommand) {
await prompts.log.success(`Python ${detected.version.raw} detected (${detected.command}) — all BMAD features supported.`);
return { status, detected };
}
if (detected && !detected.isRuntimeCommand) {
await prompts.log.warn(
`Python ${detected.version.raw} found via \`${detected.command}\`, but BMAD scripts invoke \`python3\`, which is not on PATH.\n` +
`Python-powered features (memlog session memory, TOML config resolution) won't run until \`python3\` resolves —\n` +
`add a python3 alias/shim, or reinstall Python with the python3 launcher enabled.`,
);
} else if (status === 'partial') {
await prompts.log.warn(
`Python ${detected.version.raw} detected (${detected.command}) — BMAD's TOML config tools need Python 3.11+ (stdlib tomllib).\n` +
`Works: memlog session memory. Won't work: config/customization resolution scripts.`,
);
} else {
const found =
status === 'unsupported' ? `Python ${detected.version.raw} detected (${detected.command}) — too old.` : 'No Python found on PATH.';
await prompts.log.warn(
`${found} BMAD installs fine without it, but Python-powered features\n` +
`(memlog session memory, TOML config resolution) won't run until Python 3.11+ is available.`,
);
}
await prompts.note(upgradeHints(), 'Python 3.11+ recommended');
if (nonInteractive) {
await prompts.log.info('Continuing anyway (non-interactive run). You can fix Python later — no reinstall needed.');
return { status, detected };
}
const choice = await prompts.select({
message: "BMAD's Python-powered features won't work yet. How do you want to proceed?",
choices: [
{
name: 'Continue install',
value: 'continue',
hint: 'BMAD works without Python — you can fix Python later, no reinstall needed',
},
{
name: 'Quit and fix Python first',
value: 'quit',
hint: 'make Python 3.11+ available as python3, then re-run the installer',
},
],
default: 'continue',
});
if (choice === 'quit') {
await prompts.cancel('Make Python 3.11+ available as `python3` (see hints above), then re-run the installer.');
process.exit(0);
}
return { status, detected };
}
module.exports = {
checkPythonEnvironment,
detectPython,
parsePythonVersion,
classifyPython,
PYTHON_FULL_SUPPORT,
PYTHON_PARTIAL_SUPPORT,
};

View File

@ -0,0 +1,97 @@
const { spawnSync } = require('node:child_process');
const prompts = require('../prompts');
// `uv` (https://docs.astral.sh/uv/) is becoming the de facto standard for
// running the Python scripts BMAD workflows shell out to: `uv run <script>`
// resolves the interpreter and any dependencies on demand, so skills don't
// have to assume a particular `python3` is on PATH. The ecosystem is mid-
// migration — some skills still call `python3` directly — so a missing `uv`
// is a warning, not a blocker: BMAD installs and runs either way.
const RUNTIME_COMMAND = 'uv';
/**
* Parse `uv --version` output into version parts.
* Example outputs: "uv 0.5.31", "uv 0.5.31 (Homebrew 2025-02-12)".
* @param {string} output - stdout/stderr from `uv --version`
* @returns {{major: number, minor: number, patch: number, raw: string}|null}
*/
function parseUvVersion(output) {
if (!output) return null;
const match = output.match(/uv\s+(\d+)\.(\d+)(?:\.(\d+))?/i);
if (!match) return null;
return {
major: Number(match[1]),
minor: Number(match[2]),
patch: Number(match[3] || 0),
raw: `${match[1]}.${match[2]}.${match[3] || 0}`,
};
}
/**
* Probe the local environment for `uv`.
* @returns {{version: {major: number, minor: number, patch: number, raw: string}}|null}
*/
function detectUv() {
let result;
try {
result = spawnSync(RUNTIME_COMMAND, ['--version'], {
encoding: 'utf8',
timeout: 5000,
windowsHide: true,
});
} catch {
return null;
}
if (!result || result.error) return null;
const version = parseUvVersion(`${result.stdout || ''}\n${result.stderr || ''}`);
return version ? { version } : null;
}
function setupHints() {
return [
'BMAD workflows increasingly run Python scripts via `uv run`, which manages',
'the interpreter and dependencies for you — no manual venv or pip needed.',
'',
'Easiest path: ask your AI agent to "install and set up uv for me".',
'',
'Or install it yourself:',
' macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh',
' Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"',
' Homebrew: brew install uv',
' Docs: https://docs.astral.sh/uv/getting-started/installation/',
].join('\n');
}
/**
* Check whether `uv` is available and inform the user.
*
* Warn-don't-block, and no acknowledgement prompt: `uv` is on its way to being
* the standard runner for BMAD's Python scripts, but the migration is still in
* progress, so the install never stops on its account. The note tells the user
* how to set it up (preferably by asking their agent).
*
* @returns {Promise<{status: 'found'|'missing', detected: Object|null}>}
*/
async function checkUvEnvironment() {
// Called via module.exports so tests can stub detection.
const detected = module.exports.detectUv();
if (detected) {
await prompts.log.success(`uv ${detected.version.raw} detected — ready to run BMAD's Python-powered scripts via \`uv run\`.`);
return { status: 'found', detected };
}
await prompts.log.warn(
"uv not found on PATH. uv is becoming the de facto standard for running BMAD's Python\n" +
'scripts (`uv run <script>`), and it provisions the interpreter for you. BMAD installs\n' +
'fine without it, but setting up uv now keeps you ahead as workflows adopt it.',
);
await prompts.note(setupHints(), 'uv recommended');
return { status: 'missing', detected: null };
}
module.exports = {
checkUvEnvironment,
detectUv,
parseUvVersion,
};

View File

@ -12,6 +12,10 @@ startMessage: |
🌟 100% free. 100% open source. Always. 🌟 100% free. 100% open source. Always.
No paywalls. No gated content. Knowledge shared, not sold. No paywalls. No gated content. Knowledge shared, not sold.
🐍 HEADS UP: uv (https://docs.astral.sh/uv/) is becoming the de facto standard
for running the Python scripts BMAD workflows rely on (`uv run <script>`).
If it's not set up yet, ask your AI agent to "install and set up uv for me".
🌐 CONNECT: 🌐 CONNECT:
Website: https://bmadcode.com/ Website: https://bmadcode.com/
Discord: https://discord.gg/gk8jAdXWmj Discord: https://discord.gg/gk8jAdXWmj

View File

@ -161,15 +161,16 @@ class UI {
const messageLoader = new MessageLoader(); const messageLoader = new MessageLoader();
await messageLoader.displayStartMessage(); await messageLoader.displayStartMessage();
// Probe the local Python before any other prompts: several BMAD features // Probe for `uv` before any other prompts: it's becoming the de facto
// (memlog session memory, TOML config resolution) need Python 3.11+ at // runner for the Python scripts BMAD workflows shell out to
// runtime. Warn-don't-block, but require an explicit ack so the warning // (`uv run <script>`), and uv provisions the interpreter itself, so it's
// can't scroll past unseen. The installer runs in the destination // the single thing worth checking for. The migration is still in progress
// environment, so probing PATH here tests the right machine. // (some skills still call `python3` directly), so this is informational —
// Skip the ack when stdin isn't a TTY (CI/Docker/piped): clack's select // warn-don't-block, no ack prompt — and just points the user at setup
// on closed stdin resolves to cancel, which would silently exit 0. // (ideally "ask your agent to set up uv"). The installer runs in the
const { checkPythonEnvironment } = require('./core/python-check'); // destination environment, so probing PATH here tests the right machine.
await checkPythonEnvironment({ nonInteractive: !!options.yes || !process.stdin.isTTY }); const { checkUvEnvironment } = require('./core/uv-check');
await checkUvEnvironment();
// Parse channel flags (--channel/--all-*/--next=/--pin) once. Warnings // Parse channel flags (--channel/--all-*/--next=/--pin) once. Warnings
// are surfaced immediately so the user sees them before any git ops run. // are surfaced immediately so the user sees them before any git ops run.

View File

@ -312,6 +312,11 @@ function validateSkill(skillDir) {
const name = skillFm && skillFm.name; const name = skillFm && skillFm.name;
const description = skillFm && skillFm.description; const description = skillFm && skillFm.description;
// Deprecated skills are thin compatibility shims that forward to a replacement.
// They intentionally omit a "Use when" trigger so users are steered to the new
// skill instead, so exempt them from the SKILL-06 trigger-phrase requirement.
const isDeprecated = typeof description === 'string' && /^\s*deprecated\b/i.test(description);
// --- SKILL-04: name format --- // --- SKILL-04: name format ---
if (name && !NAME_REGEX.test(name)) { if (name && !NAME_REGEX.test(name)) {
findings.push({ findings.push({
@ -349,7 +354,7 @@ function validateSkill(skillDir) {
}); });
} }
if (!/use\s+when\b/i.test(description) && !/use\s+if\b/i.test(description)) { if (!isDeprecated && !/use\s+when\b/i.test(description) && !/use\s+if\b/i.test(description)) {
findings.push({ findings.push({
rule: 'SKILL-06', rule: 'SKILL-06',
title: 'description Quality', title: 'description Quality',