bmad-method 6.2.2 → 6.2.3-next.1

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 (77) hide show
  1. package/.claude-plugin/marketplace.json +78 -0
  2. package/package.json +8 -8
  3. package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +1 -1
  4. package/src/core-skills/bmad-init/scripts/bmad_init.py +35 -4
  5. package/src/core-skills/bmad-init/scripts/tests/test_bmad_init.py +64 -0
  6. package/tools/{cli → installer}/bmad-cli.js +3 -1
  7. package/tools/{cli/lib → installer}/cli-utils.js +3 -4
  8. package/tools/{cli → installer}/commands/install.js +3 -3
  9. package/tools/{cli → installer}/commands/status.js +4 -4
  10. package/tools/{cli → installer}/commands/uninstall.js +5 -5
  11. package/tools/installer/core/config.js +52 -0
  12. package/tools/{cli/installers/lib → installer}/core/custom-module-cache.js +1 -1
  13. package/tools/installer/core/existing-install.js +127 -0
  14. package/tools/installer/core/install-paths.js +129 -0
  15. package/tools/installer/core/installer.js +1790 -0
  16. package/tools/{cli/installers/lib → installer}/core/manifest-generator.js +3 -3
  17. package/tools/{cli/installers/lib → installer}/core/manifest.js +2 -2
  18. package/tools/{cli/installers/lib/custom/handler.js → installer/custom-handler.js} +1 -1
  19. package/tools/{cli/installers/lib → installer}/ide/_config-driven.js +30 -397
  20. package/tools/{cli/installers/lib → installer}/ide/manager.js +1 -53
  21. package/tools/installer/ide/platform-codes.js +37 -0
  22. package/tools/installer/ide/platform-codes.yaml +190 -0
  23. package/tools/{cli/installers/lib → installer}/ide/shared/module-injections.js +1 -1
  24. package/tools/{cli/installers/lib → installer}/message-loader.js +2 -2
  25. package/tools/installer/modules/custom-modules.js +197 -0
  26. package/tools/installer/modules/external-manager.js +323 -0
  27. package/tools/{cli/installers/lib/core/config-collector.js → installer/modules/official-modules.js} +714 -43
  28. package/tools/{cli/lib → installer}/ui.js +65 -299
  29. package/tools/javascript-conventions.md +5 -0
  30. package/tools/bmad-npx-wrapper.js +0 -38
  31. package/tools/cli/installers/lib/core/dependency-resolver.js +0 -743
  32. package/tools/cli/installers/lib/core/detector.js +0 -223
  33. package/tools/cli/installers/lib/core/ide-config-manager.js +0 -157
  34. package/tools/cli/installers/lib/core/installer.js +0 -3002
  35. package/tools/cli/installers/lib/ide/_base-ide.js +0 -657
  36. package/tools/cli/installers/lib/ide/platform-codes.js +0 -100
  37. package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -341
  38. package/tools/cli/installers/lib/modules/external-manager.js +0 -136
  39. package/tools/cli/installers/lib/modules/manager.js +0 -928
  40. package/tools/cli/lib/config.js +0 -213
  41. package/tools/cli/lib/platform-codes.js +0 -116
  42. package/tools/lib/xml-utils.js +0 -13
  43. /package/tools/{cli → installer}/README.md +0 -0
  44. /package/tools/{cli → installer}/external-official-modules.yaml +0 -0
  45. /package/tools/{cli/lib → installer}/file-ops.js +0 -0
  46. /package/tools/{cli/installers/lib → installer}/ide/shared/agent-command-generator.js +0 -0
  47. /package/tools/{cli/installers/lib → installer}/ide/shared/bmad-artifacts.js +0 -0
  48. /package/tools/{cli/installers/lib → installer}/ide/shared/path-utils.js +0 -0
  49. /package/tools/{cli/installers/lib → installer}/ide/shared/skill-manifest.js +0 -0
  50. /package/tools/{cli/installers/lib → installer}/ide/templates/agent-command-template.md +0 -0
  51. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/antigravity.md +0 -0
  52. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-agent.md +0 -0
  53. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-task.md +0 -0
  54. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-tool.md +0 -0
  55. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-workflow.md +0 -0
  56. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-agent.toml +0 -0
  57. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-task.toml +0 -0
  58. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-tool.toml +0 -0
  59. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-workflow-yaml.toml +0 -0
  60. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-workflow.toml +0 -0
  61. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-agent.md +0 -0
  62. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-task.md +0 -0
  63. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-tool.md +0 -0
  64. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-workflow.md +0 -0
  65. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-agent.md +0 -0
  66. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-task.md +0 -0
  67. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-tool.md +0 -0
  68. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-workflow-yaml.md +0 -0
  69. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-workflow.md +0 -0
  70. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/rovodev.md +0 -0
  71. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/trae.md +0 -0
  72. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/windsurf-workflow.md +0 -0
  73. /package/tools/{cli/installers/lib → installer}/ide/templates/split/.gitkeep +0 -0
  74. /package/tools/{cli/installers → installer}/install-messages.yaml +0 -0
  75. /package/tools/{cli/lib → installer}/project-root.js +0 -0
  76. /package/tools/{cli/lib → installer}/prompts.js +0 -0
  77. /package/tools/{cli/lib → installer}/yaml-format.js +0 -0
