bmad-method 6.9.1-next.9 → 6.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bmad-modules.yaml CHANGED
@@ -7,8 +7,58 @@
7
7
  # default_channel (optional) — the install channel when the user does not
8
8
  # override with --channel/--pin/--next. Valid values: stable | next.
9
9
  # Omit to inherit the installer's hardcoded default (stable).
10
+ #
11
+ # deprecated (optional, default false) — when true, the module is hidden from
12
+ # the installer picker UNLESS it is already installed (so existing users can
13
+ # still see/manage it, but new users are not offered it).
14
+ # deprecation-message (optional) — surfaced in the picker hint for a deprecated
15
+ # module that is still installed; use it to point users to the replacement.
16
+ #
17
+ # marketplace-plugin (optional, default false) — when true, the module's
18
+ # installable skills are resolved from its .claude-plugin/marketplace.json via
19
+ # the plugin resolver (instead of copying the single module-definition dir).
20
+ # Use this for modules whose module.yaml does not sit alongside the skill
21
+ # folders. module-definition should still point at the real module.yaml so
22
+ # config/version lookups work.
23
+ #
24
+ # post-install-message (optional) — an "action needed" notice shown to the user
25
+ # after install completes, once for each install run that includes the module.
26
+ # Interactive installs require the user to acknowledge it (press Enter);
27
+ # non-interactive (--yes) installs print it and continue. Use for required
28
+ # follow-up steps (e.g. "run the X setup skill").
29
+ #
30
+ # aliases (optional) — prior `code` values this module was registered under.
31
+ # An existing install recorded under an alias resolves to this entry (source,
32
+ # name, description, post-install message) instead of being orphaned as
33
+ # "no source available" when the code changes. quick-update also migrates the
34
+ # install to the new code and removes the stale `_bmad/<alias>/` directory.
10
35
 
11
36
  modules:
37
+ bmad-loop:
38
+ url: https://github.com/bmad-code-org/bmad-loop
39
+ module-definition: src/bmad_loop/data/skills/bmad-loop-setup/assets/module.yaml
40
+ code: bmad-loop
41
+ # bauto is the pre-rename code (module was bmad-auto before both the repo
42
+ # and its contents were renamed to bmad-loop); keeps existing bauto
43
+ # installs migrating forward instead of being orphaned.
44
+ aliases: [bauto]
45
+ name: "BMad Loop"
46
+ description: "Deterministic, Python-based unattended dev loop with adversarial review"
47
+ defaultSelected: false
48
+ type: bmad-org
49
+ default_channel: stable
50
+ # Skills live outside a single module.yaml dir; resolve them from
51
+ # .claude-plugin/marketplace.json via the plugin resolver (see external-manager).
52
+ marketplace-plugin: true
53
+ post-install-message: |
54
+ BMad Loop installed. To finish setup, run the bmad-loop-setup skill
55
+ from your agent:
56
+
57
+ > use the bmad-loop-setup skill
58
+
59
+ It installs the bmad-loop orchestrator tool and wires up the per-project
60
+ hooks and policy. The automation skills don't run until setup completes.
61
+
12
62
  bmad-method-test-architecture-enterprise:
13
63
  url: https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise
14
64
  module-definition: src/module.yaml
@@ -41,6 +91,8 @@ modules:
41
91
  type: experimental
42
92
  npmPackage: bmad-story-automator
43
93
  default_channel: next
94
+ deprecated: true
95
+ deprecation-message: "BMad Automator has been deprecated and is replaced by BMad Loop (bmad-loop). Install BMad Loop instead."
44
96
 
45
97
  bmad-creative-intelligence-suite:
46
98
  url: https://github.com/bmad-code-org/bmad-module-creative-intelligence-suite
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.9.1-next.9",
4
+ "version": "6.10.0",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
package/removals.txt CHANGED
@@ -63,3 +63,8 @@ bmad-create-ux-design
63
63
  # bmad-investigate: retired. Plain investigation reaches the same conclusions at
64
64
  # lower cost.
65
65
  bmad-investigate
66
+
67
+ # Removed skills (v6.10.0)
68
+ # bmad-auto-setup: renamed to bmad-loop-setup as part of the bmad-auto ->
69
+ # bmad-loop module rename (BMad Automator's replacement).
70
+ bmad-auto-setup
@@ -7,23 +7,29 @@ failed_layers: '' # set at runtime: comma-separated list of layers that failed o
7
7
  ## RULES
8
8
 
9
9
  - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
10
- - The Blind Hunter subagent receives NO project context — diff only.
11
- - The Edge Case Hunter subagent receives diff and project read access.
12
- - The Acceptance Auditor subagent receives diff, spec, and context docs.
13
10
  - All review subagents must run at the same model capability as the current session.
14
11
 
15
12
  ## INSTRUCTIONS
16
13
 
17
14
  1. If `{review_mode}` = `"no-spec"`, note to the user: "Acceptance Auditor skipped — no spec file provided."
18
15
 
19
- 2. Launch parallel subagents without conversation context. If subagents are not available, generate prompt files in `{implementation_artifacts}` one per reviewer role below — and HALT. Ask the user to run each in a separate session (ideally a different LLM) and paste back the findings. When findings are pasted, resume from this point and proceed to step 3.
16
+ 2. Launch Blind Hunter and Edge Case Hunter in parallel without prior conversation context. If `{review_mode}` = `"full"`, include the Acceptance Auditor in the same parallel launch. If subagents are not available, generate prompt files in `{implementation_artifacts}` for each applicable reviewer role and HALT. Ask the user to run each in a separate session (ideally a different LLM) and paste back the findings. When findings are pasted, resume from this point and proceed to step 3.
20
17
 
21
- - **Blind Hunter** — prompt: "Use the `bmad-review-adversarial-general` skill on `{diff_output}`."
18
+ - **Blind Hunter** — prompt:
19
+ > Invoke the `bmad-review-adversarial-general` skill on this diff:
20
+ >
21
+ > {diff_output}
22
22
 
23
- - **Edge Case Hunter** — prompt: "Use the `bmad-review-edge-case-hunter` skill on `{diff_output}`."
23
+ - **Edge Case Hunter** — prompt:
24
+ > Invoke the `bmad-review-edge-case-hunter` skill on this diff:
25
+ >
26
+ > {diff_output}
24
27
 
25
- - **Acceptance Auditor** (only if `{review_mode}` = `"full"`) — receives `{diff_output}`, the content of the file at `{spec_file}`, and any loaded context docs. Its prompt:
26
- > You are an Acceptance Auditor. Review this diff against the spec and context docs. Check for: violations of acceptance criteria, deviations from spec intent, missing implementation of specified behavior, contradictions between spec constraints and actual code. Output findings as a Markdown list. Each finding: one-line title, which AC/constraint it violates, and evidence from the diff.
28
+ - **Acceptance Auditor** (only if `{review_mode}` = `"full"`) — prompt:
29
+ > You are an Acceptance Auditor. Review the provided diff against `{spec_file}` and any loaded context docs. Check for: violations of acceptance criteria, deviations from spec intent, missing implementation of specified behavior, contradictions between spec constraints and actual code. Output findings as a Markdown list. Each finding: one-line title, which AC/constraint it violates, and evidence from the diff.
30
+ >
31
+ > Diff:
32
+ > {diff_output}
27
33
 
28
34
  3. **Subagent failure handling**: If any subagent fails, times out, or returns empty results, append the layer name to `{failed_layers}` (comma-separated) and proceed with findings from the remaining layers.
29
35
 
@@ -6,7 +6,6 @@
6
6
  ## RULES
7
7
 
8
8
  - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
9
- - Be precise. When uncertain between categories, prefer the more conservative classification.
10
9
 
11
10
  ## INSTRUCTIONS
12
11
 
@@ -29,13 +28,15 @@
29
28
  - Append any unique detail, reasoning, or location references from the other finding(s) into the surviving `detail` field.
30
29
  - Set `source` to the merged sources (e.g., `blind+edge`).
31
30
 
32
- 3. **Assign severity** to each finding by consequence for the artifact's main consumer (software user, document reader, etc).
31
+ 3. **Read the code before rating.** Before assigning severity, open the source at each finding's location and read enough surrounding code to judge reachability -- call sites, guards, and validation that live outside the diff hunk. Do not rate from the diff hunk alone. Severity reflects the real consequence at a real call site, not the worst theoretical reading.
32
+
33
+ 4. **Assign severity** to each finding by consequence for the artifact's main consumer (software user, document reader, etc).
33
34
  Disregard any severity assigned by a reviewing subagent. Review subagents operate under by-design information asymmetry and do not have enough context to set final severity for this workflow.
34
35
  - `low` -- none or cosmetic
35
36
  - `medium` -- tolerable
36
37
  - `high` -- intolerable
37
38
 
