Compare commits
No commits in common. "cd6e17610d37691bf8ea859723cb21788e0b4f38" and "c31e334dd80f7ae2e01fa026c45989d374222e5e" have entirely different histories.
cd6e17610d
...
c31e334dd8
|
|
@ -20,8 +20,8 @@
|
||||||
"author": "Brian (BMad) Madison",
|
"author": "Brian (BMad) Madison",
|
||||||
"main": "tools/cli/bmad-cli.js",
|
"main": "tools/cli/bmad-cli.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"bmad": "tools/cli/bmad-cli.js",
|
"bmad": "tools/bmad-npx-wrapper.js",
|
||||||
"bmad-method": "tools/cli/bmad-cli.js"
|
"bmad-method": "tools/bmad-npx-wrapper.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bmad:install": "node tools/cli/bmad-cli.js install",
|
"bmad:install": "node tools/cli/bmad-cli.js install",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BMad Method CLI - Direct execution wrapper for npx
|
||||||
|
* This file ensures proper execution when run via npx from GitHub or npm registry
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { execSync } = require('node:child_process');
|
||||||
|
const path = require('node:path');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
|
||||||
|
// Check if we're running in an npx temporary directory
|
||||||
|
const isNpxExecution = __dirname.includes('_npx') || __dirname.includes('.npm');
|
||||||
|
|
||||||
|
if (isNpxExecution) {
|
||||||
|
// Running via npx - spawn child process to preserve user's working directory
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const bmadCliPath = path.join(__dirname, 'cli', 'bmad-cli.js');
|
||||||
|
|
||||||
|
if (!fs.existsSync(bmadCliPath)) {
|
||||||
|
console.error('Error: Could not find bmad-cli.js at', bmadCliPath);
|
||||||
|
console.error('Current directory:', __dirname);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Execute CLI from user's working directory (process.cwd()), not npm cache
|
||||||
|
execSync(`node "${bmadCliPath}" ${args.join(' ')}`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: process.cwd(), // This preserves the user's working directory
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
process.exit(error.status || 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Local execution - use require
|
||||||
|
require('./cli/bmad-cli.js');
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const { program } = require('commander');
|
const { program } = require('commander');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const { FileOps } = require('../../../lib/file-ops');
|
||||||
const { Config } = require('./config');
|
const { Config } = require('./config');
|
||||||
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
|
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
|
||||||
const { ManifestGenerator } = require('./manifest-generator');
|
const { ManifestGenerator } = require('./manifest-generator');
|
||||||
|
const { CustomHandler } = require('../custom-handler');
|
||||||
const prompts = require('../../../lib/prompts');
|
const prompts = require('../../../lib/prompts');
|
||||||
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
||||||
const { InstallPaths } = require('./install-paths');
|
const { InstallPaths } = require('./install-paths');
|
||||||
|
|
@ -40,6 +41,7 @@ class Installer {
|
||||||
const config = Config.build(originalConfig);
|
const config = Config.build(originalConfig);
|
||||||
const paths = await InstallPaths.create(config);
|
const paths = await InstallPaths.create(config);
|
||||||
const officialModules = await OfficialModules.build(config, paths);
|
const officialModules = await OfficialModules.build(config, paths);
|
||||||
|
|
||||||
const existingInstall = await ExistingInstall.detect(paths.bmadDir);
|
const existingInstall = await ExistingInstall.detect(paths.bmadDir);
|
||||||
|
|
||||||
await this.customModules.discoverPaths(config, paths);
|
await this.customModules.discoverPaths(config, paths);
|
||||||
|
|
@ -61,11 +63,7 @@ class Installer {
|
||||||
|
|
||||||
await this._cacheCustomModules(paths, addResult);
|
await this._cacheCustomModules(paths, addResult);
|
||||||
|
|
||||||
// Compute module lists: official = selected minus custom, all = both
|
const { officialModuleIds, allModules } = await this._buildModuleLists(config, customConfig, paths);
|
||||||
const customModuleIds = new Set(this.customModules.paths.keys());
|
|
||||||
const officialModuleIds = (config.modules || []).filter((m) => !customModuleIds.has(m));
|
|
||||||
const allModules = [...officialModuleIds, ...[...customModuleIds].filter((id) => !officialModuleIds.includes(id))];
|
|
||||||
|
|
||||||
await this._installAndConfigure(config, customConfig, paths, officialModuleIds, allModules, addResult, officialModules);
|
await this._installAndConfigure(config, customConfig, paths, officialModuleIds, allModules, addResult, officialModules);
|
||||||
|
|
||||||
await this._setupIdes(config, allModules, paths, addResult);
|
await this._setupIdes(config, allModules, paths, addResult);
|
||||||
|
|
@ -197,6 +195,51 @@ class Installer {
|
||||||
addResult('Custom modules cached', 'ok');
|
addResult('Custom modules cached', 'ok');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the official and combined module lists from config and custom sources.
|
||||||
|
* @returns {{ officialModuleIds: string[], allModules: string[] }}
|
||||||
|
*/
|
||||||
|
async _buildModuleLists(config, customConfig, paths) {
|
||||||
|
const finalCustomContent = customConfig.customContent;
|
||||||
|
|
||||||
|
const customModuleIds = new Set();
|
||||||
|
for (const id of this.customModules.paths.keys()) {
|
||||||
|
customModuleIds.add(id);
|
||||||
|
}
|
||||||
|
if (customConfig._customModuleSources) {
|
||||||
|
for (const [moduleId, customInfo] of customConfig._customModuleSources) {
|
||||||
|
if (!customModuleIds.has(moduleId) && (await fs.pathExists(customInfo.sourcePath))) {
|
||||||
|
customModuleIds.add(moduleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finalCustomContent && finalCustomContent.cachedModules) {
|
||||||
|
for (const cachedModule of finalCustomContent.cachedModules) {
|
||||||
|
customModuleIds.add(cachedModule.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
for (const customFile of finalCustomContent.selectedFiles) {
|
||||||
|
const customInfo = await customHandler.getCustomInfo(customFile, paths.projectRoot);
|
||||||
|
if (customInfo && customInfo.id) {
|
||||||
|
customModuleIds.add(customInfo.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const officialModuleIds = (config.modules || []).filter((m) => !customModuleIds.has(m));
|
||||||
|
|
||||||
|
const allModules = [...officialModuleIds];
|
||||||
|
for (const id of customModuleIds) {
|
||||||
|
if (!allModules.includes(id)) {
|
||||||
|
allModules.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { officialModuleIds, allModules };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install modules, create directories, generate configs and manifests.
|
* Install modules, create directories, generate configs and manifests.
|
||||||
*/
|
*/
|
||||||
|
|
@ -220,7 +263,7 @@ class Installer {
|
||||||
installedModuleNames,
|
installedModuleNames,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this._installCustomModules(config, paths, addResult, officialModules, {
|
await this._installCustomModules(customConfig, paths, finalCustomContent, addResult, isQuickUpdate, officialModules, {
|
||||||
message,
|
message,
|
||||||
installedModuleNames,
|
installedModuleNames,
|
||||||
});
|
});
|
||||||
|
|
@ -583,27 +626,88 @@ class Installer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install custom modules using CustomModules.install().
|
* Install custom modules from all custom module sources.
|
||||||
* Source paths come from this.customModules.paths (populated by discoverPaths).
|
* @param {Object} config - Installation configuration
|
||||||
|
* @param {Object} paths - InstallPaths instance
|
||||||
|
* @param {Object|undefined} finalCustomContent - Custom content from config
|
||||||
|
* @param {Function} addResult - Callback to record installation results
|
||||||
|
* @param {boolean} isQuickUpdate - Whether this is a quick update
|
||||||
|
* @param {Object} ctx - Shared context: { message, installedModuleNames }
|
||||||
*/
|
*/
|
||||||
async _installCustomModules(config, paths, addResult, officialModules, ctx) {
|
async _installCustomModules(customConfig, paths, finalCustomContent, addResult, isQuickUpdate, officialModules, ctx) {
|
||||||
const { message, installedModuleNames } = ctx;
|
const { message, installedModuleNames } = ctx;
|
||||||
const isQuickUpdate = config.isQuickUpdate();
|
|
||||||
|
|
||||||
for (const [moduleName, sourcePath] of this.customModules.paths) {
|
// Collect all custom module IDs with their info from all sources
|
||||||
|
const customModules = new Map();
|
||||||
|
|
||||||
|
// First: cached modules from finalCustomContent
|
||||||
|
if (finalCustomContent && finalCustomContent.cachedModules) {
|
||||||
|
for (const cachedModule of finalCustomContent.cachedModules) {
|
||||||
|
if (!customModules.has(cachedModule.id)) {
|
||||||
|
customModules.set(cachedModule.id, { id: cachedModule.id, path: cachedModule.cachePath, config: {} });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second: custom module sources from manifest (for quick update)
|
||||||
|
if (customConfig._customModuleSources) {
|
||||||
|
for (const [moduleId, customInfo] of customConfig._customModuleSources) {
|
||||||
|
if (!customModules.has(moduleId)) {
|
||||||
|
const info = { ...customInfo };
|
||||||
|
if (info.sourcePath && !info.path) {
|
||||||
|
info.path = path.isAbsolute(info.sourcePath) ? info.sourcePath : path.join(paths.bmadDir, info.sourcePath);
|
||||||
|
}
|
||||||
|
customModules.set(moduleId, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third: regular custom content from user input (non-cached)
|
||||||
|
if (finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
for (const customFile of finalCustomContent.selectedFiles) {
|
||||||
|
const info = await customHandler.getCustomInfo(customFile, paths.projectRoot);
|
||||||
|
if (info && info.id && !customModules.has(info.id)) {
|
||||||
|
customModules.set(info.id, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fourth: any remaining custom modules not yet covered
|
||||||
|
for (const [moduleId, modulePath] of this.customModules.paths) {
|
||||||
|
if (!customModules.has(moduleId)) {
|
||||||
|
customModules.set(moduleId, { id: moduleId, path: modulePath, config: {} });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [moduleName, customInfo] of customModules) {
|
||||||
if (installedModuleNames.has(moduleName)) continue;
|
if (installedModuleNames.has(moduleName)) continue;
|
||||||
installedModuleNames.add(moduleName);
|
installedModuleNames.add(moduleName);
|
||||||
|
|
||||||
message(`${isQuickUpdate ? 'Updating' : 'Installing'} ${moduleName}...`);
|
message(`${isQuickUpdate ? 'Updating' : 'Installing'} ${moduleName}...`);
|
||||||
|
|
||||||
const collectedModuleConfig = officialModules.moduleConfigs[moduleName] || {};
|
if (!this.customModules.paths.has(moduleName) && customInfo.path) {
|
||||||
const result = await this.customModules.install(moduleName, paths.bmadDir, (filePath) => this.installedFiles.add(filePath), {
|
this.customModules.paths.set(moduleName, customInfo.path);
|
||||||
moduleConfig: collectedModuleConfig,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Generate runtime config.yaml with merged values
|
const collectedModuleConfig = officialModules.moduleConfigs[moduleName] || {};
|
||||||
|
await officialModules.install(
|
||||||
|
moduleName,
|
||||||
|
paths.bmadDir,
|
||||||
|
(filePath) => {
|
||||||
|
this.installedFiles.add(filePath);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCustom: true,
|
||||||
|
moduleConfig: collectedModuleConfig,
|
||||||
|
isQuickUpdate: isQuickUpdate,
|
||||||
|
installer: this,
|
||||||
|
silent: true,
|
||||||
|
sourcePath: customInfo.path,
|
||||||
|
},
|
||||||
|
);
|
||||||
await this.generateModuleConfigs(paths.bmadDir, {
|
await this.generateModuleConfigs(paths.bmadDir, {
|
||||||
[moduleName]: { ...config.coreConfig, ...result.moduleConfig, ...collectedModuleConfig },
|
[moduleName]: { ...customConfig.coreConfig, ...customInfo.config, ...collectedModuleConfig },
|
||||||
});
|
});
|
||||||
|
|
||||||
addResult(`Module: ${moduleName}`, 'ok', isQuickUpdate ? 'updated' : 'installed');
|
addResult(`Module: ${moduleName}`, 'ok', isQuickUpdate ? 'updated' : 'installed');
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const fs = require('fs-extra');
|
|
||||||
const yaml = require('yaml');
|
|
||||||
const { CustomHandler } = require('../custom-handler');
|
const { CustomHandler } = require('../custom-handler');
|
||||||
const { Manifest } = require('../core/manifest');
|
|
||||||
const prompts = require('../../../lib/prompts');
|
|
||||||
|
|
||||||
class CustomModules {
|
class CustomModules {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -22,126 +18,6 @@ class CustomModules {
|
||||||
this.paths.set(moduleId, sourcePath);
|
this.paths.set(moduleId, sourcePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Install a custom module from its source path.
|
|
||||||
* @param {string} moduleName - Module identifier
|
|
||||||
* @param {string} bmadDir - Target bmad directory
|
|
||||||
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
|
||||||
* @param {Object} options - Install options
|
|
||||||
* @param {Object} options.moduleConfig - Pre-collected module configuration
|
|
||||||
* @returns {Object} Install result
|
|
||||||
*/
|
|
||||||
async install(moduleName, bmadDir, fileTrackingCallback = null, options = {}) {
|
|
||||||
const sourcePath = this.paths.get(moduleName);
|
|
||||||
if (!sourcePath) {
|
|
||||||
throw new Error(`No source path for custom module '${moduleName}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(await fs.pathExists(sourcePath))) {
|
|
||||||
throw new Error(`Source for custom module '${moduleName}' not found at: ${sourcePath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetPath = path.join(bmadDir, moduleName);
|
|
||||||
|
|
||||||
// Read custom.yaml and merge into module config
|
|
||||||
let moduleConfig = options.moduleConfig ? { ...options.moduleConfig } : {};
|
|
||||||
const customConfigPath = path.join(sourcePath, 'custom.yaml');
|
|
||||||
if (await fs.pathExists(customConfigPath)) {
|
|
||||||
try {
|
|
||||||
const content = await fs.readFile(customConfigPath, 'utf8');
|
|
||||||
const customConfig = yaml.parse(content);
|
|
||||||
if (customConfig) {
|
|
||||||
moduleConfig = { ...moduleConfig, ...customConfig };
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
await prompts.log.warn(`Failed to read custom.yaml for ${moduleName}: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove existing installation
|
|
||||||
if (await fs.pathExists(targetPath)) {
|
|
||||||
await fs.remove(targetPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy files with filtering
|
|
||||||
await this._copyWithFiltering(sourcePath, targetPath, fileTrackingCallback);
|
|
||||||
|
|
||||||
// Add to manifest
|
|
||||||
const manifest = new Manifest();
|
|
||||||
const versionInfo = await manifest.getModuleVersionInfo(moduleName, bmadDir, sourcePath);
|
|
||||||
await manifest.addModule(bmadDir, moduleName, {
|
|
||||||
version: versionInfo.version,
|
|
||||||
source: versionInfo.source,
|
|
||||||
npmPackage: versionInfo.npmPackage,
|
|
||||||
repoUrl: versionInfo.repoUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true, module: moduleName, path: targetPath, moduleConfig };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy module files, filtering out install-time-only artifacts.
|
|
||||||
* @param {string} sourcePath - Source module directory
|
|
||||||
* @param {string} targetPath - Target module directory
|
|
||||||
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
|
||||||
*/
|
|
||||||
async _copyWithFiltering(sourcePath, targetPath, fileTrackingCallback = null) {
|
|
||||||
const files = await this._getFileList(sourcePath);
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
if (file.startsWith('sub-modules/')) continue;
|
|
||||||
|
|
||||||
const isInSidecar = path
|
|
||||||
.dirname(file)
|
|
||||||
.split('/')
|
|
||||||
.some((dir) => dir.toLowerCase().endsWith('-sidecar'));
|
|
||||||
if (isInSidecar) continue;
|
|
||||||
|
|
||||||
if (file === 'module.yaml') continue;
|
|
||||||
if (file === 'config.yaml') continue;
|
|
||||||
|
|
||||||
const sourceFile = path.join(sourcePath, file);
|
|
||||||
const targetFile = path.join(targetPath, file);
|
|
||||||
|
|
||||||
// Skip web-only agents
|
|
||||||
if (file.startsWith('agents/') && file.endsWith('.md')) {
|
|
||||||
const content = await fs.readFile(sourceFile, 'utf8');
|
|
||||||
if (/<agent[^>]*\slocalskip="true"[^>]*>/.test(content)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await fs.ensureDir(path.dirname(targetFile));
|
|
||||||
await fs.copy(sourceFile, targetFile, { overwrite: true });
|
|
||||||
|
|
||||||
if (fileTrackingCallback) {
|
|
||||||
fileTrackingCallback(targetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively list all files in a directory.
|
|
||||||
* @param {string} dir - Directory to scan
|
|
||||||
* @param {string} baseDir - Base directory for relative paths
|
|
||||||
* @returns {string[]} Relative file paths
|
|
||||||
*/
|
|
||||||
async _getFileList(dir, baseDir = dir) {
|
|
||||||
const files = [];
|
|
||||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
||||||
|
|
||||||
for (const entry of entries) {
|
|
||||||
const fullPath = path.join(dir, entry.name);
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
files.push(...(await this._getFileList(fullPath, baseDir)));
|
|
||||||
} else {
|
|
||||||
files.push(path.relative(baseDir, fullPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover custom module source paths from all available sources.
|
* Discover custom module source paths from all available sources.
|
||||||
* @param {Object} config - Installation configuration
|
* @param {Object} config - Installation configuration
|
||||||
|
|
|
||||||
|
|
@ -231,25 +231,52 @@ class OfficialModules {
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
*/
|
*/
|
||||||
async install(moduleName, bmadDir, fileTrackingCallback = null, options = {}) {
|
async install(moduleName, bmadDir, fileTrackingCallback = null, options = {}) {
|
||||||
const sourcePath = await this.findModuleSource(moduleName, { silent: options.silent });
|
const sourcePath = options.sourcePath || (await this.findModuleSource(moduleName, { silent: options.silent }));
|
||||||
const targetPath = path.join(bmadDir, moduleName);
|
const targetPath = path.join(bmadDir, moduleName);
|
||||||
|
|
||||||
|
// Check if source module exists
|
||||||
if (!sourcePath) {
|
if (!sourcePath) {
|
||||||
|
// Provide a more user-friendly error message
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Source for module '${moduleName}' is not available. It will be retained but cannot be updated without its source files.`,
|
`Source for module '${moduleName}' is not available. It will be retained but cannot be updated without its source files.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this is a custom module and read its custom.yaml values
|
||||||
|
let customConfig = null;
|
||||||
|
const rootCustomConfigPath = path.join(sourcePath, 'custom.yaml');
|
||||||
|
|
||||||
|
if (await fs.pathExists(rootCustomConfigPath)) {
|
||||||
|
try {
|
||||||
|
const customContent = await fs.readFile(rootCustomConfigPath, 'utf8');
|
||||||
|
customConfig = yaml.parse(customContent);
|
||||||
|
} catch (error) {
|
||||||
|
await prompts.log.warn(`Failed to read custom.yaml for ${moduleName}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a custom module, merge its values into the module config
|
||||||
|
if (customConfig) {
|
||||||
|
options.moduleConfig = { ...options.moduleConfig, ...customConfig };
|
||||||
|
if (options.logger) {
|
||||||
|
await options.logger.log(` Merged custom configuration for ${moduleName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already installed
|
||||||
if (await fs.pathExists(targetPath)) {
|
if (await fs.pathExists(targetPath)) {
|
||||||
await fs.remove(targetPath);
|
await fs.remove(targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy module files with filtering
|
||||||
await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
|
await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
|
||||||
|
|
||||||
|
// Create directories declared in module.yaml (unless explicitly skipped)
|
||||||
if (!options.skipModuleInstaller) {
|
if (!options.skipModuleInstaller) {
|
||||||
await this.createModuleDirectories(moduleName, bmadDir, options);
|
await this.createModuleDirectories(moduleName, bmadDir, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture version info for manifest
|
||||||
const { Manifest } = require('../core/manifest');
|
const { Manifest } = require('../core/manifest');
|
||||||
const manifestObj = new Manifest();
|
const manifestObj = new Manifest();
|
||||||
const versionInfo = await manifestObj.getModuleVersionInfo(moduleName, bmadDir, sourcePath);
|
const versionInfo = await manifestObj.getModuleVersionInfo(moduleName, bmadDir, sourcePath);
|
||||||
|
|
@ -261,7 +288,12 @@ class OfficialModules {
|
||||||
repoUrl: versionInfo.repoUrl,
|
repoUrl: versionInfo.repoUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: true, module: moduleName, path: targetPath, versionInfo };
|
return {
|
||||||
|
success: true,
|
||||||
|
module: moduleName,
|
||||||
|
path: targetPath,
|
||||||
|
versionInfo,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue