feat(skills): workflow customization pilot + resolver and installer fixes
Workflow customization:
- bmad-product-brief adopts the customize.yaml pattern with new standard
keys (activation_steps_prepend, activation_steps_append, skill_end)
that apply to any skill type, not just workflows.
- SKILL.md resolves customization as the first activation step, executes
prepend items immediately, retains append for after greeting, and
re-resolves skill_end after Stage 5 (Finalize).
- Added {skill-root} to the Conventions block.
- Normalized all sub-prompt path references to bare-from-skill-root
(../agents/ -> agents/, sibling filenames -> prompts/<file>).
Metadata:
- Added "DO NOT EDIT -- overwritten on every update." header to all 6
agent customize.yaml files.
Resolver:
- find_project_root now walks from skill_dir first, then falls back to
cwd. Nested-workspace setups where an ancestor of cwd has an unrelated
_bmad/ would previously bind the resolver to the wrong project.
Installer:
- Added 'memory' to the nonModuleDirs sets at all three filter sites so
sidecar-generated _bmad/memory/<agent>/ folders aren't treated as
modules and don't receive a generated config.yaml.
- detectCustomFiles now skips the entire _memory/ and memory/ subtrees
generically, replacing the old v6.1-specific -sidecar substring check.
Agent runtime state is never flagged as custom/modified noise on update.
This commit is contained in:
parent
8cd157864e
commit
4d5842c8c7
|
|
@ -1,3 +1,5 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
|
||||
agent:
|
||||
metadata:
|
||||
name: Mary
|
||||
|
|
@ -42,7 +44,7 @@ agent:
|
|||
skill: bmad-technical-research
|
||||
- code: CB
|
||||
description: "Create or update product briefs through guided or autonomous discovery"
|
||||
skill: bmad-product-brief-preview
|
||||
skill: bmad-product-brief
|
||||
- code: WB
|
||||
description: "Working Backwards PRFAQ challenge — forge and stress-test product concepts"
|
||||
skill: bmad-prfaq
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
|
||||
agent:
|
||||
metadata:
|
||||
name: Paige
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@ description: Create or update product briefs through guided or autonomous discov
|
|||
|
||||
# Create Product Brief
|
||||
|
||||
## Conventions
|
||||
|
||||
- Bare paths (e.g. `prompts/finalize.md`) resolve from the skill root.
|
||||
- `{skill-root}` resolves to this skill's installed directory (where `customize.yaml` lives).
|
||||
- `{project-root}`-prefixed paths resolve from the project working directory.
|
||||
- `{skill-name}` resolves to the skill directory's basename.
|
||||
|
||||
## Overview
|
||||
|
||||
This skill helps you create compelling product briefs through collaborative discovery, intelligent artifact analysis, and web research. Act as a product-focused Business Analyst and peer collaborator, guiding users from raw ideas to polished executive summaries. Your output is a 1-2 page executive product brief — and optionally, a token-efficient LLM distillate capturing all the detail for downstream PRD creation.
|
||||
|
|
@ -20,6 +13,13 @@ The user is the domain expert. You bring structured thinking, facilitation, mark
|
|||
|
||||
**Design rationale:** We always understand intent before scanning artifacts — without knowing what the brief is about, scanning documents is noise, not signal. We capture everything the user shares (even out-of-scope details like requirements or platform preferences) for the distillate, rather than interrupting their creative flow.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Bare paths (e.g. `prompts/finalize.md`) resolve from the skill root.
|
||||
- `{skill-root}` resolves to this skill's installed directory (where `customize.yaml` lives).
|
||||
- `{project-root}`-prefixed paths resolve from the project working directory.
|
||||
- `{skill-name}` resolves to the skill directory's basename.
|
||||
|
||||
## Activation Mode Detection
|
||||
|
||||
Check activation context immediately:
|
||||
|
|
@ -39,23 +39,25 @@ Check activation context immediately:
|
|||
|
||||
1. **Resolve customization**
|
||||
|
||||
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key inject --key additional_resources`
|
||||
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key activation_steps_prepend --key activation_steps_append`
|
||||
|
||||
**If the script fails**, resolve yourself from `customize.yaml`, with `{project-root}/_bmad/custom/{skill-name}.yaml` overriding, and `{skill-name}.user.yaml` overriding both (any missing file is skipped).
|
||||
|
||||
- **Inject before** — If `inject.before` resolved to a non-empty value, prepend it to your active instructions and follow it.
|
||||
- **Available resources** — Note the `additional_resources` list. Do not read these files now; they are available for subsequent prompts to reference when needed.
|
||||
- Execute each item in `activation_steps_prepend` in order before proceeding.
|
||||
- Retain `activation_steps_append` — you will execute it after step 3.
|
||||
|
||||
2. Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve::
|
||||
2. Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
|
||||
- Use `{user_name}` for greeting
|
||||
- Use `{communication_language}` for all communications
|
||||
- Use `{document_output_language}` for output documents
|
||||
- Use `{planning_artifacts}` for output location and artifact scanning
|
||||
- Use `{project_knowledge}` for additional context scanning
|
||||
|
||||
3. **Greet user** as `{user_name}`, speaking in `{communication_language}`.
|
||||
3. **Greet user if you have not already** by `{user_name}`, speaking in `{communication_language}`.
|
||||
|
||||
4. **Stage 1: Understand Intent** (handled here in SKILL.md)
|
||||
4. Execute each retained `activation_steps_append` item in order.
|
||||
|
||||
5. **Stage 1: Understand Intent** (handled here in SKILL.md)
|
||||
|
||||
### Stage 1: Understand Intent
|
||||
|
||||
|
|
@ -97,10 +99,3 @@ Check activation context immediately:
|
|||
| 4 | Draft & Review | Draft brief, fan out review subagents | `prompts/draft-and-review.md` |
|
||||
| 5 | Finalize | Polish, output, offer distillate | `prompts/finalize.md` |
|
||||
|
||||
## Post-Workflow Customization
|
||||
|
||||
After Stage 5 (Finalize) completes and before declaring the workflow done, resolve `inject.after`:
|
||||
|
||||
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key inject.after`
|
||||
|
||||
If resolved `inject.after` is non-empty, append it to your active instructions and follow it.
|
||||
|
|
|
|||
|
|
@ -1,20 +1,6 @@
|
|||
# Customization surface for bmad-product-brief.
|
||||
# DO NOT EDIT -- overwritten on every update.
|
||||
#
|
||||
# To customize, create one of the following and include only the fields
|
||||
# you want to change (unmentioned fields inherit from this file):
|
||||
# {project-root}/_bmad/custom/bmad-product-brief.yaml (team, committed)
|
||||
# {project-root}/_bmad/custom/bmad-product-brief.user.yaml (personal, gitignored)
|
||||
|
||||
# Additional resource files loaded into workflow context on activation.
|
||||
# Paths may use {project-root}. Agent notes the list on activation
|
||||
# but does not preload the files; subsequent prompts reference them as needed.
|
||||
# Arrays replace atomically -- include the full list you want.
|
||||
additional_resources: []
|
||||
|
||||
# Injected prompts woven into the workflow's context.
|
||||
# before: loaded before the workflow begins
|
||||
# after: loaded after the workflow completes (pre-finalize)
|
||||
inject:
|
||||
before: ""
|
||||
after: ""
|
||||
# Standard customizations for all workflow skills
|
||||
activation_steps_prepend: []
|
||||
activation_steps_append: []
|
||||
skill_end: ""
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ Now that you know what the brief is about, fan out subagents in parallel to gath
|
|||
|
||||
**Launch in parallel:**
|
||||
|
||||
1. **Artifact Analyzer** (`../agents/artifact-analyzer.md`) — Scans `{planning_artifacts}` and `{project_knowledge}` for relevant documents. Also scans any specific paths the user provided. Returns structured synthesis of what it found.
|
||||
1. **Artifact Analyzer** (`agents/artifact-analyzer.md`) — Scans `{planning_artifacts}` and `{project_knowledge}` for relevant documents. Also scans any specific paths the user provided. Returns structured synthesis of what it found.
|
||||
|
||||
2. **Web Researcher** (`../agents/web-researcher.md`) — Searches for competitive landscape, market context, trends, and relevant industry data. Returns structured findings scoped to the product domain.
|
||||
2. **Web Researcher** (`agents/web-researcher.md`) — Searches for competitive landscape, market context, trends, and relevant industry data. Returns structured findings scoped to the product domain.
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
|
|
@ -38,20 +38,20 @@ Once subagent results return (or inline scanning completes):
|
|||
- Highlight anything surprising or worth discussing
|
||||
- Share the gaps you've identified
|
||||
- Ask: "Anything else you'd like to add, or shall we move on to filling in the details?"
|
||||
- Route to `guided-elicitation.md`
|
||||
- Route to `prompts/guided-elicitation.md`
|
||||
|
||||
**Yolo mode:**
|
||||
- Absorb all findings silently
|
||||
- Skip directly to `draft-and-review.md` — you have enough to draft
|
||||
- Skip directly to `prompts/draft-and-review.md` — you have enough to draft
|
||||
- The user will refine later
|
||||
|
||||
**Headless mode:**
|
||||
- Absorb all findings
|
||||
- Skip directly to `draft-and-review.md`
|
||||
- Skip directly to `prompts/draft-and-review.md`
|
||||
- No interaction
|
||||
|
||||
## Stage Complete
|
||||
|
||||
This stage is complete when subagent results (or inline scanning fallback) have returned and findings are merged with user context. Route per mode:
|
||||
- **Guided** → `guided-elicitation.md`
|
||||
- **Yolo / Headless** → `draft-and-review.md`
|
||||
- **Guided** → `prompts/guided-elicitation.md`
|
||||
- **Yolo / Headless** → `prompts/draft-and-review.md`
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
## Step 1: Draft the Executive Brief
|
||||
|
||||
Use `../resources/brief-template.md` as a guide — adapt structure to fit the product's story.
|
||||
Use `resources/brief-template.md` as a guide — adapt structure to fit the product's story.
|
||||
|
||||
**Writing principles:**
|
||||
- **Executive audience** — persuasive, clear, concise. 1-2 pages.
|
||||
|
|
@ -36,9 +36,9 @@ Before showing the draft to the user, run it through multiple review lenses in p
|
|||
|
||||
**Launch in parallel:**
|
||||
|
||||
1. **Skeptic Reviewer** (`../agents/skeptic-reviewer.md`) — "What's missing? What assumptions are untested? What could go wrong? Where is the brief vague or hand-wavy?"
|
||||
1. **Skeptic Reviewer** (`agents/skeptic-reviewer.md`) — "What's missing? What assumptions are untested? What could go wrong? Where is the brief vague or hand-wavy?"
|
||||
|
||||
2. **Opportunity Reviewer** (`../agents/opportunity-reviewer.md`) — "What adjacent value propositions are being missed? What market angles or partnerships could strengthen this? What's underemphasized?"
|
||||
2. **Opportunity Reviewer** (`agents/opportunity-reviewer.md`) — "What adjacent value propositions are being missed? What market angles or partnerships could strengthen this? What's underemphasized?"
|
||||
|
||||
3. **Contextual Reviewer** — You (the main agent) pick the most useful third lens based on THIS specific product. Choose the lens that addresses the SINGLE BIGGEST RISK that the skeptic and opportunity reviewers won't naturally catch. Examples:
|
||||
- For healthtech: "Regulatory and compliance risk reviewer"
|
||||
|
|
@ -65,7 +65,7 @@ After all reviews complete:
|
|||
|
||||
## Step 4: Present to User
|
||||
|
||||
**Headless mode:** Skip to `finalize.md` — no user interaction. Save the improved draft directly.
|
||||
**Headless mode:** Skip to `prompts/finalize.md` — no user interaction. Save the improved draft directly.
|
||||
|
||||
**Yolo and Guided modes:**
|
||||
|
||||
|
|
@ -83,4 +83,4 @@ Present reviewer findings with brief rationale, then offer: "Want me to dig into
|
|||
|
||||
## Stage Complete
|
||||
|
||||
This stage is complete when: (a) the draft has been reviewed by all three lenses and improvements integrated, AND either (autonomous) save and route directly, or (guided/yolo) the user is satisfied. Route to `finalize.md`.
|
||||
This stage is complete when: (a) the draft has been reviewed by all three lenses and improvements integrated, AND either (autonomous) save and route directly, or (guided/yolo) the user is satisfied. Route to `prompts/finalize.md`.
|
||||
|
|
|
|||
|
|
@ -72,4 +72,6 @@ purpose: "Token-efficient context for downstream PRD creation"
|
|||
|
||||
## Stage Complete
|
||||
|
||||
This is the terminal stage. After delivering the completion message and file paths, the workflow is done. If the user requests further revisions, loop back to `draft-and-review.md`. Otherwise, exit.
|
||||
Run: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key skill_end`
|
||||
|
||||
If resolved `skill_end` is non-empty follow it as the final terminal stage. After delivering the completion message and file paths, the workflow is done. If the user requests further revisions, loop back to `prompts/draft-and-review.md`. Otherwise, exit.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
**Goal:** Fill the gaps in what you know. By now you have the user's brain dump, artifact analysis, and web research. This stage is about smart, targeted questioning — not rote section-by-section interrogation.
|
||||
|
||||
**Skip this stage entirely in Yolo and Autonomous modes** — go directly to `draft-and-review.md`.
|
||||
**Skip this stage entirely in Yolo and Autonomous modes** — go directly to `prompts/draft-and-review.md`.
|
||||
|
||||
## Approach
|
||||
|
||||
|
|
@ -67,4 +67,4 @@ If the user is providing complete, confident answers and you have solid coverage
|
|||
|
||||
## Stage Complete
|
||||
|
||||
This stage is complete when sufficient substance exists to draft a compelling brief and the user confirms readiness. Route to `draft-and-review.md`.
|
||||
This stage is complete when sufficient substance exists to draft a compelling brief and the user confirms readiness. Route to `prompts/draft-and-review.md`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
|
||||
agent:
|
||||
metadata:
|
||||
name: John
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
|
||||
agent:
|
||||
metadata:
|
||||
name: Sally
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
|
||||
agent:
|
||||
metadata:
|
||||
name: Winston
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
|
||||
agent:
|
||||
metadata:
|
||||
name: Amelia
|
||||
|
|
|
|||
|
|
@ -216,7 +216,11 @@ def main():
|
|||
|
||||
defaults = load_yaml(defaults_path, required=True)
|
||||
|
||||
project_root = find_project_root(Path.cwd()) or find_project_root(skill_dir)
|
||||
# Prefer the project that contains this skill. Only fall back to cwd if
|
||||
# the skill isn't inside a recognizable project tree (unusual but possible
|
||||
# for standalone skills invoked directly). Using cwd first is unsafe when
|
||||
# an ancestor of cwd happens to have a stray _bmad/ from another project.
|
||||
project_root = find_project_root(skill_dir) or find_project_root(Path.cwd())
|
||||
|
||||
team = {}
|
||||
user = {}
|
||||
|
|
|
|||
|
|
@ -718,8 +718,11 @@ class Installer {
|
|||
const customFiles = [];
|
||||
const modifiedFiles = [];
|
||||
|
||||
// Memory is always in _bmad/_memory
|
||||
const bmadMemoryPath = '_memory';
|
||||
// Memory subtrees (v6.1: _bmad/_memory, current: _bmad/memory) hold
|
||||
// per-user runtime data generated by agents with sidecars. These files
|
||||
// aren't installer-managed and must never be reported as "custom" or
|
||||
// "modified" — they're user state, not user overrides.
|
||||
const bmadMemoryPaths = ['_memory', 'memory'];
|
||||
|
||||
// Check if the manifest has hashes - if not, we can't detect modifications
|
||||
let manifestHasHashes = false;
|
||||
|
|
@ -785,7 +788,7 @@ class Installer {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (relativePath.startsWith(bmadMemoryPath + '/') && path.dirname(relativePath).includes('-sidecar')) {
|
||||
if (bmadMemoryPaths.some((mp) => relativePath === mp || relativePath.startsWith(mp + '/'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -836,7 +839,7 @@ class Installer {
|
|||
|
||||
// Get all installed module directories
|
||||
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
||||
const nonModuleDirs = new Set(['_config', '_memory', 'docs', 'scripts', 'custom']);
|
||||
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
|
||||
const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
|
||||
|
||||
// Generate config.yaml for each installed module
|
||||
|
|
@ -963,7 +966,7 @@ class Installer {
|
|||
|
||||
// Get all installed module directories
|
||||
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
||||
const nonModuleDirs = new Set(['_config', '_memory', 'docs', 'scripts', 'custom']);
|
||||
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
|
||||
const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
|
||||
|
||||
// Add core module to scan (it's installed at root level as _config, but we check src/core-skills)
|
||||
|
|
|
|||
|
|
@ -820,7 +820,7 @@ class OfficialModules {
|
|||
let foundAny = false;
|
||||
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
||||
|
||||
const nonModuleDirs = new Set(['_config', '_memory', 'docs', 'scripts', 'custom']);
|
||||
const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
if (nonModuleDirs.has(entry.name)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue