fix: preserve installed community/custom modules in modify flow
When a user does "Modify Installation" and declines to browse community modules, previously installed community/custom modules are now auto-kept. If the user does browse, their selections are trusted (they can deselect). Also fix stale docs: class doc for SHA pinning, JSDoc return type.
This commit is contained in:
parent
cccc8218fb
commit
f06f21fb79
|
|
@ -13,7 +13,7 @@ const CATEGORIES_URL = `${MARKETPLACE_BASE}/categories.yaml`;
|
||||||
* Manages community modules from the BMad marketplace registry.
|
* Manages community modules from the BMad marketplace registry.
|
||||||
* Fetches community-index.yaml and categories.yaml from GitHub.
|
* Fetches community-index.yaml and categories.yaml from GitHub.
|
||||||
* Returns empty results when the registry is unreachable.
|
* Returns empty results when the registry is unreachable.
|
||||||
* Community modules are pinned to approved tags (not HEAD).
|
* Community modules are pinned to approved SHA when set; uses HEAD otherwise.
|
||||||
*/
|
*/
|
||||||
class CommunityModuleManager {
|
class CommunityModuleManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
||||||
|
|
@ -571,14 +571,34 @@ class UI {
|
||||||
// Phase 1: Official modules
|
// Phase 1: Official modules
|
||||||
const officialSelected = await this._selectOfficialModules(installedModuleIds);
|
const officialSelected = await this._selectOfficialModules(installedModuleIds);
|
||||||
|
|
||||||
|
// Determine which installed modules are NOT official (community or custom).
|
||||||
|
// These must be preserved even if the user declines to browse community/custom.
|
||||||
|
const officialCodes = new Set(officialSelected);
|
||||||
|
const externalManager = new ExternalModuleManager();
|
||||||
|
const registryModules = await externalManager.listAvailable();
|
||||||
|
const officialRegistryCodes = new Set(registryModules.map((m) => m.code));
|
||||||
|
const installedNonOfficial = [...installedModuleIds].filter((id) => !officialRegistryCodes.has(id));
|
||||||
|
|
||||||
// Phase 2: Community modules (category drill-down)
|
// Phase 2: Community modules (category drill-down)
|
||||||
const communitySelected = await this._browseCommunityModules(installedModuleIds);
|
// Returns { codes, didBrowse } so we know if the user entered the flow
|
||||||
|
const communityResult = await this._browseCommunityModules(installedModuleIds);
|
||||||
|
|
||||||
// Phase 3: Custom URL modules
|
// Phase 3: Custom URL modules
|
||||||
const customSelected = await this._addCustomUrlModules(installedModuleIds);
|
const customSelected = await this._addCustomUrlModules(installedModuleIds);
|
||||||
|
|
||||||
// Merge all selections
|
// Merge all selections
|
||||||
return [...officialSelected, ...communitySelected, ...customSelected];
|
const allSelected = new Set([...officialSelected, ...communityResult.codes, ...customSelected]);
|
||||||
|
|
||||||
|
// Auto-include installed non-official modules that the user didn't get
|
||||||
|
// a chance to manage (they declined to browse). If they did browse,
|
||||||
|
// trust their selections - they could have deselected intentionally.
|
||||||
|
if (!communityResult.didBrowse) {
|
||||||
|
for (const code of installedNonOfficial) {
|
||||||
|
allSelected.add(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...allSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -641,14 +661,14 @@ class UI {
|
||||||
* Browse and select community modules using category drill-down.
|
* Browse and select community modules using category drill-down.
|
||||||
* Featured/promoted modules appear at the top.
|
* Featured/promoted modules appear at the top.
|
||||||
* @param {Set} installedModuleIds - Currently installed module IDs
|
* @param {Set} installedModuleIds - Currently installed module IDs
|
||||||
* @returns {Array} Selected community module codes
|
* @returns {Object} { codes: string[], didBrowse: boolean }
|
||||||
*/
|
*/
|
||||||
async _browseCommunityModules(installedModuleIds = new Set()) {
|
async _browseCommunityModules(installedModuleIds = new Set()) {
|
||||||
const browseCommunity = await prompts.confirm({
|
const browseCommunity = await prompts.confirm({
|
||||||
message: 'Would you like to browse community modules?',
|
message: 'Would you like to browse community modules?',
|
||||||
default: false,
|
default: false,
|
||||||
});
|
});
|
||||||
if (!browseCommunity) return [];
|
if (!browseCommunity) return { codes: [], didBrowse: false };
|
||||||
|
|
||||||
const { CommunityModuleManager } = require('./modules/community-manager');
|
const { CommunityModuleManager } = require('./modules/community-manager');
|
||||||
const communityMgr = new CommunityModuleManager();
|
const communityMgr = new CommunityModuleManager();
|
||||||
|
|
@ -667,12 +687,12 @@ class UI {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
s.error('Failed to load community catalog');
|
s.error('Failed to load community catalog');
|
||||||
await prompts.log.warn(` ${error.message}`);
|
await prompts.log.warn(` ${error.message}`);
|
||||||
return [];
|
return { codes: [], didBrowse: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allCommunity.length === 0) {
|
if (allCommunity.length === 0) {
|
||||||
await prompts.log.info('No community modules are currently available.');
|
await prompts.log.info('No community modules are currently available.');
|
||||||
return [];
|
return { codes: [], didBrowse: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCodes = new Set();
|
const selectedCodes = new Set();
|
||||||
|
|
@ -794,13 +814,13 @@ class UI {
|
||||||
await prompts.log.message('Selected community modules:\n' + moduleLines.join('\n'));
|
await prompts.log.message('Selected community modules:\n' + moduleLines.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...selectedCodes];
|
return { codes: [...selectedCodes], didBrowse: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt user to install modules from custom GitHub URLs.
|
* Prompt user to install modules from custom GitHub URLs.
|
||||||
* @param {Set} installedModuleIds - Currently installed module IDs
|
* @param {Set} installedModuleIds - Currently installed module IDs
|
||||||
* @returns {Array} Selected custom module objects with metadata
|
* @returns {Array} Selected custom module code strings
|
||||||
*/
|
*/
|
||||||
async _addCustomUrlModules(installedModuleIds = new Set()) {
|
async _addCustomUrlModules(installedModuleIds = new Set()) {
|
||||||
const addCustom = await prompts.confirm({
|
const addCustom = await prompts.confirm({
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue