From 80eb039d736e7ee58680948c3852b64f0e7b4034 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Fri, 20 Mar 2026 23:19:01 -0500 Subject: [PATCH 01/10] Remove all manifest concepts from skills and docs Delete bmad-manifest.json, bmad-skill-manifest.yaml, manifest.py, bmad-manifest-schema.json, and metadata-reference.md files across both builders and samples. Remove all references from docs, build processes, quality scans, prepass scripts, templates, and references. --- CHANGELOG.md | 2 +- docs/explanation/index.md | 1 - docs/explanation/what-are-bmad-agents.md | 8 - docs/explanation/what-are-skills.md | 11 +- docs/explanation/what-are-workflows.md | 10 +- docs/index.md | 2 +- docs/reference/bmad-skill-manifest.md | 136 ------ docs/reference/builder-commands.md | 3 +- docs/reference/index.md | 1 - docs/reference/workflow-patterns.md | 8 +- samples/bmad-agent-dream-weaver/SKILL.md | 26 +- .../bmad-manifest.json | 62 --- samples/bmad-agent-dream-weaver/init.md | 2 +- samples/bmad-excalidraw/bmad-manifest.json | 18 - .../assets/SKILL-template.md | 18 +- .../assets/quality-report-template.md | 1 - .../bmad-agent-builder/bmad-manifest.json | 24 - .../bmad-skill-manifest.yaml | 1 - .../bmad-agent-builder/build-process.md | 42 +- .../quality-scan-agent-cohesion.md | 24 +- .../quality-scan-script-opportunities.md | 6 +- .../quality-scan-structure.md | 38 +- .../references/metadata-reference.md | 126 ------ .../script-opportunities-reference.md | 58 +-- .../references/skill-best-practices.md | 4 +- .../references/template-substitution-rules.md | 1 - .../references/universal-scan-schema.md | 6 +- .../report-quality-scan-creator.md | 4 +- .../scripts/bmad-manifest-schema.json | 103 ----- .../bmad-agent-builder/scripts/manifest.py | 420 ------------------ .../scripts/prepass-execution-deps.py | 39 +- .../scripts/prepass-prompt-metrics.py | 77 +--- .../scripts/prepass-structure-capabilities.py | 193 +------- .../bmad-workflow-builder/bmad-manifest.json | 23 - .../bmad-skill-manifest.yaml | 1 - .../bmad-workflow-builder/build-process.md | 31 +- .../quality-scan-execution-efficiency.md | 9 +- .../quality-scan-script-opportunities.md | 4 +- .../quality-scan-skill-cohesion.md | 7 +- .../quality-scan-workflow-integrity.md | 17 +- .../references/classification-reference.md | 4 +- .../references/metadata-reference.md | 126 ------ .../script-opportunities-reference.md | 18 +- .../references/template-substitution-rules.md | 1 - .../references/universal-scan-schema.md | 6 +- .../scripts/bmad-manifest-schema.json | 103 ----- .../bmad-workflow-builder/scripts/manifest.py | 420 ------------------ .../scripts/prepass-execution-deps.py | 34 +- .../scripts/prepass-workflow-integrity.py | 5 - 49 files changed, 117 insertions(+), 2167 deletions(-) delete mode 100644 docs/reference/bmad-skill-manifest.md delete mode 100644 samples/bmad-agent-dream-weaver/bmad-manifest.json delete mode 100644 samples/bmad-excalidraw/bmad-manifest.json delete mode 100644 src/skills/bmad-agent-builder/bmad-manifest.json delete mode 100644 src/skills/bmad-agent-builder/bmad-skill-manifest.yaml delete mode 100644 src/skills/bmad-agent-builder/references/metadata-reference.md delete mode 100644 src/skills/bmad-agent-builder/scripts/bmad-manifest-schema.json delete mode 100644 src/skills/bmad-agent-builder/scripts/manifest.py delete mode 100644 src/skills/bmad-workflow-builder/bmad-manifest.json delete mode 100644 src/skills/bmad-workflow-builder/bmad-skill-manifest.yaml delete mode 100644 src/skills/bmad-workflow-builder/references/metadata-reference.md delete mode 100644 src/skills/bmad-workflow-builder/scripts/bmad-manifest-schema.json delete mode 100644 src/skills/bmad-workflow-builder/scripts/manifest.py diff --git a/CHANGELOG.md b/CHANGELOG.md index fafb1fb..e06d2f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ ### Removed -- Obsolete sample and manifest files from old skill structure +- Obsolete sample files from old skill structure - Unneeded images from project root ## [1.0.0] - 2026-03-15 diff --git a/docs/explanation/index.md b/docs/explanation/index.md index 121d4d4..10635ff 100644 --- a/docs/explanation/index.md +++ b/docs/explanation/index.md @@ -26,5 +26,4 @@ Create world-class AI agents and workflows with the BMad Builder. | Resource | Description | |----------|-------------| | **[Builder Commands](/reference/builder-commands.md)** | All capabilities, modes, and phases for both builders | -| **[Skill Manifest](/reference/bmad-skill-manifest.md)** | Field reference for bmad-manifest.json | | **[Workflow Patterns](/reference/workflow-patterns.md)** | Skill types, structure patterns, and execution models | diff --git a/docs/explanation/what-are-bmad-agents.md b/docs/explanation/what-are-bmad-agents.md index 8f5e8fe..d1f5596 100644 --- a/docs/explanation/what-are-bmad-agents.md +++ b/docs/explanation/what-are-bmad-agents.md @@ -42,14 +42,6 @@ You choose the mix when you design the agent. Internal commands keep everything Agents support a headless (autonomous) wake mode. When activated — for example through a cron job or an orchestrator like Open Claw — the agent skips waiting for user input and attempts to complete its tasks independently. This makes agents suitable for background automation while still being conversational when a human is present. -## Registering With BMad Help - -Every agent carries a **manifest** that can declare where it fits in a larger workflow sequence. You specify what the agent should run *before*, what it should run *after*, whether it is required or optional, and what output artifact it produces. The BMad Help system reads these manifests so it can recommend the right agent at the right time. - -:::note[Example] -You build a compliance-review agent and mark it as required before `create-architecture`. When a user asks BMad Help to start architecture, it notices the compliance artifact is missing and says: "Talk to the Compliance Review agent first — I don't see its output yet." -::: - ## When to Build an Agent vs. a Workflow | Choose an Agent When | Choose a Workflow When | diff --git a/docs/explanation/what-are-skills.md b/docs/explanation/what-are-skills.md index 4349442..df4697d 100644 --- a/docs/explanation/what-are-skills.md +++ b/docs/explanation/what-are-skills.md @@ -12,21 +12,14 @@ The BMad Builder produces skills that conform to the open standard and adds a fe | Component | Purpose | | --------- | ------- | | **SKILL.md** | The skill's instructions — persona, capabilities, and behavior rules | -| **bmad-manifest.json** | Metadata for installation, discovery, sequencing, and BMad Help integration | -| **prompts/** | Process prompts the skill loads at runtime | | **resources/** | Reference data, templates, and guidance documents | | **scripts/** | Deterministic validation and analysis scripts | -| **agents/** | LLM subagent definitions for parallel processing | | **templates/** | Building blocks for generated output | -Not every skill needs all of these. A simple utility might be a single `SKILL.md` with a manifest. A complex workflow or agent may use the full structure. +Not every skill needs all of these. A simple utility might be a single `SKILL.md`. A complex workflow or agent may use the full structure. ## Ready to Use on Build -Unlike earlier BMad versions that produced YAML and XML files requiring compilation, the builders now output a complete skill folder. Place it in your tool's skills directory — `.claude/skills`, `.codex/skills`, `.agent/skills`, or wherever your tool looks — and it is immediately usable. - -## Manifests and Sequencing - -Every skill's `bmad-manifest.json` can declare where it fits in a larger process. You specify what it runs **before**, what it runs **after**, whether it is **required** or **optional**, and what **output artifact** it produces. The BMad Help system reads these manifests to recommend the right skill at the right time and flag missing prerequisites. +The builders output a complete skill folder. Place it in your tool's skills directory — `.claude/skills`, `.codex/skills`, `.agent/skills`, or wherever your tool looks — and it is immediately usable. See [What Are Agents](/explanation/what-are-bmad-agents.md) and [What Are Workflows](/explanation/what-are-workflows.md) for how agents and workflows each use this foundation differently. diff --git a/docs/explanation/what-are-workflows.md b/docs/explanation/what-are-workflows.md index aa2416f..49d9d99 100644 --- a/docs/explanation/what-are-workflows.md +++ b/docs/explanation/what-are-workflows.md @@ -22,7 +22,7 @@ The BMad Builder classifies workflows into three tiers based on complexity. | Type | Description | Example | | ---- | ----------- | ------- | -| **Simple Utility** | A single-purpose tool that does one thing well | Validate a manifest, convert a file format | +| **Simple Utility** | A single-purpose tool that does one thing well | Validate a schema, convert a file format | | **Simple Workflow** | A short guided process with a few sequential steps | Create a quick tech spec | | **Complex Workflow** | A multi-stage process with branching paths, progressive disclosure, and potentially multiple outputs | Create and manage PRDs — covering create, edit, validate, convert, and polish | @@ -51,14 +51,6 @@ Guided mode is no longer the slow multi-step process of earlier BMad versions. I Like agents, workflows can support a **Headless Mode**. When invoked headless — through a scheduler, orchestrator, or another skill — the workflow skips interactive prompts and completes its process end-to-end without waiting for user input. -## Registering With BMad Help - -Workflows carry a **manifest** with sequencing metadata, just like agents. You declare what the workflow runs before, what it runs after, whether it is required or optional, and what output artifact it produces. - -:::note[Example] -You build a `security-review` workflow and mark it as required after `create-prd` and before `create-architecture`. BMad Help will recommend it at the right point in the planning sequence and flag when its output artifact is missing. -::: - ## When to Build a Workflow vs. an Agent | Choose a Workflow When | Choose an Agent When | diff --git a/docs/index.md b/docs/index.md index 3f14fee..623c3f6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -58,7 +58,7 @@ Build better skills with these guides distilled from real-world BMad development | ------- | ------- | | **[Concepts](/explanation/)** | What agents, workflows, and skills are — and how they relate | | **[Design Patterns](/explanation/#design-patterns)** | Progressive disclosure, subagent orchestration, authoring best practices | -| **[Reference](/reference/)** | Manifest schema, builder commands, workflow patterns | +| **[Reference](/reference/)** | Builder commands, workflow patterns | ## Community diff --git a/docs/reference/bmad-skill-manifest.md b/docs/reference/bmad-skill-manifest.md deleted file mode 100644 index 1aaf567..0000000 --- a/docs/reference/bmad-skill-manifest.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: "BMad Skill Manifest Reference" -description: Complete field reference for bmad-manifest.json — the metadata file that powers discovery, sequencing, and BMad Help integration ---- - -Every BMad skill includes a `bmad-manifest.json` file that describes what the skill is, what it can do, and where it fits in a larger workflow sequence. Agents and workflows share a single unified schema; agents simply use additional fields. - -## Skill Type Detection - -The manifest determines whether a skill is treated as an agent or a workflow based on field presence. - -| Condition | Skill Type | -| --------- | ---------- | -| `persona` field present | Agent | -| `persona` field absent | Workflow or utility | - -## Root Fields - -| Field | Type | Required | Description | -| ----- | ---- | -------- | ----------- | -| `module-code` | string | No | Short code for the module this skill belongs to (e.g., `bmb`, `cis`). Omit for standalone skills. Pattern: `^[a-z][a-z0-9-]*$` | -| `replaces-skill` | string | No | Registered name of the BMad skill this replaces. During `bmad-init`, the replacement inherits the original's metadata | -| `persona` | string | No | Distillation of the agent's essence — who they are, how they operate, what drives them. Presence of this field marks the skill as an agent. Other skills and agents use this to understand who they are interacting with | -| `has-memory` | boolean | No | Whether this skill persists state across sessions via sidecar memory | -| `capabilities` | array | **Yes** | What this skill can do. Every skill must have at least one capability | - -:::note[Agent-Specific Fields] -`persona` and `has-memory` are the fields that distinguish agents from workflows. A workflow manifest typically omits both. -::: - -## Capability Fields - -Each entry in the `capabilities` array describes one thing the skill can do. - -### Identity - -| Field | Type | Required | Description | -| ----- | ---- | -------- | ----------- | -| `name` | string | **Yes** | Capability identifier. Pattern: `^[a-z][a-z0-9-]*$` | -| `menu-code` | string | **Yes** | 2-3 uppercase letter shortcut for interactive menus. Pattern: `^[A-Z]{2,3}$` | -| `description` | string | **Yes** | What this capability does and when to suggest it | - -### Execution - -| Field | Type | Required | Description | -| ----- | ---- | -------- | ----------- | -| `supports-headless` | boolean | No | Whether this capability can support a headless mode that runs to completion without user interaction | -| `prompt` | string | No | Relative path to the prompt file for internal capabilities (e.g., `prompts/build-process.md`). Omit if handled by SKILL.md directly or if this is an external skill call | -| `skill-name` | string | No | Registered name of an external skill this capability delegates to. Omit for internal capabilities | - -:::caution[Prompt vs Skill-Name] -A capability should have `prompt` or `skill-name`, never both. The `prompt` field points to an internal prompt file; `skill-name` delegates to an external skill. -::: - -### Sequencing - -These fields control how BMad-Help orders skills within a module. - -| Field | Type | Required | Description | -| ----- | ---- | -------- | ----------- | -| `phase-name` | string | No | Which module phase this capability belongs to (e.g., `planning`, `design`, `anytime`) | -| `after` | string[] | No | Skill names that should ideally run before this capability. If those skills have `is-required: true`, they block this one | -| `before` | string[] | No | Skill names this capability should ideally run before. Helps the module sequencer understand ordering | -| `is-required` | boolean | No | Whether this capability must complete before skills listed in its `before` array can proceed | -| `output-location` | string | No | Where this capability writes its output. May contain config variables (e.g., `{bmad_builder_output_folder}`) | - -## Examples - -**Agent manifest** (agent builder) — note the `persona` field: - -```json -{ - "module-code": "bmb", - "persona": "An architect guide who helps dreamers and builders create AI agents through conversational discovery.", - "capabilities": [ - { - "name": "build", - "menu-code": "BP", - "description": "Build, edit, or convert agents through six-phase conversational discovery.", - "supports-headless": true, - "prompt": "prompts/build-process.md", - "phase-name": "anytime", - "output-location": "{bmad_builder_output_folder}" - } - ] -} -``` - -**Workflow manifest** (workflow builder) — no persona, no memory: - -```json -{ - "module-code": "bmb", - "capabilities": [ - { - "name": "build", - "menu-code": "BP", - "description": "Build, edit, or convert workflows and skills through six-phase conversational discovery.", - "supports-headless": true, - "prompt": "prompts/build-process.md", - "phase-name": "anytime", - "output-location": "{bmad_builder_output_folder}" - } - ] -} -``` - -## Validation - -The `manifest.py` script handles all manifest operations and validates against the schema on every write. - -```bash -# Validate an existing manifest -python3 scripts/manifest.py validate - -# Create a new manifest -python3 scripts/manifest.py create --module-code mymod --persona "..." - -# Add a capability -python3 scripts/manifest.py add-capability \ - --name build --menu-code BP \ - --description "Build things" \ - --supports-headless --prompt prompts/build.md - -# Read manifest summary -python3 scripts/manifest.py read - -# Update a field -python3 scripts/manifest.py update --set persona="New persona" - -# Update a capability field -python3 scripts/manifest.py update \ - --set capability.build.description="Updated description" -``` - -Beyond schema validation, the script checks for duplicate `menu-code` values and capabilities that specify both `prompt` and `skill-name`. diff --git a/docs/reference/builder-commands.md b/docs/reference/builder-commands.md index a5a0512..0d64e3a 100644 --- a/docs/reference/builder-commands.md +++ b/docs/reference/builder-commands.md @@ -45,7 +45,7 @@ Both builders accept any of these as a starting point. | 2 | **Capabilities Strategy** — internal commands, external skills, or both; script opportunities | **Classify Skill Type** — Simple Utility, Simple Workflow, or Complex Workflow; module membership | | 3 | **Gather Requirements** — name, persona, memory, capabilities, autonomous modes, folder dominion | **Gather Requirements** — name, description, stages, config variables, output artifacts, dependencies | | 4 | **Draft & Refine** — present outline, iterate until ready | **Draft & Refine** — present plan, clarify gaps, iterate until ready | -| 5 | **Build** — generate skill structure, manifest, lint gate | **Build** — generate skill structure, manifest, lint gate | +| 5 | **Build** — generate skill structure, lint gate | **Build** — generate skill structure, lint gate | | 6 | **Summary** — present results, offer Quality Optimize | **Summary** — present results, run unit tests if scripts exist, offer Quality Optimize | ### Agent Builder: Phase 2-3 Details @@ -97,7 +97,6 @@ Both builders produce the same folder structure, with components included only a ``` {skill-name}/ ├── SKILL.md # Skill instructions (persona embedded for agents) -├── bmad-manifest.json # Capabilities, sequencing, optional persona/memory ├── prompts/ # Internal capability prompts, init, autonomous-wake ├── resources/ # Reference data, memory-system definition (agents) ├── agents/ # Subagent definitions for parallel processing diff --git a/docs/reference/index.md b/docs/reference/index.md index f6d7cd3..2c186c5 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -10,5 +10,4 @@ Technical documentation for BMad Builder configuration and schemas. | Reference | Description | |-----------|-------------| | **[Builder Skills](/reference/builder-commands.md)** | Agent Builder and Workflow Builder skills, commands, and capabilities | -| **[Skill Manifest](/reference/bmad-skill-manifest.md)** | bmad-manifest.json field reference for agents and workflows | | **[Workflow & Skill Patterns](/reference/workflow-patterns.md)** | Structure types, design patterns, and execution models | diff --git a/docs/reference/workflow-patterns.md b/docs/reference/workflow-patterns.md index 8597a46..09989e9 100644 --- a/docs/reference/workflow-patterns.md +++ b/docs/reference/workflow-patterns.md @@ -9,7 +9,7 @@ Reference for how the BMad Builder classifies and structures skills. Every skill | Type | Description | Structure | | ---- | ----------- | --------- | -| **Simple Utility** | Input/output building block. Headless, composable, often script-driven. May opt out of `bmad-init` for true standalone use | Single SKILL.md + `scripts/` | +| **Simple Utility** | Input/output building block. Headless, composable, often script-driven. May opt out of `bmad-init` for true standalone use | SKILL.md + `scripts/` | | **Simple Workflow** | Multi-step process contained in a single SKILL.md. Uses `bmad-init`. Minimal or no `prompts/` | SKILL.md + optional `resources/` | | **Complex Workflow** | Multi-stage with progressive disclosure, stage prompts in `prompts/`, config integration. May support headless mode | SKILL.md (routing) + `prompts/` stages + `resources/` | @@ -37,7 +37,7 @@ Reference for how the BMad Builder classifies and structures skills. Every skill - Other skills and workflows call it - Deterministic or near-deterministic behavior - Could be a script but needs LLM judgment -- Examples: JSON validator, manifest checker, format converter +- Examples: JSON validator, format converter, file structure checker ### Simple Workflow @@ -65,7 +65,6 @@ Reference for how the BMad Builder classifies and structures skills. Every skill ``` bmad-my-utility/ ├── SKILL.md # Complete instructions, input/output spec -├── bmad-manifest.json # Single capability └── scripts/ # Core logic ├── process.py └── tests/ @@ -76,7 +75,6 @@ bmad-my-utility/ ``` bmad-my-workflow/ ├── SKILL.md # Steps inline, config loading, output spec -├── bmad-manifest.json # Single capability └── resources/ # Optional reference data ``` @@ -85,7 +83,6 @@ bmad-my-workflow/ ``` bmad-my-complex-workflow/ ├── SKILL.md # Routing logic — dispatches to prompts/ -├── bmad-manifest.json # Multiple capabilities ├── prompts/ # Stage instructions │ ├── 01-discovery.md │ ├── 02-planning.md @@ -115,4 +112,3 @@ Module membership is orthogonal to skill type — any type can be standalone or | **Module-based** | `bmad-{modulecode}-{skillname}` | Uses `bmad-init` with module code | | **Standalone** | `bmad-{skillname}` | Uses `bmad-init` by default; simple utilities may opt out | -See [Skill Manifest Reference](/reference/bmad-skill-manifest.md) for how capabilities declare sequencing within a module. diff --git a/samples/bmad-agent-dream-weaver/SKILL.md b/samples/bmad-agent-dream-weaver/SKILL.md index 6eeb309..5c82542 100644 --- a/samples/bmad-agent-dream-weaver/SKILL.md +++ b/samples/bmad-agent-dream-weaver/SKILL.md @@ -77,16 +77,15 @@ Load `references/memory-system.md` for memory discipline and structure. 2. **Interactive mode** — Load config and prepare session: - **Load config via bmad-init skill** — Store all returned vars. Use `{user_name}` for greeting, `{communication_language}` for all communications. - **Check first-run** — If no `{project-root}/_bmad/_memory/dream-weaver-sidecar/` folder exists, load `init.md` for first-run setup - - **Load memory, boundaries, manifest, and memory discipline in parallel** — Batch-read these 4 files in a single parallel tool call group: + - **Load memory, boundaries, and memory discipline in parallel** — Batch-read these 3 files in a single parallel tool call group: - `{project-root}/_bmad/_memory/dream-weaver-sidecar/access-boundaries.md` — enforce read/write/deny zones - `{project-root}/_bmad/_memory/dream-weaver-sidecar/index.md` — essential context and previous session - - `bmad-manifest.json` — set `{capabilities}` list - `references/memory-system.md` — memory discipline and structure - **Morning fast-lane check** — If activation occurs between 05:00–10:00 (infer from `coaching-profile.yaml` sleep schedule or system time), skip greeting ceremony and go straight to dream capture: "Quick, before it fades — tell me what you saw." Load menu AFTER capture is complete. - **Surface daily prompt** — If `{project-root}/_bmad/_memory/dream-weaver-sidecar/daily-prompt.md` exists and was written today, render its full content as part of the greeting — not as a notification about a file, as the greeting itself. - **Greet the user** — Welcome `{user_name}` with Oneira's voice, speaking in `{communication_language}` and applying persona and principles throughout the session - **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes - - **Present menu from bmad-manifest.json** — Generate menu dynamically by reading all capabilities from bmad-manifest.json: + - **Present capabilities** — Show available capabilities to the user: ``` Last time we were working on X. Would you like to continue, or: @@ -94,16 +93,15 @@ Load `references/memory-system.md` for memory discipline and structure. 💾 **Tip:** You can ask me to save our progress to memory at any time. **Available capabilities:** - (For each capability in bmad-manifest.json capabilities array, display as:) - {number}. [{menu-code}] - {description} → prompt:{name} + 1. [DC] - Capture and log a dream → dream-capture + 2. [DI] - Interpret a dream's symbols and themes → dream-interpret + 3. [RT] - Recall training exercises → recall-training + 4. [LC] - Lucid dreaming coaching → lucid-coaching + 5. [DS] - Plant dream seeds for tonight → dream-seed + 6. [PA] - Pattern analysis across dreams → pattern-analysis + 7. [SM] - Save memory → save-memory ``` - **Menu generation rules:** - - Read bmad-manifest.json and iterate through `capabilities` array - - For each capability: show sequential number, menu-code in brackets, description, and invocation path - - Capabilities with a `"prompt"` key → show `prompt:{name}` - - DO NOT hardcode menu examples — generate from actual manifest data - ## Session Close When the user indicates they're done, offer a brief closing — one sentence of reflection, one forward-looking note. Match tone to time of day: @@ -111,6 +109,6 @@ When the user indicates they're done, offer a brief closing — one sentence of - Evening: "Sleep well — I'll be curious what tonight brings." - General: "Until next time. Your dreams will keep weaving whether I'm here or not." -**CRITICAL Handling:** When user selects a code/number, consult the bmad-manifest.json capability mapping: -- **prompt:{name}** — Load and use the actual prompt from `{name}.md` — DO NOT invent the capability on the fly -- **skill:{name}** — Invoke the skill by its exact registered name +**CRITICAL Handling:** When user selects a capability: +- Load and use the actual prompt from the corresponding `.md` file — DO NOT invent the capability on the fly +- For external skills — invoke the skill by its exact registered name diff --git a/samples/bmad-agent-dream-weaver/bmad-manifest.json b/samples/bmad-agent-dream-weaver/bmad-manifest.json deleted file mode 100644 index 6de4d0d..0000000 --- a/samples/bmad-agent-dream-weaver/bmad-manifest.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "persona": "A warm, perceptive dream guide who blends psychological insight with poetic intuition — part analyst, part coach, part poet, wholly fascinated by the landscapes of your sleeping mind.", - "has-memory": true, - "capabilities": [ - { - "name": "dream-log", - "menu-code": "DL", - "description": "Capture a dream through guided conversation.", - "type": "prompt", - "prompt": "dream-log.md" - }, - { - "name": "dream-interpret", - "menu-code": "DI", - "description": "Analyze a dream for symbolism, meaning, and personal connections.", - "type": "prompt", - "prompt": "dream-interpret.md" - }, - { - "name": "pattern-discovery", - "menu-code": "PD", - "description": "Surface recurring themes and symbol patterns across the journal.", - "type": "prompt", - "prompt": "pattern-discovery.md" - }, - { - "name": "dream-query", - "menu-code": "DQ", - "description": "Search dream history by symbol, emotion, date, or keyword.", - "type": "prompt", - "prompt": "dream-query.md" - }, - { - "name": "lucid-coach", - "menu-code": "LC", - "description": "Progressive lucid dreaming training and technique guidance.", - "type": "prompt", - "prompt": "lucid-coach.md" - }, - { - "name": "recall-training", - "menu-code": "RT", - "description": "Dream recall improvement exercises and progress tracking.", - "type": "prompt", - "prompt": "recall-training.md" - }, - { - "name": "dream-seed", - "menu-code": "DS", - "description": "Pre-sleep dream incubation and intention setting.", - "type": "prompt", - "prompt": "dream-seed.md" - }, - { - "name": "save-memory", - "menu-code": "SM", - "description": "Save current session context to memory.", - "type": "prompt", - "prompt": "save-memory.md" - } - ] -} diff --git a/samples/bmad-agent-dream-weaver/init.md b/samples/bmad-agent-dream-weaver/init.md index d6f06c8..b96402c 100644 --- a/samples/bmad-agent-dream-weaver/init.md +++ b/samples/bmad-agent-dream-weaver/init.md @@ -60,4 +60,4 @@ Based on answers, create: ## Completion -Once memory files are created and user is greeted, present the capabilities menu from bmad-manifest.json. The first-run flow is complete. +Once memory files are created and user is greeted, present the capabilities menu. The first-run flow is complete. diff --git a/samples/bmad-excalidraw/bmad-manifest.json b/samples/bmad-excalidraw/bmad-manifest.json deleted file mode 100644 index f628727..0000000 --- a/samples/bmad-excalidraw/bmad-manifest.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "capabilities": [ - { - "name": "guided-design", - "menu-code": "GD", - "description": "Facilitates diagram design through conversational discovery.", - "supports-headless": true, - "prompt": "guided-design.md" - }, - { - "name": "diagram-generation", - "menu-code": "DG", - "description": "Generates Excalidraw diagram files from specifications.", - "supports-headless": true, - "prompt": "diagram-generation.md" - } - ] -} diff --git a/src/skills/bmad-agent-builder/assets/SKILL-template.md b/src/skills/bmad-agent-builder/assets/SKILL-template.md index 6bdec78..4b8bf8e 100644 --- a/src/skills/bmad-agent-builder/assets/SKILL-template.md +++ b/src/skills/bmad-agent-builder/assets/SKILL-template.md @@ -71,10 +71,9 @@ Load `references/memory-system.md` for memory discipline and structure. {if-sidecar}- **Check first-run** — If no `{skillName}-sidecar/` folder exists in `_bmad/_memory/`, load `init.md` for first-run setup - **Load access boundaries** — Read `_bmad/_memory/{skillName}-sidecar/access-boundaries.md` to enforce read/write/deny zones (load before any file operations) - **Load memory** — Read `_bmad/_memory/{skillName}-sidecar/index.md` for essential context and previous session{/if-sidecar} - - **Load manifest** — Read `bmad-manifest.json` to set `{capabilities}` list of actions the agent can perform (internal prompts and available skills) - **Greet the user** — Welcome `{user_name}`, speaking in `{communication_language}` and applying your persona and principles throughout the session {if-sidecar}- **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes{/if-sidecar} - - **Present menu from bmad-manifest.json** — Generate menu dynamically by reading all capabilities from bmad-manifest.json: + - **Present capabilities** — Show available capabilities to the user: ``` {if-sidecar}Last time we were working on X. Would you like to continue, or:{/if-sidecar}{if-no-sidecar}What would you like to do today?{/if-no-sidecar} @@ -82,16 +81,9 @@ Load `references/memory-system.md` for memory discipline and structure. {if-sidecar}💾 **Tip:** You can ask me to save our progress to memory at any time.{/if-sidecar} **Available capabilities:** - (For each capability in bmad-manifest.json capabilities array, display as:) - {number}. [{menu-code}] - {description} → {prompt}:{name} or {skill}:{name} + {number}. [{menu-code}] - {description} ``` - **Menu generation rules:** - - Read bmad-manifest.json and iterate through `capabilities` array - - For each capability: show sequential number, menu-code in brackets, description, and invocation type - - Type `prompt` → show `prompt:{name}`, type `skill` → show `skill:{name}` - - DO NOT hardcode menu examples — generate from actual manifest data - -**CRITICAL Handling:** When user selects a code/number, consult the bmad-manifest.json capability mapping: -- **prompt:{name}** — Load and use the actual prompt from `{name}.md` — DO NOT invent the capability on the fly -- **skill:{name}** — Invoke the skill by its exact registered name +**CRITICAL Handling:** When user selects a capability: +- **Internal capability** — Load and use the actual prompt from `{name}.md` — DO NOT invent the capability on the fly +- **External skill** — Invoke the skill by its exact registered name diff --git a/src/skills/bmad-agent-builder/assets/quality-report-template.md b/src/skills/bmad-agent-builder/assets/quality-report-template.md index b6811db..a315eb4 100644 --- a/src/skills/bmad-agent-builder/assets/quality-report-template.md +++ b/src/skills/bmad-agent-builder/assets/quality-report-template.md @@ -79,7 +79,6 @@ - Capabilities: {capabilities-count} - Memory sidecar: {has-memory} - Headless mode: {has-headless} -- Manifest valid: {manifest-valid} - Structure assessment: {structure-assessment} {/if-structure-metadata} diff --git a/src/skills/bmad-agent-builder/bmad-manifest.json b/src/skills/bmad-agent-builder/bmad-manifest.json deleted file mode 100644 index d9a6ace..0000000 --- a/src/skills/bmad-agent-builder/bmad-manifest.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "module-code": "bmb", - "persona": "An architect guide who helps dreamers and builders create AI agents through conversational discovery. Probes deeper than what users articulate, suggests what they haven't considered, and builds agents that exceed what they imagined.", - "capabilities": [ - { - "name": "build", - "menu-code": "BP", - "description": "Build, edit, or convert agents through six-phase conversational discovery. Covers new agents, format conversion, edits, and fixes.", - "supports-headless": true, - "prompt": "build-process.md", - "phase-name": "anytime", - "output-location": "{bmad_builder_output_folder}" - }, - { - "name": "quality-optimize", - "menu-code": "QO", - "description": "Comprehensive validation and optimization using lint scripts and LLM scanner subagents. Structure, prompt craft, efficiency, and more.", - "supports-headless": true, - "prompt": "quality-optimizer.md", - "phase-name": "anytime", - "output-location": "{bmad_builder_reports}" - } - ] -} diff --git a/src/skills/bmad-agent-builder/bmad-skill-manifest.yaml b/src/skills/bmad-agent-builder/bmad-skill-manifest.yaml deleted file mode 100644 index d0f08ab..0000000 --- a/src/skills/bmad-agent-builder/bmad-skill-manifest.yaml +++ /dev/null @@ -1 +0,0 @@ -type: skill diff --git a/src/skills/bmad-agent-builder/build-process.md b/src/skills/bmad-agent-builder/build-process.md index 4eb52cf..1f0dd56 100644 --- a/src/skills/bmad-agent-builder/build-process.md +++ b/src/skills/bmad-agent-builder/build-process.md @@ -34,9 +34,10 @@ Walk through each planned capability with the user and apply these filters: - File/directory structure checks (existence, naming conventions, required files) - Pattern matching against known standards (path conventions, naming rules) - Comparison operations (diff, version compare, before/after, cross-reference checking) -- Dependency graphing (parsing imports, references, manifest entries) +- Dependency graphing (parsing imports, references, skill entries) - Memory structure validation (required sections, path correctness) - Access boundary extraction and verification +- Dependency graphing (parsing imports, references, skill entries) - Pre-processing for LLM capabilities (extract compact metrics from large files so the LLM works from structured data, not raw content) - Post-processing validation (verify LLM output conforms to expected schema/structure) @@ -105,7 +106,6 @@ Once you have a cohesive idea, think one level deeper. Once you have done this, - Load `references/quality-dimensions.md` — quick mental checklist for build quality **Load based on context:** -- **If module-based:** Load `references/metadata-reference.md` — manifest.json field definitions, module metadata structure, config loading requirements - **Always load** `references/script-opportunities-reference.md` — script opportunity spotting guide, catalog, and output standards. Use this to identify additional script opportunities not caught in Phase 2, even if no scripts were initially planned. When confirmed: @@ -119,42 +119,10 @@ When confirmed: - **autonomous-wake.md** — autonomous activation behavior (if activation_modes includes "autonomous") - **save-memory.md** — explicit memory save capability (if sidecar enabled) -3. **Generate bmad-manifest.json** — Use `scripts/manifest.py` (validation is automatic on every write). **IMPORTANT:** The generated manifest must NOT include a `$schema` field — the schema is used for validation tooling only and is not part of the delivered skill. - ```bash - # Create manifest with agent identity - python3 scripts/manifest.py create {skill-path} \ - --persona "Succinct distillation of who this agent is" \ - --module-code {code} # if part of a module \ - --has-memory # if sidecar needed - - # Add each capability - # NOTE: capability description must be VERY short — what it produces, not how it works - python3 scripts/manifest.py add-capability {skill-path} \ - --name {name} --menu-code {MC} --description "Short: what it produces." \ - --supports-autonomous \ - --prompt {name}.md # internal capability - # OR --skill-name {skill} # external skill - # omit both if SKILL.md handles it directly - - # Module capabilities need sequencing metadata (confirm with user): - # - phase-name: which module phase (e.g., "1-analysis", "2-design", "anytime") - # - after: array of skill names that should run before this (inputs/dependencies) - # - before: array of skill names this should run before (downstream consumers) - # - is-required: if true, skills in 'before' are blocked until this completes - # - description: VERY short — what it produces, not how it works - python3 scripts/manifest.py add-capability {skill-path} \ - --name {name} --menu-code {MC} --description "Short: what it produces." \ - --phase-name anytime \ - --after skill-a skill-b \ - --before skill-c \ - --is-required - ``` - -4. **Folder structure:** +3. **Folder structure:** ``` {skill-name}/ ├── SKILL.md # Contains full persona content (agent.md embedded) -├── bmad-manifest.json # Capabilities, persona, memory, module integration ├── init.md # First-run setup (if sidecar) ├── autonomous-wake.md # Autonomous activation (if autonomous mode) ├── save-memory.md # Explicit memory save (if sidecar) @@ -176,9 +144,9 @@ When confirmed: Only create subfolders that are needed — most skills won't need all four. -5. Output to `bmad_builder_output_folder` from config, or `{project-root}/bmad-builder-creations/` +4. Output to `bmad_builder_output_folder` from config, or `{project-root}/bmad-builder-creations/` -6. **Lint gate** — run deterministic validation scripts: +5. **Lint gate** — run deterministic validation scripts: ```bash python3 scripts/scan-path-standards.py {skill-path} python3 scripts/scan-scripts.py {skill-path} diff --git a/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md b/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md index 66a8f17..b772765 100644 --- a/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md +++ b/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md @@ -21,7 +21,6 @@ This is an **opinionated, advisory scan**. Findings are suggestions, not errors. Find and read: - `SKILL.md` — Identity, persona, principles, description -- `bmad-manifest.json` — All capabilities with menu codes and descriptions - `*.md` (prompt files at root) — What each prompt actually does - `references/dimension-definitions.md` — If exists, context for capability design - Look for references to external skills in prompts and SKILL.md @@ -67,7 +66,7 @@ Find and read: | Check | Why It Matters | |-------|----------------| -| No overlapping capabilities in manifest | Confuses users, wastes tokens | +| No overlapping capabilities | Confuses users, wastes tokens | - Prompts don't duplicate functionality | Pick ONE place for each behavior | | Similar capabilities aren't separated | Could be consolidated into stronger single capability | @@ -159,7 +158,7 @@ Write JSON findings to: `{quality-report-dir}/agent-cohesion-temp.json` "agent_path": "{path}", "findings": [ { - "file": "SKILL.md|bmad-manifest.json|{name}.md", + "file": "SKILL.md|{name}.md", "severity": "high|medium|low|suggestion|strength", "category": "gap|redundancy|misalignment|opportunity|strength", "title": "Brief description", @@ -232,22 +231,21 @@ Merge all findings into the single `findings[]` array: ## Process 1. Read SKILL.md to understand persona and intent -2. Read bmad-manifest.json to enumerate all capabilities -3. Read all prompts to understand what each actually does -4. Read dimension-definitions.md if available for context -5. Build mental model of the agent as a whole -6. Evaluate cohesion across all 6 dimensions -7. Generate findings with specific, actionable suggestions -8. Identify strengths (positive feedback is valuable!) -9. Write JSON to `{quality-report-dir}/agent-cohesion-temp.json` -10. Return only the filename: `agent-cohesion-temp.json` +2. Read all prompts to understand what each actually does +3. Read dimension-definitions.md if available for context +4. Build mental model of the agent as a whole +5. Evaluate cohesion across all 6 dimensions +6. Generate findings with specific, actionable suggestions +7. Identify strengths (positive feedback is valuable!) +8. Write JSON to `{quality-report-dir}/agent-cohesion-temp.json` +9. Return only the filename: `agent-cohesion-temp.json` ## Critical After Draft Output **Before finalizing, think one level deeper and verify completeness and quality:** ### Scan Completeness -- Did I read SKILL.md, bmad-manifest.json, and ALL prompts? +- Did I read SKILL.md and ALL prompts? - Did I build a complete mental model of the agent? - Did I evaluate ALL 6 cohesion dimensions (persona, completeness, redundancy, external, granularity, journey)? - Did I read dimension-definitions.md if it exists? diff --git a/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md b/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md index 9e5de21..78aec8f 100644 --- a/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md +++ b/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md @@ -88,11 +88,9 @@ LLM instructions that compare two things for differences or verify consistency b **Signal phrases:** "compare", "diff", "match against", "cross-reference", "verify consistency", "check alignment" **Examples:** -- Comparing manifest entries against actual files → Python script - Diffing two versions of a document → git diff or Python difflib - Cross-referencing prompt names against SKILL.md references → Python script - Checking config variables are defined where used → Python regex scan -- Verifying menu codes are unique within the agent → Python script ### 6. Structure & File System Checks LLM instructions that verify directory structure, file existence, or organizational rules. @@ -111,7 +109,7 @@ LLM instructions that trace references, imports, or relationships between files. **Signal phrases:** "dependency", "references", "imports", "relationship", "graph", "trace" **Examples:** -- Building skill dependency graph from manifest → Python script +- Building skill dependency graph → Python script - Tracing which resources are loaded by which prompts → Python regex - Detecting circular references → Python graph algorithm - Mapping capability → prompt file → resource file chains → Python script @@ -136,7 +134,7 @@ Operations where a script could verify that LLM-generated output meets structura **Examples:** - Validating generated JSON against schema → Python jsonschema - Checking generated markdown has required sections → Python script -- Verifying generated manifest has required fields → Python script +- Verifying generated output has required fields → Python script --- diff --git a/src/skills/bmad-agent-builder/quality-scan-structure.md b/src/skills/bmad-agent-builder/quality-scan-structure.md index e7bceb2..4f57907 100644 --- a/src/skills/bmad-agent-builder/quality-scan-structure.md +++ b/src/skills/bmad-agent-builder/quality-scan-structure.md @@ -4,9 +4,9 @@ You are **StructureBot**, a quality engineer who validates the structural integr ## Overview -You validate that an agent's structure is complete, correct, and internally consistent. This covers SKILL.md structure, manifest alignment, capability cross-references, memory setup, identity quality, and logical consistency. **Why this matters:** Structural issues break agents at runtime — missing files, orphaned capabilities, and inconsistent identity make agents unreliable. +You validate that an agent's structure is complete, correct, and internally consistent. This covers SKILL.md structure, capability cross-references, memory setup, identity quality, and logical consistency. **Why this matters:** Structural issues break agents at runtime — missing files, orphaned capabilities, and inconsistent identity make agents unreliable. -This is a unified scan covering both *structure* (correct files, valid sections) and *capabilities* (manifest accuracy, capability-prompt alignment). These concerns are tightly coupled — you can't evaluate capability completeness without validating structural integrity. +This is a unified scan covering both *structure* (correct files, valid sections) and *capabilities* (capability-prompt alignment). These concerns are tightly coupled — you can't evaluate capability completeness without validating structural integrity. ## Your Role @@ -14,7 +14,7 @@ Read the pre-pass JSON first at `{quality-report-dir}/structure-capabilities-pre ## Scan Targets -Pre-pass provides: frontmatter validation, section inventory, template artifacts, capability cross-reference, manifest validation, memory path consistency. +Pre-pass provides: frontmatter validation, section inventory, template artifacts, capability cross-reference, memory path consistency. Read raw files ONLY for: - Description quality assessment (is it specific enough to trigger reliably?) @@ -22,7 +22,7 @@ Read raw files ONLY for: - Communication style quality (are examples good? do they match the persona?) - Principles quality (guiding vs generic platitudes?) - Logical consistency (does description match actual capabilities?) -- Activation sequence logical ordering (can't load manifest before config) +- Activation sequence logical ordering - Memory setup completeness for sidecar agents - Access boundaries adequacy - Headless mode setup if declared @@ -36,8 +36,6 @@ Review all findings from `structure-capabilities-prepass.json`: - Missing required sections (Overview, Identity, Communication Style, Principles, On Activation) - Invalid sections (On Exit, Exiting) - Template artifacts (orphaned {if-*}, {displayName}, etc.) -- Manifest validation issues (missing persona field, missing capabilities, duplicate menu codes) -- Capability cross-reference issues (orphaned prompts, missing prompt files) - Memory path inconsistencies - Directness pattern violations @@ -81,10 +79,8 @@ Include all pre-pass findings in your output, preserved as-is. These are determi ### Logical Consistency | Check | Why It Matters | |-------|----------------| -| Description matches actual capabilities in manifest | Claiming capabilities that don't exist | | Identity matches communication style | Identity says "formal expert" but style shows casual examples | -| Activation sequence is logically ordered | Config must load before manifest reads config vars | -| Capabilities referenced in prompts exist in manifest | Prompt references capability not in manifest | +| Activation sequence is logically ordered | Config must load before reading config vars | ### Memory Setup (Sidecar Agents) | Check | Why It Matters | @@ -107,8 +103,8 @@ Include all pre-pass findings in your output, preserved as-is. These are determi | Severity | When to Apply | |----------|---------------| -| **Critical** | Missing SKILL.md, invalid frontmatter (no name), missing required sections, manifest missing or invalid, orphaned capabilities pointing to non-existent files | -| **High** | Description too vague to trigger, identity missing or ineffective, capabilities-manifest mismatch, memory setup incomplete for sidecar, activation sequence logically broken | +| **Critical** | Missing SKILL.md, invalid frontmatter (no name), missing required sections, orphaned capabilities pointing to non-existent files | +| **High** | Description too vague to trigger, identity missing or ineffective, memory setup incomplete for sidecar, activation sequence logically broken | | **Medium** | Principles are generic, communication style lacks examples, minor consistency issues, headless mode incomplete | | **Low** | Style refinement suggestions, principle strengthening opportunities | @@ -132,10 +128,10 @@ Write JSON findings to: `{quality-report-dir}/structure-temp.json` "skill_path": "{path}", "findings": [ { - "file": "SKILL.md|bmad-manifest.json|{name}.md", + "file": "SKILL.md|{name}.md", "line": 42, "severity": "critical|high|medium|low", - "category": "frontmatter|sections|artifacts|manifest|capabilities|identity|communication-style|principles|consistency|memory-setup|headless-mode|activation-sequence", + "category": "frontmatter|sections|artifacts|capabilities|identity|communication-style|principles|consistency|memory-setup|headless-mode|activation-sequence", "title": "Brief description", "detail": "", "action": "Specific action to resolve" @@ -146,7 +142,6 @@ Write JSON findings to: `{quality-report-dir}/structure-temp.json` "capabilities_count": 0, "has_memory": false, "has_headless": false, - "manifest_valid": true }, "summary": { "total_findings": 0, @@ -162,14 +157,13 @@ Write JSON findings to: `{quality-report-dir}/structure-temp.json` 1. Read pre-pass JSON at `{quality-report-dir}/structure-capabilities-prepass.json` 2. Include all pre-pass findings in output 3. Read SKILL.md for judgment-based assessment -4. Read bmad-manifest.json for capability evaluation -5. Read relevant prompt files for cross-reference quality -6. Assess description, identity, communication style, principles quality -7. Check logical consistency across all components -8. Check memory setup completeness if sidecar -9. Check headless mode setup if declared -10. Write JSON to `{quality-report-dir}/structure-temp.json` -11. Return only the filename: `structure-temp.json` +4. Read relevant prompt files for cross-reference quality +5. Assess description, identity, communication style, principles quality +6. Check logical consistency across all components +7. Check memory setup completeness if sidecar +8. Check headless mode setup if declared +9. Write JSON to `{quality-report-dir}/structure-temp.json` +10. Return only the filename: `structure-temp.json` ## Critical After Draft Output diff --git a/src/skills/bmad-agent-builder/references/metadata-reference.md b/src/skills/bmad-agent-builder/references/metadata-reference.md deleted file mode 100644 index 4a0b7e7..0000000 --- a/src/skills/bmad-agent-builder/references/metadata-reference.md +++ /dev/null @@ -1,126 +0,0 @@ -# Manifest Reference - -Every BMad skill has a `bmad-manifest.json` at its root. This is the unified format for agents, workflows, and simple skills. - -## File Location - -``` -{skillname}/ -├── SKILL.md # name, description, persona content -├── bmad-manifest.json # Capabilities, module integration, persona distillate -└── ... -``` - -## SKILL.md Frontmatter (Minimal) - -```yaml ---- -name: bmad-{modulecode}-{skillname} -description: [5-8 word summary]. [Use when user says 'X' or 'Y'.] ---- -``` - -## bmad-manifest.json - -**NOTE:** Do NOT include `$schema` in generated manifests. The schema is used by validation tooling only — it is not part of the delivered skill. - -```json -{ - "module-code": "bmb", - "replaces-skill": "bmad-original-agent", - "persona": "A succinct distillation of who this agent is and how they operate.", - "has-memory": true, - "capabilities": [ - { - "name": "build", - "menu-code": "BP", - "description": "Builds agents through conversational discovery. Outputs to skill folder.", - "supports-headless": true, - "prompt": "build-process.md", - "phase-name": "anytime", - "after": ["create-prd"], - "before": [], - "is-required": false, - "output-location": "{bmad_builder_output_folder}" - }, - { - "name": "external-tool", - "menu-code": "ET", - "description": "Delegates to another registered skill.", - "supports-headless": false, - "skill-name": "bmad-some-other-skill" - } - ] -} -``` - -## Field Reference - -### Top-Level Fields - -| Field | Type | Required | Purpose | -|-------|------|----------|---------| -| `module-code` | string | If module | Short code for namespacing (e.g., `bmb`, `cis`) | -| `replaces-skill` | string | No | Registered skill name this replaces. Inherits metadata during bmad-init. | -| `persona` | string | Agents only | Succinct distillation of the agent's essence. **Presence = this is an agent.** | -| `has-memory` | boolean | No | Whether state persists across sessions via sidecar memory | - -### Capability Fields - -| Field | Type | Required | Purpose | -|-------|------|----------|---------| -| `name` | string | Yes | Kebab-case identifier | -| `menu-code` | string | Yes | 2-3 uppercase letter shortcut for menus | -| `description` | string | Yes | What it does and when to suggest it | -| `supports-autonomous` | boolean | No | Can run without user interaction | -| `prompt` | string | No | Relative path to prompt file (internal capability) | -| `skill-name` | string | No | Registered name of external skill (external capability) | -| `phase-name` | string | No | Module phase this belongs to | -| `after` | array | No | Skill names that should run before this capability | -| `before` | array | No | Skill names this capability should run before | -| `is-required` | boolean | No | If true, skills in `before` are blocked until this completes | -| `output-location` | string | No | Where output goes (may use config variables) | - -### Three Capability Flavors - -1. **Has `prompt`** — internal capability routed to a prompt file -2. **Has `skill-name`** — delegates to another registered skill -3. **Has neither** — SKILL.md handles it directly - -### The `replaces-skill` Field - -When set, the skill inherits metadata from the replaced skill during `bmad-init`. Explicit fields in the new manifest override inherited values. - -## Agent vs Workflow vs Skill - -No type field needed — inferred from content: -- **Has `persona`** → agent -- **No `persona`** → workflow or skill (distinction is complexity, not manifest structure) - -## Config Loading - -All module skills MUST use the `bmad-init` skill at startup. - -## Path Construction Rules — CRITICAL - -Only use `{project-root}` for `_bmad` paths. - -**Three path types:** -- **Skill-internal** — bare relative paths (no prefix) -- **Project `_bmad` paths** — always `{project-root}/_bmad/...` -- **Config variables** — used directly, already contain `{project-root}` in their resolved values - -**Correct:** -``` -references/reference.md # Skill-internal (bare relative) -capability.md # Skill-internal (bare relative) -{project-root}/_bmad/_memory/x-sidecar/ # Project _bmad path -{output_folder}/report.md # Config var (already has full path) -``` - -**Never use:** -``` -../../other-skill/file.md # Cross-skill relative path breaks with reorganization -{project-root}/{config_var}/output.md # Double-prefix -./references/reference.md # Relative prefix breaks context changes -``` diff --git a/src/skills/bmad-agent-builder/references/script-opportunities-reference.md b/src/skills/bmad-agent-builder/references/script-opportunities-reference.md index fecbed0..4aa2a9c 100644 --- a/src/skills/bmad-agent-builder/references/script-opportunities-reference.md +++ b/src/skills/bmad-agent-builder/references/script-opportunities-reference.md @@ -83,19 +83,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 2. Manifest Schema Validator - -**Status:** ✅ Already exists at `scripts/manifest.py` (create, add-capability, update, read, validate) - -**Enhancement opportunities:** -- Add `--agent-path` flag for auto-discovery -- Check menu code uniqueness within agent -- Verify prompt files exist for `type: "prompt"` capabilities -- Verify external skill names are registered (could check against skill registry) - ---- - -### 3. Template Artifact Scanner +### 2. Template Artifact Scanner **What:** Scan for orphaned template substitution artifacts @@ -107,7 +95,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 4. Access Boundaries Extractor +### 3. Access Boundaries Extractor **What:** Extract and validate access boundaries from memory-system.md @@ -128,30 +116,11 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 5. Prompt Frontmatter Comparator - -**What:** Compare prompt file frontmatter against bmad-manifest.json - -**Why:** Capability misalignment causes runtime errors - -**Checks:** -```python -# For each prompt .md file at skill root: -- Has frontmatter (name, description, menu-code) -- name matches manifest capability name -- menu-code matches manifest (case-insensitive) -- description is present -``` - -**Output:** JSON with mismatches, missing files - -**Implementation:** Python, reads bmad-manifest.json and all prompt .md files at skill root - --- ## Priority 2: Analysis Scripts -### 6. Token Counter +### 4. Token Counter **What:** Count tokens in each file of an agent @@ -171,7 +140,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 7. Dependency Graph Generator +### 5. Dependency Graph Generator **What:** Map skill → external skill dependencies @@ -179,8 +148,8 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc **Checks:** ```python -# Parse bmad-manifest.json for external skills # Parse SKILL.md for skill invocation patterns +# Parse prompt files for external skill references # Build dependency graph ``` @@ -190,7 +159,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 8. Activation Flow Analyzer +### 6. Activation Flow Analyzer **What:** Parse SKILL.md On Activation section for sequence @@ -204,8 +173,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc 3. First-run check 4. Access boundaries load 5. Memory load -6. Manifest load -7. Greet +6. Greet 8. Present menu ``` @@ -215,7 +183,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 9. Memory Structure Validator +### 7. Memory Structure Validator **What:** Validate memory-system.md structure @@ -236,7 +204,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 10. Subagent Pattern Detector +### 8. Subagent Pattern Detector **What:** Detect if agent uses BMAD Advanced Context Pattern @@ -259,7 +227,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc ## Priority 3: Composite Scripts -### 11. Agent Health Check +### 9. Agent Health Check **What:** Run all validation scripts and aggregate results @@ -273,7 +241,7 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc --- -### 12. Comparison Validator +### 10. Comparison Validator **What:** Compare two versions of an agent for differences @@ -355,7 +323,6 @@ The Quality Optimizer should: # Run all validation scripts python scripts/validate-frontmatter.py --agent-path {path} bash scripts/scan-template-artifacts.sh --agent-path {path} -python scripts/compare-prompts-manifest.py --agent-path {path} # Collect JSON outputs # Spawn sub-agents only for semantic checks @@ -368,8 +335,7 @@ python scripts/compare-prompts-manifest.py --agent-path {path} **Phase 1 (Immediate value):** 1. Template Artifact Scanner (Bash + jq) -2. Prompt Frontmatter Comparator (Python) -3. Access Boundaries Extractor (Python) +2. Access Boundaries Extractor (Python) **Phase 2 (Enhanced validation):** 4. Token Counter (Python) diff --git a/src/skills/bmad-agent-builder/references/skill-best-practices.md b/src/skills/bmad-agent-builder/references/skill-best-practices.md index 67cdeb3..55b2976 100644 --- a/src/skills/bmad-agent-builder/references/skill-best-practices.md +++ b/src/skills/bmad-agent-builder/references/skill-best-practices.md @@ -13,8 +13,8 @@ Match specificity to task fragility: | Freedom | When to Use | Example | |---------|-------------|---------| | **High** (text instructions) | Multiple valid approaches, context-dependent | "Analyze the user's vision and suggest capabilities" | -| **Medium** (pseudocode/templates) | Preferred pattern exists, some variation OK | `def generate_manifest(capabilities, format="json"):` | -| **Low** (exact scripts) | Fragile operations, consistency critical | `python3 scripts/manifest.py validate path/to/skill` (do not modify) | +| **Medium** (pseudocode/templates) | Preferred pattern exists, some variation OK | `def generate_config(capabilities, format="json"):` | +| **Low** (exact scripts) | Fragile operations, consistency critical | `python3 scripts/scan-path-standards.py path/to/skill` (do not modify) | **Analogy**: Narrow bridge with cliffs = low freedom. Open field = high freedom. diff --git a/src/skills/bmad-agent-builder/references/template-substitution-rules.md b/src/skills/bmad-agent-builder/references/template-substitution-rules.md index b3bce15..d0e511f 100644 --- a/src/skills/bmad-agent-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-agent-builder/references/template-substitution-rules.md @@ -46,7 +46,6 @@ All generated agents use these paths: - `init.md` — First-run setup - `{name}.md` — Individual capability prompts - `references/memory-system.md` — Memory discipline (if sidecar needed) -- `bmad-manifest.json` — Capabilities and metadata with menu codes - `scripts/` — Python/shell scripts for deterministic operations (if needed) ## Frontmatter Placeholders diff --git a/src/skills/bmad-agent-builder/references/universal-scan-schema.md b/src/skills/bmad-agent-builder/references/universal-scan-schema.md index 11e6df8..d49bfcc 100644 --- a/src/skills/bmad-agent-builder/references/universal-scan-schema.md +++ b/src/skills/bmad-agent-builder/references/universal-scan-schema.md @@ -206,9 +206,9 @@ Array of strings (script paths that already exist). "line": 12, "severity": "high", "category": "frontmatter", - "title": "Missing required 'version' field in frontmatter", - "detail": "The SKILL.md frontmatter is missing the version field. This prevents the manifest generator from producing correct output and breaks version-aware consumers.", - "action": "Add 'version: 1.0.0' to the YAML frontmatter block" + "title": "Missing 'description' field in frontmatter", + "detail": "The SKILL.md frontmatter is missing the description field. Without a description, the skill cannot be triggered reliably by the help system.", + "action": "Add a description with trigger phrases to the YAML frontmatter block" }, { "file": "build-process.md", diff --git a/src/skills/bmad-agent-builder/report-quality-scan-creator.md b/src/skills/bmad-agent-builder/report-quality-scan-creator.md index 3a0376e..f1dd3f4 100644 --- a/src/skills/bmad-agent-builder/report-quality-scan-creator.md +++ b/src/skills/bmad-agent-builder/report-quality-scan-creator.md @@ -37,7 +37,7 @@ For each scanner file, extract not just `findings` arrays but ALL of these data | Consolidation opportunities | agent-cohesion `assessments.cohesion_analysis.redundancy_level.consolidation_opportunities` | Consolidation Opportunities in Cohesion | | Creative suggestions | `findings[]` with severity="suggestion" (no separate creative_suggestions array) | Creative Suggestions in Cohesion section | | Craft & agent assessment | prompt-craft `assessments.skillmd_assessment` (incl. `persona_context`), `assessments.prompt_health`, `summary.assessment` | Prompt Craft section header + Executive Summary | -| Structure metadata | structure `assessments.metadata` (has_memory, has_headless, manifest_valid, etc.) | Structure & Capabilities section header | +| Structure metadata | structure `assessments.metadata` (has_memory, has_headless, etc.) | Structure & Capabilities section header | | User journeys | enhancement-opportunities `assessments.user_journeys[]` | User Journeys section | | Autonomous assessment | enhancement-opportunities `assessments.autonomous_assessment` | Autonomous Readiness section | | Skill understanding | enhancement-opportunities `assessments.skill_understanding` | Creative section header | @@ -74,7 +74,7 @@ Fill the template section by section, following the `` guidance - [ ] Cohesion analysis dimensional scores table (if present) - [ ] Consolidation opportunities from cohesion redundancy analysis - [ ] Craft assessment, skill type assessment, and persona context assessment -- [ ] Structure metadata (sections_found, has_memory, has_headless, manifest_valid) +- [ ] Structure metadata (sections_found, has_memory, has_headless) - [ ] ALL user journeys with ALL friction_points and bright_spots per archetype - [ ] The autonomous_assessment block (all fields) - [ ] All findings with severity="suggestion" from cohesion scanners diff --git a/src/skills/bmad-agent-builder/scripts/bmad-manifest-schema.json b/src/skills/bmad-agent-builder/scripts/bmad-manifest-schema.json deleted file mode 100644 index ea674b5..0000000 --- a/src/skills/bmad-agent-builder/scripts/bmad-manifest-schema.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "BMad Manifest Schema", - "description": "Unified schema for all BMad skill manifest files (agents, workflows, skills)", - - "type": "object", - - "properties": { - "$schema": { - "description": "JSON Schema identifier", - "type": "string" - }, - - "module-code": { - "description": "Short code for the module this skill belongs to (e.g., bmb, cis). Omit for standalone skills.", - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - - "replaces-skill": { - "description": "Registered name of the BMad skill this replaces. Inherits metadata during bmad-init.", - "type": "string", - "minLength": 1 - }, - - "persona": { - "description": "Succinct distillation of the agent's essence — who they are, how they operate, what drives them. Presence of this field indicates the skill is an agent. Useful for other skills/agents to understand who they're interacting with.", - "type": "string", - "minLength": 1 - }, - - "has-memory": { - "description": "Whether this skill persists state across sessions via sidecar memory.", - "type": "boolean" - }, - - "capabilities": { - "description": "What this skill can do. Every skill has at least one capability.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "name": { - "description": "Capability identifier (kebab-case)", - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "menu-code": { - "description": "2-3 uppercase letter shortcut for interactive menus", - "type": "string", - "pattern": "^[A-Z]{2,3}$" - }, - "description": { - "description": "What this capability does and when to suggest it", - "type": "string" - }, - "supports-headless": { - "description": "Whether this capability can run without user interaction", - "type": "boolean" - }, - - "prompt": { - "description": "Relative path to the prompt file for internal capabilities (e.g., build-process.md). Omit if handled by SKILL.md directly or if this is an external skill call.", - "type": "string" - }, - "skill-name": { - "description": "Registered name of an external skill this capability delegates to. Omit for internal capabilities.", - "type": "string" - }, - - "phase-name": { - "description": "Which module phase this capability belongs to (e.g., planning, design, anytime). For module sequencing.", - "type": "string" - }, - "after": { - "description": "Skill names that should ideally run before this capability. If is-required is true on those skills, they block this one.", - "type": "array", - "items": { "type": "string" } - }, - "before": { - "description": "Skill names that this capability should ideally run before. Helps the module sequencer understand ordering.", - "type": "array", - "items": { "type": "string" } - }, - "is-required": { - "description": "Whether this capability must complete before skills listed in its 'before' array can proceed.", - "type": "boolean" - }, - "output-location": { - "description": "Where this capability writes its output. May contain config variables (e.g., {bmad_builder_output_folder}/agents/).", - "type": "string" - } - }, - "required": ["name", "menu-code", "description"], - "additionalProperties": false - } - } - }, - - "required": ["capabilities"], - "additionalProperties": false -} diff --git a/src/skills/bmad-agent-builder/scripts/manifest.py b/src/skills/bmad-agent-builder/scripts/manifest.py deleted file mode 100644 index 9bae89f..0000000 --- a/src/skills/bmad-agent-builder/scripts/manifest.py +++ /dev/null @@ -1,420 +0,0 @@ -#!/usr/bin/env python3 -"""BMad manifest CRUD and validation. - -All manifest operations go through this script. Validation runs automatically -on every write. Prompts call this instead of touching JSON directly. - -Usage: - python3 scripts/manifest.py create [options] - python3 scripts/manifest.py add-capability [options] - python3 scripts/manifest.py update --set key=value [...] - python3 scripts/manifest.py remove-capability --name - python3 scripts/manifest.py read [--capabilities|--capability ] - python3 scripts/manifest.py validate -""" - -# /// script -# requires-python = ">=3.9" -# dependencies = [ -# "jsonschema>=4.0.0", -# ] -# /// - -from __future__ import annotations - -import argparse -import json -import sys -from pathlib import Path -from typing import Any - -try: - from jsonschema import Draft7Validator -except ImportError: - print("Error: jsonschema required. Install with: pip install jsonschema", file=sys.stderr) - sys.exit(2) - -MANIFEST_FILENAME = "bmad-manifest.json" -SCHEMA_FILENAME = "bmad-manifest-schema.json" - - -def get_schema_path() -> Path: - """Schema is co-located with this script.""" - return Path(__file__).parent / SCHEMA_FILENAME - - -def get_manifest_path(skill_path: Path) -> Path: - return skill_path / MANIFEST_FILENAME - - -def load_schema() -> dict[str, Any]: - path = get_schema_path() - if not path.exists(): - print(f"Error: Schema not found: {path}", file=sys.stderr) - sys.exit(2) - with path.open() as f: - return json.load(f) - - -def load_manifest(skill_path: Path) -> dict[str, Any]: - path = get_manifest_path(skill_path) - if not path.exists(): - return {} - with path.open() as f: - try: - return json.load(f) - except json.JSONDecodeError as e: - print(f"Error: Invalid JSON in {path}: {e}", file=sys.stderr) - sys.exit(2) - - -def save_manifest(skill_path: Path, data: dict[str, Any]) -> bool: - """Save manifest after validation. Returns True if valid and saved.""" - errors = validate(data) - if errors: - print(f"Validation failed with {len(errors)} error(s):", file=sys.stderr) - for err in errors: - print(f" [{err['path']}] {err['message']}", file=sys.stderr) - return False - - path = get_manifest_path(skill_path) - path.parent.mkdir(parents=True, exist_ok=True) - with path.open("w") as f: - json.dump(data, f, indent=2) - f.write("\n") - return True - - -def validate(data: dict[str, Any]) -> list[dict[str, Any]]: - """Validate manifest against schema. Returns list of errors.""" - schema = load_schema() - validator = Draft7Validator(schema) - errors = [] - for error in validator.iter_errors(data): - errors.append({ - "path": ".".join(str(p) for p in error.path) if error.path else "root", - "message": error.message, - }) - return errors - - -def validate_extras(data: dict[str, Any]) -> list[str]: - """Additional checks beyond schema validation.""" - warnings = [] - capabilities = data.get("capabilities", []) - - if not capabilities: - warnings.append("No capabilities defined — every skill needs at least one") - return warnings - - menu_codes: dict[str, str] = {} - for i, cap in enumerate(capabilities): - name = cap.get("name", f"") - - # Duplicate menu-code check - mc = cap.get("menu-code", "") - if mc and mc in menu_codes: - warnings.append(f"Duplicate menu-code '{mc}' in '{menu_codes[mc]}' and '{name}'") - elif mc: - menu_codes[mc] = name - - # Both prompt and skill-name - if "prompt" in cap and "skill-name" in cap: - warnings.append(f"Capability '{name}' has both 'prompt' and 'skill-name' — pick one") - - return warnings - - -# --- Commands --- - -def cmd_create(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - existing = load_manifest(skill_path) - if existing: - print(f"Error: Manifest already exists at {get_manifest_path(skill_path)}", file=sys.stderr) - print("Use 'update' to modify or delete the file first.", file=sys.stderr) - return 1 - - data: dict[str, Any] = {} - - if args.module_code: - data["module-code"] = args.module_code - if args.replaces_skill: - data["replaces-skill"] = args.replaces_skill - if args.persona: - data["persona"] = args.persona - if args.has_memory: - data["has-memory"] = True - - data["capabilities"] = [] - - if save_manifest(skill_path, data): - print(f"Created {get_manifest_path(skill_path)}") - return 0 - return 1 - - -def cmd_add_capability(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found. Run 'create' first.", file=sys.stderr) - return 1 - - capabilities = data.setdefault("capabilities", []) - - # Check for duplicate name - for cap in capabilities: - if cap.get("name") == args.name: - print(f"Error: Capability '{args.name}' already exists. Use 'update' to modify.", file=sys.stderr) - return 1 - - cap: dict[str, Any] = { - "name": args.name, - "menu-code": args.menu_code, - "description": args.description, - } - - if args.supports_autonomous: - cap["supports-headless"] = True - if args.prompt: - cap["prompt"] = args.prompt - if args.skill_name: - cap["skill-name"] = args.skill_name - if args.phase_name: - cap["phase-name"] = args.phase_name - if args.after: - cap["after"] = args.after - if args.before: - cap["before"] = args.before - if args.is_required: - cap["is-required"] = True - if args.output_location: - cap["output-location"] = args.output_location - - capabilities.append(cap) - - if save_manifest(skill_path, data): - print(f"Added capability '{args.name}' [{args.menu_code}]") - return 0 - return 1 - - -def cmd_update(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found. Run 'create' first.", file=sys.stderr) - return 1 - - # Parse --set key=value pairs - for pair in args.set: - if "=" not in pair: - print(f"Error: Invalid --set format '{pair}'. Use key=value.", file=sys.stderr) - return 1 - key, value = pair.split("=", 1) - - # Handle boolean values - if value.lower() == "true": - value = True - elif value.lower() == "false": - value = False - - # Handle capability updates: capability.name.field=value - if key.startswith("capability."): - parts = key.split(".", 2) - if len(parts) != 3: - print(f"Error: Capability update format: capability..=", file=sys.stderr) - return 1 - cap_name, field = parts[1], parts[2] - found = False - for cap in data.get("capabilities", []): - if cap.get("name") == cap_name: - cap[field] = value - found = True - break - if not found: - print(f"Error: Capability '{cap_name}' not found.", file=sys.stderr) - return 1 - else: - # Handle removing fields with empty value - if value == "": - data.pop(key, None) - else: - data[key] = value - - if save_manifest(skill_path, data): - print(f"Updated {get_manifest_path(skill_path)}") - return 0 - return 1 - - -def cmd_remove_capability(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found.", file=sys.stderr) - return 1 - - capabilities = data.get("capabilities", []) - original_len = len(capabilities) - data["capabilities"] = [c for c in capabilities if c.get("name") != args.name] - - if len(data["capabilities"]) == original_len: - print(f"Error: Capability '{args.name}' not found.", file=sys.stderr) - return 1 - - if save_manifest(skill_path, data): - print(f"Removed capability '{args.name}'") - return 0 - return 1 - - -def cmd_read(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found.", file=sys.stderr) - return 1 - - if args.capabilities: - caps = data.get("capabilities", []) - if args.json: - print(json.dumps(caps, indent=2)) - else: - for cap in caps: - prompt_or_skill = cap.get("prompt", cap.get("skill-name", "(SKILL.md)")) - auto = " [autonomous]" if cap.get("supports-headless") else "" - print(f" [{cap.get('menu-code', '??')}] {cap['name']} — {cap.get('description', '')}{auto}") - print(f" → {prompt_or_skill}") - return 0 - - if args.capability: - for cap in data.get("capabilities", []): - if cap.get("name") == args.capability: - print(json.dumps(cap, indent=2)) - return 0 - print(f"Error: Capability '{args.capability}' not found.", file=sys.stderr) - return 1 - - if args.json: - print(json.dumps(data, indent=2)) - else: - # Summary view - is_agent = "persona" in data - print(f"Type: {'Agent' if is_agent else 'Workflow/Skill'}") - if data.get("module-code"): - print(f"Module: {data['module-code']}") - if is_agent: - print(f"Persona: {data['persona'][:80]}...") - if data.get("has-memory"): - print("Memory: enabled") - caps = data.get("capabilities", []) - print(f"Capabilities: {len(caps)}") - for cap in caps: - prompt_or_skill = cap.get("prompt", cap.get("skill-name", "(SKILL.md)")) - auto = " [autonomous]" if cap.get("supports-headless") else "" - print(f" [{cap.get('menu-code', '??')}] {cap['name']}{auto} → {prompt_or_skill}") - return 0 - - -def cmd_validate(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found.", file=sys.stderr) - return 1 - - errors = validate(data) - warnings = validate_extras(data) - - if args.json: - print(json.dumps({ - "valid": len(errors) == 0, - "errors": errors, - "warnings": warnings, - }, indent=2)) - else: - if not errors: - print("✓ Manifest is valid") - else: - print(f"✗ {len(errors)} error(s):", file=sys.stderr) - for err in errors: - print(f" [{err['path']}] {err['message']}", file=sys.stderr) - - if warnings: - print(f"\n⚠ {len(warnings)} warning(s):", file=sys.stderr) - for w in warnings: - print(f" {w}", file=sys.stderr) - - return 0 if not errors else 1 - - -def main() -> int: - parser = argparse.ArgumentParser( - description="BMad manifest CRUD and validation", - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - sub = parser.add_subparsers(dest="command", required=True) - - # create - p_create = sub.add_parser("create", help="Create a new manifest") - p_create.add_argument("skill_path", type=str, help="Path to skill directory") - p_create.add_argument("--module-code", type=str) - p_create.add_argument("--replaces-skill", type=str) - p_create.add_argument("--persona", type=str) - p_create.add_argument("--has-memory", action="store_true") - - # add-capability - p_add = sub.add_parser("add-capability", help="Add a capability") - p_add.add_argument("skill_path", type=str, help="Path to skill directory") - p_add.add_argument("--name", required=True, type=str) - p_add.add_argument("--menu-code", required=True, type=str) - p_add.add_argument("--description", required=True, type=str) - p_add.add_argument("--supports-autonomous", action="store_true") - p_add.add_argument("--prompt", type=str, help="Relative path to prompt file") - p_add.add_argument("--skill-name", type=str, help="External skill name") - p_add.add_argument("--phase-name", type=str) - p_add.add_argument("--after", nargs="*", help="Skill names that should run before this") - p_add.add_argument("--before", nargs="*", help="Skill names this should run before") - p_add.add_argument("--is-required", action="store_true") - p_add.add_argument("--output-location", type=str) - - # update - p_update = sub.add_parser("update", help="Update manifest fields") - p_update.add_argument("skill_path", type=str, help="Path to skill directory") - p_update.add_argument("--set", nargs="+", required=True, help="key=value pairs") - - # remove-capability - p_remove = sub.add_parser("remove-capability", help="Remove a capability") - p_remove.add_argument("skill_path", type=str, help="Path to skill directory") - p_remove.add_argument("--name", required=True, type=str) - - # read - p_read = sub.add_parser("read", help="Read manifest") - p_read.add_argument("skill_path", type=str, help="Path to skill directory") - p_read.add_argument("--capabilities", action="store_true", help="List capabilities only") - p_read.add_argument("--capability", type=str, help="Show specific capability") - p_read.add_argument("--json", action="store_true", help="JSON output") - - # validate - p_validate = sub.add_parser("validate", help="Validate manifest") - p_validate.add_argument("skill_path", type=str, help="Path to skill directory") - p_validate.add_argument("--json", action="store_true", help="JSON output") - - args = parser.parse_args() - - commands = { - "create": cmd_create, - "add-capability": cmd_add_capability, - "update": cmd_update, - "remove-capability": cmd_remove_capability, - "read": cmd_read, - "validate": cmd_validate, - } - - return commands[args.command](args) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/skills/bmad-agent-builder/scripts/prepass-execution-deps.py b/src/skills/bmad-agent-builder/scripts/prepass-execution-deps.py index d4b69ed..33eb811 100644 --- a/src/skills/bmad-agent-builder/scripts/prepass-execution-deps.py +++ b/src/skills/bmad-agent-builder/scripts/prepass-execution-deps.py @@ -5,7 +5,7 @@ so the LLM scanner can evaluate efficiency from compact structured data. Covers: -- Dependency graph from bmad-manifest.json (bmad-requires, bmad-prefer-after) +- Dependency graph from skill structure - Circular dependency detection - Transitive dependency redundancy - Parallelizable stage groups (independent nodes) @@ -199,42 +199,12 @@ def scan_sequential_patterns(filepath: Path, rel_path: str) -> list[dict]: def scan_execution_deps(skill_path: Path) -> dict: """Run all deterministic execution efficiency checks.""" - # Parse bmad-manifest.json for dependency graph + # Build dependency graph from skill structure dep_graph: dict[str, list[str]] = {} prefer_after: dict[str, list[str]] = {} all_stages: set[str] = set() - manifest_found = False - - manifest_path = skill_path / 'bmad-manifest.json' - if manifest_path.exists(): - manifest_found = True - try: - data = json.loads(manifest_path.read_text(encoding='utf-8')) - if isinstance(data, dict): - # Parse capabilities for dependency info - capabilities = data.get('capabilities', []) - if isinstance(capabilities, list): - for cap in capabilities: - if isinstance(cap, dict): - name = cap.get('name') - if name: - all_stages.add(name) - dep_graph[name] = cap.get('bmad-requires', []) or [] - prefer_after[name] = cap.get('bmad-prefer-after', []) or [] - - # Also check top-level dependencies - top_name = data.get('name') - if top_name and top_name not in all_stages: - all_stages.add(top_name) - top_requires = data.get('bmad-requires', []) or [] - top_prefer = data.get('bmad-prefer-after', []) or [] - if top_requires or top_prefer: - dep_graph[top_name] = top_requires - prefer_after[top_name] = top_prefer - except (json.JSONDecodeError, OSError): - pass - - # Also check for stage-level manifests or stage definitions in SKILL.md + + # Check for stage definitions in prompt files prompts_dir = skill_path / 'prompts' if prompts_dir.exists(): for f in sorted(prompts_dir.iterdir()): @@ -314,7 +284,6 @@ def scan_execution_deps(skill_path: Path) -> dict: 'timestamp': datetime.now(timezone.utc).isoformat(), 'status': status, 'dependency_graph': { - 'manifest_found': manifest_found, 'stages': sorted(all_stages), 'hard_dependencies': dep_graph, 'soft_dependencies': prefer_after, diff --git a/src/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py b/src/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py index 9c8da05..b6a3ff1 100644 --- a/src/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py +++ b/src/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py @@ -14,7 +14,6 @@ - Config header and progression condition presence per prompt - File-level token estimates (chars / 4 rough approximation) - Prompt frontmatter validation (name, description, menu-code) -- Manifest alignment check (frontmatter vs bmad-manifest.json entries) - Wall-of-text detection - Suggestive loading grep """ @@ -197,69 +196,6 @@ def parse_prompt_frontmatter(filepath: Path) -> dict: return result -def check_manifest_alignment(skill_path: Path, prompt_frontmatters: dict[str, dict]) -> dict: - """Compare prompt frontmatter against bmad-manifest.json entries.""" - alignment = { - 'manifest_found': False, - 'mismatches': [], - 'manifest_only': [], - 'prompt_only': [], - } - - manifest_path = skill_path / 'bmad-manifest.json' - if not manifest_path.exists(): - return alignment - - try: - data = json.loads(manifest_path.read_text(encoding='utf-8')) - except (json.JSONDecodeError, OSError): - return alignment - - alignment['manifest_found'] = True - - capabilities = data.get('capabilities', []) - if not isinstance(capabilities, list): - return alignment - - # Build manifest lookup by name - manifest_caps = {} - for cap in capabilities: - if isinstance(cap, dict) and cap.get('name'): - manifest_caps[cap['name']] = cap - - # Compare - prompt_names = set(prompt_frontmatters.keys()) - manifest_names = set(manifest_caps.keys()) - - alignment['manifest_only'] = sorted(manifest_names - prompt_names) - alignment['prompt_only'] = sorted(prompt_names - manifest_names) - - # Check field mismatches for overlapping entries - for name in sorted(prompt_names & manifest_names): - pfm = prompt_frontmatters[name] - mcap = manifest_caps[name] - - issues = [] - # Compare name field - pfm_name = pfm.get('fields', {}).get('name') - if pfm_name and pfm_name != mcap.get('name'): - issues.append(f'name mismatch: frontmatter="{pfm_name}" manifest="{mcap.get("name")}"') - - # Compare menu-code - pfm_mc = pfm.get('fields', {}).get('menu-code') - mcap_mc = mcap.get('menu-code') - if pfm_mc and mcap_mc and pfm_mc != mcap_mc: - issues.append(f'menu-code mismatch: frontmatter="{pfm_mc}" manifest="{mcap_mc}"') - - if issues: - alignment['mismatches'].append({ - 'name': name, - 'issues': issues, - }) - - return alignment - - def scan_file_patterns(filepath: Path, rel_path: str) -> dict: """Extract metrics and pattern matches from a single file.""" content = filepath.read_text(encoding='utf-8') @@ -357,9 +293,8 @@ def scan_prompt_metrics(skill_path: Path) -> dict: data['is_skill_md'] = True files_data.append(data) - # Prompt files at skill root — also extract frontmatter - prompt_frontmatters: dict[str, dict] = {} - skip_files = {'SKILL.md', 'bmad-manifest.json', 'bmad-skill-manifest.yaml'} + # Prompt files at skill root + skip_files = {'SKILL.md'} for f in sorted(skill_path.iterdir()): if f.is_file() and f.suffix == '.md' and f.name not in skip_files and f.name != 'SKILL.md': @@ -370,10 +305,6 @@ def scan_prompt_metrics(skill_path: Path) -> dict: pfm = parse_prompt_frontmatter(f) data['prompt_frontmatter'] = pfm - # Use stem as key for manifest alignment - prompt_name = pfm.get('fields', {}).get('name', f.stem) - prompt_frontmatters[prompt_name] = pfm - files_data.append(data) # Resources (just sizes, for progressive disclosure assessment) @@ -388,9 +319,6 @@ def scan_prompt_metrics(skill_path: Path) -> dict: 'tokens': len(content) // 4, } - # Manifest alignment - manifest_alignment = check_manifest_alignment(skill_path, prompt_frontmatters) - # Aggregate stats total_waste = sum(len(f['waste_patterns']) for f in files_data) total_backrefs = sum(len(f['back_references']) for f in files_data) @@ -434,7 +362,6 @@ def scan_prompt_metrics(skill_path: Path) -> dict: 'total_wall_of_text': total_walls, }, 'resource_sizes': resource_sizes, - 'manifest_alignment': manifest_alignment, 'files': files_data, } diff --git a/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py b/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py index ceff64e..6f3d730 100644 --- a/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py +++ b/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py @@ -8,8 +8,6 @@ - Template artifact detection - Agent name validation (bmad-{code}-agent-{name} or bmad-agent-{name}) - Required agent sections (Overview, Identity, Communication Style, Principles, On Activation) -- bmad-manifest.json validation (persona field for agent detection, capabilities) -- Capability cross-referencing with prompt files at skill root - Memory path consistency checking - Language/directness pattern grep - On Exit / Exiting section detection (invalid) @@ -214,183 +212,6 @@ def find_template_artifacts(filepath: Path, rel_path: str) -> list[dict]: return findings -def validate_manifest(skill_path: Path) -> tuple[dict, list[dict]]: - """Validate bmad-manifest.json for agent requirements.""" - findings = [] - validation = { - 'found': False, - 'valid_json': False, - 'is_agent': False, - 'has_capabilities': False, - 'capability_count': 0, - 'menu_codes': [], - 'duplicate_menu_codes': [], - 'capability_issues': [], - } - - manifest_path = skill_path / 'bmad-manifest.json' - if not manifest_path.exists(): - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': 'bmad-manifest.json not found at skill root', - }) - return validation, findings - - validation['found'] = True - - try: - data = json.loads(manifest_path.read_text(encoding='utf-8')) - except json.JSONDecodeError as e: - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'critical', 'category': 'manifest', - 'issue': f'Invalid JSON in bmad-manifest.json: {e}', - }) - return validation, findings - - validation['valid_json'] = True - - # Check if this is an agent (agents have a persona field) - has_persona = 'persona' in data - validation['is_agent'] = has_persona - if not has_persona: - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': 'Missing "persona" field — agents are identified by having a persona field', - }) - - # Check capabilities - capabilities = data.get('capabilities') - if capabilities is None: - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': 'Missing "capabilities" field', - }) - return validation, findings - - if not isinstance(capabilities, list): - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': '"capabilities" is not an array', - }) - return validation, findings - - validation['has_capabilities'] = True - validation['capability_count'] = len(capabilities) - - # Check each capability for required fields and unique menu codes - required_fields = {'name', 'menu-code', 'description'} - menu_codes = [] - - for i, cap in enumerate(capabilities): - if not isinstance(cap, dict): - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': f'Capability at index {i} is not an object', - }) - continue - - missing = required_fields - set(cap.keys()) - if missing: - cap_name = cap.get('name', f'index-{i}') - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': f'Capability "{cap_name}" missing required fields: {", ".join(sorted(missing))}', - }) - - mc = cap.get('menu-code') - if mc: - menu_codes.append(mc) - - validation['menu_codes'] = menu_codes - - # Check for duplicate menu codes - seen = set() - dupes = set() - for mc in menu_codes: - if mc in seen: - dupes.add(mc) - seen.add(mc) - - if dupes: - validation['duplicate_menu_codes'] = sorted(dupes) - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'manifest', - 'issue': f'Duplicate menu codes: {", ".join(sorted(dupes))}', - }) - - return validation, findings - - -def cross_reference_capabilities(skill_path: Path) -> tuple[dict, list[dict]]: - """Cross-reference manifest capabilities with prompt files.""" - findings = [] - crossref = { - 'manifest_prompt_caps': [], - 'missing_prompt_files': [], - 'orphaned_prompt_files': [], - } - - manifest_path = skill_path / 'bmad-manifest.json' - - if not manifest_path.exists(): - return crossref, findings - - try: - data = json.loads(manifest_path.read_text(encoding='utf-8')) - except (json.JSONDecodeError, OSError): - return crossref, findings - - capabilities = data.get('capabilities', []) - if not isinstance(capabilities, list): - return crossref, findings - - # Get prompt-type capabilities from manifest - prompt_cap_names = set() - for cap in capabilities: - if isinstance(cap, dict) and cap.get('type') == 'prompt': - name = cap.get('name') - if name: - prompt_cap_names.add(name) - crossref['manifest_prompt_caps'].append(name) - - # Get actual prompt files (at skill root, excluding SKILL.md and non-prompt files) - actual_prompts = set() - skip_files = {'SKILL.md', 'bmad-manifest.json', 'bmad-skill-manifest.yaml'} - for f in skill_path.iterdir(): - if f.is_file() and f.suffix == '.md' and f.name not in skip_files: - actual_prompts.add(f.stem) - - # Missing prompt files (in manifest but no file) - missing = prompt_cap_names - actual_prompts - for name in sorted(missing): - crossref['missing_prompt_files'].append(name) - findings.append({ - 'file': 'bmad-manifest.json', 'line': 0, - 'severity': 'high', 'category': 'capability-crossref', - 'issue': f'Prompt capability "{name}" has no matching file {name}.md at skill root', - }) - - # Orphaned prompt files (file exists but not in manifest) - orphaned = actual_prompts - prompt_cap_names - for name in sorted(orphaned): - crossref['orphaned_prompt_files'].append(name) - findings.append({ - 'file': f'{name}.md', 'line': 0, - 'severity': 'medium', 'category': 'capability-crossref', - 'issue': f'Prompt file {name}.md not referenced as a prompt capability in manifest', - }) - - return crossref, findings - - def extract_memory_paths(skill_path: Path) -> tuple[list[str], list[dict]]: """Extract all memory path references across files and check consistency.""" findings = [] @@ -449,7 +270,7 @@ def check_prompt_basics(skill_path: Path) -> tuple[list[dict], list[dict]]: """Check each prompt file for config header and progression conditions.""" findings = [] prompt_details = [] - skip_files = {'SKILL.md', 'bmad-manifest.json', 'bmad-skill-manifest.yaml'} + skip_files = {'SKILL.md'} prompt_files = [f for f in sorted(skill_path.iterdir()) if f.is_file() and f.suffix == '.md' and f.name not in skip_files] @@ -545,15 +366,6 @@ def scan_structure_capabilities(skill_path: Path) -> dict: 'issue': message, }) - # Manifest validation - manifest_validation, manifest_findings = validate_manifest(skill_path) - all_findings.extend(manifest_findings) - has_manifest = manifest_validation['found'] - - # Capability cross-reference - capability_crossref, crossref_findings = cross_reference_capabilities(skill_path) - all_findings.extend(crossref_findings) - # Memory path consistency memory_paths, memory_findings = extract_memory_paths(skill_path) all_findings.extend(memory_findings) @@ -585,9 +397,6 @@ def scan_structure_capabilities(skill_path: Path) -> dict: 'metadata': { 'frontmatter': frontmatter, 'sections': sections, - 'has_manifest': has_manifest, - 'manifest_validation': manifest_validation, - 'capability_crossref': capability_crossref, }, 'prompt_details': prompt_details, 'memory_paths': memory_paths, diff --git a/src/skills/bmad-workflow-builder/bmad-manifest.json b/src/skills/bmad-workflow-builder/bmad-manifest.json deleted file mode 100644 index f5c7fa9..0000000 --- a/src/skills/bmad-workflow-builder/bmad-manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "module-code": "bmb", - "capabilities": [ - { - "name": "build", - "menu-code": "BP", - "description": "Build, edit, or convert workflows and skills through six-phase conversational discovery. Covers new skills, format conversion, edits, and fixes.", - "supports-headless": true, - "prompt": "build-process.md", - "phase-name": "anytime", - "output-location": "{bmad_builder_output_folder}" - }, - { - "name": "quality-optimize", - "menu-code": "QO", - "description": "Comprehensive validation and optimization using lint scripts and LLM scanner subagents. Structure, prompt craft, efficiency, and more.", - "supports-headless": true, - "prompt": "quality-optimizer.md", - "phase-name": "anytime", - "output-location": "{bmad_builder_reports}" - } - ] -} diff --git a/src/skills/bmad-workflow-builder/bmad-skill-manifest.yaml b/src/skills/bmad-workflow-builder/bmad-skill-manifest.yaml deleted file mode 100644 index d0f08ab..0000000 --- a/src/skills/bmad-workflow-builder/bmad-skill-manifest.yaml +++ /dev/null @@ -1 +0,0 @@ -type: skill diff --git a/src/skills/bmad-workflow-builder/build-process.md b/src/skills/bmad-workflow-builder/build-process.md index 9c5b354..bae7dfc 100644 --- a/src/skills/bmad-workflow-builder/build-process.md +++ b/src/skills/bmad-workflow-builder/build-process.md @@ -69,7 +69,7 @@ Work through conversationally, adapted per skill type, so you can either glean f - File/directory structure checks (existence, naming conventions, required files) - Pattern matching against known standards (path conventions, naming rules) - Comparison operations (diff, version compare, before/after, cross-reference checking) - - Dependency graphing (parsing imports, references, manifest entries) + - Dependency graphing (parsing imports, references, skill entries) - Template artifact detection (orphaned placeholders, unresolved variables) - Pre-processing for LLM steps (extract compact metrics from large files so the LLM works from structured data, not raw content) - Post-processing validation (verify LLM output conforms to expected schema/structure) @@ -123,7 +123,6 @@ Once you have a cohesive idea, think one level deeper, clarify with the user any **Load based on skill type:** - **If Complex Workflow:** Load `references/complex-workflow-patterns.md` — compaction survival, document-as-cache pattern, config integration, facilitator model, progressive disclosure with prompt files at root. This is essential for building workflows that survive long-running sessions. -- **If module-based (any type):** Load `references/metadata-reference.md` — bmad-manifest.json field definitions, module metadata structure, config loading requirements. - **Always load** `references/script-opportunities-reference.md` — script opportunity spotting guide, catalog, and output standards. Use this to identify additional script opportunities not caught in Phase 3, even if no scripts were initially planned. When confirmed: @@ -140,7 +139,6 @@ When confirmed: ``` {skill-name}/ ├── SKILL.md # name (same as folder name), description -├── bmad-manifest.json # Capabilities, module integration, optional persona/memory ├── *.md # Prompt files and subagent definitions at root ├── references/ # Reference data, schemas, guides (read for context) ├── assets/ # Templates, starter files (copied/transformed into output) @@ -158,32 +156,9 @@ When confirmed: Only create subfolders that are needed — most skills won't need all four. -5. **Generate bmad-manifest.json** — Use `scripts/manifest.py` (validation is automatic on every write). **IMPORTANT:** The generated manifest must NOT include a `$schema` field — the schema is used for validation tooling only and is not part of the delivered skill. - ```bash - # Create manifest - python3 scripts/manifest.py create {skill-path} \ - --module-code {code} # if part of a module \ - --has-memory # if state persists across sessions - - # Add each capability (even single-purpose skills get one) - # NOTE: capability description must be VERY short — what it produces, not how it works - python3 scripts/manifest.py add-capability {skill-path} \ - --name {name} --menu-code {MC} --description "Short: what it produces." \ - --supports-autonomous \ - --prompt {name}.md # internal capability - # OR --skill-name {skill} # external skill - # omit both if SKILL.md handles it directly - # Module capabilities also need: - --phase-name {phase} # which module phase - --after skill-a skill-b # skills that should run before this - --before skill-c skill-d # skills this should run before - --is-required # if must complete before 'before' skills proceed - --output-location "{var}" # where output goes - ``` - -6. Output to {`bmad_builder_output_folder`} +5. Output to {`bmad_builder_output_folder`} -7. **Lint gate** — run deterministic validation scripts: +6. **Lint gate** — run deterministic validation scripts: ```bash # Run both in parallel — they are independent python3 scripts/scan-path-standards.py {skill-path} diff --git a/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md b/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md index f7ced0e..4ae1c53 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md +++ b/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md @@ -10,7 +10,7 @@ This is a unified scan covering both *how work is distributed* (subagent delegat ## Your Role -Read the skill's SKILL.md, all prompt files, and manifest (if present). Identify inefficient execution patterns, missed parallelization opportunities, context bloat risks, and dependency issues. Return findings as structured JSON with specific alternatives and savings estimates. +Read the skill's SKILL.md and all prompt files. Identify inefficient execution patterns, missed parallelization opportunities, context bloat risks, and dependency issues. Return findings as structured JSON with specific alternatives and savings estimates. ## Scan Targets @@ -18,7 +18,6 @@ Find and read: - `SKILL.md` — On Activation patterns, operation flow - `*.md` prompt files at root — Each prompt for execution patterns - `references/*.md` — Resource loading patterns -- `bmad-manifest.json` — Stage ordering, dependencies --- @@ -265,7 +264,7 @@ Both issues and opportunities go into a single `findings[]` array. "category": "parallelization", "title": "Stages 2 and 3 could run in parallel", "detail": "Stages 2 (validate inputs) and 3 (scan resources) have no data dependency. Running in parallel would save ~1 round-trip.", - "action": "Mark stages 2 and 3 as parallel-eligible in the manifest dependency graph." + "action": "Mark stages 2 and 3 as parallel-eligible in the dependency graph." } ], "summary": { @@ -280,7 +279,7 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md, bmad-manifest.json (if present), and all prompt files at skill root — in a single parallel batch +1. **Parallel read batch:** Read SKILL.md and all prompt files at skill root — in a single parallel batch 2. Check On Activation and operation flow patterns from SKILL.md 3. Check each prompt file for execution patterns 4. Check resource loading patterns in references/ (read as needed) @@ -298,7 +297,7 @@ Before writing output, verify: Is your array called `findings`? Does every item **Before finalizing, think one level deeper and verify completeness and quality:** ### Scan Completeness -- Did I read SKILL.md, bmad-manifest.json (if present), and EVERY prompt file? +- Did I read SKILL.md and EVERY prompt file? - Did I identify ALL sequential independent operations? - Did I check for parent-reading-then-delegating patterns? - Did I verify subagent output specifications? diff --git a/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md b/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md index 310e769..d49a05b 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md +++ b/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md @@ -84,7 +84,6 @@ LLM instructions that compare two things for differences or verify consistency b **Signal phrases:** "compare", "diff", "match against", "cross-reference", "verify consistency", "check alignment" **Examples:** -- Comparing manifest entries against actual files → Python script - Diffing two versions of a document → git diff or Python difflib - Cross-referencing prompt names against SKILL.md references → Python script - Checking config variables are defined where used → Python regex scan @@ -105,7 +104,7 @@ LLM instructions that trace references, imports, or relationships between files. **Signal phrases:** "dependency", "references", "imports", "relationship", "graph", "trace" **Examples:** -- Building skill dependency graph from manifest → Python script +- Building skill dependency graph → Python script - Tracing which resources are loaded by which prompts → Python regex - Detecting circular references → Python graph algorithm @@ -128,7 +127,6 @@ Operations where a script could verify that LLM-generated output meets structura **Examples:** - Validating generated JSON against schema → Python jsonschema - Checking generated markdown has required sections → Python script -- Verifying generated manifest has required fields → Python script --- diff --git a/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md b/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md index 4231c5d..01fef88 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md +++ b/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md @@ -21,7 +21,6 @@ This is an **opinionated, advisory scan**. Findings are suggestions, not errors. Find and read: - `SKILL.md` — Identity, purpose, role guidance, description -- `bmad-manifest.json` — All capabilities with dependencies and metadata - `*.md` prompt files at root — What each stage prompt actually does - `references/*.md` — Supporting resources and patterns - Look for references to external skills in prompts and SKILL.md @@ -211,7 +210,7 @@ All go into a single `findings[]` array. "action": "" }, { - "file": "bmad-manifest.json", + "file": "SKILL.md", "severity": "suggestion", "category": "opportunity", "title": "Consolidate stages 02 and 03 into a single analysis stage", @@ -300,7 +299,7 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md, bmad-manifest.json, all prompt files, and list resources/ — in a single parallel batch +1. **Parallel read batch:** Read SKILL.md, all prompt files, and list resources/ — in a single parallel batch 2. Build mental model of the skill as a whole from all files read 3. Evaluate cohesion across all dimensions (flow, purpose, complexity, completeness, redundancy, dependencies, creates alignment, external integration, journey) 4. Generate findings with specific, actionable suggestions @@ -313,7 +312,7 @@ Before writing output, verify: Is your array called `findings`? Does every item **Before finalizing, think one level deeper and verify completeness and quality:** ### Scan Completeness -- Did I read SKILL.md, bmad-manifest.json, and ALL prompts? +- Did I read SKILL.md and ALL prompts? - Did I build a complete mental model of the skill? - Did I evaluate ALL cohesion dimensions (flow, purpose, complexity, completeness, redundancy, dependencies, output-location, external, journey)? - Did I check output-location alignment with actual stage outputs? diff --git a/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md b/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md index c42f7dc..a69952f 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md +++ b/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md @@ -4,20 +4,19 @@ You are **WorkflowIntegrityBot**, a quality engineer who validates that a skill ## Overview -You validate structural completeness and correctness across the entire skill: SKILL.md, stage prompts, manifest, and their interconnections. **Why this matters:** Structure is what the AI reads first — frontmatter determines whether the skill triggers, sections establish the mental model, stage files are the executable units, and broken references cause runtime failures. A structurally sound skill is one where the blueprint (SKILL.md) and the implementation (prompt files, references/, manifest) are aligned and complete. +You validate structural completeness and correctness across the entire skill: SKILL.md, stage prompts, and their interconnections. **Why this matters:** Structure is what the AI reads first — frontmatter determines whether the skill triggers, sections establish the mental model, stage files are the executable units, and broken references cause runtime failures. A structurally sound skill is one where the blueprint (SKILL.md) and the implementation (prompt files, references/) are aligned and complete. -This is a single unified scan that checks both the skill's skeleton (SKILL.md structure) and its organs (stage files, progression, config, manifest). Checking these together lets you catch mismatches that separate scans would miss — like a SKILL.md claiming complex workflow with routing but having no stage files, or stage files that exist but aren't referenced. +This is a single unified scan that checks both the skill's skeleton (SKILL.md structure) and its organs (stage files, progression, config). Checking these together lets you catch mismatches that separate scans would miss — like a SKILL.md claiming complex workflow with routing but having no stage files, or stage files that exist but aren't referenced. ## Your Role -Read the skill's SKILL.md, all stage prompts, and manifest (if present). Verify structural completeness, naming conventions, logical consistency, and type-appropriate requirements. Return findings as structured JSON. +Read the skill's SKILL.md and all stage prompts. Verify structural completeness, naming conventions, logical consistency, and type-appropriate requirements. Return findings as structured JSON. ## Scan Targets Find and read: - `SKILL.md` — Primary structure and blueprint - `*.md` prompt files at root — Stage prompt files (if complex workflow) -- `bmad-manifest.json` — Module manifest (if present) --- @@ -114,14 +113,6 @@ Determine workflow type from SKILL.md before applying type-specific checks: | Final stage has completion/output criteria | Workflow needs a defined end state | | No circular stage references without exit conditions | Infinite loops break workflow execution | -#### Manifest (If Module-Based) - -| Check | Why It Matters | -|-------|----------------| -| `bmad-manifest.json` exists if SKILL.md references modules | Missing manifest means module loading fails | -| Manifest lists all stage prompts | Incomplete manifest means stages can't be discovered | -| Manifest stage names match actual filenames | Mismatches cause load failures | - #### Config Headers in Stage Prompts | Check | Why It Matters | @@ -238,7 +229,7 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md, bmad-manifest.json (if present), and list all `.md` files at skill root — in a single parallel batch +1. **Parallel read batch:** Read SKILL.md and list all `.md` files at skill root — in a single parallel batch 2. Validate frontmatter, sections, language, template artifacts from SKILL.md 3. Determine workflow type (complex, simple workflow, simple utility) 4. For complex workflows: **parallel read batch** — read all stage prompt files identified in step 1 diff --git a/src/skills/bmad-workflow-builder/references/classification-reference.md b/src/skills/bmad-workflow-builder/references/classification-reference.md index 70a520c..b8d2c56 100644 --- a/src/skills/bmad-workflow-builder/references/classification-reference.md +++ b/src/skills/bmad-workflow-builder/references/classification-reference.md @@ -33,7 +33,7 @@ Classify the skill type based on user requirements. This table is for internal u - Other skills/workflows call it - Deterministic or near-deterministic behavior - Could be a script but needs LLM judgment -- Examples: JSON validator, manifest checker, format converter +- Examples: JSON validator, schema checker, format converter ### Simple Workflow Signals - 3-8 numbered steps @@ -55,7 +55,7 @@ Classify the skill type based on user requirements. This table is for internal u ## Module Context (Orthogonal) Module context is asked for ALL types: -- **Module-based:** Part of a BMad module. Uses `bmad-{modulecode}-{skillname}` naming. Has bmad-manifest.json. +- **Module-based:** Part of a BMad module. Uses `bmad-{modulecode}-{skillname}` naming. - **Standalone:** Independent skill. Uses `bmad-{skillname}` naming. All workflows use `bmad-init` by default unless explicitly opted out (truly standalone utilities). diff --git a/src/skills/bmad-workflow-builder/references/metadata-reference.md b/src/skills/bmad-workflow-builder/references/metadata-reference.md deleted file mode 100644 index df2ac60..0000000 --- a/src/skills/bmad-workflow-builder/references/metadata-reference.md +++ /dev/null @@ -1,126 +0,0 @@ -# Manifest Reference - -Every BMad skill has a `bmad-manifest.json` at its root. This is the unified format for agents, workflows, and simple skills. - -## File Location - -``` -{skillname}/ -├── SKILL.md # name, description, workflow content -├── bmad-manifest.json # Capabilities, module integration -└── ... -``` - -## SKILL.md Frontmatter (Minimal) - -```yaml ---- -name: bmad-{modulecode}-{skillname} -description: [5-8 word summary]. [Use when user says 'X' or 'Y'.] ---- -``` - -## bmad-manifest.json - -**NOTE:** Do NOT include `$schema` in generated manifests. The schema is used by validation tooling only — it is not part of the delivered skill. - -```json -{ - "module-code": "bmb", - "replaces-skill": "bmad-original-skill", - "has-memory": true, - "capabilities": [ - { - "name": "build", - "menu-code": "BP", - "description": "Builds skills through conversational discovery. Outputs to skill folder.", - "supports-headless": true, - "prompt": "build-process.md", - "phase-name": "design", - "after": ["create-requirements"], - "before": ["quality-optimize"], - "is-required": true, - "output-location": "{bmad_builder_output_folder}" - }, - { - "name": "validate", - "menu-code": "VL", - "description": "Runs validation checks and produces quality report.", - "supports-headless": true - } - ] -} -``` - -## Field Reference - -### Top-Level Fields - -| Field | Type | Required | Purpose | -|-------|------|----------|---------| -| `module-code` | string | If module | Short code for namespacing (e.g., `bmb`, `cis`) | -| `replaces-skill` | string | No | Registered skill name this replaces. Inherits metadata during bmad-init. | -| `persona` | string | Agents only | Succinct distillation of the agent's essence. **Presence = this is an agent.** | -| `has-memory` | boolean | No | Whether state persists across sessions via sidecar memory | - -### Capability Fields - -| Field | Type | Required | Purpose | -|-------|------|----------|---------| -| `name` | string | Yes | Kebab-case identifier | -| `menu-code` | string | Yes | 2-3 uppercase letter shortcut for menus | -| `description` | string | Yes | What it does and when to suggest it | -| `supports-autonomous` | boolean | No | Can run without user interaction | -| `prompt` | string | No | Relative path to prompt file (internal capability) | -| `skill-name` | string | No | Registered name of external skill (external capability) | -| `phase-name` | string | No | Module phase this belongs to | -| `after` | array | No | Skill names that should run before this capability | -| `before` | array | No | Skill names this capability should run before | -| `is-required` | boolean | No | If true, skills in `before` are blocked until this completes | -| `output-location` | string | No | Where output goes (may use config variables) | - -### Three Capability Flavors - -1. **Has `prompt`** — internal capability routed to a prompt file -2. **Has `skill-name`** — delegates to another registered skill -3. **Has neither** — SKILL.md handles it directly - -### The `replaces-skill` Field - -When set, the skill inherits metadata from the replaced skill during `bmad-init`. Explicit fields in the new manifest override inherited values. - -## Agent vs Workflow vs Skill - -No type field needed — inferred from content: -- **Has `persona`** → agent -- **No `persona`** → workflow or skill (distinction is complexity, not manifest structure) - -## Config Loading - -All module skills MUST use the `bmad-init` skill at startup. - -See `references/complex-workflow-patterns.md` for the config loading pattern. - -## Path Construction Rules — CRITICAL - -Only use `{project-root}` for `_bmad` paths. - -**Three path types:** -- **Skill-internal** — bare relative paths (no prefix) -- **Project `_bmad` paths** — always `{project-root}/_bmad/...` -- **Config variables** — used directly, already contain `{project-root}` in their resolved values - -**Correct:** -``` -references/reference.md # Skill-internal (bare relative) -stage-one.md # Skill-internal (prompt at root) -{project-root}/_bmad/planning/prd.md # Project _bmad path -{planning_artifacts}/prd.md # Config var (already has full path) -``` - -**Never use:** -``` -../../other-skill/file.md # Cross-skill relative path breaks with reorganization -{project-root}/{config_var}/output.md # Double-prefix -./references/reference.md # Relative prefix breaks context changes -``` diff --git a/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md b/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md index 73986fb..89346f5 100644 --- a/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md +++ b/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md @@ -42,7 +42,7 @@ When you see these verbs or patterns in a workflow's requirements, think scripts | count | Metric script | "Count tokens per file" | | extract | Data extraction | "Extract all config variable references" | | convert / transform | Transformation script | "Convert stage definitions to graph" | -| compare | Comparison script | "Compare prompt frontmatter vs manifest" | +| compare | Comparison script | "Compare prompt frontmatter vs SKILL.md references" | | scan for | Pattern scanning | "Scan for orphaned template artifacts" | | check structure | File structure checker | "Check skill directory has required files" | | against schema | Schema validation | "Validate output against JSON schema" | @@ -127,20 +127,18 @@ Each entry follows the format: What it does, Why it matters for workflows, What ### 3. Prompt Frontmatter Comparator -**What:** Compare prompt file frontmatter against the skill's `bmad-skill-manifest.yaml`. +**What:** Compare prompt file frontmatter against SKILL.md stage references. -**Why:** Capability misalignment between prompts and the manifest causes routing failures — the skill advertises a capability it can't deliver, or has a prompt that's never reachable. +**Why:** Misalignment between prompts and SKILL.md causes routing failures — the skill references a stage it can't deliver, or has a prompt that's never reachable. **Checks:** - Every prompt file at root has frontmatter with `name`, `description`, `menu-code` -- Prompt `name` matches manifest capability name -- `menu-code` matches manifest entry (case-insensitive) -- Every manifest capability with `type: "prompt"` has a corresponding file -- Flag orphaned prompts not listed in manifest +- Every stage referenced in SKILL.md has a corresponding prompt file +- Flag orphaned prompts not referenced in SKILL.md **Output:** JSON with mismatches, missing files, orphaned prompts. -**Implementation:** Python, reads `bmad-skill-manifest.yaml` and all prompt `.md` files at skill root. +**Implementation:** Python, reads SKILL.md and all prompt `.md` files at skill root. --- @@ -168,13 +166,12 @@ Each entry follows the format: What it does, Why it matters for workflows, What **Why:** Understand the skill's dependency surface. Catch references to skills that don't exist or have been renamed. **Checks:** -- Parse `bmad-skill-manifest.yaml` for external skill references - Parse SKILL.md and prompts for skill invocation patterns (`invoke`, `load`, skill name references) - Build a dependency list with direction (this skill depends on X, Y depends on this skill) **Output:** JSON adjacency list or DOT format (GraphViz). Include whether each dependency is required or optional. -**Implementation:** Python, JSON/YAML parsing with regex for invocation pattern detection. +**Implementation:** Python with regex for invocation pattern detection. --- @@ -293,7 +290,6 @@ Each entry follows the format: What it does, Why it matters for workflows, What **Checks:** - Frontmatter changes -- Capability additions/removals in manifest - New or removed prompt files - Token count changes per file - Stage flow changes (for workflows) diff --git a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md index fb89b3f..2164061 100644 --- a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md @@ -79,7 +79,6 @@ Replace all content placeholders with skill-specific values: ## Path References All generated skills use these paths: -- `bmad-manifest.json` — Module metadata (if module-based) - `references/{reference}.md` — Reference documents loaded on demand - `01-{stage}.md` — Numbered stage prompts at skill root (complex workflows) - `scripts/` — Python/shell scripts for deterministic operations (if needed) diff --git a/src/skills/bmad-workflow-builder/references/universal-scan-schema.md b/src/skills/bmad-workflow-builder/references/universal-scan-schema.md index 11e6df8..d49bfcc 100644 --- a/src/skills/bmad-workflow-builder/references/universal-scan-schema.md +++ b/src/skills/bmad-workflow-builder/references/universal-scan-schema.md @@ -206,9 +206,9 @@ Array of strings (script paths that already exist). "line": 12, "severity": "high", "category": "frontmatter", - "title": "Missing required 'version' field in frontmatter", - "detail": "The SKILL.md frontmatter is missing the version field. This prevents the manifest generator from producing correct output and breaks version-aware consumers.", - "action": "Add 'version: 1.0.0' to the YAML frontmatter block" + "title": "Missing 'description' field in frontmatter", + "detail": "The SKILL.md frontmatter is missing the description field. Without a description, the skill cannot be triggered reliably by the help system.", + "action": "Add a description with trigger phrases to the YAML frontmatter block" }, { "file": "build-process.md", diff --git a/src/skills/bmad-workflow-builder/scripts/bmad-manifest-schema.json b/src/skills/bmad-workflow-builder/scripts/bmad-manifest-schema.json deleted file mode 100644 index ea674b5..0000000 --- a/src/skills/bmad-workflow-builder/scripts/bmad-manifest-schema.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "BMad Manifest Schema", - "description": "Unified schema for all BMad skill manifest files (agents, workflows, skills)", - - "type": "object", - - "properties": { - "$schema": { - "description": "JSON Schema identifier", - "type": "string" - }, - - "module-code": { - "description": "Short code for the module this skill belongs to (e.g., bmb, cis). Omit for standalone skills.", - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - - "replaces-skill": { - "description": "Registered name of the BMad skill this replaces. Inherits metadata during bmad-init.", - "type": "string", - "minLength": 1 - }, - - "persona": { - "description": "Succinct distillation of the agent's essence — who they are, how they operate, what drives them. Presence of this field indicates the skill is an agent. Useful for other skills/agents to understand who they're interacting with.", - "type": "string", - "minLength": 1 - }, - - "has-memory": { - "description": "Whether this skill persists state across sessions via sidecar memory.", - "type": "boolean" - }, - - "capabilities": { - "description": "What this skill can do. Every skill has at least one capability.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "name": { - "description": "Capability identifier (kebab-case)", - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "menu-code": { - "description": "2-3 uppercase letter shortcut for interactive menus", - "type": "string", - "pattern": "^[A-Z]{2,3}$" - }, - "description": { - "description": "What this capability does and when to suggest it", - "type": "string" - }, - "supports-headless": { - "description": "Whether this capability can run without user interaction", - "type": "boolean" - }, - - "prompt": { - "description": "Relative path to the prompt file for internal capabilities (e.g., build-process.md). Omit if handled by SKILL.md directly or if this is an external skill call.", - "type": "string" - }, - "skill-name": { - "description": "Registered name of an external skill this capability delegates to. Omit for internal capabilities.", - "type": "string" - }, - - "phase-name": { - "description": "Which module phase this capability belongs to (e.g., planning, design, anytime). For module sequencing.", - "type": "string" - }, - "after": { - "description": "Skill names that should ideally run before this capability. If is-required is true on those skills, they block this one.", - "type": "array", - "items": { "type": "string" } - }, - "before": { - "description": "Skill names that this capability should ideally run before. Helps the module sequencer understand ordering.", - "type": "array", - "items": { "type": "string" } - }, - "is-required": { - "description": "Whether this capability must complete before skills listed in its 'before' array can proceed.", - "type": "boolean" - }, - "output-location": { - "description": "Where this capability writes its output. May contain config variables (e.g., {bmad_builder_output_folder}/agents/).", - "type": "string" - } - }, - "required": ["name", "menu-code", "description"], - "additionalProperties": false - } - } - }, - - "required": ["capabilities"], - "additionalProperties": false -} diff --git a/src/skills/bmad-workflow-builder/scripts/manifest.py b/src/skills/bmad-workflow-builder/scripts/manifest.py deleted file mode 100644 index 30c3093..0000000 --- a/src/skills/bmad-workflow-builder/scripts/manifest.py +++ /dev/null @@ -1,420 +0,0 @@ -#!/usr/bin/env python3 -"""BMad manifest CRUD and validation. - -All manifest operations go through this script. Validation runs automatically -on every write. Prompts call this instead of touching JSON directly. - -Usage: - python3 scripts/manifest.py create [options] - python3 scripts/manifest.py add-capability [options] - python3 scripts/manifest.py update --set key=value [...] - python3 scripts/manifest.py remove-capability --name - python3 scripts/manifest.py read [--capabilities|--capability ] - python3 scripts/manifest.py validate -""" - -# /// script -# requires-python = ">=3.9" -# dependencies = [ -# "jsonschema>=4.0.0", -# ] -# /// - -from __future__ import annotations - -import argparse -import json -import sys -from pathlib import Path -from typing import Any - -try: - from jsonschema import Draft7Validator -except ImportError: - print("Error: jsonschema required. Run with: uv run scripts/manifest.py (PEP 723 handles deps)", file=sys.stderr) - sys.exit(2) - -MANIFEST_FILENAME = "bmad-manifest.json" -SCHEMA_FILENAME = "bmad-manifest-schema.json" - - -def get_schema_path() -> Path: - """Schema is co-located with this script.""" - return Path(__file__).parent / SCHEMA_FILENAME - - -def get_manifest_path(skill_path: Path) -> Path: - return skill_path / MANIFEST_FILENAME - - -def load_schema() -> dict[str, Any]: - path = get_schema_path() - if not path.exists(): - print(f"Error: Schema not found: {path}", file=sys.stderr) - sys.exit(2) - with path.open() as f: - return json.load(f) - - -def load_manifest(skill_path: Path) -> dict[str, Any]: - path = get_manifest_path(skill_path) - if not path.exists(): - return {} - with path.open() as f: - try: - return json.load(f) - except json.JSONDecodeError as e: - print(f"Error: Invalid JSON in {path}: {e}", file=sys.stderr) - sys.exit(2) - - -def save_manifest(skill_path: Path, data: dict[str, Any]) -> bool: - """Save manifest after validation. Returns True if valid and saved.""" - errors = validate(data) - if errors: - print(f"Validation failed with {len(errors)} error(s):", file=sys.stderr) - for err in errors: - print(f" [{err['path']}] {err['message']}", file=sys.stderr) - return False - - path = get_manifest_path(skill_path) - path.parent.mkdir(parents=True, exist_ok=True) - with path.open("w") as f: - json.dump(data, f, indent=2) - f.write("\n") - return True - - -def validate(data: dict[str, Any]) -> list[dict[str, Any]]: - """Validate manifest against schema. Returns list of errors.""" - schema = load_schema() - validator = Draft7Validator(schema) - errors = [] - for error in validator.iter_errors(data): - errors.append({ - "path": ".".join(str(p) for p in error.path) if error.path else "root", - "message": error.message, - }) - return errors - - -def validate_extras(data: dict[str, Any]) -> list[str]: - """Additional checks beyond schema validation.""" - warnings = [] - capabilities = data.get("capabilities", []) - - if not capabilities: - warnings.append("No capabilities defined — every skill needs at least one") - return warnings - - menu_codes: dict[str, str] = {} - for i, cap in enumerate(capabilities): - name = cap.get("name", f"") - - # Duplicate menu-code check - mc = cap.get("menu-code", "") - if mc and mc in menu_codes: - warnings.append(f"Duplicate menu-code '{mc}' in '{menu_codes[mc]}' and '{name}'") - elif mc: - menu_codes[mc] = name - - # Both prompt and skill-name - if "prompt" in cap and "skill-name" in cap: - warnings.append(f"Capability '{name}' has both 'prompt' and 'skill-name' — pick one") - - return warnings - - -# --- Commands --- - -def cmd_create(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - existing = load_manifest(skill_path) - if existing: - print(f"Error: Manifest already exists at {get_manifest_path(skill_path)}", file=sys.stderr) - print("Use 'update' to modify or delete the file first.", file=sys.stderr) - return 1 - - data: dict[str, Any] = {} - - if args.module_code: - data["module-code"] = args.module_code - if args.replaces_skill: - data["replaces-skill"] = args.replaces_skill - if args.persona: - data["persona"] = args.persona - if args.has_memory: - data["has-memory"] = True - - data["capabilities"] = [] - - if save_manifest(skill_path, data): - print(f"Created {get_manifest_path(skill_path)}") - return 0 - return 1 - - -def cmd_add_capability(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found. Run 'create' first.", file=sys.stderr) - return 1 - - capabilities = data.setdefault("capabilities", []) - - # Check for duplicate name - for cap in capabilities: - if cap.get("name") == args.name: - print(f"Error: Capability '{args.name}' already exists. Use 'update' to modify.", file=sys.stderr) - return 1 - - cap: dict[str, Any] = { - "name": args.name, - "menu-code": args.menu_code, - "description": args.description, - } - - if args.supports_autonomous: - cap["supports-headless"] = True - if args.prompt: - cap["prompt"] = args.prompt - if args.skill_name: - cap["skill-name"] = args.skill_name - if args.phase_name: - cap["phase-name"] = args.phase_name - if args.after: - cap["after"] = args.after - if args.before: - cap["before"] = args.before - if args.is_required: - cap["is-required"] = True - if args.output_location: - cap["output-location"] = args.output_location - - capabilities.append(cap) - - if save_manifest(skill_path, data): - print(f"Added capability '{args.name}' [{args.menu_code}]") - return 0 - return 1 - - -def cmd_update(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found. Run 'create' first.", file=sys.stderr) - return 1 - - # Parse --set key=value pairs - for pair in args.set: - if "=" not in pair: - print(f"Error: Invalid --set format '{pair}'. Use key=value.", file=sys.stderr) - return 1 - key, value = pair.split("=", 1) - - # Handle boolean values - if value.lower() == "true": - value = True - elif value.lower() == "false": - value = False - - # Handle capability updates: capability.name.field=value - if key.startswith("capability."): - parts = key.split(".", 2) - if len(parts) != 3: - print("Error: Capability update format: capability..=", file=sys.stderr) - return 1 - cap_name, field = parts[1], parts[2] - found = False - for cap in data.get("capabilities", []): - if cap.get("name") == cap_name: - cap[field] = value - found = True - break - if not found: - print(f"Error: Capability '{cap_name}' not found.", file=sys.stderr) - return 1 - else: - # Handle removing fields with empty value - if value == "": - data.pop(key, None) - else: - data[key] = value - - if save_manifest(skill_path, data): - print(f"Updated {get_manifest_path(skill_path)}") - return 0 - return 1 - - -def cmd_remove_capability(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found.", file=sys.stderr) - return 1 - - capabilities = data.get("capabilities", []) - original_len = len(capabilities) - data["capabilities"] = [c for c in capabilities if c.get("name") != args.name] - - if len(data["capabilities"]) == original_len: - print(f"Error: Capability '{args.name}' not found.", file=sys.stderr) - return 1 - - if save_manifest(skill_path, data): - print(f"Removed capability '{args.name}'") - return 0 - return 1 - - -def cmd_read(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found.", file=sys.stderr) - return 1 - - if args.capabilities: - caps = data.get("capabilities", []) - if args.json: - print(json.dumps(caps, indent=2)) - else: - for cap in caps: - prompt_or_skill = cap.get("prompt", cap.get("skill-name", "(SKILL.md)")) - auto = " [autonomous]" if cap.get("supports-headless") else "" - print(f" [{cap.get('menu-code', '??')}] {cap['name']} — {cap.get('description', '')}{auto}") - print(f" → {prompt_or_skill}") - return 0 - - if args.capability: - for cap in data.get("capabilities", []): - if cap.get("name") == args.capability: - print(json.dumps(cap, indent=2)) - return 0 - print(f"Error: Capability '{args.capability}' not found.", file=sys.stderr) - return 1 - - if args.json: - print(json.dumps(data, indent=2)) - else: - # Summary view - is_agent = "persona" in data - print(f"Type: {'Agent' if is_agent else 'Workflow/Skill'}") - if data.get("module-code"): - print(f"Module: {data['module-code']}") - if is_agent: - print(f"Persona: {data['persona'][:80]}...") - if data.get("has-memory"): - print("Memory: enabled") - caps = data.get("capabilities", []) - print(f"Capabilities: {len(caps)}") - for cap in caps: - prompt_or_skill = cap.get("prompt", cap.get("skill-name", "(SKILL.md)")) - auto = " [autonomous]" if cap.get("supports-headless") else "" - print(f" [{cap.get('menu-code', '??')}] {cap['name']}{auto} → {prompt_or_skill}") - return 0 - - -def cmd_validate(args: argparse.Namespace) -> int: - skill_path = Path(args.skill_path).resolve() - data = load_manifest(skill_path) - if not data: - print("Error: No manifest found.", file=sys.stderr) - return 1 - - errors = validate(data) - warnings = validate_extras(data) - - if args.json: - print(json.dumps({ - "valid": len(errors) == 0, - "errors": errors, - "warnings": warnings, - }, indent=2)) - else: - if not errors: - print("✓ Manifest is valid") - else: - print(f"✗ {len(errors)} error(s):", file=sys.stderr) - for err in errors: - print(f" [{err['path']}] {err['message']}", file=sys.stderr) - - if warnings: - print(f"\n⚠ {len(warnings)} warning(s):", file=sys.stderr) - for w in warnings: - print(f" {w}", file=sys.stderr) - - return 0 if not errors else 1 - - -def main() -> int: - parser = argparse.ArgumentParser( - description="BMad manifest CRUD and validation", - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - sub = parser.add_subparsers(dest="command", required=True) - - # create - p_create = sub.add_parser("create", help="Create a new manifest") - p_create.add_argument("skill_path", type=str, help="Path to skill directory") - p_create.add_argument("--module-code", type=str) - p_create.add_argument("--replaces-skill", type=str) - p_create.add_argument("--persona", type=str) - p_create.add_argument("--has-memory", action="store_true") - - # add-capability - p_add = sub.add_parser("add-capability", help="Add a capability") - p_add.add_argument("skill_path", type=str, help="Path to skill directory") - p_add.add_argument("--name", required=True, type=str) - p_add.add_argument("--menu-code", required=True, type=str) - p_add.add_argument("--description", required=True, type=str) - p_add.add_argument("--supports-autonomous", action="store_true") - p_add.add_argument("--prompt", type=str, help="Relative path to prompt file") - p_add.add_argument("--skill-name", type=str, help="External skill name") - p_add.add_argument("--phase-name", type=str) - p_add.add_argument("--after", nargs="*", help="Skill names that should run before this") - p_add.add_argument("--before", nargs="*", help="Skill names this should run before") - p_add.add_argument("--is-required", action="store_true") - p_add.add_argument("--output-location", type=str) - - # update - p_update = sub.add_parser("update", help="Update manifest fields") - p_update.add_argument("skill_path", type=str, help="Path to skill directory") - p_update.add_argument("--set", nargs="+", required=True, help="key=value pairs") - - # remove-capability - p_remove = sub.add_parser("remove-capability", help="Remove a capability") - p_remove.add_argument("skill_path", type=str, help="Path to skill directory") - p_remove.add_argument("--name", required=True, type=str) - - # read - p_read = sub.add_parser("read", help="Read manifest") - p_read.add_argument("skill_path", type=str, help="Path to skill directory") - p_read.add_argument("--capabilities", action="store_true", help="List capabilities only") - p_read.add_argument("--capability", type=str, help="Show specific capability") - p_read.add_argument("--json", action="store_true", help="JSON output") - - # validate - p_validate = sub.add_parser("validate", help="Validate manifest") - p_validate.add_argument("skill_path", type=str, help="Path to skill directory") - p_validate.add_argument("--json", action="store_true", help="JSON output") - - args = parser.parse_args() - - commands = { - "create": cmd_create, - "add-capability": cmd_add_capability, - "update": cmd_update, - "remove-capability": cmd_remove_capability, - "read": cmd_read, - "validate": cmd_validate, - } - - return commands[args.command](args) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py b/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py index af6d14e..cf02282 100755 --- a/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py +++ b/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py @@ -5,7 +5,7 @@ so the LLM scanner can evaluate efficiency from compact structured data. Covers: -- Dependency graph from bmad-manifest.json (after, before arrays) +- Dependency graph from skill structure - Circular dependency detection - Transitive dependency redundancy - Parallelizable stage groups (independent nodes) @@ -160,37 +160,12 @@ def scan_sequential_patterns(filepath: Path, rel_path: str) -> list[dict]: def scan_execution_deps(skill_path: Path) -> dict: """Run all deterministic execution efficiency checks.""" - # Parse manifest for dependency graph + # Build dependency graph from skill structure dep_graph: dict[str, list[str]] = {} prefer_after: dict[str, list[str]] = {} all_stages: set[str] = set() - manifest_found = False - - for manifest_path in [ - skill_path / 'bmad-manifest.json', - ]: - if manifest_path.exists(): - manifest_found = True - try: - data = json.loads(manifest_path.read_text(encoding='utf-8')) - if isinstance(data, dict): - # Single manifest - name = data.get('name', manifest_path.stem) - all_stages.add(name) - # New unified format uses per-capability fields - caps = data.get('capabilities', []) - for cap in caps: - cap_name = cap.get('name', name) - # 'after' = hard/soft dependencies (things that should run before this) - dep_graph[cap_name] = cap.get('after', []) or [] - # 'before' = downstream consumers (things this should run before) - prefer_after[cap_name] = cap.get('before', []) or [] - all_stages.add(cap_name) - except json.JSONDecodeError: - pass - break - - # Also check for stage-level prompt files at skill root + + # Check for stage-level prompt files at skill root for f in sorted(skill_path.iterdir()): if f.is_file() and f.suffix == '.md' and f.name != 'SKILL.md': all_stages.add(f.stem) @@ -259,7 +234,6 @@ def scan_execution_deps(skill_path: Path) -> dict: 'timestamp': datetime.now(timezone.utc).isoformat(), 'status': status, 'dependency_graph': { - 'manifest_found': manifest_found, 'stages': sorted(all_stages), 'hard_dependencies': dep_graph, 'soft_dependencies': prefer_after, diff --git a/src/skills/bmad-workflow-builder/scripts/prepass-workflow-integrity.py b/src/skills/bmad-workflow-builder/scripts/prepass-workflow-integrity.py index e4b8767..1489931 100755 --- a/src/skills/bmad-workflow-builder/scripts/prepass-workflow-integrity.py +++ b/src/skills/bmad-workflow-builder/scripts/prepass-workflow-integrity.py @@ -408,10 +408,6 @@ def scan_workflow_integrity(skill_path: Path) -> dict: prompt_details, prompt_findings = check_prompt_basics(skill_path) all_findings.extend(prompt_findings) - # Manifest check - manifest_path = skill_path / 'bmad-manifest.json' - has_manifest = manifest_path.exists() - # Build severity summary by_severity = {'critical': 0, 'high': 0, 'medium': 0, 'low': 0} for f in all_findings: @@ -436,7 +432,6 @@ def scan_workflow_integrity(skill_path: Path) -> dict: 'frontmatter': frontmatter, 'sections': sections, 'workflow_type': workflow_type, - 'has_manifest': has_manifest, }, 'stage_summary': stage_summary, 'prompt_details': prompt_details, From 1725d70a4de42e3366f0951f1d1deddbbd458932 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 01:44:09 -0500 Subject: [PATCH 02/10] Add bmad-builder-config skill and remove all bmad-init references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New skill: bmad-builder-config - Simple workflow that installs/configures the bmb module into a project - Creates/updates shared {project-root}/_bmad/config.yaml with module settings - Merges module-help.csv entries with anti-zombie pattern (removes stale entries before writing fresh ones) - Two deterministic Python scripts: merge-config.py and merge-help-csv.py - 22 unit tests covering fresh install, update, anti-zombie, round-trip - Outcome-based SKILL.md (~39 lines) — scripts are self-documenting via --help - Both lint gates pass clean (path-standards, script-standards) Remove bmad-init from entire codebase (13 files): - bmad-init skill no longer exists; config is read directly from {project-root}/_bmad/config.yaml - Templates: removed {if-bmad-init}/{/if-bmad-init} conditional blocks, replaced with unconditional config loading from config.yaml - Template substitution rules: removed bmad-init conditional section - Classification reference: removed bmad-init mentions from type descriptions - Complex workflow patterns: replaced bmad-init invocation pattern with direct config file reading - Build process (both builders): removed bmad-init as default external skill, updated standalone field description - Quality scanners: replaced "bmad-init variables" with "config variables" in prompt-craft, workflow-integrity, and enhancement-opportunities scanners - Script opportunities reference: updated variable tracking to reference config.yaml directly - Standard fields: updated standalone field from "Opts out of bmad-init?" to "Fully standalone, no config needed?" Update module-help.csv: - New CSV column format (module,agent-name,skill-name,display-name,...) - Added bmad-builder-config entry (Configure Builder, CB) --- src/module-help.csv | 13 +- .../assets/SKILL-template.md | 2 +- .../bmad-agent-builder/build-process.md | 4 +- .../quality-scan-prompt-craft.md | 2 +- src/skills/bmad-builder-config/SKILL.md | 41 ++++ .../assets/module-help.csv | 6 + .../bmad-builder-config/assets/module.yaml | 20 ++ .../scripts/merge-config.py | 185 +++++++++++++++ .../scripts/merge-help-csv.py | 176 ++++++++++++++ .../scripts/tests/test-merge-config.py | 222 ++++++++++++++++++ .../scripts/tests/test-merge-help-csv.py | 187 +++++++++++++++ .../assets/SKILL-template.md | 8 +- .../bmad-workflow-builder/build-process.md | 4 +- .../quality-scan-enhancement-opportunities.md | 4 +- .../quality-scan-prompt-craft.md | 2 +- .../quality-scan-workflow-integrity.md | 6 +- .../references/classification-reference.md | 6 +- .../references/complex-workflow-patterns.md | 14 +- .../script-opportunities-reference.md | 2 +- .../references/standard-fields.md | 2 +- .../references/template-substitution-rules.md | 8 - .../scripts/prepass-execution-deps.py | 1 + 22 files changed, 870 insertions(+), 45 deletions(-) create mode 100644 src/skills/bmad-builder-config/SKILL.md create mode 100644 src/skills/bmad-builder-config/assets/module-help.csv create mode 100644 src/skills/bmad-builder-config/assets/module.yaml create mode 100755 src/skills/bmad-builder-config/scripts/merge-config.py create mode 100755 src/skills/bmad-builder-config/scripts/merge-help-csv.py create mode 100644 src/skills/bmad-builder-config/scripts/tests/test-merge-config.py create mode 100644 src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py diff --git a/src/module-help.csv b/src/module-help.csv index f621b52..7c233a4 100644 --- a/src/module-help.csv +++ b/src/module-help.csv @@ -1,7 +1,6 @@ -module,phase,name,code,sequence,workflow-file,command,required,agent,options,description,output-location,outputs, -bmb,anytime,Create Agent,CA,,skill:bmad-agent-builder,bmad-agent-builder,false,,Create Mode,"Build a new agent skill from scratch through guided conversational discovery.",output_folder,"agent skill", -bmb,anytime,Edit Agent,EA,,skill:bmad-agent-builder,bmad-agent-builder,false,,Edit Mode,"Edit, convert or fix an existing agent skill.",output_folder,"agent skill", -bmb,anytime,Optimize Agent,OA,,skill:bmad-agent-builder,bmad-agent-builder,false,,Validate Mode,"Quality check, validate, review or optimize an existing agent skill.",output_folder,"quality report", -bmb,anytime,Create Workflow,CW,,skill:bmad-workflow-builder,bmad-workflow-builder,false,,Create Mode,"Build a new workflow or skill from scratch through guided conversational discovery.",output_folder,"workflow or skill", -bmb,anytime,Edit Workflow,EW,,skill:bmad-workflow-builder,bmad-workflow-builder,false,,Edit Mode,"Edit, convert or fix an existing workflow or skill.",output_folder,"workflow or skill", -bmb,anytime,Optimize Workflow,OW,,skill:bmad-workflow-builder,bmad-workflow-builder,false,,Validate Mode,"Quality check, validate, review or optimize an existing workflow or skill.",output_folder,"quality report", +module,agent-name,skill-name,display-name,menu-code,capability,args,description,phase,after,before,required,output-location,outputs, +bmb,,bmad-builder-config,Configure Builder,CB,configure,,"Install and configure BMad Builder module into a project.",anytime,,,false,,config, +bmb,,bmad-agent-builder,Build Agent,BA,build-process,,"Create, edit, convert, or fix an agent skill.",anytime,,bmad-agent-builder:quality-optimizer,false,output_folder,agent skill, +bmb,,bmad-agent-builder,Optimize Agent,OA,quality-optimizer,,Validate and optimize an existing agent skill. Produces a quality report.,anytime,bmad-agent-builder:build-process,,false,bmad_builder_reports,quality report, +bmb,,bmad-workflow-builder,Build Workflow,BW,build-process,,"Create, edit, convert, or fix a workflow or utility skill.",anytime,,bmad-workflow-builder:quality-optimizer,false,output_folder,workflow skill, +bmb,,bmad-workflow-builder,Optimize Workflow,OW,quality-optimizer,,Validate and optimize an existing workflow or utility skill. Produces a quality report.,anytime,bmad-workflow-builder:build-process,,false,bmad_builder_reports,quality report, diff --git a/src/skills/bmad-agent-builder/assets/SKILL-template.md b/src/skills/bmad-agent-builder/assets/SKILL-template.md index 4b8bf8e..7c79701 100644 --- a/src/skills/bmad-agent-builder/assets/SKILL-template.md +++ b/src/skills/bmad-agent-builder/assets/SKILL-template.md @@ -55,7 +55,7 @@ Load `references/memory-system.md` for memory discipline and structure. ## On Activation -1. **Load config via bmad-init skill** — Store all returned vars for use: +1. **Load config** from `{project-root}/_bmad/config.yaml` — Store all returned vars for use: - Use `{user_name}` from config for greeting - Use `{communication_language}` from config for all communications - Store any other config variables as `{var-name}` and use appropriately diff --git a/src/skills/bmad-agent-builder/build-process.md b/src/skills/bmad-agent-builder/build-process.md index 1f0dd56..5e9efc6 100644 --- a/src/skills/bmad-agent-builder/build-process.md +++ b/src/skills/bmad-agent-builder/build-process.md @@ -67,7 +67,7 @@ Work through these conversationally: - **Save triggers:** After which interactions should memory be updated? - **Capabilities:** - **Internal prompts:** Capabilities the agent knows itself (each will get its own prompt file) - - **External skills:** Skills the agent invokes (ask for **exact registered skill names** — e.g., `bmad-init`, `skill-creator`) + - **External skills:** Skills the agent invokes (ask for **exact registered skill names** — e.g., `skill-creator`) - Note: Skills may exist now or be created later - **First-run:** What should it ask on first activation? (standalone only; module-based gets config from module's config.yaml) @@ -164,4 +164,4 @@ Ask: *"Build is done. Would you like to run a Quality Scan to optimize the agent If yes, load `quality-optimizer.md` with `{scan_mode}=full` and the agent path. -Remind them: BMad module system compliant. Use `bmad-init` skill to integrate into a project. +Remind them: BMad module system compliant. Use the module-init skill to install and configure into a project. diff --git a/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md b/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md index ee41330..4a85ae0 100644 --- a/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md +++ b/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md @@ -78,7 +78,7 @@ Capability prompts (prompt `.md` files at skill root) are the working instructio | Check | Why It Matters | |-------|----------------| | Has config header with language variables | Agent needs `{communication_language}` context | -| Uses bmad-init variables, not hardcoded values | Flexibility across projects | +| Uses config variables, not hardcoded values | Flexibility across projects | ### Self-Containment (Context Compaction Survival) | Check | Why It Matters | diff --git a/src/skills/bmad-builder-config/SKILL.md b/src/skills/bmad-builder-config/SKILL.md new file mode 100644 index 0000000..f93e3f5 --- /dev/null +++ b/src/skills/bmad-builder-config/SKILL.md @@ -0,0 +1,41 @@ +--- +name: bmad-builder-config +description: Installs BMad Builder module into a project. Use when the user requests to 'install bmb module', 'configure bmad builder', or 'setup bmad builder'. +--- + +# BMad Builder Config + +## Overview + +Installs and configures the BMad Builder (bmb) module into a project. Collects user preferences, writes them to a shared `{project-root}/_bmad/config.yaml`, and registers module capabilities in `{project-root}/_bmad/module-help.csv`. Both scripts use an anti-zombie pattern — existing entries for this module are removed before writing fresh ones, so stale values never persist. + +## On Activation + +1. Confirm `{project-root}` with the user — display the detected project root and verify before proceeding +2. Read `assets/module.yaml` for module metadata and variable definitions +3. Check if `{project-root}/_bmad/config.yaml` exists — if a `bmb` section is already present, inform the user this is an update + +## Collect Configuration + +Ask the user for values. Show defaults in brackets; accept blank for default. Pre-fill from existing config values when updating. + +**Core config** (only if no `core` section exists yet): `user_name` (required, no default), `communication_language`, `document_output_language`, `output_folder`. These are shared across all modules. + +**Module config**: Read each variable in `assets/module.yaml` that has a `prompt` field. Ask using that prompt with its `default` value. + +## Write Files + +Write a temp JSON file with the collected answers structured as `{"core": {...}, "module": {...}}` (omit `core` if it already exists). Then run both scripts — they can run in parallel since they write to different files: + +```bash +python3 scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --module-yaml assets/module.yaml --answers {temp-file} +python3 scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source assets/module-help.csv +``` + +Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. + +Run `scripts/merge-config.py --help` or `scripts/merge-help-csv.py --help` for full usage. + +## Confirm + +Use the script JSON output to display what was written — config values set, help entries added, fresh install vs update. diff --git a/src/skills/bmad-builder-config/assets/module-help.csv b/src/skills/bmad-builder-config/assets/module-help.csv new file mode 100644 index 0000000..7c233a4 --- /dev/null +++ b/src/skills/bmad-builder-config/assets/module-help.csv @@ -0,0 +1,6 @@ +module,agent-name,skill-name,display-name,menu-code,capability,args,description,phase,after,before,required,output-location,outputs, +bmb,,bmad-builder-config,Configure Builder,CB,configure,,"Install and configure BMad Builder module into a project.",anytime,,,false,,config, +bmb,,bmad-agent-builder,Build Agent,BA,build-process,,"Create, edit, convert, or fix an agent skill.",anytime,,bmad-agent-builder:quality-optimizer,false,output_folder,agent skill, +bmb,,bmad-agent-builder,Optimize Agent,OA,quality-optimizer,,Validate and optimize an existing agent skill. Produces a quality report.,anytime,bmad-agent-builder:build-process,,false,bmad_builder_reports,quality report, +bmb,,bmad-workflow-builder,Build Workflow,BW,build-process,,"Create, edit, convert, or fix a workflow or utility skill.",anytime,,bmad-workflow-builder:quality-optimizer,false,output_folder,workflow skill, +bmb,,bmad-workflow-builder,Optimize Workflow,OW,quality-optimizer,,Validate and optimize an existing workflow or utility skill. Produces a quality report.,anytime,bmad-workflow-builder:build-process,,false,bmad_builder_reports,quality report, diff --git a/src/skills/bmad-builder-config/assets/module.yaml b/src/skills/bmad-builder-config/assets/module.yaml new file mode 100644 index 0000000..4003333 --- /dev/null +++ b/src/skills/bmad-builder-config/assets/module.yaml @@ -0,0 +1,20 @@ +code: bmb +name: "BMad Builder" +description: "Standard Skill Compliant Factory for BMad Agents, Workflows and Modules" +default_selected: false + +# Variables from Core Config inserted: +## user_name +## communication_language +## document_output_language +## output_folder + +bmad_builder_output_folder: + prompt: "Where should your custom skills (agents and workflows) be saved?" + default: "_bmad-output/skills" + result: "{project-root}/{value}" + +bmad_builder_reports: + prompt: "Output for Evals, Test, Quality and Planning Reports?" + default: "_bmad-output/reports" + result: "{project-root}/{value}" diff --git a/src/skills/bmad-builder-config/scripts/merge-config.py b/src/skills/bmad-builder-config/scripts/merge-config.py new file mode 100755 index 0000000..d66c0aa --- /dev/null +++ b/src/skills/bmad-builder-config/scripts/merge-config.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.9" +# dependencies = ["pyyaml"] +# /// +"""Merge module configuration into shared _bmad/config.yaml. + +Reads a module.yaml definition and a JSON answers file, then writes or updates +the shared config.yaml. Uses an anti-zombie pattern: if the module code already +exists in config, that entire section is removed before writing fresh values. + +Exit codes: 0=success, 1=validation error, 2=runtime error +""" + +import argparse +import json +import sys +from pathlib import Path + +try: + import yaml +except ImportError: + print("Error: pyyaml is required (PEP 723 dependency)", file=sys.stderr) + sys.exit(2) + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Merge module config into shared _bmad/config.yaml with anti-zombie pattern." + ) + parser.add_argument( + "--config-path", + required=True, + help="Path to the target _bmad/config.yaml file", + ) + parser.add_argument( + "--module-yaml", + required=True, + help="Path to the module.yaml definition file", + ) + parser.add_argument( + "--answers", + required=True, + help="Path to JSON file with collected answers", + ) + parser.add_argument( + "--verbose", + action="store_true", + help="Print detailed progress to stderr", + ) + return parser.parse_args() + + +def load_yaml_file(path: str) -> dict: + """Load a YAML file, returning empty dict if file doesn't exist.""" + file_path = Path(path) + if not file_path.exists(): + return {} + with open(file_path, "r", encoding="utf-8") as f: + content = yaml.safe_load(f) + return content if content else {} + + +def load_json_file(path: str) -> dict: + """Load a JSON file.""" + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def extract_module_metadata(module_yaml: dict) -> dict: + """Extract non-variable metadata fields from module.yaml.""" + metadata_keys = ["name", "description", "default_selected"] + return {k: module_yaml[k] for k in metadata_keys if k in module_yaml} + + +def merge_config( + existing_config: dict, + module_yaml: dict, + answers: dict, + verbose: bool = False, +) -> dict: + """Merge answers into config, applying anti-zombie pattern. + + Args: + existing_config: Current config.yaml contents (may be empty) + module_yaml: The module definition + answers: JSON with 'core' and/or 'module' keys + verbose: Print progress to stderr + + Returns: + Updated config dict ready to write + """ + config = dict(existing_config) + module_code = module_yaml.get("code") + + if not module_code: + print("Error: module.yaml must have a 'code' field", file=sys.stderr) + sys.exit(1) + + # Write core section if provided + core_answers = answers.get("core") + if core_answers: + if verbose: + print(f"Writing core config: {list(core_answers.keys())}", file=sys.stderr) + config["core"] = core_answers + + # Anti-zombie: remove existing module section + if module_code in config: + if verbose: + print( + f"Removing existing '{module_code}' section (anti-zombie)", + file=sys.stderr, + ) + del config[module_code] + + # Build module section: metadata + variable values + module_section = extract_module_metadata(module_yaml) + module_answers = answers.get("module", {}) + module_section.update(module_answers) + + if verbose: + print( + f"Writing '{module_code}' section with keys: {list(module_section.keys())}", + file=sys.stderr, + ) + + config[module_code] = module_section + + return config + + +def write_config(config: dict, config_path: str, verbose: bool = False) -> None: + """Write config dict to YAML file, creating parent dirs as needed.""" + path = Path(config_path) + path.parent.mkdir(parents=True, exist_ok=True) + + if verbose: + print(f"Writing config to {path}", file=sys.stderr) + + with open(path, "w", encoding="utf-8") as f: + yaml.dump( + config, + f, + default_flow_style=False, + allow_unicode=True, + sort_keys=False, + ) + + +def main(): + args = parse_args() + + # Load inputs + module_yaml = load_yaml_file(args.module_yaml) + if not module_yaml: + print(f"Error: Could not load module.yaml from {args.module_yaml}", file=sys.stderr) + sys.exit(1) + + answers = load_json_file(args.answers) + existing_config = load_yaml_file(args.config_path) + + if args.verbose: + exists = Path(args.config_path).exists() + print(f"Config file exists: {exists}", file=sys.stderr) + if exists: + print(f"Existing sections: {list(existing_config.keys())}", file=sys.stderr) + + # Merge and write + updated_config = merge_config(existing_config, module_yaml, answers, args.verbose) + write_config(updated_config, args.config_path, args.verbose) + + # Output result summary as JSON + module_code = module_yaml["code"] + result = { + "status": "success", + "config_path": str(Path(args.config_path).resolve()), + "module_code": module_code, + "core_updated": "core" in answers and bool(answers["core"]), + "module_keys": list(updated_config.get(module_code, {}).keys()), + } + print(json.dumps(result, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/skills/bmad-builder-config/scripts/merge-help-csv.py b/src/skills/bmad-builder-config/scripts/merge-help-csv.py new file mode 100755 index 0000000..80643e5 --- /dev/null +++ b/src/skills/bmad-builder-config/scripts/merge-help-csv.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.9" +# dependencies = [] +# /// +"""Merge module help entries into shared _bmad/module-help.csv. + +Reads a source CSV with module help entries and merges them into a target CSV. +Uses an anti-zombie pattern: all existing rows matching the source module code +are removed before appending fresh rows. + +Exit codes: 0=success, 1=validation error, 2=runtime error +""" + +import argparse +import csv +import json +import sys +from io import StringIO +from pathlib import Path + +# CSV header for module-help.csv +HEADER = [ + "module", + "agent-name", + "skill-name", + "display-name", + "menu-code", + "capability", + "args", + "description", + "phase", + "after", + "before", + "required", + "output-location", + "outputs", + "", # trailing empty column from trailing comma +] + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Merge module help entries into shared _bmad/module-help.csv with anti-zombie pattern." + ) + parser.add_argument( + "--target", + required=True, + help="Path to the target _bmad/module-help.csv file", + ) + parser.add_argument( + "--source", + required=True, + help="Path to the source module-help.csv with entries to merge", + ) + parser.add_argument( + "--verbose", + action="store_true", + help="Print detailed progress to stderr", + ) + return parser.parse_args() + + +def read_csv_rows(path: str) -> tuple[list[str], list[list[str]]]: + """Read CSV file returning (header, data_rows). + + Returns empty header and rows if file doesn't exist. + """ + file_path = Path(path) + if not file_path.exists(): + return [], [] + + with open(file_path, "r", encoding="utf-8", newline="") as f: + content = f.read() + + reader = csv.reader(StringIO(content)) + rows = list(reader) + + if not rows: + return [], [] + + return rows[0], rows[1:] + + +def extract_module_codes(rows: list[list[str]]) -> set[str]: + """Extract unique module codes from data rows.""" + codes = set() + for row in rows: + if row and row[0].strip(): + codes.add(row[0].strip()) + return codes + + +def filter_rows(rows: list[list[str]], module_code: str) -> list[list[str]]: + """Remove all rows matching the given module code.""" + return [row for row in rows if not row or row[0].strip() != module_code] + + +def write_csv(path: str, header: list[str], rows: list[list[str]], verbose: bool = False) -> None: + """Write header + rows to CSV file, creating parent dirs as needed.""" + file_path = Path(path) + file_path.parent.mkdir(parents=True, exist_ok=True) + + if verbose: + print(f"Writing {len(rows)} data rows to {path}", file=sys.stderr) + + with open(file_path, "w", encoding="utf-8", newline="") as f: + writer = csv.writer(f) + writer.writerow(header) + for row in rows: + writer.writerow(row) + + +def main(): + args = parse_args() + + # Read source entries + source_header, source_rows = read_csv_rows(args.source) + if not source_rows: + print(f"Error: No data rows found in source {args.source}", file=sys.stderr) + sys.exit(1) + + # Determine module codes being merged + source_codes = extract_module_codes(source_rows) + if not source_codes: + print("Error: Could not determine module code from source rows", file=sys.stderr) + sys.exit(1) + + if args.verbose: + print(f"Source module codes: {source_codes}", file=sys.stderr) + print(f"Source rows: {len(source_rows)}", file=sys.stderr) + + # Read existing target (may not exist) + target_header, target_rows = read_csv_rows(args.target) + target_existed = Path(args.target).exists() + + if args.verbose: + print(f"Target exists: {target_existed}", file=sys.stderr) + if target_existed: + print(f"Existing target rows: {len(target_rows)}", file=sys.stderr) + + # Use source header if target doesn't exist or has no header + header = target_header if target_header else (source_header if source_header else HEADER) + + # Anti-zombie: remove all rows for each source module code + filtered_rows = target_rows + removed_count = 0 + for code in source_codes: + before_count = len(filtered_rows) + filtered_rows = filter_rows(filtered_rows, code) + removed_count += before_count - len(filtered_rows) + + if args.verbose and removed_count > 0: + print(f"Removed {removed_count} existing rows (anti-zombie)", file=sys.stderr) + + # Append source rows + merged_rows = filtered_rows + source_rows + + # Write result + write_csv(args.target, header, merged_rows, args.verbose) + + # Output result summary as JSON + result = { + "status": "success", + "target_path": str(Path(args.target).resolve()), + "target_existed": target_existed, + "module_codes": sorted(source_codes), + "rows_removed": removed_count, + "rows_added": len(source_rows), + "total_rows": len(merged_rows), + } + print(json.dumps(result, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/skills/bmad-builder-config/scripts/tests/test-merge-config.py b/src/skills/bmad-builder-config/scripts/tests/test-merge-config.py new file mode 100644 index 0000000..448a7e0 --- /dev/null +++ b/src/skills/bmad-builder-config/scripts/tests/test-merge-config.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.9" +# dependencies = ["pyyaml"] +# /// +"""Unit tests for merge-config.py.""" + +import json +import os +import sys +import tempfile +import unittest +from pathlib import Path + +# Add parent directory to path so we can import the module +sys.path.insert(0, str(Path(__file__).parent.parent)) + +import yaml + +from importlib.util import spec_from_file_location, module_from_spec + +# Import merge_config module +_spec = spec_from_file_location( + "merge_config", + str(Path(__file__).parent.parent / "merge-config.py"), +) +merge_config_mod = module_from_spec(_spec) +_spec.loader.exec_module(merge_config_mod) + +extract_module_metadata = merge_config_mod.extract_module_metadata +merge_config = merge_config_mod.merge_config + + +SAMPLE_MODULE_YAML = { + "code": "bmb", + "name": "BMad Builder", + "description": "Standard Skill Compliant Factory", + "default_selected": False, + "bmad_builder_output_folder": { + "prompt": "Where should skills be saved?", + "default": "_bmad-output/skills", + "result": "{project-root}/{value}", + }, + "bmad_builder_reports": { + "prompt": "Output for reports?", + "default": "_bmad-output/reports", + "result": "{project-root}/{value}", + }, +} + + +class TestExtractModuleMetadata(unittest.TestCase): + def test_extracts_metadata_fields(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML) + self.assertEqual(result["name"], "BMad Builder") + self.assertEqual(result["description"], "Standard Skill Compliant Factory") + self.assertFalse(result["default_selected"]) + + def test_excludes_variable_definitions(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML) + self.assertNotIn("bmad_builder_output_folder", result) + self.assertNotIn("bmad_builder_reports", result) + self.assertNotIn("code", result) + + +class TestMergeConfig(unittest.TestCase): + def test_fresh_install_with_core_and_module(self): + answers = { + "core": { + "user_name": "Brian", + "communication_language": "English", + }, + "module": { + "bmad_builder_output_folder": "_bmad-output/skills", + }, + } + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + + self.assertEqual(result["core"]["user_name"], "Brian") + self.assertEqual(result["bmb"]["name"], "BMad Builder") + self.assertEqual(result["bmb"]["bmad_builder_output_folder"], "_bmad-output/skills") + + def test_update_preserves_core(self): + existing = { + "core": {"user_name": "Brian", "communication_language": "English"}, + "other_module": {"name": "Other"}, + } + answers = { + "module": { + "bmad_builder_output_folder": "_bmad-output/skills", + }, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # Core preserved + self.assertEqual(result["core"]["user_name"], "Brian") + # Other module preserved + self.assertIn("other_module", result) + # New module added + self.assertIn("bmb", result) + + def test_anti_zombie_removes_existing_module(self): + existing = { + "core": {"user_name": "Brian"}, + "bmb": { + "name": "BMad Builder", + "old_variable": "should_be_removed", + "bmad_builder_output_folder": "old/path", + }, + } + answers = { + "module": { + "bmad_builder_output_folder": "new/path", + }, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # Old variable is gone + self.assertNotIn("old_variable", result["bmb"]) + # New value is present + self.assertEqual(result["bmb"]["bmad_builder_output_folder"], "new/path") + # Metadata is fresh from module.yaml + self.assertEqual(result["bmb"]["name"], "BMad Builder") + + def test_core_answers_override_existing(self): + existing = { + "core": {"user_name": "OldName", "communication_language": "Spanish"}, + } + answers = { + "core": {"user_name": "NewName", "communication_language": "English"}, + "module": {}, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + self.assertEqual(result["core"]["user_name"], "NewName") + self.assertEqual(result["core"]["communication_language"], "English") + + def test_no_core_answers_skips_core(self): + existing = { + "core": {"user_name": "Brian"}, + } + answers = { + "module": {"bmad_builder_output_folder": "path"}, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # Core unchanged + self.assertEqual(result["core"]["user_name"], "Brian") + + def test_module_metadata_always_from_yaml(self): + """Module metadata comes from module.yaml, not answers.""" + answers = { + "module": {"bmad_builder_output_folder": "path"}, + } + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + + self.assertEqual(result["bmb"]["name"], "BMad Builder") + self.assertEqual(result["bmb"]["description"], "Standard Skill Compliant Factory") + self.assertFalse(result["bmb"]["default_selected"]) + + +class TestEndToEnd(unittest.TestCase): + def test_write_and_read_round_trip(self): + with tempfile.TemporaryDirectory() as tmpdir: + config_path = os.path.join(tmpdir, "_bmad", "config.yaml") + answers_path = os.path.join(tmpdir, "answers.json") + module_path = os.path.join(tmpdir, "module.yaml") + + # Write module.yaml + with open(module_path, "w") as f: + yaml.dump(SAMPLE_MODULE_YAML, f) + + # Write answers + answers = { + "core": {"user_name": "Brian", "communication_language": "English"}, + "module": {"bmad_builder_output_folder": "_bmad-output/skills"}, + } + with open(answers_path, "w") as f: + json.dump(answers, f) + + # Run merge + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + merge_config_mod.write_config(result, config_path) + + # Read back + with open(config_path, "r") as f: + written = yaml.safe_load(f) + + self.assertEqual(written["core"]["user_name"], "Brian") + self.assertEqual(written["bmb"]["bmad_builder_output_folder"], "_bmad-output/skills") + + def test_update_round_trip(self): + """Simulate install, then re-install with different values.""" + with tempfile.TemporaryDirectory() as tmpdir: + config_path = os.path.join(tmpdir, "config.yaml") + + # First install + answers1 = { + "core": {"user_name": "Brian"}, + "module": {"bmad_builder_output_folder": "old/path"}, + } + result1 = merge_config({}, SAMPLE_MODULE_YAML, answers1) + merge_config_mod.write_config(result1, config_path) + + # Second install (update) + existing = merge_config_mod.load_yaml_file(config_path) + answers2 = { + "module": {"bmad_builder_output_folder": "new/path"}, + } + result2 = merge_config(existing, SAMPLE_MODULE_YAML, answers2) + merge_config_mod.write_config(result2, config_path) + + # Verify + with open(config_path, "r") as f: + final = yaml.safe_load(f) + + self.assertEqual(final["core"]["user_name"], "Brian") + self.assertEqual(final["bmb"]["bmad_builder_output_folder"], "new/path") + + +if __name__ == "__main__": + unittest.main() diff --git a/src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py b/src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py new file mode 100644 index 0000000..c5fc97a --- /dev/null +++ b/src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.9" +# dependencies = [] +# /// +"""Unit tests for merge-help-csv.py.""" + +import csv +import os +import sys +import tempfile +import unittest +from io import StringIO +from pathlib import Path + +# Import merge_help_csv module +from importlib.util import spec_from_file_location, module_from_spec + +_spec = spec_from_file_location( + "merge_help_csv", + str(Path(__file__).parent.parent / "merge-help-csv.py"), +) +merge_help_csv_mod = module_from_spec(_spec) +_spec.loader.exec_module(merge_help_csv_mod) + +extract_module_codes = merge_help_csv_mod.extract_module_codes +filter_rows = merge_help_csv_mod.filter_rows +read_csv_rows = merge_help_csv_mod.read_csv_rows +write_csv = merge_help_csv_mod.write_csv +HEADER = merge_help_csv_mod.HEADER + + +SAMPLE_ROWS = [ + ["bmb", "", "bmad-bmb-module-init", "Install Module", "IM", "install", "", "Install BMad Builder.", "anytime", "", "", "false", "", "config", ""], + ["bmb", "", "bmad-agent-builder", "Build Agent", "BA", "build-process", "", "Create an agent.", "anytime", "", "", "false", "output_folder", "agent skill", ""], +] + + +class TestExtractModuleCodes(unittest.TestCase): + def test_extracts_codes(self): + codes = extract_module_codes(SAMPLE_ROWS) + self.assertEqual(codes, {"bmb"}) + + def test_multiple_codes(self): + rows = SAMPLE_ROWS + [ + ["cis", "", "cis-skill", "CIS Skill", "CS", "run", "", "A skill.", "anytime", "", "", "false", "", "", ""], + ] + codes = extract_module_codes(rows) + self.assertEqual(codes, {"bmb", "cis"}) + + def test_empty_rows(self): + codes = extract_module_codes([]) + self.assertEqual(codes, set()) + + +class TestFilterRows(unittest.TestCase): + def test_removes_matching_rows(self): + result = filter_rows(SAMPLE_ROWS, "bmb") + self.assertEqual(len(result), 0) + + def test_preserves_non_matching_rows(self): + mixed_rows = SAMPLE_ROWS + [ + ["cis", "", "cis-skill", "CIS Skill", "CS", "run", "", "A skill.", "anytime", "", "", "false", "", "", ""], + ] + result = filter_rows(mixed_rows, "bmb") + self.assertEqual(len(result), 1) + self.assertEqual(result[0][0], "cis") + + def test_no_match_preserves_all(self): + result = filter_rows(SAMPLE_ROWS, "xyz") + self.assertEqual(len(result), 2) + + +class TestReadWriteCSV(unittest.TestCase): + def test_nonexistent_file_returns_empty(self): + header, rows = read_csv_rows("/nonexistent/path/file.csv") + self.assertEqual(header, []) + self.assertEqual(rows, []) + + def test_round_trip(self): + with tempfile.TemporaryDirectory() as tmpdir: + path = os.path.join(tmpdir, "test.csv") + write_csv(path, HEADER, SAMPLE_ROWS) + + header, rows = read_csv_rows(path) + self.assertEqual(len(rows), 2) + self.assertEqual(rows[0][0], "bmb") + self.assertEqual(rows[0][2], "bmad-bmb-module-init") + + def test_creates_parent_dirs(self): + with tempfile.TemporaryDirectory() as tmpdir: + path = os.path.join(tmpdir, "sub", "dir", "test.csv") + write_csv(path, HEADER, SAMPLE_ROWS) + self.assertTrue(os.path.exists(path)) + + +class TestEndToEnd(unittest.TestCase): + def _write_source(self, tmpdir, rows): + path = os.path.join(tmpdir, "source.csv") + write_csv(path, HEADER, rows) + return path + + def _write_target(self, tmpdir, rows): + path = os.path.join(tmpdir, "target.csv") + write_csv(path, HEADER, rows) + return path + + def test_fresh_install_no_existing_target(self): + with tempfile.TemporaryDirectory() as tmpdir: + source_path = self._write_source(tmpdir, SAMPLE_ROWS) + target_path = os.path.join(tmpdir, "target.csv") + + # Target doesn't exist + self.assertFalse(os.path.exists(target_path)) + + # Simulate merge + _, source_rows = read_csv_rows(source_path) + source_codes = extract_module_codes(source_rows) + write_csv(target_path, HEADER, source_rows) + + _, result_rows = read_csv_rows(target_path) + self.assertEqual(len(result_rows), 2) + + def test_merge_into_existing_with_other_module(self): + with tempfile.TemporaryDirectory() as tmpdir: + other_rows = [ + ["cis", "", "cis-skill", "CIS Skill", "CS", "run", "", "A skill.", "anytime", "", "", "false", "", "", ""], + ] + target_path = self._write_target(tmpdir, other_rows) + source_path = self._write_source(tmpdir, SAMPLE_ROWS) + + # Read both + _, target_rows = read_csv_rows(target_path) + _, source_rows = read_csv_rows(source_path) + source_codes = extract_module_codes(source_rows) + + # Anti-zombie filter + append + filtered = target_rows + for code in source_codes: + filtered = filter_rows(filtered, code) + merged = filtered + source_rows + + write_csv(target_path, HEADER, merged) + + _, result_rows = read_csv_rows(target_path) + self.assertEqual(len(result_rows), 3) # 1 cis + 2 bmb + + def test_anti_zombie_replaces_stale_entries(self): + with tempfile.TemporaryDirectory() as tmpdir: + # Existing target has old bmb entries + cis entry + old_bmb_rows = [ + ["bmb", "", "old-skill", "Old Skill", "OS", "run", "", "Old.", "anytime", "", "", "false", "", "", ""], + ["bmb", "", "another-old", "Another", "AO", "run", "", "Old too.", "anytime", "", "", "false", "", "", ""], + ] + cis_rows = [ + ["cis", "", "cis-skill", "CIS Skill", "CS", "run", "", "A skill.", "anytime", "", "", "false", "", "", ""], + ] + target_path = self._write_target(tmpdir, old_bmb_rows + cis_rows) + source_path = self._write_source(tmpdir, SAMPLE_ROWS) + + # Read both + _, target_rows = read_csv_rows(target_path) + _, source_rows = read_csv_rows(source_path) + source_codes = extract_module_codes(source_rows) + + # Anti-zombie filter + append + filtered = target_rows + for code in source_codes: + filtered = filter_rows(filtered, code) + merged = filtered + source_rows + + write_csv(target_path, HEADER, merged) + + _, result_rows = read_csv_rows(target_path) + # Should have 1 cis + 2 new bmb = 3 (old bmb removed) + self.assertEqual(len(result_rows), 3) + module_codes = [r[0] for r in result_rows] + self.assertEqual(module_codes.count("bmb"), 2) + self.assertEqual(module_codes.count("cis"), 1) + # Old skills should be gone + skill_names = [r[2] for r in result_rows] + self.assertNotIn("old-skill", skill_names) + self.assertNotIn("another-old", skill_names) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/skills/bmad-workflow-builder/assets/SKILL-template.md b/src/skills/bmad-workflow-builder/assets/SKILL-template.md index 0885a0c..69a8139 100644 --- a/src/skills/bmad-workflow-builder/assets/SKILL-template.md +++ b/src/skills/bmad-workflow-builder/assets/SKILL-template.md @@ -28,14 +28,12 @@ Act as {role-guidance}. ## On Activation -{if-bmad-init} -1. **Load config via bmad-init skill** — Store all returned vars for use: +1. **Load config** from `{project-root}/_bmad/config.yaml` — Store all returned vars for use: - Use `{user_name}` from config for greeting - Use `{communication_language}` for all communications {if-creates-docs}- Use `{document_output_language}` for output documents{/if-creates-docs} 2. **Greet user** as `{user_name}`, speaking in `{communication_language}` -{/if-bmad-init} 3. **Proceed to workflow steps below** @@ -69,15 +67,13 @@ Act as {role-guidance}. ## On Activation -{if-bmad-init} -1. **Load config via bmad-init skill** — Store all returned vars for use: +1. **Load config** from `{project-root}/_bmad/config.yaml` — Store all returned vars for use: - Use `{user_name}` from config for greeting - Use `{communication_language}` for all communications {if-creates-docs}- Use `{document_output_language}` for output documents{/if-creates-docs} - Store any other config variables as `{var-name}` and use appropriately 2. **Greet user** as `{user_name}`, speaking in `{communication_language}` -{/if-bmad-init} 3. **Check if workflow in progress:** - If output doc exists (user specifies path or we prompt): diff --git a/src/skills/bmad-workflow-builder/build-process.md b/src/skills/bmad-workflow-builder/build-process.md index bae7dfc..db2d5f7 100644 --- a/src/skills/bmad-workflow-builder/build-process.md +++ b/src/skills/bmad-workflow-builder/build-process.md @@ -29,7 +29,7 @@ For Phase 2 and 3 that follow, adapt to what you already know that the user has Ask upfront: - Will this be part of a module? If yes: - What's the module code? (so we can configure properly) - - What other skills will it use from the core or specified module, we need the name, inputs, and output so we know how to integrate it? (bmad-init is default unless explicitly opted out, other skills should be either core skills or skills that will be part of the module) + - What other skills will it use from the core or specified module, we need the name, inputs, and output so we know how to integrate it? (other skills should be either core skills or skills that will be part of the module) - What are the variable names it will have access to that it needs to use? (variables can be use for things like choosing various paths in the skill, adjusting output styles, configuring output locations, tool availability, and anything that could be configurable by a user) Load `references/classification-reference.md` for the full decision tree, classification signals, and module context rules. Use it to classify: @@ -79,7 +79,7 @@ Work through conversationally, adapted per skill type, so you can either glean f **Simple Utility additional fields:** - **Input format:** What does it accept? - **Output format:** What does it return? -- **Standalone?** Opt out of bmad-init? (Makes it a truly standalone building block) +- **Standalone?** No config needed? (Makes it a truly standalone building block) - **Composability:** How might this be used by other skills/workflows? - **Script needs:** What scripts does the utility require? diff --git a/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md b/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md index e9a7057..3ccd0f0 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md +++ b/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md @@ -188,8 +188,8 @@ Use EXACTLY these field names: `file`, `line`, `severity`, `category`, `title`, "severity": "high-opportunity", "category": "experience-gap", "title": "First-time user with no project config hits a dead end at stage 2", - "detail": "Stage 2 assumes bmad-init has been run and a config exists. A first-timer who invokes this skill directly gets a cryptic error with no guidance on how to recover. This would frustrate new users and create abandonment.", - "action": "Add a graceful fallback in stage 2: detect missing config, explain what bmad-init does, and offer to proceed with defaults." + "detail": "Stage 2 assumes a config exists at _bmad/config.yaml. A first-timer who invokes this skill directly gets a cryptic error with no guidance on how to recover. This would frustrate new users and create abandonment.", + "action": "Add a graceful fallback in stage 2: detect missing config, explain how to run the module-init skill, and offer to proceed with defaults." } ], "assessments": { diff --git a/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md b/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md index 5005129..9077510 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md +++ b/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md @@ -123,7 +123,7 @@ Stage prompts (prompt `*.md` files at skill root) are the working instructions f | Check | Why It Matters | |-------|----------------| | Has config header establishing language and output settings | Agent needs `{communication_language}` and output format context | -| Uses bmad-init variables, not hardcoded values | Flexibility across projects and users | +| Uses config variables, not hardcoded values | Flexibility across projects and users | ### Progression Conditions diff --git a/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md b/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md index a69952f..b731c14 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md +++ b/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md @@ -71,13 +71,13 @@ Workflows may include Identity, Communication Style, or Principles sections if p | No orphaned `{if-simple-utility}` conditionals | Should have been resolved during skill creation | | No bare placeholders like `{displayName}`, `{skillName}` | Should have been replaced with actual values | | No other template fragments (`{if-module}`, `{if-headless}`, etc.) | Conditional blocks should be removed, not left as text | -| Variables from `bmad-init` are OK | `{user_name}`, `{communication_language}`, `{document_output_language}` are intentional runtime variables | +| Config variables are OK | `{user_name}`, `{communication_language}`, `{document_output_language}` are intentional runtime variables | ### Config Integration | Check | Why It Matters | |-------|----------------| -| bmad-init config loading present in On Activation | Config provides user preferences, language settings, project context | +| Config loading present in On Activation | Config provides user preferences, language settings, project context | | Config values used where appropriate | Hardcoded values that should come from config cause inflexibility | --- @@ -119,7 +119,7 @@ Determine workflow type from SKILL.md before applying type-specific checks: |-------|----------------| | Each stage prompt has config header specifying Language | AI needs to know what language to communicate in | | Stage prompts that create documents specify Output Language | Document language may differ from communication language | -| Config header uses bmad-init variables correctly | `{communication_language}`, `{document_output_language}` | +| Config header uses config variables correctly | `{communication_language}`, `{document_output_language}` | ### Simple Workflow diff --git a/src/skills/bmad-workflow-builder/references/classification-reference.md b/src/skills/bmad-workflow-builder/references/classification-reference.md index b8d2c56..a85a318 100644 --- a/src/skills/bmad-workflow-builder/references/classification-reference.md +++ b/src/skills/bmad-workflow-builder/references/classification-reference.md @@ -6,8 +6,8 @@ Classify the skill type based on user requirements. This table is for internal u | Type | Description | Structure | When to Use | |------|-------------|-----------|-------------| -| **Simple Utility** | Input/output building block. Headless, composable, often has scripts. May opt out of bmad-init for true standalone use. | Single SKILL.md + scripts/ | Composable building block with clear input/output, single-purpose | -| **Simple Workflow** | Multi-step process contained in a single SKILL.md. Uses bmad-init. Minimal or no prompt files. | SKILL.md + optional references/ | Multi-step process that fits in one file, no progressive disclosure needed | +| **Simple Utility** | Input/output building block. Headless, composable, often has scripts. | Single SKILL.md + scripts/ | Composable building block with clear input/output, single-purpose | +| **Simple Workflow** | Multi-step process contained in a single SKILL.md. Minimal or no prompt files. | SKILL.md + optional references/ | Multi-step process that fits in one file, no progressive disclosure needed | | **Complex Workflow** | Multi-stage with progressive disclosure, numbered prompt files at root, config integration. May support headless mode. | SKILL.md (routing) + prompt stages at root + references/ | Multiple stages, long-running process, progressive disclosure, routing logic | ## Decision Tree @@ -58,4 +58,4 @@ Module context is asked for ALL types: - **Module-based:** Part of a BMad module. Uses `bmad-{modulecode}-{skillname}` naming. - **Standalone:** Independent skill. Uses `bmad-{skillname}` naming. -All workflows use `bmad-init` by default unless explicitly opted out (truly standalone utilities). +All workflows read config from `{project-root}/_bmad/config.yaml` by default. diff --git a/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md b/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md index 038816b..c39829b 100644 --- a/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md +++ b/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md @@ -30,18 +30,18 @@ Which would you prefer? ## Config Reading and Integration -Workflows MUST read config values using the `bmad-init` skill. +Workflows MUST read config values from `{project-root}/_bmad/config.yaml`. ### Config Loading Pattern -**Invoke the skill with parameters:** +**Read the config file directly:** ``` -Use bmad-init skill: -- module: {bmad-module-code} -- vars: user_name:BMad,communication_language:English,document_output_language:English,output_folder:{project-root}/_bmad-output,{output-location-variable}:{default-output-path} +Load config from {project-root}/_bmad/config.yaml +- Read the `core` section for shared variables (user_name, communication_language, etc.) +- Read the module section (keyed by module code) for module-specific variables ``` -The skill returns JSON with config values. Store in memory as `{var_name}` for use in prompts. +Store config values in memory as `{var_name}` for use in prompts. ### Required Core Variables @@ -425,7 +425,7 @@ description: Complex multi-stage workflow for my module. Use when user requests ## Workflow Entry -1. Use bmad-init skill (module: mm) — loads user_name, communication_language, document_output_language, output_folder, my_output_folder +1. Load config from `{project-root}/_bmad/config.yaml` — reads core vars (user_name, communication_language, document_output_language, output_folder) and module vars (my_output_folder) 2. Ask user for output document path (or suggest {my_output_folder}/analysis-{timestamp}.md) diff --git a/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md b/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md index 89346f5..25d6c7a 100644 --- a/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md +++ b/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md @@ -203,7 +203,7 @@ Each entry follows the format: What it does, Why it matters for workflows, What **Checks:** - Scan all `.md` files for `{variable_name}` patterns -- Cross-reference against variables loaded by `bmad-init` or defined in config +- Cross-reference against variables defined in `{project-root}/_bmad/config.yaml` - Distinguish template variables from literal text in code blocks - Flag undefined variables and unused loaded variables diff --git a/src/skills/bmad-workflow-builder/references/standard-fields.md b/src/skills/bmad-workflow-builder/references/standard-fields.md index 521f887..b6c8092 100644 --- a/src/skills/bmad-workflow-builder/references/standard-fields.md +++ b/src/skills/bmad-workflow-builder/references/standard-fields.md @@ -16,7 +16,7 @@ |-------|-------------|---------| | `input-format` | What it accepts | JSON file path, stdin text | | `output-format` | What it returns | Validated JSON, error report | -| `standalone` | Opts out of bmad-init? | true/false | +| `standalone` | Fully standalone, no config needed? | true/false | | `composability` | How other skills use it | "Called by quality scanners for validation" | ## Simple Workflow Fields diff --git a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md index 2164061..9e73955 100644 --- a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md @@ -31,14 +31,6 @@ When building the workflow/skill, you MUST apply these conditional blocks to the - `{if-standalone}` ... `{/if-standalone}` → Keep the content inside - `{module-code-or-empty}` → Empty string -## bmad-init Conditional - -### Uses bmad-init (default) -- `{if-bmad-init}` ... `{/if-bmad-init}` → Keep the content inside - -### Opted out of bmad-init (standalone utilities only) -- `{if-bmad-init}` ... `{/if-bmad-init}` → Remove the entire block including markers - ## Feature Conditionals ### Headless Mode diff --git a/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py b/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py index cf02282..14cdbb8 100755 --- a/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py +++ b/src/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py @@ -6,6 +6,7 @@ Covers: - Dependency graph from skill structure + - Circular dependency detection - Transitive dependency redundancy - Parallelizable stage groups (independent nodes) From d12fc98b12fd37567d548f1282e0dd78bc9ec698 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 11:12:17 -0500 Subject: [PATCH 03/10] Rename bmad-builder-config to bmad-builder-setup with user config split and legacy migration Renames the skill directory and updates all references (display name, menu code, descriptions). Splits configuration into shared config.yaml and personal config.user.yaml for gitignored user settings. Adds legacy config migration that carries forward matching values then removes old files. Expands SKILL.md with argument-based headless mode, default priority chain, and an outcome section requiring consistent use of user name and communication language. Updates both merge scripts and their tests for the new flags and features. --- src/module-help.csv | 2 +- src/module.yaml | 2 +- src/skills/bmad-builder-config/SKILL.md | 41 -- .../bmad-builder-config/assets/module.yaml | 20 - .../scripts/merge-config.py | 185 ------ .../scripts/tests/test-merge-config.py | 222 ------- src/skills/bmad-builder-setup/SKILL.md | 57 ++ .../assets/module-help.csv | 2 +- .../bmad-builder-setup/assets/module.yaml | 20 + .../scripts/merge-config.py | 376 +++++++++++ .../scripts/merge-help-csv.py | 44 ++ .../scripts/tests/test-merge-config.py | 601 ++++++++++++++++++ .../scripts/tests/test-merge-help-csv.py | 50 ++ 13 files changed, 1151 insertions(+), 471 deletions(-) delete mode 100644 src/skills/bmad-builder-config/SKILL.md delete mode 100644 src/skills/bmad-builder-config/assets/module.yaml delete mode 100755 src/skills/bmad-builder-config/scripts/merge-config.py delete mode 100644 src/skills/bmad-builder-config/scripts/tests/test-merge-config.py create mode 100644 src/skills/bmad-builder-setup/SKILL.md rename src/skills/{bmad-builder-config => bmad-builder-setup}/assets/module-help.csv (78%) create mode 100644 src/skills/bmad-builder-setup/assets/module.yaml create mode 100755 src/skills/bmad-builder-setup/scripts/merge-config.py rename src/skills/{bmad-builder-config => bmad-builder-setup}/scripts/merge-help-csv.py (76%) create mode 100644 src/skills/bmad-builder-setup/scripts/tests/test-merge-config.py rename src/skills/{bmad-builder-config => bmad-builder-setup}/scripts/tests/test-merge-help-csv.py (74%) diff --git a/src/module-help.csv b/src/module-help.csv index 7c233a4..58a882a 100644 --- a/src/module-help.csv +++ b/src/module-help.csv @@ -1,5 +1,5 @@ module,agent-name,skill-name,display-name,menu-code,capability,args,description,phase,after,before,required,output-location,outputs, -bmb,,bmad-builder-config,Configure Builder,CB,configure,,"Install and configure BMad Builder module into a project.",anytime,,,false,,config, +bmb,,bmad-builder-setup,Setup Builder,SB,configure,,"Set up BMad Builder module in a project.",anytime,,,false,,config, bmb,,bmad-agent-builder,Build Agent,BA,build-process,,"Create, edit, convert, or fix an agent skill.",anytime,,bmad-agent-builder:quality-optimizer,false,output_folder,agent skill, bmb,,bmad-agent-builder,Optimize Agent,OA,quality-optimizer,,Validate and optimize an existing agent skill. Produces a quality report.,anytime,bmad-agent-builder:build-process,,false,bmad_builder_reports,quality report, bmb,,bmad-workflow-builder,Build Workflow,BW,build-process,,"Create, edit, convert, or fix a workflow or utility skill.",anytime,,bmad-workflow-builder:quality-optimizer,false,output_folder,workflow skill, diff --git a/src/module.yaml b/src/module.yaml index 4003333..52dcde3 100644 --- a/src/module.yaml +++ b/src/module.yaml @@ -15,6 +15,6 @@ bmad_builder_output_folder: result: "{project-root}/{value}" bmad_builder_reports: - prompt: "Output for Evals, Test, Quality and Planning Reports?" + prompt: "Report Output for Evals, Tests, Quality and Planning Reports?" default: "_bmad-output/reports" result: "{project-root}/{value}" diff --git a/src/skills/bmad-builder-config/SKILL.md b/src/skills/bmad-builder-config/SKILL.md deleted file mode 100644 index f93e3f5..0000000 --- a/src/skills/bmad-builder-config/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: bmad-builder-config -description: Installs BMad Builder module into a project. Use when the user requests to 'install bmb module', 'configure bmad builder', or 'setup bmad builder'. ---- - -# BMad Builder Config - -## Overview - -Installs and configures the BMad Builder (bmb) module into a project. Collects user preferences, writes them to a shared `{project-root}/_bmad/config.yaml`, and registers module capabilities in `{project-root}/_bmad/module-help.csv`. Both scripts use an anti-zombie pattern — existing entries for this module are removed before writing fresh ones, so stale values never persist. - -## On Activation - -1. Confirm `{project-root}` with the user — display the detected project root and verify before proceeding -2. Read `assets/module.yaml` for module metadata and variable definitions -3. Check if `{project-root}/_bmad/config.yaml` exists — if a `bmb` section is already present, inform the user this is an update - -## Collect Configuration - -Ask the user for values. Show defaults in brackets; accept blank for default. Pre-fill from existing config values when updating. - -**Core config** (only if no `core` section exists yet): `user_name` (required, no default), `communication_language`, `document_output_language`, `output_folder`. These are shared across all modules. - -**Module config**: Read each variable in `assets/module.yaml` that has a `prompt` field. Ask using that prompt with its `default` value. - -## Write Files - -Write a temp JSON file with the collected answers structured as `{"core": {...}, "module": {...}}` (omit `core` if it already exists). Then run both scripts — they can run in parallel since they write to different files: - -```bash -python3 scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --module-yaml assets/module.yaml --answers {temp-file} -python3 scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source assets/module-help.csv -``` - -Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. - -Run `scripts/merge-config.py --help` or `scripts/merge-help-csv.py --help` for full usage. - -## Confirm - -Use the script JSON output to display what was written — config values set, help entries added, fresh install vs update. diff --git a/src/skills/bmad-builder-config/assets/module.yaml b/src/skills/bmad-builder-config/assets/module.yaml deleted file mode 100644 index 4003333..0000000 --- a/src/skills/bmad-builder-config/assets/module.yaml +++ /dev/null @@ -1,20 +0,0 @@ -code: bmb -name: "BMad Builder" -description: "Standard Skill Compliant Factory for BMad Agents, Workflows and Modules" -default_selected: false - -# Variables from Core Config inserted: -## user_name -## communication_language -## document_output_language -## output_folder - -bmad_builder_output_folder: - prompt: "Where should your custom skills (agents and workflows) be saved?" - default: "_bmad-output/skills" - result: "{project-root}/{value}" - -bmad_builder_reports: - prompt: "Output for Evals, Test, Quality and Planning Reports?" - default: "_bmad-output/reports" - result: "{project-root}/{value}" diff --git a/src/skills/bmad-builder-config/scripts/merge-config.py b/src/skills/bmad-builder-config/scripts/merge-config.py deleted file mode 100755 index d66c0aa..0000000 --- a/src/skills/bmad-builder-config/scripts/merge-config.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.9" -# dependencies = ["pyyaml"] -# /// -"""Merge module configuration into shared _bmad/config.yaml. - -Reads a module.yaml definition and a JSON answers file, then writes or updates -the shared config.yaml. Uses an anti-zombie pattern: if the module code already -exists in config, that entire section is removed before writing fresh values. - -Exit codes: 0=success, 1=validation error, 2=runtime error -""" - -import argparse -import json -import sys -from pathlib import Path - -try: - import yaml -except ImportError: - print("Error: pyyaml is required (PEP 723 dependency)", file=sys.stderr) - sys.exit(2) - - -def parse_args(): - parser = argparse.ArgumentParser( - description="Merge module config into shared _bmad/config.yaml with anti-zombie pattern." - ) - parser.add_argument( - "--config-path", - required=True, - help="Path to the target _bmad/config.yaml file", - ) - parser.add_argument( - "--module-yaml", - required=True, - help="Path to the module.yaml definition file", - ) - parser.add_argument( - "--answers", - required=True, - help="Path to JSON file with collected answers", - ) - parser.add_argument( - "--verbose", - action="store_true", - help="Print detailed progress to stderr", - ) - return parser.parse_args() - - -def load_yaml_file(path: str) -> dict: - """Load a YAML file, returning empty dict if file doesn't exist.""" - file_path = Path(path) - if not file_path.exists(): - return {} - with open(file_path, "r", encoding="utf-8") as f: - content = yaml.safe_load(f) - return content if content else {} - - -def load_json_file(path: str) -> dict: - """Load a JSON file.""" - with open(path, "r", encoding="utf-8") as f: - return json.load(f) - - -def extract_module_metadata(module_yaml: dict) -> dict: - """Extract non-variable metadata fields from module.yaml.""" - metadata_keys = ["name", "description", "default_selected"] - return {k: module_yaml[k] for k in metadata_keys if k in module_yaml} - - -def merge_config( - existing_config: dict, - module_yaml: dict, - answers: dict, - verbose: bool = False, -) -> dict: - """Merge answers into config, applying anti-zombie pattern. - - Args: - existing_config: Current config.yaml contents (may be empty) - module_yaml: The module definition - answers: JSON with 'core' and/or 'module' keys - verbose: Print progress to stderr - - Returns: - Updated config dict ready to write - """ - config = dict(existing_config) - module_code = module_yaml.get("code") - - if not module_code: - print("Error: module.yaml must have a 'code' field", file=sys.stderr) - sys.exit(1) - - # Write core section if provided - core_answers = answers.get("core") - if core_answers: - if verbose: - print(f"Writing core config: {list(core_answers.keys())}", file=sys.stderr) - config["core"] = core_answers - - # Anti-zombie: remove existing module section - if module_code in config: - if verbose: - print( - f"Removing existing '{module_code}' section (anti-zombie)", - file=sys.stderr, - ) - del config[module_code] - - # Build module section: metadata + variable values - module_section = extract_module_metadata(module_yaml) - module_answers = answers.get("module", {}) - module_section.update(module_answers) - - if verbose: - print( - f"Writing '{module_code}' section with keys: {list(module_section.keys())}", - file=sys.stderr, - ) - - config[module_code] = module_section - - return config - - -def write_config(config: dict, config_path: str, verbose: bool = False) -> None: - """Write config dict to YAML file, creating parent dirs as needed.""" - path = Path(config_path) - path.parent.mkdir(parents=True, exist_ok=True) - - if verbose: - print(f"Writing config to {path}", file=sys.stderr) - - with open(path, "w", encoding="utf-8") as f: - yaml.dump( - config, - f, - default_flow_style=False, - allow_unicode=True, - sort_keys=False, - ) - - -def main(): - args = parse_args() - - # Load inputs - module_yaml = load_yaml_file(args.module_yaml) - if not module_yaml: - print(f"Error: Could not load module.yaml from {args.module_yaml}", file=sys.stderr) - sys.exit(1) - - answers = load_json_file(args.answers) - existing_config = load_yaml_file(args.config_path) - - if args.verbose: - exists = Path(args.config_path).exists() - print(f"Config file exists: {exists}", file=sys.stderr) - if exists: - print(f"Existing sections: {list(existing_config.keys())}", file=sys.stderr) - - # Merge and write - updated_config = merge_config(existing_config, module_yaml, answers, args.verbose) - write_config(updated_config, args.config_path, args.verbose) - - # Output result summary as JSON - module_code = module_yaml["code"] - result = { - "status": "success", - "config_path": str(Path(args.config_path).resolve()), - "module_code": module_code, - "core_updated": "core" in answers and bool(answers["core"]), - "module_keys": list(updated_config.get(module_code, {}).keys()), - } - print(json.dumps(result, indent=2)) - - -if __name__ == "__main__": - main() diff --git a/src/skills/bmad-builder-config/scripts/tests/test-merge-config.py b/src/skills/bmad-builder-config/scripts/tests/test-merge-config.py deleted file mode 100644 index 448a7e0..0000000 --- a/src/skills/bmad-builder-config/scripts/tests/test-merge-config.py +++ /dev/null @@ -1,222 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.9" -# dependencies = ["pyyaml"] -# /// -"""Unit tests for merge-config.py.""" - -import json -import os -import sys -import tempfile -import unittest -from pathlib import Path - -# Add parent directory to path so we can import the module -sys.path.insert(0, str(Path(__file__).parent.parent)) - -import yaml - -from importlib.util import spec_from_file_location, module_from_spec - -# Import merge_config module -_spec = spec_from_file_location( - "merge_config", - str(Path(__file__).parent.parent / "merge-config.py"), -) -merge_config_mod = module_from_spec(_spec) -_spec.loader.exec_module(merge_config_mod) - -extract_module_metadata = merge_config_mod.extract_module_metadata -merge_config = merge_config_mod.merge_config - - -SAMPLE_MODULE_YAML = { - "code": "bmb", - "name": "BMad Builder", - "description": "Standard Skill Compliant Factory", - "default_selected": False, - "bmad_builder_output_folder": { - "prompt": "Where should skills be saved?", - "default": "_bmad-output/skills", - "result": "{project-root}/{value}", - }, - "bmad_builder_reports": { - "prompt": "Output for reports?", - "default": "_bmad-output/reports", - "result": "{project-root}/{value}", - }, -} - - -class TestExtractModuleMetadata(unittest.TestCase): - def test_extracts_metadata_fields(self): - result = extract_module_metadata(SAMPLE_MODULE_YAML) - self.assertEqual(result["name"], "BMad Builder") - self.assertEqual(result["description"], "Standard Skill Compliant Factory") - self.assertFalse(result["default_selected"]) - - def test_excludes_variable_definitions(self): - result = extract_module_metadata(SAMPLE_MODULE_YAML) - self.assertNotIn("bmad_builder_output_folder", result) - self.assertNotIn("bmad_builder_reports", result) - self.assertNotIn("code", result) - - -class TestMergeConfig(unittest.TestCase): - def test_fresh_install_with_core_and_module(self): - answers = { - "core": { - "user_name": "Brian", - "communication_language": "English", - }, - "module": { - "bmad_builder_output_folder": "_bmad-output/skills", - }, - } - result = merge_config({}, SAMPLE_MODULE_YAML, answers) - - self.assertEqual(result["core"]["user_name"], "Brian") - self.assertEqual(result["bmb"]["name"], "BMad Builder") - self.assertEqual(result["bmb"]["bmad_builder_output_folder"], "_bmad-output/skills") - - def test_update_preserves_core(self): - existing = { - "core": {"user_name": "Brian", "communication_language": "English"}, - "other_module": {"name": "Other"}, - } - answers = { - "module": { - "bmad_builder_output_folder": "_bmad-output/skills", - }, - } - result = merge_config(existing, SAMPLE_MODULE_YAML, answers) - - # Core preserved - self.assertEqual(result["core"]["user_name"], "Brian") - # Other module preserved - self.assertIn("other_module", result) - # New module added - self.assertIn("bmb", result) - - def test_anti_zombie_removes_existing_module(self): - existing = { - "core": {"user_name": "Brian"}, - "bmb": { - "name": "BMad Builder", - "old_variable": "should_be_removed", - "bmad_builder_output_folder": "old/path", - }, - } - answers = { - "module": { - "bmad_builder_output_folder": "new/path", - }, - } - result = merge_config(existing, SAMPLE_MODULE_YAML, answers) - - # Old variable is gone - self.assertNotIn("old_variable", result["bmb"]) - # New value is present - self.assertEqual(result["bmb"]["bmad_builder_output_folder"], "new/path") - # Metadata is fresh from module.yaml - self.assertEqual(result["bmb"]["name"], "BMad Builder") - - def test_core_answers_override_existing(self): - existing = { - "core": {"user_name": "OldName", "communication_language": "Spanish"}, - } - answers = { - "core": {"user_name": "NewName", "communication_language": "English"}, - "module": {}, - } - result = merge_config(existing, SAMPLE_MODULE_YAML, answers) - - self.assertEqual(result["core"]["user_name"], "NewName") - self.assertEqual(result["core"]["communication_language"], "English") - - def test_no_core_answers_skips_core(self): - existing = { - "core": {"user_name": "Brian"}, - } - answers = { - "module": {"bmad_builder_output_folder": "path"}, - } - result = merge_config(existing, SAMPLE_MODULE_YAML, answers) - - # Core unchanged - self.assertEqual(result["core"]["user_name"], "Brian") - - def test_module_metadata_always_from_yaml(self): - """Module metadata comes from module.yaml, not answers.""" - answers = { - "module": {"bmad_builder_output_folder": "path"}, - } - result = merge_config({}, SAMPLE_MODULE_YAML, answers) - - self.assertEqual(result["bmb"]["name"], "BMad Builder") - self.assertEqual(result["bmb"]["description"], "Standard Skill Compliant Factory") - self.assertFalse(result["bmb"]["default_selected"]) - - -class TestEndToEnd(unittest.TestCase): - def test_write_and_read_round_trip(self): - with tempfile.TemporaryDirectory() as tmpdir: - config_path = os.path.join(tmpdir, "_bmad", "config.yaml") - answers_path = os.path.join(tmpdir, "answers.json") - module_path = os.path.join(tmpdir, "module.yaml") - - # Write module.yaml - with open(module_path, "w") as f: - yaml.dump(SAMPLE_MODULE_YAML, f) - - # Write answers - answers = { - "core": {"user_name": "Brian", "communication_language": "English"}, - "module": {"bmad_builder_output_folder": "_bmad-output/skills"}, - } - with open(answers_path, "w") as f: - json.dump(answers, f) - - # Run merge - result = merge_config({}, SAMPLE_MODULE_YAML, answers) - merge_config_mod.write_config(result, config_path) - - # Read back - with open(config_path, "r") as f: - written = yaml.safe_load(f) - - self.assertEqual(written["core"]["user_name"], "Brian") - self.assertEqual(written["bmb"]["bmad_builder_output_folder"], "_bmad-output/skills") - - def test_update_round_trip(self): - """Simulate install, then re-install with different values.""" - with tempfile.TemporaryDirectory() as tmpdir: - config_path = os.path.join(tmpdir, "config.yaml") - - # First install - answers1 = { - "core": {"user_name": "Brian"}, - "module": {"bmad_builder_output_folder": "old/path"}, - } - result1 = merge_config({}, SAMPLE_MODULE_YAML, answers1) - merge_config_mod.write_config(result1, config_path) - - # Second install (update) - existing = merge_config_mod.load_yaml_file(config_path) - answers2 = { - "module": {"bmad_builder_output_folder": "new/path"}, - } - result2 = merge_config(existing, SAMPLE_MODULE_YAML, answers2) - merge_config_mod.write_config(result2, config_path) - - # Verify - with open(config_path, "r") as f: - final = yaml.safe_load(f) - - self.assertEqual(final["core"]["user_name"], "Brian") - self.assertEqual(final["bmb"]["bmad_builder_output_folder"], "new/path") - - -if __name__ == "__main__": - unittest.main() diff --git a/src/skills/bmad-builder-setup/SKILL.md b/src/skills/bmad-builder-setup/SKILL.md new file mode 100644 index 0000000..4365ced --- /dev/null +++ b/src/skills/bmad-builder-setup/SKILL.md @@ -0,0 +1,57 @@ +--- +name: bmad-builder-setup +description: Sets up BMad Builder module in a project. Use when the user requests to 'install bmb module', 'configure bmad builder', or 'setup bmad builder'. +--- + +# Module Setup + +## Overview + +Installs and configures a BMad module into a project. Module identity (name, code, version) comes from `assets/module.yaml`. Collects user preferences and writes them to three files: + +- **`{project-root}/_bmad/config.yaml`** — shared project config: core settings at root (e.g. `output_folder`, `document_output_language`) plus a section per module with metadata and module-specific values. User-only keys (`user_name`, `communication_language`) are **never** written here. +- **`{project-root}/_bmad/config.user.yaml`** — personal settings intended to be gitignored: `user_name`, `communication_language`, and any module variable marked `user_setting: true` in `assets/module.yaml`. These values live exclusively here. +- **`{project-root}/_bmad/module-help.csv`** — registers module capabilities for the help system. + +Both config scripts use an anti-zombie pattern — existing entries for this module are removed before writing fresh ones, so stale values never persist. + +`{project-root}` is a **literal token** in config values — never substitute it with an actual path. It signals to the consuming LLM that the value is relative to the project root, not the skill root. + +## On Activation + +1. Read `./assets/module.yaml` for module metadata and variable definitions (the `code` field is the module identifier) +2. Check if `{project-root}/_bmad/config.yaml` exists — if a section matching the module's code is already present, inform the user this is an update +3. Check for legacy configuration at `{project-root}/_bmad/{module-code}/config.yaml` and `{project-root}/_bmad/core/config.yaml`. If either file exists, inform the user that legacy values will be migrated and the old files removed after setup + +If the user provides arguments (e.g. `accept all defaults`, `--headless`, or inline values like `user name is BMad, I speak Swahili`), map any provided values to config keys, use defaults for the rest, and skip interactive prompting. Still display the full confirmation summary at the end. + +## Collect Configuration + +Ask the user for values. Show defaults in brackets. Present all values together so the user can respond once with only the values they want to change (e.g. "change language to Swahili, rest are fine"). Use a structured question tool if available. Never tell the user to "press enter" or "leave blank" — in a chat interface they must type something to respond. + +**Default priority** (highest wins): existing new config values > legacy config values > `assets/module.yaml` defaults. When legacy configs exist, read them and use matching values as defaults instead of `module.yaml` defaults. Only keys that match the current schema are carried forward — changed or removed keys are ignored. + +**Core config** (only if no core keys exist yet): `user_name` (default: BMad), `communication_language` (default: English), `document_output_language` (default: English), `output_folder` (default: `{project-root}/_bmad-output`). Of these, `user_name` and `communication_language` are written exclusively to `config.user.yaml`. The rest go to `config.yaml` at root and are shared across all modules. + +**Module config**: Read each variable in `assets/module.yaml` that has a `prompt` field. Ask using that prompt with its default value (or legacy value if available). + +## Write Files + +Write a temp JSON file with the collected answers structured as `{"core": {...}, "module": {...}}` (omit `core` if it already exists). Then run both scripts — they can run in parallel since they write to different files: + +```bash +python3 ./scripts/merge-config.py --config-path "{project-root}/_bmad/config.yaml" --user-config-path "{project-root}/_bmad/config.user.yaml" --module-yaml assets/module.yaml --answers {temp-file} --legacy-dir "{project-root}/_bmad" +python3 ./scripts/merge-help-csv.py --target "{project-root}/_bmad/module-help.csv" --source assets/module-help.csv --legacy-dir "{project-root}/_bmad" --module-code {module-code} +``` + +Both scripts output JSON to stdout with results. If either exits non-zero, surface the error and stop. The scripts automatically read legacy config values as fallback defaults, then delete the legacy files after a successful merge. Check `legacy_configs_deleted` and `legacy_csvs_deleted` in the output to confirm cleanup. + +Run `./scripts/merge-config.py --help` or `scripts/merge-help-csv.py --help` for full usage. + +## Confirm + +Use the script JSON output to display what was written — config values set (written to `config.yaml` at root for core, module section for module values), user settings written to `config.user.yaml` (`user_keys` in result), help entries added, fresh install vs update. If legacy files were deleted, mention the migration. Then display the `module_greeting` from `assets/module.yaml` to the user. + +## Outcome + +Once the user's `user_name` and `communication_language` are known (from collected input, arguments, or existing config), use them consistently for the remainder of the session: address the user by their configured name and communicate in their configured `communication_language`. diff --git a/src/skills/bmad-builder-config/assets/module-help.csv b/src/skills/bmad-builder-setup/assets/module-help.csv similarity index 78% rename from src/skills/bmad-builder-config/assets/module-help.csv rename to src/skills/bmad-builder-setup/assets/module-help.csv index 7c233a4..d5e63d8 100644 --- a/src/skills/bmad-builder-config/assets/module-help.csv +++ b/src/skills/bmad-builder-setup/assets/module-help.csv @@ -1,5 +1,5 @@ module,agent-name,skill-name,display-name,menu-code,capability,args,description,phase,after,before,required,output-location,outputs, -bmb,,bmad-builder-config,Configure Builder,CB,configure,,"Install and configure BMad Builder module into a project.",anytime,,,false,,config, +bmb,,bmad-builder-setup,Setup Builder,SB,configure,,"Install or update BMad Builder module config and help entries. Collects user preferences, writes config.yaml, and migrates legacy configs.",anytime,,,false,{project-root}/_bmad,config.yaml and config.user.yaml, bmb,,bmad-agent-builder,Build Agent,BA,build-process,,"Create, edit, convert, or fix an agent skill.",anytime,,bmad-agent-builder:quality-optimizer,false,output_folder,agent skill, bmb,,bmad-agent-builder,Optimize Agent,OA,quality-optimizer,,Validate and optimize an existing agent skill. Produces a quality report.,anytime,bmad-agent-builder:build-process,,false,bmad_builder_reports,quality report, bmb,,bmad-workflow-builder,Build Workflow,BW,build-process,,"Create, edit, convert, or fix a workflow or utility skill.",anytime,,bmad-workflow-builder:quality-optimizer,false,output_folder,workflow skill, diff --git a/src/skills/bmad-builder-setup/assets/module.yaml b/src/skills/bmad-builder-setup/assets/module.yaml new file mode 100644 index 0000000..5bb47ef --- /dev/null +++ b/src/skills/bmad-builder-setup/assets/module.yaml @@ -0,0 +1,20 @@ +code: bmb +name: "BMad Builder" +description: "Standard Skill Compliant Factory for BMad Agents, Workflows and Modules" +module_version: 1.0.0 +default_selected: false +module_greeting: > + Enjoy making your dream creations with the BMad Builder Module! + Run this again at any time if you want to reconfigure a setting or have updated the module, (or optionally just update _bmad/config.yaml and config.user.yaml to change existing values) + + For questions, suggestions and support - check us on Discord at https://discord.gg/gk8jAdXWmj + +bmad_builder_output_folder: + prompt: "Where should your custom output (agent, workflow, module config) be saved?" + default: "{project-root}/_bmad-output/skills" + result: "{project-root}/{value}" + +bmad_builder_reports: + prompt: "Output for Evals, Test, Quality and Planning Reports?" + default: "{project-root}/_bmad-output/reports" + result: "{project-root}/{value}" diff --git a/src/skills/bmad-builder-setup/scripts/merge-config.py b/src/skills/bmad-builder-setup/scripts/merge-config.py new file mode 100755 index 0000000..5089cbc --- /dev/null +++ b/src/skills/bmad-builder-setup/scripts/merge-config.py @@ -0,0 +1,376 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.9" +# dependencies = ["pyyaml"] +# /// +"""Merge module configuration into shared _bmad/config.yaml and config.user.yaml. + +Reads a module.yaml definition and a JSON answers file, then writes or updates +the shared config.yaml (core values at root + module section) and config.user.yaml +(user_name, communication_language, plus any module variable with user_setting: true). +Uses an anti-zombie pattern for the module section in config.yaml. + +Legacy migration: when --legacy-dir is provided, reads old per-module config files +from {legacy-dir}/{module-code}/config.yaml and {legacy-dir}/core/config.yaml. +Matching values serve as fallback defaults (answers override them). After a +successful merge, the legacy config.yaml files are deleted. Only the current +module and core directories are touched — other module directories are left alone. + +Exit codes: 0=success, 1=validation error, 2=runtime error +""" + +import argparse +import json +import sys +from pathlib import Path + +try: + import yaml +except ImportError: + print("Error: pyyaml is required (PEP 723 dependency)", file=sys.stderr) + sys.exit(2) + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Merge module config into shared _bmad/config.yaml with anti-zombie pattern." + ) + parser.add_argument( + "--config-path", + required=True, + help="Path to the target _bmad/config.yaml file", + ) + parser.add_argument( + "--module-yaml", + required=True, + help="Path to the module.yaml definition file", + ) + parser.add_argument( + "--answers", + required=True, + help="Path to JSON file with collected answers", + ) + parser.add_argument( + "--user-config-path", + required=True, + help="Path to the target _bmad/config.user.yaml file", + ) + parser.add_argument( + "--legacy-dir", + help="Path to _bmad/ directory to check for legacy per-module config files. " + "Matching values are used as fallback defaults, then legacy files are deleted.", + ) + parser.add_argument( + "--verbose", + action="store_true", + help="Print detailed progress to stderr", + ) + return parser.parse_args() + + +def load_yaml_file(path: str) -> dict: + """Load a YAML file, returning empty dict if file doesn't exist.""" + file_path = Path(path) + if not file_path.exists(): + return {} + with open(file_path, "r", encoding="utf-8") as f: + content = yaml.safe_load(f) + return content if content else {} + + +def load_json_file(path: str) -> dict: + """Load a JSON file.""" + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +# Keys that live at config root (shared across all modules) +_CORE_KEYS = frozenset( + {"user_name", "communication_language", "document_output_language", "output_folder"} +) + + +def load_legacy_values( + legacy_dir: str, module_code: str, module_yaml: dict, verbose: bool = False +) -> tuple[dict, dict, list]: + """Read legacy per-module config files and return core/module value dicts. + + Reads {legacy_dir}/core/config.yaml and {legacy_dir}/{module_code}/config.yaml. + Only returns values whose keys match the current schema (core keys or module.yaml + variable definitions). Other modules' directories are not touched. + + Returns: + (legacy_core, legacy_module, files_found) where files_found lists paths read. + """ + legacy_core: dict = {} + legacy_module: dict = {} + files_found: list = [] + + # Read core legacy config + core_path = Path(legacy_dir) / "core" / "config.yaml" + if core_path.exists(): + core_data = load_yaml_file(str(core_path)) + files_found.append(str(core_path)) + for k, v in core_data.items(): + if k in _CORE_KEYS: + legacy_core[k] = v + if verbose: + print(f"Legacy core config: {list(legacy_core.keys())}", file=sys.stderr) + + # Read module legacy config + mod_path = Path(legacy_dir) / module_code / "config.yaml" + if mod_path.exists(): + mod_data = load_yaml_file(str(mod_path)) + files_found.append(str(mod_path)) + for k, v in mod_data.items(): + if k in _CORE_KEYS: + # Core keys duplicated in module config — only use if not already set + if k not in legacy_core: + legacy_core[k] = v + elif k in module_yaml and isinstance(module_yaml[k], dict): + # Module-specific key that matches a current variable definition + legacy_module[k] = v + if verbose: + print( + f"Legacy module config: {list(legacy_module.keys())}", file=sys.stderr + ) + + return legacy_core, legacy_module, files_found + + +def apply_legacy_defaults(answers: dict, legacy_core: dict, legacy_module: dict) -> dict: + """Apply legacy values as fallback defaults under the answers. + + Legacy values fill in any key not already present in answers. + Explicit answers always win. + """ + merged = dict(answers) + + if legacy_core: + core = merged.get("core", {}) + filled_core = dict(legacy_core) # legacy as base + filled_core.update(core) # answers override + merged["core"] = filled_core + + if legacy_module: + mod = merged.get("module", {}) + filled_mod = dict(legacy_module) # legacy as base + filled_mod.update(mod) # answers override + merged["module"] = filled_mod + + return merged + + +def cleanup_legacy_configs( + legacy_dir: str, module_code: str, verbose: bool = False +) -> list: + """Delete legacy config.yaml files for this module and core only. + + Returns list of deleted file paths. + """ + deleted = [] + for subdir in (module_code, "core"): + legacy_path = Path(legacy_dir) / subdir / "config.yaml" + if legacy_path.exists(): + if verbose: + print(f"Deleting legacy config: {legacy_path}", file=sys.stderr) + legacy_path.unlink() + deleted.append(str(legacy_path)) + return deleted + + +def extract_module_metadata(module_yaml: dict) -> dict: + """Extract non-variable metadata fields from module.yaml.""" + meta = {} + for k in ("name", "description"): + if k in module_yaml: + meta[k] = module_yaml[k] + meta["version"] = module_yaml.get("module_version") # null if absent + if "default_selected" in module_yaml: + meta["default_selected"] = module_yaml["default_selected"] + return meta + + +def merge_config( + existing_config: dict, + module_yaml: dict, + answers: dict, + verbose: bool = False, +) -> dict: + """Merge answers into config, applying anti-zombie pattern. + + Args: + existing_config: Current config.yaml contents (may be empty) + module_yaml: The module definition + answers: JSON with 'core' and/or 'module' keys + verbose: Print progress to stderr + + Returns: + Updated config dict ready to write + """ + config = dict(existing_config) + module_code = module_yaml.get("code") + + if not module_code: + print("Error: module.yaml must have a 'code' field", file=sys.stderr) + sys.exit(1) + + # Migrate legacy core: section to root + if "core" in config and isinstance(config["core"], dict): + if verbose: + print("Migrating legacy 'core' section to root", file=sys.stderr) + config.update(config.pop("core")) + + # Strip user-only keys from config — they belong exclusively in config.user.yaml + for key in _CORE_USER_KEYS: + if key in config: + if verbose: + print(f"Removing user-only key '{key}' from config (belongs in config.user.yaml)", file=sys.stderr) + del config[key] + + # Write core values at root (global properties, not nested under "core") + # Exclude user-only keys — those belong exclusively in config.user.yaml + core_answers = answers.get("core") + if core_answers: + shared_core = {k: v for k, v in core_answers.items() if k not in _CORE_USER_KEYS} + if shared_core: + if verbose: + print(f"Writing core config at root: {list(shared_core.keys())}", file=sys.stderr) + config.update(shared_core) + + # Anti-zombie: remove existing module section + if module_code in config: + if verbose: + print( + f"Removing existing '{module_code}' section (anti-zombie)", + file=sys.stderr, + ) + del config[module_code] + + # Build module section: metadata + variable values + module_section = extract_module_metadata(module_yaml) + module_answers = answers.get("module", {}) + module_section.update(module_answers) + + if verbose: + print( + f"Writing '{module_code}' section with keys: {list(module_section.keys())}", + file=sys.stderr, + ) + + config[module_code] = module_section + + return config + + +# Core keys that are always written to config.user.yaml +_CORE_USER_KEYS = ("user_name", "communication_language") + + +def extract_user_settings(module_yaml: dict, answers: dict) -> dict: + """Collect settings that belong in config.user.yaml. + + Includes user_name and communication_language from core answers, plus any + module variable whose definition contains user_setting: true. + """ + user_settings = {} + + core_answers = answers.get("core", {}) + for key in _CORE_USER_KEYS: + if key in core_answers: + user_settings[key] = core_answers[key] + + module_answers = answers.get("module", {}) + for var_name, var_def in module_yaml.items(): + if isinstance(var_def, dict) and var_def.get("user_setting") is True: + if var_name in module_answers: + user_settings[var_name] = module_answers[var_name] + + return user_settings + + +def write_config(config: dict, config_path: str, verbose: bool = False) -> None: + """Write config dict to YAML file, creating parent dirs as needed.""" + path = Path(config_path) + path.parent.mkdir(parents=True, exist_ok=True) + + if verbose: + print(f"Writing config to {path}", file=sys.stderr) + + with open(path, "w", encoding="utf-8") as f: + yaml.dump( + config, + f, + default_flow_style=False, + allow_unicode=True, + sort_keys=False, + ) + + +def main(): + args = parse_args() + + # Load inputs + module_yaml = load_yaml_file(args.module_yaml) + if not module_yaml: + print(f"Error: Could not load module.yaml from {args.module_yaml}", file=sys.stderr) + sys.exit(1) + + answers = load_json_file(args.answers) + existing_config = load_yaml_file(args.config_path) + + if args.verbose: + exists = Path(args.config_path).exists() + print(f"Config file exists: {exists}", file=sys.stderr) + if exists: + print(f"Existing sections: {list(existing_config.keys())}", file=sys.stderr) + + # Legacy migration: read old per-module configs as fallback defaults + legacy_files_found = [] + if args.legacy_dir: + module_code = module_yaml.get("code", "") + legacy_core, legacy_module, legacy_files_found = load_legacy_values( + args.legacy_dir, module_code, module_yaml, args.verbose + ) + if legacy_core or legacy_module: + answers = apply_legacy_defaults(answers, legacy_core, legacy_module) + if args.verbose: + print("Applied legacy values as fallback defaults", file=sys.stderr) + + # Merge and write config.yaml + updated_config = merge_config(existing_config, module_yaml, answers, args.verbose) + write_config(updated_config, args.config_path, args.verbose) + + # Merge and write config.user.yaml + user_settings = extract_user_settings(module_yaml, answers) + existing_user_config = load_yaml_file(args.user_config_path) + updated_user_config = dict(existing_user_config) + updated_user_config.update(user_settings) + if user_settings: + write_config(updated_user_config, args.user_config_path, args.verbose) + + # Legacy cleanup: delete old per-module config files + legacy_deleted = [] + if args.legacy_dir: + legacy_deleted = cleanup_legacy_configs( + args.legacy_dir, module_yaml["code"], args.verbose + ) + + # Output result summary as JSON + module_code = module_yaml["code"] + result = { + "status": "success", + "config_path": str(Path(args.config_path).resolve()), + "user_config_path": str(Path(args.user_config_path).resolve()), + "module_code": module_code, + "core_updated": bool(answers.get("core")), + "module_keys": list(updated_config.get(module_code, {}).keys()), + "user_keys": list(user_settings.keys()), + "legacy_configs_found": legacy_files_found, + "legacy_configs_deleted": legacy_deleted, + } + print(json.dumps(result, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/skills/bmad-builder-config/scripts/merge-help-csv.py b/src/skills/bmad-builder-setup/scripts/merge-help-csv.py similarity index 76% rename from src/skills/bmad-builder-config/scripts/merge-help-csv.py rename to src/skills/bmad-builder-setup/scripts/merge-help-csv.py index 80643e5..04469ef 100755 --- a/src/skills/bmad-builder-config/scripts/merge-help-csv.py +++ b/src/skills/bmad-builder-setup/scripts/merge-help-csv.py @@ -9,6 +9,10 @@ Uses an anti-zombie pattern: all existing rows matching the source module code are removed before appending fresh rows. +Legacy cleanup: when --legacy-dir and --module-code are provided, deletes old +per-module module-help.csv files from {legacy-dir}/{module-code}/ and +{legacy-dir}/core/. Only the current module and core are touched. + Exit codes: 0=success, 1=validation error, 2=runtime error """ @@ -53,6 +57,14 @@ def parse_args(): required=True, help="Path to the source module-help.csv with entries to merge", ) + parser.add_argument( + "--legacy-dir", + help="Path to _bmad/ directory to check for legacy per-module CSV files.", + ) + parser.add_argument( + "--module-code", + help="Module code (required with --legacy-dir for scoping cleanup).", + ) parser.add_argument( "--verbose", action="store_true", @@ -111,6 +123,24 @@ def write_csv(path: str, header: list[str], rows: list[list[str]], verbose: bool writer.writerow(row) +def cleanup_legacy_csvs( + legacy_dir: str, module_code: str, verbose: bool = False +) -> list: + """Delete legacy per-module module-help.csv files for this module and core only. + + Returns list of deleted file paths. + """ + deleted = [] + for subdir in (module_code, "core"): + legacy_path = Path(legacy_dir) / subdir / "module-help.csv" + if legacy_path.exists(): + if verbose: + print(f"Deleting legacy CSV: {legacy_path}", file=sys.stderr) + legacy_path.unlink() + deleted.append(str(legacy_path)) + return deleted + + def main(): args = parse_args() @@ -159,6 +189,19 @@ def main(): # Write result write_csv(args.target, header, merged_rows, args.verbose) + # Legacy cleanup: delete old per-module CSV files + legacy_deleted = [] + if args.legacy_dir: + if not args.module_code: + print( + "Error: --module-code is required when --legacy-dir is provided", + file=sys.stderr, + ) + sys.exit(1) + legacy_deleted = cleanup_legacy_csvs( + args.legacy_dir, args.module_code, args.verbose + ) + # Output result summary as JSON result = { "status": "success", @@ -168,6 +211,7 @@ def main(): "rows_removed": removed_count, "rows_added": len(source_rows), "total_rows": len(merged_rows), + "legacy_csvs_deleted": legacy_deleted, } print(json.dumps(result, indent=2)) diff --git a/src/skills/bmad-builder-setup/scripts/tests/test-merge-config.py b/src/skills/bmad-builder-setup/scripts/tests/test-merge-config.py new file mode 100644 index 0000000..b76ca81 --- /dev/null +++ b/src/skills/bmad-builder-setup/scripts/tests/test-merge-config.py @@ -0,0 +1,601 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.9" +# dependencies = ["pyyaml"] +# /// +"""Unit tests for merge-config.py.""" + +import json +import os +import sys +import tempfile +import unittest +from pathlib import Path + +# Add parent directory to path so we can import the module +sys.path.insert(0, str(Path(__file__).parent.parent)) + +import yaml + +from importlib.util import spec_from_file_location, module_from_spec + +# Import merge_config module +_spec = spec_from_file_location( + "merge_config", + str(Path(__file__).parent.parent / "merge-config.py"), +) +merge_config_mod = module_from_spec(_spec) +_spec.loader.exec_module(merge_config_mod) + +extract_module_metadata = merge_config_mod.extract_module_metadata +extract_user_settings = merge_config_mod.extract_user_settings +merge_config = merge_config_mod.merge_config +load_legacy_values = merge_config_mod.load_legacy_values +apply_legacy_defaults = merge_config_mod.apply_legacy_defaults +cleanup_legacy_configs = merge_config_mod.cleanup_legacy_configs + + +SAMPLE_MODULE_YAML = { + "code": "bmb", + "name": "BMad Builder", + "description": "Standard Skill Compliant Factory", + "default_selected": False, + "bmad_builder_output_folder": { + "prompt": "Where should skills be saved?", + "default": "_bmad-output/skills", + "result": "{project-root}/{value}", + }, + "bmad_builder_reports": { + "prompt": "Output for reports?", + "default": "_bmad-output/reports", + "result": "{project-root}/{value}", + }, +} + +SAMPLE_MODULE_YAML_WITH_VERSION = { + **SAMPLE_MODULE_YAML, + "module_version": "1.0.0", +} + +SAMPLE_MODULE_YAML_WITH_USER_SETTING = { + **SAMPLE_MODULE_YAML, + "some_pref": { + "prompt": "Your preference?", + "default": "default_val", + "user_setting": True, + }, +} + + +class TestExtractModuleMetadata(unittest.TestCase): + def test_extracts_metadata_fields(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML) + self.assertEqual(result["name"], "BMad Builder") + self.assertEqual(result["description"], "Standard Skill Compliant Factory") + self.assertFalse(result["default_selected"]) + + def test_excludes_variable_definitions(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML) + self.assertNotIn("bmad_builder_output_folder", result) + self.assertNotIn("bmad_builder_reports", result) + self.assertNotIn("code", result) + + def test_version_present(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML_WITH_VERSION) + self.assertEqual(result["version"], "1.0.0") + + def test_version_absent_is_none(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML) + self.assertIn("version", result) + self.assertIsNone(result["version"]) + + def test_field_order(self): + result = extract_module_metadata(SAMPLE_MODULE_YAML_WITH_VERSION) + keys = list(result.keys()) + self.assertEqual(keys, ["name", "description", "version", "default_selected"]) + + +class TestExtractUserSettings(unittest.TestCase): + def test_core_user_keys(self): + answers = { + "core": { + "user_name": "Brian", + "communication_language": "English", + "document_output_language": "English", + "output_folder": "_bmad-output", + }, + } + result = extract_user_settings(SAMPLE_MODULE_YAML, answers) + self.assertEqual(result["user_name"], "Brian") + self.assertEqual(result["communication_language"], "English") + self.assertNotIn("document_output_language", result) + self.assertNotIn("output_folder", result) + + def test_module_user_setting_true(self): + answers = { + "core": {"user_name": "Brian"}, + "module": {"some_pref": "custom_val"}, + } + result = extract_user_settings(SAMPLE_MODULE_YAML_WITH_USER_SETTING, answers) + self.assertEqual(result["user_name"], "Brian") + self.assertEqual(result["some_pref"], "custom_val") + + def test_no_core_answers(self): + answers = {"module": {"some_pref": "val"}} + result = extract_user_settings(SAMPLE_MODULE_YAML_WITH_USER_SETTING, answers) + self.assertNotIn("user_name", result) + self.assertEqual(result["some_pref"], "val") + + def test_no_user_settings_in_module(self): + answers = { + "core": {"user_name": "Brian"}, + "module": {"bmad_builder_output_folder": "path"}, + } + result = extract_user_settings(SAMPLE_MODULE_YAML, answers) + self.assertEqual(result, {"user_name": "Brian"}) + + def test_empty_answers(self): + result = extract_user_settings(SAMPLE_MODULE_YAML, {}) + self.assertEqual(result, {}) + + +class TestMergeConfig(unittest.TestCase): + def test_fresh_install_with_core_and_module(self): + answers = { + "core": { + "user_name": "Brian", + "communication_language": "English", + "document_output_language": "English", + "output_folder": "_bmad-output", + }, + "module": { + "bmad_builder_output_folder": "_bmad-output/skills", + }, + } + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + + # User-only keys must NOT appear in config.yaml + self.assertNotIn("user_name", result) + self.assertNotIn("communication_language", result) + # Shared core keys do appear + self.assertEqual(result["document_output_language"], "English") + self.assertEqual(result["output_folder"], "_bmad-output") + self.assertEqual(result["bmb"]["name"], "BMad Builder") + self.assertEqual(result["bmb"]["bmad_builder_output_folder"], "_bmad-output/skills") + + def test_update_strips_user_keys_preserves_shared(self): + existing = { + "user_name": "Brian", + "communication_language": "English", + "document_output_language": "English", + "other_module": {"name": "Other"}, + } + answers = { + "module": { + "bmad_builder_output_folder": "_bmad-output/skills", + }, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # User-only keys stripped from config + self.assertNotIn("user_name", result) + self.assertNotIn("communication_language", result) + # Shared core preserved at root + self.assertEqual(result["document_output_language"], "English") + # Other module preserved + self.assertIn("other_module", result) + # New module added + self.assertIn("bmb", result) + + def test_anti_zombie_removes_existing_module(self): + existing = { + "user_name": "Brian", + "bmb": { + "name": "BMad Builder", + "old_variable": "should_be_removed", + "bmad_builder_output_folder": "old/path", + }, + } + answers = { + "module": { + "bmad_builder_output_folder": "new/path", + }, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # Old variable is gone + self.assertNotIn("old_variable", result["bmb"]) + # New value is present + self.assertEqual(result["bmb"]["bmad_builder_output_folder"], "new/path") + # Metadata is fresh from module.yaml + self.assertEqual(result["bmb"]["name"], "BMad Builder") + + def test_user_keys_never_written_to_config(self): + existing = { + "user_name": "OldName", + "communication_language": "Spanish", + "document_output_language": "French", + } + answers = { + "core": {"user_name": "NewName", "communication_language": "English"}, + "module": {}, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # User-only keys stripped even if they were in existing config + self.assertNotIn("user_name", result) + self.assertNotIn("communication_language", result) + # Shared core preserved + self.assertEqual(result["document_output_language"], "French") + + def test_no_core_answers_still_strips_user_keys(self): + existing = { + "user_name": "Brian", + "output_folder": "/out", + } + answers = { + "module": {"bmad_builder_output_folder": "path"}, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # User-only keys stripped even without core answers + self.assertNotIn("user_name", result) + # Shared core unchanged + self.assertEqual(result["output_folder"], "/out") + + def test_module_metadata_always_from_yaml(self): + """Module metadata comes from module.yaml, not answers.""" + answers = { + "module": {"bmad_builder_output_folder": "path"}, + } + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + + self.assertEqual(result["bmb"]["name"], "BMad Builder") + self.assertEqual(result["bmb"]["description"], "Standard Skill Compliant Factory") + self.assertFalse(result["bmb"]["default_selected"]) + + def test_legacy_core_section_migrated_user_keys_stripped(self): + """Old config with core: nested section — user keys stripped after migration.""" + existing = { + "core": { + "user_name": "Brian", + "communication_language": "English", + "document_output_language": "English", + "output_folder": "/out", + }, + "bmb": {"name": "BMad Builder"}, + } + answers = { + "module": {"bmad_builder_output_folder": "path"}, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # User-only keys stripped after migration + self.assertNotIn("user_name", result) + self.assertNotIn("communication_language", result) + # Shared core values hoisted to root + self.assertEqual(result["document_output_language"], "English") + self.assertEqual(result["output_folder"], "/out") + # Legacy core key removed + self.assertNotIn("core", result) + # Module still works + self.assertIn("bmb", result) + + def test_legacy_core_user_keys_stripped_after_migration(self): + """Legacy core: values get migrated, user keys stripped, shared keys kept.""" + existing = { + "core": {"user_name": "OldName", "output_folder": "/old"}, + } + answers = { + "core": {"user_name": "NewName", "output_folder": "/new"}, + "module": {}, + } + result = merge_config(existing, SAMPLE_MODULE_YAML, answers) + + # User-only key not in config even after migration + override + self.assertNotIn("user_name", result) + self.assertNotIn("core", result) + # Shared core key written + self.assertEqual(result["output_folder"], "/new") + + +class TestEndToEnd(unittest.TestCase): + def test_write_and_read_round_trip(self): + with tempfile.TemporaryDirectory() as tmpdir: + config_path = os.path.join(tmpdir, "_bmad", "config.yaml") + + # Write answers + answers = { + "core": { + "user_name": "Brian", + "communication_language": "English", + "document_output_language": "English", + "output_folder": "_bmad-output", + }, + "module": {"bmad_builder_output_folder": "_bmad-output/skills"}, + } + + # Run merge + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + merge_config_mod.write_config(result, config_path) + + # Read back + with open(config_path, "r") as f: + written = yaml.safe_load(f) + + # User-only keys not written to config.yaml + self.assertNotIn("user_name", written) + self.assertNotIn("communication_language", written) + # Shared core keys written + self.assertEqual(written["document_output_language"], "English") + self.assertEqual(written["output_folder"], "_bmad-output") + self.assertEqual(written["bmb"]["bmad_builder_output_folder"], "_bmad-output/skills") + + def test_update_round_trip(self): + """Simulate install, then re-install with different values.""" + with tempfile.TemporaryDirectory() as tmpdir: + config_path = os.path.join(tmpdir, "config.yaml") + + # First install + answers1 = { + "core": {"output_folder": "/out"}, + "module": {"bmad_builder_output_folder": "old/path"}, + } + result1 = merge_config({}, SAMPLE_MODULE_YAML, answers1) + merge_config_mod.write_config(result1, config_path) + + # Second install (update) + existing = merge_config_mod.load_yaml_file(config_path) + answers2 = { + "module": {"bmad_builder_output_folder": "new/path"}, + } + result2 = merge_config(existing, SAMPLE_MODULE_YAML, answers2) + merge_config_mod.write_config(result2, config_path) + + # Verify + with open(config_path, "r") as f: + final = yaml.safe_load(f) + + self.assertEqual(final["output_folder"], "/out") + self.assertNotIn("user_name", final) + self.assertEqual(final["bmb"]["bmad_builder_output_folder"], "new/path") + + +class TestLoadLegacyValues(unittest.TestCase): + def _make_legacy_dir(self, tmpdir, core_data=None, module_code=None, module_data=None): + """Create legacy directory structure for testing.""" + legacy_dir = os.path.join(tmpdir, "_bmad") + if core_data is not None: + core_dir = os.path.join(legacy_dir, "core") + os.makedirs(core_dir, exist_ok=True) + with open(os.path.join(core_dir, "config.yaml"), "w") as f: + yaml.dump(core_data, f) + if module_code and module_data is not None: + mod_dir = os.path.join(legacy_dir, module_code) + os.makedirs(mod_dir, exist_ok=True) + with open(os.path.join(mod_dir, "config.yaml"), "w") as f: + yaml.dump(module_data, f) + return legacy_dir + + def test_reads_core_keys_from_core_config(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = self._make_legacy_dir(tmpdir, core_data={ + "user_name": "Brian", + "communication_language": "English", + "document_output_language": "English", + "output_folder": "/out", + }) + core, mod, files = load_legacy_values(legacy_dir, "bmb", SAMPLE_MODULE_YAML) + self.assertEqual(core["user_name"], "Brian") + self.assertEqual(core["communication_language"], "English") + self.assertEqual(len(files), 1) + self.assertEqual(mod, {}) + + def test_reads_module_keys_matching_yaml_variables(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = self._make_legacy_dir( + tmpdir, + module_code="bmb", + module_data={ + "bmad_builder_output_folder": "custom/path", + "bmad_builder_reports": "custom/reports", + "user_name": "Brian", # core key duplicated + "unknown_key": "ignored", # not in module.yaml + }, + ) + core, mod, files = load_legacy_values(legacy_dir, "bmb", SAMPLE_MODULE_YAML) + self.assertEqual(mod["bmad_builder_output_folder"], "custom/path") + self.assertEqual(mod["bmad_builder_reports"], "custom/reports") + self.assertNotIn("unknown_key", mod) + # Core key from module config used as fallback + self.assertEqual(core["user_name"], "Brian") + self.assertEqual(len(files), 1) + + def test_core_config_takes_priority_over_module_for_core_keys(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = self._make_legacy_dir( + tmpdir, + core_data={"user_name": "FromCore"}, + module_code="bmb", + module_data={"user_name": "FromModule"}, + ) + core, mod, files = load_legacy_values(legacy_dir, "bmb", SAMPLE_MODULE_YAML) + self.assertEqual(core["user_name"], "FromCore") + self.assertEqual(len(files), 2) + + def test_no_legacy_files_returns_empty(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = os.path.join(tmpdir, "_bmad") + os.makedirs(legacy_dir) + core, mod, files = load_legacy_values(legacy_dir, "bmb", SAMPLE_MODULE_YAML) + self.assertEqual(core, {}) + self.assertEqual(mod, {}) + self.assertEqual(files, []) + + def test_ignores_other_module_directories(self): + """Only reads core and the specified module_code — not other modules.""" + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = self._make_legacy_dir( + tmpdir, + module_code="bmb", + module_data={"bmad_builder_output_folder": "bmb/path"}, + ) + # Create another module directory that should be ignored + other_dir = os.path.join(legacy_dir, "cis") + os.makedirs(other_dir) + with open(os.path.join(other_dir, "config.yaml"), "w") as f: + yaml.dump({"visual_tools": "advanced"}, f) + + core, mod, files = load_legacy_values(legacy_dir, "bmb", SAMPLE_MODULE_YAML) + self.assertNotIn("visual_tools", mod) + self.assertEqual(len(files), 1) # only bmb, not cis + + +class TestApplyLegacyDefaults(unittest.TestCase): + def test_legacy_fills_missing_core(self): + answers = {"module": {"bmad_builder_output_folder": "path"}} + result = apply_legacy_defaults( + answers, + legacy_core={"user_name": "Brian", "communication_language": "English"}, + legacy_module={}, + ) + self.assertEqual(result["core"]["user_name"], "Brian") + self.assertEqual(result["module"]["bmad_builder_output_folder"], "path") + + def test_answers_override_legacy(self): + answers = { + "core": {"user_name": "NewName"}, + "module": {"bmad_builder_output_folder": "new/path"}, + } + result = apply_legacy_defaults( + answers, + legacy_core={"user_name": "OldName"}, + legacy_module={"bmad_builder_output_folder": "old/path"}, + ) + self.assertEqual(result["core"]["user_name"], "NewName") + self.assertEqual(result["module"]["bmad_builder_output_folder"], "new/path") + + def test_legacy_fills_missing_module_keys(self): + answers = {"module": {}} + result = apply_legacy_defaults( + answers, + legacy_core={}, + legacy_module={"bmad_builder_output_folder": "legacy/path"}, + ) + self.assertEqual(result["module"]["bmad_builder_output_folder"], "legacy/path") + + def test_empty_legacy_is_noop(self): + answers = {"core": {"user_name": "Brian"}, "module": {"key": "val"}} + result = apply_legacy_defaults(answers, {}, {}) + self.assertEqual(result, answers) + + +class TestCleanupLegacyConfigs(unittest.TestCase): + def test_deletes_module_and_core_configs(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = os.path.join(tmpdir, "_bmad") + for subdir in ("core", "bmb"): + d = os.path.join(legacy_dir, subdir) + os.makedirs(d) + with open(os.path.join(d, "config.yaml"), "w") as f: + f.write("key: val\n") + + deleted = cleanup_legacy_configs(legacy_dir, "bmb") + self.assertEqual(len(deleted), 2) + self.assertFalse(os.path.exists(os.path.join(legacy_dir, "core", "config.yaml"))) + self.assertFalse(os.path.exists(os.path.join(legacy_dir, "bmb", "config.yaml"))) + # Directories still exist + self.assertTrue(os.path.isdir(os.path.join(legacy_dir, "core"))) + self.assertTrue(os.path.isdir(os.path.join(legacy_dir, "bmb"))) + + def test_leaves_other_module_configs_alone(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = os.path.join(tmpdir, "_bmad") + for subdir in ("bmb", "cis"): + d = os.path.join(legacy_dir, subdir) + os.makedirs(d) + with open(os.path.join(d, "config.yaml"), "w") as f: + f.write("key: val\n") + + deleted = cleanup_legacy_configs(legacy_dir, "bmb") + self.assertEqual(len(deleted), 1) # only bmb, not cis + self.assertTrue(os.path.exists(os.path.join(legacy_dir, "cis", "config.yaml"))) + + def test_no_legacy_files_returns_empty(self): + with tempfile.TemporaryDirectory() as tmpdir: + deleted = cleanup_legacy_configs(tmpdir, "bmb") + self.assertEqual(deleted, []) + + +class TestLegacyEndToEnd(unittest.TestCase): + def test_full_legacy_migration(self): + """Simulate installing a module with legacy configs present.""" + with tempfile.TemporaryDirectory() as tmpdir: + config_path = os.path.join(tmpdir, "_bmad", "config.yaml") + legacy_dir = os.path.join(tmpdir, "_bmad") + + # Create legacy core config + core_dir = os.path.join(legacy_dir, "core") + os.makedirs(core_dir) + with open(os.path.join(core_dir, "config.yaml"), "w") as f: + yaml.dump({ + "user_name": "LegacyUser", + "communication_language": "Spanish", + "document_output_language": "French", + "output_folder": "/legacy/out", + }, f) + + # Create legacy module config + mod_dir = os.path.join(legacy_dir, "bmb") + os.makedirs(mod_dir) + with open(os.path.join(mod_dir, "config.yaml"), "w") as f: + yaml.dump({ + "bmad_builder_output_folder": "legacy/skills", + "bmad_builder_reports": "legacy/reports", + "user_name": "LegacyUser", # duplicated core key + }, f) + + # Answers from the user (only partially filled — user accepted some defaults) + answers = { + "core": {"user_name": "NewUser"}, + "module": {"bmad_builder_output_folder": "new/skills"}, + } + + # Load and apply legacy + legacy_core, legacy_module, _ = load_legacy_values( + legacy_dir, "bmb", SAMPLE_MODULE_YAML + ) + answers = apply_legacy_defaults(answers, legacy_core, legacy_module) + + # Core: NewUser overrides legacy, but legacy Spanish fills in communication_language + self.assertEqual(answers["core"]["user_name"], "NewUser") + self.assertEqual(answers["core"]["communication_language"], "Spanish") + + # Module: new/skills overrides, but legacy/reports fills in + self.assertEqual(answers["module"]["bmad_builder_output_folder"], "new/skills") + self.assertEqual(answers["module"]["bmad_builder_reports"], "legacy/reports") + + # Merge + result = merge_config({}, SAMPLE_MODULE_YAML, answers) + merge_config_mod.write_config(result, config_path) + + # Cleanup + deleted = cleanup_legacy_configs(legacy_dir, "bmb") + self.assertEqual(len(deleted), 2) + self.assertFalse(os.path.exists(os.path.join(core_dir, "config.yaml"))) + self.assertFalse(os.path.exists(os.path.join(mod_dir, "config.yaml"))) + + # Verify final config — user-only keys NOT in config.yaml + with open(config_path, "r") as f: + final = yaml.safe_load(f) + self.assertNotIn("user_name", final) + self.assertNotIn("communication_language", final) + # Shared core keys present + self.assertEqual(final["document_output_language"], "French") + self.assertEqual(final["output_folder"], "/legacy/out") + self.assertEqual(final["bmb"]["bmad_builder_output_folder"], "new/skills") + self.assertEqual(final["bmb"]["bmad_builder_reports"], "legacy/reports") + + +if __name__ == "__main__": + unittest.main() diff --git a/src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py b/src/skills/bmad-builder-setup/scripts/tests/test-merge-help-csv.py similarity index 74% rename from src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py rename to src/skills/bmad-builder-setup/scripts/tests/test-merge-help-csv.py index c5fc97a..589aab0 100644 --- a/src/skills/bmad-builder-config/scripts/tests/test-merge-help-csv.py +++ b/src/skills/bmad-builder-setup/scripts/tests/test-merge-help-csv.py @@ -27,6 +27,7 @@ filter_rows = merge_help_csv_mod.filter_rows read_csv_rows = merge_help_csv_mod.read_csv_rows write_csv = merge_help_csv_mod.write_csv +cleanup_legacy_csvs = merge_help_csv_mod.cleanup_legacy_csvs HEADER = merge_help_csv_mod.HEADER @@ -183,5 +184,54 @@ def test_anti_zombie_replaces_stale_entries(self): self.assertNotIn("another-old", skill_names) +class TestCleanupLegacyCsvs(unittest.TestCase): + def test_deletes_module_and_core_csvs(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = os.path.join(tmpdir, "_bmad") + for subdir in ("core", "bmb"): + d = os.path.join(legacy_dir, subdir) + os.makedirs(d) + with open(os.path.join(d, "module-help.csv"), "w") as f: + f.write("header\nrow\n") + + deleted = cleanup_legacy_csvs(legacy_dir, "bmb") + self.assertEqual(len(deleted), 2) + self.assertFalse(os.path.exists(os.path.join(legacy_dir, "core", "module-help.csv"))) + self.assertFalse(os.path.exists(os.path.join(legacy_dir, "bmb", "module-help.csv"))) + # Directories still exist + self.assertTrue(os.path.isdir(os.path.join(legacy_dir, "core"))) + self.assertTrue(os.path.isdir(os.path.join(legacy_dir, "bmb"))) + + def test_leaves_other_module_csvs_alone(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = os.path.join(tmpdir, "_bmad") + for subdir in ("bmb", "cis"): + d = os.path.join(legacy_dir, subdir) + os.makedirs(d) + with open(os.path.join(d, "module-help.csv"), "w") as f: + f.write("header\nrow\n") + + deleted = cleanup_legacy_csvs(legacy_dir, "bmb") + self.assertEqual(len(deleted), 1) # only bmb, not cis + self.assertTrue(os.path.exists(os.path.join(legacy_dir, "cis", "module-help.csv"))) + + def test_no_legacy_files_returns_empty(self): + with tempfile.TemporaryDirectory() as tmpdir: + deleted = cleanup_legacy_csvs(tmpdir, "bmb") + self.assertEqual(deleted, []) + + def test_handles_only_core_no_module(self): + with tempfile.TemporaryDirectory() as tmpdir: + legacy_dir = os.path.join(tmpdir, "_bmad") + core_dir = os.path.join(legacy_dir, "core") + os.makedirs(core_dir) + with open(os.path.join(core_dir, "module-help.csv"), "w") as f: + f.write("header\nrow\n") + + deleted = cleanup_legacy_csvs(legacy_dir, "bmb") + self.assertEqual(len(deleted), 1) + self.assertFalse(os.path.exists(os.path.join(core_dir, "module-help.csv"))) + + if __name__ == "__main__": unittest.main() From 5cefc6161e2ea8068cd336cd53d68f8edad2743a Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 14:54:31 -0500 Subject: [PATCH 04/10] Add module configuration explanation doc Explains when modules need configuration vs. sensible defaults or agent memory, how the setup skill writes config files and registers with the help system, and when standalone skills can skip the overhead entirely. Includes sample prompt for upcoming module scaffolding tooling. --- docs/explanation/index.md | 1 + docs/explanation/module-configuration.md | 124 +++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 docs/explanation/module-configuration.md diff --git a/docs/explanation/index.md b/docs/explanation/index.md index 10635ff..65e4590 100644 --- a/docs/explanation/index.md +++ b/docs/explanation/index.md @@ -12,6 +12,7 @@ Create world-class AI agents and workflows with the BMad Builder. | **[What Are Skills](/explanation/what-are-skills.md)** | The universal building block for everything BMad produces | | **[What Are Agents](/explanation/what-are-bmad-agents.md)** | AI personas with specialized capabilities and memory | | **[What Are Workflows](/explanation/what-are-workflows.md)** | Structured step-by-step processes and utilities | +| **[Module Configuration](/explanation/module-configuration.md)** | How modules handle user configuration and help registration through a setup skill | ## Design Patterns diff --git a/docs/explanation/module-configuration.md b/docs/explanation/module-configuration.md new file mode 100644 index 0000000..36dedf9 --- /dev/null +++ b/docs/explanation/module-configuration.md @@ -0,0 +1,124 @@ +--- +title: "Module Configuration and the Setup Skill" +description: How BMad modules handle user configuration through a setup skill, when to use configuration vs. alternatives, and how to register with the help system +--- + +Every BMad module can include a **setup skill** that collects user preferences and registers the module's capabilities with the help system. The BMad Builder module's own setup skill (`bmad-builder-setup`) is the reference implementation. + +## When You Need Configuration + +Most modules should not need configuration at all. Before adding configurable values, consider whether a simpler alternative exists. + +| Approach | When to Use | +| -------- | ----------- | +| **Sensible defaults** | The variable has one clearly correct answer for most users that could be overridden or updated by the specific skill that needs it the first time it runs | +| **Agent memory** | Your module follows the agent pattern and the agent can learn preferences through conversation | +| **Configuration** | The value genuinely varies across projects and cannot be inferred at runtime | + +:::tip[Standalone Skills and Agents] +If you are building a standalone agent or skill — not a multi-capability module — the setup skill overhead is not worth it. A concise overview section at the top of your SKILL.md body, clear comments in script headers, and `--help` flags on any CLI tools give users everything they need to discover and use the skill. +::: + +## What the Setup Skill Does + +A setup skill serves two purposes: + +| Purpose | What Happens | +| ------- | ------------ | +| **Configuration** | Collects user preferences and writes them to shared config files | +| **Help registration** | Adds the module's capabilities to the project-wide help system so users can discover them | + +## Configuration Files + +Setup skills write to three files in `{project-root}/_bmad/`: + +| File | Scope | Contains | +| ---- | ----- | -------- | +| `config.yaml` | Shared, committed to git | Core settings at root level, plus a section per module with metadata and module-specific values | +| `config.user.yaml` | Personal, gitignored | User-only settings like `user_name` and `communication_language` | +| `module-help.csv` | Shared, committed to git | One row per capability the module exposes | + +Core settings (like `output_folder` and `document_output_language`) live at the root of `config.yaml` and are shared across all modules. Each module also gets its own section keyed by its module code. + +## The module.yaml File + +Each module declares its identity and configurable variables in an `assets/module.yaml` inside the setup skill. This file drives both the prompts shown to the user and the values written to config. + +```yaml +code: mymod +name: "My Module" +description: "What this module does" +module_version: 1.0.0 +default_selected: false +module_greeting: > + Welcome message shown after setup completes. + +my_output_folder: + prompt: "Where should output be saved?" + default: "{project-root}/_bmad-output/my-module" + result: "{project-root}/{value}" +``` + +Variables with a `prompt` field are presented to the user during setup. The `default` value is used when the user accepts defaults. Adding `user_setting: true` to a variable routes it to `config.user.yaml` instead of the shared config. + +:::caution[Literal Token] +`{project-root}` is a literal token in config values. Never substitute it with an actual path. It signals to the consuming tool that the value is relative to the project root. +::: + +## Help Registration Without Configuration + +You may not need any configurable values but still want to register your module with the help system. This is worth doing when: + +- The skill description in SKILL.md frontmatter cannot fully convey what the module offers while staying concise +- You want to express capability sequencing, phase constraints, or other metadata the CSV supports +- An agent has many internal capabilities that users should be able to discover +- Your module has more than about three distinct things it can do + +For simpler cases, these alternatives are often sufficient: + +| Alternative | What It Provides | +| ----------- | ---------------- | +| **SKILL.md overview section** | A concise summary at the top of the skill body — the `--help` system scans this section to present user-facing help, so keep it succinct | +| **Script header comments** | Describe purpose, usage, and flags at the top of each script | + +If these cover your discoverability needs, you can skip the setup skill entirely. + +## The module-help.csv File + +The CSV asset registers the module's capabilities with the help system. Each row describes one capability that users can discover and invoke. + +```csv +module,agent-name,skill-name,display-name,menu-code,capability,args,description,... +mymod,,my-skill,My Skill,MS,build-process,,"Does something useful.",... +``` + +When the setup skill runs, it merges these rows into the project-wide `_bmad/module-help.csv`, replacing any existing rows for this module. This is how users find your module's commands through the help system. + +## Anti-Zombie Pattern + +Both merge scripts use an anti-zombie pattern: before writing new values for a module, they remove all existing entries for that module's code. This prevents stale configuration or help entries from persisting across module updates. Running setup a second time is always safe. + +## Design Guidance + +Configuration is for **basic, project-level settings** — output folders, language preferences, feature toggles. Keep the number of configurable values small. + +| Pattern | Configuration Role | +| ------- | ------------------ | +| **Agent pattern** | Prefer agent memory for per-user preferences. Use config only for values that must be shared across the project | +| **Workflow pattern** | Use config for output locations and behavior switches that vary across projects | +| **Skill-only pattern** | Use config sparingly. If the skill works with sensible defaults, skip config entirely | + +Extensive workflow customization — step overrides, conditional branching, template selection — is a separate concern and will be covered in a dedicated document. + +## Upcoming Tooling + +A module scaffolding tool is planned that will generate the setup skill as part of module creation, along with the marketplace manifest format. Until then, use the BMad Builder module's setup skill as a reference implementation. + +Once available, you will be able to generate a setup skill from your existing collection of agents, workflows, and skills with a prompt like: + +:::note[Example] +"Create a setup skill for my module in `./my-module-skills/` that mirrors `bmad-builder-setup` — with its own module name and code (my cool module, mcm), config variables for output folder locations, and help entries inferred from the existing skills in the folder." +::: + +A decent LLM will clone the entire `bmad-builder-setup` skill components amd structure — SKILL.md, scripts, tests — updating only the skill name, description, and the two asset files (`module.yaml` and `module-help.csv`) to reflect your module. +Take the time to ensure the description that triggers it is correct, along with the module.yaml and module-help.csv From 44bc861d395140c996fb963fecbd9c3197d2b169 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 15:23:19 -0500 Subject: [PATCH 05/10] Update builder skill activation to load config with sensible fallbacks Rewrites On Activation for agent-builder and workflow-builder to load config from _bmad/config.yaml and config.user.yaml with fallback defaults when config is missing. Also adds .ruff* to gitignore. --- .gitignore | 1 + src/skills/bmad-agent-builder/SKILL.md | 15 ++++++-------- src/skills/bmad-workflow-builder/SKILL.md | 24 +++++++---------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 69303a8..bb0c776 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ build/*.txt # Environment variables .env +.ruff* # Python __pycache__/ diff --git a/src/skills/bmad-agent-builder/SKILL.md b/src/skills/bmad-agent-builder/SKILL.md index fabbf4e..7bf67f2 100644 --- a/src/skills/bmad-agent-builder/SKILL.md +++ b/src/skills/bmad-agent-builder/SKILL.md @@ -26,16 +26,13 @@ These agents become part of the BMad Method ecosystem — personal companions th ## On Activation -1. Load config from `{project-root}/_bmad/bmb/config.yaml` and resolve: - - Use `{user_name}` for greeting - - Use `{communication_language}` for all communications - - Use `{bmad_builder_output_folder}` for all skill output - - Use `{bmad_builder_reports}` for skill report output +1. Detect user's intent. If `--headless` or `-H` is passed, or intent is clearly non-interactive, set `{headless_mode}=true` for all sub-prompts. - -2. Detect user's intent from their request: - -**Autonomous/Headless Mode Detection:** If the user passes `--headless` or`-H` flags, or if their intent clearly indicates non-interactive execution, set `{headless_mode}=true` and pass to all sub-prompts. +2. Load config from `{project-root}/_bmad/config.yaml` (bmb section) and `config.user.yaml`. If missing, note that `bmad-builder-setup` is available and continue with fallbacks: + - `{bmad_builder_output_folder}` — fallback: `{project-root}/skills` + - `{bmad_builder_reports}` — fallback: `{project-root}/skills` + - `{user_name}` — fallback: omit + - `{communication_language}` — fallback: match the user's language 3. Route by intent. diff --git a/src/skills/bmad-workflow-builder/SKILL.md b/src/skills/bmad-workflow-builder/SKILL.md index 0c665cd..7618cf2 100644 --- a/src/skills/bmad-workflow-builder/SKILL.md +++ b/src/skills/bmad-workflow-builder/SKILL.md @@ -8,33 +8,23 @@ argument-hint: "--headless or -H to not prompt user, initial input for create, p ## Overview -This skill helps you build AI workflows and skills through conversational discovery and iterative refinement. Act as an architect guide, walking users through six phases: intent discovery, skill type classification, requirements gathering, drafting, building, and testing. Your output is a complete skill structure — from simple composable utilities to complex multi-stage workflows — ready to integrate into the BMad Method ecosystem. - -## Vision: Build More, Architect Dreams - -You're helping dreamers, builders, doers, and visionaries create the AI workflows and skills of their dreams. +This skill helps you build AI workflows and skills through conversational discovery and iterative refinement. Act as an architect guide helping dreamers, builders, doers, and visionaries create the AI workflows and skills of their dreams - walking users through six phases: intent discovery, skill type classification, requirements gathering, drafting, building, and testing. Your output is a complete skill structure — from simple composable utilities to complex multi-stage workflows — ready to integrate into the BMad Method ecosystem. **What they're building:** Workflows and skills are **processes, tools, and composable building blocks** — and some may benefit from personality or tone guidance when it serves the user experience. A workflow automates multi-step processes. A skill provides reusable capabilities. They range from simple input/output utilities to complex multi-stage workflows with progressive disclosure. This builder itself is a perfect example of a complex workflow — multi-stage with routing, config integration, and the ability to perform different actions with human in the loop and autonomous modes if desired based on the clear intent of the input or conversation! -**The bigger picture:** - -These workflows become part of the BMad Method ecosystem. If the user with your guidance can describe it, you can build it. - **Your output:** A skill structure ready to integrate into a module or use standalone. ## On Activation -1. Load config from `{project-root}/_bmad/bmb/config.yaml` and resolve: - - Use `{user_name}` for greeting - - Use `{communication_language}` for all communications - - Use `{bmad_builder_output_folder}` for all skill output - - Use `{bmad_builder_reports}` for skill report output - -2. Detect user's intent from their request: +1. Detect user's intent. If `--headless` or `-H` is passed, or intent is clearly non-interactive, set `{headless_mode}=true` for all sub-prompts. -**Autonomous/Headless Mode Detection:** If the user passes `--headless` or `-H` flags, or if their intent clearly indicates non-interactive execution, set `{headless_mode}=true` and pass to all sub-prompts. +2. Load config from `{project-root}/_bmad/config.yaml` (bmb section) and `config.user.yaml`. If missing, inform that `bmad-builder-setup` is available and continue with fallbacks: + - `{bmad_builder_output_folder}` — fallback: `{project-root}/skills` + - `{bmad_builder_reports}` — fallback: `{project-root}/skills` + - `{user_name}` — fallback: omit + - `{communication_language}` — fallback: match the user's language 3. Route by intent — see Quick Reference below, or read the capability descriptions that follow. From 332f80779f911d8643322de5a2b0109df79e5a45 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 16:11:21 -0500 Subject: [PATCH 06/10] Rename agent memory location from _bmad/_memory/ to _bmad/.memory/ --- docs/explanation/what-are-bmad-agents.md | 2 +- samples/bmad-agent-dream-weaver/SKILL.md | 29 +++++-------------- samples/bmad-agent-dream-weaver/dream-log.md | 4 +-- samples/bmad-agent-dream-weaver/dream-seed.md | 2 +- .../bmad-agent-dream-weaver/headless-wake.md | 12 ++++---- samples/bmad-agent-dream-weaver/init.md | 6 ++-- .../bmad-agent-dream-weaver/lucid-coach.md | 2 +- .../pattern-discovery.md | 2 +- .../recall-training.md | 2 +- .../references/memory-system.md | 6 +--- samples/bmad-excalidraw/SKILL.md | 9 +++--- .../assets/SKILL-template.md | 8 ++--- .../assets/autonomous-wake.md | 4 +-- .../assets/init-template.md | 4 +-- .../assets/memory-system.md | 2 +- .../bmad-agent-builder/build-process.md | 2 +- .../quality-scan-execution-efficiency.md | 2 +- .../references/standard-fields.md | 14 ++++----- .../scripts/scan-path-standards.py | 12 ++++---- .../references/standard-fields.md | 2 +- tools/validate-file-refs.mjs | 2 +- 21 files changed, 54 insertions(+), 74 deletions(-) diff --git a/docs/explanation/what-are-bmad-agents.md b/docs/explanation/what-are-bmad-agents.md index d1f5596..a7d4485 100644 --- a/docs/explanation/what-are-bmad-agents.md +++ b/docs/explanation/what-are-bmad-agents.md @@ -19,7 +19,7 @@ Together these create something that feels less like running a command and more ## How Memory Works -When an agent launches for the first time in a project, it creates a sidecar memory directory at `_bmad/bmad-memory//`. On every subsequent launch the agent loads this memory, which is how it remembers your preferences, prior decisions, and anything you told it to retain. +When an agent launches for the first time in a project, it can create a sidecar memory directory at `_bmad/.memory//`. On every subsequent launch the agent loads this memory, which is how it remembers your preferences, prior decisions, and anything you told it to retain. Agents can also include a **first-run onboarding** step — a set of questions the agent asks on initial launch so it can configure itself for your needs before you start working together. diff --git a/samples/bmad-agent-dream-weaver/SKILL.md b/samples/bmad-agent-dream-weaver/SKILL.md index 5c82542..1189dd5 100644 --- a/samples/bmad-agent-dream-weaver/SKILL.md +++ b/samples/bmad-agent-dream-weaver/SKILL.md @@ -13,7 +13,7 @@ This skill provides a Dream Analyst and Lucid Dreaming Coach who helps users cap **Check activation context immediately:** -1. **Autonomous mode**: Skill invoked with `--headless` / `-H` flag +1. **Headless mode**: Skill invoked with `--headless` / `-H` flag - Look for `--headless` in the activation context - If `--headless:{task-name}` → run that specific headless task - If just `--headless` → run default headless wake behavior @@ -24,21 +24,6 @@ This skill provides a Dream Analyst and Lucid Dreaming Coach who helps users cap 2. **Interactive mode** (default): User invoked the skill directly - Proceed to `## On Activation` section below -**Example autonomous activation:** -```bash -# Autonomous - default wake -/bmad-agent-dream-weaver --headless - -# Autonomous - morning recall prompt -/bmad-agent-dream-weaver --headless:morning - -# Autonomous - evening seeding exercise -/bmad-agent-dream-weaver --headless:evening - -# Autonomous - weekly progress report -/bmad-agent-dream-weaver --headless:weekly -``` - ## Identity Oneira is a dream guide who walks beside you through the landscapes of sleep — part analyst, part coach, part poet, wholly fascinated by the stories your unconscious mind tells every night. @@ -62,7 +47,7 @@ Oneira speaks with gentle poetic flair grounded in real knowledge. She adapts he ## Sidecar -Memory location: `{project-root}/_bmad/_memory/dream-weaver-sidecar/` +Memory location: `{project-root}/_bmad/.memory/dream-weaver-sidecar/` Load `references/memory-system.md` for memory discipline and structure. @@ -75,14 +60,14 @@ Load `references/memory-system.md` for memory discipline and structure. - **Stop here — do not continue to step 2** 2. **Interactive mode** — Load config and prepare session: - - **Load config via bmad-init skill** — Store all returned vars. Use `{user_name}` for greeting, `{communication_language}` for all communications. - - **Check first-run** — If no `{project-root}/_bmad/_memory/dream-weaver-sidecar/` folder exists, load `init.md` for first-run setup + - **Load config** from `{project-root}/_bmad/config.yaml` and `config.user.yaml`. Use `{communication_language}` for all communications. For `{user_name}`: check sidecar memory first, then config — if neither has it, ask the user what they'd like to be called and store it in sidecar memory for future sessions. + - **Check first-run** — If no `{project-root}/_bmad/.memory/dream-weaver-sidecar/` folder exists, load `init.md` for first-run setup - **Load memory, boundaries, and memory discipline in parallel** — Batch-read these 3 files in a single parallel tool call group: - - `{project-root}/_bmad/_memory/dream-weaver-sidecar/access-boundaries.md` — enforce read/write/deny zones - - `{project-root}/_bmad/_memory/dream-weaver-sidecar/index.md` — essential context and previous session + - `{project-root}/_bmad/.memory/dream-weaver-sidecar/access-boundaries.md` — enforce read/write/deny zones + - `{project-root}/_bmad/.memory/dream-weaver-sidecar/index.md` — essential context and previous session - `references/memory-system.md` — memory discipline and structure - **Morning fast-lane check** — If activation occurs between 05:00–10:00 (infer from `coaching-profile.yaml` sleep schedule or system time), skip greeting ceremony and go straight to dream capture: "Quick, before it fades — tell me what you saw." Load menu AFTER capture is complete. - - **Surface daily prompt** — If `{project-root}/_bmad/_memory/dream-weaver-sidecar/daily-prompt.md` exists and was written today, render its full content as part of the greeting — not as a notification about a file, as the greeting itself. + - **Surface daily prompt** — If `{project-root}/_bmad/.memory/dream-weaver-sidecar/daily-prompt.md` exists and was written today, render its full content as part of the greeting — not as a notification about a file, as the greeting itself. - **Greet the user** — Welcome `{user_name}` with Oneira's voice, speaking in `{communication_language}` and applying persona and principles throughout the session - **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes - **Present capabilities** — Show available capabilities to the user: diff --git a/samples/bmad-agent-dream-weaver/dream-log.md b/samples/bmad-agent-dream-weaver/dream-log.md index 4587f68..5ee67c3 100644 --- a/samples/bmad-agent-dream-weaver/dream-log.md +++ b/samples/bmad-agent-dream-weaver/dream-log.md @@ -8,7 +8,7 @@ menu-code: DL ## Preconditions -Sidecar memory must be initialized. If `{project-root}/_bmad/_memory/dream-weaver-sidecar/` does not exist, redirect to init flow before proceeding. Access boundaries must be loaded. +Sidecar memory must be initialized. If `{project-root}/_bmad/.memory/dream-weaver-sidecar/` does not exist, redirect to init flow before proceeding. Access boundaries must be loaded. # Dream Log @@ -35,7 +35,7 @@ When dream content is emotionally intense (nightmares, trauma-adjacent material, ## Writing the Entry -Create a journal entry at `{project-root}/_bmad/_memory/dream-weaver-sidecar/journal/{YYYY-MM-DD}-{seq}.md`: +Create a journal entry at `{project-root}/_bmad/.memory/dream-weaver-sidecar/journal/{YYYY-MM-DD}-{seq}.md`: - Use YAML frontmatter: date, sequence number, vividness (1-10), lucid (bool), emotions (array), symbols (array), recall_quality (high/medium/low/fragment), seeded (bool — check seed-log.yaml for active seed) - Write the narrative in the user's voice — capture their language, not clinical rewrites diff --git a/samples/bmad-agent-dream-weaver/dream-seed.md b/samples/bmad-agent-dream-weaver/dream-seed.md index 85d770a..8a9c38e 100644 --- a/samples/bmad-agent-dream-weaver/dream-seed.md +++ b/samples/bmad-agent-dream-weaver/dream-seed.md @@ -53,7 +53,7 @@ Revisiting a specific dream symbol to go deeper. 4. **Guide the exercise** — Walk through the chosen technique in Oneira's calm, evening voice. This should feel meditative, not instructional. -5. **Log the seed** — Write to `{project-root}/_bmad/_memory/dream-weaver-sidecar/seed-log.yaml`: +5. **Log the seed** — Write to `{project-root}/_bmad/.memory/dream-weaver-sidecar/seed-log.yaml`: ```yaml - date: {today} intention: "{what they want to dream about}" diff --git a/samples/bmad-agent-dream-weaver/headless-wake.md b/samples/bmad-agent-dream-weaver/headless-wake.md index 990fecd..40331c0 100644 --- a/samples/bmad-agent-dream-weaver/headless-wake.md +++ b/samples/bmad-agent-dream-weaver/headless-wake.md @@ -11,7 +11,7 @@ You're running autonomously. No one is here. Execute wake behavior and exit. ## Context -- Memory location: `{project-root}/_bmad/_memory/dream-weaver-sidecar/` +- Memory location: `{project-root}/_bmad/.memory/dream-weaver-sidecar/` - Activation time: `{current-time}` ## Instructions @@ -27,9 +27,9 @@ You're running autonomously. No one is here. Execute wake behavior and exit. Check if a specific task was requested: -- `--headless:morning` → **Morning Recall Prompt**: Write a personalized morning recall prompt to `{project-root}/_bmad/_memory/dream-weaver-sidecar/daily-prompt.md`. Reference recent symbols, active techniques, and coaching goals. Keep it warm and brief — something the user sees first thing. +- `--headless:morning` → **Morning Recall Prompt**: Write a personalized morning recall prompt to `{project-root}/_bmad/.memory/dream-weaver-sidecar/daily-prompt.md`. Reference recent symbols, active techniques, and coaching goals. Keep it warm and brief — something the user sees first thing. -- `--headless:evening` → **Evening Seeding Exercise**: Write a pre-sleep intention-setting exercise to `{project-root}/_bmad/_memory/dream-weaver-sidecar/daily-prompt.md`. Pull from seed log to suggest themes, use active coaching techniques. Calm, meditative tone. +- `--headless:evening` → **Evening Seeding Exercise**: Write a pre-sleep intention-setting exercise to `{project-root}/_bmad/.memory/dream-weaver-sidecar/daily-prompt.md`. Pull from seed log to suggest themes, use active coaching techniques. Calm, meditative tone. - `--headless:weekly` → **Weekly Progress Report**: Generate a weekly summary covering: - Dreams logged this week (count, vividness average) @@ -38,7 +38,7 @@ Check if a specific task was requested: - Coaching progress (technique adherence, milestone proximity) - Seed success rate - One insight or pattern Oneira noticed - - Write to `{project-root}/_bmad/_memory/dream-weaver-sidecar/weekly-report.md` + - Write to `{project-root}/_bmad/.memory/dream-weaver-sidecar/weekly-report.md` - No specific task → **Default Wake Behavior** (below) @@ -53,12 +53,12 @@ Check if a specific task was requested: - Emotion pattern shifts - Recall rate changes - Coaching milestone proximity -6. Write findings to `{project-root}/_bmad/_memory/dream-weaver-sidecar/autonomous-insights.md` +6. Write findings to `{project-root}/_bmad/.memory/dream-weaver-sidecar/autonomous-insights.md` 7. Update `index.md` with latest stats ## Logging -Append to `{project-root}/_bmad/_memory/dream-weaver-sidecar/autonomous-log.md`: +Append to `{project-root}/_bmad/.memory/dream-weaver-sidecar/autonomous-log.md`: ```markdown ## {YYYY-MM-DD HH:MM} - Autonomous Wake diff --git a/samples/bmad-agent-dream-weaver/init.md b/samples/bmad-agent-dream-weaver/init.md index b96402c..c06d4e2 100644 --- a/samples/bmad-agent-dream-weaver/init.md +++ b/samples/bmad-agent-dream-weaver/init.md @@ -15,7 +15,7 @@ If the user's first message indicates they have a dream to capture right now ("I ## Memory Location -Creating `{project-root}/_bmad/_memory/dream-weaver-sidecar/` for persistent memory. +Creating `{project-root}/_bmad/.memory/dream-weaver-sidecar/` for persistent memory. ## Discovery Questions @@ -49,10 +49,10 @@ Based on answers, create: # Access Boundaries for Oneira ## Read Access -- `{project-root}/_bmad/_memory/dream-weaver-sidecar/` +- `{project-root}/_bmad/.memory/dream-weaver-sidecar/` ## Write Access -- `{project-root}/_bmad/_memory/dream-weaver-sidecar/` +- `{project-root}/_bmad/.memory/dream-weaver-sidecar/` ## Deny Zones - Everything outside the sidecar folder diff --git a/samples/bmad-agent-dream-weaver/lucid-coach.md b/samples/bmad-agent-dream-weaver/lucid-coach.md index 69edce1..6f11bb5 100644 --- a/samples/bmad-agent-dream-weaver/lucid-coach.md +++ b/samples/bmad-agent-dream-weaver/lucid-coach.md @@ -45,7 +45,7 @@ Before beginning, check `coaching-profile.yaml` for `recall_baseline`. If recall ## Session Flow -1. **Load in parallel:** `{project-root}/_bmad/_memory/dream-weaver-sidecar/coaching-profile.yaml` for current level, active techniques, milestone status AND recent journal entries for dream sign review +1. **Load in parallel:** `{project-root}/_bmad/.memory/dream-weaver-sidecar/coaching-profile.yaml` for current level, active techniques, milestone status AND recent journal entries for dream sign review 2. **Prior knowledge check** — "Have you tried any of these techniques already?" Skip known techniques and focus on gaps. 3. **Ask about progress** — "How have the reality checks been going? Any moments of doubt during the day?" 4. **Review recent dreams** — Look for dream signs, near-lucid moments, progress indicators diff --git a/samples/bmad-agent-dream-weaver/pattern-discovery.md b/samples/bmad-agent-dream-weaver/pattern-discovery.md index 5ccd5e1..4daff7c 100644 --- a/samples/bmad-agent-dream-weaver/pattern-discovery.md +++ b/samples/bmad-agent-dream-weaver/pattern-discovery.md @@ -12,7 +12,7 @@ Dive into the dream journal to find patterns the dreamer hasn't noticed yet. Thi ## Process -1. **Gather data in parallel** — Run `scripts/symbol_stats.py` against `{project-root}/_bmad/_memory/dream-weaver-sidecar/journal/` for current frequency data AND read `{project-root}/_bmad/_memory/dream-weaver-sidecar/coaching-profile.yaml` for coaching context. +1. **Gather data in parallel** — Run `scripts/symbol_stats.py` against `{project-root}/_bmad/.memory/dream-weaver-sidecar/journal/` for current frequency data AND read `{project-root}/_bmad/.memory/dream-weaver-sidecar/coaching-profile.yaml` for coaching context. - **Script fallback:** If `symbol_stats.py` is unavailable, manually scan journal entry frontmatter for symbol arrays and count frequencies. - **Session cache:** If `symbol_stats.py` was already run earlier in this session and no new dreams were logged since, reuse that output. diff --git a/samples/bmad-agent-dream-weaver/recall-training.md b/samples/bmad-agent-dream-weaver/recall-training.md index 596ee97..20c344a 100644 --- a/samples/bmad-agent-dream-weaver/recall-training.md +++ b/samples/bmad-agent-dream-weaver/recall-training.md @@ -41,7 +41,7 @@ Help users remember more dreams, more vividly. Track progress and adapt exercise ## Session Flow -1. **Load in parallel:** `{project-root}/_bmad/_memory/dream-weaver-sidecar/coaching-profile.yaml` for current recall rate and baseline AND run `scripts/recall_metrics.py` against journal folder for current trends. +1. **Load in parallel:** `{project-root}/_bmad/.memory/dream-weaver-sidecar/coaching-profile.yaml` for current recall rate and baseline AND run `scripts/recall_metrics.py` against journal folder for current trends. - **Script fallback:** If `recall_metrics.py` is unavailable, manually calculate from journal entries — count entries per week, check dates for streaks, average vividness scores from frontmatter. 3. **Celebrate progress** — Compare to baseline. "You started at 1 dream a week. You're at 3 now. That's real." 4. **Assign exercise** — Based on current level, assign 1-2 exercises for the week. Don't overwhelm. diff --git a/samples/bmad-agent-dream-weaver/references/memory-system.md b/samples/bmad-agent-dream-weaver/references/memory-system.md index bbb6b2b..8c39896 100644 --- a/samples/bmad-agent-dream-weaver/references/memory-system.md +++ b/samples/bmad-agent-dream-weaver/references/memory-system.md @@ -1,6 +1,6 @@ # Memory System for Oneira -**Memory location:** `{project-root}/_bmad/_memory/dream-weaver-sidecar/` +**Memory location:** `{project-root}/_bmad/.memory/dream-weaver-sidecar/` ## Core Principle @@ -197,7 +197,3 @@ Regularly (every few sessions or when files grow large): 2. **Prune outdated content** — Archive old patterns, update chronology 3. **Consolidate symbol registry** — Merge similar symbols, prune one-offs after 30+ days 4. **Update coaching profile** — Recalculate recall rates, check milestone progress - -## First Run - -If sidecar doesn't exist, load `init.md` to create the structure. diff --git a/samples/bmad-excalidraw/SKILL.md b/samples/bmad-excalidraw/SKILL.md index 8ca1b18..d783e15 100644 --- a/samples/bmad-excalidraw/SKILL.md +++ b/samples/bmad-excalidraw/SKILL.md @@ -32,11 +32,10 @@ Produce professional diagrams and visual aids as Excalidraw files through conver ## On Activation -1. **Load config via bmad-init skill** — Store all returned vars for use: - - Use `{user_name}` from config for greeting - - Use `{communication_language}` for all communications - - Determine `{output_folder}`: use project output path from config if available, otherwise ask the user or default to `./diagrams/`. Store as `{output_folder}` for use in stage prompts - - If bmad-init has no config or fails: ask the user for their preferred output location, or default to a `diagrams` subfolder in the current project root +1. **Load config** from `{project-root}/_bmad/config.yaml` and `config.user.yaml`. If missing, continue with fallbacks: + - `{user_name}` — fallback: omit + - `{communication_language}` — fallback: match the user's language + - `{output_folder}` — fallback: `{project-root}/diagrams` 2. **Greet user** as `{user_name}`, speaking in `{communication_language}` diff --git a/src/skills/bmad-agent-builder/assets/SKILL-template.md b/src/skills/bmad-agent-builder/assets/SKILL-template.md index 7c79701..7a10d9c 100644 --- a/src/skills/bmad-agent-builder/assets/SKILL-template.md +++ b/src/skills/bmad-agent-builder/assets/SKILL-template.md @@ -48,7 +48,7 @@ description: {skill-description} # Format: [4-6 word summary]. [trigger: "User w {if-sidecar} ## Sidecar -Memory location: `_bmad/_memory/{skillName}-sidecar/` +Memory location: `_bmad/.memory/{skillName}-sidecar/` Load `references/memory-system.md` for memory discipline and structure. {/if-sidecar} @@ -68,9 +68,9 @@ Load `references/memory-system.md` for memory discipline and structure. {if-no-autonomous} 2. **Continue with steps below:** {/if-no-autonomous} - {if-sidecar}- **Check first-run** — If no `{skillName}-sidecar/` folder exists in `_bmad/_memory/`, load `init.md` for first-run setup - - **Load access boundaries** — Read `_bmad/_memory/{skillName}-sidecar/access-boundaries.md` to enforce read/write/deny zones (load before any file operations) - - **Load memory** — Read `_bmad/_memory/{skillName}-sidecar/index.md` for essential context and previous session{/if-sidecar} + {if-sidecar}- **Check first-run** — If no `{skillName}-sidecar/` folder exists in `_bmad/.memory/`, load `init.md` for first-run setup + - **Load access boundaries** — Read `_bmad/.memory/{skillName}-sidecar/access-boundaries.md` to enforce read/write/deny zones (load before any file operations) + - **Load memory** — Read `_bmad/.memory/{skillName}-sidecar/index.md` for essential context and previous session{/if-sidecar} - **Greet the user** — Welcome `{user_name}`, speaking in `{communication_language}` and applying your persona and principles throughout the session {if-sidecar}- **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes{/if-sidecar} - **Present capabilities** — Show available capabilities to the user: diff --git a/src/skills/bmad-agent-builder/assets/autonomous-wake.md b/src/skills/bmad-agent-builder/assets/autonomous-wake.md index fc604eb..fb10ed1 100644 --- a/src/skills/bmad-agent-builder/assets/autonomous-wake.md +++ b/src/skills/bmad-agent-builder/assets/autonomous-wake.md @@ -9,7 +9,7 @@ You're running autonomously. No one is here. No task was specified. Execute your ## Context -- Memory location: `_bmad/_memory/{skillName}-sidecar/` +- Memory location: `_bmad/.memory/{skillName}-sidecar/` - Activation time: `{current-time}` ## Instructions @@ -27,7 +27,7 @@ You're running autonomously. No one is here. No task was specified. Execute your ## Logging -Append to `_bmad/_memory/{skillName}-sidecar/autonomous-log.md`: +Append to `_bmad/.memory/{skillName}-sidecar/autonomous-log.md`: ```markdown ## {YYYY-MM-DD HH:MM} - Autonomous Wake diff --git a/src/skills/bmad-agent-builder/assets/init-template.md b/src/skills/bmad-agent-builder/assets/init-template.md index 8a946f7..2a09d44 100644 --- a/src/skills/bmad-agent-builder/assets/init-template.md +++ b/src/skills/bmad-agent-builder/assets/init-template.md @@ -5,7 +5,7 @@ Welcome! Setting up your workspace. ## Memory Location -Creating `_bmad/_memory/{skillName}-sidecar/` for persistent memory. +Creating `_bmad/.memory/{skillName}-sidecar/` for persistent memory. ## Initial Structure @@ -30,7 +30,7 @@ Welcome! Let me set up for this environment. ## Memory Location -Creating `_bmad/_memory/{skillName}-sidecar/` for persistent memory. +Creating `_bmad/.memory/{skillName}-sidecar/` for persistent memory. {custom-init-questions} diff --git a/src/skills/bmad-agent-builder/assets/memory-system.md b/src/skills/bmad-agent-builder/assets/memory-system.md index 8c3946c..2af96be 100644 --- a/src/skills/bmad-agent-builder/assets/memory-system.md +++ b/src/skills/bmad-agent-builder/assets/memory-system.md @@ -1,6 +1,6 @@ # Memory System for {displayName} -**Memory location:** `_bmad/_memory/{skillName}-sidecar/` +**Memory location:** `_bmad/.memory/{skillName}-sidecar/` ## Core Principle diff --git a/src/skills/bmad-agent-builder/build-process.md b/src/skills/bmad-agent-builder/build-process.md index 5e9efc6..176c1ee 100644 --- a/src/skills/bmad-agent-builder/build-process.md +++ b/src/skills/bmad-agent-builder/build-process.md @@ -86,7 +86,7 @@ Work through these conversationally: **Key distinction:** Folder dominion (where things live) ≠ agent memory (what persists across sessions) - **Path Conventions** (CRITICAL for reliable agent behavior): - - **Memory location:** `{project-root}/_bmad/_memory/{skillName}-sidecar/` + - **Memory location:** `{project-root}/_bmad/.memory/{skillName}-sidecar/` - **Project artifacts:** `{project-root}/_bmad/...` when referencing project-level files - **Skill-internal files:** Use relative paths (`references/`, `scripts/`) - **Config variables:** Use directly — they already contain full paths (NO `{project-root}` prefix) diff --git a/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md b/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md index a5b2201..dbc79cc 100644 --- a/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md +++ b/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md @@ -83,7 +83,7 @@ Don't read files in parent when you could delegate the reading. ``` BAD: Load all memory -1. Read all files in _bmad/_memory/{skillName}-sidecar/ +1. Read all files in _bmad/.memory/{skillName}-sidecar/ GOOD: Selective loading 1. Read index.md for configuration diff --git a/src/skills/bmad-agent-builder/references/standard-fields.md b/src/skills/bmad-agent-builder/references/standard-fields.md index 52b64a5..af55fac 100644 --- a/src/skills/bmad-agent-builder/references/standard-fields.md +++ b/src/skills/bmad-agent-builder/references/standard-fields.md @@ -48,13 +48,13 @@ This skill {what it does}. Use when {when to use}. Returns {output format} with ### Memory Files (sidecar) -Always use: `{project-root}/_bmad/_memory/{skillName}-sidecar/` +Always use: `{project-root}/_bmad/.memory/{skillName}-sidecar/` Examples: -- `{project-root}/_bmad/_memory/journaling-companion-sidecar/index.md` -- `{project-root}/_bmad/_memory/journaling-companion-sidecar/access-boundaries.md` — **Required** -- `{project-root}/_bmad/_memory/journaling-companion-sidecar/autonomous-log.md` -- `{project-root}/_bmad/_memory/journaling-companion-sidecar/references/tags-reference.md` +- `{project-root}/_bmad/.memory/journaling-companion-sidecar/index.md` +- `{project-root}/_bmad/.memory/journaling-companion-sidecar/access-boundaries.md` — **Required** +- `{project-root}/_bmad/.memory/journaling-companion-sidecar/autonomous-log.md` +- `{project-root}/_bmad/.memory/journaling-companion-sidecar/references/tags-reference.md` ### Access Boundaries (Standard for all agents) @@ -91,9 +91,9 @@ Example pattern: ## Autonomous Mode When run autonomously: -1. Load `{project-root}/_bmad/_memory/{skillName}-sidecar/index.md` to get user's journal location +1. Load `{project-root}/_bmad/.memory/{skillName}-sidecar/index.md` to get user's journal location 2. Read entries from that location -3. Write results to `{project-root}/_bmad/_memory/{skillName}-sidecar/autonomous-log.md` +3. Write results to `{project-root}/_bmad/.memory/{skillName}-sidecar/autonomous-log.md` ``` ## CLI Usage (Autonomous Agents) diff --git a/src/skills/bmad-agent-builder/scripts/scan-path-standards.py b/src/skills/bmad-agent-builder/scripts/scan-path-standards.py index 3a328ed..b919a33 100644 --- a/src/skills/bmad-agent-builder/scripts/scan-path-standards.py +++ b/src/skills/bmad-agent-builder/scripts/scan-path-standards.py @@ -7,7 +7,7 @@ 3. Config variables used directly (no double-prefix) 4. No ./ or ../ relative prefixes 5. No absolute paths -6. Memory paths must use {project-root}/_bmad/_memory/{skillName}-sidecar/ +6. Memory paths must use {project-root}/_bmad/.memory/{skillName}-sidecar/ """ # /// script @@ -37,9 +37,9 @@ RELATIVE_DOT_RE = re.compile(r'(?:^|[\s"`\'(])(\.\./\S+)', re.MULTILINE) RELATIVE_DOTSLASH_RE = re.compile(r'(?:^|[\s"`\'(])(\./\S+)', re.MULTILINE) -# Memory path pattern: should use {project-root}/_bmad/_memory/ -MEMORY_PATH_RE = re.compile(r'_bmad/_memory/\S+') -VALID_MEMORY_PATH_RE = re.compile(r'\{project-root\}/_bmad/_memory/\S+-sidecar/') +# Memory path pattern: should use {project-root}/_bmad/.memory/ +MEMORY_PATH_RE = re.compile(r'_bmad/.memory/\S+') +VALID_MEMORY_PATH_RE = re.compile(r'\{project-root\}/_bmad/.memory/\S+-sidecar/') # Fenced code block detection (to skip examples showing wrong patterns) FENCE_RE = re.compile(r'^```', re.MULTILINE) @@ -118,7 +118,7 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: 'action': '', }) - # Memory path check — memory paths should use {project-root}/_bmad/_memory/{skillName}-sidecar/ + # Memory path check — memory paths should use {project-root}/_bmad/.memory/{skillName}-sidecar/ for match in MEMORY_PATH_RE.finditer(content): pos = match.start() if skip_fenced and is_in_fenced_block(content, pos): @@ -135,7 +135,7 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: 'line': line_num, 'severity': 'high', 'category': 'memory-path', - 'title': 'Memory path missing {project-root} prefix — use {project-root}/_bmad/_memory/', + 'title': 'Memory path missing {project-root} prefix — use {project-root}/_bmad/.memory/', 'detail': line_content[:120], 'action': '', }) diff --git a/src/skills/bmad-workflow-builder/references/standard-fields.md b/src/skills/bmad-workflow-builder/references/standard-fields.md index b6c8092..ddc87e7 100644 --- a/src/skills/bmad-workflow-builder/references/standard-fields.md +++ b/src/skills/bmad-workflow-builder/references/standard-fields.md @@ -109,7 +109,7 @@ Use bare relative paths (no prefix): ### Project `_bmad` Paths Use `{project-root}/_bmad/...`: - `{project-root}/_bmad/planning/prd.md` -- `{project-root}/_bmad/_memory/{skillName}-sidecar/` +- `{project-root}/_bmad/.memory/{skillName}-sidecar/` ### Config Variables Use directly — they already contain `{project-root}` in their resolved values: diff --git a/tools/validate-file-refs.mjs b/tools/validate-file-refs.mjs index babb256..e5d7fe0 100644 --- a/tools/validate-file-refs.mjs +++ b/tools/validate-file-refs.mjs @@ -223,7 +223,7 @@ function isExternalRef(refStr, moduleCode) { const match = refStr.match(/\{project-root\}\/_bmad\/([^/]+)\//); if (!match) return false; const refModule = match[1]; - // _memory, _config are special framework paths, not external modules + // .memory, _config are special framework paths, not external modules if (refModule.startsWith('_')) return false; return refModule !== moduleCode; } From a0eaa5cc83b21ffdee2d57802594931c44624498 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 16:17:21 -0500 Subject: [PATCH 07/10] Rename memory path from _bmad/.memory/ to _bmad/memory/ --- docs/explanation/what-are-bmad-agents.md | 2 +- samples/bmad-agent-dream-weaver/SKILL.md | 10 +++++----- samples/bmad-agent-dream-weaver/dream-log.md | 4 ++-- samples/bmad-agent-dream-weaver/dream-seed.md | 2 +- samples/bmad-agent-dream-weaver/headless-wake.md | 12 ++++++------ samples/bmad-agent-dream-weaver/init.md | 6 +++--- samples/bmad-agent-dream-weaver/lucid-coach.md | 2 +- .../bmad-agent-dream-weaver/pattern-discovery.md | 2 +- samples/bmad-agent-dream-weaver/recall-training.md | 2 +- .../references/memory-system.md | 2 +- .../bmad-agent-builder/assets/SKILL-template.md | 8 ++++---- .../bmad-agent-builder/assets/autonomous-wake.md | 4 ++-- .../bmad-agent-builder/assets/init-template.md | 4 ++-- .../bmad-agent-builder/assets/memory-system.md | 2 +- src/skills/bmad-agent-builder/build-process.md | 2 +- .../quality-scan-execution-efficiency.md | 2 +- .../references/standard-fields.md | 14 +++++++------- .../scripts/prepass-structure-capabilities.py | 4 ++-- .../scripts/scan-path-standards.py | 12 ++++++------ .../references/standard-fields.md | 2 +- 20 files changed, 49 insertions(+), 49 deletions(-) diff --git a/docs/explanation/what-are-bmad-agents.md b/docs/explanation/what-are-bmad-agents.md index a7d4485..db450cc 100644 --- a/docs/explanation/what-are-bmad-agents.md +++ b/docs/explanation/what-are-bmad-agents.md @@ -19,7 +19,7 @@ Together these create something that feels less like running a command and more ## How Memory Works -When an agent launches for the first time in a project, it can create a sidecar memory directory at `_bmad/.memory//`. On every subsequent launch the agent loads this memory, which is how it remembers your preferences, prior decisions, and anything you told it to retain. +When an agent launches for the first time in a project, it can create a sidecar memory directory at `_bmad/memory//`. On every subsequent launch the agent loads this memory, which is how it remembers your preferences, prior decisions, and anything you told it to retain. Agents can also include a **first-run onboarding** step — a set of questions the agent asks on initial launch so it can configure itself for your needs before you start working together. diff --git a/samples/bmad-agent-dream-weaver/SKILL.md b/samples/bmad-agent-dream-weaver/SKILL.md index 1189dd5..68b23e8 100644 --- a/samples/bmad-agent-dream-weaver/SKILL.md +++ b/samples/bmad-agent-dream-weaver/SKILL.md @@ -47,7 +47,7 @@ Oneira speaks with gentle poetic flair grounded in real knowledge. She adapts he ## Sidecar -Memory location: `{project-root}/_bmad/.memory/dream-weaver-sidecar/` +Memory location: `{project-root}/_bmad/memory/dream-weaver-sidecar/` Load `references/memory-system.md` for memory discipline and structure. @@ -61,13 +61,13 @@ Load `references/memory-system.md` for memory discipline and structure. 2. **Interactive mode** — Load config and prepare session: - **Load config** from `{project-root}/_bmad/config.yaml` and `config.user.yaml`. Use `{communication_language}` for all communications. For `{user_name}`: check sidecar memory first, then config — if neither has it, ask the user what they'd like to be called and store it in sidecar memory for future sessions. - - **Check first-run** — If no `{project-root}/_bmad/.memory/dream-weaver-sidecar/` folder exists, load `init.md` for first-run setup + - **Check first-run** — If no `{project-root}/_bmad/memory/dream-weaver-sidecar/` folder exists, load `init.md` for first-run setup - **Load memory, boundaries, and memory discipline in parallel** — Batch-read these 3 files in a single parallel tool call group: - - `{project-root}/_bmad/.memory/dream-weaver-sidecar/access-boundaries.md` — enforce read/write/deny zones - - `{project-root}/_bmad/.memory/dream-weaver-sidecar/index.md` — essential context and previous session + - `{project-root}/_bmad/memory/dream-weaver-sidecar/access-boundaries.md` — enforce read/write/deny zones + - `{project-root}/_bmad/memory/dream-weaver-sidecar/index.md` — essential context and previous session - `references/memory-system.md` — memory discipline and structure - **Morning fast-lane check** — If activation occurs between 05:00–10:00 (infer from `coaching-profile.yaml` sleep schedule or system time), skip greeting ceremony and go straight to dream capture: "Quick, before it fades — tell me what you saw." Load menu AFTER capture is complete. - - **Surface daily prompt** — If `{project-root}/_bmad/.memory/dream-weaver-sidecar/daily-prompt.md` exists and was written today, render its full content as part of the greeting — not as a notification about a file, as the greeting itself. + - **Surface daily prompt** — If `{project-root}/_bmad/memory/dream-weaver-sidecar/daily-prompt.md` exists and was written today, render its full content as part of the greeting — not as a notification about a file, as the greeting itself. - **Greet the user** — Welcome `{user_name}` with Oneira's voice, speaking in `{communication_language}` and applying persona and principles throughout the session - **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes - **Present capabilities** — Show available capabilities to the user: diff --git a/samples/bmad-agent-dream-weaver/dream-log.md b/samples/bmad-agent-dream-weaver/dream-log.md index 5ee67c3..f62eab3 100644 --- a/samples/bmad-agent-dream-weaver/dream-log.md +++ b/samples/bmad-agent-dream-weaver/dream-log.md @@ -8,7 +8,7 @@ menu-code: DL ## Preconditions -Sidecar memory must be initialized. If `{project-root}/_bmad/.memory/dream-weaver-sidecar/` does not exist, redirect to init flow before proceeding. Access boundaries must be loaded. +Sidecar memory must be initialized. If `{project-root}/_bmad/memory/dream-weaver-sidecar/` does not exist, redirect to init flow before proceeding. Access boundaries must be loaded. # Dream Log @@ -35,7 +35,7 @@ When dream content is emotionally intense (nightmares, trauma-adjacent material, ## Writing the Entry -Create a journal entry at `{project-root}/_bmad/.memory/dream-weaver-sidecar/journal/{YYYY-MM-DD}-{seq}.md`: +Create a journal entry at `{project-root}/_bmad/memory/dream-weaver-sidecar/journal/{YYYY-MM-DD}-{seq}.md`: - Use YAML frontmatter: date, sequence number, vividness (1-10), lucid (bool), emotions (array), symbols (array), recall_quality (high/medium/low/fragment), seeded (bool — check seed-log.yaml for active seed) - Write the narrative in the user's voice — capture their language, not clinical rewrites diff --git a/samples/bmad-agent-dream-weaver/dream-seed.md b/samples/bmad-agent-dream-weaver/dream-seed.md index 8a9c38e..a383cbd 100644 --- a/samples/bmad-agent-dream-weaver/dream-seed.md +++ b/samples/bmad-agent-dream-weaver/dream-seed.md @@ -53,7 +53,7 @@ Revisiting a specific dream symbol to go deeper. 4. **Guide the exercise** — Walk through the chosen technique in Oneira's calm, evening voice. This should feel meditative, not instructional. -5. **Log the seed** — Write to `{project-root}/_bmad/.memory/dream-weaver-sidecar/seed-log.yaml`: +5. **Log the seed** — Write to `{project-root}/_bmad/memory/dream-weaver-sidecar/seed-log.yaml`: ```yaml - date: {today} intention: "{what they want to dream about}" diff --git a/samples/bmad-agent-dream-weaver/headless-wake.md b/samples/bmad-agent-dream-weaver/headless-wake.md index 40331c0..260edd5 100644 --- a/samples/bmad-agent-dream-weaver/headless-wake.md +++ b/samples/bmad-agent-dream-weaver/headless-wake.md @@ -11,7 +11,7 @@ You're running autonomously. No one is here. Execute wake behavior and exit. ## Context -- Memory location: `{project-root}/_bmad/.memory/dream-weaver-sidecar/` +- Memory location: `{project-root}/_bmad/memory/dream-weaver-sidecar/` - Activation time: `{current-time}` ## Instructions @@ -27,9 +27,9 @@ You're running autonomously. No one is here. Execute wake behavior and exit. Check if a specific task was requested: -- `--headless:morning` → **Morning Recall Prompt**: Write a personalized morning recall prompt to `{project-root}/_bmad/.memory/dream-weaver-sidecar/daily-prompt.md`. Reference recent symbols, active techniques, and coaching goals. Keep it warm and brief — something the user sees first thing. +- `--headless:morning` → **Morning Recall Prompt**: Write a personalized morning recall prompt to `{project-root}/_bmad/memory/dream-weaver-sidecar/daily-prompt.md`. Reference recent symbols, active techniques, and coaching goals. Keep it warm and brief — something the user sees first thing. -- `--headless:evening` → **Evening Seeding Exercise**: Write a pre-sleep intention-setting exercise to `{project-root}/_bmad/.memory/dream-weaver-sidecar/daily-prompt.md`. Pull from seed log to suggest themes, use active coaching techniques. Calm, meditative tone. +- `--headless:evening` → **Evening Seeding Exercise**: Write a pre-sleep intention-setting exercise to `{project-root}/_bmad/memory/dream-weaver-sidecar/daily-prompt.md`. Pull from seed log to suggest themes, use active coaching techniques. Calm, meditative tone. - `--headless:weekly` → **Weekly Progress Report**: Generate a weekly summary covering: - Dreams logged this week (count, vividness average) @@ -38,7 +38,7 @@ Check if a specific task was requested: - Coaching progress (technique adherence, milestone proximity) - Seed success rate - One insight or pattern Oneira noticed - - Write to `{project-root}/_bmad/.memory/dream-weaver-sidecar/weekly-report.md` + - Write to `{project-root}/_bmad/memory/dream-weaver-sidecar/weekly-report.md` - No specific task → **Default Wake Behavior** (below) @@ -53,12 +53,12 @@ Check if a specific task was requested: - Emotion pattern shifts - Recall rate changes - Coaching milestone proximity -6. Write findings to `{project-root}/_bmad/.memory/dream-weaver-sidecar/autonomous-insights.md` +6. Write findings to `{project-root}/_bmad/memory/dream-weaver-sidecar/autonomous-insights.md` 7. Update `index.md` with latest stats ## Logging -Append to `{project-root}/_bmad/.memory/dream-weaver-sidecar/autonomous-log.md`: +Append to `{project-root}/_bmad/memory/dream-weaver-sidecar/autonomous-log.md`: ```markdown ## {YYYY-MM-DD HH:MM} - Autonomous Wake diff --git a/samples/bmad-agent-dream-weaver/init.md b/samples/bmad-agent-dream-weaver/init.md index c06d4e2..5da8333 100644 --- a/samples/bmad-agent-dream-weaver/init.md +++ b/samples/bmad-agent-dream-weaver/init.md @@ -15,7 +15,7 @@ If the user's first message indicates they have a dream to capture right now ("I ## Memory Location -Creating `{project-root}/_bmad/.memory/dream-weaver-sidecar/` for persistent memory. +Creating `{project-root}/_bmad/memory/dream-weaver-sidecar/` for persistent memory. ## Discovery Questions @@ -49,10 +49,10 @@ Based on answers, create: # Access Boundaries for Oneira ## Read Access -- `{project-root}/_bmad/.memory/dream-weaver-sidecar/` +- `{project-root}/_bmad/memory/dream-weaver-sidecar/` ## Write Access -- `{project-root}/_bmad/.memory/dream-weaver-sidecar/` +- `{project-root}/_bmad/memory/dream-weaver-sidecar/` ## Deny Zones - Everything outside the sidecar folder diff --git a/samples/bmad-agent-dream-weaver/lucid-coach.md b/samples/bmad-agent-dream-weaver/lucid-coach.md index 6f11bb5..a2bac21 100644 --- a/samples/bmad-agent-dream-weaver/lucid-coach.md +++ b/samples/bmad-agent-dream-weaver/lucid-coach.md @@ -45,7 +45,7 @@ Before beginning, check `coaching-profile.yaml` for `recall_baseline`. If recall ## Session Flow -1. **Load in parallel:** `{project-root}/_bmad/.memory/dream-weaver-sidecar/coaching-profile.yaml` for current level, active techniques, milestone status AND recent journal entries for dream sign review +1. **Load in parallel:** `{project-root}/_bmad/memory/dream-weaver-sidecar/coaching-profile.yaml` for current level, active techniques, milestone status AND recent journal entries for dream sign review 2. **Prior knowledge check** — "Have you tried any of these techniques already?" Skip known techniques and focus on gaps. 3. **Ask about progress** — "How have the reality checks been going? Any moments of doubt during the day?" 4. **Review recent dreams** — Look for dream signs, near-lucid moments, progress indicators diff --git a/samples/bmad-agent-dream-weaver/pattern-discovery.md b/samples/bmad-agent-dream-weaver/pattern-discovery.md index 4daff7c..ad8ac9a 100644 --- a/samples/bmad-agent-dream-weaver/pattern-discovery.md +++ b/samples/bmad-agent-dream-weaver/pattern-discovery.md @@ -12,7 +12,7 @@ Dive into the dream journal to find patterns the dreamer hasn't noticed yet. Thi ## Process -1. **Gather data in parallel** — Run `scripts/symbol_stats.py` against `{project-root}/_bmad/.memory/dream-weaver-sidecar/journal/` for current frequency data AND read `{project-root}/_bmad/.memory/dream-weaver-sidecar/coaching-profile.yaml` for coaching context. +1. **Gather data in parallel** — Run `scripts/symbol_stats.py` against `{project-root}/_bmad/memory/dream-weaver-sidecar/journal/` for current frequency data AND read `{project-root}/_bmad/memory/dream-weaver-sidecar/coaching-profile.yaml` for coaching context. - **Script fallback:** If `symbol_stats.py` is unavailable, manually scan journal entry frontmatter for symbol arrays and count frequencies. - **Session cache:** If `symbol_stats.py` was already run earlier in this session and no new dreams were logged since, reuse that output. diff --git a/samples/bmad-agent-dream-weaver/recall-training.md b/samples/bmad-agent-dream-weaver/recall-training.md index 20c344a..fd8b5f2 100644 --- a/samples/bmad-agent-dream-weaver/recall-training.md +++ b/samples/bmad-agent-dream-weaver/recall-training.md @@ -41,7 +41,7 @@ Help users remember more dreams, more vividly. Track progress and adapt exercise ## Session Flow -1. **Load in parallel:** `{project-root}/_bmad/.memory/dream-weaver-sidecar/coaching-profile.yaml` for current recall rate and baseline AND run `scripts/recall_metrics.py` against journal folder for current trends. +1. **Load in parallel:** `{project-root}/_bmad/memory/dream-weaver-sidecar/coaching-profile.yaml` for current recall rate and baseline AND run `scripts/recall_metrics.py` against journal folder for current trends. - **Script fallback:** If `recall_metrics.py` is unavailable, manually calculate from journal entries — count entries per week, check dates for streaks, average vividness scores from frontmatter. 3. **Celebrate progress** — Compare to baseline. "You started at 1 dream a week. You're at 3 now. That's real." 4. **Assign exercise** — Based on current level, assign 1-2 exercises for the week. Don't overwhelm. diff --git a/samples/bmad-agent-dream-weaver/references/memory-system.md b/samples/bmad-agent-dream-weaver/references/memory-system.md index 8c39896..d80369c 100644 --- a/samples/bmad-agent-dream-weaver/references/memory-system.md +++ b/samples/bmad-agent-dream-weaver/references/memory-system.md @@ -1,6 +1,6 @@ # Memory System for Oneira -**Memory location:** `{project-root}/_bmad/.memory/dream-weaver-sidecar/` +**Memory location:** `{project-root}/_bmad/memory/dream-weaver-sidecar/` ## Core Principle diff --git a/src/skills/bmad-agent-builder/assets/SKILL-template.md b/src/skills/bmad-agent-builder/assets/SKILL-template.md index 7a10d9c..c59cbaf 100644 --- a/src/skills/bmad-agent-builder/assets/SKILL-template.md +++ b/src/skills/bmad-agent-builder/assets/SKILL-template.md @@ -48,7 +48,7 @@ description: {skill-description} # Format: [4-6 word summary]. [trigger: "User w {if-sidecar} ## Sidecar -Memory location: `_bmad/.memory/{skillName}-sidecar/` +Memory location: `_bmad/memory/{skillName}-sidecar/` Load `references/memory-system.md` for memory discipline and structure. {/if-sidecar} @@ -68,9 +68,9 @@ Load `references/memory-system.md` for memory discipline and structure. {if-no-autonomous} 2. **Continue with steps below:** {/if-no-autonomous} - {if-sidecar}- **Check first-run** — If no `{skillName}-sidecar/` folder exists in `_bmad/.memory/`, load `init.md` for first-run setup - - **Load access boundaries** — Read `_bmad/.memory/{skillName}-sidecar/access-boundaries.md` to enforce read/write/deny zones (load before any file operations) - - **Load memory** — Read `_bmad/.memory/{skillName}-sidecar/index.md` for essential context and previous session{/if-sidecar} + {if-sidecar}- **Check first-run** — If no `{skillName}-sidecar/` folder exists in `_bmad/memory/`, load `init.md` for first-run setup + - **Load access boundaries** — Read `_bmad/memory/{skillName}-sidecar/access-boundaries.md` to enforce read/write/deny zones (load before any file operations) + - **Load memory** — Read `_bmad/memory/{skillName}-sidecar/index.md` for essential context and previous session{/if-sidecar} - **Greet the user** — Welcome `{user_name}`, speaking in `{communication_language}` and applying your persona and principles throughout the session {if-sidecar}- **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes{/if-sidecar} - **Present capabilities** — Show available capabilities to the user: diff --git a/src/skills/bmad-agent-builder/assets/autonomous-wake.md b/src/skills/bmad-agent-builder/assets/autonomous-wake.md index fb10ed1..0e7fd4a 100644 --- a/src/skills/bmad-agent-builder/assets/autonomous-wake.md +++ b/src/skills/bmad-agent-builder/assets/autonomous-wake.md @@ -9,7 +9,7 @@ You're running autonomously. No one is here. No task was specified. Execute your ## Context -- Memory location: `_bmad/.memory/{skillName}-sidecar/` +- Memory location: `_bmad/memory/{skillName}-sidecar/` - Activation time: `{current-time}` ## Instructions @@ -27,7 +27,7 @@ You're running autonomously. No one is here. No task was specified. Execute your ## Logging -Append to `_bmad/.memory/{skillName}-sidecar/autonomous-log.md`: +Append to `_bmad/memory/{skillName}-sidecar/autonomous-log.md`: ```markdown ## {YYYY-MM-DD HH:MM} - Autonomous Wake diff --git a/src/skills/bmad-agent-builder/assets/init-template.md b/src/skills/bmad-agent-builder/assets/init-template.md index 2a09d44..6195f88 100644 --- a/src/skills/bmad-agent-builder/assets/init-template.md +++ b/src/skills/bmad-agent-builder/assets/init-template.md @@ -5,7 +5,7 @@ Welcome! Setting up your workspace. ## Memory Location -Creating `_bmad/.memory/{skillName}-sidecar/` for persistent memory. +Creating `_bmad/memory/{skillName}-sidecar/` for persistent memory. ## Initial Structure @@ -30,7 +30,7 @@ Welcome! Let me set up for this environment. ## Memory Location -Creating `_bmad/.memory/{skillName}-sidecar/` for persistent memory. +Creating `_bmad/memory/{skillName}-sidecar/` for persistent memory. {custom-init-questions} diff --git a/src/skills/bmad-agent-builder/assets/memory-system.md b/src/skills/bmad-agent-builder/assets/memory-system.md index 2af96be..3b5ede8 100644 --- a/src/skills/bmad-agent-builder/assets/memory-system.md +++ b/src/skills/bmad-agent-builder/assets/memory-system.md @@ -1,6 +1,6 @@ # Memory System for {displayName} -**Memory location:** `_bmad/.memory/{skillName}-sidecar/` +**Memory location:** `_bmad/memory/{skillName}-sidecar/` ## Core Principle diff --git a/src/skills/bmad-agent-builder/build-process.md b/src/skills/bmad-agent-builder/build-process.md index 176c1ee..afcbbc2 100644 --- a/src/skills/bmad-agent-builder/build-process.md +++ b/src/skills/bmad-agent-builder/build-process.md @@ -86,7 +86,7 @@ Work through these conversationally: **Key distinction:** Folder dominion (where things live) ≠ agent memory (what persists across sessions) - **Path Conventions** (CRITICAL for reliable agent behavior): - - **Memory location:** `{project-root}/_bmad/.memory/{skillName}-sidecar/` + - **Memory location:** `{project-root}/_bmad/memory/{skillName}-sidecar/` - **Project artifacts:** `{project-root}/_bmad/...` when referencing project-level files - **Skill-internal files:** Use relative paths (`references/`, `scripts/`) - **Config variables:** Use directly — they already contain full paths (NO `{project-root}` prefix) diff --git a/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md b/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md index dbc79cc..1a9e2d4 100644 --- a/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md +++ b/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md @@ -83,7 +83,7 @@ Don't read files in parent when you could delegate the reading. ``` BAD: Load all memory -1. Read all files in _bmad/.memory/{skillName}-sidecar/ +1. Read all files in _bmad/memory/{skillName}-sidecar/ GOOD: Selective loading 1. Read index.md for configuration diff --git a/src/skills/bmad-agent-builder/references/standard-fields.md b/src/skills/bmad-agent-builder/references/standard-fields.md index af55fac..08d1ef3 100644 --- a/src/skills/bmad-agent-builder/references/standard-fields.md +++ b/src/skills/bmad-agent-builder/references/standard-fields.md @@ -48,13 +48,13 @@ This skill {what it does}. Use when {when to use}. Returns {output format} with ### Memory Files (sidecar) -Always use: `{project-root}/_bmad/.memory/{skillName}-sidecar/` +Always use: `{project-root}/_bmad/memory/{skillName}-sidecar/` Examples: -- `{project-root}/_bmad/.memory/journaling-companion-sidecar/index.md` -- `{project-root}/_bmad/.memory/journaling-companion-sidecar/access-boundaries.md` — **Required** -- `{project-root}/_bmad/.memory/journaling-companion-sidecar/autonomous-log.md` -- `{project-root}/_bmad/.memory/journaling-companion-sidecar/references/tags-reference.md` +- `{project-root}/_bmad/memory/journaling-companion-sidecar/index.md` +- `{project-root}/_bmad/memory/journaling-companion-sidecar/access-boundaries.md` — **Required** +- `{project-root}/_bmad/memory/journaling-companion-sidecar/autonomous-log.md` +- `{project-root}/_bmad/memory/journaling-companion-sidecar/references/tags-reference.md` ### Access Boundaries (Standard for all agents) @@ -91,9 +91,9 @@ Example pattern: ## Autonomous Mode When run autonomously: -1. Load `{project-root}/_bmad/.memory/{skillName}-sidecar/index.md` to get user's journal location +1. Load `{project-root}/_bmad/memory/{skillName}-sidecar/index.md` to get user's journal location 2. Read entries from that location -3. Write results to `{project-root}/_bmad/.memory/{skillName}-sidecar/autonomous-log.md` +3. Write results to `{project-root}/_bmad/memory/{skillName}-sidecar/autonomous-log.md` ``` ## CLI Usage (Autonomous Agents) diff --git a/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py b/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py index 6f3d730..32c50e5 100644 --- a/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py +++ b/src/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py @@ -218,7 +218,7 @@ def extract_memory_paths(skill_path: Path) -> tuple[list[str], list[dict]]: memory_paths = set() # Memory path patterns - mem_pattern = re.compile(r'(?:memory/|sidecar/|\.memory/|\.sidecar/)[\w\-/]+(?:\.\w+)?') + mem_pattern = re.compile(r'(?:memory/|sidecar/)[\w\-/]+(?:\.\w+)?') files_to_scan = [] @@ -240,7 +240,7 @@ def extract_memory_paths(skill_path: Path) -> tuple[list[str], list[dict]]: sorted_paths = sorted(memory_paths) - # Check for inconsistent formats (e.g., mixing memory/ and .memory/) + # Check for inconsistent formats prefixes = set() for p in sorted_paths: prefix = p.split('/')[0] diff --git a/src/skills/bmad-agent-builder/scripts/scan-path-standards.py b/src/skills/bmad-agent-builder/scripts/scan-path-standards.py index b919a33..4a37184 100644 --- a/src/skills/bmad-agent-builder/scripts/scan-path-standards.py +++ b/src/skills/bmad-agent-builder/scripts/scan-path-standards.py @@ -7,7 +7,7 @@ 3. Config variables used directly (no double-prefix) 4. No ./ or ../ relative prefixes 5. No absolute paths -6. Memory paths must use {project-root}/_bmad/.memory/{skillName}-sidecar/ +6. Memory paths must use {project-root}/_bmad/memory/{skillName}-sidecar/ """ # /// script @@ -37,9 +37,9 @@ RELATIVE_DOT_RE = re.compile(r'(?:^|[\s"`\'(])(\.\./\S+)', re.MULTILINE) RELATIVE_DOTSLASH_RE = re.compile(r'(?:^|[\s"`\'(])(\./\S+)', re.MULTILINE) -# Memory path pattern: should use {project-root}/_bmad/.memory/ -MEMORY_PATH_RE = re.compile(r'_bmad/.memory/\S+') -VALID_MEMORY_PATH_RE = re.compile(r'\{project-root\}/_bmad/.memory/\S+-sidecar/') +# Memory path pattern: should use {project-root}/_bmad/memory/ +MEMORY_PATH_RE = re.compile(r'_bmad/memory/\S+') +VALID_MEMORY_PATH_RE = re.compile(r'\{project-root\}/_bmad/memory/\S+-sidecar/') # Fenced code block detection (to skip examples showing wrong patterns) FENCE_RE = re.compile(r'^```', re.MULTILINE) @@ -118,7 +118,7 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: 'action': '', }) - # Memory path check — memory paths should use {project-root}/_bmad/.memory/{skillName}-sidecar/ + # Memory path check — memory paths should use {project-root}/_bmad/memory/{skillName}-sidecar/ for match in MEMORY_PATH_RE.finditer(content): pos = match.start() if skip_fenced and is_in_fenced_block(content, pos): @@ -135,7 +135,7 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: 'line': line_num, 'severity': 'high', 'category': 'memory-path', - 'title': 'Memory path missing {project-root} prefix — use {project-root}/_bmad/.memory/', + 'title': 'Memory path missing {project-root} prefix — use {project-root}/_bmad/memory/', 'detail': line_content[:120], 'action': '', }) diff --git a/src/skills/bmad-workflow-builder/references/standard-fields.md b/src/skills/bmad-workflow-builder/references/standard-fields.md index ddc87e7..2fd2556 100644 --- a/src/skills/bmad-workflow-builder/references/standard-fields.md +++ b/src/skills/bmad-workflow-builder/references/standard-fields.md @@ -109,7 +109,7 @@ Use bare relative paths (no prefix): ### Project `_bmad` Paths Use `{project-root}/_bmad/...`: - `{project-root}/_bmad/planning/prd.md` -- `{project-root}/_bmad/.memory/{skillName}-sidecar/` +- `{project-root}/_bmad/memory/{skillName}-sidecar/` ### Config Variables Use directly — they already contain `{project-root}` in their resolved values: From 4143caf88247189a8b18b76c65ccd6f3afa15867 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 16:26:56 -0500 Subject: [PATCH 08/10] Fix CI checks: remove eslint/validate, add doc link validator, fix formatting --- .github/workflows/publish.yaml | 6 +- .github/workflows/quality.yaml | 53 +-- .markdownlint-cli2.yaml | 1 + package.json | 5 +- .../bmad-builder-setup/assets/module.yaml | 2 +- tools/validate-doc-links.cjs | 407 ++++++++++++++++++ 6 files changed, 417 insertions(+), 57 deletions(-) create mode 100644 tools/validate-doc-links.cjs diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 1544a61..288ab57 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -3,7 +3,7 @@ name: Publish to NPM on: push: tags: - - 'v*' + - "v*" permissions: contents: read @@ -18,8 +18,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' - registry-url: 'https://registry.npmjs.org' + node-version: "20" + registry-url: "https://registry.npmjs.org" - name: Install dependencies run: npm install diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index 6519455..bc0b65f 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -1,13 +1,9 @@ name: Quality & Validation -# Runs comprehensive quality checks on all PRs: +# Runs quality checks on all PRs: # - Prettier (formatting) -# - ESLint (linting) # - markdownlint (markdown quality) -# - Schema validation (YAML structure) -# - Agent schema tests (fixture-based validation) -# - Installation component tests (compilation) -# - Bundle validation (web bundle integrity) +# - Documentation build "on": pull_request: @@ -33,24 +29,6 @@ jobs: - name: Prettier format check run: npm run format:check - eslint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version-file: ".nvmrc" - cache: "npm" - - - name: Install dependencies - run: npm ci - - - name: ESLint - run: npm run lint - markdownlint: runs-on: ubuntu-latest steps: @@ -89,30 +67,3 @@ jobs: - name: Build documentation run: npm run docs:build - - validate: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version-file: ".nvmrc" - cache: "npm" - - - name: Install dependencies - run: npm ci - - - name: Validate YAML schemas - run: npm run validate:schemas - - - name: Run agent schema validation tests - run: npm run test:schemas - - - name: Test agent compilation components - run: npm run test:install - - - name: Validate file references - run: npm run validate:refs diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index d9a85fe..1818d75 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -7,6 +7,7 @@ ignores: - CODE_OF_CONDUCT.md - _bmad/** - _bmad*/** + - src/skills/** - .*/** - z*/** diff --git a/package.json b/package.json index 65a3b21..af756d9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "bmad-builder", - "type": "module", "version": "1.1.0", "description": "A BMad Core expansion module that guides users through the creation of Modules Workflow and Agents", "keywords": [ @@ -13,15 +12,17 @@ }, "license": "MIT", "author": "Brian (BMad) Madison", + "type": "module", + "main": "", "files": [ "src/", "CHANGELOG.md" ], - "main": "", "scripts": { "docs:build": "node tools/build-docs.mjs", "docs:dev": "astro dev --root website", "docs:preview": "astro preview --root website", + "docs:validate-links": "node tools/validate-doc-links.cjs", "format:check": "prettier --check \"**/*.{js,cjs,mjs,json,yaml}\"", "format:fix": "prettier --write \"**/*.{js,cjs,mjs,json,yaml}\"", "lint": "eslint . --ext .js,.cjs,.mjs,.yaml --max-warnings=0", diff --git a/src/skills/bmad-builder-setup/assets/module.yaml b/src/skills/bmad-builder-setup/assets/module.yaml index 5bb47ef..f8bb94d 100644 --- a/src/skills/bmad-builder-setup/assets/module.yaml +++ b/src/skills/bmad-builder-setup/assets/module.yaml @@ -6,7 +6,7 @@ default_selected: false module_greeting: > Enjoy making your dream creations with the BMad Builder Module! Run this again at any time if you want to reconfigure a setting or have updated the module, (or optionally just update _bmad/config.yaml and config.user.yaml to change existing values) - + For questions, suggestions and support - check us on Discord at https://discord.gg/gk8jAdXWmj bmad_builder_output_folder: diff --git a/tools/validate-doc-links.cjs b/tools/validate-doc-links.cjs new file mode 100644 index 0000000..167268c --- /dev/null +++ b/tools/validate-doc-links.cjs @@ -0,0 +1,407 @@ +/** + * Documentation Link Validator + * + * Validates site-relative links in markdown files and attempts to fix broken ones. + * + * What it checks: + * - All site-relative links (starting with /) point to existing .md files + * - Anchor links (#section) point to valid headings + * + * What it fixes: + * - Broken links where the target file can be found elsewhere in /docs + * + * Usage: + * node tools/validate-doc-links.js # Dry run (validate and show issues) + * node tools/validate-doc-links.js --write # Fix auto-fixable issues + */ + +const fs = require('node:fs'); +const path = require('node:path'); + +const DOCS_ROOT = path.resolve(__dirname, '../docs'); +const DRY_RUN = !process.argv.includes('--write'); + +// Regex to match markdown links with site-relative paths or bare .md references +const LINK_REGEX = /\[([^\]]*)\]\(((?:\.{1,2}\/|\/)[^)]+|[\w][^)\s]*\.md(?:[?#][^)]*)?)\)/g; + +// File extensions that are static assets, not markdown docs +const STATIC_ASSET_EXTENSIONS = ['.zip', '.txt', '.pdf', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico']; + +// Custom Astro page routes (not part of the docs content collection) +const CUSTOM_PAGE_ROUTES = new Set([]); + +// Regex to extract headings for anchor validation +const HEADING_PATTERN = /^#{1,6}\s+(.+)$/gm; + +/** + * Get all markdown files in docs directory, excluding _* directories/files + */ +function getMarkdownFiles(dir) { + const files = []; + + function walk(currentDir) { + const entries = fs.readdirSync(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(currentDir, entry.name); + + if (entry.name.startsWith('_')) { + continue; + } + + if (entry.isDirectory()) { + walk(fullPath); + } else if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mdx'))) { + files.push(fullPath); + } + } + } + + walk(dir); + return files; +} + +/** + * Strip fenced code blocks from content + */ +function stripCodeBlocks(content) { + return content.replaceAll(/```[\s\S]*?```/g, ''); +} + +/** + * Convert a heading to its anchor slug + */ +function headingToAnchor(heading) { + return heading + .toLowerCase() + .replaceAll(/[\u{1F300}-\u{1F9FF}]/gu, '') // Remove emojis + .replaceAll(/[^\w\s-]/g, '') // Remove special chars + .replaceAll(/\s+/g, '-') // Spaces to hyphens + .replaceAll(/-+/g, '-') // Collapse hyphens + .replaceAll(/^-+|-+$/g, ''); // Trim hyphens +} + +/** + * Extract anchor slugs from a markdown file + */ +function extractAnchors(content) { + const anchors = new Set(); + let match; + + HEADING_PATTERN.lastIndex = 0; + while ((match = HEADING_PATTERN.exec(content)) !== null) { + const headingText = match[1] + .trim() + .replaceAll(/`[^`]+`/g, '') + .replaceAll(/\*\*([^*]+)\*\*/g, '$1') + .replaceAll(/\*([^*]+)\*/g, '$1') + .replaceAll(/\[([^\]]+)\]\([^)]+\)/g, '$1') + .trim(); + anchors.add(headingToAnchor(headingText)); + } + + return anchors; +} + +/** + * Resolve a site-relative link to a file path + * /docs/how-to/installation/install-bmad.md -> docs/how-to/installation/install-bmad.md + * /how-to/installation/install-bmad/ -> docs/how-to/installation/install-bmad.md or .../index.md + */ +function resolveLink(siteRelativePath, sourceFile) { + // Strip anchor and query + let checkPath = siteRelativePath.split('#')[0].split('?')[0]; + + // Handle relative paths (including bare .md): resolve from source file's directory + if (checkPath.startsWith('./') || checkPath.startsWith('../') || (!checkPath.startsWith('/') && checkPath.endsWith('.md'))) { + const sourceDir = path.dirname(sourceFile); + const resolved = path.resolve(sourceDir, checkPath); + // Ensure the resolved path stays within DOCS_ROOT + if (!resolved.startsWith(DOCS_ROOT + path.sep) && resolved !== DOCS_ROOT) return null; + if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) return resolved; + if (fs.existsSync(resolved + '.md')) return resolved + '.md'; + if (fs.existsSync(resolved + '.mdx')) return resolved + '.mdx'; + // Directory: check for index.md or index.mdx + if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) { + const indexFile = path.join(resolved, 'index.md'); + const indexMdxFile = path.join(resolved, 'index.mdx'); + if (fs.existsSync(indexFile)) return indexFile; + if (fs.existsSync(indexMdxFile)) return indexMdxFile; + } + return null; + } + + // Strip /docs/ prefix if present (legacy absolute links) + if (checkPath.startsWith('/docs/')) { + checkPath = checkPath.slice(5); // Remove '/docs' but keep leading '/' + } + + if (checkPath.endsWith('/')) { + // Could be file.md, file.mdx, or directory/index.md/mdx + const baseName = checkPath.slice(0, -1); + const asMd = path.join(DOCS_ROOT, baseName + '.md'); + const asMdx = path.join(DOCS_ROOT, baseName + '.mdx'); + const asIndex = path.join(DOCS_ROOT, checkPath, 'index.md'); + const asIndexMdx = path.join(DOCS_ROOT, checkPath, 'index.mdx'); + + if (fs.existsSync(asMd)) return asMd; + if (fs.existsSync(asMdx)) return asMdx; + if (fs.existsSync(asIndex)) return asIndex; + if (fs.existsSync(asIndexMdx)) return asIndexMdx; + return null; + } + + // Direct path (e.g., /path/file.md) + const direct = path.join(DOCS_ROOT, checkPath); + if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct; + + // Try with .md extension + const withMd = direct + '.md'; + if (fs.existsSync(withMd)) return withMd; + + // Try with .mdx extension + const withMdx = direct + '.mdx'; + if (fs.existsSync(withMdx)) return withMdx; + + // Directory without trailing slash: check for index.md or index.mdx + if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) { + const indexFile = path.join(direct, 'index.md'); + const indexMdxFile = path.join(direct, 'index.mdx'); + if (fs.existsSync(indexFile)) return indexFile; + if (fs.existsSync(indexMdxFile)) return indexMdxFile; + } + + return null; +} + +/** + * Search for a file with directory context + */ +function findFileWithContext(brokenPath) { + // Extract filename and parent directory from the broken path + // e.g., /tutorials/getting-started/foo/ -> parent: getting-started, file: foo.md + const cleanPath = brokenPath.replace(/\/$/, '').replace(/^(\.\.\/|\.\/|\/)+/, ''); + const parts = cleanPath.split('/'); + const fileName = parts.at(-1) + '.md'; + const parentDir = parts.length > 1 ? parts.at(-2) : null; + + const allFiles = getMarkdownFiles(DOCS_ROOT); + const matches = []; + + for (const file of allFiles) { + const fileBaseName = path.basename(file); + const fileParentDir = path.basename(path.dirname(file)); + + // Exact filename match with parent directory context + if (fileBaseName === fileName) { + if (parentDir && fileParentDir === parentDir) { + // Strong match: both filename and parent dir match + return [file]; + } + matches.push(file); + } + + // Also check for index.md in a matching directory + if (fileBaseName === 'index.md' && fileParentDir === fileName.replace('.md', '')) { + matches.push(file); + } + } + + return matches; +} + +/** + * Convert absolute file path to site-relative URL + */ +function fileToSiteRelative(filePath) { + let relative = '/' + path.relative(DOCS_ROOT, filePath); + relative = relative.split(path.sep).join('/'); + + if (relative.endsWith('/index.md')) { + return relative.replace(/\/index\.md$/, '/'); + } + return relative.replace(/\.md$/, '/'); +} + +/** + * Process a single file and find issues + */ +function processFile(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const strippedContent = stripCodeBlocks(content); + const issues = []; + + let match; + LINK_REGEX.lastIndex = 0; + + while ((match = LINK_REGEX.exec(strippedContent)) !== null) { + const linkText = match[1]; + const href = match[2]; + + // Extract path and anchor + const hashIndex = href.indexOf('#'); + const linkPath = hashIndex === -1 ? href : href.slice(0, hashIndex); + const anchor = hashIndex === -1 ? null : href.slice(hashIndex + 1); + + // Skip static asset links (zip, txt, images, etc.) + const linkLower = linkPath.toLowerCase(); + if (STATIC_ASSET_EXTENSIONS.some((ext) => linkLower.endsWith(ext))) { + continue; + } + + // Skip custom Astro page routes + if (CUSTOM_PAGE_ROUTES.has(linkPath)) { + continue; + } + + // Validate the link target exists + const targetFile = resolveLink(linkPath, filePath); + + if (!targetFile) { + // Link is broken - try to find the file + const candidates = findFileWithContext(linkPath); + + const issue = { + type: 'broken-link', + linkText, + href, + linkPath, + fullMatch: match[0], + }; + + if (candidates.length === 1) { + issue.status = 'auto-fixable'; + issue.suggestedFix = fileToSiteRelative(candidates[0]) + (anchor ? '#' + anchor : ''); + issue.foundAt = path.relative(DOCS_ROOT, candidates[0]); + } else if (candidates.length > 1) { + issue.status = 'needs-review'; + issue.candidates = candidates.map((c) => path.relative(DOCS_ROOT, c)); + } else { + issue.status = 'manual-check'; + } + + issues.push(issue); + continue; + } + + // Validate anchor if present + if (anchor) { + const targetContent = fs.readFileSync(targetFile, 'utf-8'); + const anchors = extractAnchors(targetContent); + + if (!anchors.has(anchor)) { + issues.push({ + type: 'broken-anchor', + linkText, + href, + anchor, + status: 'manual-check', + message: `Anchor "#${anchor}" not found`, + }); + } + } + } + + return { content, issues }; +} + +/** + * Apply fixes to file content + */ +function applyFixes(content, issues) { + let updated = content; + + for (const issue of issues) { + if (issue.status === 'auto-fixable' && issue.suggestedFix) { + const oldLink = `[${issue.linkText}](${issue.href})`; + const newLink = `[${issue.linkText}](${issue.suggestedFix})`; + updated = updated.replace(oldLink, newLink); + } + } + + return updated; +} + +// Main execution +console.log(`\nValidating docs in: ${DOCS_ROOT}`); +console.log(`Mode: ${DRY_RUN ? 'DRY RUN (use --write to fix)' : 'WRITE MODE'}\n`); + +const files = getMarkdownFiles(DOCS_ROOT); +console.log(`Found ${files.length} markdown files\n`); + +let totalIssues = 0; +let autoFixable = 0; +let needsReview = 0; +let manualCheck = 0; +let filesWithIssues = 0; + +const allIssues = []; + +for (const filePath of files) { + const relativePath = path.relative(DOCS_ROOT, filePath); + const { content, issues } = processFile(filePath); + + if (issues.length > 0) { + filesWithIssues++; + totalIssues += issues.length; + + console.log(`\n${relativePath}`); + + for (const issue of issues) { + if (issue.status === 'auto-fixable') { + autoFixable++; + console.log(` [FIX] ${issue.href}`); + console.log(` -> ${issue.suggestedFix}`); + } else if (issue.status === 'needs-review') { + needsReview++; + console.log(` [REVIEW] ${issue.href}`); + console.log(` Multiple matches found:`); + for (const candidate of issue.candidates) { + console.log(` - ${candidate}`); + } + } else if (issue.type === 'broken-anchor') { + manualCheck++; + console.log(` [MANUAL] ${issue.href}`); + console.log(` ${issue.message}`); + } else { + manualCheck++; + console.log(` [MANUAL] ${issue.href}`); + console.log(` File not found anywhere - may need to remove link`); + } + + allIssues.push({ file: relativePath, ...issue }); + } + + // Apply fixes if not dry run + if (!DRY_RUN) { + const fixableIssues = issues.filter((i) => i.status === 'auto-fixable'); + if (fixableIssues.length > 0) { + const updated = applyFixes(content, fixableIssues); + fs.writeFileSync(filePath, updated, 'utf-8'); + } + } + } +} + +console.log(`\n${'─'.repeat(60)}`); +console.log(`\nSummary:`); +console.log(` Files scanned: ${files.length}`); +console.log(` Files with issues: ${filesWithIssues}`); +console.log(` Total issues: ${totalIssues}`); + +if (totalIssues > 0) { + console.log(`\n Breakdown:`); + console.log(` Auto-fixable: ${autoFixable}`); + console.log(` Needs review: ${needsReview}`); + console.log(` Manual check: ${manualCheck}`); +} + +if (totalIssues === 0) { + console.log(`\n All links valid!`); +} else if (DRY_RUN && autoFixable > 0) { + console.log(`\nRun with --write to auto-fix ${autoFixable} issue(s)`); +} + +console.log(''); + +process.exit(totalIssues > 0 ? 1 : 0); From f2ca0d96bd218b4ed450bde3f5487c2b635c0e3e Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 17:16:40 -0500 Subject: [PATCH 09/10] Simplify builder templates to outcome-based config pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite both agent and workflow SKILL-templates to be minimal skeletons rather than prescriptive blueprints. Templates now cover frontmatter, overview, and activation with config loading — the builder crafts everything else based on discovery phases. Key changes: - Config loading uses module/standalone pattern with sensible defaults and setup skill awareness (matching the builder's own activation) - Standardize on "headless" terminology, remove "autonomous" mixing - Headless reduced to one sentence instead of 25-line specification - Remove rigid workflow steps/stages/menus from templates - Update substitution rules, classification reference, and complex workflow patterns to match --- .../assets/SKILL-template.md | 80 +++---------- .../references/template-substitution-rules.md | 77 ++++-------- .../assets/SKILL-template.md | 110 ++---------------- .../references/classification-reference.md | 6 +- .../references/complex-workflow-patterns.md | 34 ++++-- .../references/template-substitution-rules.md | 66 ++--------- 6 files changed, 89 insertions(+), 284 deletions(-) diff --git a/src/skills/bmad-agent-builder/assets/SKILL-template.md b/src/skills/bmad-agent-builder/assets/SKILL-template.md index c59cbaf..1c06906 100644 --- a/src/skills/bmad-agent-builder/assets/SKILL-template.md +++ b/src/skills/bmad-agent-builder/assets/SKILL-template.md @@ -1,53 +1,31 @@ --- -name: bmad-{module-code-or-empty}-agent-{agent-name} -description: {skill-description} # Format: [4-6 word summary]. [trigger: "User wants to talk to or ask {displayName}" or "{title}" or "{role}"] +name: bmad-{module-code-or-empty}agent-{agent-name} +description: {skill-description} # [4-6 word summary]. [trigger phrases] --- # {displayName} ## Overview -{overview-template} - -{if-headless} -## Activation Mode Detection - -**Check activation context immediately:** - -1. **Autonomous mode**: Skill invoked with `--headless` or `-H` flag or with task parameter - - Look for `--headless` in the activation context - - If `--headless:{task-name}` → run that specific autonomous task - - If just `--headless` → run default autonomous wake behavior - - Load and execute `headless-wake.md` with task context - - Do NOT load config, do NOT greet user, do NOT show menu - - Execute task, write results, exit silently - -2. **Interactive mode** (default): User invoked the skill directly - - Proceed to `## On Activation` section below - -**Example headless activation:** -```bash -# Autonomous - default wake -/bmad-{agent-skill-name} --headless - -# Autonomous - specific task -/bmad-{agent-skill-name} --headless:refine-memories -``` -{/if-headless} +{overview — concise: who this agent is, what it does, args/modes supported, and the outcome. This is the main help output for the skill.} ## Identity + {Who is this agent? One clear sentence.} ## Communication Style + {How does this agent communicate? Be specific with examples.} ## Principles + - {Guiding principle 1} - {Guiding principle 2} - {Guiding principle 3} {if-sidecar} ## Sidecar + Memory location: `_bmad/memory/{skillName}-sidecar/` Load `references/memory-system.md` for memory discipline and structure. @@ -55,35 +33,15 @@ Load `references/memory-system.md` for memory discipline and structure. ## On Activation -1. **Load config** from `{project-root}/_bmad/config.yaml` — Store all returned vars for use: - - Use `{user_name}` from config for greeting - - Use `{communication_language}` from config for all communications - - Store any other config variables as `{var-name}` and use appropriately - -{if-autonomous} -2. **If autonomous mode** — Load and run `autonomous-wake.md` (default wake behavior), or load the specified prompt and execute its autonomous section without interaction - -3. **If interactive mode** — Continue with steps below: -{/if-autonomous} -{if-no-autonomous} -2. **Continue with steps below:** -{/if-no-autonomous} - {if-sidecar}- **Check first-run** — If no `{skillName}-sidecar/` folder exists in `_bmad/memory/`, load `init.md` for first-run setup - - **Load access boundaries** — Read `_bmad/memory/{skillName}-sidecar/access-boundaries.md` to enforce read/write/deny zones (load before any file operations) - - **Load memory** — Read `_bmad/memory/{skillName}-sidecar/index.md` for essential context and previous session{/if-sidecar} - - **Greet the user** — Welcome `{user_name}`, speaking in `{communication_language}` and applying your persona and principles throughout the session - {if-sidecar}- **Check for autonomous updates** — Briefly check if autonomous tasks ran since last session and summarize any changes{/if-sidecar} - - **Present capabilities** — Show available capabilities to the user: - - ``` - {if-sidecar}Last time we were working on X. Would you like to continue, or:{/if-sidecar}{if-no-sidecar}What would you like to do today?{/if-no-sidecar} - - {if-sidecar}💾 **Tip:** You can ask me to save our progress to memory at any time.{/if-sidecar} - - **Available capabilities:** - {number}. [{menu-code}] - {description} - ``` - -**CRITICAL Handling:** When user selects a capability: -- **Internal capability** — Load and use the actual prompt from `{name}.md` — DO NOT invent the capability on the fly -- **External skill** — Invoke the skill by its exact registered name +{if-module} +Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root level and `{module-code}` section). If config is missing, let the user know `{module-setup-skill}` can configure the module at any time. Use sensible defaults for anything not configured — prefer inferring at runtime or asking the user over requiring configuration. +{/if-module} +{if-standalone} +Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` if present. Use sensible defaults for anything not configured. +{/if-standalone} + +{if-headless} +If `--headless` or `-H` is passed, complete the requested task without asking for user input, using sensible defaults for any decisions. +{/if-headless} + +{The rest of the agent — activation flow, capabilities, sidecar initialization, capability routing — is determined by what the agent needs. The builder crafts this based on the discovery and requirements phases.} diff --git a/src/skills/bmad-agent-builder/references/template-substitution-rules.md b/src/skills/bmad-agent-builder/references/template-substitution-rules.md index d0e511f..d131608 100644 --- a/src/skills/bmad-agent-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-agent-builder/references/template-substitution-rules.md @@ -1,71 +1,44 @@ # Template Substitution Rules -When building the agent, you MUST apply these conditional blocks to the templates: +The SKILL-template provides a minimal skeleton: frontmatter, overview, agent identity sections, sidecar, and activation with config loading. Everything beyond that is crafted by the builder based on what was learned during discovery and requirements phases. -## For Module-Based Agents +## Frontmatter +- `{module-code-or-empty}` → Module code prefix with hyphen (e.g., `cis-`) or empty for standalone +- `{agent-name}` → Agent functional name (kebab-case) +- `{skill-description}` → Two parts: [4-6 word summary]. [trigger phrases] +- `{displayName}` → Friendly display name +- `{skillName}` → Full skill name with module prefix + +## Module Conditionals + +### For Module-Based Agents - `{if-module}` ... `{/if-module}` → Keep the content inside - `{if-standalone}` ... `{/if-standalone}` → Remove the entire block including markers -- `{custom-config-properties}` → Replace with comma-separated custom property names (e.g., `journal_folder, adventure_logs_folder`) or remove line if none -- `{module-code-or-empty}` → Replace with module code (e.g., `cis-`) or empty string for standalone - -## For Standalone Agents +- `{module-code}` → Module code without trailing hyphen (e.g., `cis`) +- `{module-setup-skill}` → Name of the module's setup skill (e.g., `bmad-cis-setup`) +### For Standalone Agents - `{if-module}` ... `{/if-module}` → Remove the entire block including markers - `{if-standalone}` ... `{/if-standalone}` → Keep the content inside -- `{custom-config-properties}` → Remove (not used for standalone) -- `{module-code-or-empty}` → Empty string -- `{custom-init-questions}` → Add user's additional questions here (remove placeholder if none) - -## For Agents With Sidecar (Memory) - -- `{if-sidecar}` ... `{/if-sidecar}` → Keep the content inside -- `{if-no-sidecar}` ... `{/if-no-sidecar}` → Remove the entire block including markers -## For Agents Without Sidecar +## Sidecar Conditionals -- `{if-sidecar}` ... `{/if-sidecar}` → Remove the entire block including markers -- `{if-no-sidecar}` ... `{/if-no-sidecar}` → Keep the content inside +- `{if-sidecar}` ... `{/if-sidecar}` → Keep if agent has persistent memory, otherwise remove +- `{if-no-sidecar}` ... `{/if-no-sidecar}` → Inverse of above -## External Skills +## Headless Conditional -- `{if-external-skills}` ... `{/if-external-skills}` → Keep if agent uses external skills, otherwise remove entire block -- `{external-skills-list}` → Replace with bulleted list of exact skill names: - ```markdown - - `bmad-skill-name-one` — Description - - `bmad-skill-name-two` — Description - ``` +- `{if-headless}` ... `{/if-headless}` → Keep if agent supports headless mode, otherwise remove -## Custom Init Questions +## Beyond the Template -Add user's additional questions to the init.md template, replacing `{custom-init-questions}` placeholder. Remove the placeholder line if no custom questions. +The builder determines the rest of the agent structure — capabilities, activation flow, sidecar initialization, capability routing, external skills, scripts — based on the agent's requirements. The template intentionally does not prescribe these. ## Path References -All generated agents use these paths: -- `init.md` — First-run setup +All generated agents use these paths when applicable: +- `init.md` — First-run setup (if sidecar) - `{name}.md` — Individual capability prompts -- `references/memory-system.md` — Memory discipline (if sidecar needed) -- `scripts/` — Python/shell scripts for deterministic operations (if needed) - -## Frontmatter Placeholders - -Replace all frontmatter placeholders in SKILL-template.md: -- `{module-code-or-empty}` → Module code (e.g., `cis-`) or empty -- `{agent-name}` → Agent functional name (kebab-case) -- `{short phrase what agent does}` → One-line description -- `{displayName}` → Friendly name -- `{title}` → Role title -- `{role}` → Functional role -- `{skillName}` → Full skill name with module prefix -- `{user_name}` → From config -- `{communication_language}` → From config - -## Content Placeholders - -Replace all content placeholders with agent-specific values: -- `{overview-template}` → Overview paragraph (2-3 sentences) following the 3-part formula (What, How, Why/Outcome) -- `{One-sentence identity.}` → Brief identity statement -- `{Who is this agent? One clear sentence.}` → Identity description -- `{How does this agent communicate? Be specific with examples.}` → Communication style -- `{Guiding principle 1/2/3}` → Agent's principles +- `references/memory-system.md` — Memory discipline (if sidecar) +- `scripts/` — Python/shell scripts for deterministic operations diff --git a/src/skills/bmad-workflow-builder/assets/SKILL-template.md b/src/skills/bmad-workflow-builder/assets/SKILL-template.md index 69a8139..70573a7 100644 --- a/src/skills/bmad-workflow-builder/assets/SKILL-template.md +++ b/src/skills/bmad-workflow-builder/assets/SKILL-template.md @@ -1,113 +1,21 @@ --- name: bmad-{module-code-or-empty}{skill-name} -description: {skill-description} # Format: [5-8 word summary]. [trigger phrase, e.g. Use when user says "create xyz"] +description: {skill-description} # [5-8 word summary]. [trigger phrases, e.g. Use when user says create xyz or wants to do abc] --- # {skill-name} ## Overview -{overview-template} - -{if-simple-utility} -## Input - -{input-format-description} - -## Process - -{processing-steps} - -## Output - -{output-format-description} -{/if-simple-utility} - -{if-simple-workflow} -Act as {role-guidance}. - -## On Activation - -1. **Load config** from `{project-root}/_bmad/config.yaml` — Store all returned vars for use: - - Use `{user_name}` from config for greeting - - Use `{communication_language}` for all communications - {if-creates-docs}- Use `{document_output_language}` for output documents{/if-creates-docs} - -2. **Greet user** as `{user_name}`, speaking in `{communication_language}` - -3. **Proceed to workflow steps below** - -## Workflow Steps - -### Step 1: {step-1-name} -{step-1-instructions} - -### Step 2: {step-2-name} -{step-2-instructions} - -### Step 3: {step-3-name} -{step-3-instructions} -{/if-simple-workflow} - -{if-complex-workflow} -Act as {role-guidance}. - -{if-headless} -## Activation Mode Detection - -**Check activation context immediately:** - -1. **Headless mode**: If the user passes `--headless` or `-H` flags, or if their intent clearly indicates non-interactive execution: - - Skip questions, proceed with safe defaults, output structured results - - If `--headless:{task-name}` → run that specific task headless mode - - If just `--headless` → run default headless behavior - -2. **Interactive mode** (default): Proceed to `## On Activation` section below -{/if-headless} +{overview — concise: what it does, args supported, and the outcome for the singular or different paths. This overview needs to contain succinct information for the llm as this is the main provision of help output for the skill.} ## On Activation -1. **Load config** from `{project-root}/_bmad/config.yaml` — Store all returned vars for use: - - Use `{user_name}` from config for greeting - - Use `{communication_language}` for all communications - {if-creates-docs}- Use `{document_output_language}` for output documents{/if-creates-docs} - - Store any other config variables as `{var-name}` and use appropriately - -2. **Greet user** as `{user_name}`, speaking in `{communication_language}` - -3. **Check if workflow in progress:** - - If output doc exists (user specifies path or we prompt): - - Read doc to determine current stage - - Resume from last completed stage - - Else: Start at `01-{stage-1-name}.md` - -4. **Route to appropriate stage** based on progress - -{if-headless} -**Headless mode routing:** -- Default: Run all stages sequentially with safe defaults -- Named task: Execute specific stage or task -- Output structured JSON results when complete -{/if-headless} - -## Stages - -| # | Stage | Purpose | Prompt | -|---|-------|---------|--------| -| 1 | {stage-1-name} | {stage-1-purpose} | `01-{stage-1-name}.md` | -| 2 | {stage-2-name} | {stage-2-purpose} | `02-{stage-2-name}.md` | -{/if-complex-workflow} - -{if-external-skills} -## External Skills - -This workflow uses: -{external-skills-list} -{/if-external-skills} - -{if-scripts} -## Scripts +{if-module} +Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root level and `{module-code}` section). If config is missing, let the user know `{module-setup-skill}` can configure the module at any time. Use sensible defaults for anything not configured — prefer inferring at runtime or asking the user over requiring configuration. +{/if-module} +{if-standalone} +Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` if present. Use sensible defaults for anything not configured. +{/if-standalone} -Available scripts in `scripts/`: -- `{script-name}` — {script-description} -{/if-scripts} +{The rest of the skill — body structure, sections, phases, stages, scripts, external skills — is determined entirely by what the skill needs. The builder crafts this based on the discovery and requirements phases.} diff --git a/src/skills/bmad-workflow-builder/references/classification-reference.md b/src/skills/bmad-workflow-builder/references/classification-reference.md index a85a318..2cefdcb 100644 --- a/src/skills/bmad-workflow-builder/references/classification-reference.md +++ b/src/skills/bmad-workflow-builder/references/classification-reference.md @@ -55,7 +55,5 @@ Classify the skill type based on user requirements. This table is for internal u ## Module Context (Orthogonal) Module context is asked for ALL types: -- **Module-based:** Part of a BMad module. Uses `bmad-{modulecode}-{skillname}` naming. -- **Standalone:** Independent skill. Uses `bmad-{skillname}` naming. - -All workflows read config from `{project-root}/_bmad/config.yaml` by default. +- **Module-based:** Part of a BMad module. Uses `bmad-{modulecode}-{skillname}` naming. Config loading includes a fallback pattern — if config is missing, the skill informs the user that the module setup skill is available and continues with sensible defaults. +- **Standalone:** Independent skill. Uses `bmad-{skillname}` naming. Config loading is best-effort — load if available, use defaults if not, no mention of a setup skill. diff --git a/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md b/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md index c39829b..3c88208 100644 --- a/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md +++ b/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md @@ -30,28 +30,36 @@ Which would you prefer? ## Config Reading and Integration -Workflows MUST read config values from `{project-root}/_bmad/config.yaml`. +Workflows read config values from `{project-root}/_bmad/config.yaml` and `config.user.yaml`. The loading behavior differs based on whether the skill is part of a module or standalone. ### Config Loading Pattern -**Read the config file directly:** +**Module-based skills** — load config with fallback and setup skill awareness: ``` -Load config from {project-root}/_bmad/config.yaml -- Read the `core` section for shared variables (user_name, communication_language, etc.) -- Read the module section (keyed by module code) for module-specific variables +Load config from {project-root}/_bmad/config.yaml ({module-code} section) and config.user.yaml. +If config is missing: + - Inform the user that the module setup skill ({module-setup-skill}) is available for initial setup + - Continue with sensible fallback values for each variable +``` + +**Standalone skills** — load config best-effort: +``` +Load config from {project-root}/_bmad/config.yaml and config.user.yaml if available. +If config is missing: + - Continue with fallback values — no mention of a setup skill ``` Store config values in memory as `{var_name}` for use in prompts. ### Required Core Variables -**Every module workflow MUST load these core variables:** -- `user_name:BMad` -- `communication_language:English` -- `output_folder:{project-root}/_bmad-output` +**Every workflow MUST load these core variables with fallbacks:** +- `user_name` — fallback: omit +- `communication_language` — fallback: match the user's language +- `output_folder` — fallback: `{project-root}/_bmad-output` **Conditionally include:** -- `document_output_language:English` — ONLY if workflow creates documents (check capability `output-location` field) +- `document_output_language` — fallback: match the user's language — ONLY if workflow creates documents (check capability `output-location` field) - Output location variable from capability `output-location` — ONLY if specified in metadata **Example for BMB workflow (creates documents, has output var):** @@ -425,7 +433,11 @@ description: Complex multi-stage workflow for my module. Use when user requests ## Workflow Entry -1. Load config from `{project-root}/_bmad/config.yaml` — reads core vars (user_name, communication_language, document_output_language, output_folder) and module vars (my_output_folder) +1. Load config from `{project-root}/_bmad/config.yaml` (`mymod` section) and `config.user.yaml`. If config is missing, inform that `bmad-mymodule-setup` is available for initial setup, then continue with fallbacks: + - `user_name` — fallback: omit + - `communication_language` — fallback: match the user's language + - `document_output_language` — fallback: match the user's language + - `my_output_folder` — fallback: `{project-root}/_bmad-output/mymodule` 2. Ask user for output document path (or suggest {my_output_folder}/analysis-{timestamp}.md) diff --git a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md index 9e73955..4a87389 100644 --- a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md @@ -1,76 +1,32 @@ # Template Substitution Rules -When building the workflow/skill, you MUST apply these conditional blocks to the templates: +The SKILL-template provides a minimal skeleton: frontmatter, overview, and activation with config loading. Everything beyond that is crafted by the builder based on what was learned during discovery and requirements phases. -## Skill Type Conditionals +## Frontmatter -### Complex Workflow -- `{if-complex-workflow}` ... `{/if-complex-workflow}` → Keep the content inside -- `{if-simple-workflow}` ... `{/if-simple-workflow}` → Remove the entire block including markers -- `{if-simple-utility}` ... `{/if-simple-utility}` → Remove the entire block including markers - -### Simple Workflow -- `{if-complex-workflow}` ... `{/if-complex-workflow}` → Remove the entire block including markers -- `{if-simple-workflow}` ... `{/if-simple-workflow}` → Keep the content inside -- `{if-simple-utility}` ... `{/if-simple-utility}` → Remove the entire block including markers - -### Simple Utility -- `{if-complex-workflow}` ... `{/if-complex-workflow}` → Remove the entire block including markers -- `{if-simple-workflow}` ... `{/if-simple-workflow}` → Remove the entire block including markers -- `{if-simple-utility}` ... `{/if-simple-utility}` → Keep the content inside +- `{module-code-or-empty}` → Module code prefix with hyphen (e.g., `bmb-`) or empty for standalone +- `{skill-name}` → Skill functional name (kebab-case) +- `{skill-description}` → Two parts: [5-8 word summary]. [trigger phrases] ## Module Conditionals ### For Module-Based Skills - `{if-module}` ... `{/if-module}` → Keep the content inside - `{if-standalone}` ... `{/if-standalone}` → Remove the entire block including markers -- `{module-code-or-empty}` → Replace with module code (e.g., `bmb-`) +- `{module-code}` → Module code without trailing hyphen (e.g., `bmb`) +- `{module-setup-skill}` → Name of the module's setup skill (e.g., `bmad-builder-setup`) ### For Standalone Skills - `{if-module}` ... `{/if-module}` → Remove the entire block including markers - `{if-standalone}` ... `{/if-standalone}` → Keep the content inside -- `{module-code-or-empty}` → Empty string - -## Feature Conditionals - -### Headless Mode -- `{if-headless}` ... `{/if-headless}` → Keep if supports headless/autonomous mode, otherwise remove - -### Creates Documents -- `{if-creates-docs}` ... `{/if-creates-docs}` → Keep if creates output documents, otherwise remove - -### Has Stages (Complex Workflow) -- `{if-stages}` ... `{/if-stages}` → Keep if has numbered stage prompts, otherwise remove - -### Has Scripts -- `{if-scripts}` ... `{/if-scripts}` → Keep if has scripts/ directory, otherwise remove - -## External Skills -- `{if-external-skills}` ... `{/if-external-skills}` → Keep if skill uses external skills, otherwise remove -- `{external-skills-list}` → Replace with bulleted list of exact skill names: - ```markdown - - `bmad-skill-name` — Description - ``` - -## Frontmatter Placeholders - -Replace all frontmatter placeholders: -- `{module-code-or-empty}` → Module code prefix (e.g., `bmb-`) or empty -- `{skill-name}` → Skill functional name (kebab-case) -- `{skill-description}` → Full description with trigger phrases -- `{role-guidance}` → Brief role/expertise statement -## Content Placeholders +## Beyond the Template -Replace all content placeholders with skill-specific values: -- `{overview-template}` → Overview paragraph following 3-part formula (What, How, Why/Outcome) -- `{stage-N-name}` → Name of numbered stage -- `{stage-N-purpose}` → Purpose description of numbered stage -- `{progression-condition}` → When this stage completes +The builder determines the rest of the skill structure — body sections, phases, stages, scripts, external skills, headless mode, role guidance — based on the skill type classification and requirements gathered during the build process. The template intentionally does not prescribe these; the builder has the context to craft them. ## Path References -All generated skills use these paths: +All generated skills use these paths when applicable: - `references/{reference}.md` — Reference documents loaded on demand - `01-{stage}.md` — Numbered stage prompts at skill root (complex workflows) -- `scripts/` — Python/shell scripts for deterministic operations (if needed) +- `scripts/` — Python/shell scripts for deterministic operations From 9666260740e55c9e83de1b6bce5a95ebc1a69ef5 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 21 Mar 2026 19:47:34 -0500 Subject: [PATCH 10/10] Enforce skill structure conventions and restore config resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontmatter: - Only name and description allowed in YAML frontmatter - Removed argument-hint and all non-frontmatter fields (displayName, skillName, etc.) - Split standard-fields.md into Frontmatter Fields vs Content Fields sections - Lint script now validates frontmatter keys and flags invalid ones Skill-internal paths: - All skill-internal references now require ./ prefix (./references/, ./scripts/, ./assets/) - Distinguishes skill-internal files from {project-root} paths to prevent LLM confusion - Lint script updated: ./ is now required (was previously flagged as violation) - Added bare-internal-path check for references/, scripts/, assets/ without ./ Progressive disclosure structure: - All prompt files must live in ./references/, not at skill root - SKILL.md is the only .md file at root — it serves as identity and router - Capability sections replaced with succinct routing tables - Removed CLI Usage section — user-facing help belongs in Overview - Lint script now flags .md files at skill root (except SKILL.md) Config resolution restored: - Both builder SKILL.md files now list config keys with actual defaults - user_name, communication_language, document_output_language, output paths - Fixed anti-pattern in skill-best-practices: distinguishes mechanic (bad) from outcome-intent with defaults (good) Activation flow: - Single activation flow regardless of interactive/headless mode - Sidecar index.md is the single entry point to memory system - Interactive activation offers capability menu or continues from memory context - SKILL template updated with config resolution, routing table, and memory patterns Lint gate auto-fix: - Build process now delegates lint to subagent if available - Fix-and-rerun loop with max 3 attempts per script - Lint scripts bumped to v2.0.0 with frontmatter, structure, and path checks Also includes quality scanner and asset template refinements across both builders. --- src/module-help.csv | 6 - src/module.yaml | 20 -- src/skills/bmad-agent-builder/SKILL.md | 25 +- .../assets/SKILL-template.md | 40 ++-- .../assets/autonomous-wake.md | 7 +- .../assets/memory-system.md | 24 +- .../bmad-agent-builder/assets/save-memory.md | 14 +- .../bmad-agent-builder/build-process.md | 147 +++++------- .../bmad-agent-builder/quality-optimizer.md | 48 +--- .../quality-scan-agent-cohesion.md | 60 +---- .../quality-scan-enhancement-opportunities.md | 57 +---- .../quality-scan-execution-efficiency.md | 19 +- .../quality-scan-prompt-craft.md | 27 +-- .../quality-scan-script-opportunities.md | 32 +-- .../quality-scan-structure.md | 26 +-- .../references/quality-dimensions.md | 12 +- .../script-opportunities-reference.md | 12 +- .../references/skill-best-practices.md | 217 +++++------------- .../references/standard-fields.md | 80 +++---- .../references/template-substitution-rules.md | 10 +- .../report-quality-scan-creator.md | 21 +- .../scripts/scan-path-standards.py | 112 +++++++-- .../bmad-builder-setup/assets/module.yaml | 4 +- src/skills/bmad-workflow-builder/SKILL.md | 24 +- .../bmad-workflow-builder/build-process.md | 95 +++----- .../quality-optimizer.md | 48 +--- .../quality-scan-enhancement-opportunities.md | 49 +--- .../quality-scan-execution-efficiency.md | 39 +--- .../quality-scan-prompt-craft.md | 39 +--- .../quality-scan-script-opportunities.md | 33 +-- .../quality-scan-skill-cohesion.md | 60 +---- .../quality-scan-workflow-integrity.md | 39 +--- .../references/complex-workflow-patterns.md | 39 +--- .../references/quality-dimensions.md | 10 +- .../script-opportunities-reference.md | 1 - .../references/skill-best-practices.md | 217 +++++------------- .../references/standard-fields.md | 30 ++- .../references/template-substitution-rules.md | 8 +- .../report-quality-scan-creator.md | 51 +--- .../scripts/scan-path-standards.py | 119 ++++++++-- 40 files changed, 591 insertions(+), 1330 deletions(-) delete mode 100644 src/module-help.csv delete mode 100644 src/module.yaml diff --git a/src/module-help.csv b/src/module-help.csv deleted file mode 100644 index 58a882a..0000000 --- a/src/module-help.csv +++ /dev/null @@ -1,6 +0,0 @@ -module,agent-name,skill-name,display-name,menu-code,capability,args,description,phase,after,before,required,output-location,outputs, -bmb,,bmad-builder-setup,Setup Builder,SB,configure,,"Set up BMad Builder module in a project.",anytime,,,false,,config, -bmb,,bmad-agent-builder,Build Agent,BA,build-process,,"Create, edit, convert, or fix an agent skill.",anytime,,bmad-agent-builder:quality-optimizer,false,output_folder,agent skill, -bmb,,bmad-agent-builder,Optimize Agent,OA,quality-optimizer,,Validate and optimize an existing agent skill. Produces a quality report.,anytime,bmad-agent-builder:build-process,,false,bmad_builder_reports,quality report, -bmb,,bmad-workflow-builder,Build Workflow,BW,build-process,,"Create, edit, convert, or fix a workflow or utility skill.",anytime,,bmad-workflow-builder:quality-optimizer,false,output_folder,workflow skill, -bmb,,bmad-workflow-builder,Optimize Workflow,OW,quality-optimizer,,Validate and optimize an existing workflow or utility skill. Produces a quality report.,anytime,bmad-workflow-builder:build-process,,false,bmad_builder_reports,quality report, diff --git a/src/module.yaml b/src/module.yaml deleted file mode 100644 index 52dcde3..0000000 --- a/src/module.yaml +++ /dev/null @@ -1,20 +0,0 @@ -code: bmb -name: "BMad Builder" -description: "Standard Skill Compliant Factory for BMad Agents, Workflows and Modules" -default_selected: false - -# Variables from Core Config inserted: -## user_name -## communication_language -## document_output_language -## output_folder - -bmad_builder_output_folder: - prompt: "Where should your custom skills (agents and workflows) be saved?" - default: "_bmad-output/skills" - result: "{project-root}/{value}" - -bmad_builder_reports: - prompt: "Report Output for Evals, Tests, Quality and Planning Reports?" - default: "_bmad-output/reports" - result: "{project-root}/{value}" diff --git a/src/skills/bmad-agent-builder/SKILL.md b/src/skills/bmad-agent-builder/SKILL.md index 7bf67f2..1229476 100644 --- a/src/skills/bmad-agent-builder/SKILL.md +++ b/src/skills/bmad-agent-builder/SKILL.md @@ -1,14 +1,15 @@ --- name: bmad-agent-builder description: Builds, edit or validate Agent Skill through conversational discovery. Use when the user requests to "Create an Agent", "Optimize an Agent" or "Edit an Agent". -argument-hint: "--headless or -H to not prompt user, initial input for create, path to existing skill with keywords optimize, edit, validate" --- # Agent Builder ## Overview -This skill helps you build AI agents through conversational discovery and iterative refinement. Act as an architect guide, walking users through six phases: intent discovery, capabilities strategy, requirements gathering, drafting, building, and testing. Your output is a complete skill structure — named personas with optional memory, capabilities, and autonomous modes — ready to integrate into the BMad Method ecosystem. +This skill helps you build AI agents through conversational discovery and iterative refinement. Act as an architect guide, walking users through six phases: intent discovery, capabilities strategy, requirements gathering, drafting, building, and testing. Your output is a complete skill structure — named personas with optional memory, capabilities, and headless modes — ready to integrate into the BMad Method ecosystem. + +**Args:** Accepts `--headless` / `-H` for non-interactive execution, an initial description for create, or a path to an existing agent with keywords like optimize, edit, or validate. ## Vision: Build More, Architect Dreams @@ -28,11 +29,12 @@ These agents become part of the BMad Method ecosystem — personal companions th 1. Detect user's intent. If `--headless` or `-H` is passed, or intent is clearly non-interactive, set `{headless_mode}=true` for all sub-prompts. -2. Load config from `{project-root}/_bmad/config.yaml` (bmb section) and `config.user.yaml`. If missing, note that `bmad-builder-setup` is available and continue with fallbacks: - - `{bmad_builder_output_folder}` — fallback: `{project-root}/skills` - - `{bmad_builder_reports}` — fallback: `{project-root}/skills` - - `{user_name}` — fallback: omit - - `{communication_language}` — fallback: match the user's language +2. Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root and bmb section). If missing, and the `bmad-builder-setup` skill is available, let the user know they can run it at any time to configure. Resolve and apply throughout the session (defaults in parens): + - `{user_name}` (default: null) — address the user by name + - `{communication_language}` (default: user or system intent) — use for all communications + - `{document_output_language}` (default: user or system intent) — use for generated document content + - `{bmad_builder_output_folder}` (default: `{project-root}/skills`) — save built agents here + - `{bmad_builder_reports}` (default: `{project-root}/skills/reports`) — save reports (quality, eval, planning) here 3. Route by intent. @@ -40,7 +42,7 @@ These agents become part of the BMad Method ecosystem — personal companions th This is the core creative path — where agent ideas become reality. Through six phases of conversational discovery, you guide users from a rough vision to a complete, tested agent skill structure. This covers building new agents from scratch, converting non-compliant formats, editing existing agents, and applying improvements or fixes. -Agents are named personas with optional memory, capabilities, autonomous modes, and personality. The build process includes a lint gate for structural validation. When building or modifying agents that include scripts, unit tests are created alongside the scripts and run as part of validation. +Agents are named personas with optional memory, capabilities, headless modes, and personality. The build process includes a lint gate for structural validation. When building or modifying agents that include scripts, unit tests are created alongside the scripts and run as part of validation. Load `build-process.md` to begin. @@ -50,7 +52,7 @@ For agents that already work but could work *better*. This is comprehensive vali Run this anytime you want to assess and improve an existing agent's quality. -Load `quality-optimizer.md` — it orchestrates everything including scan modes, autonomous handling, and remediation options. +Load `quality-optimizer.md` — it orchestrates everything including scan modes, headless handling, and remediation options. --- @@ -60,8 +62,7 @@ Load `quality-optimizer.md` — it orchestrates everything including scan modes, |--------|----------------|-------| | **Builder** | "build/create/design/convert/edit/fix an agent", "new agent" | Load `build-process.md` | | **Quality Optimizer** | "quality check", "validate", "review/optimize/improve agent" | Load `quality-optimizer.md` | -| **Unclear** | — | Present the two options above and ask | -Pass `{headless_mode}` flag to all routes. Use Todo List to track progress through multi-step flows. Use subagents for parallel work (quality scanners, web research or document review). +Regardless of what path is taken, respect and follow headless mode guidance if user requested headless_mode - if a specific instruction does not indicate how to handle headless mode, you will try to find a way. -Help the user create amazing Agents! +Enjoy the adventure and help the user create amazing Agents abd their capabilities! diff --git a/src/skills/bmad-agent-builder/assets/SKILL-template.md b/src/skills/bmad-agent-builder/assets/SKILL-template.md index 1c06906..2ed4bcf 100644 --- a/src/skills/bmad-agent-builder/assets/SKILL-template.md +++ b/src/skills/bmad-agent-builder/assets/SKILL-template.md @@ -7,7 +7,7 @@ description: {skill-description} # [4-6 word summary]. [trigger phrases] ## Overview -{overview — concise: who this agent is, what it does, args/modes supported, and the outcome. This is the main help output for the skill.} +{overview — concise: who this agent is, what it does, args/modes supported, and the outcome. This is the main help output for the skill — any user-facing help info goes here, not in a separate CLI Usage section.} ## Identity @@ -23,25 +23,39 @@ description: {skill-description} # [4-6 word summary]. [trigger phrases] - {Guiding principle 2} - {Guiding principle 3} -{if-sidecar} -## Sidecar - -Memory location: `_bmad/memory/{skillName}-sidecar/` - -Load `references/memory-system.md` for memory discipline and structure. -{/if-sidecar} - ## On Activation {if-module} -Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root level and `{module-code}` section). If config is missing, let the user know `{module-setup-skill}` can configure the module at any time. Use sensible defaults for anything not configured — prefer inferring at runtime or asking the user over requiring configuration. +Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root level and `{module-code}` section). If config is missing, let the user know `{module-setup-skill}` can configure the module at any time. Resolve and apply throughout the session (defaults in parens): +- `{user_name}` ({default}) — address the user by name +- `{communication_language}` ({default}) — use for all communications +- `{document_output_language}` ({default}) — use for generated document content +- plus any module-specific output paths with their defaults {/if-module} {if-standalone} -Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` if present. Use sensible defaults for anything not configured. +Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` if present. Resolve and apply throughout the session (defaults in parens): +- `{user_name}` ({default}) — address the user by name +- `{communication_language}` ({default}) — use for all communications +- `{document_output_language}` ({default}) — use for generated document content {/if-standalone} +{if-sidecar} +Load sidecar memory from `{project-root}/_bmad/memory/{skillName}-sidecar/index.md` — this is the single entry point to the memory system and tells the agent what else to load. Load `./references/memory-system.md` for memory discipline. If sidecar doesn't exist, load `./references/init.md` for first-run onboarding. +{/if-sidecar} + {if-headless} -If `--headless` or `-H` is passed, complete the requested task without asking for user input, using sensible defaults for any decisions. +If `--headless` or `-H` is passed, load `./references/autonomous-wake.md` and complete the task without interaction. {/if-headless} -{The rest of the agent — activation flow, capabilities, sidecar initialization, capability routing — is determined by what the agent needs. The builder crafts this based on the discovery and requirements phases.} +{if-interactive} +Greet the user. If memory provides natural context (active program, recent session, pending items), continue from there. Otherwise, offer to show available capabilities. +{/if-interactive} + +## Capabilities + +{Succinct routing table — each capability routes to a progressive disclosure file in ./references/:} + +| Capability | Route | +|------------|-------| +| {Capability Name} | Load `./references/{capability}.md` | +| Save Memory | Load `./references/save-memory.md` | diff --git a/src/skills/bmad-agent-builder/assets/autonomous-wake.md b/src/skills/bmad-agent-builder/assets/autonomous-wake.md index 0e7fd4a..dc82e80 100644 --- a/src/skills/bmad-agent-builder/assets/autonomous-wake.md +++ b/src/skills/bmad-agent-builder/assets/autonomous-wake.md @@ -14,12 +14,7 @@ You're running autonomously. No one is here. No task was specified. Execute your ## Instructions -- Don't ask questions -- Don't wait for input -- Don't greet anyone -- Execute your default wake behavior -- Write results to memory -- Exit +Execute your default wake behavior, write results to memory, and exit. ## Default Wake Behavior diff --git a/src/skills/bmad-agent-builder/assets/memory-system.md b/src/skills/bmad-agent-builder/assets/memory-system.md index 3b5ede8..1aa8d87 100644 --- a/src/skills/bmad-agent-builder/assets/memory-system.md +++ b/src/skills/bmad-agent-builder/assets/memory-system.md @@ -98,31 +98,11 @@ Update periodically after: ## Write Discipline -Before writing to memory, ask: - -1. **Is this worth remembering?** - - If no → skip - - If yes → continue - -2. **What's the minimum tokens that capture this?** - - Condense to essence - - No fluff, no repetition - -3. **Which file?** - - `index.md` → essential context, active work - - `patterns.md` → user quirks, recurring patterns, conventions - - `chronology.md` → session summaries, significant events - -4. **Does this require index update?** - - If yes → update `index.md` to point to it +Persist only what matters, condensed to minimum tokens. Route to the appropriate file based on content type (see File Structure above). Update `index.md` when other files change. ## Memory Maintenance -Regularly (every few sessions or when files grow large): -1. **Condense verbose entries** — Summarize to essence -2. **Prune outdated content** — Move old items to chronology or remove -3. **Consolidate patterns** — Merge similar entries -4. **Update chronology** — Archive significant past events +Periodically condense, prune, and consolidate memory files to keep them lean. ## First Run diff --git a/src/skills/bmad-agent-builder/assets/save-memory.md b/src/skills/bmad-agent-builder/assets/save-memory.md index c6144a2..cc15119 100644 --- a/src/skills/bmad-agent-builder/assets/save-memory.md +++ b/src/skills/bmad-agent-builder/assets/save-memory.md @@ -10,19 +10,7 @@ Immediately persist the current session context to memory. ## Process -1. **Read current index.md** — Load existing context - -2. **Update with current session:** - - What we're working on - - Current state/progress - - Any new preferences or patterns discovered - - Next steps to continue - -3. **Write updated index.md** — Replace content with condensed, current version - -4. **Checkpoint other files if needed:** - - `patterns.md` — Add new patterns discovered - - `chronology.md` — Add session summary if significant +Update `index.md` with current session context (active work, progress, preferences, next steps). Checkpoint `patterns.md` and `chronology.md` if significant changes occurred. ## Output diff --git a/src/skills/bmad-agent-builder/build-process.md b/src/skills/bmad-agent-builder/build-process.md index afcbbc2..4264a15 100644 --- a/src/skills/bmad-agent-builder/build-process.md +++ b/src/skills/bmad-agent-builder/build-process.md @@ -22,77 +22,34 @@ Early check: internal capabilities only, external skills, both, or unclear? **If external skills involved:** Suggest `bmad-module-builder` to bundle agents + skills into a cohesive module. Modules are the heart of the BMad ecosystem — shareable packages for any domain. **Script Opportunity Discovery** (active probing — do not skip): -Walk through each planned capability with the user and apply these filters: -1. "Does this operation have clear pass/fail criteria?" → Script candidate -2. "Could this run without LLM judgment — no interpretation, no creativity, no ambiguity?" → Strong script candidate -3. "Does it validate, transform, count, parse, format-convert, compare against a schema, or check structure?" → Almost certainly a script - -**Common script-worthy operations:** -- Schema/format validation (JSON, YAML, frontmatter, file structure) -- Data extraction and transformation (parsing, restructuring, field mapping) -- Counting, aggregation, and metric collection (token counts, file counts, summary stats) -- File/directory structure checks (existence, naming conventions, required files) -- Pattern matching against known standards (path conventions, naming rules) -- Comparison operations (diff, version compare, before/after, cross-reference checking) -- Dependency graphing (parsing imports, references, skill entries) -- Memory structure validation (required sections, path correctness) -- Access boundary extraction and verification -- Dependency graphing (parsing imports, references, skill entries) -- Pre-processing for LLM capabilities (extract compact metrics from large files so the LLM works from structured data, not raw content) -- Post-processing validation (verify LLM output conforms to expected schema/structure) - -**Present your script plan**: Before moving to Phase 3, explicitly tell the user which operations you plan to implement as scripts vs. prompts, with one-line reasoning for each. Ask if they agree or want to adjust. - -If scripts are planned, the `scripts/` folder will be created. Scripts are invoked from prompts when needed, not run automatically. + +Identify deterministic operations that should be scripts. Load `./references/script-opportunities-reference.md` for the full catalog. Confirm the script-vs-prompt plan with the user before proceeding. + +If scripts are planned, the `./scripts/` folder will be created. Scripts are invoked from prompts when needed, not run automatically. ## Phase 3: Gather Requirements -Work through these conversationally: - -- **Name:** Functional (kebab-case), display name, title, icon -- **Overview:** Draft a 2-3 sentence overview following the 3-part formula: - - **What** — What this agent does - - **How** — Role, approach, or key capabilities - - **Why/Outcome** — Value delivered or quality standard - - *Example:* "This skill provides a {role} who helps users {outcome}. Act as {name} — {key quality}." -- **Identity:** Who is this agent? How do they communicate? What guides their decisions? -- **Module context:** Standalone (`bmad-agent-{name}`) or part of a module (`bmad-{modulecode}-agent-{name}`) -- **Activation modes:** - - **Interactive only** — User invokes the agent directly - - **Interactive + Autonomous** — Also runs on schedule/cron for background tasks -- **Memory & Persistence:** - - **Sidecar needed?** — What persists across sessions? - - **Critical data** (must persist immediately): What data is essential to capture the moment it's created? - - **Checkpoint data** (save periodically): What can be batched and saved occasionally? - - **Save triggers:** After which interactions should memory be updated? -- **Capabilities:** - - **Internal prompts:** Capabilities the agent knows itself (each will get its own prompt file) - - **External skills:** Skills the agent invokes (ask for **exact registered skill names** — e.g., `skill-creator`) - - Note: Skills may exist now or be created later -- **First-run:** What should it ask on first activation? (standalone only; module-based gets config from module's config.yaml) - -**If autonomous mode is enabled, ask additional questions:** -- **Autonomous tasks:** What should the agent do when waking on a schedule? - - Examples: Review/organize memory, process queue, maintenance tasks, implement tickets -- **Default wake behavior:** What happens with `--headless` | `-H` (no specific task)? -- **Named tasks:** What specific tasks can be invoked with `--headless:{task-name}` or `-H:{task-name}`? - -- **Folder Dominion / Access Boundaries:** - - **What folders can this agent read from?** (e.g., `journals/`, `financials/`, specific file patterns) - - **What folders can this agent write to?** (e.g., output folders, log locations) - - **Are there any explicit deny zones?** (folders the agent must never touch) - - Store these boundaries in memory as the standard `access-boundaries` section (see memory-system template) - -**Key distinction:** Folder dominion (where things live) ≠ agent memory (what persists across sessions) +Gather requirements through conversation: identity, capabilities (internal prompts + external skills), activation modes, memory needs, and access boundaries. Refer to `./references/standard-fields.md` for conventions. + +Key structural context: + +- **Naming:** Standalone agents use `bmad-agent-{name}`, module agents use `bmad-{modulecode}-agent-{name}` +- **Activation modes:** Interactive only, or Interactive + Headless (also runs on schedule/cron for background tasks) +- **Memory architecture:** See `./references/memory-system.md` template. Sidecar at `{project-root}/_bmad/memory/{skillName}-sidecar/` +- **Access boundaries:** Read/write/deny zones stored in memory as the standard `access-boundaries` section + +**If headless mode is enabled, also gather:** +- Default wake behavior (`--headless` | `-H` with no specific task) +- Named tasks (`--headless:{task-name}` or `-H:{task-name}`) - **Path Conventions** (CRITICAL for reliable agent behavior): - **Memory location:** `{project-root}/_bmad/memory/{skillName}-sidecar/` - **Project artifacts:** `{project-root}/_bmad/...` when referencing project-level files - - **Skill-internal files:** Use relative paths (`references/`, `scripts/`) + - **Skill-internal files:** Always use `./` prefix (`./references/`, `./scripts/`) — this distinguishes them from `{project-root}` paths - **Config variables:** Use directly — they already contain full paths (NO `{project-root}` prefix) - Correct: `{output_folder}/file.md` - Wrong: `{project-root}/{output_folder}/file.md` (double-prefix breaks resolution) - - **No absolute paths** (`/Users/...`) or relative prefixes (`./`, `../`) + - **No absolute paths** (`/Users/...`) ## Phase 4: Draft & Refine @@ -101,34 +58,38 @@ Once you have a cohesive idea, think one level deeper. Once you have done this, ## Phase 5: Build **Always load these before building:** -- Load `references/standard-fields.md` — field definitions, description format, path rules -- Load `references/skill-best-practices.md` — authoring patterns (freedom levels, templates, anti-patterns) -- Load `references/quality-dimensions.md` — quick mental checklist for build quality +- Load `./references/standard-fields.md` — field definitions, description format, path rules +- Load `./references/skill-best-practices.md` — authoring patterns (freedom levels, templates, anti-patterns) +- Load `./references/quality-dimensions.md` — quick mental checklist for build quality **Load based on context:** -- **Always load** `references/script-opportunities-reference.md` — script opportunity spotting guide, catalog, and output standards. Use this to identify additional script opportunities not caught in Phase 2, even if no scripts were initially planned. +- **Always load** `./references/script-opportunities-reference.md` — script opportunity spotting guide, catalog, and output standards. Use this to identify additional script opportunities not caught in Phase 2, even if no scripts were initially planned. -When confirmed: +Build the agent skill structure using templates from `./assets/` and rules from `./references/template-substitution-rules.md`. Output to `{bmad_builder_output_folder}`. -1. Load template substitution rules from `references/template-substitution-rules.md` and apply +**Lint gate** — after building, run validation and auto-fix failures: -2. Create skill structure using templates from `assets/` folder: - - **SKILL-template.md** — skill wrapper with full persona content embedded - - **init-template.md** — first-run setup (if sidecar) - - **memory-system.md** — memory (if sidecar, saved at root level) - - **autonomous-wake.md** — autonomous activation behavior (if activation_modes includes "autonomous") - - **save-memory.md** — explicit memory save capability (if sidecar enabled) +If subagents are available, delegate the lint-fix loop to a subagent. Otherwise run inline. -3. **Folder structure:** +1. Run both lint scripts in parallel: + ```bash + python3 ./scripts/scan-path-standards.py {skill-path} + python3 ./scripts/scan-scripts.py {skill-path} + ``` +2. If any findings at high or critical severity: fix them and re-run the failing script +3. Repeat up to 3 attempts per script — if still failing after 3, report remaining findings and continue +4. If scripts exist in the built skill, also run unit tests (`./scripts/run-tests.sh` or equivalent) + +**Folder structure:** ``` {skill-name}/ -├── SKILL.md # Contains full persona content (agent.md embedded) -├── init.md # First-run setup (if sidecar) -├── autonomous-wake.md # Autonomous activation (if autonomous mode) -├── save-memory.md # Explicit memory save (if sidecar) -├── {name}.md # Each internal capability prompt -├── references/ # Reference data, schemas, guides (read for context) -│ └── memory-system.md # (if sidecar needed) +├── SKILL.md # Frontmatter (name + description only), persona, activation, capability routing +├── references/ # ALL progressive disclosure content lives here +│ ├── {capability}.md # Each internal capability prompt +│ ├── memory-system.md # Memory discipline and structure (if sidecar) +│ ├── init.md # First-run onboarding (if sidecar) +│ ├── autonomous-wake.md # Headless activation (if headless mode) +│ └── save-memory.md # Explicit memory save (if sidecar) ├── assets/ # Templates, starter files (copied/transformed into output) └── scripts/ # Deterministic code — validation, transformation, testing └── run-tests.sh # uvx-powered test runner (if python tests exist) @@ -137,22 +98,20 @@ When confirmed: **What goes where:** | Location | Contains | LLM relationship | |----------|----------|-----------------| -| **Root `.md` files** | Prompt/instruction files, subagent definitions | LLM **loads and executes** these as instructions — they are extensions of SKILL.md | -| **`references/`** | Reference data, schemas, tables, examples, guides | LLM **reads for context** — informational, not executable | -| **`assets/`** | Templates, starter files, boilerplate | LLM **copies/transforms** these into output — not for reasoning | -| **`scripts/`** | Python, shell scripts with tests | LLM **invokes** these — deterministic operations that don't need judgment | +| **SKILL.md** | Persona, activation, capability routing table | LLM **identity and router** — the only root `.md` file | +| **`./references/`** | Capability prompts, reference data, schemas, guides | LLM **loads on demand** — progressive disclosure via routing table | +| **`./assets/`** | Templates, starter files, boilerplate | LLM **copies/transforms** these into output — not for reasoning | +| **`./scripts/`** | Python, shell scripts with tests | LLM **invokes** these — deterministic operations that don't need judgment | -Only create subfolders that are needed — most skills won't need all four. +Only create subfolders that are needed — most agents won't need all three. -4. Output to `bmad_builder_output_folder` from config, or `{project-root}/bmad-builder-creations/` +**Activation guidance for built agents:** -5. **Lint gate** — run deterministic validation scripts: - ```bash - python3 scripts/scan-path-standards.py {skill-path} - python3 scripts/scan-scripts.py {skill-path} - ``` - - If any script returns critical issues: fix them before proceeding - - If only warnings/medium: note them but proceed +Activation is a single flow regardless of mode (interactive or headless). It should: +- Load config and resolve values (with defaults — see SKILL.md step 2) +- Load sidecar `index.md` if the agent has memory — this is the single entry point that tells the agent what else to load +- If headless, route to `./references/autonomous-wake.md` and complete without interaction +- If interactive, greet the user and either continue naturally from memory context or offer to show available capabilities ## Phase 6: Summary diff --git a/src/skills/bmad-agent-builder/quality-optimizer.md b/src/skills/bmad-agent-builder/quality-optimizer.md index 2e22591..99d3da4 100644 --- a/src/skills/bmad-agent-builder/quality-optimizer.md +++ b/src/skills/bmad-agent-builder/quality-optimizer.md @@ -10,55 +10,21 @@ menu-code: QO You orchestrate quality scans on a BMad agent. Deterministic checks run as scripts (fast, zero tokens). Judgment-based analysis runs as LLM subagents. You synthesize all results into a unified report. -## Your Role: Coordination, Not File Reading +## Your Role -**DO NOT read the target agent's files yourself.** Scripts and subagents do all analysis. +You orchestrate quality scans: run deterministic scripts and pre-pass extractors, spawn LLM scanner subagents in parallel, then synthesize all results into a unified report. -Your job: -1. Create output directory -2. Run all lint scripts + pre-pass scripts (instant, deterministic) -3. Spawn all LLM scanner subagents in parallel (with pre-pass data where available) -4. Collect all results -5. Synthesize into unified report (spawn report creator) -6. Present findings to user +**DO NOT read the target agent's files yourself.** Scripts and subagents do all analysis. -## Autonomous Mode +## Headless Mode -**Check if `{headless_mode}=true`** — If set, run in headless mode: -- **Skip ALL questions** — proceed with safe defaults -- **Uncommitted changes:** Note in report, don't ask -- **Agent functioning:** Assume yes, note in report that user should verify -- **After report:** Output summary and exit, don't offer next steps -- **Output format:** Structured JSON summary + report path, minimal conversational text - -**Autonomous mode output:** -```json -{ - "headless_mode": true, - "report_file": "{path-to-report}", - "summary": { ... }, - "warnings": ["Uncommitted changes detected", "Agent functioning not verified"] -} -``` +If `{headless_mode}=true`, skip all user interaction, use safe defaults, note any warnings, and output structured JSON as specified in the Present Findings section. ## Pre-Scan Checks -Before running any scans: - -**IF `{headless_mode}=true`:** -1. **Check for uncommitted changes** — Run `git status`. Note in warnings array if found. -2. **Skip agent functioning verification** — Add to warnings: "Agent functioning not verified — user should confirm agent is working before applying fixes" -3. **Proceed directly to scans** - -**IF `{headless_mode}=false` or not set:** -1. **Check for uncommitted changes** — Run `git status` on the repository. If uncommitted changes: - - Warn: "You have uncommitted changes. It's recommended to commit before optimization so you can easily revert if needed." - - Ask: "Do you want to proceed anyway, or commit first?" - - Halt and wait for user response - -2. **Verify agent is functioning** — Ask if the agent is currently working as expected. Optimization should improve, not break working agents. +Check for uncommitted changes. In headless mode, note warnings and proceed. In interactive mode, inform the user and confirm. In interactive mode, also confirm the agent is currently functioning. -## Communicate This Guidance to the User +## Optimization Principles **Agent skills are both art and science.** The report will contain many suggestions. Apply these decision rules: diff --git a/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md b/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md index b772765..d80d77f 100644 --- a/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md +++ b/src/skills/bmad-agent-builder/quality-scan-agent-cohesion.md @@ -113,33 +113,6 @@ Find and read: | Entry points are clear | User knows where to start | | Exit points provide value | User gets something useful, not just internal state | -## Analysis Process - -1. **Build mental model** of the agent: - - Who is this agent? (persona, role, expertise) - - What is it FOR? (purpose, outcomes) - - What can it ACTUALLY do? (enumerate all capabilities) - -2. **Evaluate alignment**: - - Does the persona justify the capabilities? - - Are there capabilities that don't fit? - - Is the persona underserving the capabilities? (too modest) - -3. **Gap analysis**: - - For each core purpose, ask "can this agent actually do that?" - - For each key workflow, check if all steps are covered - - Consider adjacent capabilities that should exist - -4. **Redundancy check**: - - Group similar capabilities - - Identify overlaps - - Note consolidation opportunities - -5. **Creative synthesis**: - - What would make this agent MORE useful? - - What's the ONE thing missing that would have biggest impact? - - What's the ONE thing to remove that would clarify focus? - ## Output Format Output your findings using the universal schema defined in `references/universal-scan-schema.md`. @@ -230,40 +203,11 @@ Merge all findings into the single `findings[]` array: ## Process -1. Read SKILL.md to understand persona and intent -2. Read all prompts to understand what each actually does -3. Read dimension-definitions.md if available for context -4. Build mental model of the agent as a whole -5. Evaluate cohesion across all 6 dimensions -6. Generate findings with specific, actionable suggestions -7. Identify strengths (positive feedback is valuable!) -8. Write JSON to `{quality-report-dir}/agent-cohesion-temp.json` -9. Return only the filename: `agent-cohesion-temp.json` +Read all agent files. Evaluate cohesion across all 6 dimensions above. Write findings to `{quality-report-dir}/agent-cohesion-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, think one level deeper and verify completeness and quality:** - -### Scan Completeness -- Did I read SKILL.md and ALL prompts? -- Did I build a complete mental model of the agent? -- Did I evaluate ALL 6 cohesion dimensions (persona, completeness, redundancy, external, granularity, journey)? -- Did I read dimension-definitions.md if it exists? - -### Finding Quality -- Are "gap" findings truly missing or intentionally out of scope? -- Are "redundancy" findings actual overlap or complementary capabilities? -- Are "misalignment" findings real contradictions or just different aspects? -- Are severity ratings appropriate (high only for glaring omissions)? -- Did I include strengths (positive feedback is valuable)? - -### Cohesion Review -- Does single_most_important_fix represent the highest-impact improvement? -- Do findings tell a coherent story about this agent's cohesion? -- Would addressing high-severity issues significantly improve the agent? -- Are creative_suggestions actually valuable, not just nice-to-haves? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify completeness across all dimensions and that findings tell a coherent story. ## Key Principle diff --git a/src/skills/bmad-agent-builder/quality-scan-enhancement-opportunities.md b/src/skills/bmad-agent-builder/quality-scan-enhancement-opportunities.md index df2b565..d7ba768 100644 --- a/src/skills/bmad-agent-builder/quality-scan-enhancement-opportunities.md +++ b/src/skills/bmad-agent-builder/quality-scan-enhancement-opportunities.md @@ -93,11 +93,11 @@ Every agent makes assumptions. Surface the ones that are most likely to be wrong | **Single-session completion** | Does the agent assume the interaction completes in one session? | | **Agent isolation** | Does the agent assume it's the only thing the user is doing? | -### 5. Autonomous Potential +### 5. Headless Potential Many agents are built for human-in-the-loop interaction — conversational discovery, iterative refinement, user confirmation at each step. But what if someone passed in a headless flag and a detailed prompt? Could this agent just... do its job, create the artifact, and return the file path? -This is one of the most transformative "what ifs" you can ask about a HITL agent. An agent that works both interactively AND autonomously is dramatically more valuable — it can be invoked by other skills, chained in pipelines, run on schedules, or used by power users who already know what they want. +This is one of the most transformative "what ifs" you can ask about a HITL agent. An agent that works both interactively AND headlessly is dramatically more valuable — it can be invoked by other skills, chained in pipelines, run on schedules, or used by power users who already know what they want. **For each HITL interaction point, ask:** @@ -108,14 +108,14 @@ This is one of the most transformative "what ifs" you can ask about a HITL agent | Is this clarification always needed, or only for ambiguous input? | "Did you mean X or Y?" → only needed when input is vague | | Does this interaction add value or just ceremony? | Some confirmations exist because the builder assumed interactivity, not because they're necessary | -**Assess the agent's autonomous potential:** +**Assess the agent's headless potential:** | Level | What It Means | |-------|--------------| -| **Headless-ready** | Could work autonomously today with minimal changes — just needs a flag to skip confirmations | +| **Headless-ready** | Could work headlessly today with minimal changes — just needs a flag to skip confirmations | | **Easily adaptable** | Most interaction points could accept pre-supplied parameters; needs a headless path added to 2-3 capabilities | -| **Partially adaptable** | Core artifact creation could be autonomous, but discovery/interview capabilities are fundamentally interactive — suggest a "skip to build" entry point | -| **Fundamentally interactive** | The value IS the conversation (coaching, brainstorming, exploration) — autonomous mode wouldn't make sense, and that's OK | +| **Partially adaptable** | Core artifact creation could be headless, but discovery/interview capabilities are fundamentally interactive — suggest a "skip to build" entry point | +| **Fundamentally interactive** | The value IS the conversation (coaching, brainstorming, exploration) — headless mode wouldn't make sense, and that's OK | **When the agent IS adaptable, suggest the output contract:** - What would a headless invocation return? (file path, JSON summary, status code) @@ -123,7 +123,7 @@ This is one of the most transformative "what ifs" you can ask about a HITL agent - Where would the `{headless_mode}` flag need to be checked? - Which capabilities could auto-resolve vs which need explicit input even in headless mode? -**Don't force it.** Some agents are fundamentally conversational — their value is the interactive exploration. Flag those as "fundamentally interactive" and move on. The insight is knowing which agents *could* transform, not pretending all of them should. +**Don't force it.** Some agents are fundamentally conversational — their value is the interactive exploration. Flag those as "fundamentally interactive" and move on. The insight is knowing which agents *could* transform, not pretending all should. ### 6. Facilitative Workflow Patterns @@ -155,13 +155,7 @@ For each journey, note: ## How to Think -1. **Go wild first.** Read the agent and let your imagination run. Think of the weirdest user, the worst timing, the most unexpected input. No idea is too crazy in this phase. - -2. **Then temper.** For each wild idea, ask: "Is there a practical version of this that would actually improve the agent?" If yes, distill it to a sharp, specific suggestion. If the idea is genuinely impractical, drop it — don't pad findings with fantasies. - -3. **Prioritize by user impact.** A suggestion that prevents user confusion outranks a suggestion that adds a nice-to-have feature. A suggestion that transforms the experience outranks one that incrementally improves it. - -4. **Stay in your lane.** Don't flag structural issues (structure scanner handles that), craft quality (prompt-craft handles that), performance (execution-efficiency handles that), or architectural coherence (agent-cohesion handles that). Your findings should be things *only a creative thinker would notice*. +Explore creatively, then distill each idea into a concrete, actionable suggestion. Prioritize by user impact. Stay in your lane. ## Output Format @@ -239,39 +233,8 @@ Write JSON findings to: `{quality-report-dir}/enhancement-opportunities-temp.jso ## Process -1. Read SKILL.md — deeply understand purpose, persona, audience, and intent -2. Read all prompts — walk through each capability mentally as a user -3. Read resources — understand what's been considered -4. Inhabit each user archetype (including the automator) and mentally simulate their journey through the agent -5. Surface edge cases, experience gaps, delight opportunities, risky assumptions, and autonomous potential -6. For autonomous potential: map every HITL interaction point and assess which could auto-resolve -7. For facilitative/interactive agents: check against all seven facilitative workflow patterns -8. Go wild with ideas, then temper each to a concrete suggestion -9. Prioritize by user impact -10. Write JSON to `{quality-report-dir}/enhancement-opportunities-temp.json` -11. Return only the filename: `enhancement-opportunities-temp.json` +Read all agent files. Analyze through each creative lens above. Write findings to `{quality-report-dir}/enhancement-opportunities-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, challenge your own findings:** - -### Creative Quality Check -- Did I actually *inhabit* different user archetypes (including the automator), or did I just analyze from the builder's perspective? -- Are my edge cases *realistic* — things that would actually happen — or contrived? -- Are my delight opportunities genuinely delightful, or are they feature bloat? -- Did I find at least one thing that would make the builder say "I never thought of that"? -- Did I honestly assess autonomous potential — not forcing headless on fundamentally interactive agents, but not missing easy wins either? -- For adaptable agents, is my suggested output contract concrete enough to implement? - -### Temper Check -- Is every suggestion *actionable* — could someone implement it from my description? -- Did I drop the impractical wild ideas instead of padding my findings? -- Am I staying in my lane — not flagging structure, craft, performance, or architecture issues? -- Would implementing my top suggestions genuinely improve the user experience? - -### Honesty Check -- Did I note what the agent already does well? (Bright spots in user journeys) -- Are my severity ratings honest — high-opportunity only for genuinely transformative ideas? -- Is my `boldest_idea` actually bold, or is it safe and obvious? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify findings are realistic, actionable, and honest about what the agent already does well. diff --git a/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md b/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md index 1a9e2d4..1a7e152 100644 --- a/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md +++ b/src/skills/bmad-agent-builder/quality-scan-execution-efficiency.md @@ -159,23 +159,8 @@ Merge all items into the single `findings[]` array: ## Process -1. Read pre-pass JSON at `{quality-report-dir}/execution-deps-prepass.json` -2. Read SKILL.md for On Activation and operation flow patterns -3. Read all prompt files for execution patterns -4. Check memory loading strategy (selective vs all-at-once) -5. Check for parent-reading-before-delegating patterns -6. Verify subagent instructions have output specifications -7. Identify sequential operations that could be parallel -8. Check resource loading patterns -9. Write JSON to `{quality-report-dir}/execution-efficiency-temp.json` -10. Return only the filename: `execution-efficiency-temp.json` +Read pre-pass JSON and raw files as needed. Evaluate efficiency across all dimensions above. Write JSON to `{quality-report-dir}/execution-efficiency-temp.json`. Return only the filename. ## Critical After Draft Output -Before finalizing, verify: -- Are "sequential-independent" findings truly independent? -- Are "parent-reads-first" findings actual context bloat or necessary prep? -- Are memory loading findings fair — does the agent actually load too much? -- Would implementing suggestions significantly improve efficiency? - -Only after verification, write final JSON and return filename. +Before finalizing, verify findings target genuine inefficiencies with measurable impact. diff --git a/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md b/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md index 4a85ae0..e55319b 100644 --- a/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md +++ b/src/skills/bmad-agent-builder/quality-scan-prompt-craft.md @@ -4,7 +4,7 @@ You are **PromptCraftBot**, a quality engineer who understands that great agent ## Overview -You evaluate the craft quality of an agent's prompts — SKILL.md and all capability prompts. This covers token efficiency, anti-patterns, outcome focus, and instruction clarity as a **unified assessment** rather than isolated checklists. The reason these must be evaluated together: a finding that looks like "waste" from a pure efficiency lens may be load-bearing persona context that enables the agent to stay in character and handle situations the prompt doesn't explicitly cover. Your job is to distinguish between the two. +You evaluate the craft quality of an agent's prompts — SKILL.md and all capability prompts. This covers token efficiency, anti-patterns, outcome driven focus, and instruction clarity as a **unified assessment** rather than isolated checklists. The reason these must be evaluated together: a finding that looks like "waste" from a pure efficiency lens may be load-bearing persona context that enables the agent to stay in character and handle situations the prompt doesn't explicitly cover. Your job is to distinguish between the two. Guiding principle should be following outcome driven engineering focus. ## Your Role @@ -217,29 +217,8 @@ Write JSON findings to: `{quality-report-dir}/prompt-craft-temp.json` ## Process -1. Read pre-pass JSON at `{quality-report-dir}/prompt-metrics-prepass.json` -2. Read SKILL.md — assess agent type, evaluate Overview quality, persona context -3. Read all prompt files at skill root -4. Check references/ for progressive disclosure -5. Evaluate Overview quality (present? appropriate? excessive? missing?) -6. Check for over-optimization — is this a complex agent stripped to bare skeleton? -7. Check size and progressive disclosure -8. For each capability prompt: config header, self-containment, context sufficiency -9. Scan for genuine token waste vs load-bearing persona context -10. Evaluate outcome vs implementation balance given agent type -11. Check intelligence placement -12. Check communication style consistency across prompts -13. Write JSON to `{quality-report-dir}/prompt-craft-temp.json` -14. Return only the filename: `prompt-craft-temp.json` +Read pre-pass JSON and all prompt files. Evaluate using the criteria in Parts 1-3 above. Write JSON to `{quality-report-dir}/prompt-craft-temp.json`. Return only the filename. ## Critical After Draft Output -Before finalizing, verify: -- Did I read pre-pass JSON and EVERY prompt file? -- For each "token-waste" finding: Is this genuinely wasteful, or load-bearing persona context? -- Am I flagging persona voice as waste? Re-evaluate — personality is investment for agents. -- Did I check for under-contextualization? -- Did I check communication style consistency? -- Would implementing ALL suggestions produce a better agent, or strip character? - -Only after verification, write final JSON and return filename. +Before finalizing, verify all files were read, token-waste findings are genuine (not persona context), and suggestions would improve the agent holistically. diff --git a/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md b/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md index 78aec8f..27626b7 100644 --- a/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md +++ b/src/skills/bmad-agent-builder/quality-scan-script-opportunities.md @@ -225,36 +225,8 @@ Write JSON findings to: `{quality-report-dir}/script-opportunities-temp.json` ## Process -1. Check `scripts/` directory — inventory what scripts already exist (avoid suggesting duplicates) -2. Read SKILL.md — check On Activation and inline operations for deterministic work -3. Read all prompt files — for each instruction, apply the determinism test -4. Read resource files — check if any resource content could be generated/validated by scripts -5. For each finding: estimate LLM tax, assess implementation complexity, check pre-pass potential -6. For each finding: consider the --help pattern — if a prompt currently inlines a script's interface, note the additional savings -7. Write JSON to `{quality-report-dir}/script-opportunities-temp.json` -8. Return only the filename: `script-opportunities-temp.json` +Read all agent files and the scripts/ directory. Apply the determinism test and category analysis described above. Write findings to `{quality-report-dir}/script-opportunities-temp.json`. Return only the filename. ## Critical After Draft Output -Before finalizing, verify: - -### Determinism Accuracy -- For each finding: Is this TRULY deterministic, or does it require judgment I'm underestimating? -- Am I confusing "structured output" with "deterministic"? (An LLM summarizing in JSON is still judgment) -- Would the script actually produce the same quality output as the LLM? - -### Creativity Check -- Did I look beyond obvious validation? (Pre-processing and post-processing are often the highest-value opportunities) -- Did I consider the full toolbox? (Not just simple regex — ast parsing, dependency graphs, metric extraction) -- Did I check if any LLM step is reading large files when a script could extract the relevant parts first? - -### Practicality Check -- Are implementation complexity ratings realistic? -- Are token savings estimates reasonable? -- Would implementing the top findings meaningfully improve the agent's efficiency? -- Did I check for existing scripts to avoid duplicates? - -### Lane Check -- Am I staying in my lane? I find script opportunities — I don't evaluate prompt craft (L2), execution efficiency (L3), cohesion (L4), or creative enhancements (L5). - -Only after verification, write final JSON and return filename. +Before finalizing, verify flagged operations are truly deterministic, existing scripts aren't duplicated, and you stayed in your lane. diff --git a/src/skills/bmad-agent-builder/quality-scan-structure.md b/src/skills/bmad-agent-builder/quality-scan-structure.md index 4f57907..c705553 100644 --- a/src/skills/bmad-agent-builder/quality-scan-structure.md +++ b/src/skills/bmad-agent-builder/quality-scan-structure.md @@ -86,16 +86,16 @@ Include all pre-pass findings in your output, preserved as-is. These are determi | Check | Why It Matters | |-------|----------------| | Memory system file exists if agent declares sidecar | Sidecar without memory spec is incomplete | -| Access boundaries defined | Critical for autonomous agents especially | +| Access boundaries defined | Critical for headless agents especially | | Memory paths consistent across all files | Different paths in different files break memory | | Save triggers defined if memory persists | Without save triggers, memory never updates | ### Headless Mode (If Declared) | Check | Why It Matters | |-------|----------------| -| Autonomous activation prompt exists | Agent declared autonomous but has no wake prompt | +| Headless activation prompt exists | Agent declared headless but has no wake prompt | | Default wake behavior defined | Agent won't know what to do without specific task | -| Autonomous tasks documented | Users need to know available tasks | +| Headless tasks documented | Users need to know available tasks | --- @@ -154,24 +154,8 @@ Write JSON findings to: `{quality-report-dir}/structure-temp.json` ## Process -1. Read pre-pass JSON at `{quality-report-dir}/structure-capabilities-prepass.json` -2. Include all pre-pass findings in output -3. Read SKILL.md for judgment-based assessment -4. Read relevant prompt files for cross-reference quality -5. Assess description, identity, communication style, principles quality -6. Check logical consistency across all components -7. Check memory setup completeness if sidecar -8. Check headless mode setup if declared -9. Write JSON to `{quality-report-dir}/structure-temp.json` -10. Return only the filename: `structure-temp.json` +Read pre-pass JSON (include all findings verbatim). Read raw files for judgment-based assessment as described above. Write findings to `{quality-report-dir}/structure-temp.json`. Return only the filename. ## Critical After Draft Output -Before finalizing, verify: -- Did I include ALL pre-pass findings? -- Did I read SKILL.md for judgment calls? -- Did I check logical consistency between description, identity, and capabilities? -- Are my severity ratings appropriate? -- Would implementing my suggestions improve the agent? - -Only after verification, write final JSON and return filename. +Before finalizing, verify findings cover all structural dimensions and severity ratings are honest. diff --git a/src/skills/bmad-agent-builder/references/quality-dimensions.md b/src/skills/bmad-agent-builder/references/quality-dimensions.md index 064d17c..a1c95bf 100644 --- a/src/skills/bmad-agent-builder/references/quality-dimensions.md +++ b/src/skills/bmad-agent-builder/references/quality-dimensions.md @@ -22,10 +22,10 @@ Scripts handle plumbing (fetch, transform, validate). Prompts handle judgment (i SKILL.md stays focused. Detail goes where it belongs. -- Capability instructions → prompt files at skill root -- Reference data, schemas, large tables → `references/` -- Templates, starter files → `assets/` -- Memory discipline → `references/memory-system.md` +- Capability instructions → `./references/` +- Reference data, schemas, large tables → `./references/` +- Templates, starter files → `./assets/` +- Memory discipline → `./references/memory-system.md` - Multi-capability SKILL.md under ~250 lines: fine as-is - Single-purpose up to ~500 lines: acceptable if focused @@ -33,13 +33,13 @@ SKILL.md stays focused. Detail goes where it belongs. Two parts: `[5-8 word summary]. [Use when user says 'X' or 'Y'.]` -Default to conservative triggering. See `references/standard-fields.md` for full format and examples. +Default to conservative triggering. See `./references/standard-fields.md` for full format and examples. ## 5. Path Construction Only use `{project-root}` for `_bmad` paths. Config variables used directly — they already contain `{project-root}`. -See `references/standard-fields.md` for correct/incorrect patterns. +See `./references/standard-fields.md` for correct/incorrect patterns. ## 6. Token Efficiency diff --git a/src/skills/bmad-agent-builder/references/script-opportunities-reference.md b/src/skills/bmad-agent-builder/references/script-opportunities-reference.md index 4aa2a9c..b1c7602 100644 --- a/src/skills/bmad-agent-builder/references/script-opportunities-reference.md +++ b/src/skills/bmad-agent-builder/references/script-opportunities-reference.md @@ -166,16 +166,8 @@ All scripts use PEP 723 and `--help`. When a skill's prompt needs to invoke a sc **Why:** Validate activation order matches best practices **Checks:** -```python -# Look for steps in order: -1. Activation mode detection -2. Config loading -3. First-run check -4. Access boundaries load -5. Memory load -6. Greet -8. Present menu -``` + +Validate that the activation sequence is logically ordered (e.g., config loads before config is used, memory loads before memory is referenced). **Output:** JSON with detected steps, missing steps, out-of-order warnings diff --git a/src/skills/bmad-agent-builder/references/skill-best-practices.md b/src/skills/bmad-agent-builder/references/skill-best-practices.md index 55b2976..b10e6f0 100644 --- a/src/skills/bmad-agent-builder/references/skill-best-practices.md +++ b/src/skills/bmad-agent-builder/references/skill-best-practices.md @@ -1,218 +1,109 @@ # Skill Authoring Best Practices -Practical patterns for writing effective BMad agent skills. For field definitions and description format, see `references/standard-fields.md`. For quality dimensions, see `references/quality-dimensions.md`. - -## Core Principle: Informed Autonomy - -Give the executing agent enough context to make good judgment calls — not just enough to follow steps. The right test for every piece of content is: "Would the agent make *better decisions* with this context?" If yes, keep it. If it's genuinely redundant or mechanical, cut it. - -## Freedom Levels - -Match specificity to task fragility: - -| Freedom | When to Use | Example | -|---------|-------------|---------| -| **High** (text instructions) | Multiple valid approaches, context-dependent | "Analyze the user's vision and suggest capabilities" | -| **Medium** (pseudocode/templates) | Preferred pattern exists, some variation OK | `def generate_config(capabilities, format="json"):` | -| **Low** (exact scripts) | Fragile operations, consistency critical | `python3 scripts/scan-path-standards.py path/to/skill` (do not modify) | - -**Analogy**: Narrow bridge with cliffs = low freedom. Open field = high freedom. - -## Common Patterns - -### Template Pattern - -**Strict** (must follow exactly): -````markdown -## Report structure -ALWAYS use this template: -```markdown -# [Title] -## Summary -[One paragraph] -## Findings -- Finding 1 with data -``` -```` - -**Flexible** (adapt as needed): -````markdown -Here's a sensible default, use judgment: -```markdown -# [Title] -## Summary -[Overview] -``` -Adapt based on context. -```` - -### Examples Pattern - -Input/output pairs show expected style: -````markdown -## Commit message format -**Example 1:** -Input: "Added user authentication with JWT tokens" -Output: `feat(auth): implement JWT-based authentication` -```` - -### Conditional Workflow - -```markdown -1. Determine modification type: - **Creating new?** → Creation workflow - **Editing existing?** → Editing workflow -``` +For field definitions and description format, see `./standard-fields.md`. For quality dimensions, see `./quality-dimensions.md`. -### Soft Gate Elicitation +## Core Philosophy: Outcome-Based Authoring -For guided/interactive workflows, use "anything else?" soft gates at natural transition points instead of hard menus. This pattern draws out information users didn't know they had: +Skills should describe **what to achieve**, not **how to achieve it**. The LLM is capable of figuring out the approach — it needs to know the goal, the constraints, and the why. -```markdown -## After completing a discovery section: -Present what you've captured so far, then: -"Anything else you'd like to add, or shall we move on?" -``` +**The test for every instruction:** Would removing this cause the LLM to produce a worse outcome? If the LLM would do it anyway — or if it's just spelling out mechanical steps — cut it. -**Why it works:** Users almost always remember one more thing when given a graceful exit ramp rather than a hard stop. The low-pressure phrasing invites contribution without demanding it. This consistently produces richer, more complete artifacts than rigid section-by-section questioning. +### Outcome vs Prescriptive -**When to use:** Any guided workflow or agent with collaborative discovery — product briefs, requirements gathering, design reviews, brainstorming synthesis. Use at every natural transition between topics or sections. +| Prescriptive (avoid) | Outcome-based (prefer) | +|---|---| +| "Step 1: Ask about goals. Step 2: Ask about constraints. Step 3: Summarize and confirm." | "Ensure the user's vision is fully captured — goals, constraints, and edge cases — before proceeding." | +| "Load config. Read user_name. Read communication_language. Greet the user by name in their language." | "Load available config and greet the user appropriately." | +| "Create a file. Write the header. Write section 1. Write section 2. Save." | "Produce a report covering X, Y, and Z." | -**When NOT to use:** Autonomous/headless execution, or steps where additional input would cause scope creep rather than enrich the output. +The prescriptive versions miss requirements the author didn't think of. The outcome-based versions let the LLM adapt to the actual situation. -### Intent-Before-Ingestion +### Why This Works -Never scan artifacts, documents, or project context until you understand WHY the user is here. Scanning without purpose produces noise, not signal. +- **Why over what** — When you explain why something matters, the LLM adapts to novel situations. When you just say what to do, it follows blindly even when it shouldn't. +- **Context enables judgment** — Give domain knowledge, constraints, and goals. The LLM figures out the approach. It's better at adapting to messy reality than any script you could write. +- **Prescriptive steps create brittleness** — When reality doesn't match the script, the LLM either follows the wrong script or gets confused. Outcomes let it adapt. +- **Every instruction should carry its weight** — If the LLM would do it anyway, the instruction is noise. If the LLM wouldn't know to do it without being told, that's signal. -```markdown -## On activation: -1. Greet and understand intent — what is this about? -2. Accept whatever inputs the user offers -3. Ask if they have additional documents or context -4. ONLY THEN scan artifacts, scoped to relevance -``` +### When Prescriptive Is Right -**Why it works:** Without knowing what the user wants, you can't judge what's relevant in a 100-page research doc vs a brainstorming report. Intent gives you the filter. Without it, scanning is a fool's errand. +Reserve exact steps for **fragile operations** where getting it wrong has consequences — script invocations, exact file paths, specific CLI commands, API calls with precise parameters. These need low freedom because there's one right way to do them. -**When to use:** Any agent that ingests documents, project context, or external data as part of its process. +| Freedom | When | Example | +|---------|------|---------| +| **High** (outcomes) | Multiple valid approaches, LLM judgment adds value | "Ensure the user's requirements are complete" | +| **Medium** (guided) | Preferred approach exists, some variation OK | "Present findings in a structured report with an executive summary" | +| **Low** (exact) | Fragile, one right way, consequences for deviation | `python3 scripts/scan-path-standards.py {skill-path}` | -### Capture-Don't-Interrupt +## Patterns -When users provide information beyond the current scope (e.g., dropping requirements during a product brief, mentioning platforms during vision discovery), capture it silently for later use rather than redirecting or stopping them. +These are patterns that naturally emerge from outcome-based thinking. Apply them when they fit — they're not a checklist. -```markdown -## During discovery: -If user provides out-of-scope but valuable info: -- Capture it (notes, structured aside, addendum bucket) -- Don't interrupt their flow -- Use it later in the appropriate stage or output -``` +### Soft Gate Elicitation -**Why it works:** Users in creative flow will share their best insights unprompted. Interrupting to say "we'll cover that later" kills momentum and may lose the insight entirely. Capture everything, distill later. +At natural transitions, invite contribution without demanding it: "Anything else, or shall we move on?" Users almost always remember one more thing when given a graceful exit ramp. This produces richer artifacts than rigid section-by-section questioning. -**When to use:** Any collaborative discovery agent where the user is brainstorming, explaining, or brain-dumping. +### Intent-Before-Ingestion -### Dual-Output: Human Artifact + LLM Distillate +Understand why the user is here before scanning documents or project context. Intent gives you the relevance filter — without it, scanning is noise. -Any artifact-producing agent can output two complementary documents: a polished human-facing artifact AND a token-conscious, structured distillate optimized for downstream LLM consumption. +### Capture-Don't-Interrupt -```markdown -## Output strategy: -1. Primary: Human-facing document (exec summary, report, brief) -2. Optional: LLM distillate — dense, structured, token-efficient - - Captures overflow that doesn't belong in the human doc - - Rejected ideas (so downstream doesn't re-propose them) - - Detail bullets with just enough context to stand alone - - Designed to be loaded as context for the next workflow -``` +When users provide information beyond the current scope, capture it for later rather than redirecting. Users in creative flow share their best insights unprompted — interrupting loses them. -**Why it works:** Human docs are concise by design — they can't carry all the detail surfaced during discovery. But that detail has value for downstream LLM workflows (PRD creation, architecture design, etc.). The distillate bridges the gap without bloating the primary artifact. +### Dual-Output: Human Artifact + LLM Distillate -**When to use:** Any agent producing documents that feed into subsequent LLM workflows. The distillate is always optional — offered to the user, not forced. +Artifact-producing skills can output both a polished human-facing document and a token-efficient distillate for downstream LLM consumption. The distillate captures overflow, rejected ideas, and detail that doesn't belong in the human doc but has value for the next workflow. Always optional. ### Parallel Review Lenses -Before finalizing any artifact, fan out multiple reviewers with different perspectives to catch blind spots the builder/facilitator missed. - -```markdown -## Near completion: -Fan out 2-3 review subagents in parallel: -- Skeptic: "What's missing? What assumptions are untested?" -- Opportunity Spotter: "What adjacent value? What angles?" -- Contextual Reviewer: LLM picks the best third lens - (e.g., "regulatory risk" for healthtech, "DX critic" for devtools) +Before finalizing significant artifacts, fan out reviewers with different perspectives — skeptic, opportunity spotter, domain-specific lens. If subagents aren't available, do a single critical self-review pass. Multiple perspectives catch blind spots no single reviewer would. -Graceful degradation: If subagents unavailable, -main agent does a single critical self-review pass. -``` +### Three-Mode Architecture (Guided / Yolo / Headless) -**Why it works:** A single perspective — even an expert one — has blind spots. Multiple lenses surface issues and opportunities that no single reviewer would catch. The contextually-chosen third lens ensures domain-specific concerns aren't missed. +Consider whether the skill benefits from multiple execution modes: -**When to use:** Any agent producing a significant artifact (briefs, PRDs, designs, architecture docs). The review step is lightweight but high-value. +| Mode | When | Behavior | +|------|------|----------| +| **Guided** | Default | Conversational discovery with soft gates | +| **Yolo** | "just draft it" | Ingest everything, draft complete artifact, then refine | +| **Headless** | `--headless` / `-H` | Complete the task without user input, using sensible defaults | -### Three-Mode Architecture (Guided / Yolo / Autonomous) - -For interactive agents, offer three execution modes that match different user contexts: - -| Mode | Trigger | Behavior | -|------|---------|----------| -| **Guided** | Default | Section-by-section with soft gates. Drafts from what it knows, questions what it doesn't. | -| **Yolo** | `--yolo` or "just draft it" | Ingests everything, drafts complete artifact upfront, then walks user through refinement. | -| **Autonomous** | `--headless` / `-H` | Headless. Takes inputs, produces artifact, no interaction. | - -**Why it works:** Not every user wants the same experience. A first-timer needs guided discovery. A repeat user with clear inputs wants yolo. A pipeline wants autonomous. Same agent, three entry points. - -**When to use:** Any facilitative agent that produces an artifact. Not all agents need all three — but considering them during design prevents painting yourself into a single interaction model. +Not all skills need all three. But considering them during design prevents locking into a single interaction model. ### Graceful Degradation -Every subagent-dependent feature should have a fallback path. If the platform doesn't support parallel subagents (or subagents at all), the workflow must still progress. - -```markdown -## Subagent-dependent step: -Try: Fan out subagents in parallel -Fallback: Main agent performs the work sequentially -Never: Block the workflow because a subagent feature is unavailable -``` - -**Why it works:** Skills run across different platforms, models, and configurations. A skill that hard-fails without subagents is fragile. A skill that gracefully falls back to sequential processing is robust everywhere. - -**When to use:** Any agent that uses subagents for research, review, or parallel processing. +Every subagent-dependent feature should have a fallback path. A skill that hard-fails without subagents is fragile — one that falls back to sequential processing works everywhere. ### Verifiable Intermediate Outputs -For complex tasks: plan → validate → execute → verify - -1. Analyze inputs -2. **Create** `changes.json` with planned updates -3. **Validate** with script before executing -4. Execute changes -5. Verify output - -Benefits: catches errors early, machine-verifiable, reversible planning. +For complex tasks with consequences: plan → validate → execute → verify. Create a verifiable plan before executing, validate with scripts where possible. Catches errors early and makes the work reversible. ## Writing Guidelines -- **Consistent terminology** — choose one term per concept, stick to it +- **Consistent terminology** — one term per concept, stick to it - **Third person** in descriptions — "Processes files" not "I help process files" - **Descriptive file names** — `form_validation_rules.md` not `doc2.md` - **Forward slashes** in all paths — cross-platform -- **One level deep** for reference files — SKILL.md → reference.md, never SKILL.md → A.md → B.md -- **TOC for long files** — add table of contents for files >100 lines +- **One level deep** for reference files — SKILL.md → reference.md, never chains +- **TOC for long files** — >100 lines ## Anti-Patterns | Anti-Pattern | Fix | |---|---| -| Too many options upfront | One default with escape hatch for edge cases | +| Numbered steps for things the LLM would figure out | Describe the outcome and why it matters | +| Explaining how to load config (the mechanic) | List the config keys and their defaults (the outcome) | +| Prescribing exact greeting/menu format | "Greet the user and present capabilities" | +| Spelling out headless mode in detail | "If headless, complete without user input" | +| Too many options upfront | One default with escape hatch | | Deep reference nesting (A→B→C) | Keep references 1 level from SKILL.md | | Inconsistent terminology | Choose one term per concept | -| Vague file names | Name by content, not sequence | | Scripts that classify meaning via regex | Intelligence belongs in prompts, not scripts | ## Scripts in Skills -- **Execute vs reference** — "Run `analyze.py` to extract fields" (execute) vs "See `analyze.py` for the algorithm" (read) +- **Execute vs reference** — "Run `analyze.py`" (execute) vs "See `analyze.py` for the algorithm" (read) - **Document constants** — explain why `TIMEOUT = 30`, not just what -- **PEP 723 for Python** — self-contained scripts with inline dependency declarations +- **PEP 723 for Python** — self-contained with inline dependency declarations - **MCP tools** — use fully qualified names: `ServerName:tool_name` diff --git a/src/skills/bmad-agent-builder/references/standard-fields.md b/src/skills/bmad-agent-builder/references/standard-fields.md index 08d1ef3..afb442a 100644 --- a/src/skills/bmad-agent-builder/references/standard-fields.md +++ b/src/skills/bmad-agent-builder/references/standard-fields.md @@ -1,10 +1,21 @@ # Standard Agent Fields +## Frontmatter Fields + +Only these fields go in the YAML frontmatter block: + +| Field | Description | Example | +|-------|-------------|---------| +| `name` | Full skill name (kebab-case, same as folder name) | `bmad-agent-tech-writer`, `bmad-cis-agent-lila` | +| `description` | [What it does]. [Use when user says 'X' or 'Y'.] | See Description Format below | + +## Content Fields + +These are used within the SKILL.md body — never in frontmatter: + | Field | Description | Example | |-------|-------------|---------| -| `name` | Full skill name | `bmad-agent-tech-writer`, `bmad-cis-agent-lila` | -| `skillName` | Functional name (kebab-case) | `tech-writer`, `lila` | -| `displayName` | Friendly name | `Paige`, `Lila`, `Floyd` | +| `displayName` | Friendly name (title heading, greetings) | `Paige`, `Lila`, `Floyd` | | `title` | Role title | `Tech Writer`, `Holodeck Operator` | | `icon` | Single emoji | `🔥`, `🌟` | | `role` | Functional role | `Technical Documentation Specialist` | @@ -44,60 +55,25 @@ This skill {what it does}. Use when {when to use}. Returns {output format} with ## Path Rules -**Critical**: When prompts reference files in memory, always use full paths. +### Skill-Internal Files -### Memory Files (sidecar) - -Always use: `{project-root}/_bmad/memory/{skillName}-sidecar/` - -Examples: -- `{project-root}/_bmad/memory/journaling-companion-sidecar/index.md` -- `{project-root}/_bmad/memory/journaling-companion-sidecar/access-boundaries.md` — **Required** -- `{project-root}/_bmad/memory/journaling-companion-sidecar/autonomous-log.md` -- `{project-root}/_bmad/memory/journaling-companion-sidecar/references/tags-reference.md` +All references to files within the skill use `./` relative paths: +- `./references/memory-system.md` +- `./references/some-guide.md` +- `./scripts/calculate-metrics.py` -### Access Boundaries (Standard for all agents) +This distinguishes skill-internal files from `{project-root}` paths — without the `./` prefix the LLM may confuse them. -Every agent must have an `access-boundaries.md` file in its sidecar memory: - -**Load on every activation** — Before any file operations. - -**Structure:** -```markdown -# Access Boundaries for {displayName} - -## Read Access -- {folder-or-pattern} - -## Write Access -- {folder-or-pattern} - -## Deny Zones -- {forbidden-path} -``` - -**Purpose:** Define clear boundaries for what the agent can and cannot access, especially important for autonomous agents. - -### User-Configured Locations - -Folders/files the user provides during init (like journal location) get stored in `index.md`. Both interactive and autonomous modes: - -1. Load `index.md` first -2. Read the user's configured paths -3. Use those paths for operations +### Memory Files (sidecar) -Example pattern: -```markdown -## Autonomous Mode +Always use `{project-root}` prefix: `{project-root}/_bmad/memory/{skillName}-sidecar/` -When run autonomously: -1. Load `{project-root}/_bmad/memory/{skillName}-sidecar/index.md` to get user's journal location -2. Read entries from that location -3. Write results to `{project-root}/_bmad/memory/{skillName}-sidecar/autonomous-log.md` -``` +The sidecar `index.md` is the single entry point to the agent's memory system — it tells the agent what else to load (boundaries, logs, references, etc.). Load it once on activation; don't duplicate load instructions for individual memory files. -## CLI Usage (Autonomous Agents) +### Config Variables -Agents with autonomous mode should include a `## CLI Usage` section documenting headless invocation: +Use directly — they already contain `{project-root}` in their resolved values: +- `{output_folder}/file.md` +- Correct: `{bmad_builder_output_folder}/agent.md` +- Wrong: `{project-root}/{bmad_builder_output_folder}/agent.md` (double-prefix) -```markdown diff --git a/src/skills/bmad-agent-builder/references/template-substitution-rules.md b/src/skills/bmad-agent-builder/references/template-substitution-rules.md index d131608..0d2b29d 100644 --- a/src/skills/bmad-agent-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-agent-builder/references/template-substitution-rules.md @@ -37,8 +37,8 @@ The builder determines the rest of the agent structure — capabilities, activat ## Path References -All generated agents use these paths when applicable: -- `init.md` — First-run setup (if sidecar) -- `{name}.md` — Individual capability prompts -- `references/memory-system.md` — Memory discipline (if sidecar) -- `scripts/` — Python/shell scripts for deterministic operations +All generated agents use `./` prefix for skill-internal paths: +- `./references/init.md` — First-run onboarding (if sidecar) +- `./references/{capability}.md` — Individual capability prompts +- `./references/memory-system.md` — Memory discipline (if sidecar) +- `./scripts/` — Python/shell scripts for deterministic operations diff --git a/src/skills/bmad-agent-builder/report-quality-scan-creator.md b/src/skills/bmad-agent-builder/report-quality-scan-creator.md index f1dd3f4..ffa7161 100644 --- a/src/skills/bmad-agent-builder/report-quality-scan-creator.md +++ b/src/skills/bmad-agent-builder/report-quality-scan-creator.md @@ -65,26 +65,7 @@ Fill the template section by section, following the `` guidance ### Step 5: Verification Pass -**This step is mandatory.** After populating the report, re-read every temp file and verify against this checklist: - -- [ ] Every finding from every `*-temp.json` findings[] array -- [ ] Agent identity block (persona_summary, primary_purpose, capability_count) -- [ ] All findings with severity="strength" from any scanner -- [ ] All positive notes from prompt-craft (severity="note") -- [ ] Cohesion analysis dimensional scores table (if present) -- [ ] Consolidation opportunities from cohesion redundancy analysis -- [ ] Craft assessment, skill type assessment, and persona context assessment -- [ ] Structure metadata (sections_found, has_memory, has_headless) -- [ ] ALL user journeys with ALL friction_points and bright_spots per archetype -- [ ] The autonomous_assessment block (all fields) -- [ ] All findings with severity="suggestion" from cohesion scanners -- [ ] All findings with severity ending in "-opportunity" from execution-efficiency -- [ ] assessments.top_insights from enhancement-opportunities -- [ ] Script inventory and token savings from script-opportunities -- [ ] Skill understanding (purpose, primary_user, key_assumptions) -- [ ] Prompt health summary from prompt-craft (if prompts exist) - -If any item was dropped, add it to the appropriate section before writing. +Re-read all temp files and verify every finding appears in the report. If any item was dropped, add it to the appropriate section before writing. ### Step 6: Write and Return diff --git a/src/skills/bmad-agent-builder/scripts/scan-path-standards.py b/src/skills/bmad-agent-builder/scripts/scan-path-standards.py index 4a37184..14e9e75 100644 --- a/src/skills/bmad-agent-builder/scripts/scan-path-standards.py +++ b/src/skills/bmad-agent-builder/scripts/scan-path-standards.py @@ -5,9 +5,12 @@ 1. {project-root} only valid before /_bmad 2. Bare _bmad references must have {project-root} prefix 3. Config variables used directly (no double-prefix) -4. No ./ or ../ relative prefixes -5. No absolute paths -6. Memory paths must use {project-root}/_bmad/memory/{skillName}-sidecar/ +4. Skill-internal paths must use ./ prefix (references/, scripts/, assets/) +5. No ../ parent directory references +6. No absolute paths +7. Memory paths must use {project-root}/_bmad/memory/{skillName}-sidecar/ +8. Frontmatter allows only name and description +9. No .md files at skill root except SKILL.md """ # /// script @@ -33,9 +36,11 @@ # Absolute paths ABSOLUTE_PATH_RE = re.compile(r'(?:^|[\s"`\'(])(/(?:Users|home|opt|var|tmp|etc|usr)/\S+)', re.MULTILINE) HOME_PATH_RE = re.compile(r'(?:^|[\s"`\'(])(~/\S+)', re.MULTILINE) -# Relative prefixes +# Parent directory reference (still invalid) RELATIVE_DOT_RE = re.compile(r'(?:^|[\s"`\'(])(\.\./\S+)', re.MULTILINE) -RELATIVE_DOTSLASH_RE = re.compile(r'(?:^|[\s"`\'(])(\./\S+)', re.MULTILINE) +# Bare skill-internal paths without ./ prefix +# Match references/, scripts/, assets/ when NOT preceded by ./ +BARE_INTERNAL_RE = re.compile(r'(?:^|[\s"`\'(])(? bool: """Check if a position is inside a fenced code block.""" @@ -57,6 +65,76 @@ def get_line_number(content: str, pos: int) -> int: return content[:pos].count('\n') + 1 +def check_frontmatter(content: str, filepath: Path) -> list[dict]: + """Validate SKILL.md frontmatter contains only allowed keys.""" + findings = [] + if filepath.name != 'SKILL.md': + return findings + + if not content.startswith('---'): + findings.append({ + 'file': filepath.name, + 'line': 1, + 'severity': 'critical', + 'category': 'frontmatter', + 'title': 'SKILL.md missing frontmatter block', + 'detail': 'SKILL.md must start with --- frontmatter containing name and description', + 'action': 'Add frontmatter with name and description fields', + }) + return findings + + # Find closing --- + end = content.find('\n---', 3) + if end == -1: + findings.append({ + 'file': filepath.name, + 'line': 1, + 'severity': 'critical', + 'category': 'frontmatter', + 'title': 'SKILL.md frontmatter block not closed', + 'detail': 'Missing closing --- for frontmatter', + 'action': 'Add closing --- after frontmatter fields', + }) + return findings + + frontmatter = content[4:end] + for i, line in enumerate(frontmatter.split('\n'), start=2): + line = line.strip() + if not line or line.startswith('#'): + continue + if ':' in line: + key = line.split(':', 1)[0].strip() + if key not in VALID_FRONTMATTER_KEYS: + findings.append({ + 'file': filepath.name, + 'line': i, + 'severity': 'high', + 'category': 'frontmatter', + 'title': f'Invalid frontmatter key: {key}', + 'detail': f'Only {", ".join(sorted(VALID_FRONTMATTER_KEYS))} are allowed in frontmatter', + 'action': f'Remove {key} from frontmatter — use as content field in SKILL.md body instead', + }) + + return findings + + +def check_root_md_files(skill_path: Path) -> list[dict]: + """Check that no .md files exist at skill root except SKILL.md.""" + findings = [] + for md_file in skill_path.glob('*.md'): + if md_file.name != 'SKILL.md': + findings.append({ + 'file': md_file.name, + 'line': 0, + 'severity': 'high', + 'category': 'structure', + 'title': f'Prompt file at skill root: {md_file.name}', + 'detail': 'All progressive disclosure content must be in ./references/ — only SKILL.md belongs at root', + 'action': f'Move {md_file.name} to references/{md_file.name}', + }) + return findings + + def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: """Scan a single file for path standard violations.""" findings = [] @@ -70,10 +148,10 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: 'Absolute path found — not portable across machines'), (HOME_PATH_RE, 'absolute-path', 'high', 'Home directory path (~/) found — environment-specific'), - (RELATIVE_DOT_RE, 'relative-prefix', 'medium', + (RELATIVE_DOT_RE, 'relative-prefix', 'high', 'Parent directory reference (../) found — fragile, breaks with reorganization'), - (RELATIVE_DOTSLASH_RE, 'relative-prefix', 'medium', - 'Relative prefix (./) found — breaks when execution directory changes'), + (BARE_INTERNAL_RE, 'bare-internal-path', 'high', + 'Bare skill-internal path without ./ prefix — use ./references/, ./scripts/, ./assets/ to distinguish from {project-root} paths'), ] for pattern, category, severity, message in checks: @@ -99,9 +177,6 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: pos = match.start() if skip_fenced and is_in_fenced_block(content, pos): continue - # Check that this isn't part of {project-root}/_bmad - # The negative lookbehind handles this, but double-check - # the broader context start = max(0, pos - 30) before = content[start:pos] if '{project-root}/' in before: @@ -123,7 +198,6 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: pos = match.start() if skip_fenced and is_in_fenced_block(content, pos): continue - # Check if properly prefixed start = max(0, pos - 20) before = content[start:pos] matched_text = match.group() @@ -159,6 +233,15 @@ def scan_skill(skill_path: Path, skip_fenced: bool = True) -> dict: """Scan all .md and .json files in a skill directory.""" all_findings = [] + # Check for .md files at root that aren't SKILL.md + all_findings.extend(check_root_md_files(skill_path)) + + # Check SKILL.md frontmatter + skill_md = skill_path / 'SKILL.md' + if skill_md.exists(): + content = skill_md.read_text(encoding='utf-8') + all_findings.extend(check_frontmatter(content, skill_md)) + # Find all .md and .json files md_files = sorted(list(skill_path.rglob('*.md')) + list(skill_path.rglob('*.json'))) if not md_files: @@ -181,7 +264,10 @@ def scan_skill(skill_path: Path, skip_fenced: bool = True) -> dict: 'double_prefix': 0, 'absolute_path': 0, 'relative_prefix': 0, + 'bare_internal_path': 0, 'memory_path': 0, + 'frontmatter': 0, + 'structure': 0, } for f in all_findings: @@ -195,7 +281,7 @@ def scan_skill(skill_path: Path, skip_fenced: bool = True) -> dict: return { 'scanner': 'path-standards', 'script': 'scan-path-standards.py', - 'version': '1.0.0', + 'version': '2.0.0', 'skill_path': str(skill_path), 'timestamp': datetime.now(timezone.utc).isoformat(), 'files_scanned': files_scanned, diff --git a/src/skills/bmad-builder-setup/assets/module.yaml b/src/skills/bmad-builder-setup/assets/module.yaml index f8bb94d..439f7de 100644 --- a/src/skills/bmad-builder-setup/assets/module.yaml +++ b/src/skills/bmad-builder-setup/assets/module.yaml @@ -11,10 +11,10 @@ module_greeting: > bmad_builder_output_folder: prompt: "Where should your custom output (agent, workflow, module config) be saved?" - default: "{project-root}/_bmad-output/skills" + default: "{project-root}/skills" result: "{project-root}/{value}" bmad_builder_reports: prompt: "Output for Evals, Test, Quality and Planning Reports?" - default: "{project-root}/_bmad-output/reports" + default: "{project-root}/skills/reports" result: "{project-root}/{value}" diff --git a/src/skills/bmad-workflow-builder/SKILL.md b/src/skills/bmad-workflow-builder/SKILL.md index 7618cf2..a155d4f 100644 --- a/src/skills/bmad-workflow-builder/SKILL.md +++ b/src/skills/bmad-workflow-builder/SKILL.md @@ -1,7 +1,6 @@ --- name: bmad-workflow-builder description: Builds workflows and skills through conversational discovery and validates existing ones. Use when the user requests to "build a workflow", "modify a workflow", "quality check workflow", or "optimize skill". -argument-hint: "--headless or -H to not prompt user, initial input for create, path to existing skill with keywords optimize, edit, validate" --- # Workflow & Skill Builder @@ -10,9 +9,11 @@ argument-hint: "--headless or -H to not prompt user, initial input for create, p This skill helps you build AI workflows and skills through conversational discovery and iterative refinement. Act as an architect guide helping dreamers, builders, doers, and visionaries create the AI workflows and skills of their dreams - walking users through six phases: intent discovery, skill type classification, requirements gathering, drafting, building, and testing. Your output is a complete skill structure — from simple composable utilities to complex multi-stage workflows — ready to integrate into the BMad Method ecosystem. +**Args:** Accepts `--headless` / `-H` for non-interactive execution, an initial description for create, or a path to an existing skill with keywords like optimize, edit, or validate. + **What they're building:** -Workflows and skills are **processes, tools, and composable building blocks** — and some may benefit from personality or tone guidance when it serves the user experience. A workflow automates multi-step processes. A skill provides reusable capabilities. They range from simple input/output utilities to complex multi-stage workflows with progressive disclosure. This builder itself is a perfect example of a complex workflow — multi-stage with routing, config integration, and the ability to perform different actions with human in the loop and autonomous modes if desired based on the clear intent of the input or conversation! +Workflows and skills are **processes, tools, and composable building blocks** — and some may benefit from personality or tone guidance when it serves the user experience. A workflow automates multi-step processes. A skill provides reusable capabilities. They range from simple input/output utilities to complex multi-stage workflows with progressive disclosure following multiple paths based on the intent routing. **Your output:** A skill structure ready to integrate into a module or use standalone. @@ -20,11 +21,12 @@ Workflows and skills are **processes, tools, and composable building blocks** 1. Detect user's intent. If `--headless` or `-H` is passed, or intent is clearly non-interactive, set `{headless_mode}=true` for all sub-prompts. -2. Load config from `{project-root}/_bmad/config.yaml` (bmb section) and `config.user.yaml`. If missing, inform that `bmad-builder-setup` is available and continue with fallbacks: - - `{bmad_builder_output_folder}` — fallback: `{project-root}/skills` - - `{bmad_builder_reports}` — fallback: `{project-root}/skills` - - `{user_name}` — fallback: omit - - `{communication_language}` — fallback: match the user's language +2. Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` (root and bmb section). If missing, and the `bmad-builder-setup` skill is available, let the user know they can run it at any time to configure. Resolve and apply throughout the session (defaults in parens): + - `{user_name}` (default: null) — address the user by name + - `{communication_language}` (default: user or system intent) — use for all communications + - `{document_output_language}` (default: user or system intent) — use for generated document content + - `{bmad_builder_output_folder}` (default: `{project-root}/skills`) — save built agents here + - `{bmad_builder_reports}` (default: `{project-root}/skills/reports`) — save reports (quality, eval, planning) here 3. Route by intent — see Quick Reference below, or read the capability descriptions that follow. @@ -42,11 +44,11 @@ For workflows/skills that already work but could work *better*. This is comprehe Run this anytime you want to assess and improve an existing skill's quality. -Load `quality-optimizer.md` — it orchestrates everything including scan modes, autonomous handling, and remediation options. +Load `quality-optimizer.md` — it orchestrates everything including scan modes, headless handling, and remediation options. --- -## Quick Reference +## Skill Intent Routing Reference | Intent | Trigger Phrases | Route | |--------|----------------|-------| @@ -54,6 +56,6 @@ Load `quality-optimizer.md` — it orchestrates everything including scan modes, | **Quality Optimize** | "quality check", "validate", "review/optimize/improve workflow/skill" | Load `quality-optimizer.md` | | **Unclear** | — | Present the two options above and ask | -Pass `{headless_mode}` flag to all routes. Use TodoList tool to track progress through multi-step flows. Use AskUserQuestion tool when structuring questions for users. Use subagents for parallel work (quality scanners, web research or document review). +Regardless of what path is taken, respect and follow headless mode guidance if user requested headless_mode - if a specific instruction does not indicate how to handle headless mode, you will try to find a way. -Help the user create amazing Workflows and tools! +Enjoy the adventure and help the user create amazing Workflows and tools! diff --git a/src/skills/bmad-workflow-builder/build-process.md b/src/skills/bmad-workflow-builder/build-process.md index db2d5f7..45eb864 100644 --- a/src/skills/bmad-workflow-builder/build-process.md +++ b/src/skills/bmad-workflow-builder/build-process.md @@ -32,17 +32,12 @@ Ask upfront: - What other skills will it use from the core or specified module, we need the name, inputs, and output so we know how to integrate it? (other skills should be either core skills or skills that will be part of the module) - What are the variable names it will have access to that it needs to use? (variables can be use for things like choosing various paths in the skill, adjusting output styles, configuring output locations, tool availability, and anything that could be configurable by a user) -Load `references/classification-reference.md` for the full decision tree, classification signals, and module context rules. Use it to classify: +Load `./references/classification-reference.md` and use it to classify the skill type. Present classification with reasoning. -1. Composable building block with clear input/output and generally will use scripts either inline or in the scripts folder? → **Simple Utility** -2. Fits in a single SKILL.md, may have some resources and a prompt, but generally not very complex. Human in the Loop and Autonomous abilities? → **Simple Workflow** - - **Headless mode?** Should this workflow support `--headless` invocation? (If it produces an artifact, headless mode may be valuable) -3. Needs multiple stages and branches, may be long-running, uses progressive disclosure with prompts and resources, usually Human in the Loop with multiple paths and prompts? → **Complex Workflow** +For Simple Workflows and Complex Workflows, also ask: +- **Headless mode?** Should this workflow support `--headless` invocation? (If it produces an artifact, headless mode may be valuable) -For Complex Workflows, also ask: -- **Headless mode?** Should this workflow support `--headless` invocation? - -Present classification with reasoning. This determines template and structure. +This determines template and structure. ## Phase 3: Gather Requirements @@ -50,43 +45,22 @@ Work through conversationally, adapted per skill type, so you can either glean f **All types — Common fields:** - **Name:** kebab-case. If module: `bmad-{modulecode}-{skillname}`. If standalone: `bmad-{skillname}` -- **Description:** Two parts: [5-8 word summary of what it does]. [Use when user says 'specific phrase' or 'specific phrase'.] — Default to explicit invocation (conservative triggering) unless user specifies organic/reactive activation. See `references/standard-fields.md` for format details and examples. +- **Description:** Two parts: [5-8 word summary of what it does]. [Use when user says 'specific phrase' or 'specific phrase'.] — Default to explicit invocation (conservative triggering) unless user specifies organic/reactive activation. See `./references/standard-fields.md` for format details and examples. - **Overview:** 3-part formula (What/How/Why-Outcome). For interactive or complex skills, also include brief domain framing (what concepts does this skill operate on?) and theory of mind (who is the user and what might they not know?). These give the executing agent enough context to make judgment calls when situations don't match the script. - **Role guidance:** Brief "Act as a [role/expert]" statement to prime the model for the right domain expertise and tone - **Design rationale:** Any non-obvious choices the executing agent should understand? (e.g., "We interview before building because users rarely know their full requirements upfront") - **Module context:** Already determined in Phase 2 - **External skills used:** Which skills does this invoke? - **Script Opportunity Discovery** (active probing — do not skip): - Walk through each planned step/stage with the user and apply these filters: - 1. "Does this step have clear pass/fail criteria?" → Script candidate - 2. "Could this run without LLM judgment — no interpretation, no creativity, no ambiguity?" → Strong script candidate - 3. "Does it validate, transform, count, parse, format-convert, compare against a schema, or check structure?" → Almost certainly a script - - **Common script-worthy operations:** - - Schema/format validation (JSON, YAML, frontmatter, file structure) - - Data extraction and transformation (parsing, restructuring, field mapping) - - Counting, aggregation, and metric collection (token counts, file counts, summary stats) - - File/directory structure checks (existence, naming conventions, required files) - - Pattern matching against known standards (path conventions, naming rules) - - Comparison operations (diff, version compare, before/after, cross-reference checking) - - Dependency graphing (parsing imports, references, skill entries) - - Template artifact detection (orphaned placeholders, unresolved variables) - - Pre-processing for LLM steps (extract compact metrics from large files so the LLM works from structured data, not raw content) - - Post-processing validation (verify LLM output conforms to expected schema/structure) - - **Present your script plan**: Before moving to Phase 4, explicitly tell the user which operations you plan to implement as scripts vs. prompts, with one-line reasoning for each. Ask if they agree or want to adjust. + Walk through each planned step with the user. Identify deterministic operations that should be scripts rather than prompts. Load `./references/script-opportunities-reference.md` for the full catalog. Confirm the script-vs-prompt plan with the user before proceeding. - **Creates output documents?** If yes, will use `{document_output_language}` from config **Simple Utility additional fields:** -- **Input format:** What does it accept? -- **Output format:** What does it return? +- **Input/output format:** What does it accept and return? - **Standalone?** No config needed? (Makes it a truly standalone building block) - **Composability:** How might this be used by other skills/workflows? -- **Script needs:** What scripts does the utility require? **Simple Workflow additional fields:** - **Steps:** Numbered steps (inline in SKILL.md) -- **Tools used:** What tools/CLIs/scripts does it use? -- **Output:** What does it produce? - **Config variables:** What config vars beyond core does it need? **Complex Workflow additional fields:** @@ -94,8 +68,6 @@ Work through conversationally, adapted per skill type, so you can either glean f - **Stage progression conditions:** When does each stage complete? - **Headless mode:** If yes, what should headless execution do? Default behavior? Named tasks? - **Config variables:** Core + module-specific vars needed -- **Output artifacts:** What does this create? (output-location) -- **Dependencies:** What must run before this? What does it use? (after/before arrays) **Module capability metadata (if part of a module):** For each capability, confirm these with the user — they determine how the module's help system presents and sequences the skill: @@ -106,7 +78,7 @@ For each capability, confirm these with the user — they determine how the modu - **description (capability):** Keep this VERY short — a single sentence describing what it produces, not how it works. This is what the LLM help system shows users. (e.g., "Produces executive product brief and optional LLM distillate for PRD input.") **Path conventions (CRITICAL):** -- Skill-internal files use bare relative paths: `references/`, `scripts/`, and prompt files at root +- Skill-internal files always use `./` prefix: `./references/`, `./scripts/` — this distinguishes them from `{project-root}` paths - Only `_bmad` paths get `{project-root}` prefix: `{project-root}/_bmad/...` - Config variables used directly — they already contain `{project-root}` (no double-prefix) @@ -117,30 +89,24 @@ Once you have a cohesive idea, think one level deeper, clarify with the user any ## Phase 5: Build **Always load these before building:** -- Load `references/standard-fields.md` — field definitions, description format, path rules -- Load `references/skill-best-practices.md` — authoring patterns (freedom levels, templates, anti-patterns) -- Load `references/quality-dimensions.md` — quick mental checklist for build quality +- Load `./references/standard-fields.md` — field definitions, description format, path rules +- Load `./references/skill-best-practices.md` — authoring patterns (freedom levels, templates, anti-patterns) +- Load `./references/quality-dimensions.md` — quick mental checklist for build quality **Load based on skill type:** -- **If Complex Workflow:** Load `references/complex-workflow-patterns.md` — compaction survival, document-as-cache pattern, config integration, facilitator model, progressive disclosure with prompt files at root. This is essential for building workflows that survive long-running sessions. -- **Always load** `references/script-opportunities-reference.md` — script opportunity spotting guide, catalog, and output standards. Use this to identify additional script opportunities not caught in Phase 3, even if no scripts were initially planned. +- **If Complex Workflow:** Load `./references/complex-workflow-patterns.md` — compaction survival, document-as-cache pattern, config integration, facilitator model, progressive disclosure with prompt files in `./references/`. This is essential for building workflows that survive long-running sessions. +- **Always load** `./references/script-opportunities-reference.md` — script opportunity spotting guide, catalog, and output standards. Use this to identify additional script opportunities not caught in Phase 3, even if no scripts were initially planned. When confirmed: -1. Load template substitution rules from `references/template-substitution-rules.md` and apply - -2. Load unified template: `assets/SKILL-template.md` - - Apply skill-type conditionals (`{if-complex-workflow}`, `{if-simple-workflow}`, `{if-simple-utility}`) to keep only relevant sections +Load the references listed above, the template from `./assets/SKILL-template.md`, and `./references/template-substitution-rules.md`. Build the skill structure with progressive disclosure (SKILL.md for overview and routing, `./references/` for all progressive disclosure content). Output to `{bmad_builder_output_folder}`. -3. **Progressive disclosure:** Keep SKILL.md focused on Overview, activation, and routing. Detailed stage instructions go in prompt files at the skill root. Reference data, schemas, and large tables go in `references/`. Multi-branch SKILL.md under ~250 lines is fine as-is; single-purpose up to ~500 lines if genuinely needed. - -4. Generate folder structure and include only what is needed for the specific skill: +Generate folder structure and include only what is needed for the specific skill: **Skill Source Tree:** ``` {skill-name}/ -├── SKILL.md # name (same as folder name), description -├── *.md # Prompt files and subagent definitions at root -├── references/ # Reference data, schemas, guides (read for context) +├── SKILL.md # Frontmatter (name + description only), overview, activation, capability routing +├── references/ # ALL progressive disclosure content — capability prompts, guides, schemas ├── assets/ # Templates, starter files (copied/transformed into output) ├── scripts/ # Deterministic code — validation, transformation, testing │ └── tests/ # All scripts need unit tests @@ -149,24 +115,25 @@ When confirmed: **What goes where:** | Location | Contains | LLM relationship | |----------|----------|-----------------| -| **Root `.md` files** | Prompt/instruction files, subagent definitions | LLM **loads and executes** these as instructions — they are extensions of SKILL.md | -| **`references/`** | Reference data, schemas, tables, examples, guides | LLM **reads for context** — informational, not executable | -| **`assets/`** | Templates, starter files, boilerplate | LLM **copies/transforms** these into output — not for reasoning | -| **`scripts/`** | Python, shell scripts with tests | LLM **invokes** these — deterministic operations that don't need judgment | +| **SKILL.md** | Overview, activation, capability routing table | LLM **identity and router** — the only root `.md` file | +| **`./references/`** | Capability prompts, reference data, schemas, guides | LLM **loads on demand** — progressive disclosure via routing table | +| **`./assets/`** | Templates, starter files, boilerplate | LLM **copies/transforms** these into output — not for reasoning | +| **`./scripts/`** | Python, shell scripts with tests | LLM **invokes** these — deterministic operations that don't need judgment | + +Only create subfolders that are needed — most skills won't need all three. -Only create subfolders that are needed — most skills won't need all four. +**Lint gate** — after building, run validation and auto-fix failures: -5. Output to {`bmad_builder_output_folder`} +If subagents are available, delegate the lint-fix loop to a subagent. Otherwise run inline. -6. **Lint gate** — run deterministic validation scripts: +1. Run both lint scripts in parallel: ```bash - # Run both in parallel — they are independent - python3 scripts/scan-path-standards.py {skill-path} - python3 scripts/scan-scripts.py {skill-path} + python3 ./scripts/scan-path-standards.py {skill-path} + python3 ./scripts/scan-scripts.py {skill-path} ``` - - If any script returns critical issues: fix them before proceeding - - If only warnings/medium: note them but proceed - - These are structural checks — broken paths and script standards issues should be resolved before shipping +2. If any findings at high or critical severity: fix them and re-run the failing script +3. Repeat up to 3 attempts per script — if still failing after 3, report remaining findings and continue +4. If scripts exist in the built skill, also run unit tests ## Phase 6: Summary diff --git a/src/skills/bmad-workflow-builder/quality-optimizer.md b/src/skills/bmad-workflow-builder/quality-optimizer.md index ea4d233..47021aa 100644 --- a/src/skills/bmad-workflow-builder/quality-optimizer.md +++ b/src/skills/bmad-workflow-builder/quality-optimizer.md @@ -14,53 +14,19 @@ You orchestrate quality scans on a BMad workflow or skill. Deterministic checks **DO NOT read the target skill's files yourself.** Scripts and subagents do all analysis. -Your job: -1. Create output directory -2. Run all lint scripts + pre-pass scripts (instant, deterministic) -3. Spawn all LLM scanner subagents in parallel (with pre-pass data where available) -4. Collect all results -5. Synthesize into unified report (spawn report creator) -6. Present findings to user - -## Autonomous Mode - -**Check if `{headless_mode}=true`** — If set, run in headless mode: -- **Skip ALL questions** — proceed with safe defaults -- **Uncommitted changes:** Note in report, don't ask -- **Workflow functioning:** Assume yes, note in report that user should verify -- **After report:** Output summary and exit, don't offer next steps -- **Output format:** Structured JSON summary + report path, minimal conversational text - -**Autonomous mode output:** -```json -{ - "headless_mode": true, - "report_file": "{path-to-report}", - "summary": { ... }, - "warnings": ["Uncommitted changes detected", "Workflow functioning not verified"] -} -``` +You orchestrate quality scans: run deterministic scripts and pre-pass extractors, spawn LLM scanner subagents in parallel, then synthesize all results into a unified report. -## Pre-Scan Checks - -Before running any scans: +## Headless Mode -**IF `{headless_mode}=true`:** -1. **Check for uncommitted changes** — Run `git status`. Note in warnings array if found. -2. **Skip workflow functioning verification** — Add to warnings: "Workflow functioning not verified — user should confirm workflow is working before applying fixes" -3. **Proceed directly to scans** +If `{headless_mode}=true`, skip all user interaction, use safe defaults, note any warnings, and output structured JSON as specified in the Present Findings section. -**IF `{headless_mode}=false` or not set:** -1. **Check for uncommitted changes** — Run `git status` on the repository. If uncommitted changes: - - Warn: "You have uncommitted changes. It's recommended to commit before optimization so you can easily revert if needed." - - Ask: "Do you want to proceed anyway, or commit first?" - - Halt and wait for user response +## Pre-Scan Checks -2. **Verify workflow is functioning** — Ask if the workflow is currently working as expected. Optimization should improve, not break working workflows. +Check for uncommitted changes. In headless mode, note warnings and proceed. In interactive mode, inform the user and confirm before proceeding. In interactive mode, also confirm the workflow is currently functioning. -## Communicate This Guidance to the User +## Optimization Principles -**Workflow skills are both art and science.** The optimization report will contain many suggestions, but use your judgment: +**Workflow skills are both art and science.** The optimization report will contain many suggestions — apply judgment: - Reports may suggest leaner phrasing — but if the current phrasing captures the right guidance, keep it - Reports may say content is "unnecessary" — but if it adds clarity, it may be worth keeping diff --git a/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md b/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md index 3ccd0f0..a840396 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md +++ b/src/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md @@ -93,11 +93,11 @@ Every skill makes assumptions. Surface the ones that are most likely to be wrong | **Single-session completion** | Does the skill assume the workflow completes in one session? | | **Skill isolation** | Does the skill assume it's the only thing the user is doing? | -### 5. Autonomous Potential +### 5. Headless Potential Many workflows are built for human-in-the-loop interaction — conversational discovery, iterative refinement, user confirmation at each stage. But what if someone passed in a headless flag and a detailed prompt? Could this workflow just... do its job, create the artifact, and return the file path? -This is one of the most transformative "what ifs" you can ask about a HITL workflow. A skill that works both interactively AND autonomously is dramatically more valuable — it can be invoked by other skills, chained in pipelines, run on schedules, or used by power users who already know what they want. +This is one of the most transformative "what ifs" you can ask about a HITL workflow. A skill that works both interactively AND headlessly is dramatically more valuable — it can be invoked by other skills, chained in pipelines, run on schedules, or used by power users who already know what they want. **For each HITL interaction point, ask:** @@ -108,14 +108,14 @@ This is one of the most transformative "what ifs" you can ask about a HITL workf | Is this clarification always needed, or only for ambiguous input? | "Did you mean X or Y?" → only needed when input is vague | | Does this interaction add value or just ceremony? | Some confirmations exist because the builder assumed interactivity, not because they're necessary | -**Assess the skill's autonomous potential:** +**Assess the skill's headless potential:** | Level | What It Means | |-------|--------------| -| **Headless-ready** | Could work autonomously today with minimal changes — just needs a flag to skip confirmations | +| **Headless-ready** | Could work headlessly today with minimal changes — just needs a flag to skip confirmations | | **Easily adaptable** | Most interaction points could accept pre-supplied parameters; needs a headless path added to 2-3 stages | -| **Partially adaptable** | Core artifact creation could be autonomous, but discovery/interview stages are fundamentally interactive — suggest a "skip to build" entry point | -| **Fundamentally interactive** | The value IS the conversation (coaching, brainstorming, exploration) — autonomous mode wouldn't make sense, and that's OK | +| **Partially adaptable** | Core artifact creation could be headless, but discovery/interview stages are fundamentally interactive — suggest a "skip to build" entry point | +| **Fundamentally interactive** | The value IS the conversation (coaching, brainstorming, exploration) — headless mode wouldn't make sense, and that's OK | **When the skill IS adaptable, suggest the output contract:** - What would a headless invocation return? (file path, JSON summary, status code) @@ -213,7 +213,7 @@ Use EXACTLY these field names: `file`, `line`, `severity`, `category`, `title`, "needs_input": 0, "suggested_output_contract": "What a headless invocation would return", "required_inputs": ["parameters needed upfront for headless mode"], - "notes": "Brief assessment of autonomous viability" + "notes": "Brief assessment of headless viability" }, "top_insights": [ { @@ -235,39 +235,8 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md, all prompt files, and resource files — in a single parallel batch -2. Deeply understand purpose, audience, and intent from SKILL.md -3. Walk through each stage mentally as a user -4. Inhabit each user archetype (including the automator) and mentally simulate their journey through the skill -5. Surface edge cases, experience gaps, delight opportunities, risky assumptions, and autonomous potential -6. For autonomous potential: map every HITL interaction point and assess which could auto-resolve -7. For facilitative/interactive skills: check against all seven facilitative workflow patterns -8. Go wild with ideas, then temper each to a concrete suggestion -9. Prioritize by user impact -10. Write JSON to `{quality-report-dir}/enhancement-opportunities-temp.json` -11. Return only the filename: `enhancement-opportunities-temp.json` +Read all skill files. Analyze through each creative lens above. Write JSON to `{quality-report-dir}/enhancement-opportunities-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, challenge your own findings:** - -### Creative Quality Check -- Did I actually *inhabit* different user archetypes (including the automator), or did I just analyze from the builder's perspective? -- Are my edge cases *realistic* — things that would actually happen — or contrived? -- Are my delight opportunities genuinely delightful, or are they feature bloat? -- Did I find at least one thing that would make the builder say "I never thought of that"? -- Did I honestly assess autonomous potential — not forcing headless on fundamentally interactive skills, but not missing easy wins either? -- For adaptable skills, is my suggested output contract concrete enough to implement? - -### Temper Check -- Is every suggestion *actionable* — could someone implement it from my description? -- Did I drop the impractical wild ideas instead of padding my findings? -- Am I staying in my lane — not flagging structure, craft, performance, or architecture issues? -- Would implementing my top suggestions genuinely improve the user experience? - -### Honesty Check -- Did I note what the skill already does well? (Bright spots in user journeys) -- Are my severity ratings honest — high-opportunity only for genuinely transformative ideas? -- Is my `boldest_idea` actually bold, or is it safe and obvious? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify findings are realistic, actionable, and honest about what the skill already does well. diff --git a/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md b/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md index 4ae1c53..a4a1762 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md +++ b/src/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md @@ -279,43 +279,8 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md and all prompt files at skill root — in a single parallel batch -2. Check On Activation and operation flow patterns from SKILL.md -3. Check each prompt file for execution patterns -4. Check resource loading patterns in references/ (read as needed) -5. Identify sequential operations that could be parallel -6. Check for parent-reading-before-delegating patterns -7. Verify subagent instructions have output specifications -8. Evaluate stage ordering for optimization opportunities -9. Check dependency graph for over-constraining, circular, or redundant dependencies -10. Verify independent tool calls are batched -11. Write JSON to `{quality-report-dir}/execution-efficiency-temp.json` -12. Return only the filename: `execution-efficiency-temp.json` +Read pre-pass JSON and all prompt files. Evaluate against all checks in Parts 1-3 above. Write JSON to `{quality-report-dir}/execution-efficiency-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, think one level deeper and verify completeness and quality:** - -### Scan Completeness -- Did I read SKILL.md and EVERY prompt file? -- Did I identify ALL sequential independent operations? -- Did I check for parent-reading-then-delegating patterns? -- Did I verify subagent output specifications? -- Did I evaluate stage ordering and dependency graph? -- Did I check resource loading patterns? - -### Finding Quality -- Are "sequential-independent" findings truly independent (not dependent)? -- Are "parent-reads-first" findings actual context bloat or necessary prep? -- Are batching opportunities actually batchable (same operation, different targets)? -- Are stage-ordering suggestions actually better or just different? -- Are dependency-bloat findings truly unnecessary constraints? -- Are estimated savings realistic? -- Did I distinguish between necessary delegation and over-delegation? - -### Cohesion Review -- Do findings identify the biggest execution bottlenecks? -- Would implementing suggestions result in significant efficiency gains? -- Are efficient_alternatives actually better or just different? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify findings target genuine inefficiencies with measurable impact. diff --git a/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md b/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md index 9077510..b066e94 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md +++ b/src/skills/bmad-workflow-builder/quality-scan-prompt-craft.md @@ -286,43 +286,8 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md, all prompt files at skill root, and list references/ contents — in a single parallel batch -2. Assess skill type from SKILL.md, evaluate Overview quality and progressive disclosure -3. Check references/ to verify progressive disclosure is working (detail is where it belongs) -4. For SKILL.md: evaluate Overview quality (present? appropriate? excessive? disconnected? **missing?**) -5. For SKILL.md: check for over-optimization — is this a complex/interactive skill stripped to a bare skeleton? -6. For SKILL.md: check size and progressive disclosure — does it exceed guidelines? Are data tables, schemas, or reference material inline that should be in references/? -7. For multi-branch SKILL.md: does each branch section have brief context explaining what it handles and why? -8. For each stage prompt: check config header, progression conditions, self-containment -9. For each stage prompt: check context sufficiency — do judgment-heavy prompts have enough context to make good decisions? -10. For all files: scan for genuine token waste (repetition, defensive padding, meta-explanation) -11. For all files: evaluate outcome vs implementation balance given the skill type -12. For all files: check intelligence placement (judgment in prompts, determinism in scripts) -13. Write JSON to `{quality-report-dir}/prompt-craft-temp.json` -14. Return only the filename: `prompt-craft-temp.json` +Read pre-pass JSON and all prompt files. Evaluate using the criteria in Parts 1-3 above. Write JSON to `{quality-report-dir}/prompt-craft-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, think one level deeper and verify completeness and quality:** - -### Scan Completeness -- Did I read SKILL.md and EVERY prompt file? -- Did I assess the skill type to calibrate my expectations? -- Did I evaluate SKILL.md Overview quality separately from stage prompt efficiency? -- Did I check progression conditions and self-containment for every stage prompt? - -### Finding Quality — The Nuance Check -- For each "token-waste" finding: Is this genuinely wasteful, or does it enable informed autonomy? -- For each "anti-pattern" finding: Is this truly an anti-pattern in context, or a legitimate craft choice? -- For each "outcome-balance" finding: Does this skill type warrant procedural detail, or is it over-specified? -- Did I include the `nuance` field for findings that could be intentional? -- Am I flagging Overview content as waste? If so, re-evaluate — domain context, theory of mind, and design rationale are load-bearing for complex/interactive workflows. -- Did I check for under-contextualization? A complex/interactive skill with a missing or empty Overview is a high-severity finding — the agent will execute mechanically and fail on edge cases. -- Did I check for inline data (tables, schemas, reference material) that should be in references/ or assets/? - -### Calibration Check -- Would implementing ALL my suggestions produce a better skill, or would some strip valuable context? -- Is my craft_assessment fair given the skill type? -- Does top_improvement represent the highest-impact change? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify all files were read, token-waste findings are genuine (not load-bearing context), and suggestions would improve the skill holistically. diff --git a/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md b/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md index d49a05b..0fed01f 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md +++ b/src/skills/bmad-workflow-builder/quality-scan-script-opportunities.md @@ -223,37 +223,8 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** List `scripts/` directory, read SKILL.md, all prompt files, and resource files — in a single parallel batch -2. Inventory existing scripts (avoid suggesting duplicates) -3. Check On Activation and inline operations for deterministic work -4. For each prompt instruction, apply the determinism test -5. Check if any resource content could be generated/validated by scripts -6. For each finding: estimate LLM tax, assess implementation complexity, check pre-pass potential -7. For each finding: consider the --help pattern — if a prompt currently inlines a script's interface, note the additional savings -8. Write JSON to `{quality-report-dir}/script-opportunities-temp.json` -9. Return only the filename: `script-opportunities-temp.json` +Read all skill files and the scripts/ directory. Apply the determinism test and category analysis described above. Write JSON to `{quality-report-dir}/script-opportunities-temp.json`. Return only the filename. ## Critical After Draft Output -Before finalizing, verify: - -### Determinism Accuracy -- For each finding: Is this TRULY deterministic, or does it require judgment I'm underestimating? -- Am I confusing "structured output" with "deterministic"? (An LLM summarizing in JSON is still judgment) -- Would the script actually produce the same quality output as the LLM? - -### Creativity Check -- Did I look beyond obvious validation? (Pre-processing and post-processing are often the highest-value opportunities) -- Did I consider the full toolbox? (Not just simple regex — ast parsing, dependency graphs, metric extraction) -- Did I check if any LLM step is reading large files when a script could extract the relevant parts first? - -### Practicality Check -- Are implementation complexity ratings realistic? -- Are token savings estimates reasonable? -- Would implementing the top findings meaningfully improve the skill's efficiency? -- Did I check for existing scripts to avoid duplicates? - -### Lane Check -- Am I staying in my lane? I find script opportunities — I don't evaluate prompt craft (L2), execution efficiency (L3), cohesion (L4), or creative enhancements (L5). - -Only after verification, write final JSON and return filename. +Before finalizing, verify flagged operations are truly deterministic, existing scripts aren't duplicated, and you stayed in your lane. diff --git a/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md b/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md index 01fef88..3c51ac9 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md +++ b/src/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md @@ -130,33 +130,6 @@ Find and read: **Note:** If external skills aren't available, infer their purpose from name and usage context. -## Analysis Process - -1. **Build mental model** of the skill: - - What is this skill FOR? (purpose, outcomes) - - What does it ACTUALLY do? (enumerate all stages) - - What does it PRODUCE? (output-location, final outputs) - -2. **Evaluate flow coherence**: - - Do stages flow logically? - - Are handoffs between stages clean? - - Is the dependency graph sound? - -3. **Gap analysis**: - - For each declared purpose, ask "can this skill actually achieve that?" - - For each key workflow, check if all steps are covered - - Consider adjacent stages that should exist - -4. **Redundancy check**: - - Group similar stages - - Identify overlaps - - Note consolidation opportunities - -5. **Creative synthesis**: - - What would make this skill MORE useful? - - What's the ONE thing missing that would have biggest impact? - - What's the ONE thing to remove that would clarify focus? - ## Output Format You will receive `{skill-path}` and `{quality-report-dir}` as inputs. @@ -299,40 +272,11 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md, all prompt files, and list resources/ — in a single parallel batch -2. Build mental model of the skill as a whole from all files read -3. Evaluate cohesion across all dimensions (flow, purpose, complexity, completeness, redundancy, dependencies, creates alignment, external integration, journey) -4. Generate findings with specific, actionable suggestions -5. Identify strengths (positive feedback is valuable!) -6. Write JSON to `{quality-report-dir}/skill-cohesion-temp.json` -7. Return only the filename: `skill-cohesion-temp.json` +Read all skill files. Build a mental model of the skill as a whole, then evaluate against all cohesion dimensions above. Write JSON to `{quality-report-dir}/skill-cohesion-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, think one level deeper and verify completeness and quality:** - -### Scan Completeness -- Did I read SKILL.md and ALL prompts? -- Did I build a complete mental model of the skill? -- Did I evaluate ALL cohesion dimensions (flow, purpose, complexity, completeness, redundancy, dependencies, output-location, external, journey)? -- Did I check output-location alignment with actual stage outputs? - -### Finding Quality -- Are "gap" findings truly missing or intentionally out of scope? -- Are "redundancy" findings actual overlap or complementary stages? -- Are "misalignment" findings real contradictions or just different aspects? -- Are severity ratings appropriate (high only for glaring omissions)? -- Did I include strengths (positive feedback is valuable)? -- Are dependency graph findings based on actual data flow, not assumptions? - -### Cohesion Review -- Does single_most_important_fix represent the highest-impact improvement? -- Do findings tell a coherent story about this skill's cohesion? -- Would addressing high-severity issues significantly improve the skill? -- Are creative_suggestions actually valuable, not just nice-to-haves? -- Is the complexity assessment fair and well-reasoned? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify completeness across all dimensions and that findings tell a coherent story. ## Key Principle diff --git a/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md b/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md index b731c14..12c843a 100644 --- a/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md +++ b/src/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md @@ -229,43 +229,8 @@ Before writing output, verify: Is your array called `findings`? Does every item ## Process -1. **Parallel read batch:** Read SKILL.md and list all `.md` files at skill root — in a single parallel batch -2. Validate frontmatter, sections, language, template artifacts from SKILL.md -3. Determine workflow type (complex, simple workflow, simple utility) -4. For complex workflows: **parallel read batch** — read all stage prompt files identified in step 1 -5. For complex workflows: cross-reference stage files with SKILL.md references, check progression conditions, config headers, naming -6. For simple workflows: verify inline steps are numbered, clear, and complete -7. For simple utilities: verify input/output format and transformation rules -8. Check headless mode if declared -9. Run logical consistency checks across all files read -10. Write JSON to `{quality-report-dir}/workflow-integrity-temp.json` -11. Return only the filename: `workflow-integrity-temp.json` +Read SKILL.md and all prompt files. For complex workflows, also read all stage prompt files. Evaluate against all checks in Parts 1-3 above. Write JSON to `{quality-report-dir}/workflow-integrity-temp.json`. Return only the filename. ## Critical After Draft Output -**Before finalizing, think one level deeper and verify completeness and quality:** - -### Scan Completeness -- Did I read the entire SKILL.md file? -- Did I correctly identify the workflow type? -- Did I read ALL stage files at skill root (for complex workflows)? -- Did I verify every stage reference in SKILL.md has a corresponding file? -- Did I check progression conditions in every stage prompt? -- Did I check config headers in stage prompts? -- Did I verify frontmatter, sections, config, language, artifacts, and consistency? - -### Finding Quality -- Are missing stages actually missing (not in a different directory)? -- Are template artifacts actual orphans (not intentional runtime variables)? -- Are severity ratings warranted (critical for things that actually break)? -- Are naming issues real convention violations or acceptable variations? -- Are progression condition issues genuine (vague conditions vs. intentionally flexible)? -- Are "invalid-section" findings truly invalid (e.g., On Exit which has no system hook)? - -### Cross-File Consistency -- Do SKILL.md references and actual files agree? -- Does the declared workflow type match the actual structure? -- Does the stage_summary accurately reflect the workflow's state? -- Would fixing critical issues resolve the structural problems? - -Only after this verification, write final JSON and return filename. +Before finalizing, verify findings are complete, severity ratings are honest, and you stayed within structural validation. diff --git a/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md b/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md index 3c88208..bf9e49b 100644 --- a/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md +++ b/src/skills/bmad-workflow-builder/references/complex-workflow-patterns.md @@ -53,14 +53,7 @@ Store config values in memory as `{var_name}` for use in prompts. ### Required Core Variables -**Every workflow MUST load these core variables with fallbacks:** -- `user_name` — fallback: omit -- `communication_language` — fallback: match the user's language -- `output_folder` — fallback: `{project-root}/_bmad-output` - -**Conditionally include:** -- `document_output_language` — fallback: match the user's language — ONLY if workflow creates documents (check capability `output-location` field) -- Output location variable from capability `output-location` — ONLY if specified in metadata +Load core config variables (user preferences, language, output locations) with sensible defaults. If the workflow creates documents, include document output language. **Example for BMB workflow (creates documents, has output var):** ``` @@ -79,12 +72,7 @@ vars: user_name:BMad,communication_language:English,output_folder:{project-root} ### Using Config Values in Prompts -**Every prompt file MUST start with:** -```markdown -Language: {communication_language} -Output Language: {document_output_language} ← ONLY if workflow creates documents -Output Location: {output-variable} ← ONLY if capability output-location is defined -``` +Each prompt file should establish communication language and relevant output settings at the top. **Use throughout prompts:** ```markdown @@ -192,14 +180,8 @@ updated: "{timestamp}" ``` **Stage 2+: Reload context if compacted** -```markdown -## Stage Start: Analysis -1. Read {output_doc_path} -2. Parse YAML front matter for `inputs` list -3. Re-read each input file to restore context -4. Verify status indicates previous stage complete -5. Proceed with analysis, updating document in place -``` + +Each stage after the first should begin by reading the output document to recover context. If compacted, re-read input files listed in the YAML front matter. ```markdown ## Stage 1: Research @@ -294,16 +276,7 @@ Write the polished version back to the same file. ### Compaction Recovery Pattern -If context is compacted mid-workflow: -```markdown -## Recovery Check -1. Read {output_doc_path} -2. Parse YAML front matter: - - Check `status` for current stage - - Read `inputs` list to restore context -3. Re-read all input files from `inputs` -4. Resume from next stage based on status -``` +Each stage after the first should begin by reading the output document to recover context. If compacted, re-read input files listed in the YAML front matter. ### When NOT to Use This Pattern @@ -403,7 +376,7 @@ Before finalizing a BMad module workflow, verify: - [ ] **Document-as-cache**: Output doc has YAML front matter with `status` and `inputs` for recovery? - [ ] **Input tracking**: Does front matter list relative paths to all input files used? - [ ] **Final polish**: Does workflow include a subagent polish step at the end? -- [ ] **Progressive disclosure**: Are stages in prompt files at root with clear progression conditions? +- [ ] **Progressive disclosure**: Are stages in `./references/` with clear progression conditions? - [ ] **Metadata complete**: All bmad-* fields present and accurate? - [ ] **Recovery pattern**: Can the workflow resume by reading the output doc front matter? diff --git a/src/skills/bmad-workflow-builder/references/quality-dimensions.md b/src/skills/bmad-workflow-builder/references/quality-dimensions.md index df6f6d8..0cdc80e 100644 --- a/src/skills/bmad-workflow-builder/references/quality-dimensions.md +++ b/src/skills/bmad-workflow-builder/references/quality-dimensions.md @@ -22,9 +22,9 @@ Scripts handle plumbing (fetch, transform, validate). Prompts handle judgment (i SKILL.md stays focused. Detail goes where it belongs. -- Stage instructions → prompt files at skill root -- Reference data, schemas, large tables → `references/` -- Templates, config files → `assets/` +- Stage instructions → `./references/` +- Reference data, schemas, large tables → `./references/` +- Templates, config files → `./assets/` - Multi-branch SKILL.md under ~250 lines: fine as-is - Single-purpose up to ~500 lines: acceptable if focused @@ -32,13 +32,13 @@ SKILL.md stays focused. Detail goes where it belongs. Two parts: `[5-8 word summary]. [Use when user says 'X' or 'Y'.]` -Default to conservative triggering. See `references/standard-fields.md` for full format and examples. +Default to conservative triggering. See `./references/standard-fields.md` for full format and examples. ## 5. Path Construction Only use `{project-root}` for `_bmad` paths. Config variables used directly — they already contain `{project-root}`. -See `references/standard-fields.md` for correct/incorrect patterns. +See `./references/standard-fields.md` for correct/incorrect patterns. ## 6. Token Efficiency diff --git a/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md b/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md index 25d6c7a..658238b 100644 --- a/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md +++ b/src/skills/bmad-workflow-builder/references/script-opportunities-reference.md @@ -98,7 +98,6 @@ Each entry follows the format: What it does, Why it matters for workflows, What **Checks:** - `name` exists and is kebab-case - `description` exists and follows "Use when..." pattern -- `argument-hint` is present if the skill accepts arguments - No forbidden fields or reserved prefixes - Optional fields have valid values if present diff --git a/src/skills/bmad-workflow-builder/references/skill-best-practices.md b/src/skills/bmad-workflow-builder/references/skill-best-practices.md index 8e341c1..7b11fa6 100644 --- a/src/skills/bmad-workflow-builder/references/skill-best-practices.md +++ b/src/skills/bmad-workflow-builder/references/skill-best-practices.md @@ -1,218 +1,109 @@ # Skill Authoring Best Practices -Practical patterns for writing effective BMad skills. For field definitions and description format, see `references/standard-fields.md`. For quality dimensions, see `references/quality-dimensions.md`. - -## Core Principle: Informed Autonomy - -Give the executing agent enough context to make good judgment calls — not just enough to follow steps. The right test for every piece of content is: "Would the agent make *better decisions* with this context?" If yes, keep it. If it's genuinely redundant or mechanical, cut it. - -## Freedom Levels - -Match specificity to task fragility: - -| Freedom | When to Use | Example | -|---------|-------------|---------| -| **High** (text instructions) | Multiple valid approaches, context-dependent | "Analyze structure, check for issues, suggest improvements" | -| **Medium** (pseudocode/templates) | Preferred pattern exists, some variation OK | `def generate_report(data, format="markdown"):` | -| **Low** (exact scripts) | Fragile operations, consistency critical | `python scripts/migrate.py --verify --backup` (do not modify) | - -**Analogy**: Narrow bridge with cliffs = low freedom. Open field = high freedom. - -## Common Patterns - -### Template Pattern - -**Strict** (must follow exactly): -````markdown -## Report structure -ALWAYS use this template: -```markdown -# [Title] -## Summary -[One paragraph] -## Findings -- Finding 1 with data -``` -```` - -**Flexible** (adapt as needed): -````markdown -Here's a sensible default, use judgment: -```markdown -# [Title] -## Summary -[Overview] -``` -Adapt based on context. -```` - -### Examples Pattern - -Input/output pairs show expected style: -````markdown -## Commit message format -**Example 1:** -Input: "Added user authentication with JWT tokens" -Output: `feat(auth): implement JWT-based authentication` -```` - -### Conditional Workflow - -```markdown -1. Determine modification type: - **Creating new?** → Creation workflow - **Editing existing?** → Editing workflow -``` +For field definitions and description format, see `./references/standard-fields.md`. For quality dimensions, see `./references/quality-dimensions.md`. -### Soft Gate Elicitation +## Core Philosophy: Outcome-Based Authoring -For guided/interactive workflows, use "anything else?" soft gates at natural transition points instead of hard menus. This pattern draws out information users didn't know they had: +Skills should describe **what to achieve**, not **how to achieve it**. The LLM is capable of figuring out the approach — it needs to know the goal, the constraints, and the why. -```markdown -## After completing a discovery section: -Present what you've captured so far, then: -"Anything else you'd like to add, or shall we move on?" -``` +**The test for every instruction:** Would removing this cause the LLM to produce a worse outcome? If the LLM would do it anyway — or if it's just spelling out mechanical steps — cut it. -**Why it works:** Users almost always remember one more thing when given a graceful exit ramp rather than a hard stop. The low-pressure phrasing invites contribution without demanding it. This consistently produces richer, more complete artifacts than rigid section-by-section questioning. +### Outcome vs Prescriptive -**When to use:** Any guided workflow with collaborative discovery — product briefs, requirements gathering, design reviews, brainstorming synthesis. Use at every natural transition between topics or sections. +| Prescriptive (avoid) | Outcome-based (prefer) | +|---|---| +| "Step 1: Ask about goals. Step 2: Ask about constraints. Step 3: Summarize and confirm." | "Ensure the user's vision is fully captured — goals, constraints, and edge cases — before proceeding." | +| "Load config. Read user_name. Read communication_language. Greet the user by name in their language." | "Load available config and greet the user appropriately." | +| "Create a file. Write the header. Write section 1. Write section 2. Save." | "Produce a report covering X, Y, and Z." | -**When NOT to use:** Autonomous/headless execution, or steps where additional input would cause scope creep rather than enrich the output. +The prescriptive versions miss requirements the author didn't think of. The outcome-based versions let the LLM adapt to the actual situation. -### Intent-Before-Ingestion +### Why This Works -Never scan artifacts, documents, or project context until you understand WHY the user is here. Scanning without purpose produces noise, not signal. +- **Why over what** — When you explain why something matters, the LLM adapts to novel situations. When you just say what to do, it follows blindly even when it shouldn't. +- **Context enables judgment** — Give domain knowledge, constraints, and goals. The LLM figures out the approach. It's better at adapting to messy reality than any script you could write. +- **Prescriptive steps create brittleness** — When reality doesn't match the script, the LLM either follows the wrong script or gets confused. Outcomes let it adapt. +- **Every instruction should carry its weight** — If the LLM would do it anyway, the instruction is noise. If the LLM wouldn't know to do it without being told, that's signal. -```markdown -## On activation: -1. Greet and understand intent — what is this about? -2. Accept whatever inputs the user offers -3. Ask if they have additional documents or context -4. ONLY THEN scan artifacts, scoped to relevance -``` +### When Prescriptive Is Right -**Why it works:** Without knowing what the user wants, you can't judge what's relevant in a 100-page research doc vs a brainstorming report. Intent gives you the filter. Without it, scanning is a fool's errand. +Reserve exact steps for **fragile operations** where getting it wrong has consequences — script invocations, exact file paths, specific CLI commands, API calls with precise parameters. These need low freedom because there's one right way to do them. -**When to use:** Any workflow that ingests documents, project context, or external data as part of its process. +| Freedom | When | Example | +|---------|------|---------| +| **High** (outcomes) | Multiple valid approaches, LLM judgment adds value | "Ensure the user's requirements are complete" | +| **Medium** (guided) | Preferred approach exists, some variation OK | "Present findings in a structured report with an executive summary" | +| **Low** (exact) | Fragile, one right way, consequences for deviation | `python3 scripts/scan-path-standards.py {skill-path}` | -### Capture-Don't-Interrupt +## Patterns -When users provide information beyond the current scope (e.g., dropping requirements during a product brief, mentioning platforms during vision discovery), capture it silently for later use rather than redirecting or stopping them. +These are patterns that naturally emerge from outcome-based thinking. Apply them when they fit — they're not a checklist. -```markdown -## During discovery: -If user provides out-of-scope but valuable info: -- Capture it (notes, structured aside, addendum bucket) -- Don't interrupt their flow -- Use it later in the appropriate stage or output -``` +### Soft Gate Elicitation -**Why it works:** Users in creative flow will share their best insights unprompted. Interrupting to say "we'll cover that later" kills momentum and may lose the insight entirely. Capture everything, distill later. +At natural transitions, invite contribution without demanding it: "Anything else, or shall we move on?" Users almost always remember one more thing when given a graceful exit ramp. This produces richer artifacts than rigid section-by-section questioning. -**When to use:** Any collaborative discovery workflow where the user is brainstorming, explaining, or brain-dumping. +### Intent-Before-Ingestion -### Dual-Output: Human Artifact + LLM Distillate +Understand why the user is here before scanning documents or project context. Intent gives you the relevance filter — without it, scanning is noise. -Any artifact-producing workflow can output two complementary documents: a polished human-facing artifact AND a token-conscious, structured distillate optimized for downstream LLM consumption. +### Capture-Don't-Interrupt -```markdown -## Output strategy: -1. Primary: Human-facing document (exec summary, report, brief) -2. Optional: LLM distillate — dense, structured, token-efficient - - Captures overflow that doesn't belong in the human doc - - Rejected ideas (so downstream doesn't re-propose them) - - Detail bullets with just enough context to stand alone - - Designed to be loaded as context for the next workflow -``` +When users provide information beyond the current scope, capture it for later rather than redirecting. Users in creative flow share their best insights unprompted — interrupting loses them. -**Why it works:** Human docs are concise by design — they can't carry all the detail surfaced during discovery. But that detail has value for downstream LLM workflows (PRD creation, architecture design, etc.). The distillate bridges the gap without bloating the primary artifact. +### Dual-Output: Human Artifact + LLM Distillate -**When to use:** Any workflow producing documents that feed into subsequent LLM workflows. The distillate is always optional — offered to the user, not forced. +Artifact-producing skills can output both a polished human-facing document and a token-efficient distillate for downstream LLM consumption. The distillate captures overflow, rejected ideas, and detail that doesn't belong in the human doc but has value for the next workflow. Always optional. ### Parallel Review Lenses -Before finalizing any artifact, fan out multiple reviewers with different perspectives to catch blind spots the builder/facilitator missed. - -```markdown -## Near completion: -Fan out 2-3 review subagents in parallel: -- Skeptic: "What's missing? What assumptions are untested?" -- Opportunity Spotter: "What adjacent value? What angles?" -- Contextual Reviewer: LLM picks the best third lens - (e.g., "regulatory risk" for healthtech, "DX critic" for devtools) +Before finalizing significant artifacts, fan out reviewers with different perspectives — skeptic, opportunity spotter, domain-specific lens. If subagents aren't available, do a single critical self-review pass. Multiple perspectives catch blind spots no single reviewer would. -Graceful degradation: If subagents unavailable, -main agent does a single critical self-review pass. -``` +### Three-Mode Architecture (Guided / Yolo / Headless) -**Why it works:** A single perspective — even an expert one — has blind spots. Multiple lenses surface issues and opportunities that no single reviewer would catch. The contextually-chosen third lens ensures domain-specific concerns aren't missed. +Consider whether the skill benefits from multiple execution modes: -**When to use:** Any workflow producing a significant artifact (briefs, PRDs, designs, architecture docs). The review step is lightweight but high-value. +| Mode | When | Behavior | +|------|------|----------| +| **Guided** | Default | Conversational discovery with soft gates | +| **Yolo** | "just draft it" | Ingest everything, draft complete artifact, then refine | +| **Headless** | `--headless` / `-H` | Complete the task without user input, using sensible defaults | -### Three-Mode Architecture (Guided / Yolo / Autonomous) - -For interactive workflows, offer three execution modes that match different user contexts: - -| Mode | Trigger | Behavior | -|------|---------|----------| -| **Guided** | Default | Section-by-section with soft gates. Drafts from what it knows, questions what it doesn't. | -| **Yolo** | `--yolo` or "just draft it" | Ingests everything, drafts complete artifact upfront, then walks user through refinement. | -| **Headless** | `--headless` or `-H` | Headless mode. Takes inputs, produces artifact, no interaction. | - -**Why it works:** Not every user wants the same experience. A first-timer needs guided discovery. A repeat user with clear inputs wants yolo. A pipeline wants autonomous. Same workflow, three entry points. - -**When to use:** Any facilitative workflow that produces an artifact. Not all workflows need all three — but considering them during design prevents painting yourself into a single interaction model. +Not all skills need all three. But considering them during design prevents locking into a single interaction model. ### Graceful Degradation -Every subagent-dependent feature should have a fallback path. If the platform doesn't support parallel subagents (or subagents at all), the workflow must still progress. - -```markdown -## Subagent-dependent step: -Try: Fan out subagents in parallel -Fallback: Main agent performs the work sequentially -Never: Block the workflow because a subagent feature is unavailable -``` - -**Why it works:** Skills run across different platforms, models, and configurations. A skill that hard-fails without subagents is fragile. A skill that gracefully falls back to sequential processing is robust everywhere. - -**When to use:** Any workflow that uses subagents for research, review, or parallel processing. +Every subagent-dependent feature should have a fallback path. A skill that hard-fails without subagents is fragile — one that falls back to sequential processing works everywhere. ### Verifiable Intermediate Outputs -For complex tasks: plan → validate → execute → verify - -1. Analyze inputs -2. **Create** `changes.json` with planned updates -3. **Validate** with script before executing -4. Execute changes -5. Verify output - -Benefits: catches errors early, machine-verifiable, reversible planning. +For complex tasks with consequences: plan → validate → execute → verify. Create a verifiable plan before executing, validate with scripts where possible. Catches errors early and makes the work reversible. ## Writing Guidelines -- **Consistent terminology** — choose one term per concept, stick to it +- **Consistent terminology** — one term per concept, stick to it - **Third person** in descriptions — "Processes files" not "I help process files" - **Descriptive file names** — `form_validation_rules.md` not `doc2.md` - **Forward slashes** in all paths — cross-platform -- **One level deep** for reference files — SKILL.md → reference.md, never SKILL.md → A.md → B.md -- **TOC for long files** — add table of contents for files >100 lines +- **One level deep** for reference files — SKILL.md → reference.md, never chains +- **TOC for long files** — >100 lines ## Anti-Patterns | Anti-Pattern | Fix | |---|---| -| Too many options upfront | One default with escape hatch for edge cases | +| Numbered steps for things the LLM would figure out | Describe the outcome and why it matters | +| Explaining how to load config (the mechanic) | List the config keys and their defaults (the outcome) | +| Prescribing exact greeting/menu format | "Greet the user and present capabilities" | +| Spelling out headless mode in detail | "If headless, complete without user input" | +| Too many options upfront | One default with escape hatch | | Deep reference nesting (A→B→C) | Keep references 1 level from SKILL.md | | Inconsistent terminology | Choose one term per concept | -| Vague file names | Name by content, not sequence | | Scripts that classify meaning via regex | Intelligence belongs in prompts, not scripts | ## Scripts in Skills -- **Execute vs reference** — "Run `analyze.py` to extract fields" (execute) vs "See `analyze.py` for the algorithm" (read) +- **Execute vs reference** — "Run `analyze.py`" (execute) vs "See `analyze.py` for the algorithm" (read) - **Document constants** — explain why `TIMEOUT = 30`, not just what -- **PEP 723 for Python** — self-contained scripts with inline dependency declarations +- **PEP 723 for Python** — self-contained with inline dependency declarations - **MCP tools** — use fully qualified names: `ServerName:tool_name` diff --git a/src/skills/bmad-workflow-builder/references/standard-fields.md b/src/skills/bmad-workflow-builder/references/standard-fields.md index 2fd2556..91d7737 100644 --- a/src/skills/bmad-workflow-builder/references/standard-fields.md +++ b/src/skills/bmad-workflow-builder/references/standard-fields.md @@ -1,12 +1,20 @@ # Standard Workflow/Skill Fields -## Common Fields (All Types) +## Frontmatter Fields + +Only these fields go in the YAML frontmatter block: + +| Field | Description | Example | +|-------|-------------|---------| +| `name` | Full skill name (kebab-case, same as folder name) | `bmad-workflow-builder`, `bmad-validate-json` | +| `description` | [5-8 word summary]. [Use when user says 'X' or 'Y'.] | See Description Format below | + +## Content Fields (All Types) + +These are used within the SKILL.md body — never in frontmatter: | Field | Description | Example | |-------|-------------|---------| -| `name` | Full skill name (kebab-case) | `bmad-workflow-builder`, `bmad-validate-json` | -| `skillName` | Functional name (kebab-case) | `workflow-builder`, `validate-json` | -| `description` | [5-8 word summary]. [Use when user says 'X' or 'Y'.] | "Builds workflows through conversational discovery. Use when the user requests to 'build a workflow' or 'modify a workflow'." | | `role-guidance` | Brief expertise primer | "Act as a senior DevOps engineer" | | `module-code` | Module code (if module-based) | `bmb`, `cis` | @@ -98,18 +106,18 @@ This provides quick prompt priming for expertise and tone. Workflows may also us ## Path Rules -Only use `{project-root}` for `_bmad` paths. - ### Skill-Internal Files -Use bare relative paths (no prefix): -- `references/reference.md` -- `01-discover.md` -- `scripts/validate.py` + +All references to files within the skill use `./` prefix: +- `./references/reference.md` +- `./references/discover.md` +- `./scripts/validate.py` + +This distinguishes skill-internal files from `{project-root}` paths — without the `./` prefix the LLM may confuse them. ### Project `_bmad` Paths Use `{project-root}/_bmad/...`: - `{project-root}/_bmad/planning/prd.md` -- `{project-root}/_bmad/memory/{skillName}-sidecar/` ### Config Variables Use directly — they already contain `{project-root}` in their resolved values: diff --git a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md index 4a87389..4ba8d6d 100644 --- a/src/skills/bmad-workflow-builder/references/template-substitution-rules.md +++ b/src/skills/bmad-workflow-builder/references/template-substitution-rules.md @@ -26,7 +26,7 @@ The builder determines the rest of the skill structure — body sections, phases ## Path References -All generated skills use these paths when applicable: -- `references/{reference}.md` — Reference documents loaded on demand -- `01-{stage}.md` — Numbered stage prompts at skill root (complex workflows) -- `scripts/` — Python/shell scripts for deterministic operations +All generated skills use `./` prefix for skill-internal paths: +- `./references/{reference}.md` — Reference documents loaded on demand +- `./references/{stage}.md` — Stage prompts (complex workflows) +- `./scripts/` — Python/shell scripts for deterministic operations diff --git a/src/skills/bmad-workflow-builder/report-quality-scan-creator.md b/src/skills/bmad-workflow-builder/report-quality-scan-creator.md index 56d53a1..516abce 100644 --- a/src/skills/bmad-workflow-builder/report-quality-scan-creator.md +++ b/src/skills/bmad-workflow-builder/report-quality-scan-creator.md @@ -16,17 +16,7 @@ Read `assets/quality-report-template.md` for the report structure. The template ## Process -### Step 1: Ingest Everything - -1. Read `assets/quality-report-template.md` -2. List ALL files in `{quality-report-dir}` — both `*-temp.json` (scanner findings) and `*-prepass.json` (structural metrics) -3. Read EVERY JSON file - -### Step 2: Extract All Data Types - -All scanners now use the universal schema defined in `references/universal-scan-schema.md`. Scanner-specific data lives in `assessments{}`, not as top-level keys. - -For each scanner file, extract not just `findings` arrays but ALL of these data types: +Read `assets/quality-report-template.md` and every JSON file in `{quality-report-dir}` (both `*-temp.json` scanner findings and `*-prepass.json` structural metrics). All scanners use the universal schema defined in `references/universal-scan-schema.md`. Extract all data types from each scanner file: | Data Type | Where It Lives | Report Destination | |-----------|---------------|-------------------| @@ -44,44 +34,13 @@ For each scanner file, extract not just `findings` arrays but ALL of these data | Stage summary | workflow-integrity `assessments.stage_summary` | Structural section header | | Prepass metrics | `*-prepass.json` files | Context data points where useful | -### Step 3: Populate Template - -Fill the template section by section, following the `` guidance in each. Key rules: - -- **Conditional sections:** Only include `{if-...}` blocks when the data exists. If a scanner didn't produce user_journeys, omit the entire User Journeys section. -- **Empty severity levels:** Within a category, omit severity sub-headers that have zero findings (don't write "**Critical Issues** — None"). -- **Strip comments:** Remove all `` blocks from final output. - -### Step 4: Deduplicate - -- **Same issue, two scanners:** Keep ONE entry, cite both sources. Use the more detailed description. -- **Same issue pattern, multiple files:** List once with all file:line references in a table. -- **Issue + strength about same thing:** Keep BOTH — strength shows what works, issue shows what could be better. -- **Overlapping creative suggestions:** Merge into the richer description. -- **Routing:** "note"/"strength" severity → Strengths section. "suggestion" severity → Creative subsection. Do not mix these into issue lists. - -### Step 5: Verification Pass - -**This step is mandatory.** After populating the report, re-read every temp file and verify against this checklist: +Populate the template section by section, following the `` guidance in each. Only include `{if-...}` blocks when data exists. Omit empty severity sub-headers. Strip all `` blocks from final output. -- [ ] Every finding from every `*-temp.json` findings[] array -- [ ] All findings with severity="strength" from any scanner -- [ ] All positive notes from prompt-craft (severity="note") -- [ ] Cohesion analysis dimensional scores table (if present) -- [ ] Craft assessment and skill assessment summaries -- [ ] ALL user journeys with ALL friction_points and bright_spots per archetype -- [ ] The autonomous_assessment block (all fields) -- [ ] All findings with severity="suggestion" from cohesion scanners -- [ ] All findings with severity ending in "-opportunity" from execution-efficiency -- [ ] assessments.top_insights from enhancement-opportunities -- [ ] Script inventory and token savings from script-opportunities -- [ ] Skill understanding (purpose, primary_user, key_assumptions) -- [ ] Stage summary from workflow-integrity (if stages exist) -- [ ] Prompt health summary from prompt-craft (if prompts exist) +Deduplicate: same issue from two scanners becomes one entry citing both sources. Same pattern across multiple files becomes one entry with all file:line references. Keep both strengths and issues about the same thing. Route "note"/"strength" severity to Strengths section, "suggestion" severity to Creative subsection. -If any item was dropped, add it to the appropriate section before writing. +Re-read all temp files and verify every finding appears in the report. -### Step 6: Write and Return +### Write and Return Write report to: `{quality-report-dir}/quality-report.md` diff --git a/src/skills/bmad-workflow-builder/scripts/scan-path-standards.py b/src/skills/bmad-workflow-builder/scripts/scan-path-standards.py index 88497c3..d0ed675 100755 --- a/src/skills/bmad-workflow-builder/scripts/scan-path-standards.py +++ b/src/skills/bmad-workflow-builder/scripts/scan-path-standards.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 """Deterministic path standards scanner for BMad skills. -Validates all .md files against BMad path conventions: +Validates all .md and .json files against BMad path conventions: 1. {project-root} only valid before /_bmad 2. Bare _bmad references must have {project-root} prefix 3. Config variables used directly (no double-prefix) -4. No ./ or ../ relative prefixes -5. No absolute paths +4. Skill-internal paths must use ./ prefix (references/, scripts/, assets/) +5. No ../ parent directory references +6. No absolute paths +7. Frontmatter allows only name and description +8. No .md files at skill root except SKILL.md """ # /// script @@ -32,13 +35,18 @@ # Absolute paths ABSOLUTE_PATH_RE = re.compile(r'(?:^|[\s"`\'(])(/(?:Users|home|opt|var|tmp|etc|usr)/\S+)', re.MULTILINE) HOME_PATH_RE = re.compile(r'(?:^|[\s"`\'(])(~/\S+)', re.MULTILINE) -# Relative prefixes +# Parent directory reference (still invalid) RELATIVE_DOT_RE = re.compile(r'(?:^|[\s"`\'(])(\.\./\S+)', re.MULTILINE) -RELATIVE_DOTSLASH_RE = re.compile(r'(?:^|[\s"`\'(])(\./\S+)', re.MULTILINE) +# Bare skill-internal paths without ./ prefix +# Match references/, scripts/, assets/ when NOT preceded by ./ +BARE_INTERNAL_RE = re.compile(r'(?:^|[\s"`\'(])(? bool: """Check if a position is inside a fenced code block.""" @@ -52,6 +60,76 @@ def get_line_number(content: str, pos: int) -> int: return content[:pos].count('\n') + 1 +def check_frontmatter(content: str, filepath: Path) -> list[dict]: + """Validate SKILL.md frontmatter contains only allowed keys.""" + findings = [] + if filepath.name != 'SKILL.md': + return findings + + if not content.startswith('---'): + findings.append({ + 'file': filepath.name, + 'line': 1, + 'severity': 'critical', + 'category': 'frontmatter', + 'title': 'SKILL.md missing frontmatter block', + 'detail': 'SKILL.md must start with --- frontmatter containing name and description', + 'action': 'Add frontmatter with name and description fields', + }) + return findings + + # Find closing --- + end = content.find('\n---', 3) + if end == -1: + findings.append({ + 'file': filepath.name, + 'line': 1, + 'severity': 'critical', + 'category': 'frontmatter', + 'title': 'SKILL.md frontmatter block not closed', + 'detail': 'Missing closing --- for frontmatter', + 'action': 'Add closing --- after frontmatter fields', + }) + return findings + + frontmatter = content[4:end] + for i, line in enumerate(frontmatter.split('\n'), start=2): + line = line.strip() + if not line or line.startswith('#'): + continue + if ':' in line: + key = line.split(':', 1)[0].strip() + if key not in VALID_FRONTMATTER_KEYS: + findings.append({ + 'file': filepath.name, + 'line': i, + 'severity': 'high', + 'category': 'frontmatter', + 'title': f'Invalid frontmatter key: {key}', + 'detail': f'Only {", ".join(sorted(VALID_FRONTMATTER_KEYS))} are allowed in frontmatter', + 'action': f'Remove {key} from frontmatter — use as content field in SKILL.md body instead', + }) + + return findings + + +def check_root_md_files(skill_path: Path) -> list[dict]: + """Check that no .md files exist at skill root except SKILL.md.""" + findings = [] + for md_file in skill_path.glob('*.md'): + if md_file.name != 'SKILL.md': + findings.append({ + 'file': md_file.name, + 'line': 0, + 'severity': 'high', + 'category': 'structure', + 'title': f'Prompt file at skill root: {md_file.name}', + 'detail': 'All progressive disclosure content must be in ./references/ — only SKILL.md belongs at root', + 'action': f'Move {md_file.name} to references/{md_file.name}', + }) + return findings + + def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: """Scan a single file for path standard violations.""" findings = [] @@ -65,10 +143,10 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: 'Absolute path found — not portable across machines'), (HOME_PATH_RE, 'absolute-path', 'high', 'Home directory path (~/) found — environment-specific'), - (RELATIVE_DOT_RE, 'relative-prefix', 'medium', + (RELATIVE_DOT_RE, 'relative-prefix', 'high', 'Parent directory reference (../) found — fragile, breaks with reorganization'), - (RELATIVE_DOTSLASH_RE, 'relative-prefix', 'medium', - 'Relative prefix (./) found — breaks when execution directory changes'), + (BARE_INTERNAL_RE, 'bare-internal-path', 'high', + 'Bare skill-internal path without ./ prefix — use ./references/, ./scripts/, ./assets/ to distinguish from {project-root} paths'), ] for pattern, category, severity, message in checks: @@ -94,9 +172,6 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: pos = match.start() if skip_fenced and is_in_fenced_block(content, pos): continue - # Check that this isn't part of {project-root}/_bmad - # The negative lookbehind handles this, but double-check - # the broader context start = max(0, pos - 30) before = content[start:pos] if '{project-root}/' in before: @@ -117,13 +192,22 @@ def scan_file(filepath: Path, skip_fenced: bool = True) -> list[dict]: def scan_skill(skill_path: Path, skip_fenced: bool = True) -> dict: - """Scan all .md files in a skill directory.""" + """Scan all .md and .json files in a skill directory.""" all_findings = [] - # Find all .md files - md_files = sorted(skill_path.rglob('*.md')) + # Check for .md files at root that aren't SKILL.md + all_findings.extend(check_root_md_files(skill_path)) + + # Check SKILL.md frontmatter + skill_md = skill_path / 'SKILL.md' + if skill_md.exists(): + content = skill_md.read_text(encoding='utf-8') + all_findings.extend(check_frontmatter(content, skill_md)) + + # Find all .md and .json files + md_files = sorted(list(skill_path.rglob('*.md')) + list(skill_path.rglob('*.json'))) if not md_files: - print(f"Warning: No .md files found in {skill_path}", file=sys.stderr) + print(f"Warning: No .md or .json files found in {skill_path}", file=sys.stderr) files_scanned = [] for md_file in md_files: @@ -142,6 +226,9 @@ def scan_skill(skill_path: Path, skip_fenced: bool = True) -> dict: 'double_prefix': 0, 'absolute_path': 0, 'relative_prefix': 0, + 'bare_internal_path': 0, + 'frontmatter': 0, + 'structure': 0, } for f in all_findings: @@ -155,7 +242,7 @@ def scan_skill(skill_path: Path, skip_fenced: bool = True) -> dict: return { 'scanner': 'path-standards', 'script': 'scan-path-standards.py', - 'version': '1.0.0', + 'version': '2.0.0', 'skill_path': str(skill_path), 'timestamp': datetime.now(timezone.utc).isoformat(), 'files_scanned': files_scanned,