Compare commits
15 Commits
4d4344f9ce
...
ddc34ea26f
| Author | SHA1 | Date |
|---|---|---|
|
|
ddc34ea26f | |
|
|
1da6bf80df | |
|
|
b4f47e1f05 | |
|
|
a08522631b | |
|
|
61bba10cb3 | |
|
|
9bcafdef51 | |
|
|
92498ebb52 | |
|
|
fdfe23fc22 | |
|
|
f32d1d4e8d | |
|
|
f9e7d65cf9 | |
|
|
bfdeef0453 | |
|
|
3ac8736756 | |
|
|
417fc44a98 | |
|
|
4e96a50515 | |
|
|
ad77c8e1c6 |
|
|
@ -1,5 +1,11 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v6.7.1 - 2026-05-18
|
||||||
|
|
||||||
|
### 🐛 Fixes
|
||||||
|
|
||||||
|
* **Installer no longer errors when a previously installed module's source can no longer be found** — In v6.7.0 the experimental BMad Automator module's installer code (the value used for its `_bmad/<code>/` folder and manifest entry) was renamed from `baut` to `automator`. Anyone who had installed it under the old `baut` code saw `quick-update` fail with `Source for module 'baut' is not available` and risked having the existing install removed. The installer now detects installed modules that can no longer be resolved from any source, leaves them in place untouched, and continues the update. If you previously installed it as `baut` and want the renamed `automator` version, run `npx bmad-method install`, choose **Modify BMAD Installation**, and reselect **BMad Automator**; the old `_bmad/baut/` directory can then be deleted manually
|
||||||
|
|
||||||
## v6.7.0 - 2026-05-17
|
## v6.7.0 - 2026-05-17
|
||||||
|
|
||||||
### ✨ Headline
|
### ✨ Headline
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "6.7.0",
|
"version": "6.7.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "6.7.0",
|
"version": "6.7.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/core": "^1.3.1",
|
"@clack/core": "^1.3.1",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "6.7.0",
|
"version": "6.7.1",
|
||||||
"description": "Breakthrough Method of Agile AI-driven Development",
|
"description": "Breakthrough Method of Agile AI-driven Development",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"agile",
|
"agile",
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,70 @@
|
||||||
num,category,method_name,description,output_pattern
|
num,category,method_name,description,output_pattern
|
||||||
1,collaboration,Stakeholder Round Table,Convene multiple personas to contribute diverse perspectives - essential for requirements gathering and finding balanced solutions across competing interests,perspectives → synthesis → alignment
|
1,advanced,Tree of Thoughts,Explore multiple reasoning paths simultaneously then evaluate and select the best - perfect for complex problems with multiple valid approaches,paths → evaluation → selection
|
||||||
2,collaboration,Expert Panel Review,Assemble domain experts for deep specialized analysis - ideal when technical depth and peer review quality are needed,expert views → consensus → recommendations
|
2,advanced,Graph of Thoughts,Model reasoning as an interconnected network of ideas to reveal hidden relationships - ideal for systems thinking and discovering emergent patterns,nodes → connections → patterns
|
||||||
3,collaboration,Debate Club Showdown,Two personas argue opposing positions while a moderator scores points - great for exploring controversial decisions and finding middle ground,thesis → antithesis → synthesis
|
3,advanced,Thread of Thought,Maintain coherent reasoning across long contexts by weaving a continuous narrative thread - essential for RAG systems and maintaining consistency,context → thread → synthesis
|
||||||
4,collaboration,User Persona Focus Group,Gather your product's user personas to react to proposals and share frustrations - essential for validating features and discovering unmet needs,reactions → concerns → priorities
|
4,advanced,Self-Consistency Validation,Generate multiple independent approaches then compare for consistency - crucial for high-stakes decisions where verification matters,approaches → comparison → consensus
|
||||||
5,collaboration,Time Traveler Council,Past-you and future-you advise present-you on decisions - powerful for gaining perspective on long-term consequences vs short-term pressures,past wisdom → present choice → future impact
|
5,advanced,Meta-Prompting Analysis,Step back to analyze the approach structure and methodology itself - valuable for optimizing prompts and improving problem-solving,current → analysis → optimization
|
||||||
6,collaboration,Cross-Functional War Room,Product manager + engineer + designer tackle a problem together - reveals trade-offs between feasibility desirability and viability,constraints → trade-offs → balanced solution
|
6,advanced,Reasoning via Planning,Build a reasoning tree guided by world models and goal states - excellent for strategic planning and sequential decision-making,model → planning → strategy
|
||||||
7,collaboration,Mentor and Apprentice,Senior expert teaches junior while junior asks naive questions - surfaces hidden assumptions through teaching,explanation → questions → deeper understanding
|
7,advanced,Chain-of-Thought Scaffolding,Force explicit intermediate reasoning steps before any conclusion — prevents intuitive leaps that skip flawed logic,premise → step → step → conclusion
|
||||||
8,collaboration,Good Cop Bad Cop,Supportive persona and critical persona alternate - finds both strengths to build on and weaknesses to address,encouragement → criticism → balanced view
|
8,advanced,Few-Shot Exemplar Priming,Provide 2-3 worked examples of the desired reasoning pattern before the real task — aligns output format and depth through demonstration,examples → pattern recognition → application
|
||||||
9,collaboration,Improv Yes-And,Multiple personas build on each other's ideas without blocking - generates unexpected creative directions through collaborative building,idea → build → build → surprising result
|
9,collaboration,Stakeholder Round Table,Convene multiple personas to contribute diverse perspectives - essential for requirements gathering and finding balanced solutions across competing interests,perspectives → synthesis → alignment
|
||||||
10,collaboration,Customer Support Theater,Angry customer and support rep roleplay to find pain points - reveals real user frustrations and service gaps,complaint → investigation → resolution → prevention
|
10,collaboration,Expert Panel Review,Assemble domain experts for deep specialized analysis - ideal when technical depth and peer review quality are needed,expert views → consensus → recommendations
|
||||||
11,advanced,Tree of Thoughts,Explore multiple reasoning paths simultaneously then evaluate and select the best - perfect for complex problems with multiple valid approaches,paths → evaluation → selection
|
11,collaboration,Debate Club Showdown,Two personas argue opposing positions while a moderator scores points - great for exploring controversial decisions and finding middle ground,thesis → antithesis → synthesis
|
||||||
12,advanced,Graph of Thoughts,Model reasoning as an interconnected network of ideas to reveal hidden relationships - ideal for systems thinking and discovering emergent patterns,nodes → connections → patterns
|
12,collaboration,User Persona Focus Group,Gather your product's user personas to react to proposals and share frustrations - essential for validating features and discovering unmet needs,reactions → concerns → priorities
|
||||||
13,advanced,Thread of Thought,Maintain coherent reasoning across long contexts by weaving a continuous narrative thread - essential for RAG systems and maintaining consistency,context → thread → synthesis
|
13,collaboration,Time Traveler Council,Past-you and future-you advise present-you on decisions - powerful for gaining perspective on long-term consequences vs short-term pressures,past wisdom → present choice → future impact
|
||||||
14,advanced,Self-Consistency Validation,Generate multiple independent approaches then compare for consistency - crucial for high-stakes decisions where verification matters,approaches → comparison → consensus
|
14,collaboration,Cross-Functional War Room,Product manager + engineer + designer tackle a problem together - reveals trade-offs between feasibility desirability and viability,constraints → trade-offs → balanced solution
|
||||||
15,advanced,Meta-Prompting Analysis,Step back to analyze the approach structure and methodology itself - valuable for optimizing prompts and improving problem-solving,current → analysis → optimization
|
15,collaboration,Mentor and Apprentice,Senior expert teaches junior while junior asks naive questions - surfaces hidden assumptions through teaching,explanation → questions → deeper understanding
|
||||||
16,advanced,Reasoning via Planning,Build a reasoning tree guided by world models and goal states - excellent for strategic planning and sequential decision-making,model → planning → strategy
|
16,collaboration,Good Cop Bad Cop,Supportive persona and critical persona alternate - finds both strengths to build on and weaknesses to address,encouragement → criticism → balanced view
|
||||||
17,competitive,Red Team vs Blue Team,Adversarial attack-defend analysis to find vulnerabilities - critical for security testing and building robust solutions,defense → attack → hardening
|
17,collaboration,Improv Yes-And,Multiple personas build on each other's ideas without blocking - generates unexpected creative directions through collaborative building,idea → build → build → surprising result
|
||||||
18,competitive,Shark Tank Pitch,Entrepreneur pitches to skeptical investors who poke holes - stress-tests business viability and forces clarity on value proposition,pitch → challenges → refinement
|
18,collaboration,Customer Support Theater,Angry customer and support rep roleplay to find pain points - reveals real user frustrations and service gaps,complaint → investigation → resolution → prevention
|
||||||
19,competitive,Code Review Gauntlet,Senior devs with different philosophies review the same code - surfaces style debates and finds consensus on best practices,reviews → debates → standards
|
19,collaboration,Six Thinking Hats,Rotate through six modes (facts - feelings - caution - optimism - creativity - process) to ensure a group covers every angle without crosstalk,white → red → black → yellow → green → blue
|
||||||
20,technical,Architecture Decision Records,Multiple architect personas propose and debate architectural choices with explicit trade-offs - ensures decisions are well-reasoned and documented,options → trade-offs → decision → rationale
|
20,collaboration,Delphi Method,Experts give independent estimates - see anonymized results - then revise — converges on calibrated group judgment while avoiding anchoring bias,independent estimates → reveal → revise → converge
|
||||||
21,technical,Rubber Duck Debugging Evolved,Explain your code to progressively more technical ducks until you find the bug - forces clarity at multiple abstraction levels,simple → detailed → technical → aha
|
21,competitive,Red Team vs Blue Team,Adversarial attack-defend analysis to find vulnerabilities - critical for security testing and building robust solutions,defense → attack → hardening
|
||||||
22,technical,Algorithm Olympics,Multiple approaches compete on the same problem with benchmarks - finds optimal solution through direct comparison,implementations → benchmarks → winner
|
22,competitive,Shark Tank Pitch,Entrepreneur pitches to skeptical investors who poke holes - stress-tests business viability and forces clarity on value proposition,pitch → challenges → refinement
|
||||||
23,technical,Security Audit Personas,Hacker + defender + auditor examine system from different threat models - comprehensive security review from multiple angles,vulnerabilities → defenses → compliance
|
23,competitive,Code Review Gauntlet,Senior devs with different philosophies review the same code - surfaces style debates and finds consensus on best practices,reviews → debates → standards
|
||||||
24,technical,Performance Profiler Panel,Database expert + frontend specialist + DevOps engineer diagnose slowness - finds bottlenecks across the full stack,symptoms → analysis → optimizations
|
24,core,First Principles Analysis,Strip away assumptions to rebuild from fundamental truths - breakthrough technique for innovation and solving impossible problems,assumptions → truths → new approach
|
||||||
25,creative,SCAMPER Method,Apply seven creativity lenses (Substitute/Combine/Adapt/Modify/Put/Eliminate/Reverse) - systematic ideation for product innovation,S→C→A→M→P→E→R
|
25,core,5 Whys Deep Dive,Repeatedly ask why to drill down to root causes - simple but powerful for understanding failures,why chain → root cause → solution
|
||||||
26,creative,Reverse Engineering,Work backwards from desired outcome to find implementation path - powerful for goal achievement and understanding endpoints,end state → steps backward → path forward
|
26,core,Socratic Questioning,Use targeted questions to reveal hidden assumptions and guide discovery - excellent for teaching and self-discovery,questions → revelations → understanding
|
||||||
27,creative,What If Scenarios,Explore alternative realities to understand possibilities and implications - valuable for contingency planning and exploration,scenarios → implications → insights
|
27,core,Critique and Refine,Systematic review to identify strengths and weaknesses then improve - standard quality check for drafts,strengths/weaknesses → improvements → refined
|
||||||
28,creative,Random Input Stimulus,Inject unrelated concepts to spark unexpected connections - breaks creative blocks through forced lateral thinking,random word → associations → novel ideas
|
28,core,Explain Reasoning,Walk through step-by-step thinking to show how conclusions were reached - crucial for transparency,steps → logic → conclusion
|
||||||
29,creative,Exquisite Corpse Brainstorm,Each persona adds to the idea seeing only the previous contribution - generates surprising combinations through constrained collaboration,contribution → handoff → contribution → surprise
|
29,core,Expand or Contract for Audience,Dynamically adjust detail level and technical depth for target audience - matches content to reader capabilities,audience → adjustments → refined content
|
||||||
30,creative,Genre Mashup,Combine two unrelated domains to find fresh approaches - innovation through unexpected cross-pollination,domain A + domain B → hybrid insights
|
30,core,Second-Order Thinking,Think beyond immediate consequences to anticipate cascading effects and long-term implications - essential for strategic decisions where first-order solutions create hidden downstream problems,action → consequences → second-order effects → informed choice
|
||||||
31,research,Literature Review Personas,Optimist researcher + skeptic researcher + synthesizer review sources - balanced assessment of evidence quality,sources → critiques → synthesis
|
31,core,Inversion Analysis,Flip the problem by asking what would guarantee failure instead of how to succeed - reveals hidden obstacles and blind spots by approaching challenges from the opposite direction,goal → invert → failure paths → avoidance → solution
|
||||||
32,research,Thesis Defense Simulation,Student defends hypothesis against committee with different concerns - stress-tests research methodology and conclusions,thesis → challenges → defense → refinements
|
32,core,Problem Decomposition,Break a complex problem into independent sub-problems - solve each - then reassemble — essential when a task is too large or tangled to tackle whole,whole → parts → solutions → reassembly
|
||||||
33,research,Comparative Analysis Matrix,Multiple analysts evaluate options against weighted criteria - structured decision-making with explicit scoring,options → criteria → scores → recommendation
|
33,core,Analogy Mapping,Find a well-understood parallel domain and transfer its structure to the current problem — unlocks insight by borrowing proven mental models,source domain → mapping → target insight
|
||||||
34,risk,Pre-mortem Analysis,Imagine future failure then work backwards to prevent it - powerful technique for risk mitigation before major launches,failure scenario → causes → prevention
|
34,core,Steelmanning,Construct the strongest possible version of an opposing argument before responding — builds credibility and catches blind spots that strawmanning misses,opposing view → strongest form → honest rebuttal
|
||||||
35,risk,Failure Mode Analysis,Systematically explore how each component could fail - critical for reliability engineering and safety-critical systems,components → failures → prevention
|
35,creative,SCAMPER Method,Apply seven creativity lenses (Substitute/Combine/Adapt/Modify/Put/Eliminate/Reverse) - systematic ideation for product innovation,S→C→A→M→P→E→R
|
||||||
36,risk,Challenge from Critical Perspective,Play devil's advocate to stress-test ideas and find weaknesses - essential for overcoming groupthink,assumptions → challenges → strengthening
|
36,creative,Reverse Engineering,Work backwards from desired outcome to find implementation path - powerful for goal achievement and understanding endpoints,end state → steps backward → path forward
|
||||||
37,risk,Identify Potential Risks,Brainstorm what could go wrong across all categories - fundamental for project planning and deployment preparation,categories → risks → mitigations
|
37,creative,What If Scenarios,Explore alternative realities to understand possibilities and implications - valuable for contingency planning and exploration,scenarios → implications → insights
|
||||||
38,risk,Chaos Monkey Scenarios,Deliberately break things to test resilience and recovery - ensures systems handle failures gracefully,break → observe → harden
|
38,creative,Random Input Stimulus,Inject unrelated concepts to spark unexpected connections - breaks creative blocks through forced lateral thinking,random word → associations → novel ideas
|
||||||
39,core,First Principles Analysis,Strip away assumptions to rebuild from fundamental truths - breakthrough technique for innovation and solving impossible problems,assumptions → truths → new approach
|
39,creative,Exquisite Corpse Brainstorm,Each persona adds to the idea seeing only the previous contribution - generates surprising combinations through constrained collaboration,contribution → handoff → contribution → surprise
|
||||||
40,core,5 Whys Deep Dive,Repeatedly ask why to drill down to root causes - simple but powerful for understanding failures,why chain → root cause → solution
|
40,creative,Genre Mashup,Combine two unrelated domains to find fresh approaches - innovation through unexpected cross-pollination,domain A + domain B → hybrid insights
|
||||||
41,core,Socratic Questioning,Use targeted questions to reveal hidden assumptions and guide discovery - excellent for teaching and self-discovery,questions → revelations → understanding
|
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
|
||||||
42,core,Critique and Refine,Systematic review to identify strengths and weaknesses then improve - standard quality check for drafts,strengths/weaknesses → improvements → refined
|
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
|
||||||
43,core,Explain Reasoning,Walk through step-by-step thinking to show how conclusions were reached - crucial for transparency,steps → logic → conclusion
|
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 ↔ abstract → right level
|
||||||
44,core,Expand or Contract for Audience,Dynamically adjust detail level and technical depth for target audience - matches content to reader capabilities,audience → adjustments → refined content
|
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
|
||||||
45,learning,Feynman Technique,Explain complex concepts simply as if teaching a child - the ultimate test of true understanding,complex → simple → gaps → mastery
|
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 A → B → C → gaps found
|
||||||
46,learning,Active Recall Testing,Test understanding without references to verify true knowledge - essential for identifying gaps,test → gaps → reinforcement
|
46,learning,Feynman Technique,Explain complex concepts simply as if teaching a child - the ultimate test of true understanding,complex → simple → gaps → mastery
|
||||||
47,philosophical,Occam's Razor Application,Find the simplest sufficient explanation by eliminating unnecessary complexity - essential for debugging,options → simplification → selection
|
47,learning,Active Recall Testing,Test understanding without references to verify true knowledge - essential for identifying gaps,test → gaps → reinforcement
|
||||||
48,philosophical,Trolley Problem Variations,Explore ethical trade-offs through moral dilemmas - valuable for understanding values and difficult decisions,dilemma → analysis → decision
|
48,learning,Deliberate Practice Loop,Identify a specific sub-skill - drill it with immediate feedback - adjust - repeat — targeted improvement beats general repetition,isolate → drill → feedback → adjust → repeat
|
||||||
49,retrospective,Hindsight Reflection,Imagine looking back from the future to gain perspective - powerful for project reviews,future view → insights → application
|
49,philosophical,Occam's Razor Application,Find the simplest sufficient explanation by eliminating unnecessary complexity - essential for debugging,options → simplification → selection
|
||||||
50,retrospective,Lessons Learned Extraction,Systematically identify key takeaways and actionable improvements - essential for continuous improvement,experience → lessons → actions
|
50,philosophical,Trolley Problem Variations,Explore ethical trade-offs through moral dilemmas - valuable for understanding values and difficult decisions,dilemma → analysis → decision
|
||||||
|
51,research,Literature Review Personas,Optimist researcher + skeptic researcher + synthesizer review sources - balanced assessment of evidence quality,sources → critiques → synthesis
|
||||||
|
52,research,Thesis Defense Simulation,Student defends hypothesis against committee with different concerns - stress-tests research methodology and conclusions,thesis → challenges → defense → refinements
|
||||||
|
53,research,Comparative Analysis Matrix,Multiple analysts evaluate options against weighted criteria - structured decision-making with explicit scoring,options → criteria → scores → recommendation
|
||||||
|
54,research,Source Triangulation,Require at least three independent source types (quantitative - qualitative - expert) before accepting a claim — guards against single-source bias,claim → source A → source B → source C → confidence rating
|
||||||
|
55,retrospective,Hindsight Reflection,Imagine looking back from the future to gain perspective - powerful for project reviews,future view → insights → application
|
||||||
|
56,retrospective,Lessons Learned Extraction,Systematically identify key takeaways and actionable improvements - essential for continuous improvement,experience → lessons → actions
|
||||||
|
57,risk,Pre-mortem Analysis,Imagine future failure then work backwards to prevent it - powerful technique for risk mitigation before major launches,failure scenario → causes → prevention
|
||||||
|
58,risk,Failure Mode Analysis,Systematically explore how each component could fail - critical for reliability engineering and safety-critical systems,components → failures → prevention
|
||||||
|
59,risk,Challenge from Critical Perspective,Play devil's advocate to stress-test ideas and find weaknesses - essential for overcoming groupthink,assumptions → challenges → strengthening
|
||||||
|
60,risk,Identify Potential Risks,Brainstorm what could go wrong across all categories - fundamental for project planning and deployment preparation,categories → risks → mitigations
|
||||||
|
61,risk,Chaos Monkey Scenarios,Deliberately break things to test resilience and recovery - ensures systems handle failures gracefully,break → observe → harden
|
||||||
|
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,list → rate → stress-test → shore up
|
||||||
|
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
|
||||||
|
64,technical,Architecture Decision Records,Multiple architect personas propose and debate architectural choices with explicit trade-offs - ensures decisions are well-reasoned and documented,options → trade-offs → decision → rationale
|
||||||
|
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,simple → detailed → technical → aha
|
||||||
|
66,technical,Algorithm Olympics,Multiple approaches compete on the same problem with benchmarks - finds optimal solution through direct comparison,implementations → benchmarks → winner
|
||||||
|
67,technical,Security Audit Personas,Hacker + defender + auditor examine system from different threat models - comprehensive security review from multiple angles,vulnerabilities → defenses → compliance
|
||||||
|
68,technical,Performance Profiler Panel,Database expert + frontend specialist + DevOps engineer diagnose slowness - finds bottlenecks across the full stack,symptoms → analysis → optimizations
|
||||||
|
69,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
|
||||||
|
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ class Installer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingInstall.installed) {
|
if (existingInstall.installed) {
|
||||||
await this._removeDeselectedModules(existingInstall, config, paths);
|
await this._removeDeselectedModules(existingInstall, config, paths, originalConfig._preserveModules || []);
|
||||||
updateState = await this._prepareUpdateState(paths, config, existingInstall, officialModules);
|
updateState = await this._prepareUpdateState(paths, config, existingInstall, officialModules);
|
||||||
await this._removeDeselectedIdes(existingInstall, config, paths);
|
await this._removeDeselectedIdes(existingInstall, config, paths);
|
||||||
}
|
}
|
||||||
|
|
@ -76,25 +76,23 @@ class Installer {
|
||||||
const results = [];
|
const results = [];
|
||||||
const addResult = (step, status, detail = '', meta = {}) => results.push({ step, status, detail, ...meta });
|
const addResult = (step, status, detail = '', meta = {}) => results.push({ step, status, detail, ...meta });
|
||||||
|
|
||||||
// Capture previously installed skill IDs before they get overwritten
|
// Capture previously installed skill rows before they get overwritten
|
||||||
const previousSkillIds = new Set();
|
const preservedModules = originalConfig._preserveModules || [];
|
||||||
const prevCsvPath = path.join(paths.bmadDir, '_config', 'skill-manifest.csv');
|
const previousSkillManifestRows = await this._readSkillManifestRows(paths.bmadDir);
|
||||||
if (await fs.pathExists(prevCsvPath)) {
|
const previousSkillIds = this._getPreviousSkillIdsForCleanup(previousSkillManifestRows, preservedModules);
|
||||||
try {
|
|
||||||
const csvParse = require('csv-parse/sync');
|
|
||||||
const content = await fs.readFile(prevCsvPath, 'utf8');
|
|
||||||
const records = csvParse.parse(content, { columns: true, skip_empty_lines: true });
|
|
||||||
for (const r of records) {
|
|
||||||
if (r.canonicalId) previousSkillIds.add(r.canonicalId);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
await prompts.log.warn(`Failed to parse skill-manifest.csv: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const allModules = config.modules || [];
|
const allModules = config.modules || [];
|
||||||
|
|
||||||
await this._installAndConfigure(config, originalConfig, paths, allModules, allModules, addResult, officialModules);
|
await this._installAndConfigure(
|
||||||
|
config,
|
||||||
|
originalConfig,
|
||||||
|
paths,
|
||||||
|
allModules,
|
||||||
|
allModules,
|
||||||
|
addResult,
|
||||||
|
officialModules,
|
||||||
|
previousSkillManifestRows,
|
||||||
|
);
|
||||||
|
|
||||||
await this._setupIdes(config, allModules, paths, addResult, previousSkillIds);
|
await this._setupIdes(config, allModules, paths, addResult, previousSkillIds);
|
||||||
|
|
||||||
|
|
@ -144,10 +142,11 @@ class Installer {
|
||||||
* Remove modules that were previously installed but are no longer selected.
|
* Remove modules that were previously installed but are no longer selected.
|
||||||
* No confirmation — the user's module selection is the decision.
|
* No confirmation — the user's module selection is the decision.
|
||||||
*/
|
*/
|
||||||
async _removeDeselectedModules(existingInstall, config, paths) {
|
async _removeDeselectedModules(existingInstall, config, paths, preservedModules = []) {
|
||||||
const previouslyInstalled = new Set(existingInstall.moduleIds);
|
const previouslyInstalled = new Set(existingInstall.moduleIds);
|
||||||
const newlySelected = new Set(config.modules || []);
|
const newlySelected = new Set(config.modules || []);
|
||||||
const toRemove = [...previouslyInstalled].filter((m) => !newlySelected.has(m) && m !== 'core');
|
const preserved = new Set(preservedModules);
|
||||||
|
const toRemove = [...previouslyInstalled].filter((m) => !newlySelected.has(m) && m !== 'core' && !preserved.has(m));
|
||||||
|
|
||||||
for (const moduleId of toRemove) {
|
for (const moduleId of toRemove) {
|
||||||
const modulePath = paths.moduleDir(moduleId);
|
const modulePath = paths.moduleDir(moduleId);
|
||||||
|
|
@ -212,7 +211,16 @@ class Installer {
|
||||||
/**
|
/**
|
||||||
* Install modules, create directories, generate configs and manifests.
|
* Install modules, create directories, generate configs and manifests.
|
||||||
*/
|
*/
|
||||||
async _installAndConfigure(config, originalConfig, paths, officialModuleIds, allModules, addResult, officialModules) {
|
async _installAndConfigure(
|
||||||
|
config,
|
||||||
|
originalConfig,
|
||||||
|
paths,
|
||||||
|
officialModuleIds,
|
||||||
|
allModules,
|
||||||
|
addResult,
|
||||||
|
officialModules,
|
||||||
|
previousSkillManifestRows = [],
|
||||||
|
) {
|
||||||
const isQuickUpdate = config.isQuickUpdate();
|
const isQuickUpdate = config.isQuickUpdate();
|
||||||
const moduleConfigs = officialModules.moduleConfigs;
|
const moduleConfigs = officialModules.moduleConfigs;
|
||||||
|
|
||||||
|
|
@ -291,25 +299,29 @@ class Installer {
|
||||||
|
|
||||||
message('Generating manifests...');
|
message('Generating manifests...');
|
||||||
const manifestGen = new ManifestGenerator();
|
const manifestGen = new ManifestGenerator();
|
||||||
|
const preservedModules = originalConfig._preserveModules || [];
|
||||||
|
|
||||||
const allModulesForManifest = config.isQuickUpdate()
|
const allModulesForManifest = config.isQuickUpdate()
|
||||||
? originalConfig._existingModules || allModules || []
|
? originalConfig._existingModules || allModules || []
|
||||||
: originalConfig._preserveModules
|
: preservedModules.length > 0
|
||||||
? [...allModules, ...originalConfig._preserveModules]
|
? [...allModules, ...preservedModules]
|
||||||
: allModules || [];
|
: allModules || [];
|
||||||
|
|
||||||
let modulesForCsvPreserve;
|
let modulesForCsvPreserve;
|
||||||
if (config.isQuickUpdate()) {
|
if (config.isQuickUpdate()) {
|
||||||
modulesForCsvPreserve = originalConfig._existingModules || allModules || [];
|
modulesForCsvPreserve = originalConfig._existingModules || allModules || [];
|
||||||
} else {
|
} else {
|
||||||
modulesForCsvPreserve = originalConfig._preserveModules ? [...allModules, ...originalConfig._preserveModules] : allModules;
|
modulesForCsvPreserve = preservedModules.length > 0 ? [...allModules, ...preservedModules] : allModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this._trackPreservedModuleFiles(paths.bmadDir, preservedModules);
|
||||||
|
|
||||||
await manifestGen.generateManifests(paths.bmadDir, allModulesForManifest, [...this.installedFiles], {
|
await manifestGen.generateManifests(paths.bmadDir, allModulesForManifest, [...this.installedFiles], {
|
||||||
ides: config.ides || [],
|
ides: config.ides || [],
|
||||||
preservedModules: modulesForCsvPreserve,
|
preservedModules: modulesForCsvPreserve,
|
||||||
moduleConfigs,
|
moduleConfigs,
|
||||||
});
|
});
|
||||||
|
await this._appendPreservedSkillManifestRows(paths.bmadDir, previousSkillManifestRows, preservedModules);
|
||||||
|
|
||||||
// Apply post-install --set TOML patches. Runs after writeCentralConfig
|
// Apply post-install --set TOML patches. Runs after writeCentralConfig
|
||||||
// (inside generateManifests above) so the patch operates on the
|
// (inside generateManifests above) so the patch operates on the
|
||||||
|
|
@ -411,6 +423,62 @@ class Installer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _readSkillManifestRows(bmadDir) {
|
||||||
|
const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
|
||||||
|
if (!(await fs.pathExists(csvPath))) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const csvParse = require('csv-parse/sync');
|
||||||
|
const content = await fs.readFile(csvPath, 'utf8');
|
||||||
|
return csvParse.parse(content, { columns: true, skip_empty_lines: true });
|
||||||
|
} catch (error) {
|
||||||
|
await prompts.log.warn(`Failed to parse skill-manifest.csv: ${error.message}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPreviousSkillIdsForCleanup(previousRows, preservedModules = []) {
|
||||||
|
const preservedModuleSet = new Set(preservedModules || []);
|
||||||
|
const ids = new Set();
|
||||||
|
for (const row of previousRows || []) {
|
||||||
|
if (row.canonicalId && !preservedModuleSet.has(row.module)) {
|
||||||
|
ids.add(row.canonicalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _appendPreservedSkillManifestRows(bmadDir, previousRows, preservedModules = []) {
|
||||||
|
if (!previousRows || previousRows.length === 0 || preservedModules.length === 0) return;
|
||||||
|
|
||||||
|
const preservedModuleSet = new Set(preservedModules);
|
||||||
|
const rowsToPreserve = previousRows.filter((row) => row.canonicalId && row.module && preservedModuleSet.has(row.module));
|
||||||
|
if (rowsToPreserve.length === 0) return;
|
||||||
|
|
||||||
|
const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
|
||||||
|
if (!(await fs.pathExists(csvPath))) return;
|
||||||
|
|
||||||
|
const currentRows = await this._readSkillManifestRows(bmadDir);
|
||||||
|
const activeIds = new Set(currentRows.map((row) => row.canonicalId).filter(Boolean));
|
||||||
|
const appendedRows = [];
|
||||||
|
|
||||||
|
for (const row of rowsToPreserve) {
|
||||||
|
if (activeIds.has(row.canonicalId)) continue;
|
||||||
|
activeIds.add(row.canonicalId);
|
||||||
|
appendedRows.push(
|
||||||
|
[row.canonicalId, row.name || row.canonicalId, row.description || '', row.module, row.path || '']
|
||||||
|
.map((field) => this.escapeCSVField(field))
|
||||||
|
.join(','),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appendedRows.length === 0) return;
|
||||||
|
|
||||||
|
const currentContent = await fs.readFile(csvPath, 'utf8');
|
||||||
|
const prefix = currentContent.endsWith('\n') ? currentContent : `${currentContent}\n`;
|
||||||
|
await fs.writeFile(csvPath, prefix + appendedRows.join('\n') + '\n', 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore custom and modified files that were backed up before the update.
|
* Restore custom and modified files that were backed up before the update.
|
||||||
* No-op for fresh installs (updateState is null).
|
* No-op for fresh installs (updateState is null).
|
||||||
|
|
@ -597,6 +665,15 @@ class Installer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _trackPreservedModuleFiles(bmadDir, preservedModules = []) {
|
||||||
|
for (const moduleName of preservedModules) {
|
||||||
|
const modulePath = path.join(bmadDir, moduleName);
|
||||||
|
if (await fs.pathExists(modulePath)) {
|
||||||
|
await this._trackFilesRecursive(modulePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install official (non-custom) modules.
|
* Install official (non-custom) modules.
|
||||||
* @param {Object} config - Installation configuration
|
* @param {Object} config - Installation configuration
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ class ConfigDrivenIdeSetup {
|
||||||
|
|
||||||
// Build removal set: previously installed skills + removals.txt entries
|
// Build removal set: previously installed skills + removals.txt entries
|
||||||
let removalSet;
|
let removalSet;
|
||||||
if (options.previousSkillIds && options.previousSkillIds.size > 0) {
|
if (options.previousSkillIds) {
|
||||||
// Install/update flow: use pre-captured skill IDs (before manifest was overwritten)
|
// Install/update flow: use pre-captured skill IDs (before manifest was overwritten)
|
||||||
removalSet = new Set(options.previousSkillIds);
|
removalSet = new Set(options.previousSkillIds);
|
||||||
if (resolvedBmadDir) {
|
if (resolvedBmadDir) {
|
||||||
|
|
@ -547,7 +547,7 @@ class ConfigDrivenIdeSetup {
|
||||||
// previousSkillIds — full uninstall or per-IDE removal via
|
// previousSkillIds — full uninstall or per-IDE removal via
|
||||||
// cleanupByList), don't spare anything; the IDE itself is going away,
|
// cleanupByList), don't spare anything; the IDE itself is going away,
|
||||||
// so its pointers should go with it.
|
// so its pointers should go with it.
|
||||||
const isInstallFlow = options.previousSkillIds && options.previousSkillIds.size > 0;
|
const isInstallFlow = !!options.previousSkillIds;
|
||||||
const activeSkillIds = isInstallFlow ? await this._readActiveSkillIds(resolvedBmadDir) : new Set();
|
const activeSkillIds = isInstallFlow ? await this._readActiveSkillIds(resolvedBmadDir) : new Set();
|
||||||
const extension = this.installerConfig.commands_extension || '.md';
|
const extension = this.installerConfig.commands_extension || '.md';
|
||||||
await this.cleanupCommandPointers(
|
await this.cleanupCommandPointers(
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,44 @@ async function getModuleVersion(moduleCode, { repoUrl = null, registryDefault =
|
||||||
* UI utilities for the installer
|
* UI utilities for the installer
|
||||||
*/
|
*/
|
||||||
class UI {
|
class UI {
|
||||||
|
async _retainUnavailableInstalledModules(selectedModules, installedModuleIds, bmadDir, options = {}) {
|
||||||
|
const { OfficialModules } = require('./modules/official-modules');
|
||||||
|
const officialCodes = new Set(['core']);
|
||||||
|
|
||||||
|
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
|
||||||
|
for (const mod of builtInModules) {
|
||||||
|
officialCodes.add(mod.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const externalManager = new ExternalModuleManager();
|
||||||
|
const registryModules = await externalManager.listAvailable();
|
||||||
|
for (const mod of registryModules) {
|
||||||
|
officialCodes.add(mod.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { CustomModuleManager } = require('./modules/custom-module-manager');
|
||||||
|
const customMgr = new CustomModuleManager();
|
||||||
|
const selectedSet = new Set(selectedModules);
|
||||||
|
const preserveModules = [];
|
||||||
|
|
||||||
|
for (const moduleId of installedModuleIds) {
|
||||||
|
if (moduleId === 'core') continue;
|
||||||
|
if (!selectedSet.has(moduleId) && !options.preserveUnselected) continue;
|
||||||
|
if (officialCodes.has(moduleId)) continue;
|
||||||
|
|
||||||
|
const customSource = await customMgr.findModuleSourceByCode(moduleId, { bmadDir });
|
||||||
|
if (!customSource) {
|
||||||
|
preserveModules.push(moduleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const preservedSet = new Set(preserveModules);
|
||||||
|
return {
|
||||||
|
selectedModules: selectedModules.filter((moduleId) => !preservedSet.has(moduleId)),
|
||||||
|
preserveModules,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt for installation configuration
|
* Prompt for installation configuration
|
||||||
* @param {Object} options - Command-line options from install command
|
* @param {Object} options - Command-line options from install command
|
||||||
|
|
@ -273,6 +311,18 @@ class UI {
|
||||||
selectedModules.unshift('core');
|
selectedModules.unshift('core');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const retainedModuleResult = await this._retainUnavailableInstalledModules(selectedModules, installedModuleIds, bmadDir, {
|
||||||
|
preserveUnselected: options.yes && !options.modules,
|
||||||
|
});
|
||||||
|
selectedModules = retainedModuleResult.selectedModules;
|
||||||
|
const preservedModules = retainedModuleResult.preserveModules;
|
||||||
|
|
||||||
|
if (preservedModules.length > 0) {
|
||||||
|
await prompts.log.warn(
|
||||||
|
`Retaining ${preservedModules.length} installed module(s) with no available source: ${preservedModules.join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// For existing installs, resolve per-module update decisions BEFORE
|
// For existing installs, resolve per-module update decisions BEFORE
|
||||||
// we clone anything. Reads the existing manifest's recorded channel
|
// we clone anything. Reads the existing manifest's recorded channel
|
||||||
// per module and prompts the user on available upgrades (patch/minor
|
// per module and prompts the user on available upgrades (patch/minor
|
||||||
|
|
@ -317,6 +367,7 @@ class UI {
|
||||||
setOverrides,
|
setOverrides,
|
||||||
skipPrompts: options.yes || false,
|
skipPrompts: options.yes || false,
|
||||||
channelOptions,
|
channelOptions,
|
||||||
|
_preserveModules: preservedModules,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue