Compare commits
6 Commits
cefdaddfbe
...
53a75cbf4a
| Author | SHA1 | Date |
|---|---|---|
|
|
53a75cbf4a | |
|
|
1677d54f22 | |
|
|
1ee10ddcab | |
|
|
147144a1ec | |
|
|
7a016d5efa | |
|
|
c017a5fdba |
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -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/)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
});
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue