bmad-method 6.2.3-next.3 → 6.2.3-next.31

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.
Files changed (127) hide show
  1. package/.claude-plugin/marketplace.json +0 -4
  2. package/README.md +8 -9
  3. package/README_CN.md +1 -1
  4. package/README_VN.md +110 -0
  5. package/package.json +2 -1
  6. package/removals.txt +17 -0
  7. package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +7 -4
  8. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +6 -4
  9. package/src/bmm-skills/1-analysis/bmad-document-project/workflow.md +8 -10
  10. package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +96 -0
  11. package/src/bmm-skills/1-analysis/bmad-prfaq/agents/artifact-analyzer.md +60 -0
  12. package/src/bmm-skills/1-analysis/bmad-prfaq/agents/web-researcher.md +49 -0
  13. package/src/bmm-skills/1-analysis/bmad-prfaq/assets/prfaq-template.md +62 -0
  14. package/src/bmm-skills/1-analysis/bmad-prfaq/bmad-manifest.json +16 -0
  15. package/src/bmm-skills/1-analysis/bmad-prfaq/references/customer-faq.md +55 -0
  16. package/src/bmm-skills/1-analysis/bmad-prfaq/references/internal-faq.md +51 -0
  17. package/src/bmm-skills/1-analysis/bmad-prfaq/references/press-release.md +60 -0
  18. package/src/bmm-skills/1-analysis/bmad-prfaq/references/verdict.md +79 -0
  19. package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +1 -6
  20. package/src/bmm-skills/1-analysis/bmad-product-brief/bmad-manifest.json +1 -1
  21. package/src/bmm-skills/1-analysis/research/bmad-domain-research/workflow.md +8 -6
  22. package/src/bmm-skills/1-analysis/research/bmad-market-research/workflow.md +8 -6
  23. package/src/bmm-skills/1-analysis/research/bmad-technical-research/workflow.md +8 -6
  24. package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +6 -4
  25. package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +6 -4
  26. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/workflow.md +8 -9
  27. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +1 -1
  28. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/workflow.md +8 -9
  29. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md +1 -1
  30. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +1 -1
  31. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md +1 -1
  32. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md +1 -1
  33. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +1 -3
  34. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/workflow.md +8 -9
  35. package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/workflow.md +8 -9
  36. package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +6 -4
  37. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +1 -1
  38. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +1 -1
  39. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +1 -1
  40. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +9 -11
  41. package/src/bmm-skills/3-solutioning/bmad-create-architecture/workflow.md +8 -14
  42. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +10 -12
  43. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/workflow.md +8 -12
  44. package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +11 -4
  45. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +29 -0
  46. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/generate-trail.md +38 -0
  47. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-01-orientation.md +105 -0
  48. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-02-walkthrough.md +89 -0
  49. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-03-detail-pass.md +106 -0
  50. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-04-testing.md +74 -0
  51. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-05-wrapup.md +24 -0
  52. package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-01-gather-context.md +38 -15
  53. package/src/bmm-skills/4-implementation/bmad-correct-course/checklist.md +2 -2
  54. package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +8 -8
  55. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/checklist.md +1 -1
  56. package/src/bmm-skills/4-implementation/bmad-quick-dev/compile-epic-context.md +62 -0
  57. package/src/bmm-skills/4-implementation/bmad-quick-dev/spec-template.md +1 -1
  58. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +33 -6
  59. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +20 -8
  60. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-03-implement.md +2 -0
  61. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +16 -4
  62. package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +1 -5
  63. package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +134 -134
  64. package/src/bmm-skills/4-implementation/bmad-sprint-planning/sprint-status-template.yaml +1 -1
  65. package/src/bmm-skills/4-implementation/bmad-sprint-planning/workflow.md +3 -3
  66. package/src/bmm-skills/4-implementation/bmad-sprint-status/workflow.md +2 -2
  67. package/src/bmm-skills/module-help.csv +4 -1
  68. package/src/core-skills/bmad-advanced-elicitation/SKILL.md +1 -2
  69. package/src/core-skills/bmad-distillator/SKILL.md +0 -1
  70. package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +9 -9
  71. package/src/core-skills/bmad-help/SKILL.md +4 -2
  72. package/src/core-skills/bmad-party-mode/SKILL.md +121 -2
  73. package/src/core-skills/module-help.csv +1 -0
  74. package/tools/installer/cli-utils.js +18 -9
  75. package/tools/installer/commands/install.js +0 -1
  76. package/tools/installer/core/existing-install.js +2 -8
  77. package/tools/installer/core/install-paths.js +0 -3
  78. package/tools/installer/core/installer.js +176 -464
  79. package/tools/installer/core/manifest-generator.js +4 -12
  80. package/tools/installer/core/manifest.js +82 -97
  81. package/tools/installer/ide/_config-driven.js +149 -38
  82. package/tools/installer/ide/platform-codes.yaml +6 -4
  83. package/tools/installer/ide/shared/skill-manifest.js +1 -16
  84. package/tools/installer/install-messages.yaml +19 -26
  85. package/tools/installer/modules/community-manager.js +377 -0
  86. package/tools/installer/modules/custom-module-manager.js +308 -0
  87. package/tools/installer/modules/external-manager.js +65 -49
  88. package/tools/installer/modules/official-modules.js +37 -65
  89. package/tools/installer/modules/registry-client.js +66 -0
  90. package/tools/installer/{external-official-modules.yaml → modules/registry-fallback.yaml} +3 -12
  91. package/tools/installer/ui.js +340 -672
  92. package/tools/platform-codes.yaml +6 -0
  93. package/src/bmm-skills/2-plan-workflows/create-prd/data/domain-complexity.csv +0 -15
  94. package/src/bmm-skills/2-plan-workflows/create-prd/data/project-types.csv +0 -11
  95. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +0 -224
  96. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +0 -191
  97. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +0 -209
  98. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +0 -174
  99. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -214
  100. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +0 -228
  101. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +0 -217
  102. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -205
  103. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -243
  104. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +0 -263
  105. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +0 -209
  106. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -264
  107. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +0 -242
  108. package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +0 -232
  109. package/src/bmm-skills/2-plan-workflows/create-prd/workflow-validate-prd.md +0 -65
  110. package/src/bmm-skills/4-implementation/bmad-agent-qa/SKILL.md +0 -59
  111. package/src/bmm-skills/4-implementation/bmad-agent-qa/bmad-skill-manifest.yaml +0 -11
  112. package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/SKILL.md +0 -51
  113. package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/bmad-skill-manifest.yaml +0 -11
  114. package/src/bmm-skills/4-implementation/bmad-agent-sm/SKILL.md +0 -53
  115. package/src/bmm-skills/4-implementation/bmad-agent-sm/bmad-skill-manifest.yaml +0 -11
  116. package/src/core-skills/bmad-init/SKILL.md +0 -100
  117. package/src/core-skills/bmad-init/resources/core-module.yaml +0 -25
  118. package/src/core-skills/bmad-init/scripts/bmad_init.py +0 -624
  119. package/src/core-skills/bmad-init/scripts/tests/test_bmad_init.py +0 -393
  120. package/src/core-skills/bmad-party-mode/steps/step-01-agent-loading.md +0 -138
  121. package/src/core-skills/bmad-party-mode/steps/step-02-discussion-orchestration.md +0 -187
  122. package/src/core-skills/bmad-party-mode/steps/step-03-graceful-exit.md +0 -167
  123. package/src/core-skills/bmad-party-mode/workflow.md +0 -190
  124. package/tools/installer/core/custom-module-cache.js +0 -260
  125. package/tools/installer/custom-handler.js +0 -112
  126. package/tools/installer/modules/custom-modules.js +0 -197
  127. /package/src/bmm-skills/2-plan-workflows/{create-prd → bmad-edit-prd}/data/prd-purpose.md +0 -0
