bmad-method 6.2.3-next.3 → 6.2.3-next.31
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 -4
- package/README.md +8 -9
- package/README_CN.md +1 -1
- package/README_VN.md +110 -0
- package/package.json +2 -1
- package/removals.txt +17 -0
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +7 -4
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +6 -4
- package/src/bmm-skills/1-analysis/bmad-document-project/workflow.md +8 -10
- package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +96 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/agents/artifact-analyzer.md +60 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/agents/web-researcher.md +49 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/assets/prfaq-template.md +62 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/bmad-manifest.json +16 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/customer-faq.md +55 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/internal-faq.md +51 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/press-release.md +60 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/verdict.md +79 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +1 -6
- package/src/bmm-skills/1-analysis/bmad-product-brief/bmad-manifest.json +1 -1
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/workflow.md +8 -6
- package/src/bmm-skills/1-analysis/research/bmad-market-research/workflow.md +8 -6
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/workflow.md +8 -6
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +6 -4
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +6 -4
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/workflow.md +8 -9
- 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-create-ux-design/workflow.md +8 -9
- 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/2-plan-workflows/bmad-edit-prd/workflow.md +8 -9
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/workflow.md +8 -9
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +6 -4
- 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 +9 -11
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/workflow.md +8 -14
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +10 -12
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/workflow.md +8 -12
- package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +11 -4
- 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 +4 -1
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +1 -2
- package/src/core-skills/bmad-distillator/SKILL.md +0 -1
- package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +9 -9
- 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 +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 +176 -464
- package/tools/installer/core/manifest-generator.js +4 -12
- package/tools/installer/core/manifest.js +82 -97
- package/tools/installer/ide/_config-driven.js +149 -38
- package/tools/installer/ide/platform-codes.yaml +6 -4
- 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 +308 -0
- package/tools/installer/modules/external-manager.js +65 -49
- package/tools/installer/modules/official-modules.js +37 -65
- 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 +340 -672
- package/tools/platform-codes.yaml +6 -0
- package/src/bmm-skills/2-plan-workflows/create-prd/data/domain-complexity.csv +0 -15
- package/src/bmm-skills/2-plan-workflows/create-prd/data/project-types.csv +0 -11
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +0 -224
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +0 -191
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +0 -209
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +0 -174
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -214
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +0 -228
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +0 -217
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -205
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -243
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +0 -263
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +0 -209
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -264
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +0 -242
- package/src/bmm-skills/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +0 -232
- package/src/bmm-skills/2-plan-workflows/create-prd/workflow-validate-prd.md +0 -65
- package/src/bmm-skills/4-implementation/bmad-agent-qa/SKILL.md +0 -59
- 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 -51
- 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 -53
- package/src/bmm-skills/4-implementation/bmad-agent-sm/bmad-skill-manifest.yaml +0 -11
- package/src/core-skills/bmad-init/SKILL.md +0 -100
- package/src/core-skills/bmad-init/resources/core-module.yaml +0 -25
- package/src/core-skills/bmad-init/scripts/bmad_init.py +0 -624
- package/src/core-skills/bmad-init/scripts/tests/test_bmad_init.py +0 -393
- 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 -190
- 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
- /package/src/bmm-skills/2-plan-workflows/{create-prd → bmad-edit-prd}/data/prd-purpose.md +0 -0
|
@@ -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,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
|
-
|
|
141
|
-
if (await fs.pathExists(moduleConfigPath)) {
|
|
142
|
-
configPath = moduleConfigPath;
|
|
143
|
-
} else if (await fs.pathExists(rootCustomConfigPath)) {
|
|
144
|
-
configPath = rootCustomConfigPath;
|
|
145
|
-
}
|
|
146
136
|
|
|
147
|
-
|
|
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
|
|
@@ -217,6 +202,22 @@ class OfficialModules {
|
|
|
217
202
|
return externalSource;
|
|
218
203
|
}
|
|
219
204
|
|
|
205
|
+
// Check community modules
|
|
206
|
+
const { CommunityModuleManager } = require('./community-manager');
|
|
207
|
+
const communityMgr = new CommunityModuleManager();
|
|
208
|
+
const communitySource = await communityMgr.findModuleSource(moduleCode, options);
|
|
209
|
+
if (communitySource) {
|
|
210
|
+
return communitySource;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check custom modules (from user-provided URLs, already cloned to cache)
|
|
214
|
+
const { CustomModuleManager } = require('./custom-module-manager');
|
|
215
|
+
const customMgr = new CustomModuleManager();
|
|
216
|
+
const customSource = await customMgr.findModuleSourceByCode(moduleCode, options);
|
|
217
|
+
if (customSource) {
|
|
218
|
+
return customSource;
|
|
219
|
+
}
|
|
220
|
+
|
|
220
221
|
return null;
|
|
221
222
|
}
|
|
222
223
|
|
|
@@ -824,20 +825,15 @@ class OfficialModules {
|
|
|
824
825
|
const results = [];
|
|
825
826
|
|
|
826
827
|
for (const moduleName of modules) {
|
|
827
|
-
// Resolve module.yaml path -
|
|
828
|
+
// Resolve module.yaml path - standard location first, then OfficialModules search
|
|
828
829
|
let moduleConfigPath = null;
|
|
829
|
-
const
|
|
830
|
-
if (
|
|
831
|
-
moduleConfigPath =
|
|
830
|
+
const standardPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
831
|
+
if (await fs.pathExists(standardPath)) {
|
|
832
|
+
moduleConfigPath = standardPath;
|
|
832
833
|
} 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
|
-
}
|
|
834
|
+
const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
|
|
835
|
+
if (moduleSourcePath) {
|
|
836
|
+
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
|
|
841
837
|
}
|
|
842
838
|
}
|
|
843
839
|
|
|
@@ -882,12 +878,9 @@ class OfficialModules {
|
|
|
882
878
|
* @param {Array} modules - List of modules to configure (including 'core')
|
|
883
879
|
* @param {string} projectDir - Target project directory
|
|
884
880
|
* @param {Object} options - Additional options
|
|
885
|
-
* @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
|
|
886
881
|
* @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
|
|
887
882
|
*/
|
|
888
883
|
async collectAllConfigurations(modules, projectDir, options = {}) {
|
|
889
|
-
// Store custom module paths for use in collectModuleConfig
|
|
890
|
-
this.customModulePaths = options.customModulePaths || new Map();
|
|
891
884
|
this.skipPrompts = options.skipPrompts || false;
|
|
892
885
|
this.modulesToCustomize = undefined;
|
|
893
886
|
await this.loadExistingConfig(projectDir);
|
|
@@ -1042,25 +1035,7 @@ class OfficialModules {
|
|
|
1042
1035
|
}
|
|
1043
1036
|
}
|
|
1044
1037
|
|
|
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
|
-
|
|
1038
|
+
if (!(await fs.pathExists(moduleConfigPath))) {
|
|
1064
1039
|
// No config schema for this module - use existing values
|
|
1065
1040
|
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
1066
1041
|
if (!this.collectedConfig[moduleName]) {
|
|
@@ -1071,7 +1046,7 @@ class OfficialModules {
|
|
|
1071
1046
|
return false;
|
|
1072
1047
|
}
|
|
1073
1048
|
|
|
1074
|
-
const configContent = await fs.readFile(
|
|
1049
|
+
const configContent = await fs.readFile(moduleConfigPath, 'utf8');
|
|
1075
1050
|
const moduleConfig = yaml.parse(configContent);
|
|
1076
1051
|
|
|
1077
1052
|
if (!moduleConfig) {
|
|
@@ -1172,7 +1147,13 @@ class OfficialModules {
|
|
|
1172
1147
|
// Collect all answers (static + prompted)
|
|
1173
1148
|
let allAnswers = { ...staticAnswers };
|
|
1174
1149
|
|
|
1175
|
-
if (questions.length > 0) {
|
|
1150
|
+
if (questions.length > 0 && silentMode) {
|
|
1151
|
+
// In silent mode (quick update), use defaults for new fields instead of prompting
|
|
1152
|
+
for (const q of questions) {
|
|
1153
|
+
allAnswers[q.name] = typeof q.default === 'function' ? q.default({}) : q.default;
|
|
1154
|
+
}
|
|
1155
|
+
await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configured with defaults`);
|
|
1156
|
+
} else if (questions.length > 0) {
|
|
1176
1157
|
// Only show header if we actually have questions
|
|
1177
1158
|
await CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
|
1178
1159
|
await prompts.log.message('');
|
|
@@ -1332,16 +1313,7 @@ class OfficialModules {
|
|
|
1332
1313
|
this.allAnswers = {};
|
|
1333
1314
|
}
|
|
1334
1315
|
// 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
|
-
}
|
|
1316
|
+
let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
|
|
1345
1317
|
|
|
1346
1318
|
// If not found in src/modules or custom paths, search the project
|
|
1347
1319
|
if (!(await fs.pathExists(moduleConfigPath))) {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const https = require('node:https');
|
|
2
|
+
const yaml = require('yaml');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared HTTP client for fetching registry data from GitHub.
|
|
6
|
+
* Used by ExternalModuleManager, CommunityModuleManager, and CustomModuleManager.
|
|
7
|
+
*/
|
|
8
|
+
class RegistryClient {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.timeout = options.timeout || 10_000;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetch a URL and return the response body as a string.
|
|
15
|
+
* Follows one redirect (GitHub sometimes 301s).
|
|
16
|
+
* @param {string} url - URL to fetch
|
|
17
|
+
* @param {number} [timeout] - Timeout in ms (overrides default)
|
|
18
|
+
* @returns {Promise<string>} Response body
|
|
19
|
+
*/
|
|
20
|
+
fetch(url, timeout) {
|
|
21
|
+
const timeoutMs = timeout || this.timeout;
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const req = https
|
|
24
|
+
.get(url, { timeout: timeoutMs }, (res) => {
|
|
25
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
26
|
+
return this.fetch(res.headers.location, timeoutMs).then(resolve, reject);
|
|
27
|
+
}
|
|
28
|
+
if (res.statusCode !== 200) {
|
|
29
|
+
return reject(new Error(`HTTP ${res.statusCode}`));
|
|
30
|
+
}
|
|
31
|
+
let data = '';
|
|
32
|
+
res.on('data', (chunk) => (data += chunk));
|
|
33
|
+
res.on('end', () => resolve(data));
|
|
34
|
+
})
|
|
35
|
+
.on('error', reject)
|
|
36
|
+
.on('timeout', () => {
|
|
37
|
+
req.destroy();
|
|
38
|
+
reject(new Error('Request timed out'));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Fetch a URL and parse the response as YAML.
|
|
45
|
+
* @param {string} url - URL to fetch
|
|
46
|
+
* @param {number} [timeout] - Timeout in ms
|
|
47
|
+
* @returns {Promise<Object>} Parsed YAML content
|
|
48
|
+
*/
|
|
49
|
+
async fetchYaml(url, timeout) {
|
|
50
|
+
const content = await this.fetch(url, timeout);
|
|
51
|
+
return yaml.parse(content);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Fetch a URL and parse the response as JSON.
|
|
56
|
+
* @param {string} url - URL to fetch
|
|
57
|
+
* @param {number} [timeout] - Timeout in ms
|
|
58
|
+
* @returns {Promise<Object>} Parsed JSON content
|
|
59
|
+
*/
|
|
60
|
+
async fetchJson(url, timeout) {
|
|
61
|
+
const content = await this.fetch(url, timeout);
|
|
62
|
+
return JSON.parse(content);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { RegistryClient };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
1
|
+
# Fallback module registry — used only when the BMad Marketplace repo
|
|
2
|
+
# (bmad-code-org/bmad-plugins-marketplace) is unreachable.
|
|
3
|
+
# The remote registry/official.yaml is the source of truth.
|
|
3
4
|
|
|
4
5
|
modules:
|
|
5
6
|
bmad-builder:
|
|
@@ -41,13 +42,3 @@ modules:
|
|
|
41
42
|
defaultSelected: false
|
|
42
43
|
type: bmad-org
|
|
43
44
|
npmPackage: bmad-method-test-architecture-enterprise
|
|
44
|
-
|
|
45
|
-
whiteport-design-studio:
|
|
46
|
-
url: https://github.com/bmad-code-org/bmad-method-wds-expansion
|
|
47
|
-
module-definition: src/module.yaml
|
|
48
|
-
code: wds
|
|
49
|
-
name: "Whiteport Design Studio (For UX Professionals)"
|
|
50
|
-
description: "Whiteport Design Studio (For UX Professionals)"
|
|
51
|
-
defaultSelected: false
|
|
52
|
-
type: community
|
|
53
|
-
npmPackage: bmad-method-wds-expansion
|