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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Drop-in replacement for fs-extra using native node:fs APIs.
|
|
2
|
+
// Eliminates graceful-fs monkey-patching that causes non-deterministic
|
|
3
|
+
// file loss during multi-module installs on macOS (issue #1779).
|
|
4
|
+
const fsp = require('node:fs/promises');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
async function pathExists(p) {
|
|
9
|
+
try {
|
|
10
|
+
await fsp.access(p);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function ensureDir(dir) {
|
|
18
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function remove(p) {
|
|
22
|
+
await fsp.rm(p, { recursive: true, force: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function copy(src, dest, options = {}) {
|
|
26
|
+
const filterFn = options.filter;
|
|
27
|
+
const overwrite = options.overwrite !== false;
|
|
28
|
+
const srcStat = await fsp.stat(src);
|
|
29
|
+
|
|
30
|
+
if (srcStat.isFile()) {
|
|
31
|
+
if (filterFn && !(await filterFn(src, dest))) return;
|
|
32
|
+
await fsp.mkdir(path.dirname(dest), { recursive: true });
|
|
33
|
+
if (!overwrite) {
|
|
34
|
+
try {
|
|
35
|
+
await fsp.access(dest);
|
|
36
|
+
if (options.errorOnExist) throw new Error(`${dest} already exists`);
|
|
37
|
+
return;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error.message.includes('already exists')) throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
await fsp.copyFile(src, dest);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (srcStat.isDirectory()) {
|
|
47
|
+
if (filterFn && !(await filterFn(src, dest))) return;
|
|
48
|
+
await fsp.mkdir(dest, { recursive: true });
|
|
49
|
+
const entries = await fsp.readdir(src, { withFileTypes: true });
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
await copy(path.join(src, entry.name), path.join(dest, entry.name), options);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function move(src, dest) {
|
|
57
|
+
try {
|
|
58
|
+
await fsp.rename(src, dest);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error.code === 'EXDEV') {
|
|
61
|
+
await copy(src, dest);
|
|
62
|
+
await fsp.rm(src, { recursive: true, force: true });
|
|
63
|
+
} else {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function readJsonSync(p) {
|
|
70
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function writeJson(p, data, options = {}) {
|
|
74
|
+
const spaces = options.spaces ?? 2;
|
|
75
|
+
await fsp.writeFile(p, JSON.stringify(data, null, spaces) + '\n', 'utf8');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
// Native async (node:fs/promises)
|
|
80
|
+
readFile: fsp.readFile,
|
|
81
|
+
writeFile: fsp.writeFile,
|
|
82
|
+
stat: fsp.stat,
|
|
83
|
+
readdir: fsp.readdir,
|
|
84
|
+
access: fsp.access,
|
|
85
|
+
realpath: fsp.realpath,
|
|
86
|
+
rename: fsp.rename,
|
|
87
|
+
rmdir: fsp.rmdir,
|
|
88
|
+
unlink: fsp.unlink,
|
|
89
|
+
chmod: fsp.chmod,
|
|
90
|
+
mkdir: fsp.mkdir,
|
|
91
|
+
mkdtemp: fsp.mkdtemp,
|
|
92
|
+
copyFile: fsp.copyFile,
|
|
93
|
+
rm: fsp.rm,
|
|
94
|
+
|
|
95
|
+
// fs-extra compatible helpers (native implementations)
|
|
96
|
+
pathExists,
|
|
97
|
+
ensureDir,
|
|
98
|
+
remove,
|
|
99
|
+
copy,
|
|
100
|
+
move,
|
|
101
|
+
readJsonSync,
|
|
102
|
+
writeJson,
|
|
103
|
+
|
|
104
|
+
// Sync methods from core node:fs
|
|
105
|
+
existsSync: fs.existsSync.bind(fs),
|
|
106
|
+
readFileSync: fs.readFileSync.bind(fs),
|
|
107
|
+
writeFileSync: fs.writeFileSync.bind(fs),
|
|
108
|
+
statSync: fs.statSync.bind(fs),
|
|
109
|
+
accessSync: fs.accessSync.bind(fs),
|
|
110
|
+
readdirSync: fs.readdirSync.bind(fs),
|
|
111
|
+
createReadStream: fs.createReadStream.bind(fs),
|
|
112
|
+
pathExistsSync: fs.existsSync.bind(fs),
|
|
113
|
+
|
|
114
|
+
// Constants
|
|
115
|
+
constants: fs.constants,
|
|
116
|
+
};
|
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
* - standalone/agents/fred.md → bmad-agent-standalone-fred.md
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
// Type segments - agents are included in naming, others are filtered out
|
|
19
|
-
const TYPE_SEGMENTS = ['workflows', 'tasks', 'tools'];
|
|
20
18
|
const AGENT_SEGMENT = 'agents';
|
|
21
19
|
|
|
22
20
|
// BMAD installation folder name - centralized constant for all installers
|
|
@@ -194,125 +192,6 @@ function parseDashName(filename) {
|
|
|
194
192
|
};
|
|
195
193
|
}
|
|
196
194
|
|
|
197
|
-
// ============================================================================
|
|
198
|
-
// LEGACY FUNCTIONS (underscore format) - kept for backward compatibility
|
|
199
|
-
// ============================================================================
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Convert hierarchical path to flat underscore-separated name (LEGACY)
|
|
203
|
-
* @deprecated Use toDashName instead
|
|
204
|
-
*/
|
|
205
|
-
function toUnderscoreName(module, type, name) {
|
|
206
|
-
const isAgent = type === AGENT_SEGMENT;
|
|
207
|
-
if (module === 'core') {
|
|
208
|
-
return isAgent ? `bmad_agent_${name}.md` : `bmad_${name}.md`;
|
|
209
|
-
}
|
|
210
|
-
if (module === 'standalone') {
|
|
211
|
-
return isAgent ? `bmad_agent_standalone_${name}.md` : `bmad_standalone_${name}.md`;
|
|
212
|
-
}
|
|
213
|
-
return isAgent ? `bmad_${module}_agent_${name}.md` : `bmad_${module}_${name}.md`;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Convert relative path to flat underscore-separated name (LEGACY)
|
|
218
|
-
* @deprecated Use toDashPath instead
|
|
219
|
-
*/
|
|
220
|
-
function toUnderscorePath(relativePath) {
|
|
221
|
-
// Strip common file extensions (same as toDashPath for consistency)
|
|
222
|
-
const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, '');
|
|
223
|
-
const parts = withoutExt.split(/[/\\]/);
|
|
224
|
-
|
|
225
|
-
const module = parts[0];
|
|
226
|
-
const type = parts[1];
|
|
227
|
-
const name = parts.slice(2).join('_');
|
|
228
|
-
|
|
229
|
-
return toUnderscoreName(module, type, name);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Create custom agent underscore name (LEGACY)
|
|
234
|
-
* @deprecated Use customAgentDashName instead
|
|
235
|
-
*/
|
|
236
|
-
function customAgentUnderscoreName(agentName) {
|
|
237
|
-
return `bmad_custom_${agentName}.md`;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Check if a filename uses underscore format (LEGACY)
|
|
242
|
-
* @deprecated Use isDashFormat instead
|
|
243
|
-
*/
|
|
244
|
-
function isUnderscoreFormat(filename) {
|
|
245
|
-
return filename.startsWith('bmad_') && filename.includes('_');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Extract parts from an underscore-formatted filename (LEGACY)
|
|
250
|
-
* @deprecated Use parseDashName instead
|
|
251
|
-
*/
|
|
252
|
-
function parseUnderscoreName(filename) {
|
|
253
|
-
const withoutExt = filename.replace('.md', '');
|
|
254
|
-
const parts = withoutExt.split('_');
|
|
255
|
-
|
|
256
|
-
if (parts.length < 2 || parts[0] !== 'bmad') {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const agentIndex = parts.indexOf('agent');
|
|
261
|
-
|
|
262
|
-
if (agentIndex !== -1) {
|
|
263
|
-
if (agentIndex === 1) {
|
|
264
|
-
// bmad_agent_... - check for standalone
|
|
265
|
-
if (parts.length >= 4 && parts[2] === 'standalone') {
|
|
266
|
-
return {
|
|
267
|
-
prefix: parts[0],
|
|
268
|
-
module: 'standalone',
|
|
269
|
-
type: 'agents',
|
|
270
|
-
name: parts.slice(3).join('_'),
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
prefix: parts[0],
|
|
275
|
-
module: 'core',
|
|
276
|
-
type: 'agents',
|
|
277
|
-
name: parts.slice(agentIndex + 1).join('_'),
|
|
278
|
-
};
|
|
279
|
-
} else {
|
|
280
|
-
return {
|
|
281
|
-
prefix: parts[0],
|
|
282
|
-
module: parts[1],
|
|
283
|
-
type: 'agents',
|
|
284
|
-
name: parts.slice(agentIndex + 1).join('_'),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (parts.length === 2) {
|
|
290
|
-
return {
|
|
291
|
-
prefix: parts[0],
|
|
292
|
-
module: 'core',
|
|
293
|
-
type: 'workflows',
|
|
294
|
-
name: parts[1],
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Check for standalone non-agent: bmad_standalone_name
|
|
299
|
-
if (parts[1] === 'standalone') {
|
|
300
|
-
return {
|
|
301
|
-
prefix: parts[0],
|
|
302
|
-
module: 'standalone',
|
|
303
|
-
type: 'workflows',
|
|
304
|
-
name: parts.slice(2).join('_'),
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
prefix: parts[0],
|
|
310
|
-
module: parts[1],
|
|
311
|
-
type: 'workflows',
|
|
312
|
-
name: parts.slice(2).join('_'),
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
195
|
/**
|
|
317
196
|
* Resolve the skill name for an artifact.
|
|
318
197
|
* Prefers canonicalId from a bmad-skill-manifest.yaml sidecar when available,
|
|
@@ -328,37 +207,13 @@ function resolveSkillName(artifact) {
|
|
|
328
207
|
return toDashPath(artifact.relativePath);
|
|
329
208
|
}
|
|
330
209
|
|
|
331
|
-
// Backward compatibility aliases (colon format was same as underscore)
|
|
332
|
-
const toColonName = toUnderscoreName;
|
|
333
|
-
const toColonPath = toUnderscorePath;
|
|
334
|
-
const customAgentColonName = customAgentUnderscoreName;
|
|
335
|
-
const isColonFormat = isUnderscoreFormat;
|
|
336
|
-
const parseColonName = parseUnderscoreName;
|
|
337
|
-
|
|
338
210
|
module.exports = {
|
|
339
|
-
// New standard (dash-based)
|
|
340
211
|
toDashName,
|
|
341
212
|
toDashPath,
|
|
342
213
|
resolveSkillName,
|
|
343
214
|
customAgentDashName,
|
|
344
215
|
isDashFormat,
|
|
345
216
|
parseDashName,
|
|
346
|
-
|
|
347
|
-
// Legacy (underscore-based) - kept for backward compatibility
|
|
348
|
-
toUnderscoreName,
|
|
349
|
-
toUnderscorePath,
|
|
350
|
-
customAgentUnderscoreName,
|
|
351
|
-
isUnderscoreFormat,
|
|
352
|
-
parseUnderscoreName,
|
|
353
|
-
|
|
354
|
-
// Backward compatibility aliases
|
|
355
|
-
toColonName,
|
|
356
|
-
toColonPath,
|
|
357
|
-
customAgentColonName,
|
|
358
|
-
isColonFormat,
|
|
359
|
-
parseColonName,
|
|
360
|
-
|
|
361
|
-
TYPE_SEGMENTS,
|
|
362
217
|
AGENT_SEGMENT,
|
|
363
218
|
BMAD_FOLDER_NAME,
|
|
364
219
|
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel plan: the per-module resolution decision applied at install time.
|
|
3
|
+
*
|
|
4
|
+
* A "plan entry" for a module is:
|
|
5
|
+
* { channel: 'stable'|'next'|'pinned', pin?: string }
|
|
6
|
+
*
|
|
7
|
+
* We build the plan from:
|
|
8
|
+
* 1. CLI flags (--channel / --all-* / --next=CODE / --pin CODE=TAG)
|
|
9
|
+
* 2. Interactive answers (the "all stable?" gate + per-module picker)
|
|
10
|
+
* 3. Registry defaults (default_channel from registry-fallback.yaml / official.yaml)
|
|
11
|
+
* 4. Hardcoded fallback 'stable'
|
|
12
|
+
*
|
|
13
|
+
* Precedence: --pin > --next=CODE > --channel (global) > registry default > 'stable'.
|
|
14
|
+
*
|
|
15
|
+
* This module is pure. No prompts, no git, no filesystem.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const VALID_CHANNELS = new Set(['stable', 'next']);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse raw commander options into a structured channel options object.
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} options - raw command-line options
|
|
24
|
+
* @returns {{
|
|
25
|
+
* global: 'stable'|'next'|null,
|
|
26
|
+
* nextSet: Set<string>,
|
|
27
|
+
* pins: Map<string, string>,
|
|
28
|
+
* warnings: string[]
|
|
29
|
+
* }}
|
|
30
|
+
*/
|
|
31
|
+
function parseChannelOptions(options = {}) {
|
|
32
|
+
const warnings = [];
|
|
33
|
+
|
|
34
|
+
// Global channel from --channel / --all-stable / --all-next.
|
|
35
|
+
let global = null;
|
|
36
|
+
const aliases = [];
|
|
37
|
+
if (options.channel) aliases.push({ flag: '--channel', value: normalizeChannel(options.channel, warnings, '--channel') });
|
|
38
|
+
if (options.allStable) aliases.push({ flag: '--all-stable', value: 'stable' });
|
|
39
|
+
if (options.allNext) aliases.push({ flag: '--all-next', value: 'next' });
|
|
40
|
+
|
|
41
|
+
const distinct = new Set(aliases.map((a) => a.value).filter(Boolean));
|
|
42
|
+
if (distinct.size > 1) {
|
|
43
|
+
warnings.push(
|
|
44
|
+
`Conflicting channel flags: ${aliases
|
|
45
|
+
.filter((a) => a.value)
|
|
46
|
+
.map((a) => a.flag + '=' + a.value)
|
|
47
|
+
.join(', ')}. Using first: ${aliases.find((a) => a.value).flag}.`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const firstValid = aliases.find((a) => a.value);
|
|
51
|
+
if (firstValid) global = firstValid.value;
|
|
52
|
+
|
|
53
|
+
// --next=CODE (repeatable)
|
|
54
|
+
const nextSet = new Set();
|
|
55
|
+
for (const code of options.next || []) {
|
|
56
|
+
const trimmed = String(code).trim();
|
|
57
|
+
if (!trimmed) continue;
|
|
58
|
+
nextSet.add(trimmed);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// --pin CODE=TAG (repeatable)
|
|
62
|
+
const pins = new Map();
|
|
63
|
+
for (const spec of options.pin || []) {
|
|
64
|
+
const parsed = parsePinSpec(spec);
|
|
65
|
+
if (!parsed) {
|
|
66
|
+
warnings.push(`Ignoring malformed --pin value '${spec}'. Expected CODE=TAG.`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (pins.has(parsed.code)) {
|
|
70
|
+
warnings.push(`--pin specified multiple times for '${parsed.code}'. Using last: ${parsed.tag}.`);
|
|
71
|
+
}
|
|
72
|
+
pins.set(parsed.code, parsed.tag);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// --yes auto-confirms the community-module curator-bypass prompt so
|
|
76
|
+
// headless installs with --next=/--pin for a community module don't hang.
|
|
77
|
+
const acceptBypass = options.yes === true || options.acceptBypass === true;
|
|
78
|
+
|
|
79
|
+
return { global, nextSet, pins, warnings, acceptBypass };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeChannel(raw, warnings, flagName) {
|
|
83
|
+
if (typeof raw !== 'string') return null;
|
|
84
|
+
const lower = raw.trim().toLowerCase();
|
|
85
|
+
if (VALID_CHANNELS.has(lower)) return lower;
|
|
86
|
+
warnings.push(`Ignoring invalid ${flagName} value '${raw}'. Expected one of: stable, next.`);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parsePinSpec(spec) {
|
|
91
|
+
if (typeof spec !== 'string') return null;
|
|
92
|
+
const idx = spec.indexOf('=');
|
|
93
|
+
if (idx <= 0 || idx === spec.length - 1) return null;
|
|
94
|
+
const code = spec.slice(0, idx).trim();
|
|
95
|
+
const tag = spec.slice(idx + 1).trim();
|
|
96
|
+
if (!code || !tag) return null;
|
|
97
|
+
return { code, tag };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Build a per-module plan entry, applying precedence.
|
|
102
|
+
*
|
|
103
|
+
* @param {Object} args
|
|
104
|
+
* @param {string} args.code
|
|
105
|
+
* @param {Object} args.channelOptions - from parseChannelOptions
|
|
106
|
+
* @param {string} [args.registryDefault] - module's default_channel, if any
|
|
107
|
+
* @returns {{channel: 'stable'|'next'|'pinned', pin?: string, source: string}}
|
|
108
|
+
* source describes where the decision came from, for logging / debugging.
|
|
109
|
+
*/
|
|
110
|
+
function decideChannelForModule({ code, channelOptions, registryDefault }) {
|
|
111
|
+
const { global, nextSet, pins } = channelOptions || { nextSet: new Set(), pins: new Map() };
|
|
112
|
+
|
|
113
|
+
if (pins && pins.has(code)) {
|
|
114
|
+
return { channel: 'pinned', pin: pins.get(code), source: 'flag:--pin' };
|
|
115
|
+
}
|
|
116
|
+
if (nextSet && nextSet.has(code)) {
|
|
117
|
+
return { channel: 'next', source: 'flag:--next' };
|
|
118
|
+
}
|
|
119
|
+
if (global) {
|
|
120
|
+
return { channel: global, source: 'flag:--channel' };
|
|
121
|
+
}
|
|
122
|
+
if (registryDefault && VALID_CHANNELS.has(registryDefault)) {
|
|
123
|
+
return { channel: registryDefault, source: 'registry' };
|
|
124
|
+
}
|
|
125
|
+
return { channel: 'stable', source: 'default' };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Build a full channel plan map for a set of modules.
|
|
130
|
+
*
|
|
131
|
+
* @param {Object} args
|
|
132
|
+
* @param {Array<{code: string, defaultChannel?: string, builtIn?: boolean}>} args.modules
|
|
133
|
+
* Only the modules that need a channel entry; callers should filter out
|
|
134
|
+
* bundled modules (core/bmm) before calling.
|
|
135
|
+
* @param {Object} args.channelOptions - from parseChannelOptions
|
|
136
|
+
* @returns {Map<string, {channel: string, pin?: string, source: string}>}
|
|
137
|
+
*/
|
|
138
|
+
function buildPlan({ modules, channelOptions }) {
|
|
139
|
+
const plan = new Map();
|
|
140
|
+
for (const mod of modules || []) {
|
|
141
|
+
plan.set(
|
|
142
|
+
mod.code,
|
|
143
|
+
decideChannelForModule({
|
|
144
|
+
code: mod.code,
|
|
145
|
+
channelOptions,
|
|
146
|
+
registryDefault: mod.defaultChannel,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return plan;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Report any --pin CODE=TAG entries that don't correspond to a selected module.
|
|
155
|
+
* These get warned about but don't abort the install.
|
|
156
|
+
*/
|
|
157
|
+
function orphanPinWarnings(channelOptions, selectedCodes) {
|
|
158
|
+
const warnings = [];
|
|
159
|
+
const selected = new Set(selectedCodes || []);
|
|
160
|
+
for (const code of channelOptions?.pins?.keys() || []) {
|
|
161
|
+
if (!selected.has(code)) {
|
|
162
|
+
warnings.push(`--pin for '${code}' has no effect (module not selected).`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const code of channelOptions?.nextSet || []) {
|
|
166
|
+
if (!selected.has(code)) {
|
|
167
|
+
warnings.push(`--next for '${code}' has no effect (module not selected).`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return warnings;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Warn when --pin / --next targets a bundled module (core, bmm). Those are
|
|
175
|
+
* shipped inside the installer binary — there's no git clone to override, so
|
|
176
|
+
* the flag has no effect. Users who actually want a prerelease core/bmm
|
|
177
|
+
* should use `npx bmad-method@next install`.
|
|
178
|
+
*/
|
|
179
|
+
function bundledTargetWarnings(channelOptions, bundledCodes) {
|
|
180
|
+
const warnings = [];
|
|
181
|
+
const bundled = new Set(bundledCodes || []);
|
|
182
|
+
const hint = '(bundled module; use `npx bmad-method@next install` for a prerelease)';
|
|
183
|
+
for (const code of channelOptions?.pins?.keys() || []) {
|
|
184
|
+
if (bundled.has(code)) {
|
|
185
|
+
warnings.push(`--pin for '${code}' has no effect ${hint}.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const code of channelOptions?.nextSet || []) {
|
|
189
|
+
if (bundled.has(code)) {
|
|
190
|
+
warnings.push(`--next for '${code}' has no effect ${hint}.`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return warnings;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
parseChannelOptions,
|
|
198
|
+
decideChannelForModule,
|
|
199
|
+
buildPlan,
|
|
200
|
+
orphanPinWarnings,
|
|
201
|
+
bundledTargetWarnings,
|
|
202
|
+
parsePinSpec,
|
|
203
|
+
};
|