@@ -9,7 +9,6 @@ const {
9
9
  loadSkillManifest: loadSkillManifestShared,
10
10
  getCanonicalId: getCanonicalIdShared,
11
11
  getArtifactType: getArtifactTypeShared,
12
- getInstallToBmad: getInstallToBmadShared,
13
12
  } = require('../ide/shared/skill-manifest');
14
13
 
15
14
  // Load package.json for version info
@@ -42,11 +41,6 @@ class ManifestGenerator {
42
41
  return getArtifactTypeShared(manifest, filename);
43
42
  }
44
43
 
45
- /** Delegate to shared skill-manifest module */
46
- getInstallToBmad(manifest, filename) {
47
- return getInstallToBmadShared(manifest, filename);
48
- }
49
-
50
44
  /**
51
45
  * Clean text for CSV output by normalizing whitespace.
52
46
  * Note: Quote escaping is handled by escapeCsv() at write time.
@@ -127,7 +121,7 @@ class ManifestGenerator {
127
121
  * Recursively walk a module directory tree, collecting native SKILL.md entrypoints.
128
122
  * A directory is discovered as a skill when it contains a SKILL.md file with
129
123
  * valid name/description frontmatter (name must match directory name).
130
- * Manifest YAML is loaded only when present — for install_to_bmad and agent metadata.
124
+ * Manifest YAML is loaded only when present — for agent metadata.
131
125
  * Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths).
132
126
  */
