bmad-method 6.2.3-next.24 → 6.2.3-next.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +4 -5
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +7 -7
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +0 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +1 -5
- package/tools/installer/commands/install.js +0 -1
- package/tools/installer/core/existing-install.js +2 -8
- package/tools/installer/core/install-paths.js +0 -3
- package/tools/installer/core/installer.js +5 -396
- package/tools/installer/core/manifest-generator.js +0 -9
- package/tools/installer/core/manifest.js +1 -70
- package/tools/installer/modules/official-modules.js +14 -64
- package/tools/installer/ui.js +1 -613
- package/tools/installer/core/custom-module-cache.js +0 -260
- package/tools/installer/custom-handler.js +0 -112
- package/tools/installer/modules/custom-modules.js +0 -302
|
@@ -375,8 +375,6 @@ class ManifestGenerator {
|
|
|
375
375
|
// Read existing manifest to preserve install date
|
|
376
376
|
let existingInstallDate = null;
|
|
377
377
|
const existingModulesMap = new Map();
|
|
378
|
-
let existingCustomModules = [];
|
|
379
|
-
|
|
380
378
|
if (await fs.pathExists(manifestPath)) {
|
|
381
379
|
try {
|
|
382
380
|
const existingContent = await fs.readFile(manifestPath, 'utf8');
|
|
@@ -397,12 +395,6 @@ class ManifestGenerator {
|
|
|
397
395
|
}
|
|
398
396
|
}
|
|
399
397
|
}
|
|
400
|
-
|
|
401
|
-
if (existingManifest.customModules && Array.isArray(existingManifest.customModules)) {
|
|
402
|
-
// We filter here so manifest regeneration preserves source metadata only for custom modules that
|
|
403
|
-
// are still installed. Without that, customModules can retain stale entries for modules that were removed.
|
|
404
|
-
existingCustomModules = existingManifest.customModules.filter((customModule) => installedModuleSet.has(customModule?.id));
|
|
405
|
-
}
|
|
406
398
|
} catch {
|
|
407
399
|
// If we can't read existing manifest, continue with defaults
|
|
408
400
|
}
|
|
@@ -438,7 +430,6 @@ class ManifestGenerator {
|
|
|
438
430
|
lastUpdated: new Date().toISOString(),
|
|
439
431
|
},
|
|
440
432
|
modules: updatedModules,
|
|
441
|
-
customModules: existingCustomModules,
|
|
442
433
|
ides: this.selectedIdes,
|
|
443
434
|
};
|
|
444
435
|
|
|
@@ -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
|
|
@@ -866,29 +818,8 @@ class Manifest {
|
|
|
866
818
|
};
|
|
867
819
|
}
|
|
868
820
|
|
|
869
|
-
// Custom module: resolve path from source or cache before reading version
|
|
870
|
-
const customSourcePath = moduleSourcePath || path.join(bmadDir, '_config', 'custom', moduleName);
|
|
871
|
-
const version = await this._readMarketplaceVersion(moduleName, customSourcePath);
|
|
872
|
-
|
|
873
|
-
const cacheDir = path.join(bmadDir, '_config', 'custom', moduleName);
|
|
874
|
-
const moduleYamlPath = path.join(cacheDir, 'module.yaml');
|
|
875
|
-
|
|
876
|
-
if (await fs.pathExists(moduleYamlPath)) {
|
|
877
|
-
try {
|
|
878
|
-
const yamlContent = await fs.readFile(moduleYamlPath, 'utf8');
|
|
879
|
-
const moduleConfig = yaml.parse(yamlContent);
|
|
880
|
-
return {
|
|
881
|
-
version: version || moduleConfig.version || null,
|
|
882
|
-
source: 'custom',
|
|
883
|
-
npmPackage: moduleConfig.npmPackage || null,
|
|
884
|
-
repoUrl: moduleConfig.repoUrl || null,
|
|
885
|
-
};
|
|
886
|
-
} catch (error) {
|
|
887
|
-
await prompts.log.warn(`Failed to read module.yaml for ${moduleName}: ${error.message}`);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
821
|
// Unknown module
|
|
822
|
+
const version = await this._readMarketplaceVersion(moduleName, moduleSourcePath);
|
|
892
823
|
return {
|
|
893
824
|
version,
|
|
894
825
|
source: 'unknown',
|
|
@@ -98,11 +98,10 @@ class OfficialModules {
|
|
|
98
98
|
/**
|
|
99
99
|
* List all available built-in modules (core and bmm).
|
|
100
100
|
* All other modules come from external-official-modules.yaml
|
|
101
|
-
* @returns {Object} Object with modules array
|
|
101
|
+
* @returns {Object} Object with modules array
|
|
102
102
|
*/
|
|
103
103
|
async listAvailable() {
|
|
104
104
|
const modules = [];
|
|
105
|
-
const customModules = [];
|
|
106
105
|
|
|
107
106
|
// Add built-in core module (directly under src/core-skills)
|
|
108
107
|
const corePath = getSourcePath('core-skills');
|
|
@@ -122,7 +121,7 @@ class OfficialModules {
|
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
return { modules
|
|
124
|
+
return { modules };
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
/**
|
|
@@ -133,25 +132,12 @@ class OfficialModules {
|
|
|
133
132
|
* @returns {Object|null} Module info or null if not a valid module
|
|
134
133
|
*/
|
|
135
134
|
async getModuleInfo(modulePath, defaultName, sourceDescription) {
|
|
136
|
-
// Check for module structure (module.yaml OR custom.yaml)
|
|
137
135
|
const moduleConfigPath = path.join(modulePath, 'module.yaml');
|
|
138
|
-
const rootCustomConfigPath = path.join(modulePath, 'custom.yaml');
|
|
139
|
-
let configPath = null;
|
|
140
136
|
|
|
141
|
-
if (await fs.pathExists(moduleConfigPath)) {
|
|
142
|
-
configPath = moduleConfigPath;
|
|
143
|
-
} else if (await fs.pathExists(rootCustomConfigPath)) {
|
|
144
|
-
configPath = rootCustomConfigPath;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Skip if this doesn't look like a module
|
|
148
|
-
if (!configPath) {
|
|
137
|
+
if (!(await fs.pathExists(moduleConfigPath))) {
|
|
149
138
|
return null;
|
|
150
139
|
}
|
|
151
140
|
|
|
152
|
-
// Mark as custom if it's using custom.yaml OR if it's outside src/bmm or src/core
|
|
153
|
-
const isCustomSource =
|
|
154
|
-
sourceDescription !== 'src/bmm-skills' && sourceDescription !== 'src/core-skills' && sourceDescription !== 'src/modules';
|
|
155
141
|
const moduleInfo = {
|
|
156
142
|
id: defaultName,
|
|
157
143
|
path: modulePath,
|
|
@@ -162,12 +148,11 @@ class OfficialModules {
|
|
|
162
148
|
description: 'BMAD Module',
|
|
163
149
|
version: '5.0.0',
|
|
164
150
|
source: sourceDescription,
|
|
165
|
-
isCustom: configPath === rootCustomConfigPath || isCustomSource,
|
|
166
151
|
};
|
|
167
152
|
|
|
168
153
|
// Read module config for metadata
|
|
169
154
|
try {
|
|
170
|
-
const configContent = await fs.readFile(
|
|
155
|
+
const configContent = await fs.readFile(moduleConfigPath, 'utf8');
|
|
171
156
|
const config = yaml.parse(configContent);
|
|
172
157
|
|
|
173
158
|
// Use the code property as the id if available
|
|
@@ -824,20 +809,15 @@ class OfficialModules {
|
|
|
824
809
|
const results = [];
|
|
825
810
|
|
|
826
811
|
for (const moduleName of modules) {
|
|
827
|
-
// Resolve module.yaml path -
|
|
812
|
+
// Resolve module.yaml path - standard location first, then OfficialModules search
|
|
828
813
|
let moduleConfigPath = null;
|
|
829
|
-
const
|
|
830
|
-
if (
|
|
831
|
-
moduleConfigPath =
|
|
814
|
+
const standardPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
815
|
+
if (await fs.pathExists(standardPath)) {
|
|
816
|
+
moduleConfigPath = standardPath;
|
|
832
817
|
} else {
|
|
833
|
-
const
|
|
834
|
-
if (
|
|
835
|
-
moduleConfigPath =
|
|
836
|
-
} else {
|
|
837
|
-
const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
|
|
838
|
-
if (moduleSourcePath) {
|
|
839
|
-
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
|
|
840
|
-
}
|
|
818
|
+
const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
|
|
819
|
+
if (moduleSourcePath) {
|
|
820
|
+
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
|
|
841
821
|
}
|
|
842
822
|
}
|
|
843
823
|
|
|
@@ -882,12 +862,9 @@ class OfficialModules {
|
|
|
882
862
|
* @param {Array} modules - List of modules to configure (including 'core')
|
|
883
863
|
* @param {string} projectDir - Target project directory
|
|
884
864
|
* @param {Object} options - Additional options
|
|
885
|
-
* @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
|
|
886
865
|
* @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
|
|
887
866
|
*/
|
|
888
867
|
async collectAllConfigurations(modules, projectDir, options = {}) {
|
|
889
|
-
// Store custom module paths for use in collectModuleConfig
|
|
890
|
-
this.customModulePaths = options.customModulePaths || new Map();
|
|
891
868
|
this.skipPrompts = options.skipPrompts || false;
|
|
892
869
|
this.modulesToCustomize = undefined;
|
|
893
870
|
await this.loadExistingConfig(projectDir);
|
|
@@ -1042,25 +1019,7 @@ class OfficialModules {
|
|
|
1042
1019
|
}
|
|
1043
1020
|
}
|
|
1044
1021
|
|
|
1045
|
-
|
|
1046
|
-
let isCustomModule = false;
|
|
1047
|
-
|
|
1048
|
-
if (await fs.pathExists(moduleConfigPath)) {
|
|
1049
|
-
configPath = moduleConfigPath;
|
|
1050
|
-
} else {
|
|
1051
|
-
// Check if this is a custom module with custom.yaml
|
|
1052
|
-
const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
|
|
1053
|
-
|
|
1054
|
-
if (moduleSourcePath) {
|
|
1055
|
-
const rootCustomConfigPath = path.join(moduleSourcePath, 'custom.yaml');
|
|
1056
|
-
|
|
1057
|
-
if (await fs.pathExists(rootCustomConfigPath)) {
|
|
1058
|
-
isCustomModule = true;
|
|
1059
|
-
// For custom modules, we don't have an install-config schema, so just use existing values
|
|
1060
|
-
// The custom.yaml values will be loaded and merged during installation
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1022
|
+
if (!(await fs.pathExists(moduleConfigPath))) {
|
|
1064
1023
|
// No config schema for this module - use existing values
|
|
1065
1024
|
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
1066
1025
|
if (!this.collectedConfig[moduleName]) {
|
|
@@ -1071,7 +1030,7 @@ class OfficialModules {
|
|
|
1071
1030
|
return false;
|
|
1072
1031
|
}
|
|
1073
1032
|
|
|
1074
|
-
const configContent = await fs.readFile(
|
|
1033
|
+
const configContent = await fs.readFile(moduleConfigPath, 'utf8');
|
|
1075
1034
|
const moduleConfig = yaml.parse(configContent);
|
|
1076
1035
|
|
|
1077
1036
|
if (!moduleConfig) {
|
|
@@ -1332,16 +1291,7 @@ class OfficialModules {
|
|
|
1332
1291
|
this.allAnswers = {};
|
|
1333
1292
|
}
|
|
1334
1293
|
// Load module's config
|
|
1335
|
-
|
|
1336
|
-
let moduleConfigPath = null;
|
|
1337
|
-
|
|
1338
|
-
if (this.customModulePaths && this.customModulePaths.has(moduleName)) {
|
|
1339
|
-
const customPath = this.customModulePaths.get(moduleName);
|
|
1340
|
-
moduleConfigPath = path.join(customPath, 'module.yaml');
|
|
1341
|
-
} else {
|
|
1342
|
-
// Try the standard src/modules location
|
|
1343
|
-
moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
1344
|
-
}
|
|
1294
|
+
let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
1345
1295
|
|
|
1346
1296
|
// If not found in src/modules or custom paths, search the project
|
|
1347
1297
|
if (!(await fs.pathExists(moduleConfigPath))) {
|