bmad-method 6.2.3-next.8 → 6.3.0
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/.claude-plugin/marketplace.json +0 -3
- package/README.md +8 -9
- package/README_CN.md +1 -1
- package/README_VN.md +110 -0
- package/package.json +1 -1
- package/removals.txt +17 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/data/prd-purpose.md +197 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +1 -3
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +1 -1
- package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +5 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +29 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/generate-trail.md +38 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-01-orientation.md +105 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-02-walkthrough.md +89 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-03-detail-pass.md +106 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-04-testing.md +74 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-05-wrapup.md +24 -0
- package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-01-gather-context.md +38 -15
- package/src/bmm-skills/4-implementation/bmad-correct-course/checklist.md +2 -2
- package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +8 -8
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/checklist.md +1 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/compile-epic-context.md +62 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/spec-template.md +1 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +33 -6
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +20 -8
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-03-implement.md +2 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +16 -4
- package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +1 -5
- package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +134 -134
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/sprint-status-template.yaml +1 -1
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/workflow.md +3 -3
- package/src/bmm-skills/4-implementation/bmad-sprint-status/workflow.md +2 -2
- package/src/bmm-skills/module-help.csv +2 -0
- package/src/core-skills/bmad-help/SKILL.md +4 -2
- package/src/core-skills/bmad-party-mode/SKILL.md +121 -2
- package/src/core-skills/module-help.csv +1 -0
- package/tools/installer/cli-utils.js +18 -9
- package/tools/installer/commands/install.js +1 -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 +180 -463
- package/tools/installer/core/manifest-generator.js +8 -14
- package/tools/installer/core/manifest.js +94 -102
- package/tools/installer/ide/_config-driven.js +149 -38
- package/tools/installer/ide/shared/skill-manifest.js +1 -16
- package/tools/installer/install-messages.yaml +19 -26
- package/tools/installer/modules/community-manager.js +377 -0
- package/tools/installer/modules/custom-module-manager.js +644 -0
- package/tools/installer/modules/external-manager.js +65 -49
- package/tools/installer/modules/official-modules.js +117 -65
- package/tools/installer/modules/plugin-resolver.js +398 -0
- package/tools/installer/modules/registry-client.js +66 -0
- package/tools/installer/{external-official-modules.yaml → modules/registry-fallback.yaml} +3 -12
- package/tools/installer/ui.js +549 -666
- package/src/bmm-skills/4-implementation/bmad-agent-qa/SKILL.md +0 -61
- package/src/bmm-skills/4-implementation/bmad-agent-qa/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/SKILL.md +0 -53
- package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-agent-sm/SKILL.md +0 -55
- package/src/bmm-skills/4-implementation/bmad-agent-sm/bmad-skill-manifest.yaml +0 -11
- package/src/core-skills/bmad-party-mode/steps/step-01-agent-loading.md +0 -138
- package/src/core-skills/bmad-party-mode/steps/step-02-discussion-orchestration.md +0 -187
- package/src/core-skills/bmad-party-mode/steps/step-03-graceful-exit.md +0 -167
- package/src/core-skills/bmad-party-mode/workflow.md +0 -183
- 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 -197
|
@@ -4,64 +4,98 @@ const path = require('node:path');
|
|
|
4
4
|
const { execSync } = require('node:child_process');
|
|
5
5
|
const yaml = require('yaml');
|
|
6
6
|
const prompts = require('../prompts');
|
|
7
|
+
const { RegistryClient } = require('./registry-client');
|
|
8
|
+
|
|
9
|
+
const REGISTRY_RAW_URL = 'https://raw.githubusercontent.com/bmad-code-org/bmad-plugins-marketplace/main/registry/official.yaml';
|
|
10
|
+
const FALLBACK_CONFIG_PATH = path.join(__dirname, 'registry-fallback.yaml');
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
|
-
* Manages
|
|
10
|
-
*
|
|
13
|
+
* Manages official modules from the remote BMad marketplace registry.
|
|
14
|
+
* Fetches registry/official.yaml from GitHub; falls back to the bundled
|
|
15
|
+
* external-official-modules.yaml when the network is unavailable.
|
|
11
16
|
*
|
|
12
17
|
* @class ExternalModuleManager
|
|
13
18
|
*/
|
|
14
19
|
class ExternalModuleManager {
|
|
15
20
|
constructor() {
|
|
16
|
-
this.
|
|
17
|
-
this.cachedModules = null;
|
|
21
|
+
this._client = new RegistryClient();
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
/**
|
|
21
|
-
* Load
|
|
22
|
-
*
|
|
25
|
+
* Load the official modules registry from GitHub, falling back to the
|
|
26
|
+
* bundled YAML file if the fetch fails.
|
|
27
|
+
* @returns {Object} Parsed YAML content with modules array
|
|
23
28
|
*/
|
|
24
29
|
async loadExternalModulesConfig() {
|
|
25
30
|
if (this.cachedModules) {
|
|
26
31
|
return this.cachedModules;
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
// Try remote registry first
|
|
35
|
+
try {
|
|
36
|
+
const content = await this._client.fetch(REGISTRY_RAW_URL);
|
|
37
|
+
const config = yaml.parse(content);
|
|
38
|
+
if (config?.modules?.length) {
|
|
39
|
+
this.cachedModules = config;
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// Fall through to local fallback
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fallback to bundled file
|
|
29
47
|
try {
|
|
30
|
-
const content = await fs.readFile(
|
|
48
|
+
const content = await fs.readFile(FALLBACK_CONFIG_PATH, 'utf8');
|
|
31
49
|
const config = yaml.parse(content);
|
|
32
50
|
this.cachedModules = config;
|
|
51
|
+
await prompts.log.warn('Could not reach BMad registry; using bundled module list.');
|
|
33
52
|
return config;
|
|
34
53
|
} catch (error) {
|
|
35
|
-
await prompts.log.warn(`Failed to load
|
|
36
|
-
return { modules:
|
|
54
|
+
await prompts.log.warn(`Failed to load modules config: ${error.message}`);
|
|
55
|
+
return { modules: [] };
|
|
37
56
|
}
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
/**
|
|
41
|
-
*
|
|
60
|
+
* Normalize a module entry from either the remote registry format
|
|
61
|
+
* (snake_case, array) or the legacy bundled format (kebab-case, object map).
|
|
62
|
+
* @param {Object} mod - Raw module config from YAML
|
|
63
|
+
* @param {string} [key] - Key name (only for legacy map format)
|
|
64
|
+
* @returns {Object} Normalized module info
|
|
65
|
+
*/
|
|
66
|
+
_normalizeModule(mod, key) {
|
|
67
|
+
return {
|
|
68
|
+
key: key || mod.name,
|
|
69
|
+
url: mod.repository || mod.url,
|
|
70
|
+
moduleDefinition: mod.module_definition || mod['module-definition'],
|
|
71
|
+
code: mod.code,
|
|
72
|
+
name: mod.display_name || mod.name,
|
|
73
|
+
description: mod.description || '',
|
|
74
|
+
defaultSelected: mod.default_selected === true || mod.defaultSelected === true,
|
|
75
|
+
type: mod.type || 'bmad-org',
|
|
76
|
+
npmPackage: mod.npm_package || mod.npmPackage || null,
|
|
77
|
+
builtIn: mod.built_in === true,
|
|
78
|
+
isExternal: mod.built_in !== true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get list of available modules from the registry
|
|
42
84
|
* @returns {Array<Object>} Array of module info objects
|
|
43
85
|
*/
|
|
44
86
|
async listAvailable() {
|
|
45
87
|
const config = await this.loadExternalModulesConfig();
|
|
46
|
-
const modules = [];
|
|
47
88
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
url: moduleConfig.url,
|
|
52
|
-
moduleDefinition: moduleConfig['module-definition'],
|
|
53
|
-
code: moduleConfig.code,
|
|
54
|
-
name: moduleConfig.name,
|
|
55
|
-
header: moduleConfig.header,
|
|
56
|
-
subheader: moduleConfig.subheader,
|
|
57
|
-
description: moduleConfig.description || '',
|
|
58
|
-
defaultSelected: moduleConfig.defaultSelected === true,
|
|
59
|
-
type: moduleConfig.type || 'community', // bmad-org or community
|
|
60
|
-
npmPackage: moduleConfig.npmPackage || null, // Include npm package name
|
|
61
|
-
isExternal: true,
|
|
62
|
-
});
|
|
89
|
+
// Remote format: modules is an array
|
|
90
|
+
if (Array.isArray(config.modules)) {
|
|
91
|
+
return config.modules.map((mod) => this._normalizeModule(mod));
|
|
63
92
|
}
|
|
64
93
|
|
|
94
|
+
// Legacy bundled format: modules is an object map
|
|
95
|
+
const modules = [];
|
|
96
|
+
for (const [key, mod] of Object.entries(config.modules || {})) {
|
|
97
|
+
modules.push(this._normalizeModule(mod, key));
|
|
98
|
+
}
|
|
65
99
|
return modules;
|
|
66
100
|
}
|
|
67
101
|
|
|
@@ -81,27 +115,8 @@ class ExternalModuleManager {
|
|
|
81
115
|
* @returns {Object|null} Module info or null if not found
|
|
82
116
|
*/
|
|
83
117
|
async getModuleByKey(key) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (!moduleConfig) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
key,
|
|
93
|
-
url: moduleConfig.url,
|
|
94
|
-
moduleDefinition: moduleConfig['module-definition'],
|
|
95
|
-
code: moduleConfig.code,
|
|
96
|
-
name: moduleConfig.name,
|
|
97
|
-
header: moduleConfig.header,
|
|
98
|
-
subheader: moduleConfig.subheader,
|
|
99
|
-
description: moduleConfig.description || '',
|
|
100
|
-
defaultSelected: moduleConfig.defaultSelected === true,
|
|
101
|
-
type: moduleConfig.type || 'community', // bmad-org or community
|
|
102
|
-
npmPackage: moduleConfig.npmPackage || null, // Include npm package name
|
|
103
|
-
isExternal: true,
|
|
104
|
-
};
|
|
118
|
+
const modules = await this.listAvailable();
|
|
119
|
+
return modules.find((m) => m.key === key) || null;
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
/**
|
|
@@ -154,7 +169,7 @@ class ExternalModuleManager {
|
|
|
154
169
|
const moduleInfo = await this.getModuleByCode(moduleCode);
|
|
155
170
|
|
|
156
171
|
if (!moduleInfo) {
|
|
157
|
-
throw new Error(`External module '${moduleCode}' not found in
|
|
172
|
+
throw new Error(`External module '${moduleCode}' not found in the BMad registry`);
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
const cacheDir = this.getExternalCacheDir();
|
|
@@ -304,7 +319,7 @@ class ExternalModuleManager {
|
|
|
304
319
|
async findExternalModuleSource(moduleCode, options = {}) {
|
|
305
320
|
const moduleInfo = await this.getModuleByCode(moduleCode);
|
|
306
321
|
|
|
307
|
-
if (!moduleInfo) {
|
|
322
|
+
if (!moduleInfo || moduleInfo.builtIn) {
|
|
308
323
|
return null;
|
|
309
324
|
}
|
|
310
325
|
|
|
@@ -349,6 +364,7 @@ class ExternalModuleManager {
|
|
|
349
364
|
// Nothing found: return configured path (preserves old behavior for error messaging)
|
|
350
365
|
return path.dirname(configuredPath);
|
|
351
366
|
}
|
|
367
|
+
cachedModules = null;
|
|
352
368
|
}
|
|
353
369
|
|
|
354
370
|
module.exports = { ExternalModuleManager };
|
|
@@ -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,28 @@ 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
|
-
|
|
141
|
-
if (await fs.pathExists(moduleConfigPath)) {
|
|
142
|
-
configPath = moduleConfigPath;
|
|
143
|
-
} else if (await fs.pathExists(rootCustomConfigPath)) {
|
|
144
|
-
configPath = rootCustomConfigPath;
|
|
145
|
-
}
|
|
146
136
|
|
|
147
|
-
|
|
148
|
-
|
|
137
|
+
if (!(await fs.pathExists(moduleConfigPath))) {
|
|
138
|
+
// Check resolution cache for strategy 5 modules (no module.yaml on disk)
|
|
139
|
+
const { CustomModuleManager } = require('./custom-module-manager');
|
|
140
|
+
const customMgr = new CustomModuleManager();
|
|
141
|
+
const resolved = customMgr.getResolution(defaultName);
|
|
142
|
+
if (resolved && resolved.synthesizedModuleYaml) {
|
|
143
|
+
return {
|
|
144
|
+
id: resolved.code,
|
|
145
|
+
path: modulePath,
|
|
146
|
+
name: resolved.name,
|
|
147
|
+
description: resolved.description,
|
|
148
|
+
version: resolved.version || '1.0.0',
|
|
149
|
+
source: sourceDescription,
|
|
150
|
+
dependencies: [],
|
|
151
|
+
defaultSelected: false,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
149
154
|
return null;
|
|
150
155
|
}
|
|
151
156
|
|
|
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
157
|
const moduleInfo = {
|
|
156
158
|
id: defaultName,
|
|
157
159
|
path: modulePath,
|
|
@@ -162,12 +164,11 @@ class OfficialModules {
|
|
|
162
164
|
description: 'BMAD Module',
|
|
163
165
|
version: '5.0.0',
|
|
164
166
|
source: sourceDescription,
|
|
165
|
-
isCustom: configPath === rootCustomConfigPath || isCustomSource,
|
|
166
167
|
};
|
|
167
168
|
|
|
168
169
|
// Read module config for metadata
|
|
169
170
|
try {
|
|
170
|
-
const configContent = await fs.readFile(
|
|
171
|
+
const configContent = await fs.readFile(moduleConfigPath, 'utf8');
|
|
171
172
|
const config = yaml.parse(configContent);
|
|
172
173
|
|
|
173
174
|
// Use the code property as the id if available
|
|
@@ -217,6 +218,22 @@ class OfficialModules {
|
|
|
217
218
|
return externalSource;
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
// Check community modules
|
|
222
|
+
const { CommunityModuleManager } = require('./community-manager');
|
|
223
|
+
const communityMgr = new CommunityModuleManager();
|
|
224
|
+
const communitySource = await communityMgr.findModuleSource(moduleCode, options);
|
|
225
|
+
if (communitySource) {
|
|
226
|
+
return communitySource;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Check custom modules (from user-provided URLs, already cloned to cache)
|
|
230
|
+
const { CustomModuleManager } = require('./custom-module-manager');
|
|
231
|
+
const customMgr = new CustomModuleManager();
|
|
232
|
+
const customSource = await customMgr.findModuleSourceByCode(moduleCode, options);
|
|
233
|
+
if (customSource) {
|
|
234
|
+
return customSource;
|
|
235
|
+
}
|
|
236
|
+
|
|
220
237
|
return null;
|
|
221
238
|
}
|
|
222
239
|
|
|
@@ -231,6 +248,14 @@ class OfficialModules {
|
|
|
231
248
|
* @param {Object} options.logger - Logger instance for output
|
|
232
249
|
*/
|
|
233
250
|
async install(moduleName, bmadDir, fileTrackingCallback = null, options = {}) {
|
|
251
|
+
// Check if this module has a plugin resolution (custom marketplace install)
|
|
252
|
+
const { CustomModuleManager } = require('./custom-module-manager');
|
|
253
|
+
const customMgr = new CustomModuleManager();
|
|
254
|
+
const resolved = customMgr.getResolution(moduleName);
|
|
255
|
+
if (resolved) {
|
|
256
|
+
return this.installFromResolution(resolved, bmadDir, fileTrackingCallback, options);
|
|
257
|
+
}
|
|
258
|
+
|
|
234
259
|
const sourcePath = await this.findModuleSource(moduleName, { silent: options.silent });
|
|
235
260
|
const targetPath = path.join(bmadDir, moduleName);
|
|
236
261
|
|
|
@@ -264,6 +289,62 @@ class OfficialModules {
|
|
|
264
289
|
return { success: true, module: moduleName, path: targetPath, versionInfo };
|
|
265
290
|
}
|
|
266
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Install a module from a PluginResolver resolution result.
|
|
294
|
+
* Copies specific skill directories and places module-help.csv at the target root.
|
|
295
|
+
* @param {Object} resolved - ResolvedModule from PluginResolver
|
|
296
|
+
* @param {string} bmadDir - Target bmad directory
|
|
297
|
+
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
|
298
|
+
* @param {Object} options - Installation options
|
|
299
|
+
*/
|
|
300
|
+
async installFromResolution(resolved, bmadDir, fileTrackingCallback = null, options = {}) {
|
|
301
|
+
const targetPath = path.join(bmadDir, resolved.code);
|
|
302
|
+
|
|
303
|
+
if (await fs.pathExists(targetPath)) {
|
|
304
|
+
await fs.remove(targetPath);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
await fs.ensureDir(targetPath);
|
|
308
|
+
|
|
309
|
+
// Copy each skill directory, flattened by leaf name
|
|
310
|
+
for (const skillPath of resolved.skillPaths) {
|
|
311
|
+
const skillDirName = path.basename(skillPath);
|
|
312
|
+
const skillTarget = path.join(targetPath, skillDirName);
|
|
313
|
+
await this.copyModuleWithFiltering(skillPath, skillTarget, fileTrackingCallback, options.moduleConfig);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Place module-help.csv at the module root
|
|
317
|
+
if (resolved.moduleHelpCsvPath) {
|
|
318
|
+
// Strategies 1-4: copy the existing file
|
|
319
|
+
const helpTarget = path.join(targetPath, 'module-help.csv');
|
|
320
|
+
await fs.copy(resolved.moduleHelpCsvPath, helpTarget, { overwrite: true });
|
|
321
|
+
if (fileTrackingCallback) fileTrackingCallback(helpTarget);
|
|
322
|
+
} else if (resolved.synthesizedHelpCsv) {
|
|
323
|
+
// Strategy 5: write synthesized content
|
|
324
|
+
const helpTarget = path.join(targetPath, 'module-help.csv');
|
|
325
|
+
await fs.writeFile(helpTarget, resolved.synthesizedHelpCsv, 'utf8');
|
|
326
|
+
if (fileTrackingCallback) fileTrackingCallback(helpTarget);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Create directories declared in module.yaml (strategies 1-4 may have these)
|
|
330
|
+
if (!options.skipModuleInstaller) {
|
|
331
|
+
await this.createModuleDirectories(resolved.code, bmadDir, options);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Update manifest
|
|
335
|
+
const { Manifest } = require('../core/manifest');
|
|
336
|
+
const manifestObj = new Manifest();
|
|
337
|
+
|
|
338
|
+
await manifestObj.addModule(bmadDir, resolved.code, {
|
|
339
|
+
version: resolved.version || null,
|
|
340
|
+
source: 'custom',
|
|
341
|
+
npmPackage: null,
|
|
342
|
+
repoUrl: resolved.repoUrl || null,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
return { success: true, module: resolved.code, path: targetPath, versionInfo: { version: resolved.version || '' } };
|
|
346
|
+
}
|
|
347
|
+
|
|
267
348
|
/**
|
|
268
349
|
* Update an existing module
|
|
269
350
|
* @param {string} moduleName - Name of the module to update
|
|
@@ -824,20 +905,15 @@ class OfficialModules {
|
|
|
824
905
|
const results = [];
|
|
825
906
|
|
|
826
907
|
for (const moduleName of modules) {
|
|
827
|
-
// Resolve module.yaml path -
|
|
908
|
+
// Resolve module.yaml path - standard location first, then OfficialModules search
|
|
828
909
|
let moduleConfigPath = null;
|
|
829
|
-
const
|
|
830
|
-
if (
|
|
831
|
-
moduleConfigPath =
|
|
910
|
+
const standardPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
911
|
+
if (await fs.pathExists(standardPath)) {
|
|
912
|
+
moduleConfigPath = standardPath;
|
|
832
913
|
} 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
|
-
}
|
|
914
|
+
const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
|
|
915
|
+
if (moduleSourcePath) {
|
|
916
|
+
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
|
|
841
917
|
}
|
|
842
918
|
}
|
|
843
919
|
|
|
@@ -882,12 +958,9 @@ class OfficialModules {
|
|
|
882
958
|
* @param {Array} modules - List of modules to configure (including 'core')
|
|
883
959
|
* @param {string} projectDir - Target project directory
|
|
884
960
|
* @param {Object} options - Additional options
|
|
885
|
-
* @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
|
|
886
961
|
* @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
|
|
887
962
|
*/
|
|
888
963
|
async collectAllConfigurations(modules, projectDir, options = {}) {
|
|
889
|
-
// Store custom module paths for use in collectModuleConfig
|
|
890
|
-
this.customModulePaths = options.customModulePaths || new Map();
|
|
891
964
|
this.skipPrompts = options.skipPrompts || false;
|
|
892
965
|
this.modulesToCustomize = undefined;
|
|
893
966
|
await this.loadExistingConfig(projectDir);
|
|
@@ -1042,25 +1115,7 @@ class OfficialModules {
|
|
|
1042
1115
|
}
|
|
1043
1116
|
}
|
|
1044
1117
|
|
|
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
|
-
|
|
1118
|
+
if (!(await fs.pathExists(moduleConfigPath))) {
|
|
1064
1119
|
// No config schema for this module - use existing values
|
|
1065
1120
|
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
1066
1121
|
if (!this.collectedConfig[moduleName]) {
|
|
@@ -1071,7 +1126,7 @@ class OfficialModules {
|
|
|
1071
1126
|
return false;
|
|
1072
1127
|
}
|
|
1073
1128
|
|
|
1074
|
-
const configContent = await fs.readFile(
|
|
1129
|
+
const configContent = await fs.readFile(moduleConfigPath, 'utf8');
|
|
1075
1130
|
const moduleConfig = yaml.parse(configContent);
|
|
1076
1131
|
|
|
1077
1132
|
if (!moduleConfig) {
|
|
@@ -1172,7 +1227,13 @@ class OfficialModules {
|
|
|
1172
1227
|
// Collect all answers (static + prompted)
|
|
1173
1228
|
let allAnswers = { ...staticAnswers };
|
|
1174
1229
|
|
|
1175
|
-
if (questions.length > 0) {
|
|
1230
|
+
if (questions.length > 0 && silentMode) {
|
|
1231
|
+
// In silent mode (quick update), use defaults for new fields instead of prompting
|
|
1232
|
+
for (const q of questions) {
|
|
1233
|
+
allAnswers[q.name] = typeof q.default === 'function' ? q.default({}) : q.default;
|
|
1234
|
+
}
|
|
1235
|
+
await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configured with defaults`);
|
|
1236
|
+
} else if (questions.length > 0) {
|
|
1176
1237
|
// Only show header if we actually have questions
|
|
1177
1238
|
await CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
|
1178
1239
|
await prompts.log.message('');
|
|
@@ -1332,16 +1393,7 @@ class OfficialModules {
|
|
|
1332
1393
|
this.allAnswers = {};
|
|
1333
1394
|
}
|
|
1334
1395
|
// 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
|
-
}
|
|
1396
|
+
let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
1345
1397
|
|
|
1346
1398
|
// If not found in src/modules or custom paths, search the project
|
|
1347
1399
|
if (!(await fs.pathExists(moduleConfigPath))) {
|