133
127
  async collectSkills() {
@@ -156,7 +150,7 @@ class ManifestGenerator {
156
150
  const skillMeta = await this.parseSkillMd(skillMdPath, dir, dirName, debug);
157
151
 
158
152
  if (skillMeta) {
159
- // Load manifest when present (for install_to_bmad and agent metadata)
153
+ // Load manifest when present (for agent metadata)
160
154
  const manifest = await this.loadSkillManifest(dir);
161
155
  const artifactType = this.getArtifactType(manifest, skillFile);
162
156
 
@@ -182,7 +176,6 @@ class ManifestGenerator {
182
176
  module: moduleName,
183
177
  path: installPath,
184
178
  canonicalId,
185
- install_to_bmad: this.getInstallToBmad(manifest, skillFile),
186
179
  });
187
180
 
188
181
  // Add to files list
@@ -377,11 +370,11 @@ class ManifestGenerator {
377
370
  */
378
371
  async writeMainManifest(cfgDir) {
379
372
  const manifestPath = path.join(cfgDir, 'manifest.yaml');
373
+ const installedModuleSet = new Set(this.modules);
380
374
 
381
375
  // Read existing manifest to preserve install date
382
376
  let existingInstallDate = null;
383
377
  const existingModulesMap = new Map();
384
-
385
378
  if (await fs.pathExists(manifestPath)) {
386
379
  try {
387
380
  const existingContent = await fs.readFile(manifestPath, 'utf8');
@@ -463,7 +456,7 @@ class ManifestGenerator {
463
456
  const csvPath = path.join(cfgDir, 'skill-manifest.csv');
464
457
  const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
465
458
 
466
- let csvContent = 'canonicalId,name,description,module,path,install_to_bmad\n';
459
+ let csvContent = 'canonicalId,name,description,module,path\n';
467
460
 
468
461
  for (const skill of this.skills) {
469
462
  const row = [
@@ -472,7 +465,6 @@ class ManifestGenerator {
472
465
  escapeCsv(skill.description),
473
466
  escapeCsv(skill.module),
474
467
  escapeCsv(skill.path),
475
- escapeCsv(skill.install_to_bmad),
476
468
  ].join(',');
477
469
  csvContent += row + '\n';
478
470
  }
@@ -97,7 +97,6 @@ class Manifest {
97
97
  lastUpdated: manifestData.installation?.lastUpdated,
98
98
  modules: moduleNames, // Simple array of module names for backward compatibility
99
99
  modulesDetailed: hasDetailedModules ? modules : null, // New detailed format
100
- customModules: manifestData.customModules || [], // Keep for backward compatibility
101
100
  ides: manifestData.ides || [],
102
101
  };
103
102
  } catch (error) {
@@ -254,7 +253,6 @@ class Manifest {
254
253
  lastUpdated: manifest.installation?.lastUpdated,
255
254
  modules: moduleNames,
256
255
  modulesDetailed: hasDetailedModules ? modules : null,
257
- customModules: manifest.customModules || [],
258
256
  ides: manifest.ides || [],
259
257
  };
260
258
  }
@@ -783,52 +781,6 @@ class Manifest {
783
781
 
784
782
  return configs;
785
783
  }
786
- /**
787
- * Add a custom module to the manifest with its source path
788
- * @param {string} bmadDir - Path to bmad directory
789
- * @param {Object} customModule - Custom module info
790
- */
791
- async addCustomModule(bmadDir, customModule) {
792
- const manifest = await this.read(bmadDir);
793
- if (!manifest) {
794
- throw new Error('No manifest found');
795
- }
796
-
797
- if (!manifest.customModules) {
798
- manifest.customModules = [];
799
- }
800
-
801
- // Check if custom module already exists
802
- const existingIndex = manifest.customModules.findIndex((m) => m.id === customModule.id);
803
- if (existingIndex === -1) {
804
- // Add new entry
805
- manifest.customModules.push(customModule);
806
- } else {
807
- // Update existing entry
808
- manifest.customModules[existingIndex] = customModule;
809
- }
810
-
811
- await this.update(bmadDir, { customModules: manifest.customModules });
812
- }
813
-
814
- /**
815
- * Remove a custom module from the manifest
816
- * @param {string} bmadDir - Path to bmad directory
817
- * @param {string} moduleId - Module ID to remove
818
- */
819
- async removeCustomModule(bmadDir, moduleId) {
820
- const manifest = await this.read(bmadDir);
821
- if (!manifest || !manifest.customModules) {
822
- return;
823
- }
824
-
825
- const index = manifest.customModules.findIndex((m) => m.id === moduleId);
826
- if (index !== -1) {
827
- manifest.customModules.splice(index, 1);
828
- await this.update(bmadDir, { customModules: manifest.customModules });
829
- }
830
- }
831
-
832
784
  /**
833
785
  * Get module version info from source
834
786
  * @param {string} moduleName - Module name/code
@@ -837,14 +789,13 @@ class Manifest {
837
789
  * @returns {Object} Version info object with version, source, npmPackage, repoUrl
838
790
  */
839
791
  async getModuleVersionInfo(moduleName, bmadDir, moduleSourcePath = null) {
840
- const os = require('node:os');
841
792
  const yaml = require('yaml');
842
793
 
843
- // Built-in modules use BMad version (only core and bmm are in BMAD-METHOD repo)
794
+ // Resolve source type first, then read version with the correct path context
844
795
  if (['core', 'bmm'].includes(moduleName)) {
845
- const bmadVersion = require(path.join(getProjectRoot(), 'package.json')).version;
796
+ const version = await this._readMarketplaceVersion(moduleName, moduleSourcePath);
846
797
  return {
847
- version: bmadVersion,
798
+ version,
848
799
  source: 'built-in',
849
800
  npmPackage: null,
850
801
  repoUrl: null,
@@ -857,69 +808,103 @@ class Manifest {
857
808
  const moduleInfo = await extMgr.getModuleByCode(moduleName);
858
809
 
859
810
  if (moduleInfo) {
860
- // External module - try to get version from npm registry first, then fall back to cache
861
- let version = null;
862
-
863
- if (moduleInfo.npmPackage) {
864
- // Fetch version from npm registry
865
- try {
866
- version = await this.fetchNpmVersion(moduleInfo.npmPackage);
867
- } catch {
868
- // npm fetch failed, try cache as fallback
869
- }
870
- }
871
-
872
- // If npm didn't work, try reading from cached repo's package.json
873
- if (!version) {
874
- const cacheDir = path.join(os.homedir(), '.bmad', 'cache', 'external-modules', moduleName);
875
- const packageJsonPath = path.join(cacheDir, 'package.json');
876
-
877
- if (await fs.pathExists(packageJsonPath)) {
878
- try {
879
- const pkg = require(packageJsonPath);
880
- version = pkg.version;
881
- } catch (error) {
882
- await prompts.log.warn(`Failed to read package.json for ${moduleName}: ${error.message}`);
883
- }
884
- }
885
- }
886
-
811
+ // External module: use moduleSourcePath if provided, otherwise fall back to cache
812
+ const version = await this._readMarketplaceVersion(moduleName, moduleSourcePath);
887
813
  return {
888
- version: version,
814
+ version,
889
815
  source: 'external',
890
816
  npmPackage: moduleInfo.npmPackage || null,
891
817
  repoUrl: moduleInfo.url || null,
892
818
  };
893
819
  }
894
820
 
895
- // Custom module - check cache directory
896
- const cacheDir = path.join(bmadDir, '_config', 'custom', moduleName);
897
- const moduleYamlPath = path.join(cacheDir, 'module.yaml');
821
+ // Check if this is a community module
822
+ const { CommunityModuleManager } = require('../modules/community-manager');
823
+ const communityMgr = new CommunityModuleManager();
824
+ const communityInfo = await communityMgr.getModuleByCode(moduleName);
825
+ if (communityInfo) {
826
+ const communityVersion = await this._readMarketplaceVersion(moduleName, moduleSourcePath);
827
+ return {
828
+ version: communityVersion || communityInfo.version,
829
+ source: 'community',
830
+ npmPackage: communityInfo.npmPackage || null,
831
+ repoUrl: communityInfo.url || null,
832
+ };
833
+ }
898
834
 
899
- if (await fs.pathExists(moduleYamlPath)) {
900
- try {
901
- const yamlContent = await fs.readFile(moduleYamlPath, 'utf8');
902
- const moduleConfig = yaml.parse(yamlContent);
903
- return {
904
- version: moduleConfig.version || null,
905
- source: 'custom',
906
- npmPackage: moduleConfig.npmPackage || null,
907
- repoUrl: moduleConfig.repoUrl || null,
908
- };
909
- } catch (error) {
910
- await prompts.log.warn(`Failed to read module.yaml for ${moduleName}: ${error.message}`);
911
- }
835
+ // Check if this is a custom module (from user-provided URL)
836
+ const { CustomModuleManager } = require('../modules/custom-module-manager');
837
+ const customMgr = new CustomModuleManager();
838
+ const customSource = await customMgr.findModuleSourceByCode(moduleName);
839
+ if (customSource) {
840
+ const customVersion = await this._readMarketplaceVersion(moduleName, moduleSourcePath);
841
+ return {
842
+ version: customVersion,
843
+ source: 'custom',
844
+ npmPackage: null,
845
+ repoUrl: null,
846
+ };
912
847
  }
913
848
 
914
849
  // Unknown module
850
+ const version = await this._readMarketplaceVersion(moduleName, moduleSourcePath);
915
851
  return {
916
- version: null,
852
+ version,
917
853
  source: 'unknown',
918
854
  npmPackage: null,
919
855
  repoUrl: null,
920
856
  };
921
857
  }
922
858
 
859
+ /**
860
+ * Read version from .claude-plugin/marketplace.json for a module
861
+ * @param {string} moduleName - Module code
862
+ * @returns {string|null} Version or null
863
+ */
864
+ async _readMarketplaceVersion(moduleName, moduleSourcePath = null) {
865
+ const os = require('node:os');
866
+ let marketplacePath;
867
+
868
+ if (['core', 'bmm'].includes(moduleName)) {
869
+ marketplacePath = path.join(getProjectRoot(), '.claude-plugin', 'marketplace.json');
870
+ } else if (moduleSourcePath) {
871
+ // Walk up from source path to find marketplace.json
872
+ let dir = moduleSourcePath;
873
+ for (let i = 0; i < 5; i++) {
874
+ const candidate = path.join(dir, '.claude-plugin', 'marketplace.json');
875
+ if (await fs.pathExists(candidate)) {
876
+ marketplacePath = candidate;
877
+ break;
878
+ }
879
+ const parent = path.dirname(dir);
880
+ if (parent === dir) break;
881
+ dir = parent;
882
+ }
883
+ }
884
+
885
+ // Fallback to external module cache
886
+ if (!marketplacePath) {
887
+ const cacheDir = path.join(os.homedir(), '.bmad', 'cache', 'external-modules', moduleName);
888
+ marketplacePath = path.join(cacheDir, '.claude-plugin', 'marketplace.json');
889
+ }
890
+
891
+ try {
892
+ if (await fs.pathExists(marketplacePath)) {
893
+ const data = JSON.parse(await fs.readFile(marketplacePath, 'utf8'));
894
+ const plugins = data?.plugins;
895
+ if (!Array.isArray(plugins) || plugins.length === 0) return null;
896
+ let best = null;
897
+ for (const p of plugins) {
898
+ if (p.version && (!best || p.version > best)) best = p.version;
899
+ }
900
+ return best;
901
+ }
902
+ } catch {
903
+ // ignore
904
+ }
905
+ return null;
906
+ }
907
+
923
908
  /**
924
909
  * Fetch latest version from npm for a package
925
910
  * @param {string} packageName - npm package name
@@ -86,7 +86,7 @@ class ConfigDrivenIdeSetup {
86
86
  if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`);
87
87
 
88
88
  // Clean up any old BMAD installation first
89
- await this.cleanup(projectDir, options);
89
+ await this.cleanup(projectDir, options, bmadDir);
90
90
 
91
91
  if (!this.installerConfig) {
92
92
  return { success: false, reason: 'no-config' };
@@ -183,18 +183,6 @@ class ConfigDrivenIdeSetup {
183
183
  count++;
184
184
  }
185
185
 
186
- // Post-install cleanup: remove _bmad/ directories for skills with install_to_bmad === "false"
187
- for (const record of records) {
188
- if (record.install_to_bmad === 'false') {
189
- const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path;
190
- const sourceFile = path.join(bmadDir, relativePath);
191
- const sourceDir = path.dirname(sourceFile);
192
- if (await fs.pathExists(sourceDir)) {
193
- await fs.remove(sourceDir);
194
- }
195
- }
196
- }
197
-
198
186
  return count;
199
187
  }
200
188
 
@@ -215,16 +203,42 @@ class ConfigDrivenIdeSetup {
215
203
  * Cleanup IDE configuration
216
204
  * @param {string} projectDir - Project directory
217
205
  */
218
- async cleanup(projectDir, options = {}) {
206
+ async cleanup(projectDir, options = {}, bmadDir = null) {
207
+ const resolvedBmadDir = bmadDir || (await this._findBmadDir(projectDir));
208
+
209
+ // Build removal set: previously installed skills + removals.txt entries
210
+ let removalSet;
211
+ if (options.previousSkillIds && options.previousSkillIds.size > 0) {
212
+ // Install/update flow: use pre-captured skill IDs (before manifest was overwritten)
213
+ removalSet = new Set(options.previousSkillIds);
214
+ if (resolvedBmadDir) {
215
+ const removals = await this.loadRemovalLists(resolvedBmadDir);
216
+ for (const entry of removals) removalSet.add(entry);
217
+ }
218
+ } else if (resolvedBmadDir) {
219
+ // Uninstall flow: read from current skill-manifest.csv + removals.txt
220
+ removalSet = await this._buildUninstallSet(resolvedBmadDir);
221
+ } else {
222
+ removalSet = new Set();
223
+ }
224
+
219
225
  // Migrate legacy target directories (e.g. .opencode/agent → .opencode/agents)
226
+ // Legacy dirs are abandoned entirely, so use prefix matching (null removalSet)
220
227
  if (this.installerConfig?.legacy_targets) {
221
- if (!options.silent) await prompts.log.message(' Migrating legacy directories...');
222
- for (const legacyDir of this.installerConfig.legacy_targets) {
223
- if (this.isGlobalPath(legacyDir)) {
224
- await this.warnGlobalLegacy(legacyDir, options);
225
- } else {
226
- await this.cleanupTarget(projectDir, legacyDir, options);
227
- await this.removeEmptyParents(projectDir, legacyDir);
228
+ const legacyDirsExist = await Promise.all(
229
+ this.installerConfig.legacy_targets.map((d) =>
230
+ this.isGlobalPath(d) ? fs.pathExists(d.replace(/^~/, os.homedir())) : fs.pathExists(path.join(projectDir, d)),
231
+ ),
232
+ );
233
+ if (legacyDirsExist.some(Boolean)) {
234
+ if (!options.silent) await prompts.log.message(' Migrating legacy directories...');
235
+ for (const legacyDir of this.installerConfig.legacy_targets) {
236
+ if (this.isGlobalPath(legacyDir)) {
237
+ await this.warnGlobalLegacy(legacyDir, options);
238
+ } else {
239
+ await this.cleanupTarget(projectDir, legacyDir, options, null);
240
+ await this.removeEmptyParents(projectDir, legacyDir);
241
+ }
228
242
  }
229
243
  }
230
244
  }
@@ -244,9 +258,9 @@ class ConfigDrivenIdeSetup {
244
258
  await this.cleanupRovoDevPrompts(projectDir, options);
245
259
  }
246
260
 
247
- // Clean target directory
261
+ // Clean current target directory
248
262
  if (this.installerConfig?.target_dir) {
249
- await this.cleanupTarget(projectDir, this.installerConfig.target_dir, options);
263
+ await this.cleanupTarget(projectDir, this.installerConfig.target_dir, options, removalSet);
250
264
  }
251
265
  }
252
266
 
@@ -286,23 +300,117 @@ class ConfigDrivenIdeSetup {
286
300
  }
287
301
 
288
302
  /**
289
- * Cleanup a specific target directory
303
+ * Find the _bmad directory in a project
304
+ * @param {string} projectDir - Project directory
305
+ * @returns {string|null} Path to bmad dir or null
306
+ */
307
+ async _findBmadDir(projectDir) {
308
+ const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME);
309
+ return (await fs.pathExists(bmadDir)) ? bmadDir : null;
310
+ }
311
+
312
+ /**
313
+ * Build the full set of entries to remove for uninstall.
314
+ * Reads skill-manifest.csv to know exactly what was installed, plus removal lists.
315
+ * @param {string} bmadDir - BMAD installation directory
316
+ * @returns {Set<string>} Set of entries to remove
317
+ */
318
+ async _buildUninstallSet(bmadDir) {
319
+ const removals = await this.loadRemovalLists(bmadDir);
320
+
321
+ // Also add all currently installed skills from skill-manifest.csv
322
+ const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
323
+ try {
324
+ if (await fs.pathExists(csvPath)) {
325
+ const content = await fs.readFile(csvPath, 'utf8');
326
+ const records = csv.parse(content, { columns: true, skip_empty_lines: true });
327
+ for (const record of records) {
328
+ if (record.canonicalId) {
329
+ removals.add(record.canonicalId);
330
+ }
331
+ }
332
+ }
333
+ } catch {
334
+ // If we can't read the manifest, we still have the removal lists
335
+ }
336
+
337
+ return removals;
338
+ }
339
+
340
+ /**
341
+ * Load removal lists from all module sources in the bmad directory.
342
+ * Each module can have an optional removals.txt listing entries to remove.
343
+ * @param {string} bmadDir - BMAD installation directory
344
+ * @returns {Set<string>} Set of entries to remove
345
+ */
346
+ async loadRemovalLists(bmadDir) {
347
+ const removals = new Set();
348
+ const { getProjectRoot } = require('../project-root');
349
+
350
+ // Read project-level removals.txt (covers core and bmm)
351
+ const projectRemovalsPath = path.join(getProjectRoot(), 'removals.txt');
352
+ await this._readRemovalFile(projectRemovalsPath, removals);
353
+
354
+ // Read per-module removals.txt from installed module directories
355
+ try {
356
+ const entries = await fs.readdir(bmadDir);
357
+ for (const entry of entries) {
358
+ if (entry.startsWith('_')) continue;
359
+ const removalPath = path.join(bmadDir, entry, 'removals.txt');
360
+ await this._readRemovalFile(removalPath, removals);
361
+ }
362
+ } catch {
363
+ // bmadDir may not exist yet on fresh install
364
+ }
365
+
366
+ return removals;
367
+ }
368
+
369
+ /**
370
+ * Read a removals.txt file and add entries to the set
371
+ * @param {string} filePath - Path to removals.txt
372
+ * @param {Set<string>} removals - Set to add entries to
373
+ */
374
+ async _readRemovalFile(filePath, removals) {
375
+ try {
376
+ if (await fs.pathExists(filePath)) {
377
+ const content = await fs.readFile(filePath, 'utf8');
378
+ for (const line of content.split('\n')) {
379
+ const trimmed = line.trim();
380
+ if (trimmed && !trimmed.startsWith('#')) {
381
+ removals.add(trimmed);
382
+ }
383
+ }
384
+ }
385
+ } catch {
386
+ // Optional file — ignore errors
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Cleanup a specific target directory.
392
+ * When removalSet is provided, only removes entries in that set.
393
+ * When removalSet is null (legacy dirs), removes all bmad-prefixed entries.
290
394
  * @param {string} projectDir - Project directory
291
395
  * @param {string} targetDir - Target directory to clean
396
+ * @param {Object} options - Cleanup options
397
+ * @param {Set<string>|null} removalSet - Entries to remove, or null for legacy prefix matching
292
398
  */
293
- async cleanupTarget(projectDir, targetDir, options = {}) {
399
+ async cleanupTarget(projectDir, targetDir, options = {}, removalSet = new Set()) {
294
400
  const targetPath = path.join(projectDir, targetDir);
295
401
 
296
402
  if (!(await fs.pathExists(targetPath))) {
297
403
  return;
298
404
  }
299
405
 
300
- // Remove all bmad* files
406
+ if (removalSet && removalSet.size === 0) {
407
+ return;
408
+ }
409
+
301
410
  let entries;
302
411
  try {
303
412
  entries = await fs.readdir(targetPath);
304
413
  } catch {
305
- // Directory exists but can't be read - skip cleanup
306
414
  return;
307
415
  }
308
416
 
@@ -313,23 +421,26 @@ class ConfigDrivenIdeSetup {
313
421
  let removedCount = 0;
314
422
 
315
423
  for (const entry of entries) {
316
- if (!entry || typeof entry !== 'string') {
317
- continue;
318
- }
319
- if (entry.startsWith('bmad') && !entry.startsWith('bmad-os-')) {
320
- const entryPath = path.join(targetPath, entry);
424
+ if (!entry || typeof entry !== 'string') continue;
425
+
426
+ // Always preserve bmad-os-* utility skills regardless of cleanup mode
427
+ if (entry.startsWith('bmad-os-')) continue;
428
+
429
+ // Surgical removal from set, or legacy prefix matching when set is null
430
+ const shouldRemove = removalSet ? removalSet.has(entry) : entry.startsWith('bmad');
431
+
432
+ if (shouldRemove) {
321
433
  try {
322
- await fs.remove(entryPath);
434
+ await fs.remove(path.join(targetPath, entry));
323
435
  removedCount++;
324
436
  } catch {
325
- // Skip entries that can't be removed (broken symlinks, permission errors)
437
+ // Skip entries that can't be removed
326
438
  }
327
439
  }
328
440
  }
329
441
 
330
- if (removedCount > 0 && !options.silent) {
331
- await prompts.log.message(` Cleaned ${removedCount} BMAD files from ${targetDir}`);
332
- }
442
+ // Only log cleanup when it's not a routine reinstall (legacy dir cleanup or actual removals)
443
+ // Suppress for current target_dir since it's always cleaned before a fresh write
333
444
 
334
445
  // Remove empty directory after cleanup
335
446
  if (removedCount > 0) {
@@ -339,7 +450,7 @@ class ConfigDrivenIdeSetup {
339
450
  await fs.remove(targetPath);
340
451
  }
341
452
  } catch {
342
- // Directory may already be gone or in use — skip
453
+ // Directory may already be gone or in use
343
454
  }
344
455
  }
345
456
  }
@@ -33,7 +33,6 @@ platforms:
33
33
  legacy_targets:
34
34
  - .claude/commands
35
35
  target_dir: .claude/skills
36
- ancestor_conflict_check: true
37
36
 
38
37
  cline:
39
38
  name: "Cline"
@@ -51,7 +50,6 @@ platforms:
51
50
  - .codex/prompts
52
51
  - ~/.codex/prompts
53
52
  target_dir: .agents/skills
54
- ancestor_conflict_check: true
55
53
 
56
54
  codebuddy:
57
55
  name: "CodeBuddy"
@@ -102,10 +100,15 @@ platforms:
102
100
  - .iflow/commands
103
101
  target_dir: .iflow/skills
104
102
 
103
+ junie:
104
+ name: "Junie"
105
+ preferred: false
106
+ installer:
107
+ target_dir: .agents/skills
108
+
105
109
  kilo:
106
110
  name: "KiloCoder"
107
111
  preferred: false
108
- suspended: "Kilo Code does not yet support the Agent Skills standard. Support is paused until they implement it. See https://github.com/kilocode/kilo-code/issues for updates."
109
112
  installer:
110
113
  legacy_targets:
111
114
  - .kilocode/workflows
@@ -135,7 +138,6 @@ platforms:
135
138
  - .opencode/agent
136
139
  - .opencode/command
137
140
  target_dir: .opencode/skills
138
- ancestor_conflict_check: true
139
141
 
140
142
  pi:
141
143
  name: "Pi"
@@ -54,19 +54,4 @@ function getArtifactType(manifest, filename) {
54
54
  return null;
55
55
  }
56
56
 
57
- /**
58
- * Get the install_to_bmad flag for a specific file from a loaded skill manifest.
59
- * @param {Object|null} manifest - Loaded manifest (from loadSkillManifest)
60
- * @param {string} filename - Source filename to look up
61
- * @returns {boolean} install_to_bmad value (defaults to true)
62
- */
63
- function getInstallToBmad(manifest, filename) {
64
- if (!manifest) return true;
65
- // Single-entry manifest applies to all files in the directory
66
- if (manifest.__single) return manifest.__single.install_to_bmad !== false;
67
- // Multi-entry: look up by filename directly
68
- if (manifest[filename]) return manifest[filename].install_to_bmad !== false;
69
- return true;
70
- }
71
-
72
- module.exports = { loadSkillManifest, getCanonicalId, getArtifactType, getInstallToBmad };
57
+ module.exports = { loadSkillManifest, getCanonicalId, getArtifactType };
@@ -6,32 +6,25 @@
6
6
  startMessage: |
7
7
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8
8
 
9
- 🎉 V6 IS HERE! Welcome to BMad Method V6 - Official Stable Release!
10
-
11
- The BMad Method is now a Platform powered by the BMad Method Core and Module Ecosystem!
12
- - Select and install modules during setup - customize your experience
13
- - New BMad Method for Agile AI-Driven Development (the evolution of V4)
14
- - Exciting new modules available during installation, with community modules coming soon
15
- - Documentation: https://docs.bmad-method.org
16
-
17
- 🌟 BMad is 100% free and open source.
18
- - No gated Discord. No paywalls. No gated content.
19
- - We believe in empowering everyone, not just those who can pay.
20
- - Knowledge should be shared, not sold.
21
-
22
- 🎤 SPEAKING & MEDIA:
23
- - Available for conferences, podcasts, and media appearances
24
- - Topics: AI-Native Transformation, Spec and Context Engineering, BMad Method
25
- - For speaking inquiries or interviews, reach out to BMad on Discord!
26
-
27
- HELP US GROW:
28
- - Star us on GitHub: https://github.com/bmad-code-org/BMAD-METHOD/
29
- - Subscribe on YouTube: https://www.youtube.com/@BMadCode
30
- - Free Community and Support: https://discord.gg/gk8jAdXWmj
31
- - Donate: https://buymeacoffee.com/bmad
32
- - Corporate Sponsorship available
33
-
34
- Latest updates: https://github.com/bmad-code-org/BMAD-METHOD/blob/main/CHANGELOG.md
9
+ Agile AI-Driven Development. Powered by BMad Core and a growing module ecosystem.
10
+ Install official and community modules during setup to customize your experience.
11
+
12
+ 🌟 100% free. 100% open source. Always.
13
+ No paywalls. No gated content. Knowledge shared, not sold.
14
+
15
+ 🌐 CONNECT:
16
+ Website: https://bmadcode.com/
17
+ Discord: https://discord.gg/gk8jAdXWmj
18
+ YouTube: https://www.youtube.com/@BMadCode
19
+ X: https://x.com/BMadCode
20
+ Facebook: https://facebook.com/@BMadCode
21
+
22
+ SUPPORT THE PROJECT:
23
+ Star us: https://github.com/bmad-code-org/BMAD-METHOD/
24
+ Donate: https://buymeacoffee.com/bmad
25
+ Corporate sponsorship and speaking inquiries: contact@bmadcode.com
26
+
27
+ Docs, blog, and latest updates: https://bmadcode.com/
35
28
 
36
29
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
37
30