@@ -2,8 +2,8 @@ const path = require('node:path');
2
2
  const os = require('node:os');
3
3
  const fs = require('fs-extra');
4
4
  const { CLIUtils } = require('./cli-utils');
5
- const { CustomHandler } = require('../installers/lib/custom/handler');
6
- const { ExternalModuleManager } = require('../installers/lib/modules/external-manager');
5
+ const { CustomHandler } = require('./custom-handler');
6
+ const { ExternalModuleManager } = require('./modules/external-manager');
7
7
  const prompts = require('./prompts');
8
8
 
9
9
  // Separator class for visual grouping in select/multiselect prompts
@@ -32,7 +32,7 @@ class UI {
32
32
  await CLIUtils.displayLogo();
33
33
 
34
34
  // Display version-specific start message from install-messages.yaml
35
- const { MessageLoader } = require('../installers/lib/message-loader');
35
+ const { MessageLoader } = require('./message-loader');
36
36
  const messageLoader = new MessageLoader();
37
37
  await messageLoader.displayStartMessage();
38
38
 
@@ -51,125 +51,11 @@ class UI {
51
51
  confirmedDirectory = await this.getConfirmedDirectory();
52
52
  }
53
53
 
54
- // Preflight: Check for legacy BMAD v4 footprints immediately after getting directory
55
- const { Detector } = require('../installers/lib/core/detector');
56
- const { Installer } = require('../installers/lib/core/installer');
57
- const detector = new Detector();
54
+ const { Installer } = require('./core/installer');
58
55
  const installer = new Installer();
59
- const legacyV4 = await detector.detectLegacyV4(confirmedDirectory);
60
- if (legacyV4.hasLegacyV4) {
61
- await installer.handleLegacyV4Migration(confirmedDirectory, legacyV4);
62
- }
63
-
64
- // Check for legacy folders and prompt for rename before showing any menus
65
- let hasLegacyCfg = false;
66
- let hasLegacyBmadFolder = false;
67
- let bmadDir = null;
68
- let legacyBmadPath = null;
69
-
70
- // First check for legacy .bmad folder (instead of _bmad)
71
- // Only check if directory exists
72
- if (await fs.pathExists(confirmedDirectory)) {
73
- const entries = await fs.readdir(confirmedDirectory, { withFileTypes: true });
74
- for (const entry of entries) {
75
- if (entry.isDirectory() && (entry.name === '.bmad' || entry.name === 'bmad')) {
76
- hasLegacyBmadFolder = true;
77
- legacyBmadPath = path.join(confirmedDirectory, entry.name);
78
- bmadDir = legacyBmadPath;
79
-
80
- // Check if it has _cfg folder
81
- const cfgPath = path.join(legacyBmadPath, '_cfg');
82
- if (await fs.pathExists(cfgPath)) {
83
- hasLegacyCfg = true;
84
- }
85
- break;
86
- }
87
- }
88
- }
89
-
90
- // If no .bmad or bmad found, check for current installations _bmad
91
- if (!hasLegacyBmadFolder) {
92
- const bmadResult = await installer.findBmadDir(confirmedDirectory);
93
- bmadDir = bmadResult.bmadDir;
94
- hasLegacyCfg = bmadResult.hasLegacyCfg;
95
- }
56
+ const { bmadDir } = await installer.findBmadDir(confirmedDirectory);
96
57
 
97
- // Handle legacy .bmad or _cfg folder - these are very old (v4 or alpha)
98
- // Show version warning instead of offering conversion
99
- if (hasLegacyBmadFolder || hasLegacyCfg) {
100
- await prompts.log.warn('LEGACY INSTALLATION DETECTED');
101
- await prompts.note(
102
- 'Found a ".bmad"/"bmad" folder, or a legacy "_cfg" folder under the bmad folder -\n' +
103
- 'this is from an old BMAD version that is out of date for automatic upgrade,\n' +
104
- 'manual intervention required.\n\n' +
105
- 'You have a legacy version installed (v4 or alpha).\n' +
106
- 'Legacy installations may have compatibility issues.\n\n' +
107
- 'For the best experience, we strongly recommend:\n' +
108
- ' 1. Delete your current BMAD installation folder (.bmad or bmad)\n' +
109
- ' 2. Run a fresh installation\n\n' +
110
- 'If you do not want to start fresh, you can attempt to proceed beyond this\n' +
111
- 'point IF you have ensured the bmad folder is named _bmad, and under it there\n' +
112
- 'is a _config folder. If you have a folder under your bmad folder named _cfg,\n' +
113
- 'you would need to rename it _config, and then restart the installer.\n\n' +
114
- 'Benefits of a fresh install:\n' +
115
- ' \u2022 Cleaner configuration without legacy artifacts\n' +
116
- ' \u2022 All new features properly configured\n' +
117
- ' \u2022 Fewer potential conflicts\n\n' +
118
- 'If you have already produced output from an earlier alpha version, you can\n' +
119
- 'still retain those artifacts. After installation, ensure you configured during\n' +
120
- 'install the proper file locations for artifacts depending on the module you\n' +
121
- 'are using, or move the files to the proper locations.',
122
- 'Legacy Installation Detected',
123
- );
124
-
125
- const proceed = await prompts.select({
126
- message: 'How would you like to proceed?',
127
- choices: [
128
- {
129
- name: 'Cancel and do a fresh install (recommended)',
130
- value: 'cancel',
131
- },
132
- {
133
- name: 'Proceed anyway (will attempt update, potentially may fail or have unstable behavior)',
134
- value: 'proceed',
135
- },
136
- ],
137
- default: 'cancel',
138
- });
139
-
140
- if (proceed === 'cancel') {
141
- await prompts.note('1. Delete the existing bmad folder in your project\n' + "2. Run 'bmad install' again", 'To do a fresh install');
142
- process.exit(0);
143
- return;
144
- }
145
-
146
- const s = await prompts.spinner();
147
- s.start('Updating folder structure...');
148
- try {
149
- // Handle .bmad folder
150
- if (hasLegacyBmadFolder) {
151
- const newBmadPath = path.join(confirmedDirectory, '_bmad');
152
- await fs.move(legacyBmadPath, newBmadPath);
153
- bmadDir = newBmadPath;
154
- s.stop(`Renamed "${path.basename(legacyBmadPath)}" to "_bmad"`);
155
- }
156
-
157
- // Handle _cfg folder (either from .bmad or standalone)
158
- const cfgPath = path.join(bmadDir, '_cfg');
159
- if (await fs.pathExists(cfgPath)) {
160
- s.start('Renaming configuration folder...');
161
- const newCfgPath = path.join(bmadDir, '_config');
162
- await fs.move(cfgPath, newCfgPath);
163
- s.stop('Renamed "_cfg" to "_config"');
164
- }
165
- } catch (error) {
166
- s.stop('Failed to update folder structure');
167
- await prompts.log.error(`Error: ${error.message}`);
168
- process.exit(1);
169
- }
170
- }
171
-
172
- // Check if there's an existing BMAD installation (after any folder renames)
58
+ // Check if there's an existing BMAD installation
173
59
  const hasExistingInstall = await fs.pathExists(bmadDir);
174
60
 
175
61
  let customContentConfig = { hasCustomContent: false };
@@ -184,18 +70,9 @@ class UI {
184
70
  if (hasExistingInstall) {
185
71
  // Get version information
186
72
  const { existingInstall, bmadDir } = await this.getExistingInstallation(confirmedDirectory);
187
- const packageJsonPath = path.join(__dirname, '../../../package.json');
73
+ const packageJsonPath = path.join(__dirname, '../../package.json');
188
74
  const currentVersion = require(packageJsonPath).version;
189
- const installedVersion = existingInstall.version || 'unknown';
190
-
191
- // Check if version is pre beta
192
- const shouldProceed = await this.showLegacyVersionWarning(installedVersion, currentVersion, path.basename(bmadDir), options);
193
-
194
- // If user chose to cancel, exit the installer
195
- if (!shouldProceed) {
196
- process.exit(0);
197
- return;
198
- }
75
+ const installedVersion = existingInstall.installed ? existingInstall.version || 'unknown' : 'unknown';
199
76
 
200
77
  // Build menu choices dynamically
201
78
  const choices = [];
@@ -402,7 +279,7 @@ class UI {
402
279
  customModuleResult = await this.handleCustomModulesInModifyFlow(confirmedDirectory, selectedModules);
403
280
  } else {
404
281
  // Preserve existing custom modules if user doesn't want to modify them
405
- const { Installer } = require('../installers/lib/core/installer');
282
+ const { Installer } = require('./core/installer');
406
283
  const installer = new Installer();
407
284
  const { bmadDir } = await installer.findBmadDir(confirmedDirectory);
408
285
 
@@ -423,22 +300,24 @@ class UI {
423
300
  selectedModules.push(...customModuleResult.selectedCustomModules);
424
301
  }
425
302
 
426
- // Filter out core - it's always installed via installCore flag
427
- selectedModules = selectedModules.filter((m) => m !== 'core');
303
+ // Ensure core is in the modules list
304
+ if (!selectedModules.includes('core')) {
305
+ selectedModules.unshift('core');
306
+ }
428
307
 
429
308
  // Get tool selection
430
309
  const toolSelection = await this.promptToolSelection(confirmedDirectory, options);
431
310
 
432
- const coreConfig = await this.collectCoreConfig(confirmedDirectory, options);
311
+ const moduleConfigs = await this.collectModuleConfigs(confirmedDirectory, selectedModules, options);
433
312
 
434
313
  return {
435
314
  actionType: 'update',
436
315
  directory: confirmedDirectory,
437
- installCore: true,
438
316
  modules: selectedModules,
439
317
  ides: toolSelection.ides,
440
318
  skipIde: toolSelection.skipIde,
441
- coreConfig: coreConfig,
319
+ coreConfig: moduleConfigs.core || {},
320
+ moduleConfigs: moduleConfigs,
442
321
  customContent: customModuleResult.customContentConfig,
443
322
  skipPrompts: options.yes || false,
444
323
  };
@@ -543,18 +422,21 @@ class UI {
543
422
  selectedModules.push(...customContentConfig.selectedModuleIds);
544
423
  }
545
424
 
546
- selectedModules = selectedModules.filter((m) => m !== 'core');
425
+ // Ensure core is in the modules list
426
+ if (!selectedModules.includes('core')) {
427
+ selectedModules.unshift('core');
428
+ }
547
429
  let toolSelection = await this.promptToolSelection(confirmedDirectory, options);
548
- const coreConfig = await this.collectCoreConfig(confirmedDirectory, options);
430
+ const moduleConfigs = await this.collectModuleConfigs(confirmedDirectory, selectedModules, options);
549
431
 
550
432
  return {
551
433
  actionType: 'install',
552
434
  directory: confirmedDirectory,
553
- installCore: true,
554
435
  modules: selectedModules,
555
436
  ides: toolSelection.ides,
556
437
  skipIde: toolSelection.skipIde,
557
- coreConfig: coreConfig,
438
+ coreConfig: moduleConfigs.core || {},
439
+ moduleConfigs: moduleConfigs,
558
440
  customContent: customContentConfig,
559
441
  skipPrompts: options.yes || false,
560
442
  };
@@ -570,18 +452,15 @@ class UI {
570
452
  * @returns {Object} Tool configuration
571
453
  */
572
454
  async promptToolSelection(projectDir, options = {}) {
573
- // Check for existing configured IDEs - use findBmadDir to detect custom folder names
574
- const { Detector } = require('../installers/lib/core/detector');
575
- const { Installer } = require('../installers/lib/core/installer');
576
- const detector = new Detector();
455
+ const { ExistingInstall } = require('./core/existing-install');
456
+ const { Installer } = require('./core/installer');
577
457
  const installer = new Installer();
578
- const bmadResult = await installer.findBmadDir(projectDir || process.cwd());
579
- const bmadDir = bmadResult.bmadDir;
580
- const existingInstall = await detector.detect(bmadDir);
581
- const configuredIdes = existingInstall.ides || [];
458
+ const { bmadDir } = await installer.findBmadDir(projectDir || process.cwd());
459
+ const existingInstall = await ExistingInstall.detect(bmadDir);
460
+ const configuredIdes = existingInstall.ides;
582
461
 
583
462
  // Get IDE manager to fetch available IDEs dynamically
584
- const { IdeManager } = require('../installers/lib/ide/manager');
463
+ const { IdeManager } = require('./ide/manager');
585
464
  const ideManager = new IdeManager();
586
465
  await ideManager.ensureInitialized(); // IMPORTANT: Must initialize before getting IDEs
587
466
 
@@ -811,29 +690,29 @@ class UI {
811
690
  * @returns {Object} Object with existingInstall, installedModuleIds, and bmadDir
812
691
  */
813
692
  async getExistingInstallation(directory) {
814
- const { Detector } = require('../installers/lib/core/detector');
815
- const { Installer } = require('../installers/lib/core/installer');
816
- const detector = new Detector();
693
+ const { ExistingInstall } = require('./core/existing-install');
694
+ const { Installer } = require('./core/installer');
817
695
  const installer = new Installer();
818
- const bmadDirResult = await installer.findBmadDir(directory);
819
- const bmadDir = bmadDirResult.bmadDir;
820
- const existingInstall = await detector.detect(bmadDir);
821
- const installedModuleIds = new Set(existingInstall.modules.map((mod) => mod.id));
696
+ const { bmadDir } = await installer.findBmadDir(directory);
697
+ const existingInstall = await ExistingInstall.detect(bmadDir);
698
+ const installedModuleIds = new Set(existingInstall.moduleIds);
822
699
 
823
700
  return { existingInstall, installedModuleIds, bmadDir };
824
701
  }
825
702
 
826
703
  /**
827
- * Collect core configuration
704
+ * Collect all module configurations (core + selected modules).
705
+ * All interactive prompting happens here in the UI layer.
828
706
  * @param {string} directory - Installation directory
707
+ * @param {string[]} modules - Modules to configure (including 'core')
829
708
  * @param {Object} options - Command-line options
830
- * @returns {Object} Core configuration
709
+ * @returns {Object} Collected module configurations keyed by module name
831
710
  */
832
- async collectCoreConfig(directory, options = {}) {
833
- const { ConfigCollector } = require('../installers/lib/core/config-collector');
834
- const configCollector = new ConfigCollector();
711
+ async collectModuleConfigs(directory, modules, options = {}) {
712
+ const { OfficialModules } = require('./modules/official-modules');
713
+ const configCollector = new OfficialModules();
835
714
 
836
- // If options are provided, set them directly
715
+ // Seed core config from CLI options if provided
837
716
  if (options.userName || options.communicationLanguage || options.documentOutputLanguage || options.outputFolder) {
838
717
  const coreConfig = {};
839
718
  if (options.userName) {
@@ -855,8 +734,6 @@ class UI {
855
734
 
856
735
  // Load existing config to merge with provided options
857
736
  await configCollector.loadExistingConfig(directory);
858
-
859
- // Merge provided options with existing config (or defaults)
860
737
  const existingConfig = configCollector.collectedConfig.core || {};
861
738
  configCollector.collectedConfig.core = { ...existingConfig, ...coreConfig };
862
739
 
@@ -872,7 +749,6 @@ class UI {
872
749
  await configCollector.loadExistingConfig(directory);
873
750
  const existingConfig = configCollector.collectedConfig.core || {};
874
751
 
875
- // If no existing config, use defaults
876
752
  if (Object.keys(existingConfig).length === 0) {
877
753
  let safeUsername;
878
754
  try {
@@ -889,16 +765,14 @@ class UI {
889
765
  };
890
766
  await prompts.log.info('Using default configuration (--yes flag)');
891
767
  }
892
- } else {
893
- // Load existing configs first if they exist
894
- await configCollector.loadExistingConfig(directory);
895
- // Now collect with existing values as defaults (false = don't skip loading, true = skip completion message)
896
- await configCollector.collectModuleConfig('core', directory, false, true);
897
768
  }
898
769
 
899
- const coreConfig = configCollector.collectedConfig.core;
900
- // Ensure we always have a core config object, even if empty
901
- return coreConfig || {};
770
+ // Collect all module configs — core is skipped if already seeded above
771
+ await configCollector.collectAllConfigurations(modules, directory, {
772
+ skipPrompts: options.yes || false,
773
+ });
774
+
775
+ return configCollector.collectedConfig;
902
776
  }
903
777
 
904
778
  /**
@@ -935,9 +809,9 @@ class UI {
935
809
  }
936
810
 
937
811
  // Add official modules
938
- const { ModuleManager } = require('../installers/lib/modules/manager');
939
- const moduleManager = new ModuleManager();
940
- const { modules: availableModules, customModules: customModulesFromCache } = await moduleManager.listAvailable();
812
+ const { OfficialModules } = require('./modules/official-modules');
813
+ const officialModules = new OfficialModules();
814
+ const { modules: availableModules, customModules: customModulesFromCache } = await officialModules.listAvailable();
941
815
 
942
816
  // First, add all items to appropriate sections
943
817
  const allCustomModules = [];
@@ -992,9 +866,9 @@ class UI {
992
866
  * @returns {Array} Selected module codes (excluding core)
993
867
  */
994
868
  async selectAllModules(installedModuleIds = new Set()) {
995
- const { ModuleManager } = require('../installers/lib/modules/manager');
996
- const moduleManager = new ModuleManager();
997
- const { modules: localModules } = await moduleManager.listAvailable();
869
+ const { OfficialModules } = require('./modules/official-modules');
870
+ const officialModulesSource = new OfficialModules();
871
+ const { modules: localModules } = await officialModulesSource.listAvailable();
998
872
 
999
873
  // Get external modules
1000
874
  const externalManager = new ExternalModuleManager();
@@ -1069,7 +943,7 @@ class UI {
1069
943
  maxItems: allOptions.length,
1070
944
  });
1071
945
 
1072
- const result = selected ? selected.filter((m) => m !== 'core') : [];
946
+ const result = selected ? [...selected] : [];
1073
947
 
1074
948
  // Display selected modules as bulleted list
1075
949
  if (result.length > 0) {
@@ -1089,9 +963,9 @@ class UI {
1089
963
  * @returns {Array} Default module codes
1090
964
  */
1091
965
  async getDefaultModules(installedModuleIds = new Set()) {
1092
- const { ModuleManager } = require('../installers/lib/modules/manager');
1093
- const moduleManager = new ModuleManager();
1094
- const { modules: localModules } = await moduleManager.listAvailable();
966
+ const { OfficialModules } = require('./modules/official-modules');
967
+ const officialModules = new OfficialModules();
968
+ const { modules: localModules } = await officialModules.listAvailable();
1095
969
 
1096
970
  const defaultModules = [];
1097
971
 
@@ -1149,7 +1023,7 @@ class UI {
1149
1023
  const files = await fs.readdir(directory);
1150
1024
  if (files.length > 0) {
1151
1025
  // Check for any bmad installation (any folder with _config/manifest.yaml)
1152
- const { Installer } = require('../installers/lib/core/installer');
1026
+ const { Installer } = require('./core/installer');
1153
1027
  const installer = new Installer();
1154
1028
  const bmadResult = await installer.findBmadDir(directory);
1155
1029
  const hasBmadInstall =
@@ -1385,50 +1259,18 @@ class UI {
1385
1259
  return path.resolve(expanded);
1386
1260
  }
1387
1261
 
1388
- /**
1389
- * Load existing configurations to use as defaults
1390
- * @param {string} directory - Installation directory
1391
- * @returns {Object} Existing configurations
1392
- */
1393
- async loadExistingConfigurations(directory) {
1394
- const configs = {
1395
- hasCustomContent: false,
1396
- coreConfig: {},
1397
- ideConfig: { ides: [], skipIde: false },
1398
- };
1399
-
1400
- try {
1401
- // Load core config
1402
- configs.coreConfig = await this.collectCoreConfig(directory);
1403
-
1404
- // Load IDE configuration
1405
- const configuredIdes = await this.getConfiguredIdes(directory);
1406
- if (configuredIdes.length > 0) {
1407
- configs.ideConfig.ides = configuredIdes;
1408
- configs.ideConfig.skipIde = false;
1409
- }
1410
-
1411
- return configs;
1412
- } catch {
1413
- // If loading fails, return empty configs
1414
- await prompts.log.warn('Could not load existing configurations');
1415
- return configs;
1416
- }
1417
- }
1418
-
1419
1262
  /**
1420
1263
  * Get configured IDEs from existing installation
1421
1264
  * @param {string} directory - Installation directory
1422
1265
  * @returns {Array} List of configured IDEs
1423
1266
  */
1424
1267
  async getConfiguredIdes(directory) {
1425
- const { Detector } = require('../installers/lib/core/detector');
1426
- const { Installer } = require('../installers/lib/core/installer');
1427
- const detector = new Detector();
1268
+ const { ExistingInstall } = require('./core/existing-install');
1269
+ const { Installer } = require('./core/installer');
1428
1270
  const installer = new Installer();
1429
- const bmadResult = await installer.findBmadDir(directory);
1430
- const existingInstall = await detector.detect(bmadResult.bmadDir);
1431
- return existingInstall.ides || [];
1271
+ const { bmadDir } = await installer.findBmadDir(directory);
1272
+ const existingInstall = await ExistingInstall.detect(bmadDir);
1273
+ return existingInstall.ides;
1432
1274
  }
1433
1275
 
1434
1276
  /**
@@ -1573,7 +1415,7 @@ class UI {
1573
1415
  const { existingInstall } = await this.getExistingInstallation(directory);
1574
1416
 
1575
1417
  // Check if there are any custom modules in cache
1576
- const { Installer } = require('../installers/lib/core/installer');
1418
+ const { Installer } = require('./core/installer');
1577
1419
  const installer = new Installer();
1578
1420
  const { bmadDir } = await installer.findBmadDir(directory);
1579
1421
 
@@ -1707,82 +1549,6 @@ class UI {
1707
1549
  return result;
1708
1550
  }
1709
1551
 
1710
- /**
1711
- * Check if installed version is a legacy version that needs fresh install
1712
- * @param {string} installedVersion - The installed version
1713
- * @returns {boolean} True if legacy (v4 or any alpha)
1714
- */
1715
- isLegacyVersion(installedVersion) {
1716
- if (!installedVersion || installedVersion === 'unknown') {
1717
- return true; // Treat unknown as legacy for safety
1718
- }
1719
- // Check if version string contains -alpha or -Alpha (any v6 alpha)
1720
- return /-alpha\./i.test(installedVersion);
1721
- }
1722
-
1723
- /**
1724
- * Show warning for legacy version (v4 or alpha) and ask if user wants to proceed
1725
- * @param {string} installedVersion - The installed version
1726
- * @param {string} currentVersion - The current version
1727
- * @param {string} bmadFolderName - Name of the BMAD folder
1728
- * @returns {Promise<boolean>} True if user wants to proceed, false if they cancel
1729
- */
1730
- async showLegacyVersionWarning(installedVersion, currentVersion, bmadFolderName, options = {}) {
1731
- if (!this.isLegacyVersion(installedVersion)) {
1732
- return true; // Not legacy, proceed
1733
- }
1734
-
1735
- let warningContent;
1736
- if (installedVersion === 'unknown') {
1737
- warningContent = 'Unable to detect your installed BMAD version.\n' + 'This appears to be a legacy or unsupported installation.';
1738
- } else {
1739
- warningContent =
1740
- `You are updating from ${installedVersion} to ${currentVersion}.\n` + 'You have a legacy version installed (v4 or alpha).';
1741
- }
1742
-
1743
- warningContent +=
1744
- '\n\nFor the best experience, we recommend:\n' +
1745
- ' 1. Delete your current BMAD installation folder\n' +
1746
- ` (the "${bmadFolderName}/" folder in your project)\n` +
1747
- ' 2. Run a fresh installation\n\n' +
1748
- 'Benefits of a fresh install:\n' +
1749
- ' \u2022 Cleaner configuration without legacy artifacts\n' +
1750
- ' \u2022 All new features properly configured\n' +
1751
- ' \u2022 Fewer potential conflicts';
1752
-
1753
- await prompts.log.warn('VERSION WARNING');
1754
- await prompts.note(warningContent, 'Version Warning');
1755
-
1756
- if (options.yes) {
1757
- await prompts.log.warn('Non-interactive mode (--yes): auto-proceeding with legacy update');
1758
- return true;
1759
- }
1760
-
1761
- const proceed = await prompts.select({
1762
- message: 'How would you like to proceed?',
1763
- choices: [
1764
- {
1765
- name: 'Proceed with update anyway (may have issues)',
1766
- value: 'proceed',
1767
- },
1768
- {
1769
- name: 'Cancel (recommended - do a fresh install instead)',
1770
- value: 'cancel',
1771
- },
1772
- ],
1773
- default: 'cancel',
1774
- });
1775
-
1776
- if (proceed === 'cancel') {
1777
- await prompts.note(
1778
- `1. Delete the "${bmadFolderName}/" folder in your project\n` + "2. Run 'bmad install' again",
1779
- 'To do a fresh install',
1780
- );
1781
- }
1782
-
1783
- return proceed === 'proceed';
1784
- }
1785
-
1786
1552
  /**
1787
1553
  * Display module versions with update availability
1788
1554
  * @param {Array} modules - Array of module info objects with version info
@@ -0,0 +1,5 @@
1
+ # JavaScript Conventions
2
+
3
+ ## Function ordering
4
+
5
+ Define functions top-to-bottom in call order: callers above callees. If `install()` calls `_initPaths()`, then `install` appears first and `_initPaths` appears after it.
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * BMad Method CLI - Direct execution wrapper for npx
5
- * This file ensures proper execution when run via npx from GitHub or npm registry
6
- */
7
-
8
- const { execFileSync } = require('node:child_process');
9
- const path = require('node:path');
10
- const fs = require('node:fs');
11
-
12
- // Check if we're running in an npx temporary directory
13
- const isNpxExecution = __dirname.includes('_npx') || __dirname.includes('.npm');
14
-
15
- if (isNpxExecution) {
16
- // Running via npx - spawn child process to preserve user's working directory
17
- const args = process.argv.slice(2);
18
- const bmadCliPath = path.join(__dirname, 'cli', 'bmad-cli.js');
19
-
20
- if (!fs.existsSync(bmadCliPath)) {
21
- console.error('Error: Could not find bmad-cli.js at', bmadCliPath);
22
- console.error('Current directory:', __dirname);
23
- process.exit(1);
24
- }
25
-
26
- try {
27
- // Execute CLI from user's working directory (process.cwd()), not npm cache
28
- execFileSync('node', [bmadCliPath, ...args], {
29
- stdio: 'inherit',
30
- cwd: process.cwd(), // This preserves the user's working directory
31
- });
32
- } catch (error) {
33
- process.exit(error.status || 1);
34
- }
35
- } else {
36
- // Local execution - use require
37
- require('./cli/bmad-cli.js');
38
- }