Compare commits

...

7 Commits

Author SHA1 Message Date
Markus Ende 1d199b6def
Merge 147144a1ec into b83ccc71b9 2026-02-23 19:05:49 -06:00
Brian Madison b83ccc71b9 docs(changelog): add 6.0.3 release notes 2026-02-23 17:12:12 -06:00
Alex Verkhovsky d43663e3af
fix(workflows): remove ambiguous with-argument from help task chaining (#1740)
* fix(workflows): remove ambiguous with-argument from help task chaining

The "with argument" clause in 7 workflow completion steps caused LLMs
to interpret "Read fully and follow: help.md with argument X" as a
skill/function invocation rather than a file-read instruction. Drop the
clause entirely — help.md already infers the completed workflow from
the preceding "[Workflow] complete." text in conversation context.

Closes #1637

* fix(workflows): correct broken qa/automate file references

The QA workflow was renamed to qa-generate-e2e-tests but three files
still referenced the old qa/automate path, breaking CI file-ref
validation.

* fix(test): update QA agent test to match renamed workflow path

The workflow path changed from qa/automate to qa-generate-e2e-tests
but the installation component test was not updated to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Brian <bmadcode@gmail.com>
2026-02-23 15:48:52 -06:00
Brian Madison 0d858cbc5b standardize all workflow descriptions to use proper quotes to not break command or skilll front matter 2026-02-23 15:31:03 -06: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
46 changed files with 351 additions and 62 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## [6.0.3]
### 🐛 Bug Fixes
* Fix workflow descriptions to use proper quotes so they format better in skill conversion and don't break yaml front matter
---
## [6.0.2]
### 🎁 Features

View File

@ -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",

View File

@ -29,7 +29,7 @@ agent:
menu:
- trigger: QA or fuzzy match on qa-automate
workflow: "{project-root}/_bmad/bmm/workflows/qa/automate/workflow.yaml"
workflow: "{project-root}/_bmad/bmm/workflows/qa-generate-e2e-tests/workflow.yaml"
description: "[QA] Automate - Generate tests for existing features (simplified)"
prompts:

View File

@ -27,5 +27,5 @@ bmm,4-implementation,Validate Story,VS,35,_bmad/bmm/workflows/4-implementation/c
bmm,4-implementation,Create Story,CS,30,_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml,bmad-bmm-create-story,true,sm,Create Mode,"Story cycle start: Prepare first found story in the sprint plan that is next, or if the command is run with a specific epic and story designation with context. Once complete, then VS then DS then CR then back to DS if needed or next CS or ER",implementation_artifacts,story,
bmm,4-implementation,Dev Story,DS,40,_bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml,bmad-bmm-dev-story,true,dev,Create Mode,"Story cycle: Execute story implementation tasks and tests then CR then back to DS if fixes needed",,,
bmm,4-implementation,Code Review,CR,50,_bmad/bmm/workflows/4-implementation/code-review/workflow.yaml,bmad-bmm-code-review,false,dev,Create Mode,"Story cycle: If issues back to DS if approved then next CS or ER if epic complete",,,
bmm,4-implementation,QA Automation Test,QA,45,_bmad/bmm/workflows/qa/automate/workflow.yaml,bmad-bmm-qa-automate,false,qa,Create Mode,"Generate automated API and E2E tests for implemented code using the project's existing test framework (detects existing well known in use test frameworks). Use after implementation to add test coverage. NOT for code review or story validation - use CR for that.",implementation_artifacts,"test suite",
bmm,4-implementation,QA Automation Test,QA,45,_bmad/bmm/workflows/qa-generate-e2e-tests/workflow.yaml,bmad-bmm-qa-automate,false,qa,Create Mode,"Generate automated API and E2E tests for implemented code using the project's existing test framework (detects existing well known in use test frameworks). Use after implementation to add test coverage. NOT for code review or story validation - use CR for that.",implementation_artifacts,"test suite",
bmm,4-implementation,Retrospective,ER,60,_bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml,bmad-bmm-retrospective,false,sm,Create Mode,"Optional at epic end: Review completed work lessons learned and next epic or if major issues consider CC",implementation_artifacts,retrospective,

1 module phase name code sequence workflow-file command required agent options description output-location outputs
27 bmm 4-implementation Create Story CS 30 _bmad/bmm/workflows/4-implementation/create-story/workflow.yaml bmad-bmm-create-story true sm Create Mode Story cycle start: Prepare first found story in the sprint plan that is next, or if the command is run with a specific epic and story designation with context. Once complete, then VS then DS then CR then back to DS if needed or next CS or ER implementation_artifacts story
28 bmm 4-implementation Dev Story DS 40 _bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml bmad-bmm-dev-story true dev Create Mode Story cycle: Execute story implementation tasks and tests then CR then back to DS if fixes needed
29 bmm 4-implementation Code Review CR 50 _bmad/bmm/workflows/4-implementation/code-review/workflow.yaml bmad-bmm-code-review false dev Create Mode Story cycle: If issues back to DS if approved then next CS or ER if epic complete
30 bmm 4-implementation QA Automation Test QA 45 _bmad/bmm/workflows/qa/automate/workflow.yaml _bmad/bmm/workflows/qa-generate-e2e-tests/workflow.yaml bmad-bmm-qa-automate false qa Create Mode Generate automated API and E2E tests for implemented code using the project's existing test framework (detects existing well known in use test frameworks). Use after implementation to add test coverage. NOT for code review or story validation - use CR for that. implementation_artifacts test suite
31 bmm 4-implementation Retrospective ER 60 _bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml bmad-bmm-retrospective false sm Create Mode Optional at epic end: Review completed work lessons learned and next epic or if major issues consider CC implementation_artifacts retrospective

View File

@ -128,7 +128,7 @@ Recap that the brief captures everything needed to guide subsequent product deve
### 5. Suggest next steps
Product Brief complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `Validate PRD`.
Product Brief complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
---

View File

@ -1,6 +1,6 @@
---
name: create-product-brief
description: Create product brief through collaborative discovery. Use when the user says 'lets create a product brief' or 'help me create a project brief'
description: 'Create product brief through collaborative discovery. Use when the user says "lets create a product brief" or "help me create a project brief"'
---
# Product Brief Workflow

View File

@ -1,6 +1,6 @@
---
name: domain-research
description: Conduct domain and industry research. Use when the user says 'lets create a research report on [domain or industry]
description: 'Conduct domain and industry research. Use when the user says "lets create a research report on [domain or industry]"'
---
# Domain Research Workflow

View File

@ -1,6 +1,6 @@
---
name: market-research
description: Conduct market research on competition and customers. Use when the user says 'create a market research report about [business idea]'.
description: 'Conduct market research on competition and customers. Use when the user says "create a market research report about [business idea]".'
---
# Market Research Workflow

View File

@ -1,6 +1,6 @@
---
name: technical-research
description: Conduct technical research on technologies and architecture. Use when the user says 'create a technical research report on [topic]'.
description: 'Conduct technical research on technologies and architecture. Use when the user says "create a technical research report on [topic]".'
---
# Technical Research Workflow

View File

@ -87,7 +87,7 @@ Offer validation workflows to ensure PRD is ready for implementation:
### 4. Suggest Next Workflows
PRD complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `Create PRD`.
PRD complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
### 5. Final Completion Confirmation

View File

@ -197,7 +197,7 @@ Display:
- **IF X (Exit):**
- Display: "**Validation Report Saved:** {validationReportPath}"
- Display: "**Summary:** {overall status} - {recommendation}"
- PRD Validation complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `Validate PRD`.
- PRD Validation complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
- **IF Any other:** Help user, then redisplay menu

View File

@ -1,6 +1,6 @@
---
name: create-prd
description: Create a PRD from scratch. Use when the user says 'lets create a product requirements document' or 'I want to create a new PRD'
description: 'Create a PRD from scratch. Use when the user says "lets create a product requirements document" or "I want to create a new PRD"'
main_config: '{project-root}/_bmad/bmm/config.yaml'
nextStep: './steps-c/step-01-init.md'
---

View File

@ -1,6 +1,6 @@
---
name: edit-prd
description: Edit an existing PRD. Use when the user says 'edit this PRD'.
description: 'Edit an existing PRD. Use when the user says "edit this PRD".'
main_config: '{project-root}/_bmad/bmm/config.yaml'
editWorkflow: './steps-e/step-e-01-discovery.md'
---

View File

@ -1,6 +1,6 @@
---
name: validate-prd
description: Validate a PRD against standards. Use when the user says 'validate this PRD' or 'run PRD validation'
description: 'Validate a PRD against standards. Use when the user says "validate this PRD" or "run PRD validation"'
main_config: '{project-root}/_bmad/bmm/config.yaml'
validateWorkflow: './steps-v/step-v-01-discovery.md'
---

View File

@ -82,7 +82,7 @@ Update the main workflow status file:
### 3. Suggest Next Steps
UX Design complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `Create UX`.
UX Design complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
### 5. Final Completion Confirmation

View File

@ -1,6 +1,6 @@
---
name: create-ux-design
description: Plan UX patterns and design specifications. Use when the user says 'lets create UX design' or 'create UX specifications' or 'help me plan the UX'
description: 'Plan UX patterns and design specifications. Use when the user says "lets create UX design" or "create UX specifications" or "help me plan the UX"'
---
# Create UX Design Workflow

View File

@ -109,7 +109,7 @@ The assessment found [number] issues requiring attention. Review the detailed re
The implementation readiness workflow is now complete. The report contains all findings and recommendations for the user to consider.
Implementation Readiness complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `implementation readiness`.
Implementation Readiness complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
---

View File

@ -1,6 +1,6 @@
---
name: check-implementation-readiness
description: Validate PRD, UX, Architecture and Epics specs are complete. Use when the user says 'check implementation readiness'.
description: 'Validate PRD, UX, Architecture and Epics specs are complete. Use when the user says "check implementation readiness".'
---
# Implementation Readiness

View File

@ -41,7 +41,7 @@ completedAt: '{{current_date}}'
### 3. Next Steps Guidance
Architecture complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `Create Architecture`.
Architecture complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
Upon Completion of task output: offer to answer any questions about the Architecture Document.

View File

@ -1,6 +1,6 @@
---
name: create-architecture
description: Create architecture solution design decisions for AI agent consistency. Use when the user says 'lets create architecture' or 'create technical architecture' or 'create a solution design'
description: 'Create architecture solution design decisions for AI agent consistency. Use when the user says "lets create architecture" or "create technical architecture" or "create a solution design"'
---
# Architecture Workflow

View File

@ -144,6 +144,6 @@ If all validations pass:
When C is selected, the workflow is complete and the epics.md is ready for development.
Epics and Stories complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md` with argument `Create Epics and Stories`.
Epics and Stories complete. Read fully and follow: `{project-root}/_bmad/core/tasks/help.md`
Upon Completion of task output: offer to answer any questions about the Epics and Stories.

View File

@ -1,6 +1,6 @@
---
name: create-epics-and-stories
description: Break requirements into epics and user stories. Use when the user says 'create the epics and stories list'
description: 'Break requirements into epics and user stories. Use when the user says "create the epics and stories list"'
---
# Create Epics and Stories

View File

@ -1,6 +1,6 @@
# Review Story Workflow
name: code-review
description: "Perform adversarial code review finding specific issues. Use when the user says 'run code review' or 'review this code'"
description: 'Perform adversarial code review finding specific issues. Use when the user says "run code review" or "review this code"'
# Critical variables from config
config_source: "{project-root}/_bmad/bmm/config.yaml"

View File

@ -1,6 +1,6 @@
# Correct Course - Sprint Change Management Workflow
name: "correct-course"
description: "Manage significant changes during sprint execution. Use when the user says 'correct course' or 'propose sprint change'"
description: 'Manage significant changes during sprint execution. Use when the user says "correct course" or "propose sprint change"'
config_source: "{project-root}/_bmad/bmm/config.yaml"
user_name: "{config_source}:user_name"

View File

@ -1,5 +1,5 @@
name: create-story
description: "Creates a dedicated story file with all the context the agent will need to implement it later. Use when the user says 'create the next story' or 'create story [story identifier]'"
description: 'Creates a dedicated story file with all the context the agent will need to implement it later. Use when the user says "create the next story" or "create story [story identifier]"'
# Critical variables from config
config_source: "{project-root}/_bmad/bmm/config.yaml"

View File

@ -1,5 +1,5 @@
name: dev-story
description: "Execute story implementation following a context filled story spec file. Use when the user says 'dev this story [story file]' or 'implement the next story in the sprint plan'"
description: 'Execute story implementation following a context filled story spec file. Use when the user says "dev this story [story file]" or "implement the next story in the sprint plan"'
# Critical variables from config
config_source: "{project-root}/_bmad/bmm/config.yaml"

View File

@ -1,6 +1,6 @@
# Retrospective - Epic Completion Review Workflow
name: "retrospective"
description: "Post-epic review to extract lessons and assess success. Use when the user says 'run a retrospective' or 'lets retro the epic [epic]'"
description: 'Post-epic review to extract lessons and assess success. Use when the user says "run a retrospective" or "lets retro the epic [epic]"'
config_source: "{project-root}/_bmad/bmm/config.yaml"
user_name: "{config_source}:user_name"

View File

@ -1,5 +1,5 @@
name: sprint-planning
description: "Generate sprint status tracking from epics. Use when the user says 'run sprint planning' or 'generate sprint plan'"
description: 'Generate sprint status tracking from epics. Use when the user says "run sprint planning" or "generate sprint plan"'
author: "BMad"
# Critical variables from config

View File

@ -1,6 +1,6 @@
# Sprint Status - Implementation Tracker
name: sprint-status
description: "Summarize sprint status and surface risks. Use when the user says 'check sprint status' or 'show sprint status'"
description: 'Summarize sprint status and surface risks. Use when the user says "check sprint status" or "show sprint status"'
author: "BMad"
# Critical variables from config

View File

@ -1,6 +1,6 @@
---
name: quick-dev
description: "Implement a Quick Tech Spec for small changes or features. Use when the user provides a quick tech spec and says 'implement this quick spec' or 'proceed with implementation of [quick tech spec]'"
description: 'Implement a Quick Tech Spec for small changes or features. Use when the user provides a quick tech spec and says "implement this quick spec" or "proceed with implementation of [quick tech spec]"'
---
# Quick Dev Workflow

View File

@ -1,6 +1,6 @@
---
name: quick-spec
description: Very quick process to create implementation-ready quick specs for small changes or features. Use when the user says 'create a quick spec' or 'generate a quick tech spec'
description: 'Very quick process to create implementation-ready quick specs for small changes or features. Use when the user says "create a quick spec" or "generate a quick tech spec"'
main_config: '{project-root}/_bmad/bmm/config.yaml'
# Checkpoint handler paths

View File

@ -1,7 +1,7 @@
# Document Project Workflow Configuration
name: "document-project"
version: "1.2.0"
description: "Document brownfield projects for AI context. Use when the user says 'document this project' or 'generate project docs'"
description: 'Document brownfield projects for AI context. Use when the user says "document this project" or "generate project docs"'
author: "BMad"
# Critical variables

View File

@ -1,6 +1,6 @@
---
name: generate-project-context
description: Create project-context.md with AI rules. Use when the user says 'generate project context' or 'create project context'
description: 'Create project-context.md with AI rules. Use when the user says "generate project context" or "create project context"'
---
# Generate Project Context Workflow

View File

@ -1,5 +1,5 @@
name: qa-generate-e2e-tests
description: "Generate end to end automated tests for existing features. Use when the user says 'create qa automated tests for [feature]'"
description: 'Generate end to end automated tests for existing features. Use when the user says "create qa automated tests for [feature]"'
# Critical variables from config
config_source: "{project-root}/_bmad/bmm/config.yaml"
@ -10,7 +10,7 @@ document_output_language: "{config_source}:document_output_language"
date: system-generated
# Workflow components
installed_path: "{project-root}/_bmad/bmm/workflows/qa/automate"
installed_path: "{project-root}/_bmad/bmm/workflows/qa-generate-e2e-tests"
instructions: "{installed_path}/instructions.md"
validation: "{installed_path}/checklist.md"
template: false

View File

@ -1,6 +1,6 @@
<task id="_bmad/core/tasks/editorial-review-prose.xml"
name="Editorial Review - Prose"
description="Clinical copy-editor that reviews text for communication issues. Use when user says 'review for prose' or 'improve the prose'">
description="Clinical copy-editor that reviews text for communication issues. Use when user says review for prose or improve the prose">
<objective>Review text for communication issues that impede comprehension and output suggested fixes in a three-column table</objective>

View File

@ -3,7 +3,7 @@
but no context except the content to review -->
<task id="_bmad/core/tasks/editorial-review-structure.xml"
name="Editorial Review - Structure"
description="Structural editor that proposes cuts, reorganization, and simplification while preserving comprehension. Use when user requests 'structural review' or 'cohesive reivew' or 'editorial review of structure'.">
description="Structural editor that proposes cuts, reorganization, and simplification while preserving comprehension. Use when user requests structural review or editorial review of structure">
<objective>Review document structure and propose substantive changes
to improve clarity and flow-run this BEFORE copy editing</objective>
<inputs>

View File

@ -1,6 +1,6 @@
---
name: help
description: "Analyzes what is done and the users query and offers advice on what to do next. Use if user says 'bmad-help what should I do next' or 'bmad-help what do I do now'"
description: "Analyzes what is done and the users query and offers advice on what to do next. Use if user says what should I do next or what do I do now"
---
# Task: BMAD Help

View File

@ -1,5 +1,5 @@
<task id="_bmad/core/tasks/index-docs" name="Index Docs"
description="Generates or updates an index.md to reference all docs in the folder. Use if user requests to 'create an index of all files [here]' or 'reindex the folder [here].">
description="Generates or updates an index.md to reference all docs in the folder. Use if user requests to create or update an index of all files in a specific folder">
<llm critical="true">
<i>MANDATORY: Execute ALL steps in the flow section IN EXACT ORDER</i>
<i>DO NOT skip steps or change the sequence</i>

View File

@ -2,7 +2,7 @@
but no context except the content to review -->
<task id="_bmad/core/tasks/review-adversarial-general.xml" name="Adversarial Review (General)"
description="Perform a Cynical Review and produce a findings report. Use when the user requests a 'critical review of' or 'cynical review of' something.">
description="Perform a Cynical Review and produce a findings report. Use when the user requests a critical review of something">
<objective>Cynically review content and produce findings</objective>
<inputs>

View File

@ -1,5 +1,5 @@
<task id="_bmad/core/tasks/shard-doc" name="Shard Document"
description="Splits large markdown documents into smaller, organized files based on level 2 (default) sections. Use if the user says 'Shard Document [document]'">
description="Splits large markdown documents into smaller, organized files based on level 2 (default) sections. Use if the user says perform shard document">
<objective>Split large markdown documents into smaller, organized files based on level 2 sections using @kayvan/markdown-tree-parser tool</objective>
<llm critical="true">

View File

@ -1,5 +1,5 @@
<task id="_bmad/core/workflows/advanced-elicitation/workflow.xml" name="Advanced Elicitation"
description="Push the LLM to reconsider refine and improve its recent output. Use when the user asks for 'advanced elicitation'"
description="Push the LLM to reconsider refine and improve its recent output. Use when the user asks for advanced elicitation"
methods="{project-root}/_bmad/core/workflows/advanced-elicitation/methods.csv"
agent-party="{project-root}/_bmad/_config/agent-manifest.csv">
<llm critical="true">

View File

@ -1,6 +1,6 @@
---
name: brainstorming
description: "Facilitate interactive brainstorming sessions using diverse creative techniques and ideation methods. Use when the user says 'help me brainstorm' or 'help me ideate'."
description: 'Facilitate interactive brainstorming sessions using diverse creative techniques and ideation methods. Use when the user says "help me brainstorm" or "help me ideate".'
context_file: '' # Optional context file path for project-specific guidance
---

View File

@ -1,6 +1,6 @@
---
name: party-mode
description: "Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations. Use when user requests 'party mode' only."
description: 'Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations. Use when user requests "party mode" only.'
---
# Party Mode Workflow

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

@ -173,7 +173,7 @@ async function runTests() {
assert(compiled.includes('QA Engineer'), 'QA agent compilation includes agent title');
assert(compiled.includes('qa/automate'), 'QA agent menu includes automate workflow');
assert(compiled.includes('qa-generate-e2e-tests'), 'QA agent menu includes automate workflow');
// Cleanup
await fs.remove(tempOutput);

View File

@ -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');