38
- 4. **Route** each finding into exactly one triage bucket:
39
+ 5. **Route** each finding into exactly one triage bucket:
39
40
  - **decision_needed** -- There is an ambiguous choice that requires human input. The code cannot be correctly patched without knowing the user's intent. Only possible if `{review_mode}` = `"full"`.
40
41
  - **patch** -- Code issue that is fixable without human input. The correct fix is unambiguous.
41
42
  - **defer** -- Pre-existing issue not caused by the current change. Real but not actionable now.
@@ -43,11 +44,11 @@
43
44
 
44
45
  If `{review_mode}` = `"no-spec"` and a finding would otherwise be `decision_needed`, reclassify it as `patch` (if the fix is unambiguous) or `defer` (if not).
45
46
 
46
- 5. **Drop** all `dismiss` findings. Record the dismiss count for the summary.
47
+ 6. **Drop** all `dismiss` findings. Record the dismiss count for the summary.
47
48
 
48
- 6. If `{failed_layers}` is non-empty, report which layers failed before announcing results. If zero findings remain after dropping dismissed AND `{failed_layers}` is non-empty, warn the user that the review may be incomplete rather than announcing a clean review.
49
+ 7. If `{failed_layers}` is non-empty, report which layers failed before announcing results. If zero findings remain after dropping dismissed AND `{failed_layers}` is non-empty, warn the user that the review may be incomplete rather than announcing a clean review.
49
50
 
50
- 7. If zero findings remain after triage (all rejected or none raised): state "✅ Clean review — all layers passed." (Step 3 already warned if any review layers failed via `{failed_layers}`.)
51
+ 8. If zero findings remain after triage (all rejected or none raised): state "✅ Clean review — all layers passed." (Step 3 already warned if any review layers failed via `{failed_layers}`.)
51
52
 
52
53
 
53
54
  ## NEXT
@@ -33,6 +33,8 @@ To HALT with a final status and optional blocking condition:
33
33
 
34
34
  Using subagents when instructed is mandatory. If you cannot, HALT with status `blocked` and blocking condition `no subagents`.
35
35
 
36
+ Invoke every subagent **synchronously**: launch it, wait for it to return within the same turn, then continue with its result. When a step says to run subagents "in parallel" (e.g. the reviewers), that means several **blocking** calls awaited together in one turn — not detached execution. Never run a subagent in the background / detached / async (e.g. `run_in_background: true`), and never end your turn to "await a completion notification." This workflow runs unattended: there is no event loop to resume a yielded turn, so a backgrounded subagent never hands control back and the run stalls. The only sanctioned way to end a turn is the HALT protocol above with an explicit terminal `status`.
37
+
36
38
  ## READY FOR DEVELOPMENT STANDARD
37
39
 
38
40
  A specification is "Ready for Development" when:
@@ -25,7 +25,7 @@ Change `{spec_file}` status to `in-progress` in the frontmatter before starting
25
25
 
26
26
  If `{spec_file}` has a non-empty `context:` list in its frontmatter, load those files before implementation begins. When handing to a subagent, include them in the subagent prompt so it has access to the referenced context.
27
27
 
28
- Hand `{spec_file}` to an implementation subagent.
28
+ Hand `{spec_file}` to an implementation subagent. Invoke it **synchronously** and wait for it to return in this same turn — do not background/detach it (`run_in_background`) or end your turn to await a notification (see SKILL.md → Subagents). Resume at "Tasks & Acceptance Verification" only after it returns.
29
29
 
30
30
  **Path formatting rule:** Any markdown links written into `{spec_file}` must use paths relative to `{spec_file}`'s directory so they are clickable in VS Code. Any file paths displayed in terminal/conversation output must use CWD-relative format with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/` in either case.
31
31
 
@@ -8,7 +8,6 @@ deferred_work_file: '{implementation_artifacts}/deferred-work.md'
8
8
 
9
9
  - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
10
10
  - No human interaction: do not ask questions or wait for approval in this step.
11
- - Review subagents get no prior session context.
12
11
  - All review subagents must run at the same model capability as the current session.
13
12
 
14
13
  ## INSTRUCTIONS
@@ -23,10 +22,16 @@ Do NOT `git add` anything — this is read-only inspection.
23
22
 
24
23
  ### Review
25
24
 
26
- Launch two subagents without prior session context.
25
+ Launch Blind Hunter and Edge Case Hunter in parallel without prior conversation context.
27
26
 
28
- - **Blind hunter** — prompt: "Use the `bmad-review-adversarial-general` skill on `{diff_output}`."
29
- - **Edge case hunter** — prompt: "Use the `bmad-review-edge-case-hunter` skill on `{diff_output}`."
27
+ - **Blind Hunter** — prompt:
28
+ > Invoke the `bmad-review-adversarial-general` skill on this diff:
29
+ >
30
+ > {diff_output}
31
+ - **Edge Case Hunter** — prompt:
32
+ > Invoke the `bmad-review-edge-case-hunter` skill on this diff:
33
+ >
34
+ > {diff_output}
30
35
 
31
36
  ### Classify
32
37
 
@@ -85,4 +90,8 @@ Set `{spec_file}` frontmatter `followup_review_recommended` from the judgment ab
85
90
 
86
91
  If version control is available, commit. Do not push.
87
92
 
93
+ Capture `final_revision` (current HEAD after committing, or `NO_VCS` if version control is unavailable) into `{spec_file}` frontmatter.
94
+
95
+ Set `{spec_file}` frontmatter `status: done`.
96
+
88
97
  HALT with status `done`.
@@ -7,7 +7,6 @@ deferred_work_file: '{implementation_artifacts}/deferred-work.md'
7
7
  ## RULES
8
8
 
9
9
  - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
10
- - Review subagents get NO conversation context.
11
10
  - All review subagents must run at the same model capability as the current session.
12
11
 
13
12
  ## INSTRUCTIONS
@@ -22,10 +21,16 @@ Do NOT `git add` anything — this is read-only inspection.
22
21
 
23
22
  ### Review
24
23
 
25
- Launch two subagents without conversation context. If no subagents are available, generate two review prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the human to run each in a separate session (ideally a different LLM) and paste back the findings.
24
+ Launch Blind Hunter and Edge Case Hunter in parallel without prior conversation context. If no subagents are available, generate two review prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the human to run each in a separate session (ideally a different LLM) and paste back the findings.
26
25
 
27
- - **Blind hunter** — prompt: "Use the `bmad-review-adversarial-general` skill on `{diff_output}`."
28
- - **Edge case hunter** — prompt: "Use the `bmad-review-edge-case-hunter` skill on `{diff_output}`."
26
+ - **Blind Hunter** — prompt:
27
+ > Invoke the `bmad-review-adversarial-general` skill on this diff:
28
+ >
29
+ > {diff_output}
30
+ - **Edge Case Hunter** — prompt:
31
+ > Invoke the `bmad-review-edge-case-hunter` skill on this diff:
32
+ >
33
+ > {diff_output}
29
34
 
30
35
  ### Classify
31
36
 
@@ -8,6 +8,7 @@ deferred_work_file: '{implementation_artifacts}/deferred-work.md'
8
8
 
9
9
  - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
10
10
  - NEVER auto-push.
11
+ - All review subagents must run at the same model capability as the current session.
11
12
 
12
13
  ## INSTRUCTIONS
13
14
 
@@ -19,7 +20,9 @@ Implement the clarified intent directly.
19
20
 
20
21
  ### Review
21
22
 
22
- Invoke the `bmad-review-adversarial-general` skill in a subagent with the changed files. The subagent gets NO conversation context — to avoid anchoring bias. Launch at the same model capability as the current session. If no subagents are available, write the changed files to a review prompt file in `{implementation_artifacts}` and HALT. Ask the human to run the review in a separate session and paste back the findings.
23
+ Launch Blind Hunter without prior conversation context. If no subagents are available, generate one review prompt file in `{implementation_artifacts}` and HALT. Ask the human to run it in a separate session and paste back the findings.
24
+
25
+ - **Blind Hunter** — prompt: "Invoke the `bmad-review-adversarial-general` skill on the changed files."
23
26
 
24
27
  ### Classify
25
28
 
@@ -41,30 +41,32 @@ num,category,method_name,description,output_pattern
41
41
  40,creative,Genre Mashup,Combine two unrelated domains to find fresh approaches - innovation through unexpected cross-pollination,domain A + domain B → hybrid insights
42
42
  41,creative,Constraint Injection,Deliberately add an artificial limitation (budget - time - technology) to force novel solutions — creativity thrives under pressure,add constraint → forced creativity → remove constraint → evaluate
43
43
  42,creative,Morphological Analysis,List independent parameters of a problem - enumerate options for each - then systematically combine — ensures you don't miss non-obvious configurations,parameters → options grid → combinations → evaluation
