bmad-method 6.3.1-next.2 → 6.3.1-next.21
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 +3 -3
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +51 -36
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/customize.toml +90 -0
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +50 -33
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/customize.toml +81 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +57 -1
- package/src/bmm-skills/1-analysis/bmad-document-project/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-instructions.md +1 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-instructions.md +1 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +48 -9
- package/src/bmm-skills/1-analysis/bmad-prfaq/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/verdict.md +4 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +44 -9
- package/src/bmm-skills/1-analysis/bmad-product-brief/customize.toml +47 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/contextual-discovery.md +8 -7
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/draft-and-review.md +6 -5
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/finalize.md +4 -1
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/guided-elicitation.md +3 -2
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +6 -0
- package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +6 -0
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +50 -35
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/customize.toml +85 -0
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +50 -31
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/customize.toml +60 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md +99 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/customize.toml +41 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-08-scoping.md +70 -23
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-11-polish.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-12-complete.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md +70 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/customize.toml +41 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-14-complete.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md +97 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/customize.toml +42 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +2 -0
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md +99 -1
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/customize.toml +42 -0
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/steps-v/step-v-13-report-complete.md +1 -0
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +50 -30
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/customize.toml +65 -0
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +86 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md +69 -1
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/steps/step-08-complete.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md +88 -1
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/steps/step-04-final-validation.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +76 -1
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-03-complete.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +48 -43
- package/src/bmm-skills/4-implementation/bmad-agent-dev/customize.toml +90 -0
- package/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md +296 -1
- package/src/bmm-skills/4-implementation/bmad-correct-course/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md +412 -1
- package/src/bmm-skills/4-implementation/bmad-create-story/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md +171 -1
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +1507 -1
- package/src/bmm-skills/4-implementation/bmad-retrospective/customize.toml +41 -0
- package/src/bmm-skills/module.yaml +49 -0
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +7 -1
- package/src/core-skills/bmad-customize/SKILL.md +111 -0
- package/src/core-skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
- package/src/core-skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
- package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +1 -1
- package/src/core-skills/bmad-party-mode/SKILL.md +13 -10
- package/src/core-skills/module-help.csv +1 -0
- package/src/core-skills/module.yaml +3 -0
- package/src/scripts/resolve_config.py +176 -0
- package/src/scripts/resolve_customization.py +230 -0
- package/tools/installer/cli-utils.js +0 -137
- package/tools/installer/commands/install.js +13 -0
- package/tools/installer/commands/status.js +1 -1
- package/tools/installer/commands/uninstall.js +1 -1
- package/tools/installer/core/config.js +4 -1
- package/tools/installer/core/existing-install.js +1 -1
- package/tools/installer/core/install-paths.js +12 -6
- package/tools/installer/core/installer.js +182 -95
- package/tools/installer/core/manifest-generator.js +347 -190
- package/tools/installer/core/manifest.js +49 -642
- package/tools/installer/file-ops.js +1 -1
- package/tools/installer/fs-native.js +116 -0
- package/tools/installer/ide/_config-driven.js +1 -1
- package/tools/installer/ide/platform-codes.js +1 -1
- package/tools/installer/ide/shared/path-utils.js +0 -145
- package/tools/installer/ide/shared/skill-manifest.js +1 -1
- package/tools/installer/message-loader.js +1 -1
- package/tools/installer/modules/channel-plan.js +203 -0
- package/tools/installer/modules/channel-resolver.js +241 -0
- package/tools/installer/modules/community-manager.js +131 -24
- package/tools/installer/modules/custom-module-manager.js +161 -47
- package/tools/installer/modules/external-manager.js +236 -73
- package/tools/installer/modules/official-modules.js +61 -63
- package/tools/installer/modules/plugin-resolver.js +1 -1
- package/tools/installer/modules/registry-client.js +133 -12
- package/tools/installer/modules/registry-fallback.yaml +8 -0
- package/tools/installer/modules/version-resolver.js +336 -0
- package/tools/installer/project-root.js +55 -1
- package/tools/installer/prompts.js +0 -106
- package/tools/installer/ui.js +457 -51
- package/tools/migrate-custom-module-paths.js +1 -1
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/1-analysis/bmad-document-project/workflow.md +0 -25
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/workflow.md +0 -51
- package/src/bmm-skills/1-analysis/research/bmad-market-research/workflow.md +0 -51
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/workflow.md +0 -52
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/workflow.md +0 -61
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/workflow.md +0 -35
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/workflow.md +0 -62
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/workflow.md +0 -61
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +0 -47
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/workflow.md +0 -32
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +0 -51
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/workflow.md +0 -39
- package/src/bmm-skills/4-implementation/bmad-agent-dev/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +0 -267
- package/src/bmm-skills/4-implementation/bmad-create-story/workflow.md +0 -380
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/workflow.md +0 -136
- package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +0 -1479
- package/tools/installer/ide/shared/agent-command-generator.js +0 -180
- package/tools/installer/ide/shared/bmad-artifacts.js +0 -208
- package/tools/installer/ide/shared/module-injections.js +0 -136
- package/tools/installer/ide/templates/agent-command-template.md +0 -14
- package/tools/installer/ide/templates/combined/antigravity.md +0 -8
- package/tools/installer/ide/templates/combined/default-agent.md +0 -15
- package/tools/installer/ide/templates/combined/default-task.md +0 -10
- package/tools/installer/ide/templates/combined/default-tool.md +0 -10
- package/tools/installer/ide/templates/combined/default-workflow.md +0 -6
- package/tools/installer/ide/templates/combined/gemini-agent.toml +0 -14
- package/tools/installer/ide/templates/combined/gemini-task.toml +0 -11
- package/tools/installer/ide/templates/combined/gemini-tool.toml +0 -11
- package/tools/installer/ide/templates/combined/gemini-workflow-yaml.toml +0 -16
- package/tools/installer/ide/templates/combined/gemini-workflow.toml +0 -14
- package/tools/installer/ide/templates/combined/kiro-agent.md +0 -16
- package/tools/installer/ide/templates/combined/kiro-task.md +0 -9
- package/tools/installer/ide/templates/combined/kiro-tool.md +0 -9
- package/tools/installer/ide/templates/combined/kiro-workflow.md +0 -7
- package/tools/installer/ide/templates/combined/opencode-agent.md +0 -15
- package/tools/installer/ide/templates/combined/opencode-task.md +0 -13
- package/tools/installer/ide/templates/combined/opencode-tool.md +0 -13
- package/tools/installer/ide/templates/combined/opencode-workflow-yaml.md +0 -16
- package/tools/installer/ide/templates/combined/opencode-workflow.md +0 -16
- package/tools/installer/ide/templates/combined/rovodev.md +0 -9
- package/tools/installer/ide/templates/combined/trae.md +0 -9
- package/tools/installer/ide/templates/combined/windsurf-workflow.md +0 -10
- package/tools/installer/ide/templates/split/.gitkeep +0 -0
package/tools/installer/ui.js
CHANGED
|
@@ -1,50 +1,20 @@
|
|
|
1
1
|
const path = require('node:path');
|
|
2
2
|
const os = require('node:os');
|
|
3
|
-
const fs = require('fs-
|
|
3
|
+
const fs = require('./fs-native');
|
|
4
4
|
const { CLIUtils } = require('./cli-utils');
|
|
5
5
|
const { ExternalModuleManager } = require('./modules/external-manager');
|
|
6
|
-
const {
|
|
6
|
+
const { resolveModuleVersion } = require('./modules/version-resolver');
|
|
7
|
+
const { parseChannelOptions, buildPlan, orphanPinWarnings, bundledTargetWarnings } = require('./modules/channel-plan');
|
|
7
8
|
const prompts = require('./prompts');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
* Read module version from .
|
|
11
|
+
* Read a module version from the freshest local metadata available.
|
|
11
12
|
* @param {string} moduleCode - Module code (e.g., 'core', 'bmm', 'cis')
|
|
12
13
|
* @returns {string} Version string or empty string
|
|
13
14
|
*/
|
|
14
|
-
async function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
marketplacePath = path.join(getProjectRoot(), '.claude-plugin', 'marketplace.json');
|
|
18
|
-
} else {
|
|
19
|
-
const cacheDir = path.join(os.homedir(), '.bmad', 'cache', 'external-modules', moduleCode);
|
|
20
|
-
marketplacePath = path.join(cacheDir, '.claude-plugin', 'marketplace.json');
|
|
21
|
-
}
|
|
22
|
-
try {
|
|
23
|
-
if (await fs.pathExists(marketplacePath)) {
|
|
24
|
-
const data = JSON.parse(await fs.readFile(marketplacePath, 'utf8'));
|
|
25
|
-
return _extractMarketplaceVersion(data);
|
|
26
|
-
}
|
|
27
|
-
} catch {
|
|
28
|
-
// ignore
|
|
29
|
-
}
|
|
30
|
-
return '';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Extract the highest version from marketplace.json plugins array.
|
|
35
|
-
* Handles multiple plugins per file safely.
|
|
36
|
-
* @param {Object} data - Parsed marketplace.json
|
|
37
|
-
* @returns {string} Version string or empty string
|
|
38
|
-
*/
|
|
39
|
-
function _extractMarketplaceVersion(data) {
|
|
40
|
-
const plugins = data?.plugins;
|
|
41
|
-
if (!Array.isArray(plugins) || plugins.length === 0) return '';
|
|
42
|
-
// Use the highest version across all plugins in the file
|
|
43
|
-
let best = '';
|
|
44
|
-
for (const p of plugins) {
|
|
45
|
-
if (p.version && (!best || p.version > best)) best = p.version;
|
|
46
|
-
}
|
|
47
|
-
return best;
|
|
15
|
+
async function getModuleVersion(moduleCode) {
|
|
16
|
+
const versionInfo = await resolveModuleVersion(moduleCode);
|
|
17
|
+
return versionInfo.version || '';
|
|
48
18
|
}
|
|
49
19
|
|
|
50
20
|
/**
|
|
@@ -64,6 +34,13 @@ class UI {
|
|
|
64
34
|
const messageLoader = new MessageLoader();
|
|
65
35
|
await messageLoader.displayStartMessage();
|
|
66
36
|
|
|
37
|
+
// Parse channel flags (--channel/--all-*/--next=/--pin) once. Warnings
|
|
38
|
+
// are surfaced immediately so the user sees them before any git ops run.
|
|
39
|
+
const channelOptions = parseChannelOptions(options);
|
|
40
|
+
for (const warning of channelOptions.warnings) {
|
|
41
|
+
await prompts.log.warn(warning);
|
|
42
|
+
}
|
|
43
|
+
|
|
67
44
|
// Get directory from options or prompt
|
|
68
45
|
let confirmedDirectory;
|
|
69
46
|
if (options.directory) {
|
|
@@ -183,10 +160,38 @@ class UI {
|
|
|
183
160
|
selectedModules.unshift('core');
|
|
184
161
|
}
|
|
185
162
|
|
|
163
|
+
// For existing installs, resolve per-module update decisions BEFORE
|
|
164
|
+
// we clone anything. Reads the existing manifest's recorded channel
|
|
165
|
+
// per module and prompts the user on available upgrades (patch/minor
|
|
166
|
+
// default Y, major default N). Legacy entries with no channel are
|
|
167
|
+
// migrated here too. Mutates channelOptions.pins to lock rejections.
|
|
168
|
+
await this._resolveUpdateChannels({
|
|
169
|
+
bmadDir,
|
|
170
|
+
selectedModules,
|
|
171
|
+
channelOptions,
|
|
172
|
+
yes: options.yes || false,
|
|
173
|
+
});
|
|
174
|
+
|
|
186
175
|
// Get tool selection
|
|
187
176
|
const toolSelection = await this.promptToolSelection(confirmedDirectory, options);
|
|
188
177
|
|
|
189
|
-
const moduleConfigs = await this.collectModuleConfigs(confirmedDirectory, selectedModules,
|
|
178
|
+
const moduleConfigs = await this.collectModuleConfigs(confirmedDirectory, selectedModules, {
|
|
179
|
+
...options,
|
|
180
|
+
channelOptions,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Warn about --pin/--next flags that refer to modules the user didn't
|
|
184
|
+
// select, or that target bundled modules (core/bmm) where channel
|
|
185
|
+
// flags don't apply.
|
|
186
|
+
{
|
|
187
|
+
const bundledCodes = await this._bundledModuleCodes();
|
|
188
|
+
for (const warning of [
|
|
189
|
+
...orphanPinWarnings(channelOptions, selectedModules),
|
|
190
|
+
...bundledTargetWarnings(channelOptions, bundledCodes),
|
|
191
|
+
]) {
|
|
192
|
+
await prompts.log.warn(warning);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
190
195
|
|
|
191
196
|
return {
|
|
192
197
|
actionType: 'update',
|
|
@@ -197,6 +202,7 @@ class UI {
|
|
|
197
202
|
coreConfig: moduleConfigs.core || {},
|
|
198
203
|
moduleConfigs: moduleConfigs,
|
|
199
204
|
skipPrompts: options.yes || false,
|
|
205
|
+
channelOptions,
|
|
200
206
|
};
|
|
201
207
|
}
|
|
202
208
|
}
|
|
@@ -236,8 +242,31 @@ class UI {
|
|
|
236
242
|
if (!selectedModules.includes('core')) {
|
|
237
243
|
selectedModules.unshift('core');
|
|
238
244
|
}
|
|
245
|
+
|
|
246
|
+
// Interactive channel gate: "Ready to install (all stable)? [Y/n]"
|
|
247
|
+
// Only shown for fresh installs with no channel flags and an external module
|
|
248
|
+
// selected. Non-interactive installs skip this and fall through to the
|
|
249
|
+
// registry default (stable) or whatever flags were supplied.
|
|
250
|
+
await this._interactiveChannelGate({ options, channelOptions, selectedModules });
|
|
251
|
+
|
|
239
252
|
let toolSelection = await this.promptToolSelection(confirmedDirectory, options);
|
|
240
|
-
const moduleConfigs = await this.collectModuleConfigs(confirmedDirectory, selectedModules,
|
|
253
|
+
const moduleConfigs = await this.collectModuleConfigs(confirmedDirectory, selectedModules, {
|
|
254
|
+
...options,
|
|
255
|
+
channelOptions,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Warn about --pin/--next flags that refer to modules the user didn't
|
|
259
|
+
// select, or that target bundled modules (core/bmm) where channel
|
|
260
|
+
// flags don't apply.
|
|
261
|
+
{
|
|
262
|
+
const bundledCodes = await this._bundledModuleCodes();
|
|
263
|
+
for (const warning of [
|
|
264
|
+
...orphanPinWarnings(channelOptions, selectedModules),
|
|
265
|
+
...bundledTargetWarnings(channelOptions, bundledCodes),
|
|
266
|
+
]) {
|
|
267
|
+
await prompts.log.warn(warning);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
241
270
|
|
|
242
271
|
return {
|
|
243
272
|
actionType: 'install',
|
|
@@ -248,6 +277,7 @@ class UI {
|
|
|
248
277
|
coreConfig: moduleConfigs.core || {},
|
|
249
278
|
moduleConfigs: moduleConfigs,
|
|
250
279
|
skipPrompts: options.yes || false,
|
|
280
|
+
channelOptions,
|
|
251
281
|
};
|
|
252
282
|
}
|
|
253
283
|
|
|
@@ -519,7 +549,7 @@ class UI {
|
|
|
519
549
|
*/
|
|
520
550
|
async collectModuleConfigs(directory, modules, options = {}) {
|
|
521
551
|
const { OfficialModules } = require('./modules/official-modules');
|
|
522
|
-
const configCollector = new OfficialModules();
|
|
552
|
+
const configCollector = new OfficialModules({ channelOptions: options.channelOptions });
|
|
523
553
|
|
|
524
554
|
// Seed core config from CLI options if provided
|
|
525
555
|
if (options.userName || options.communicationLanguage || options.documentOutputLanguage || options.outputFolder) {
|
|
@@ -598,7 +628,7 @@ class UI {
|
|
|
598
628
|
const officialCodes = new Set(officialSelected);
|
|
599
629
|
const externalManager = new ExternalModuleManager();
|
|
600
630
|
const registryModules = await externalManager.listAvailable();
|
|
601
|
-
const officialRegistryCodes = new Set(registryModules.map((m) => m.code));
|
|
631
|
+
const officialRegistryCodes = new Set(['core', 'bmm', ...registryModules.map((m) => m.code)]);
|
|
602
632
|
const installedNonOfficial = [...installedModuleIds].filter((id) => !officialRegistryCodes.has(id));
|
|
603
633
|
|
|
604
634
|
// Phase 2: Community modules (category drill-down)
|
|
@@ -630,6 +660,11 @@ class UI {
|
|
|
630
660
|
* @returns {Array} Selected official module codes
|
|
631
661
|
*/
|
|
632
662
|
async _selectOfficialModules(installedModuleIds = new Set()) {
|
|
663
|
+
// Built-in modules (core, bmm) come from local source, not the registry
|
|
664
|
+
const { OfficialModules } = require('./modules/official-modules');
|
|
665
|
+
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
|
|
666
|
+
|
|
667
|
+
// External modules come from the registry (with fallback)
|
|
633
668
|
const externalManager = new ExternalModuleManager();
|
|
634
669
|
const registryModules = await externalManager.listAvailable();
|
|
635
670
|
|
|
@@ -637,20 +672,34 @@ class UI {
|
|
|
637
672
|
const initialValues = [];
|
|
638
673
|
const lockedValues = ['core'];
|
|
639
674
|
|
|
640
|
-
const buildModuleEntry = async (
|
|
641
|
-
const isInstalled = installedModuleIds.has(
|
|
642
|
-
const version = await
|
|
643
|
-
const label = version ? `${
|
|
675
|
+
const buildModuleEntry = async (code, name, description, isDefault) => {
|
|
676
|
+
const isInstalled = installedModuleIds.has(code);
|
|
677
|
+
const version = await getModuleVersion(code);
|
|
678
|
+
const label = version ? `${name} (v${version})` : name;
|
|
644
679
|
return {
|
|
645
680
|
label,
|
|
646
|
-
value:
|
|
647
|
-
hint:
|
|
648
|
-
selected: isInstalled,
|
|
681
|
+
value: code,
|
|
682
|
+
hint: description,
|
|
683
|
+
selected: isInstalled || isDefault,
|
|
649
684
|
};
|
|
650
685
|
};
|
|
651
686
|
|
|
687
|
+
// Add built-in modules first (always available regardless of network)
|
|
688
|
+
const builtInCodes = new Set();
|
|
689
|
+
for (const mod of builtInModules) {
|
|
690
|
+
const code = mod.id;
|
|
691
|
+
builtInCodes.add(code);
|
|
692
|
+
const entry = await buildModuleEntry(code, mod.name, mod.description, mod.defaultSelected);
|
|
693
|
+
allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint });
|
|
694
|
+
if (entry.selected) {
|
|
695
|
+
initialValues.push(code);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Add external registry modules (skip built-in duplicates)
|
|
652
700
|
for (const mod of registryModules) {
|
|
653
|
-
|
|
701
|
+
if (mod.builtIn || builtInCodes.has(mod.code)) continue;
|
|
702
|
+
const entry = await buildModuleEntry(mod.code, mod.name, mod.description, mod.defaultSelected);
|
|
654
703
|
allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint });
|
|
655
704
|
if (entry.selected) {
|
|
656
705
|
initialValues.push(mod.code);
|
|
@@ -1122,12 +1171,26 @@ class UI {
|
|
|
1122
1171
|
* @returns {Array} Default module codes
|
|
1123
1172
|
*/
|
|
1124
1173
|
async getDefaultModules(installedModuleIds = new Set()) {
|
|
1125
|
-
|
|
1126
|
-
const
|
|
1174
|
+
// Built-in modules with default_selected come from local source
|
|
1175
|
+
const { OfficialModules } = require('./modules/official-modules');
|
|
1176
|
+
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
|
|
1127
1177
|
|
|
1128
1178
|
const defaultModules = [];
|
|
1179
|
+
const seen = new Set();
|
|
1180
|
+
|
|
1181
|
+
for (const mod of builtInModules) {
|
|
1182
|
+
if (mod.defaultSelected || installedModuleIds.has(mod.id)) {
|
|
1183
|
+
defaultModules.push(mod.id);
|
|
1184
|
+
seen.add(mod.id);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Add external registry defaults
|
|
1189
|
+
const externalManager = new ExternalModuleManager();
|
|
1190
|
+
const registryModules = await externalManager.listAvailable();
|
|
1129
1191
|
|
|
1130
1192
|
for (const mod of registryModules) {
|
|
1193
|
+
if (mod.builtIn || seen.has(mod.code)) continue;
|
|
1131
1194
|
if (mod.defaultSelected || installedModuleIds.has(mod.code)) {
|
|
1132
1195
|
defaultModules.push(mod.code);
|
|
1133
1196
|
}
|
|
@@ -1561,6 +1624,349 @@ class UI {
|
|
|
1561
1624
|
});
|
|
1562
1625
|
await prompts.log.message('Selected tools:\n' + toolLines.join('\n'));
|
|
1563
1626
|
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Return the set of module codes the registry marks as built-in (core, bmm).
|
|
1630
|
+
* These ship with the installer binary and have no per-module channel.
|
|
1631
|
+
*/
|
|
1632
|
+
async _bundledModuleCodes() {
|
|
1633
|
+
const externalManager = new ExternalModuleManager();
|
|
1634
|
+
try {
|
|
1635
|
+
const modules = await externalManager.listAvailable();
|
|
1636
|
+
return modules.filter((m) => m.builtIn).map((m) => m.code);
|
|
1637
|
+
} catch {
|
|
1638
|
+
// Registry unreachable — fall back to the known bundled codes.
|
|
1639
|
+
return ['core', 'bmm'];
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Fast-path channel gate: confirm "all stable" or open the per-module picker.
|
|
1645
|
+
*
|
|
1646
|
+
* Skipped when:
|
|
1647
|
+
* - running non-interactively (--yes)
|
|
1648
|
+
* - the user already passed channel flags (--channel / --pin / --next)
|
|
1649
|
+
* - no externals/community modules are selected
|
|
1650
|
+
*
|
|
1651
|
+
* Mutates channelOptions.pins and channelOptions.nextSet to reflect picker choices.
|
|
1652
|
+
*/
|
|
1653
|
+
async _interactiveChannelGate({ options, channelOptions, selectedModules }) {
|
|
1654
|
+
if (options.yes) return;
|
|
1655
|
+
// If the user already declared their channel intent via flags, trust them
|
|
1656
|
+
// and skip the gate.
|
|
1657
|
+
const haveFlagIntent = channelOptions.global || channelOptions.nextSet.size > 0 || channelOptions.pins.size > 0;
|
|
1658
|
+
if (haveFlagIntent) return;
|
|
1659
|
+
|
|
1660
|
+
// Figure out which selected modules actually get a channel (externals +
|
|
1661
|
+
// community modules). Bundled core/bmm and custom modules skip the picker.
|
|
1662
|
+
const externalManager = new ExternalModuleManager();
|
|
1663
|
+
const externals = await externalManager.listAvailable();
|
|
1664
|
+
const externalByCode = new Map(externals.map((m) => [m.code, m]));
|
|
1665
|
+
|
|
1666
|
+
const { CommunityModuleManager } = require('./modules/community-manager');
|
|
1667
|
+
const communityMgr = new CommunityModuleManager();
|
|
1668
|
+
const community = await communityMgr.listAll();
|
|
1669
|
+
const communityByCode = new Map(community.map((m) => [m.code, m]));
|
|
1670
|
+
|
|
1671
|
+
const channelSelectable = selectedModules.filter((code) => {
|
|
1672
|
+
const info = externalByCode.get(code) || communityByCode.get(code);
|
|
1673
|
+
return info && !info.builtIn;
|
|
1674
|
+
});
|
|
1675
|
+
if (channelSelectable.length === 0) return;
|
|
1676
|
+
|
|
1677
|
+
const fastPath = await prompts.confirm({
|
|
1678
|
+
message: `Ready to install (all stable)? Pick "n" to customize channels or pin versions.`,
|
|
1679
|
+
default: true,
|
|
1680
|
+
});
|
|
1681
|
+
if (fastPath) return; // stable for all, registry default applies
|
|
1682
|
+
|
|
1683
|
+
// Customize path: per-module picker.
|
|
1684
|
+
const { fetchStableTags, parseGitHubRepo } = require('./modules/channel-resolver');
|
|
1685
|
+
|
|
1686
|
+
for (const code of channelSelectable) {
|
|
1687
|
+
const info = externalByCode.get(code) || communityByCode.get(code);
|
|
1688
|
+
const repoUrl = info.url;
|
|
1689
|
+
|
|
1690
|
+
// Try to pre-resolve the top stable tag so we can surface it in the picker.
|
|
1691
|
+
let stableLabel = 'stable (released version)';
|
|
1692
|
+
try {
|
|
1693
|
+
const parsed = repoUrl ? parseGitHubRepo(repoUrl) : null;
|
|
1694
|
+
if (parsed) {
|
|
1695
|
+
const tags = await fetchStableTags(parsed.owner, parsed.repo);
|
|
1696
|
+
if (tags.length > 0) {
|
|
1697
|
+
stableLabel = `stable ${tags[0].tag} (released version)`;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
} catch {
|
|
1701
|
+
// fall through with the generic label
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
const choice = await prompts.select({
|
|
1705
|
+
message: `${code}: choose a channel`,
|
|
1706
|
+
choices: [
|
|
1707
|
+
{ name: stableLabel, value: 'stable' },
|
|
1708
|
+
{ name: 'next (main HEAD \u2014 current development)', value: 'next' },
|
|
1709
|
+
{ name: 'pin (specific version)', value: 'pin' },
|
|
1710
|
+
],
|
|
1711
|
+
default: 'stable',
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
if (choice === 'next') {
|
|
1715
|
+
channelOptions.nextSet.add(code);
|
|
1716
|
+
} else if (choice === 'pin') {
|
|
1717
|
+
const pinValue = await prompts.text({
|
|
1718
|
+
message: `Enter a version tag for '${code}' (e.g. v1.6.0):`,
|
|
1719
|
+
validate: (value) => {
|
|
1720
|
+
if (!value || !/^[\w.\-+/]+$/.test(String(value).trim())) {
|
|
1721
|
+
return 'Must be a non-empty tag name (letters, digits, dots, hyphens).';
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
});
|
|
1725
|
+
channelOptions.pins.set(code, String(pinValue).trim());
|
|
1726
|
+
}
|
|
1727
|
+
// 'stable' is the default; nothing to record.
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/**
|
|
1732
|
+
* Resolve channel decisions for an update over an existing install.
|
|
1733
|
+
*
|
|
1734
|
+
* For each selected external/community module:
|
|
1735
|
+
* - Read the recorded channel from the existing manifest.
|
|
1736
|
+
* - On `stable`: query tags; if a newer stable exists, classify the diff
|
|
1737
|
+
* and prompt. Patch/minor default Y; major defaults N. `--yes` accepts
|
|
1738
|
+
* defaults (patches/minors) but NOT majors — a major under --yes stays
|
|
1739
|
+
* frozen unless the user also passes `--pin CODE=NEW_TAG`.
|
|
1740
|
+
* - On `next`: no prompt (pull HEAD).
|
|
1741
|
+
* - On `pinned`: no prompt (stays pinned).
|
|
1742
|
+
* - No channel recorded and `version: null`: one-time migration prompt
|
|
1743
|
+
* ("Switch to stable / Keep on next").
|
|
1744
|
+
*
|
|
1745
|
+
* Decisions that freeze the current version are applied by adding a pin to
|
|
1746
|
+
* `channelOptions.pins` so downstream clone logic honors them.
|
|
1747
|
+
*/
|
|
1748
|
+
async _resolveUpdateChannels({ bmadDir, selectedModules, channelOptions, yes }) {
|
|
1749
|
+
const { Manifest } = require('./core/manifest');
|
|
1750
|
+
const manifestObj = new Manifest();
|
|
1751
|
+
const manifest = await manifestObj.read(bmadDir);
|
|
1752
|
+
const existingByName = new Map();
|
|
1753
|
+
for (const m of manifest?.modulesDetailed || []) {
|
|
1754
|
+
if (m?.name) existingByName.set(m.name, m);
|
|
1755
|
+
}
|
|
1756
|
+
if (existingByName.size === 0) return;
|
|
1757
|
+
|
|
1758
|
+
const externalManager = new ExternalModuleManager();
|
|
1759
|
+
const externals = await externalManager.listAvailable();
|
|
1760
|
+
const externalByCode = new Map(externals.map((m) => [m.code, m]));
|
|
1761
|
+
|
|
1762
|
+
const { CommunityModuleManager } = require('./modules/community-manager');
|
|
1763
|
+
const communityMgr = new CommunityModuleManager();
|
|
1764
|
+
const community = await communityMgr.listAll();
|
|
1765
|
+
const communityByCode = new Map(community.map((m) => [m.code, m]));
|
|
1766
|
+
|
|
1767
|
+
const { fetchStableTags, classifyUpgrade, releaseNotesUrl } = require('./modules/channel-resolver');
|
|
1768
|
+
const { parseGitHubRepo } = require('./modules/channel-resolver');
|
|
1769
|
+
|
|
1770
|
+
// Interactive-only: offer a one-time gate to review / switch channels for
|
|
1771
|
+
// selected modules that are already installed. Default N so normal Modify
|
|
1772
|
+
// flows (add/remove modules) aren't interrupted.
|
|
1773
|
+
let reviewChannels = false;
|
|
1774
|
+
if (!yes) {
|
|
1775
|
+
const existingWithChannel = selectedModules.filter((code) => {
|
|
1776
|
+
const prev = existingByName.get(code);
|
|
1777
|
+
if (!prev) return false;
|
|
1778
|
+
const info = externalByCode.get(code) || communityByCode.get(code);
|
|
1779
|
+
return info && !info.builtIn;
|
|
1780
|
+
});
|
|
1781
|
+
if (existingWithChannel.length > 0) {
|
|
1782
|
+
reviewChannels = await prompts.confirm({
|
|
1783
|
+
message: 'Review channel assignments (stable / next / pin) for your existing modules?',
|
|
1784
|
+
default: false,
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
for (const code of selectedModules) {
|
|
1790
|
+
const prev = existingByName.get(code);
|
|
1791
|
+
if (!prev) continue;
|
|
1792
|
+
|
|
1793
|
+
const info = externalByCode.get(code) || communityByCode.get(code);
|
|
1794
|
+
if (!info) continue;
|
|
1795
|
+
// Bundled modules (core/bmm) ship with the installer binary itself —
|
|
1796
|
+
// their version is stapled to the CLI version, not a git tag. Skip
|
|
1797
|
+
// tag-API lookups for them; the "upgrade" mechanism is `npx bmad@X install`.
|
|
1798
|
+
if (info.builtIn) continue;
|
|
1799
|
+
|
|
1800
|
+
const repoUrl = info.url;
|
|
1801
|
+
const parsed = repoUrl ? parseGitHubRepo(repoUrl) : null;
|
|
1802
|
+
|
|
1803
|
+
// Legacy migration: manifest carries no channel and a null/empty
|
|
1804
|
+
// version. Offer the one-time pick between stable and next.
|
|
1805
|
+
const recordedChannel = prev.channel || null;
|
|
1806
|
+
const needsMigration = !recordedChannel && (prev.version == null || prev.version === '');
|
|
1807
|
+
if (needsMigration) {
|
|
1808
|
+
if (yes) {
|
|
1809
|
+
// Conservative headless default: stable.
|
|
1810
|
+
continue;
|
|
1811
|
+
}
|
|
1812
|
+
const chosen = await prompts.select({
|
|
1813
|
+
message: `${code}: your existing install tracks the main branch. Switch to stable releases (recommended for production), or keep on main?`,
|
|
1814
|
+
choices: [
|
|
1815
|
+
{ name: 'Switch to stable', value: 'stable' },
|
|
1816
|
+
{ name: 'Keep on main (next)', value: 'next' },
|
|
1817
|
+
],
|
|
1818
|
+
default: 'stable',
|
|
1819
|
+
});
|
|
1820
|
+
if (chosen === 'next') channelOptions.nextSet.add(code);
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// Optional channel-switch offer. Fires only when the user opted in via
|
|
1825
|
+
// the gate above. 'keep' falls through to the existing per-channel
|
|
1826
|
+
// logic (which runs upgrade classification for stable). Any switch
|
|
1827
|
+
// records the new intent into channelOptions and skips upgrade prompts.
|
|
1828
|
+
if (reviewChannels && recordedChannel) {
|
|
1829
|
+
const switchChoices = [
|
|
1830
|
+
{
|
|
1831
|
+
name: `Keep on '${recordedChannel}'${prev.version ? ` @ ${prev.version}` : ''}`,
|
|
1832
|
+
value: 'keep',
|
|
1833
|
+
},
|
|
1834
|
+
];
|
|
1835
|
+
if (recordedChannel !== 'stable') {
|
|
1836
|
+
switchChoices.push({ name: 'Switch to stable (released version)', value: 'stable' });
|
|
1837
|
+
}
|
|
1838
|
+
if (recordedChannel !== 'next') {
|
|
1839
|
+
switchChoices.push({ name: 'Switch to next (main HEAD)', value: 'next' });
|
|
1840
|
+
}
|
|
1841
|
+
switchChoices.push({ name: 'Pin to a specific version tag', value: 'pin' });
|
|
1842
|
+
|
|
1843
|
+
const choice = await prompts.select({
|
|
1844
|
+
message: `${code} channel:`,
|
|
1845
|
+
choices: switchChoices,
|
|
1846
|
+
default: 'keep',
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
if (choice === 'next') {
|
|
1850
|
+
channelOptions.nextSet.add(code);
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
if (choice === 'pin') {
|
|
1854
|
+
const pinValue = await prompts.text({
|
|
1855
|
+
message: `Enter a version tag for '${code}' (e.g. v1.6.0):`,
|
|
1856
|
+
validate: (value) => {
|
|
1857
|
+
if (!value || !/^[\w.\-+/]+$/.test(String(value).trim())) {
|
|
1858
|
+
return 'Must be a non-empty tag name (letters, digits, dots, hyphens).';
|
|
1859
|
+
}
|
|
1860
|
+
},
|
|
1861
|
+
});
|
|
1862
|
+
channelOptions.pins.set(code, String(pinValue).trim());
|
|
1863
|
+
continue;
|
|
1864
|
+
}
|
|
1865
|
+
if (choice === 'stable') {
|
|
1866
|
+
// Switch to stable: install at the top stable tag without an
|
|
1867
|
+
// upgrade-classification prompt (the user explicitly opted in).
|
|
1868
|
+
// Also warm the tag cache here so the actual clone step doesn't
|
|
1869
|
+
// need a second GitHub API call (can hit rate limits).
|
|
1870
|
+
if (parsed) {
|
|
1871
|
+
try {
|
|
1872
|
+
await fetchStableTags(parsed.owner, parsed.repo);
|
|
1873
|
+
} catch {
|
|
1874
|
+
// best effort; clone step will surface any failure
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
// 'keep' → fall through with recordedChannel below.
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (recordedChannel === 'pinned' || recordedChannel === 'next') {
|
|
1883
|
+
// Respect any explicit channel intent the user already expressed via
|
|
1884
|
+
// CLI flags (--channel / --all-* / --next=CODE / --pin CODE=TAG) or
|
|
1885
|
+
// via the interactive review gate above. Only auto-re-assert the
|
|
1886
|
+
// recorded channel when the user hasn't opted into anything else —
|
|
1887
|
+
// otherwise --all-stable (or a review "switch to stable") would be
|
|
1888
|
+
// silently clobbered by the prior channel.
|
|
1889
|
+
const alreadyDecided = channelOptions.global || channelOptions.nextSet.has(code) || channelOptions.pins.has(code);
|
|
1890
|
+
if (!alreadyDecided) {
|
|
1891
|
+
if (recordedChannel === 'pinned' && prev.version) {
|
|
1892
|
+
channelOptions.pins.set(code, prev.version);
|
|
1893
|
+
} else if (recordedChannel === 'next') {
|
|
1894
|
+
channelOptions.nextSet.add(code);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// Stable channel: check for a newer released tag.
|
|
1901
|
+
if (!parsed) continue;
|
|
1902
|
+
// Respect explicit CLI intent (--pin / --next=CODE / --all-*) and any
|
|
1903
|
+
// choice the user already made in the earlier review gate. Without this
|
|
1904
|
+
// guard the upgrade classifier below would unconditionally call
|
|
1905
|
+
// `channelOptions.pins.set(code, prev.version)` on decline/major-refuse/
|
|
1906
|
+
// fetch-error, silently clobbering the user's override.
|
|
1907
|
+
const alreadyDecided = channelOptions.global || channelOptions.nextSet.has(code) || channelOptions.pins.has(code);
|
|
1908
|
+
if (alreadyDecided) continue;
|
|
1909
|
+
let tags;
|
|
1910
|
+
try {
|
|
1911
|
+
tags = await fetchStableTags(parsed.owner, parsed.repo);
|
|
1912
|
+
} catch (error) {
|
|
1913
|
+
await prompts.log.warn(`Could not check for updates on ${code} (${error.message}). Leaving at ${prev.version}.`);
|
|
1914
|
+
if (prev.version) channelOptions.pins.set(code, prev.version);
|
|
1915
|
+
continue;
|
|
1916
|
+
}
|
|
1917
|
+
if (!tags || tags.length === 0) continue;
|
|
1918
|
+
const topTag = tags[0].tag; // e.g. "v1.7.0"
|
|
1919
|
+
const currentTag = prev.version || '';
|
|
1920
|
+
const diffClass = classifyUpgrade(currentTag, topTag);
|
|
1921
|
+
|
|
1922
|
+
if (diffClass === 'none') continue; // already at or above top tag
|
|
1923
|
+
|
|
1924
|
+
const notes = releaseNotesUrl(repoUrl, topTag);
|
|
1925
|
+
let accept;
|
|
1926
|
+
if (diffClass === 'major') {
|
|
1927
|
+
if (yes) {
|
|
1928
|
+
// Major under --yes is refused by design.
|
|
1929
|
+
await prompts.log.warn(
|
|
1930
|
+
`${code} ${currentTag} → ${topTag} is a new major release; staying on ${currentTag}. ` +
|
|
1931
|
+
`To accept, rerun with --pin ${code}=${topTag}.`,
|
|
1932
|
+
);
|
|
1933
|
+
channelOptions.pins.set(code, currentTag);
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
accept = await prompts.confirm({
|
|
1937
|
+
message:
|
|
1938
|
+
`${code} ${topTag} available — new major release (may change behavior).` +
|
|
1939
|
+
(notes ? ` Release notes: ${notes}.` : '') +
|
|
1940
|
+
' Upgrade?',
|
|
1941
|
+
default: false,
|
|
1942
|
+
});
|
|
1943
|
+
} else if (diffClass === 'minor') {
|
|
1944
|
+
if (yes) {
|
|
1945
|
+
accept = true;
|
|
1946
|
+
} else {
|
|
1947
|
+
accept = await prompts.confirm({
|
|
1948
|
+
message: `${code} ${topTag} available (new features).` + (notes ? ` Release notes: ${notes}.` : '') + ' Upgrade?',
|
|
1949
|
+
default: true,
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1952
|
+
} else {
|
|
1953
|
+
// patch
|
|
1954
|
+
if (yes) {
|
|
1955
|
+
accept = true;
|
|
1956
|
+
} else {
|
|
1957
|
+
accept = await prompts.confirm({
|
|
1958
|
+
message: `${code} ${topTag} available. Upgrade?`,
|
|
1959
|
+
default: true,
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
if (!accept && currentTag) {
|
|
1965
|
+
// Freeze the current version by pinning it for this run.
|
|
1966
|
+
channelOptions.pins.set(code, currentTag);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1564
1970
|
}
|
|
1565
1971
|
|
|
1566
1972
|
module.exports = { UI };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
type: agent
|
|
2
|
-
name: bmad-agent-analyst
|
|
3
|
-
displayName: Mary
|
|
4
|
-
title: Business Analyst
|
|
5
|
-
icon: "📊"
|
|
6
|
-
capabilities: "market research, competitive analysis, requirements elicitation, domain expertise"
|
|
7
|
-
role: Strategic Business Analyst + Requirements Expert
|
|
8
|
-
identity: "Senior analyst with deep expertise in market research, competitive analysis, and requirements elicitation. Specializes in translating vague needs into actionable specs."
|
|
9
|
-
communicationStyle: "Speaks with the excitement of a treasure hunter - thrilled by every clue, energized when patterns emerge. Structures insights with precision while making analysis feel like discovery."
|
|
10
|
-
principles: "Channel expert business analysis frameworks: draw upon Porter's Five Forces, SWOT analysis, root cause analysis, and competitive intelligence methodologies to uncover what others miss. Every business challenge has root causes waiting to be discovered. Ground findings in verifiable evidence. Articulate requirements with absolute precision. Ensure all stakeholder voices heard."
|
|
11
|
-
module: bmm
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
type: agent
|
|
2
|
-
name: bmad-agent-tech-writer
|
|
3
|
-
displayName: Paige
|
|
4
|
-
title: Technical Writer
|
|
5
|
-
icon: "📚"
|
|
6
|
-
capabilities: "documentation, Mermaid diagrams, standards compliance, concept explanation"
|
|
7
|
-
role: Technical Documentation Specialist + Knowledge Curator
|
|
8
|
-
identity: "Experienced technical writer expert in CommonMark, DITA, OpenAPI. Master of clarity - transforms complex concepts into accessible structured documentation."
|
|
9
|
-
communicationStyle: "Patient educator who explains like teaching a friend. Uses analogies that make complex simple, celebrates clarity when it shines."
|
|
10
|
-
principles: "Every Technical Document I touch helps someone accomplish a task. Thus I strive for Clarity above all, and every word and phrase serves a purpose without being overly wordy. I believe a picture/diagram is worth 1000s of words and will include diagrams over drawn out text. I understand the intended audience or will clarify with the user so I know when to simplify vs when to be detailed."
|
|
11
|
-
module: bmm
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# Document Project Workflow
|
|
2
|
-
|
|
3
|
-
**Goal:** Document brownfield projects for AI context.
|
|
4
|
-
|
|
5
|
-
**Your Role:** Project documentation specialist.
|
|
6
|
-
- Communicate all responses in {communication_language}
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## INITIALIZATION
|
|
11
|
-
|
|
12
|
-
1. Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve::
|
|
13
|
-
- Use `{user_name}` for greeting
|
|
14
|
-
- Use `{communication_language}` for all communications
|
|
15
|
-
- Use `{document_output_language}` for output documents
|
|
16
|
-
- Use `{planning_artifacts}` for output location and artifact scanning
|
|
17
|
-
- Use `{project_knowledge}` for additional context scanning
|
|
18
|
-
|
|
19
|
-
2. **Greet user** as `{user_name}`, speaking in `{communication_language}`.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## EXECUTION
|
|
24
|
-
|
|
25
|
-
Read fully and follow: `./instructions.md`
|