Compare commits

...

6 Commits

Author SHA1 Message Date
Markus Ende 53a75cbf4a
Merge 1ee10ddcab into 1677d54f22 2026-02-28 03:31:59 +07:00
Chris Chen 1677d54f22
fix: correct docs domain to docs.bmad-method.org (#1777)
* fix: correct docs domain to docs.bmad-method.org

Fixes BMAD-METHOD#1712

* fix: update all docs.bmad-method.org links to HTTPS

- Changed http:// to https:// across README.md and issue templates
- Addresses review comments on PR #1776

---------

Co-authored-by: OpenClaw Assistant <assistant@openclaw.ai>
2026-02-27 13:03:16 -06:00
Markus Ende 1ee10ddcab
Merge branch 'main' into fix/copilot-hardcoded-bmm-config-path 2026-02-25 13:18:14 +01:00
Brian 147144a1ec
Merge branch 'main' into fix/copilot-hardcoded-bmm-config-path 2026-02-20 20:36:33 -06:00
Markus Ende 7a016d5efa fix: address CodeRabbit review comments for github-copilot installer
- Deduplicate selectedModules to prevent duplicate paths in markdown output
- Remove unused primaryModule variable (dead code)
- Refactor loadModuleConfig to accept installedModules param instead of hardcoded 'bmm'
- Make tech-writer BMM-only check explicit (entry.module !== 'bmm' returns null)
- Add test/test-github-copilot-installer.js with comprehensive unit tests
- Add test:copilot script to package.json and include in main test command
2026-02-19 20:37:52 +01:00
Markus Ende c017a5fdba fix: use module-specific config.yaml paths in GitHub Copilot installer
Replace hardcoded bmm/config.yaml references with dynamic module-based paths
so custom modules load their own config.yaml instead of the non-existent bmm config.

- createWorkflowPromptContent(): use entry.module from bmad-help.csv
- createAgentActivatorPromptContent(): use artifact.module
- createTechWriterPromptContent(): use entry.module for config and agent paths
- generateCopilotInstructions(): dynamically list installed module paths

Fixes #1708
2026-02-19 19:58:06 +01:00
7 changed files with 309 additions and 28 deletions

View File

@ -1,7 +1,7 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: 📚 Documentation - name: 📚 Documentation
url: http://docs.bmad-method.org url: https://docs.bmad-method.org
about: Check the docs first — tutorials, guides, and reference about: Check the docs first — tutorials, guides, and reference
- name: 💬 Discord Community - name: 💬 Discord Community
url: https://discord.gg/gk8jAdXWmj url: https://discord.gg/gk8jAdXWmj

View File

@ -28,7 +28,7 @@ body:
attributes: attributes:
label: Documentation location label: Documentation location
description: Where is the documentation that needs improvement? description: Where is the documentation that needs improvement?
placeholder: e.g., http://docs.bmad-method.org/tutorials/getting-started/ or "In the README" placeholder: e.g., https://docs.bmad-method.org/tutorials/getting-started/ or "In the README"
validations: validations:
required: true required: true

View File

@ -20,7 +20,7 @@ Traditional AI tools do the thinking for you, producing average results. BMad ag
- **Party Mode** — Bring multiple agent personas into one session to collaborate and discuss - **Party Mode** — Bring multiple agent personas into one session to collaborate and discuss
- **Complete Lifecycle** — From brainstorming to deployment - **Complete Lifecycle** — From brainstorming to deployment
[Learn more at **docs.bmad-method.org**](http://docs.bmad-method.org) [Learn more at **docs.bmad-method.org**](https://docs.bmad-method.org)
--- ---
@ -28,7 +28,7 @@ Traditional AI tools do the thinking for you, producing average results. BMad ag
**V6 is here and we're just getting started!** The BMad Method is evolving rapidly with optimizations including Cross Platform Agent Team and Sub Agent inclusion, Skills Architecture, BMad Builder v1, Dev Loop Automation, and so much more in the works. **V6 is here and we're just getting started!** The BMad Method is evolving rapidly with optimizations including Cross Platform Agent Team and Sub Agent inclusion, Skills Architecture, BMad Builder v1, Dev Loop Automation, and so much more in the works.
**[📍 Check out the complete Roadmap →](http://docs.bmad-method.org/roadmap/)** **[📍 Check out the complete Roadmap →](https://docs.bmad-method.org/roadmap/)**
--- ---
@ -50,7 +50,7 @@ Follow the installer prompts, then open your AI IDE (Claude Code, Cursor, etc.)
npx bmad-method install --directory /path/to/project --modules bmm --tools claude-code --yes npx bmad-method install --directory /path/to/project --modules bmm --tools claude-code --yes
``` ```
[See all installation options](http://docs.bmad-method.org/how-to/non-interactive-installation/) [See all installation options](https://docs.bmad-method.org/how-to/non-interactive-installation/)
> **Not sure what to do?** Run `/bmad-help` — it tells you exactly what's next and what's optional. You can also ask questions like `/bmad-help I just finished the architecture, what do I do next?` > **Not sure what to do?** Run `/bmad-help` — it tells you exactly what's next and what's optional. You can also ask questions like `/bmad-help I just finished the architecture, what do I do next?`
@ -68,11 +68,11 @@ BMad Method extends with official modules for specialized domains. Available dur
## Documentation ## Documentation
[BMad Method Docs Site](http://docs.bmad-method.org) — Tutorials, guides, concepts, and reference [BMad Method Docs Site](https://docs.bmad-method.org) — Tutorials, guides, concepts, and reference
**Quick links:** **Quick links:**
- [Getting Started Tutorial](http://docs.bmad-method.org/tutorials/getting-started/) - [Getting Started Tutorial](https://docs.bmad-method.org/tutorials/getting-started/)
- [Upgrading from Previous Versions](http://docs.bmad-method.org/how-to/upgrade-to-v6/) - [Upgrading from Previous Versions](https://docs.bmad-method.org/how-to/upgrade-to-v6/)
- [Test Architect Documentation](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/) - [Test Architect Documentation](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/)

View File

@ -40,7 +40,8 @@
"lint:md": "markdownlint-cli2 \"**/*.md\"", "lint:md": "markdownlint-cli2 \"**/*.md\"",
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0", "prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
"rebundle": "node tools/cli/bundlers/bundle-web.js rebundle", "rebundle": "node tools/cli/bundlers/bundle-web.js rebundle",
"test": "npm run test:schemas && npm run test:refs && npm run test:install && npm run validate:schemas && npm run lint && npm run lint:md && npm run format:check", "test": "npm run test:schemas && npm run test:refs && npm run test:install && npm run test:copilot && npm run validate:schemas && npm run lint && npm run lint:md && npm run format:check",
"test:copilot": "node test/test-github-copilot-installer.js",
"test:coverage": "c8 --reporter=text --reporter=html npm run test:schemas", "test:coverage": "c8 --reporter=text --reporter=html npm run test:schemas",
"test:install": "node test/test-installation-components.js", "test:install": "node test/test-installation-components.js",
"test:refs": "node test/test-file-refs-csv.js", "test:refs": "node test/test-file-refs-csv.js",

View File

@ -0,0 +1,238 @@
/**
* GitHub Copilot Installer Tests
*
* Tests for the GitHubCopilotSetup class methods:
* - loadModuleConfig: module-aware config loading
* - createTechWriterPromptContent: BMM-only tech-writer handling
* - generateCopilotInstructions: selectedModules deduplication
*
* Usage: node test/test-github-copilot-installer.js
*/
const path = require('node:path');
const fs = require('fs-extra');
const { GitHubCopilotSetup } = require('../tools/cli/installers/lib/ide/github-copilot');
// ANSI colors
const colors = {
reset: '\u001B[0m',
green: '\u001B[32m',
red: '\u001B[31m',
yellow: '\u001B[33m',
cyan: '\u001B[36m',
dim: '\u001B[2m',
};
let passed = 0;
let failed = 0;
/**
* Test helper: Assert condition
*/
function assert(condition, testName, errorMessage = '') {
if (condition) {
console.log(`${colors.green}${colors.reset} ${testName}`);
passed++;
} else {
console.log(`${colors.red}${colors.reset} ${testName}`);
if (errorMessage) {
console.log(` ${colors.dim}${errorMessage}${colors.reset}`);
}
failed++;
}
}
/**
* Test Suite
*/
async function runTests() {
console.log(`${colors.cyan}========================================`);
console.log('GitHub Copilot Installer Tests');
console.log(`========================================${colors.reset}\n`);
const tempDir = path.join(__dirname, 'temp-copilot-test');
try {
// Clean up any leftover temp directory
await fs.remove(tempDir);
await fs.ensureDir(tempDir);
const installer = new GitHubCopilotSetup();
// ============================================================
// Test Suite 1: loadModuleConfig
// ============================================================
console.log(`${colors.yellow}Test Suite 1: loadModuleConfig${colors.reset}\n`);
// Create mock bmad directory structure with multiple modules
const bmadDir = path.join(tempDir, '_bmad');
await fs.ensureDir(path.join(bmadDir, 'core'));
await fs.ensureDir(path.join(bmadDir, 'bmm'));
await fs.ensureDir(path.join(bmadDir, 'custom-module'));
// Create config files for each module
await fs.writeFile(path.join(bmadDir, 'core', 'config.yaml'), 'project_name: Core Project\nuser_name: CoreUser\n');
await fs.writeFile(path.join(bmadDir, 'bmm', 'config.yaml'), 'project_name: BMM Project\nuser_name: BmmUser\n');
await fs.writeFile(path.join(bmadDir, 'custom-module', 'config.yaml'), 'project_name: Custom Project\nuser_name: CustomUser\n');
// Test 1a: Load config with only core module (default)
const coreConfig = await installer.loadModuleConfig(bmadDir, ['core']);
assert(
coreConfig.project_name === 'Core Project',
'loadModuleConfig loads core config when only core installed',
`Got: ${coreConfig.project_name}`,
);
// Test 1b: Load config with bmm module (should prefer bmm over core)
const bmmConfig = await installer.loadModuleConfig(bmadDir, ['bmm', 'core']);
assert(bmmConfig.project_name === 'BMM Project', 'loadModuleConfig prefers bmm config over core', `Got: ${bmmConfig.project_name}`);
// Test 1c: Load config with custom module (should prefer custom over core)
const customConfig = await installer.loadModuleConfig(bmadDir, ['custom-module', 'core']);
assert(
customConfig.project_name === 'Custom Project',
'loadModuleConfig prefers custom module config over core',
`Got: ${customConfig.project_name}`,
);
// Test 1d: Load config with multiple non-core modules (first wins)
const multiConfig = await installer.loadModuleConfig(bmadDir, ['bmm', 'custom-module', 'core']);
assert(
multiConfig.project_name === 'BMM Project',
'loadModuleConfig uses first non-core module config',
`Got: ${multiConfig.project_name}`,
);
// Test 1e: Empty modules list uses default (core)
const defaultConfig = await installer.loadModuleConfig(bmadDir);
assert(
defaultConfig.project_name === 'Core Project',
'loadModuleConfig defaults to core when no modules specified',
`Got: ${defaultConfig.project_name}`,
);
// Test 1f: Non-existent module falls back to core
const fallbackConfig = await installer.loadModuleConfig(bmadDir, ['nonexistent', 'core']);
assert(
fallbackConfig.project_name === 'Core Project',
'loadModuleConfig falls back to core for non-existent modules',
`Got: ${fallbackConfig.project_name}`,
);
console.log('');
// ============================================================
// Test Suite 2: createTechWriterPromptContent (BMM-only)
// ============================================================
console.log(`${colors.yellow}Test Suite 2: createTechWriterPromptContent (BMM-only)${colors.reset}\n`);
// Test 2a: BMM tech-writer entry should generate content
const bmmTechWriterEntry = {
'agent-name': 'tech-writer',
module: 'bmm',
name: 'Write Document',
};
const bmmResult = installer.createTechWriterPromptContent(bmmTechWriterEntry);
assert(
bmmResult !== null && bmmResult.fileName === 'bmad-bmm-write-document',
'createTechWriterPromptContent generates content for BMM tech-writer',
`Got: ${bmmResult ? bmmResult.fileName : 'null'}`,
);
// Test 2b: Non-BMM tech-writer entry should return null
const customTechWriterEntry = {
'agent-name': 'tech-writer',
module: 'custom-module',
name: 'Write Document',
};
const customResult = installer.createTechWriterPromptContent(customTechWriterEntry);
assert(customResult === null, 'createTechWriterPromptContent returns null for non-BMM tech-writer', `Got: ${customResult}`);
// Test 2c: Core tech-writer entry should return null
const coreTechWriterEntry = {
'agent-name': 'tech-writer',
module: 'core',
name: 'Write Document',
};
const coreResult = installer.createTechWriterPromptContent(coreTechWriterEntry);
assert(coreResult === null, 'createTechWriterPromptContent returns null for core tech-writer', `Got: ${coreResult}`);
// Test 2d: Non-tech-writer BMM entry should return null
const nonTechWriterEntry = {
'agent-name': 'pm',
module: 'bmm',
name: 'Write Document',
};
const nonTechResult = installer.createTechWriterPromptContent(nonTechWriterEntry);
assert(nonTechResult === null, 'createTechWriterPromptContent returns null for non-tech-writer agents', `Got: ${nonTechResult}`);
// Test 2e: Unknown tech-writer command should return null
const unknownCmdEntry = {
'agent-name': 'tech-writer',
module: 'bmm',
name: 'Unknown Command',
};
const unknownResult = installer.createTechWriterPromptContent(unknownCmdEntry);
assert(unknownResult === null, 'createTechWriterPromptContent returns null for unknown commands', `Got: ${unknownResult}`);
console.log('');
// ============================================================
// Test Suite 3: selectedModules deduplication
// ============================================================
console.log(`${colors.yellow}Test Suite 3: selectedModules deduplication${colors.reset}\n`);
// We can't easily test generateCopilotInstructions directly without mocking,
// but we can verify the deduplication logic pattern
const testDedupe = (modules) => {
const installedModules = modules.length > 0 ? [...new Set(modules)] : ['core'];
return installedModules;
};
// Test 3a: Duplicate modules should be deduplicated
const dupeResult = testDedupe(['bmm', 'core', 'bmm', 'custom', 'core', 'custom']);
assert(
dupeResult.length === 3 && dupeResult.includes('bmm') && dupeResult.includes('core') && dupeResult.includes('custom'),
'Deduplication removes duplicate modules',
`Got: ${JSON.stringify(dupeResult)}`,
);
// Test 3b: Empty array defaults to core
const emptyResult = testDedupe([]);
assert(
emptyResult.length === 1 && emptyResult[0] === 'core',
'Empty modules array defaults to core',
`Got: ${JSON.stringify(emptyResult)}`,
);
// Test 3c: Order is preserved after deduplication (first occurrence wins)
const orderResult = testDedupe(['custom', 'bmm', 'custom', 'bmm']);
assert(
orderResult[0] === 'custom' && orderResult[1] === 'bmm',
'Deduplication preserves order (first occurrence)',
`Got: ${JSON.stringify(orderResult)}`,
);
} finally {
// Cleanup
await fs.remove(tempDir);
}
// Print summary
console.log(`${colors.cyan}========================================`);
console.log('Test Results:');
console.log(` Passed: ${passed}`);
console.log(` Failed: ${failed}`);
console.log(`========================================${colors.reset}\n`);
if (failed > 0) {
console.log(`${colors.red}Some tests failed!${colors.reset}`);
process.exit(1);
} else {
console.log(`${colors.green}✨ All GitHub Copilot installer tests passed!${colors.reset}`);
}
}
runTests().catch((error) => {
console.error(`${colors.red}Test runner error:${colors.reset}`, error);
process.exit(1);
});

View File

@ -12,7 +12,7 @@ startMessage: |
- Select and install modules during setup - customize your experience - Select and install modules during setup - customize your experience
- New BMad Method for Agile AI-Driven Development (the evolution of V4) - New BMad Method for Agile AI-Driven Development (the evolution of V4)
- Exciting new modules available during installation, with community modules coming soon - Exciting new modules available during installation, with community modules coming soon
- Documentation: docs.bmad-method.com - Documentation: https://docs.bmad-method.org
🌟 BMad is 100% free and open source. 🌟 BMad is 100% free and open source.
- No gated Discord. No paywalls. No gated content. - No gated Discord. No paywalls. No gated content.

View File

@ -247,9 +247,9 @@ You must fully embody this agent's persona and follow all activation instruction
*/ */
createWorkflowPromptContent(entry, workflowFile, toolsStr) { createWorkflowPromptContent(entry, workflowFile, toolsStr) {
const description = this.escapeYamlSingleQuote(this.createPromptDescription(entry.name)); const description = this.escapeYamlSingleQuote(this.createPromptDescription(entry.name));
// bmm/config.yaml is safe to hardcode here: these prompts are only generated when // Use the module from the bmad-help.csv entry to reference the correct config.yaml
// bmad-help.csv exists (bmm module data), so bmm is guaranteed to be installed. const configModule = entry.module || 'core';
const configLine = `1. Load {project-root}/${this.bmadFolderName}/bmm/config.yaml and store ALL fields as session variables`; const configLine = `1. Load {project-root}/${this.bmadFolderName}/${configModule}/config.yaml and store ALL fields as session variables`;
let body; let body;
if (workflowFile.endsWith('.yaml')) { if (workflowFile.endsWith('.yaml')) {
@ -324,11 +324,13 @@ ${body}
/** /**
* Create prompt content for tech-writer agent-only commands (Pattern C) * Create prompt content for tech-writer agent-only commands (Pattern C)
* Tech-writer is BMM-specific - these commands only work with the BMM module.
* @param {Object} entry - bmad-help.csv row * @param {Object} entry - bmad-help.csv row
* @returns {Object|null} { fileName, content } or null if not a tech-writer command * @returns {Object|null} { fileName, content } or null if not a tech-writer command
*/ */
createTechWriterPromptContent(entry) { createTechWriterPromptContent(entry) {
if (entry['agent-name'] !== 'tech-writer') return null; // Tech-writer is BMM-specific - only process entries from the bmm module
if (entry['agent-name'] !== 'tech-writer' || entry.module !== 'bmm') return null;
const techWriterCommands = { const techWriterCommands = {
'Write Document': { code: 'WD', file: 'bmad-bmm-write-document', description: 'Write document' }, 'Write Document': { code: 'WD', file: 'bmad-bmm-write-document', description: 'Write document' },
@ -344,14 +346,16 @@ ${body}
const safeDescription = this.escapeYamlSingleQuote(cmd.description); const safeDescription = this.escapeYamlSingleQuote(cmd.description);
const toolsStr = this.getToolsForFile(`${cmd.file}.prompt.md`); const toolsStr = this.getToolsForFile(`${cmd.file}.prompt.md`);
// Use the module from the bmad-help.csv entry to reference the correct paths
const configModule = entry.module || 'core';
const content = `--- const content = `---
description: '${safeDescription}' description: '${safeDescription}'
agent: 'agent' agent: 'agent'
tools: ${toolsStr} tools: ${toolsStr}
--- ---
1. Load {project-root}/${this.bmadFolderName}/bmm/config.yaml and store ALL fields as session variables 1. Load {project-root}/${this.bmadFolderName}/${configModule}/config.yaml and store ALL fields as session variables
2. Load the full agent file from {project-root}/${this.bmadFolderName}/bmm/agents/tech-writer/tech-writer.md and activate the Paige (Technical Writer) persona 2. Load the full agent file from {project-root}/${this.bmadFolderName}/${configModule}/agents/tech-writer/tech-writer.md and activate the Paige (Technical Writer) persona
3. Execute the ${entry.name} menu command (${cmd.code}) 3. Execute the ${entry.name} menu command (${cmd.code})
`; `;
@ -376,15 +380,15 @@ tools: ${toolsStr}
const agentPath = artifact.agentPath || artifact.relativePath; const agentPath = artifact.agentPath || artifact.relativePath;
const agentFilePath = `{project-root}/${this.bmadFolderName}/${agentPath}`; const agentFilePath = `{project-root}/${this.bmadFolderName}/${agentPath}`;
// bmm/config.yaml is safe to hardcode: agent activators are only generated from // Use the agent's module to reference the correct config.yaml
// bmm agent artifacts, so bmm is guaranteed to be installed. const configModule = artifact.module || 'core';
return `--- return `---
description: '${safeDescription}' description: '${safeDescription}'
agent: 'agent' agent: 'agent'
tools: ${toolsStr} tools: ${toolsStr}
--- ---
1. Load {project-root}/${this.bmadFolderName}/bmm/config.yaml and store ALL fields as session variables 1. Load {project-root}/${this.bmadFolderName}/${configModule}/config.yaml and store ALL fields as session variables
2. Load the full agent file from ${agentFilePath} 2. Load the full agent file from ${agentFilePath}
3. Follow ALL activation instructions in the agent file 3. Follow ALL activation instructions in the agent file
4. Display the welcome/greeting as instructed 4. Display the welcome/greeting as instructed
@ -400,7 +404,13 @@ tools: ${toolsStr}
* @param {Map} agentManifest - Agent manifest data * @param {Map} agentManifest - Agent manifest data
*/ */
async generateCopilotInstructions(projectDir, bmadDir, agentManifest, options = {}) { async generateCopilotInstructions(projectDir, bmadDir, agentManifest, options = {}) {
const configVars = await this.loadModuleConfig(bmadDir); // Determine installed modules (excluding internal directories)
const selectedModules = options.selectedModules || [];
// Deduplicate selectedModules to prevent duplicate paths in generated markdown
const installedModules = selectedModules.length > 0 ? [...new Set(selectedModules)] : ['core'];
const configVars = await this.loadModuleConfig(bmadDir, installedModules);
// Filter to only non-core modules for display (core is always listed separately)
const nonCoreModules = installedModules.filter((m) => m !== 'core');
// Build the agents table from the manifest // Build the agents table from the manifest
let agentsTable = '| Agent | Persona | Title | Capabilities |\n|---|---|---|---|\n'; let agentsTable = '| Agent | Persona | Title | Capabilities |\n|---|---|---|---|\n';
@ -427,6 +437,36 @@ tools: ${toolsStr}
} }
const bmad = this.bmadFolderName; const bmad = this.bmadFolderName;
// Build dynamic module paths based on installed modules
const moduleAgentPaths = nonCoreModules.map((m) => `\`${bmad}/${m}/agents/\``).join(', ');
const moduleWorkflowPaths = nonCoreModules.map((m) => `\`${bmad}/${m}/workflows/\``).join(', ');
const moduleConfigPaths = nonCoreModules.map((m) => `\`${bmad}/${m}/config.yaml\``).join(', ');
// Build agent definitions line
let agentDefsLine;
if (nonCoreModules.length > 0) {
agentDefsLine = `- **Agent definitions**: ${moduleAgentPaths} and \`${bmad}/core/agents/\` (core)`;
} else {
agentDefsLine = `- **Agent definitions**: \`${bmad}/core/agents/\``;
}
// Build workflow definitions line
let workflowDefsLine;
if (nonCoreModules.length > 0) {
workflowDefsLine = `- **Workflow definitions**: ${moduleWorkflowPaths} (organized by phase)`;
} else {
workflowDefsLine = `- **Workflow definitions**: \`${bmad}/core/workflows/\``;
}
// Build module configuration line
let moduleConfigLine;
if (nonCoreModules.length > 0) {
moduleConfigLine = `- **Module configuration**: ${moduleConfigPaths}`;
} else {
moduleConfigLine = `- **Module configuration**: (no non-core modules installed)`;
}
const bmadSection = `# BMAD Method — Project Instructions const bmadSection = `# BMAD Method — Project Instructions
## Project Configuration ## Project Configuration
@ -443,12 +483,12 @@ tools: ${toolsStr}
## BMAD Runtime Structure ## BMAD Runtime Structure
- **Agent definitions**: \`${bmad}/bmm/agents/\` (BMM module) and \`${bmad}/core/agents/\` (core) ${agentDefsLine}
- **Workflow definitions**: \`${bmad}/bmm/workflows/\` (organized by phase) ${workflowDefsLine}
- **Core tasks**: \`${bmad}/core/tasks/\` (help, editorial review, indexing, sharding, adversarial review) - **Core tasks**: \`${bmad}/core/tasks/\` (help, editorial review, indexing, sharding, adversarial review)
- **Core workflows**: \`${bmad}/core/workflows/\` (brainstorming, party-mode, advanced-elicitation) - **Core workflows**: \`${bmad}/core/workflows/\` (brainstorming, party-mode, advanced-elicitation)
- **Workflow engine**: \`${bmad}/core/tasks/workflow.xml\` (executes YAML-based workflows) - **Workflow engine**: \`${bmad}/core/tasks/workflow.xml\` (executes YAML-based workflows)
- **Module configuration**: \`${bmad}/bmm/config.yaml\` ${moduleConfigLine}
- **Core configuration**: \`${bmad}/core/config.yaml\` - **Core configuration**: \`${bmad}/core/config.yaml\`
- **Agent manifest**: \`${bmad}/_config/agent-manifest.csv\` - **Agent manifest**: \`${bmad}/_config/agent-manifest.csv\`
- **Workflow manifest**: \`${bmad}/_config/workflow-manifest.csv\` - **Workflow manifest**: \`${bmad}/_config/workflow-manifest.csv\`
@ -457,7 +497,7 @@ tools: ${toolsStr}
## Key Conventions ## Key Conventions
- Always load \`${bmad}/bmm/config.yaml\` before any agent activation or workflow execution - Always load the agent/workflow's module \`config.yaml\` before activation or execution (each prompt file specifies which config to load)
- Store all config fields as session variables: \`{user_name}\`, \`{communication_language}\`, \`{output_folder}\`, \`{planning_artifacts}\`, \`{implementation_artifacts}\`, \`{project_knowledge}\` - Store all config fields as session variables: \`{user_name}\`, \`{communication_language}\`, \`{output_folder}\`, \`{planning_artifacts}\`, \`{implementation_artifacts}\`, \`{project_knowledge}\`
- MD-based workflows execute directly load and follow the \`.md\` file - MD-based workflows execute directly load and follow the \`.md\` file
- YAML-based workflows require the workflow engine load \`workflow.xml\` first, then pass the \`.yaml\` config - YAML-based workflows require the workflow engine load \`workflow.xml\` first, then pass the \`.yaml\` config
@ -504,13 +544,15 @@ Type \`/bmad-\` in Copilot Chat to see all available BMAD workflows and agent ac
/** /**
* Load module config.yaml for template variables * Load module config.yaml for template variables
* @param {string} bmadDir - BMAD installation directory * @param {string} bmadDir - BMAD installation directory
* @param {string[]} installedModules - List of installed modules to check for config
* @returns {Object} Config variables * @returns {Object} Config variables
*/ */
async loadModuleConfig(bmadDir) { async loadModuleConfig(bmadDir, installedModules = ['core']) {
const bmmConfigPath = path.join(bmadDir, 'bmm', 'config.yaml'); // Build config paths from installed modules (non-core first, then core as fallback)
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml'); const nonCoreModules = installedModules.filter((m) => m !== 'core');
const configPaths = [...nonCoreModules.map((m) => path.join(bmadDir, m, 'config.yaml')), path.join(bmadDir, 'core', 'config.yaml')];
for (const configPath of [bmmConfigPath, coreConfigPath]) { for (const configPath of configPaths) {
if (await fs.pathExists(configPath)) { if (await fs.pathExists(configPath)) {
try { try {
const content = await fs.readFile(configPath, 'utf8'); const content = await fs.readFile(configPath, 'utf8');