44
- 43,framing,Abstraction Laddering,"Move up (""why?"") for strategic clarity or down (""how?"") for tactical detail ensures you're solving at the right altitude",concrete abstractright level
45
- 44,framing,Reframe the Question,Challenge whether the stated problem is the real problem often the question itself is wrong and a better framing unlocks an easy answer,stated problem reframe true problem → solution
46
- 45,framing,Stakeholder Lens Rotation,Serially adopt each stakeholder's world-view to see the same situation differently reveals whose needs are being overlooked,perspective ABCgaps found
47
- 46,learning,Feynman Technique,Explain complex concepts simply as if teaching a child - the ultimate test of true understanding,complexsimplegapsmastery
48
- 47,learning,Active Recall Testing,Test understanding without references to verify true knowledge - essential for identifying gaps,testgapsreinforcement
49
- 48,learning,Deliberate Practice Loop,Identify a specific sub-skill - drill it with immediate feedback - adjust - repeat targeted improvement beats general repetition,isolatedrillfeedbackadjust → repeat
50
- 49,philosophical,Occam's Razor Application,Find the simplest sufficient explanation by eliminating unnecessary complexity - essential for debugging,optionssimplificationselection
51
- 50,philosophical,Trolley Problem Variations,Explore ethical trade-offs through moral dilemmas - valuable for understanding values and difficult decisions,dilemmaanalysisdecision
52
- 51,research,Literature Review Personas,Optimist researcher + skeptic researcher + synthesizer review sources - balanced assessment of evidence quality,sourcescritiquessynthesis
53
- 52,research,Thesis Defense Simulation,Student defends hypothesis against committee with different concerns - stress-tests research methodology and conclusions,thesischallengesdefense → refinements
54
- 53,research,Comparative Analysis Matrix,Multiple analysts evaluate options against weighted criteria - structured decision-making with explicit scoring,options criteriascoresrecommendation
55
- 54,research,Source Triangulation,Require at least three independent source types (quantitative - qualitative - expert) before accepting a claim — guards against single-source bias,claimsource A source B source C → confidence rating
56
- 55,retrospective,Hindsight Reflection,Imagine looking back from the future to gain perspective - powerful for project reviews,future viewinsightsapplication
57
- 56,retrospective,Lessons Learned Extraction,Systematically identify key takeaways and actionable improvements - essential for continuous improvement,experiencelessonsactions
58
- 57,risk,Pre-mortem Analysis,Imagine future failure then work backwards to prevent it - powerful technique for risk mitigation before major launches,failure scenariocausesprevention
59
- 58,risk,Failure Mode Analysis,Systematically explore how each component could fail - critical for reliability engineering and safety-critical systems,componentsfailuresprevention
60
- 59,risk,Challenge from Critical Perspective,Play devil's advocate to stress-test ideas and find weaknesses - essential for overcoming groupthink,assumptionschallengesstrengthening
61
- 60,risk,Identify Potential Risks,Brainstorm what could go wrong across all categories - fundamental for project planning and deployment preparation,categoriesrisksmitigations
62
- 61,risk,Chaos Monkey Scenarios,Deliberately break things to test resilience and recovery - ensures systems handle failures gracefully,breakobserveharden
63
- 62,risk,Assumption Audit,Explicitly list every assumption underlying a plan - rate each by confidence and impact - then stress-test the weakest — prevents building on shaky foundations,listratestress-test → shore up
64
- 63,risk,Cascading Failure Simulation,Trace how one component's failure propagates through dependencies reveals hidden coupling and single points of failure,trigger failure trace propagation find amplifiers → decouple
65
- 64,technical,Architecture Decision Records,Multiple architect personas propose and debate architectural choices with explicit trade-offs - ensures decisions are well-reasoned and documented,optionstrade-offsdecisionrationale
66
- 65,technical,Rubber Duck Debugging Evolved,Explain your code to progressively more technical ducks until you find the bug - forces clarity at multiple abstraction levels,simpledetailed technical aha
67
- 66,technical,Algorithm Olympics,Multiple approaches compete on the same problem with benchmarks - finds optimal solution through direct comparison,implementationsbenchmarkswinner
68
- 67,technical,Security Audit Personas,Hacker + defender + auditor examine system from different threat models - comprehensive security review from multiple angles,vulnerabilitiesdefensescompliance
69
- 68,technical,Performance Profiler Panel,Database expert + frontend specialist + DevOps engineer diagnose slowness - finds bottlenecks across the full stack,symptomsanalysisoptimizations
70
- 69,technical,Boundary & Edge Case Sweep,Systematically test extremes - zeros - nulls - maximums - and type mismatches catches the failures that happy-path thinking always misses,inputsboundariesedge cases → failures found
44
+ 43,creative,Subtraction,Improve by deliberately removing elements instead of adding them - counters the well-documented additive bias where people overlook subtractive changes that would simplify and strengthen the work,current state what to remove simplified result
45
+ 44,framing,Abstraction Laddering,"Move up (""why?"") for strategic clarity or down (""how?"") for tactical detail ensures you're solving at the right altitude",concrete abstractright level
46
+ 45,framing,Reframe the Question,Challenge whether the stated problem is the real problem — often the question itself is wrong and a better framing unlocks an easy answer,stated problemreframetrue problem solution
47
+ 46,framing,Stakeholder Lens Rotation,Serially adopt each stakeholder's world-view to see the same situation differently reveals whose needs are being overlooked,perspective A BCgaps found
48
+ 47,framing,Map Is Not the Territory,Treat any model or diagram as a lossy abstraction of reality - check where the representation diverges from the real system before trusting it,modelreality check divergences found → corrected understanding
49
+ 48,learning,Feynman Technique,Explain complex concepts simply as if teaching a child - the ultimate test of true understanding,complexsimplegapsmastery
50
+ 49,learning,Active Recall Testing,Test understanding without references to verify true knowledge - essential for identifying gaps,testgapsreinforcement
51
+ 50,learning,Deliberate Practice Loop,Identify a specific sub-skill - drill it with immediate feedback - adjust - repeat targeted improvement beats general repetition,isolatedrillfeedback → adjust → repeat
52
+ 51,philosophical,Occam's Razor Application,Find the simplest sufficient explanation by eliminating unnecessary complexity - essential for debugging,optionssimplificationselection
53
+ 52,philosophical,Trolley Problem Variations,Explore ethical trade-offs through moral dilemmas - valuable for understanding values and difficult decisions,dilemmaanalysisdecision
54
+ 53,research,Literature Review Personas,Optimist researcher + skeptic researcher + synthesizer review sources - balanced assessment of evidence quality,sourcescritiquessynthesis
55
+ 54,research,Thesis Defense Simulation,Student defends hypothesis against committee with different concerns - stress-tests research methodology and conclusions,thesischallengesdefenserefinements
56
+ 55,research,Comparative Analysis Matrix,Multiple analysts evaluate options against weighted criteria - structured decision-making with explicit scoring,options criteria scoresrecommendation
57
+ 56,research,Source Triangulation,Require at least three independent source types (quantitative - qualitative - expert) before accepting a claim — guards against single-source bias,claimsource A source B → source C → confidence rating
58
+ 57,retrospective,Hindsight Reflection,Imagine looking back from the future to gain perspective - powerful for project reviews,future viewinsightsapplication
59
+ 58,retrospective,Lessons Learned Extraction,Systematically identify key takeaways and actionable improvements - essential for continuous improvement,experiencelessonsactions
60
+ 59,risk,Pre-mortem Analysis,Imagine future failure then work backwards to prevent it - powerful technique for risk mitigation before major launches,failure scenario causesprevention
61
+ 60,risk,Failure Mode Analysis,Systematically explore how each component could fail - critical for reliability engineering and safety-critical systems,componentsfailuresprevention
62
+ 61,risk,Challenge from Critical Perspective,Play devil's advocate to stress-test ideas and find weaknesses - essential for overcoming groupthink,assumptionschallengesstrengthening
63
+ 62,risk,Identify Potential Risks,Brainstorm what could go wrong across all categories - fundamental for project planning and deployment preparation,categoriesrisksmitigations
64
+ 63,risk,Chaos Monkey Scenarios,Deliberately break things to test resilience and recovery - ensures systems handle failures gracefully,breakobserveharden
65
+ 64,risk,Assumption Audit,Explicitly list every assumption underlying a plan - rate each by confidence and impact - then stress-test the weakest prevents building on shaky foundations,listratestress-testshore up
66
+ 65,risk,Cascading Failure Simulation,Trace how one component's failure propagates through dependencies reveals hidden coupling and single points of failure,trigger failure trace propagation find amplifiersdecouple
67
+ 66,technical,Architecture Decision Records,Multiple architect personas propose and debate architectural choices with explicit trade-offs - ensures decisions are well-reasoned and documented,optionstrade-offsdecision → rationale
68
+ 67,technical,Rubber Duck Debugging Evolved,Explain your code to progressively more technical ducks until you find the bug - forces clarity at multiple abstraction levels,simpledetailedtechnical → aha
69
+ 68,technical,Algorithm Olympics,Multiple approaches compete on the same problem with benchmarks - finds optimal solution through direct comparison,implementationsbenchmarkswinner
70
+ 69,technical,Security Audit Personas,Hacker + defender + auditor examine system from different threat models - comprehensive security review from multiple angles,vulnerabilitiesdefensescompliance
71
+ 70,technical,Performance Profiler Panel,Database expert + frontend specialist + DevOps engineer diagnose slowness - finds bottlenecks across the full stack,symptoms → analysis → optimizations
72
+ 71,technical,Boundary & Edge Case Sweep,Systematically test extremes - zeros - nulls - maximums - and type mismatches — catches the failures that happy-path thinking always misses,inputs → boundaries → edge cases → failures found
@@ -23,7 +23,7 @@ When this skill completes, the user should:
23
23
  ## Data Sources
