Compare commits
11 Commits
54e4aa477e
...
6c09b8d526
| Author | SHA1 | Date |
|---|---|---|
|
|
6c09b8d526 | |
|
|
efc69ffb2c | |
|
|
44972d62b9 | |
|
|
deedf18fc5 | |
|
|
17fe438452 | |
|
|
d036d34892 | |
|
|
bc7c7f0757 | |
|
|
1ee10ddcab | |
|
|
147144a1ec | |
|
|
7a016d5efa | |
|
|
c017a5fdba |
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -1,5 +1,20 @@
|
|||
# Changelog
|
||||
|
||||
## [6.0.4]
|
||||
|
||||
### 🎁 Features
|
||||
|
||||
* Add edge case hunter review task - new reusable review task that exhaustively traces branching paths and boundary conditions in code, reporting only unhandled gaps. Method-driven analysis complementary to adversarial review (#1790)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* Fix brainstorming to not overwrite previous sessions; now prompts to continue existing brainstorming or start a new one when older brainstorming sessions are found
|
||||
* Fix installer templates - replace legacy `@` path prefixes with explicit `{project-root}` syntax for consistency (#1769)
|
||||
* Fix edge case hunter - remove zero-findings halt condition that was pressuring the LLM to hallucinate findings when none legitimately exist (#1797)
|
||||
* Fix broken docs domain references in README and GitHub issue templates (#1777)
|
||||
|
||||
---
|
||||
|
||||
## [6.0.3]
|
||||
|
||||
### 🎁 Features
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "bmad-method",
|
||||
"version": "6.0.3",
|
||||
"version": "6.0.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bmad-method",
|
||||
"version": "6.0.3",
|
||||
"version": "6.0.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@clack/core": "^1.0.0",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "bmad-method",
|
||||
"version": "6.0.3",
|
||||
"version": "6.0.4",
|
||||
"description": "Breakthrough Method of Agile AI-driven Development",
|
||||
"keywords": [
|
||||
"agile",
|
||||
|
|
@ -40,7 +40,8 @@
|
|||
"lint:md": "markdownlint-cli2 \"**/*.md\"",
|
||||
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
|
||||
"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:install": "node test/test-installation-components.js",
|
||||
"test:refs": "node test/test-file-refs-csv.js",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ code: core
|
|||
name: "BMad Core Module"
|
||||
|
||||
header: "BMad Core Configuration"
|
||||
subheader: "Configure the core settings for your BMad installation.\nThese settings will be used across all modules and agents."
|
||||
subheader: "Configure the core settings for your BMad installation.\nThese settings will be used across all installed bmad skills, workflows, and agents."
|
||||
|
||||
user_name:
|
||||
prompt: "What should agents call you? (Use your name or a team name)"
|
||||
|
|
@ -23,3 +23,13 @@ output_folder:
|
|||
prompt: "Where should output files be saved?"
|
||||
default: "_bmad-output"
|
||||
result: "{project-root}/{value}"
|
||||
|
||||
tool_supports_subagents:
|
||||
prompt: "Subagents are supported by the LLM or Tool I will be using?"
|
||||
default: true
|
||||
result: "{value}"
|
||||
|
||||
tool_supports_agent_teams:
|
||||
prompt: "Agent Teams are supported by the LLM or Tool I will be using?"
|
||||
default: false
|
||||
result: "{value}"
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ No extra text, no explanations, no markdown wrapping.</output-format>
|
|||
</flow>
|
||||
|
||||
<halt-conditions>
|
||||
<condition>HALT if zero findings - this is suspicious, re-analyze or ask for guidance</condition>
|
||||
<condition>HALT if content is empty or unreadable</condition>
|
||||
</halt-conditions>
|
||||
|
||||
|
|
|
|||
|
|
@ -29,23 +29,30 @@ Initialize the brainstorming workflow by detecting continuation state and settin
|
|||
|
||||
## INITIALIZATION SEQUENCE:
|
||||
|
||||
### 1. Check for Existing Workflow
|
||||
### 1. Check for Existing Sessions
|
||||
|
||||
First, check if the output document already exists:
|
||||
First, check the brainstorming sessions folder for existing sessions:
|
||||
|
||||
- Look for file at `{output_folder}/brainstorming/brainstorming-session-{{date}}.md`
|
||||
- If exists, read the complete file including frontmatter
|
||||
- If not exists, this is a fresh workflow
|
||||
- List all files in `{output_folder}/brainstorming/`
|
||||
- **DO NOT read any file contents** - only list filenames
|
||||
- If files exist, identify the most recent by date/time in the filename
|
||||
- If no files exist, this is a fresh workflow
|
||||
|
||||
### 2. Handle Continuation (If Document Exists)
|
||||
### 2. Handle Existing Sessions (If Files Found)
|
||||
|
||||
If the document exists and has frontmatter with `stepsCompleted`:
|
||||
If existing session files are found:
|
||||
|
||||
- **STOP here** and load `./step-01b-continue.md` immediately
|
||||
- Do not proceed with any initialization tasks
|
||||
- Let step-01b handle the continuation logic
|
||||
- Display the most recent session filename (do NOT read its content)
|
||||
- Ask the user: "Found existing session: `[filename]`. Would you like to:
|
||||
**[1]** Continue this session
|
||||
**[2]** Start a new session
|
||||
**[3]** See all existing sessions"
|
||||
|
||||
### 3. Fresh Workflow Setup (If No Document)
|
||||
- If user selects **[1]** (continue): Set `{brainstorming_session_output_file}` to that file path and load `./step-01b-continue.md`
|
||||
- If user selects **[2]** (new): Generate new filename with current date/time and proceed to step 3
|
||||
- If user selects **[3]** (see all): List all session filenames and ask which to continue or if new
|
||||
|
||||
### 3. Fresh Workflow Setup (If No Files or User Chooses New)
|
||||
|
||||
If no document exists or no `stepsCompleted` in frontmatter:
|
||||
|
||||
|
|
@ -55,10 +62,10 @@ Create the brainstorming session document:
|
|||
|
||||
```bash
|
||||
# Create directory if needed
|
||||
mkdir -p "$(dirname "{output_folder}/brainstorming/brainstorming-session-{{date}}.md")"
|
||||
mkdir -p "$(dirname "{brainstorming_session_output_file}")"
|
||||
|
||||
# Initialize from template
|
||||
cp "{template_path}" "{output_folder}/brainstorming/brainstorming-session-{{date}}.md"
|
||||
cp "{template_path}" "{brainstorming_session_output_file}"
|
||||
```
|
||||
|
||||
#### B. Context File Check and Loading
|
||||
|
|
@ -134,7 +141,7 @@ _[Content based on conversation about session parameters and facilitator approac
|
|||
|
||||
## APPEND TO DOCUMENT:
|
||||
|
||||
When user selects approach, append the session overview content directly to `{output_folder}/brainstorming/brainstorming-session-{{date}}.md` using the structure from above.
|
||||
When user selects approach, append the session overview content directly to `{brainstorming_session_output_file}` using the structure from above.
|
||||
|
||||
### E. Continue to Technique Selection
|
||||
|
||||
|
|
@ -152,7 +159,7 @@ Which approach appeals to you most? (Enter 1-4)"
|
|||
|
||||
#### When user selects approach number:
|
||||
|
||||
- **Append initial session overview to `{output_folder}/brainstorming/brainstorming-session-{{date}}.md`**
|
||||
- **Append initial session overview to `{brainstorming_session_output_file}`**
|
||||
- **Update frontmatter:** `stepsCompleted: [1]`, `selected_approach: '[selected approach]'`
|
||||
- **Load the appropriate step-02 file** based on selection
|
||||
|
||||
|
|
@ -167,7 +174,9 @@ After user selects approach number:
|
|||
|
||||
## SUCCESS METRICS:
|
||||
|
||||
✅ Existing workflow detected and continuation handled properly
|
||||
✅ Existing sessions detected without reading file contents
|
||||
✅ User prompted to continue existing session or start new
|
||||
✅ Correct session file selected for continuation
|
||||
✅ Fresh workflow initialized with correct document structure
|
||||
✅ Session context gathered and understood clearly
|
||||
✅ User's approach selection captured and routed correctly
|
||||
|
|
@ -176,7 +185,9 @@ After user selects approach number:
|
|||
|
||||
## FAILURE MODES:
|
||||
|
||||
❌ Not checking for existing document before creating new one
|
||||
❌ Reading file contents during session detection (wastes context)
|
||||
❌ Not asking user before continuing existing session
|
||||
❌ Not properly routing user's continue/new session selection
|
||||
❌ Missing continuation detection leading to duplicate work
|
||||
❌ Insufficient session context gathering
|
||||
❌ Not properly routing user's approach selection
|
||||
|
|
@ -184,7 +195,9 @@ After user selects approach number:
|
|||
|
||||
## SESSION SETUP PROTOCOLS:
|
||||
|
||||
- Always verify document existence before initialization
|
||||
- Always list sessions folder WITHOUT reading file contents
|
||||
- Ask user before continuing any existing session
|
||||
- Only load continue step after user confirms
|
||||
- Load brain techniques CSV only when needed for technique presentation
|
||||
- Use collaborative facilitation language throughout
|
||||
- Maintain psychological safety for creative exploration
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ Load existing document and analyze current state:
|
|||
|
||||
**Document Analysis:**
|
||||
|
||||
- Read existing `{output_folder}/brainstorming/brainstorming-session-{{date}}.md`
|
||||
- Read existing `{brainstorming_session_output_file}`
|
||||
- Examine frontmatter for `stepsCompleted`, `session_topic`, `session_goals`
|
||||
- Review content to understand session progress and outcomes
|
||||
- Identify current stage and next logical steps
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ After final technique element:
|
|||
|
||||
#### If 'C' (Move to organization):
|
||||
|
||||
- **Append the technique execution content to `{output_folder}/brainstorming/brainstorming-session-{{date}}.md`**
|
||||
- **Append the technique execution content to `{brainstorming_session_output_file}`**
|
||||
- **Update frontmatter:** `stepsCompleted: [1, 2, 3]`
|
||||
- **Load:** `./step-04-idea-organization.md`
|
||||
|
||||
|
|
@ -356,7 +356,7 @@ _[Short narrative describing the user and AI collaboration journey - what made t
|
|||
|
||||
## APPEND TO DOCUMENT:
|
||||
|
||||
When user selects 'C', append the content directly to `{output_folder}/brainstorming/brainstorming-session-{{date}}.md` using the structure from above.
|
||||
When user selects 'C', append the content directly to `{brainstorming_session_output_file}` using the structure from above.
|
||||
|
||||
## SUCCESS METRICS:
|
||||
|
||||
|
|
|
|||
|
|
@ -253,14 +253,14 @@ Provide final session wrap-up and forward guidance:
|
|||
|
||||
#### If [C] Complete:
|
||||
|
||||
- **Append the final session content to `{output_folder}/brainstorming/brainstorming-session-{{date}}.md`**
|
||||
- **Append the final session content to `{brainstorming_session_output_file}`**
|
||||
- Update frontmatter: `stepsCompleted: [1, 2, 3, 4]`
|
||||
- Set `session_active: false` and `workflow_completed: true`
|
||||
- Complete workflow with positive closure message
|
||||
|
||||
## APPEND TO DOCUMENT:
|
||||
|
||||
When user selects 'C', append the content directly to `{output_folder}/brainstorming/brainstorming-session-{{date}}.md` using the structure from step 7.
|
||||
When user selects 'C', append the content directly to `{brainstorming_session_output_file}` using the structure from step 7.
|
||||
|
||||
## SUCCESS METRICS:
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ Load config from `{project-root}/_bmad/core/config.yaml` and resolve:
|
|||
- `installed_path` = `{project-root}/_bmad/core/workflows/brainstorming`
|
||||
- `template_path` = `{installed_path}/template.md`
|
||||
- `brain_techniques_path` = `{installed_path}/brain-methods.csv`
|
||||
- `default_output_file` = `{output_folder}/brainstorming/brainstorming-session-{{date}}.md`
|
||||
- `brainstorming_session_output_file` = `{output_folder}/brainstorming/brainstorming-session-{{date}}-{{time}}.md` (evaluated once at workflow start)
|
||||
|
||||
All steps MUST reference `{brainstorming_session_output_file}` instead of the full path pattern.
|
||||
- `context_file` = Optional context file path from workflow invocation for project-specific guidance
|
||||
- `advancedElicitationTask` = `{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.xml`
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
@ -247,9 +247,9 @@ You must fully embody this agent's persona and follow all activation instruction
|
|||
*/
|
||||
createWorkflowPromptContent(entry, workflowFile, toolsStr) {
|
||||
const description = this.escapeYamlSingleQuote(this.createPromptDescription(entry.name));
|
||||
// bmm/config.yaml is safe to hardcode here: these prompts are only generated when
|
||||
// bmad-help.csv exists (bmm module data), so bmm is guaranteed to be installed.
|
||||
const configLine = `1. Load {project-root}/${this.bmadFolderName}/bmm/config.yaml and store ALL fields as session variables`;
|
||||
// Use the module from the bmad-help.csv entry to reference the correct config.yaml
|
||||
const configModule = entry.module || 'core';
|
||||
const configLine = `1. Load {project-root}/${this.bmadFolderName}/${configModule}/config.yaml and store ALL fields as session variables`;
|
||||
|
||||
let body;
|
||||
if (workflowFile.endsWith('.yaml')) {
|
||||
|
|
@ -324,11 +324,13 @@ ${body}
|
|||
|
||||
/**
|
||||
* 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
|
||||
* @returns {Object|null} { fileName, content } or null if not a tech-writer command
|
||||
*/
|
||||
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 = {
|
||||
'Write Document': { code: 'WD', file: 'bmad-bmm-write-document', description: 'Write document' },
|
||||
|
|
@ -344,14 +346,16 @@ ${body}
|
|||
const safeDescription = this.escapeYamlSingleQuote(cmd.description);
|
||||
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 = `---
|
||||
description: '${safeDescription}'
|
||||
agent: 'agent'
|
||||
tools: ${toolsStr}
|
||||
---
|
||||
|
||||
1. Load {project-root}/${this.bmadFolderName}/bmm/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
|
||||
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}/${configModule}/agents/tech-writer/tech-writer.md and activate the Paige (Technical Writer) persona
|
||||
3. Execute the ${entry.name} menu command (${cmd.code})
|
||||
`;
|
||||
|
||||
|
|
@ -376,15 +380,15 @@ tools: ${toolsStr}
|
|||
const agentPath = artifact.agentPath || artifact.relativePath;
|
||||
const agentFilePath = `{project-root}/${this.bmadFolderName}/${agentPath}`;
|
||||
|
||||
// bmm/config.yaml is safe to hardcode: agent activators are only generated from
|
||||
// bmm agent artifacts, so bmm is guaranteed to be installed.
|
||||
// Use the agent's module to reference the correct config.yaml
|
||||
const configModule = artifact.module || 'core';
|
||||
return `---
|
||||
description: '${safeDescription}'
|
||||
agent: 'agent'
|
||||
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}
|
||||
3. Follow ALL activation instructions in the agent file
|
||||
4. Display the welcome/greeting as instructed
|
||||
|
|
@ -400,7 +404,13 @@ tools: ${toolsStr}
|
|||
* @param {Map} agentManifest - Agent manifest data
|
||||
*/
|
||||
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
|
||||
let agentsTable = '| Agent | Persona | Title | Capabilities |\n|---|---|---|---|\n';
|
||||
|
|
@ -427,6 +437,36 @@ tools: ${toolsStr}
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
## Project Configuration
|
||||
|
|
@ -443,12 +483,12 @@ tools: ${toolsStr}
|
|||
|
||||
## BMAD Runtime Structure
|
||||
|
||||
- **Agent definitions**: \`${bmad}/bmm/agents/\` (BMM module) and \`${bmad}/core/agents/\` (core)
|
||||
- **Workflow definitions**: \`${bmad}/bmm/workflows/\` (organized by phase)
|
||||
${agentDefsLine}
|
||||
${workflowDefsLine}
|
||||
- **Core tasks**: \`${bmad}/core/tasks/\` (help, editorial review, indexing, sharding, adversarial review)
|
||||
- **Core workflows**: \`${bmad}/core/workflows/\` (brainstorming, party-mode, advanced-elicitation)
|
||||
- **Workflow engine**: \`${bmad}/core/tasks/workflow.xml\` (executes YAML-based workflows)
|
||||
- **Module configuration**: \`${bmad}/bmm/config.yaml\`
|
||||
${moduleConfigLine}
|
||||
- **Core configuration**: \`${bmad}/core/config.yaml\`
|
||||
- **Agent manifest**: \`${bmad}/_config/agent-manifest.csv\`
|
||||
- **Workflow manifest**: \`${bmad}/_config/workflow-manifest.csv\`
|
||||
|
|
@ -457,7 +497,7 @@ tools: ${toolsStr}
|
|||
|
||||
## 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}\`
|
||||
- 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
|
||||
|
|
@ -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
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {string[]} installedModules - List of installed modules to check for config
|
||||
* @returns {Object} Config variables
|
||||
*/
|
||||
async loadModuleConfig(bmadDir) {
|
||||
const bmmConfigPath = path.join(bmadDir, 'bmm', 'config.yaml');
|
||||
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
|
||||
async loadModuleConfig(bmadDir, installedModules = ['core']) {
|
||||
// Build config paths from installed modules (non-core first, then core as fallback)
|
||||
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)) {
|
||||
try {
|
||||
const content = await fs.readFile(configPath, 'utf8');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ description: '{{description}}'
|
|||
You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
|
||||
|
||||
<agent-activation CRITICAL="TRUE">
|
||||
1. LOAD the FULL agent file from @_bmad/{{module}}/agents/{{path}}
|
||||
1. LOAD the FULL agent file from {project-root}/_bmad/{{module}}/agents/{{path}}
|
||||
2. READ its entire contents - this contains the complete agent persona, menu, and instructions
|
||||
3. Execute ALL activation steps exactly as written in the agent file
|
||||
4. Follow the agent's persona and menu system precisely
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ description: '{{description}}'
|
|||
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
|
||||
|
||||
<steps CRITICAL="TRUE">
|
||||
1. Always LOAD the FULL @{project-root}/{{bmadFolderName}}/core/tasks/workflow.xml
|
||||
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config @{project-root}/{{bmadFolderName}}/{{path}}
|
||||
3. Pass the yaml path @{project-root}/{{bmadFolderName}}/{{path}} as 'workflow-config' parameter to the workflow.xml instructions
|
||||
1. Always LOAD the FULL {project-root}/{{bmadFolderName}}/core/tasks/workflow.xml
|
||||
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config {project-root}/{{bmadFolderName}}/{{path}}
|
||||
3. Pass the yaml path {project-root}/{{bmadFolderName}}/{{path}} as 'workflow-config' parameter to the workflow.xml instructions
|
||||
4. Follow workflow.xml instructions EXACTLY as written to process and follow the specific workflow config and its instructions
|
||||
5. Save outputs after EACH section when generating any documents from templates
|
||||
</steps>
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ name: '{{name}}'
|
|||
description: '{{description}}'
|
||||
---
|
||||
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @{project-root}/{{bmadFolderName}}/{{path}}, READ its entire contents and follow its directions exactly!
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL {project-root}/{{bmadFolderName}}/{{path}}, READ its entire contents and follow its directions exactly!
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ description: '{{description}}'
|
|||
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
|
||||
|
||||
<steps CRITICAL="TRUE">
|
||||
1. Always LOAD the FULL @_bmad/core/tasks/workflow.xml
|
||||
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config @{{workflow_path}}
|
||||
1. Always LOAD the FULL {project-root}/_bmad/core/tasks/workflow.xml
|
||||
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config {project-root}/{{workflow_path}}
|
||||
3. Pass the yaml path {{workflow_path}} as 'workflow-config' parameter to the workflow.xml instructions
|
||||
4. Follow workflow.xml instructions EXACTLY as written to process and follow the specific workflow config and its instructions
|
||||
5. Save outputs after EACH section when generating any documents from templates
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
description: '{{description}}'
|
||||
---
|
||||
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @{{workflow_path}}, READ its entire contents and follow its directions exactly!
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL {project-root}/{{workflow_path}}, READ its entire contents and follow its directions exactly!
|
||||
|
|
|
|||
Loading…
Reference in New Issue