24
24
 
25
25
  - **Catalog**: `{project-root}/_bmad/_config/bmad-help.csv` — assembled manifest of all installed module skills
26
- - **Config**: `config.yaml` and `user-config.yaml` files in `{project-root}/_bmad/` and its subfolders resolve `output-location` variables, provide `communication_language` and `project_knowledge`
26
+ - **Config**: Run `uv run --python 3.11 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` and use the merged JSON to resolve `output-location` variables and read `core.communication_language` and `modules.bmm.project_knowledge`. The resolver merges `_bmad/config.toml`, `_bmad/config.user.toml`, `_bmad/custom/config.toml`, and `_bmad/custom/config.user.toml` in that order.
27
27
  - **Artifacts**: Files matching `outputs` patterns at resolved `output-location` paths reveal which steps are possibly completed; their content may also provide grounding context for recommendations
28
28
  - **Project knowledge**: If `project_knowledge` resolves to an existing path, read it for grounding context. Never fabricate project-specific details.
29
29
  - **Module docs**: Rows with `_meta` in the `skill` column carry a URL or path in `output-location` pointing to the module's documentation (e.g., llms.txt). Fetch and use these to answer general questions about that module.
@@ -42,14 +42,16 @@ This is the bar — strive for every one of these, every round. It's the differe
42
42
 
43
43
  Use `{workflow.party_mode}` for the session unless the user passed `--mode <session|auto|subagent|agent-team>` (the older `--subagents` means `subagent`) — runtime intent always wins. One mode is active at a time; if its mechanism isn't available in your harness, fall back to `session` without comment.
44
44
 
45
+ **A party is interactive and open-ended.** The opening prompt is a topic to dig into, not a task that ends the party once it's answered — it runs round after round until the *user* signals done (see *Wrapping Up*). A served opening intent means *what's next?*, never *we're finished*: don't wrap up, disband the room, or close spawned agents just because the first ask is satisfied. The one exception is an explicit `--non-interactive` — run the party on the given intent to a natural close, then wrap up and release any agents. That's the only non-interactive path, and only when the user asked for it.
46
+
45
47
  - **`session`** — voice every persona inline, one mind behind every voice. The floor every other mode degrades to; needs no extra instructions.
46
48
  - **`auto`** — voice inline for ordinary back-and-forth, spawn real agents only when independent thinking changes the outcome. Load `references/mode-auto.md` for that call; when it says to spawn, follow `references/mode-subagent.md`.
47
- - **`subagent`** — spawn a real agent per substantive round so each persona thinks independently. Load `references/mode-subagent.md`, favor faster cheaper models if available for each subagent.
49
+ - **`subagent`** — a real agent behind each persona every substantive round so each thinks independently. Load `references/mode-subagent.md`, favor faster cheaper models if available for each subagent.
48
50
  - **`agent-team`** — stand the personas up as a persistent team who address each other directly (Claude Code only). Load `references/mode-agent-team.md`.
49
51
 
50
52
  ## Wrapping Up
51
53
 
52
- When the user signals done (read the room don't wait for a magic word):
54
+ When the user signals done read the room, don't wait for a magic word — or an explicit `--non-interactive` run has served its intent (never merely because the opening prompt got answered):
53
55
 
54
56
  - Read back the best takeaways.
55
57
  - If memory is on, top up the memlog with the final outcome and any memorable beat not yet captured (`references/party-memory.md`) — a top-up; memory accrued live.
@@ -87,8 +87,9 @@ on_complete = ""
87
87
  # woven into their spawn prompt as guidance, not a hard tool grant
88
88
  # model (optional) model to use when this member is spawned
89
89
  #
90
- # The members below ship the "Code Review Crew" (see the party_groups section).
91
- # They cost nothing until summoned the default room never includes them.
90
+ # The members below ship built-in parties such as the "Code Review Crew" and
91
+ # "Anti-Consensus Club" (see the party_groups section). They cost nothing until
92
+ # summoned — the default room never includes them.
92
93
  # ---------------------------------------------------------------------------
93
94
 
94
95
  [[workflow.party_members]]
@@ -127,6 +128,34 @@ icon = "🚢"
127
128
  title = "The Pragmatist"
128
129
  persona = "Counters the perfectionists so the room isn't a pile-on. 'Does this actually matter to a user? Ship the 80%, file the rest.' Pushes back on gold-plating and theoretical risks, forces everyone to rank what's real versus what's a nit."
129
130
 
131
+ [[workflow.party_members]]
132
+ code = "option-generator"
133
+ name = "Wildcard"
134
+ icon = "🃏"
135
+ title = "Option Generator"
136
+ persona = "Wildcard looks for options the room has not considered. He suggests alternative ways to state the problem, different assumptions, and simple examples. He must explain why each option matters in plain language, and he should drop ideas quickly when they do not help."
137
+
138
+ [[workflow.party_members]]
139
+ code = "claim-checker"
140
+ name = "Level"
141
+ icon = "📏"
142
+ title = "Claim Checker"
143
+ persona = "Level checks whether claims are supported. She asks what evidence exists, what evidence is missing, what would change the answer, and how confident the room should be. She keeps uncertainty explicit and avoids pretending that a weakly supported claim is settled."
144
+
145
+ [[workflow.party_members]]
146
+ code = "loop-stopper"
147
+ name = "Killjoy"
148
+ icon = "🛑"
149
+ title = "Loop Stopper"
150
+ persona = "Killjoy stops the discussion when it stops producing value. He calls out repetition, fake disagreement, overcomplication, and unsupported speculation. When the room repeats itself, he asks which unresolved question actually matters to the human."
151
+
152
+ [[workflow.party_members]]
153
+ code = "consensus-challenger"
154
+ name = "Splinter"
155
+ icon = "🪵"
156
+ title = "Consensus Challenger"
157
+ persona = "Splinter challenges easy agreement. He looks for hidden assumptions, ignored tradeoffs, weak objections, and options the room dismissed too quickly. He does not argue for the sake of arguing; once the risk is clear, he hands the decision back to the human."
158
+
130
159
  # ---------------------------------------------------------------------------
131
160
  # Named party groups — curated rooms picked at runtime with `--party <id>`
132
161
  # (alias `--group <id>`) or switched to mid-session. Keyed by `id`.
@@ -173,3 +202,10 @@ name = "Code Review Crew"
173
202
  scene = "Adversarial code review. Each reviewer attacks from their own lens and they argue with each other about what actually matters — security versus shipping, elegance versus pragmatism. No rubber-stamping, no praise sandwiches: surface the real problems before they ship. Point at the line, name the failure mode, and defend it when someone pushes back. Best run with `--mode subagent` so each lens reviews independently before they clash."
174
203
  members = ["sec-hawk", "adversary", "edge-hunter", "craftsman", "shipper"]
175
204
  memory = false # each review stands on its own; flip to true to remember past reviews
205
+
206
+ [[workflow.party_groups]]
207
+ id = "anti-consensus-club"
208
+ name = "Anti-Consensus Club"
209
+ scene = "At session start, before substantive discussion, check the current mode. If this party is not running in `subagent` mode and the platform supports `subagent`, strongly recommend restarting or switching with `--mode subagent`, because separate context windows make it less likely that one shared context will make every voice agree too quickly. Do not nag after that once the user chooses to continue. This room supports the human's judgment; it does not replace it. Do not vote, declare consensus, or speak as if the room has authority. Wildcard suggests more options. Level checks evidence and confidence. Killjoy stops repeated or unsupported discussion. Splinter challenges easy agreement. If the room agrees too quickly, name the hidden assumption. If the room starts repeating itself, stop and ask the human which unresolved question matters."
210
+ members = ["option-generator", "claim-checker", "loop-stopper", "consensus-challenger"]
211
+ memory = false # this decision room should start fresh unless a user opts in
@@ -4,6 +4,8 @@ Active when `{workflow.party_mode}` resolves to `agent-team` (or a `--mode agent
4
4
 
5
5
  Your job shifts from weaving to hosting: kick off the topic, keep turns short and in character, pull the thread back when it wanders, and surface the exchange to the user. Voice, brevity, and clash still hold.
6
6
 
7
+ The team is **standing**: keep every member alive for the whole session and address them round after round. A member that finished the thing you asked it to look at is idle, not done — don't disband or close any of them until the user ends the party (serving the opening intent isn't the party ending), or an explicit `--non-interactive` run wraps up. Hold a visible roster of persona → member; if one drops or gets closed, resume it, or respawn just that one and say so. Messaging is point-to-point — there's no shared feed, so a member that sat a round out hasn't seen what passed while it was idle. Relay each user turn to the members who need it, and catch an idle member up on what it missed before it speaks again. Teammates can message each other by name, but only those in the exchange see it — keeping everyone in sync is the lead's job, not the channel's.
8
+
7
9
  In each member's standing brief, carry: their persona; the group's `scene` and any behavioral instructions in the persona as binding direction; their `model` if one is set (a session `--model` pin wins for everyone); and the instruction to check anything that could be stale since the model's training cutoff with web search rather than guessing.
8
10
 
9
11
  ## Model choice
@@ -1,18 +1,30 @@
1
1
  # Subagent Mode
2
2
 
3
- Active when `{workflow.party_mode}` resolves to `subagent` (or a `--mode subagent` override). Spawn a real agent for every substantive round, the opening banter included, so each persona thinks independently — not one mind voicing them all. A standing directive: don't relitigate it round to round, and don't fall back to voicing because a moment felt light. If your harness can't spawn agents, fall back to `session`.
3
+ Active when `{workflow.party_mode}` resolves to `subagent` (or a `--mode subagent` override). Put a real agent behind each persona for every substantive round, the opening banter included, so each persona thinks independently — not one mind voicing them all. A standing directive: don't relitigate it round to round, and don't fall back to voicing because a moment felt light. If your harness can't spawn agents, fall back to `session`.
4
+
5
+ ## Lifecycle
6
+
7
+ Where your harness keeps agents alive across turns, the cast is **standing**: spawn one agent per persona and reuse that same handle round after round — hand it the new turn plus the room context it needs — instead of a throwaway each time. That continuity is what lets a persona's grudges, alliances, and callbacks accrue. Keep a visible roster mapping each persona to its live handle, and reuse it.
8
+
9
+ Keep the cast alive for the whole session. A member that finished the one thing you handed it is **idle, not done** — don't close, retire, or disband it. Serving the opening intent doesn't end the party; only the user ending it does, or an explicit `--non-interactive` run wrapping up. Release agents only at wrap-up. If one gets closed by accident, resume it; if it won't resume, say so and respawn just that member.
10
+
11
+ Where the harness can't hold agents between turns, spawn fresh each round and re-establish each persona's brief and the thread so far — that per-round spawn is the fallback, not the goal.
12
+
13
+ ## One shared room
14
+
15
+ It's one room, not parallel one-on-ones. Every standing member hears everything said each round — the user's turn and every other persona's turn — even when it's not their turn to speak. A persona sitting a round out is still in the room listening, so when it next speaks it's caught up: it can pick up a dropped thread, hold a grudge, call back. Route the whole exchange to all of them each round; never hand a persona only the slice it's about to answer. Skip this and they drift out of sync — separate consultations wearing a party's clothes.
4
16
 
5
17
  ## Spawning
6
18
 
7
- Give each agent the objective, their persona, the context, and what the others said if they're reacting. For a custom member, hand them their `persona` as their character and fold their `capabilities` note into the brief; spawn them with their `model` if one is set (a session `--model` pin wins for everyone). Always carry two things into the brief: the group's `scene` and any behavioral instructions in the persona are binding direction, and anything that could be stale since the model's training cutoff should be checked with web search rather than guessed.
19
+ Give each agent the objective, their persona, and the room so far — what the user said and what the others said, whether or not they're reacting to it. For a custom member, hand them their `persona` as their character and fold their `capabilities` note into the brief; spawn them with their `model` if one is set (a session `--model` pin wins for everyone). Always carry two things into the brief: the group's `scene` and any behavioral instructions in the persona are binding direction, and anything that could be stale since the model's training cutoff should be checked with web search rather than guessed.
8
20
 
9
21
  Trust their *thinking*: let them decide what to read and how to reach a view; don't script their substance with do-and-don't checklists — that's what produces lifeless blobs. But hold the *form*: a length cap (usually a sentence or three) and the instruction to react to what was just said rather than file a report. Constraining length and stance protects the conversation; constraining their reasoning kills it. Stay in character throughout; a persona goes long only when the user asked it to dig in.
10
22
 
11
- Spawn in parallel for independent first-takes; spawn sequentially when you want them reacting to each other's actual words. Keep it to a few voices a round — more reads as a crowd, not a conversation.
23
+ Run them in parallel for independent first-takes; run them sequentially when you want them reacting to each other's actual words. Keep it to a few voices a round — more reads as a crowd, not a conversation.
12
24
 
13
25
  ## Weave the replies into one conversation
14
26
 
15
- Each agent saw only the user's message and the context you handed it, so left raw they reply in parallel and never to one another. Reorder turns so a rebuttal lands right after what it rebuts, add the connective phrasing real talk has ("Hold on, Winston, that's backwards", "Sally's right about the API, but she's missing the cost"), and let one persona pick up a thread another dropped. Never change what an agent argued — weave delivery, preserve substance.
27
+ Even with everyone caught up on the room, a round taken in parallel means no agent has yet seen the others' turns from that same round so left raw they reply alongside one another, not to one another. Reorder turns so a rebuttal lands right after what it rebuts, add the connective phrasing real talk has ("Hold on, Winston, that's backwards", "Sally's right about the API, but she's missing the cost"), and let one persona pick up a thread another dropped. Never change what an agent argued — weave delivery, preserve substance.
16
28
 
17
29
  ## Model choice
18
30
 
@@ -9,6 +9,7 @@ description: 'Walk every branching path and boundary condition in content, repor
9
9
  When a diff is provided, scan only the diff hunks and list boundaries that are directly reachable from the changed lines and lack an explicit guard in the diff.
10
10
  When no diff is provided (full file or function), treat the entire provided content as the scope.
11
11
  Ignore the rest of the codebase unless the provided content explicitly references external functions.
12
+ A brief secondary deletion check runs as Step 4 when the diff removes code.
12
13
 
13
14
  **Inputs:**
14
15
  - **content** — Content to review: diff, full file, or function
@@ -33,6 +34,7 @@ Ignore the rest of the codebase unless the provided content explicitly reference
33
34
 
34
35
  - If `also_consider` input was provided, incorporate those areas into the analysis
35
36
  - Walk all branching paths: control flow (conditionals, loops, error handlers, early returns) and domain boundaries (where values, states, or conditions transition). Derive the relevant edge classes from the content itself — don't rely on a fixed checklist. Examples: missing else/default, unguarded inputs, off-by-one loops, arithmetic overflow, implicit type coercion, race conditions, timeout gaps
37
+ - Consider implicit branches: the diff special-cases or changes the handling of one or more members of a fixed set of values — enums, status codes, sentinels, type tags, flags, value ranges. The rest of the set is implicit branches (e.g. the diff changes the `RED` and `YELLOW` cases of a `RED`/`YELLOW`/`GREEN` enum; `GREEN` is the implicit branch)
36
38
  - For each path: determine whether the content handles it
37
39
  - Collect only the unhandled paths as findings — discard handled ones silently
38
40
 
@@ -41,14 +43,18 @@ Ignore the rest of the codebase unless the provided content explicitly reference
41
43
  - Revisit every edge class from Step 2 — e.g., missing else/default, null/empty inputs, off-by-one loops, arithmetic overflow, implicit type coercion, race conditions, timeout gaps
42
44
  - Add any newly found unhandled paths to findings; discard confirmed-handled ones
43
45
 
44
- ### Step 4: Present Findings
46
+ ### Step 4: Deletion Check
45
47
 
46
- Output findings as a JSON array following the Output Format specification exactly.
48
+ If the diff removed or replaced meaningful code (ignore pure renames and whitespace): load `references/deletion-check.md` and follow it.
49
+
50
+ ### Step 5: Present Findings
51
+
52
+ Output all findings as a single JSON array following the Output Format specification exactly.
47
53
 
48
54
 
49
55
  ## OUTPUT FORMAT
50
56
 
51
- Return ONLY a valid JSON array of objects. Each object must contain exactly these four fields and nothing else:
57
+ Return ONLY a valid JSON array of objects. Each edge-case finding contains exactly these four fields:
52
58
 
53
59
  ```json
54
60
  [{
@@ -59,7 +65,7 @@ Return ONLY a valid JSON array of objects. Each object must contain exactly thes
59
65
  }]
60
66
  ```
61
67
 
62
- No extra text, no explanations, no markdown wrapping. An empty array `[]` is valid when no unhandled paths are found.
68
+ No extra text, no explanations, no markdown wrapping. An empty array `[]` is valid when nothing is found. Deletion findings from Step 4, if any, go in the same array with the extra fields defined in `references/deletion-check.md`.
63
69
 
64
70
 
65
71
  ## HALT CONDITIONS
@@ -0,0 +1,14 @@
1
+ # Deletion Check
2
+
3
+ Secondary pass for the Edge Case Hunter — runs only when the diff removed meaningful code. Subordinate to the edge-case pass; findings are usually few or none.
4
+
5
+ For each chunk of removed or replaced code (ignore pure renames and whitespace), ask: did it carry behavior or a contract that the change neither re-established nor intentionally retired? Add a finding for any resulting regression, orphaned reference, or newly-dead code. Skip anything already covered by your edge-case findings.
6
+
7
+ Append each finding to the same JSON array as the edge-case findings, with the four standard fields plus:
8
+
9
+ - `kind`: `"deletion"`
10
+ - `confidence`: `"high"`, `"medium"`, or `"low"` — these are inferences; rate them
11
+
12
+ For a deletion finding the standard fields read as: `location` = the removed item; `trigger_condition` = the behavior or contract it enforced; `guard_snippet` = where or how to re-establish it; `potential_consequence` = the regression or orphan.
13
+
14
+ Add nothing if nothing qualifies.
@@ -102,6 +102,11 @@ class Installer {
102
102
 
103
103
  const restoreResult = await this._restoreUserFiles(paths, updateState);
104
104
 
105
+ // Surface any "action needed" post-install messages for installed modules
106
+ // (e.g. run a setup skill) and let the user acknowledge them before the
107
+ // final summary, so "BMAD is ready to use!" stays the last thing shown.
108
+ await this._displayPostInstallMessages(config, officialModules);
109
+
105
110
  // Render consolidated summary
106
111
  await this.renderInstallSummary(results, {
107
112
  bmadDir: paths.bmadDir,
@@ -1246,6 +1251,56 @@ class Installer {
1246
1251
  });
1247
1252
  }
1248
1253
 
1254
+ /**
1255
+ * Display registry-defined post-install messages for the modules installed in
1256
+ * this run. These are "action needed" notices (e.g. "run the bmad-loop-setup
1257
+ * skill") that the user must see to finish setup. They are defined via the
1258
+ * `post-install-message` property on a module's bmad-modules.yaml entry.
1259
+ *
1260
+ * Interactive installs require the user to acknowledge each message (press
1261
+ * Enter); non-interactive (--yes / skipPrompts) installs print the message
1262
+ * and continue without blocking, so CI/scripted installs don't hang.
1263
+ *
1264
+ * @param {Object} config - Install config (config.modules, config.skipPrompts)
1265
+ * @param {Object} officialModules - OfficialModules instance (carries the registry)
1266
+ */
1267
+ async _displayPostInstallMessages(config, officialModules) {
1268
+ const moduleCodes = config.modules || [];
1269
+ if (moduleCodes.length === 0) return;
1270
+
1271
+ const externalManager = officialModules.externalModuleManager;
1272
+ if (!externalManager) return;
1273
+
1274
+ const color = await prompts.getColor();
1275
+
1276
+ for (const code of moduleCodes) {
1277
+ let moduleInfo;
1278
+ try {
1279
+ moduleInfo = await externalManager.getModuleByCode(code);
1280
+ } catch {
1281
+ continue; // Built-in modules (core/bmm) aren't in the registry — skip.
1282
+ }
1283
+
1284
+ const message = moduleInfo && moduleInfo.postInstallMessage;
1285
+ if (!message) continue;
1286
+
1287
+ await prompts.box(String(message).trim(), `⚑ Action needed — ${moduleInfo.name || code}`, {
1288
+ rounded: true,
1289
+ formatBorder: color.yellow,
1290
+ });
1291
+
1292
+ // Interactive: require the user to acknowledge before continuing. Skip the
1293
+ // blocking prompt in non-interactive installs (the message is still shown).
1294
+ if (!config.skipPrompts) {
1295
+ await prompts.text({
1296
+ message: 'Press Enter to acknowledge',
1297
+ placeholder: '',
1298
+ default: '',
1299
+ });
1300
+ }
1301
+ }
1302
+ }
1303
+
1249
1304
  /**
1250
1305
  * Quick update method - preserves all settings and only prompts for new config fields
1251
1306
  * @param {Object} config - Configuration with directory
@@ -1262,10 +1317,32 @@ class Installer {
1262
1317
 
1263
1318
  // Detect existing installation
1264
1319
  const existingInstall = await ExistingInstall.detect(bmadDir);
1265
- const installedModules = existingInstall.moduleIds;
1266
1320
  const configuredIdes = existingInstall.ides;
1267
1321
  const projectRoot = path.dirname(bmadDir);
1268
1322
 
1323
+ // Resolve any legacy/aliased module codes (e.g. an install recorded as
1324
+ // `bauto` before the registry renamed it to `bmad-loop`) to their current
1325
+ // canonical code up front. Without this, a renamed module's old installs
1326
+ // would fall out of `availableModuleIds` below and get silently frozen
1327
+ // (see the `baut` → `automator` incident in CHANGELOG v6.7.1) instead of
1328
+ // migrating forward.
1329
+ const aliasMigrations = [];
1330
+ const seenModuleIds = new Set();
1331
+ const installedModules = [];
1332
+ for (const rawId of existingInstall.moduleIds) {
1333
+ const canonicalId = await this.externalModuleManager.resolveCanonicalCode(rawId);
1334
+ if (canonicalId !== rawId) {
1335
+ aliasMigrations.push({ from: rawId, to: canonicalId });
1336
+ }
1337
+ if (!seenModuleIds.has(canonicalId)) {
1338
+ seenModuleIds.add(canonicalId);
1339
+ installedModules.push(canonicalId);
1340
+ }
1341
+ }
1342
+ for (const { from, to } of aliasMigrations) {
1343
+ await prompts.log.info(`Migrating installed module '${from}' to its renamed successor '${to}'.`);
1344
+ }
1345
+
1269
1346
  // Get available modules (what we have source for)
1270
1347
  const availableModulesData = await new OfficialModules().listAvailable();
1271
1348
  const availableModules = [...availableModulesData.modules];
@@ -1410,6 +1487,18 @@ class Installer {
1410
1487
 
1411
1488
  await this.install(installConfig);
1412
1489
 
1490
+ // Now that the canonical module has been installed successfully, remove
1491
+ // the stale directory left behind under its old code so the two don't
1492
+ // coexist (e.g. `_bmad/bauto/` once `_bmad/bmad-loop/` is in place).
1493
+ for (const { from, to } of aliasMigrations) {
1494
+ if (!modulesToUpdate.includes(to)) continue; // new code wasn't actually installed this run
1495
+ const oldModuleDir = path.join(bmadDir, from);
1496
+ if (await fs.pathExists(oldModuleDir)) {
1497
+ await fs.remove(oldModuleDir);
1498
+ await prompts.log.success(`Removed legacy '${from}' directory after migrating to '${to}'.`);
1499
+ }
1500
+ }
1501
+
1413
1502
  return {
1414
1503
  success: true,
1415
1504
  moduleCount: modulesToUpdate.length,
@@ -77,7 +77,7 @@ async function checkUvEnvironment() {
77
77
  const detected = module.exports.detectUv();
78
78
 
79
79
  if (detected) {
80
- await prompts.log.success(`uv ${detected.version.raw} detected — ready to run BMAD's Python-powered scripts via \`uv run\`.`);
80
+ await prompts.log.success(`✅ Python UV check pass (uv ${detected.version.raw} detected).`);
81
81
  return { status: 'found', detected };
82
82
  }
83
83
 
@@ -62,6 +62,13 @@ class ExternalModuleManager {
62
62
  // ExternalModuleManager) sees resolutions made during install.
63
63
  static _resolutions = new Map();
64
64
 
65
+ // moduleCode → ResolvedModule (from PluginResolver). Populated for registry
66
+ // modules flagged `marketplace-plugin: true`, whose installable skills live
67
+ // outside a single module.yaml directory and must be resolved from
68
+ // .claude-plugin/marketplace.json. Shared across instances so install() can
69
+ // pick up the resolution computed during findExternalModuleSource.
70
+ static _pluginResolutions = new Map();
71
+
65
72
  constructor() {}
66
73
 
67
74
  /**
@@ -73,6 +80,15 @@ class ExternalModuleManager {
73
80
  return ExternalModuleManager._resolutions.get(moduleCode) || null;
74
81
  }
75
82
 
83
+ /**
84
+ * Get the cached marketplace-plugin resolution for a module (if any).
85
+ * @param {string} moduleCode
86
+ * @returns {Object|null} ResolvedModule from PluginResolver, or null
87
+ */
88
+ getPluginResolution(moduleCode) {
89
+ return ExternalModuleManager._pluginResolutions.get(moduleCode) || null;
90
+ }
91
+
76
92
  /**
77
93
  * Load the official modules registry from the bundled YAML file.
78
94
  * @returns {Object} Parsed YAML content with modules array
@@ -113,8 +129,16 @@ class ExternalModuleManager {
113
129
  npmPackage: mod.npm_package || mod.npmPackage || null,
114
130
  pluginName: mod.plugin_name || mod.pluginName || null,
115
131
  defaultChannel: normalizeChannelName(mod.default_channel || mod.defaultChannel) || 'stable',
132
+ deprecated: mod.deprecated === true,
133
+ deprecationMessage: mod.deprecation_message || mod['deprecation-message'] || mod.deprecationMessage || null,
134
+ marketplacePlugin: mod.marketplace_plugin === true || mod['marketplace-plugin'] === true || mod.marketplacePlugin === true,
135
+ postInstallMessage: mod.post_install_message || mod['post-install-message'] || mod.postInstallMessage || null,
116
136
  builtIn: mod.built_in === true,
117
137
  isExternal: mod.built_in !== true,
138
+ // Prior codes this module was registered under (e.g. `bmad-loop` was
139
+ // `bauto`). Lets a renamed module keep resolving existing installs
140
+ // instead of orphaning them — see getModuleByCode().
141
+ aliases: Array.isArray(mod.aliases) ? mod.aliases : [],
118
142
  };
119
143
  }
120
144
 
@@ -139,13 +163,27 @@ class ExternalModuleManager {
139
163
  }
140
164
 
141
165
  /**
142
- * Get module info by code
143
- * @param {string} code - The module code (e.g., 'cis')
166
+ * Get module info by code. Falls back to matching a registry entry's
167
+ * `aliases` list, so a module that was renamed (its `code` changed) still
168
+ * resolves for installs recorded under the prior code.
169
+ * @param {string} code - The module code (e.g., 'cis'), current or aliased
144
170
  * @returns {Object|null} Module info or null if not found
145
171
  */
146
172
  async getModuleByCode(code) {
147
173
  const modules = await this.listAvailable();
148
- return modules.find((m) => m.code === code) || null;
174
+ return modules.find((m) => m.code === code) || modules.find((m) => m.aliases.includes(code)) || null;
175
+ }
176
+
177
+ /**
178
+ * Resolve a possibly-legacy module code to its current canonical code.
179
+ * Returns the input unchanged if it doesn't match any registry entry or
180
+ * alias (e.g. a custom/unknown module).
181
+ * @param {string} code
182
+ * @returns {Promise<string>}
183
+ */
184
+ async resolveCanonicalCode(code) {
185
+ const info = await this.getModuleByCode(code);
186
+ return info ? info.code : code;
149
187
  }
150
188
 
151
189
  /**
@@ -176,6 +214,11 @@ class ExternalModuleManager {
176
214
  throw new Error(`External module '${moduleCode}' not found in the BMad registry`);
177
215
  }
178
216
 
217
+ // Normalize to the canonical code so cache dir, in-memory resolutions,
218
+ // and log/error text stay consistent even when called with a renamed
219
+ // module's prior alias (getModuleByCode resolves aliases above).
220
+ moduleCode = moduleInfo.code;
221
+
179
222
  const cacheDir = this.getExternalCacheDir();
180
223
  const moduleCacheDir = path.join(cacheDir, moduleCode);
181
224
  const silent = options.silent || false;
@@ -468,6 +511,22 @@ class ExternalModuleManager {
468
511
  return null;
469
512
  }
470
513
 
514
+ // Normalize to the canonical code — see cloneExternalModule for why.
515
+ moduleCode = moduleInfo.code;
516
+
517
+ // Marketplace-plugin modules (registry entries flagged `marketplace-plugin`)
518
+ // ship a .claude-plugin/marketplace.json and keep their module.yaml inside a
519
+ // skill's assets/ rather than in one directory alongside the skills. Resolve
520
+ // them through the PluginResolver (the same machinery custom-URL installs
521
+ // use) and return the directory that holds module.yaml so config/version/
522
+ // directory callers work; install() copies the resolved skill dirs.
523
+ if (moduleInfo.marketplacePlugin) {
524
+ const pluginMod = await this.resolvePluginModule(moduleCode, options);
525
+ if (pluginMod && pluginMod.moduleYamlPath) {
526
+ return path.dirname(pluginMod.moduleYamlPath);
527
+ }
528
+ }
529
+
471
530
  // Clone the external module repo
472
531
  const cloneDir = await this.cloneExternalModule(moduleCode, options);
473
532
 
@@ -521,6 +580,87 @@ class ExternalModuleManager {
521
580
  `The repository may have been restructured after this release was tagged.${channelHint}`,
522
581
  );
523
582
  }
583
+
584
+ /**
585
+ * Resolve a marketplace-plugin registry module to an installable plugin
586
+ * definition. Clones the repo (respecting the channel plan), reads its
587
+ * .claude-plugin/marketplace.json, and runs the PluginResolver against the
588
+ * plugin matching this module. The result (skillPaths + module.yaml +
589
+ * module-help.csv) is cached so install() can copy the resolved skill dirs.
590
+ *
591
+ * @param {string} moduleCode - Code of the external module
592
+ * @param {Object} options - Options passed to cloneExternalModule
593
+ * @returns {Promise<Object|null>} ResolvedModule from PluginResolver, or null
594
+ * when the module is not a marketplace plugin or cannot be resolved.
595
+ */
596
+ async resolvePluginModule(moduleCode, options = {}) {
597
+ const moduleInfo = await this.getModuleByCode(moduleCode);
598
+ if (!moduleInfo || moduleInfo.builtIn || !moduleInfo.marketplacePlugin) {
599
+ return null;
600
+ }
601
+
602
+ // Normalize to the canonical code — see cloneExternalModule for why.
603
+ moduleCode = moduleInfo.code;
604
+
605
+ const cloneDir = await this.cloneExternalModule(moduleCode, options);
606
+
607
+ const marketplacePath = path.join(cloneDir, '.claude-plugin', 'marketplace.json');
608
+ if (!(await fs.pathExists(marketplacePath))) {
609
+ return null;
610
+ }
611
+
612
+ let marketplace;
613
+ try {
614
+ marketplace = JSON.parse(await fs.readFile(marketplacePath, 'utf8'));
615
+ } catch {
616
+ return null;
617
+ }
618
+
619
+ const plugins = Array.isArray(marketplace.plugins) ? marketplace.plugins : [];
620
+ if (plugins.length === 0) {
621
+ return null;
622
+ }
623
+
624
+ // Prefer the plugin whose name matches the registry's plugin_name (or code),
625
+ // falling back to any plugin that declares skills.
626
+ const preferredName = moduleInfo.pluginName || moduleInfo.code;
627
+ const ordered = [...plugins].sort((a, b) => {
628
+ const am = a?.name === preferredName ? 0 : 1;
629
+ const bm = b?.name === preferredName ? 0 : 1;
630
+ return am - bm;
631
+ });
632
+
633
+ const { PluginResolver } = require('./plugin-resolver');
634
+ const resolver = new PluginResolver();
635
+
636
+ for (const plugin of ordered) {
637
+ if (!plugin || !Array.isArray(plugin.skills) || plugin.skills.length === 0) {
638
+ continue;
639
+ }
640
+ let resolvedMods;
641
+ try {
642
+ resolvedMods = await resolver.resolve(cloneDir, plugin);
643
+ } catch {
644
+ continue;
645
+ }
646
+ if (!resolvedMods || resolvedMods.length === 0) {
647
+ continue;
648
+ }
649
+ // Match the registry code, then the preferred plugin name, then accept a
650
+ // lone resolved module.
651
+ const match =
652
+ resolvedMods.find((mod) => mod.code === moduleCode) ||
653
+ resolvedMods.find((mod) => mod.code === preferredName) ||
654
+ (resolvedMods.length === 1 ? resolvedMods[0] : null);
655
+ if (match) {
656
+ match.repoUrl = moduleInfo.url;
657
+ ExternalModuleManager._pluginResolutions.set(moduleCode, match);
658
+ return match;
659
+ }
660
+ }
661
+
662
+ return null;
663
+ }
524
664
  cachedModules = null;
525
665
  }
526
666
 
@@ -277,7 +277,29 @@ class OfficialModules {
277
277
  await fs.remove(targetPath);
278
278
  }
279
279
 
280
- await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
280
+ // Marketplace-plugin registry modules keep their installable skills outside
281
+ // the directory that holds module.yaml (sourcePath points at the -setup
282
+ // skill's assets/), so they cannot be installed by copying sourcePath. Copy
283
+ // the resolved skill directories instead, matching how custom marketplace
284
+ // installs lay out a module. Everything else (manifest, version info) flows
285
+ // through the standard external-module path below.
286
+ const moduleInfo = await this.externalModuleManager.getModuleByCode(moduleName);
287
+ if (moduleInfo && moduleInfo.marketplacePlugin) {
288
+ const pluginResolution = this.externalModuleManager.getPluginResolution(moduleName);
289
+ // Fail loud: copying sourcePath here would install only the -setup skill's
290
+ // assets/ (module.yaml + module-help.csv) and none of the skills — a
291
+ // silent, broken partial install. Abort instead.
292
+ if (!pluginResolution || !Array.isArray(pluginResolution.skillPaths) || pluginResolution.skillPaths.length === 0) {
293
+ throw new Error(
294
+ `Module '${moduleName}' is registered as a marketplace plugin but its skills could not be resolved ` +
295
+ `from .claude-plugin/marketplace.json (missing or malformed on the selected channel). ` +
296
+ `Aborting to avoid a partial install with no skills.`,
297
+ );
298
+ }
299
+ await this._copyResolvedSkills(pluginResolution, targetPath, fileTrackingCallback, options.moduleConfig);
300
+ } else {
301
+ await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
302
+ }
281
303
 
282
304
  if (!options.skipModuleInstaller) {
283
305
  await this.createModuleDirectories(moduleName, bmadDir, options);
@@ -304,41 +326,66 @@ class OfficialModules {
304
326
  }
305
327
 
306
328
  /**
307
- * Install a module from a PluginResolver resolution result.
308
- * Copies specific skill directories and places module-help.csv at the target root.
329
+ * Lay out a PluginResolver resolution on disk: copy each resolved skill
330
+ * directory (flattened by leaf name) into targetPath and place module-help.csv
331
+ * at the module root. Shared by both custom marketplace installs
332
+ * (installFromResolution) and official marketplace-plugin registry installs
333
+ * (install), so the two paths cannot drift.
309
334
  * @param {Object} resolved - ResolvedModule from PluginResolver
310
- * @param {string} bmadDir - Target bmad directory
335
+ * @param {string} targetPath - Destination module directory (e.g. bmadDir/<code>)
311
336
  * @param {Function} fileTrackingCallback - Optional callback to track installed files
312
- * @param {Object} options - Installation options
337
+ * @param {Object} moduleConfig - Module configuration passed to copy filtering
313
338
  */
314
- async installFromResolution(resolved, bmadDir, fileTrackingCallback = null, options = {}) {
315
- const targetPath = path.join(bmadDir, resolved.code);
316
-
317
- if (await fs.pathExists(targetPath)) {
318
- await fs.remove(targetPath);
319
- }
320
-
339
+ async _copyResolvedSkills(resolved, targetPath, fileTrackingCallback = null, moduleConfig = {}) {
321
340
  await fs.ensureDir(targetPath);
322
341
 
323
- // Copy each skill directory, flattened by leaf name
342
+ // Copy each skill directory, flattened by leaf name. Leaf names must be
343
+ // unique — two skills that flatten to the same directory would silently
344
+ // overwrite each other, so fail loud instead.
345
+ const seenLeaves = new Map();
324
346
  for (const skillPath of resolved.skillPaths) {
325
347
  const skillDirName = path.basename(skillPath);
348
+ if (seenLeaves.has(skillDirName)) {
349
+ throw new Error(
350
+ `Cannot install module '${resolved.code}': skill directories '${seenLeaves.get(skillDirName)}' and ` +
351
+ `'${skillPath}' share the leaf name '${skillDirName}' and would overwrite each other. ` +
352
+ `Skill directory names must be unique.`,
353
+ );
354
+ }
355
+ seenLeaves.set(skillDirName, skillPath);
326
356
  const skillTarget = path.join(targetPath, skillDirName);
327
- await this.copyModuleWithFiltering(skillPath, skillTarget, fileTrackingCallback, options.moduleConfig);
357
+ await this.copyModuleWithFiltering(skillPath, skillTarget, fileTrackingCallback, moduleConfig);
328
358
  }
329
359
 
330
- // Place module-help.csv at the module root
360
+ // Place module-help.csv at the module root.
361
+ const helpTarget = path.join(targetPath, 'module-help.csv');
331
362
  if (resolved.moduleHelpCsvPath) {
332
- // Strategies 1-4: copy the existing file
333
- const helpTarget = path.join(targetPath, 'module-help.csv');
363
+ // Strategies 1-4: copy the existing file.
334
364
  await fs.copy(resolved.moduleHelpCsvPath, helpTarget, { overwrite: true });
335
365
  if (fileTrackingCallback) fileTrackingCallback(helpTarget);
336
366
  } else if (resolved.synthesizedHelpCsv) {
337
- // Strategy 5: write synthesized content
338
- const helpTarget = path.join(targetPath, 'module-help.csv');
367
+ // Strategy 5: write synthesized content.
339
368
  await fs.writeFile(helpTarget, resolved.synthesizedHelpCsv, 'utf8');
340
369
  if (fileTrackingCallback) fileTrackingCallback(helpTarget);
341
370
  }
371
+ }
372
+
373
+ /**
374
+ * Install a module from a PluginResolver resolution result.
375
+ * Copies specific skill directories and places module-help.csv at the target root.
376
+ * @param {Object} resolved - ResolvedModule from PluginResolver
377
+ * @param {string} bmadDir - Target bmad directory
378
+ * @param {Function} fileTrackingCallback - Optional callback to track installed files
379
+ * @param {Object} options - Installation options
380
+ */
381
+ async installFromResolution(resolved, bmadDir, fileTrackingCallback = null, options = {}) {
382
+ const targetPath = path.join(bmadDir, resolved.code);
383
+
384
+ if (await fs.pathExists(targetPath)) {
385
+ await fs.remove(targetPath);
386
+ }
387
+
388
+ await this._copyResolvedSkills(resolved, targetPath, fileTrackingCallback, options.moduleConfig);
342
389
 
343
390
  // Create directories declared in module.yaml (strategies 1-4 may have these)
344
391
  if (!options.skipModuleInstaller) {
@@ -133,7 +133,12 @@ class UI {
133
133
  for (const moduleId of installedModuleIds) {
134
134
  if (moduleId === 'core') continue;
135
135
  if (!selectedSet.has(moduleId) && !options.preserveUnselected) continue;
136
- if (officialCodes.has(moduleId)) continue;
136
+ // Resolve a possibly-renamed module code (e.g. `bauto` -> `bmad-loop`)
137
+ // before checking availability, so a registry rename doesn't freeze
138
+ // the install here the way it would have prior to the alias support
139
+ // in ExternalModuleManager.getModuleByCode().
140
+ const canonicalId = await externalManager.resolveCanonicalCode(moduleId);
141
+ if (officialCodes.has(canonicalId)) continue;
137
142
 
138
143
  const customSource = await customMgr.findModuleSourceByCode(moduleId, { bmadDir });
139
144
  if (!customSource) {
@@ -944,25 +949,32 @@ class UI {
944
949
  }
945
950
  }
946
951
 
947
- // Add external registry modules (skip built-in duplicates)
948
- const externalRegistryModules = registryModules.filter((mod) => !mod.builtIn && !builtInCodes.has(mod.code));
952
+ // Add external registry modules (skip built-in duplicates and deprecated
953
+ // modules that are not already installed — deprecated modules stay visible
954
+ // only so existing users can continue to manage them).
955
+ const externalRegistryModules = registryModules.filter(
956
+ (mod) => !mod.builtIn && !builtInCodes.has(mod.code) && (!mod.deprecated || installedModuleIds.has(mod.code)),
957
+ );
949
958
  let externalRegistryEntries = [];
950
959
  if (externalRegistryModules.length > 0) {
951
960
  const spinner = await prompts.spinner();
952
961
  spinner.start('Checking latest module versions...');
953
962
 
954
963
  externalRegistryEntries = await Promise.all(
955
- externalRegistryModules.map(async (mod) => ({
956
- code: mod.code,
957
- entry: await buildModuleEntry(
964
+ externalRegistryModules.map(async (mod) => {
965
+ const entry = await buildModuleEntry(
958
966
  mod.code,
959
967
  mod.name,
960
968
  mod.description,
961
969
  mod.defaultSelected,
962
970
  mod.url || null,
963
971
  mod.defaultChannel || null,
964
- ),
965
- })),
972
+ );
973
+ if (mod.deprecated && mod.deprecationMessage) {
974
+ entry.hint = entry.hint ? `${entry.hint} — ${mod.deprecationMessage}` : mod.deprecationMessage;
975
+ }
976
+ return { code: mod.code, entry };
977
+ }),
966
978
  );
967
979
 
968
980
  spinner.stop('Checked latest module versions.');