Skip to content

fix: use module-specific config.yaml paths in GitHub Copilot installer#1710

Open
Markus-Ende wants to merge 4 commits intobmad-code-org:mainfrom
qupaya:fix/copilot-hardcoded-bmm-config-path
Open

fix: use module-specific config.yaml paths in GitHub Copilot installer#1710
Markus-Ende wants to merge 4 commits intobmad-code-org:mainfrom
qupaya:fix/copilot-hardcoded-bmm-config-path

Conversation

@Markus-Ende
Copy link

What

Replace hardcoded bmm/config.yaml references with dynamic module-based paths in the GitHub Copilot installer so custom modules load their own config.yaml.

Why

When installing with --custom-content, ALL generated agent prompts incorrectly reference bmm/config.yaml regardless of which module the agent belongs to. This breaks custom module installations that don't include bmm.

Fixes #1708

How

  • 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 instead of hardcoding bmm

Testing

  • Manual integration test: installed with --modules core --custom-content and verified:
    • No hardcoded bmm/config.yaml in generated prompts
    • Core workflows correctly reference core/config.yaml
    • copilot-instructions.md dynamically lists custom module paths
  • npm run validate:schemas passes
  • npm run validate:refs passes (pre-existing broken refs unrelated)
  • npx eslint passes on modified file
  • Other installers already handle this correctly via templates with {{module}}

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 bmad-code-org#1708
@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

📝 Walkthrough

Walkthrough

The PR modifies the GitHub Copilot IDE installer to replace hardcoded bmm/config.yaml paths with module-aware resolution using entry.module or artifact.module (defaulting to 'core'). This enables proper support for custom module installations where agents and workflows reference their own module's configuration.

Changes

Cohort / File(s) Summary
Module-aware path resolution
tools/cli/installers/lib/ide/github-copilot.js
Updated workflow prompts, tech-writer prompts, and agent activator prompts to dynamically reference module-specific config.yaml paths instead of hardcoded bmm paths. Added installed modules computation and dynamic module path strings for agents, workflows, and configurations. Updated BMAD section generation to include non-core module definitions as fallback references.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #1606: Introduced the GitHub Copilot installer handler that is being refactored in this PR to support module-aware path resolution.

Suggested reviewers

  • bmadcode
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main change: using module-specific config.yaml paths instead of hardcoded ones in the GitHub Copilot installer.
Description check ✅ Passed The description comprehensively explains the problem, solution, implementation approach, and testing performed, all directly related to the changeset.
Linked Issues check ✅ Passed The PR implements all required fixes from issue #1708: using artifact.module in createAgentActivatorPromptContent(), entry.module in createWorkflowPromptContent() and createTechWriterPromptContent(), and dynamically listing installed modules in generateCopilotInstructions().
Out of Scope Changes check ✅ Passed All changes are focused on the single file (github-copilot.js) addressing the specific module-path resolution issue, with no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/cli/installers/lib/ide/github-copilot.js (1)

334-340: ⚠️ Potential issue | 🟡 Minor

Hardcoded bmm filenames should align with dynamic module handling.

The file values in techWriterCommands (bmad-bmm-write-document, bmad-bmm-update-standards, etc.) are hardcoded to bmm and used to generate prompt filenames, even though the method receives entry.module and uses it dynamically for config and agent paths within the prompt content. This creates an inconsistency: the generated prompt files will always be named with the bmm segment regardless of which module is being processed, potentially confusing for installations with custom or non-bmm modules.

Either:

  1. Document these as stable identifiers intentionally decoupled from the module, or
  2. Make the filenames dynamic to reflect entry.module (e.g., bmad-${configModule}-write-document)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/cli/installers/lib/ide/github-copilot.js` around lines 334 - 340, The
techWriterCommands object currently hardcodes filenames with the `bmm` segment
which causes generated prompt filenames to be incorrect for other modules;
update the code that constructs those filenames to use the actual module name
(e.g., `entry.module` or the derived `configModule`) instead of the fixed `bmm`.
Specifically, change the `file` values in `techWriterCommands` (or the logic
that consumes them) to generate names like `bmad-${entry.module}-write-document`
(and similar for update-standards, mermaid-generate, validate-document,
explain-concept) so prompt filenames reflect the dynamic module being processed.
🧹 Nitpick comments (5)
tools/cli/installers/lib/ide/github-copilot.js (5)

405-406: generateCopilotInstructions reads config via loadModuleConfig(bmadDir) but doesn't pass options — it can't resolve per-module config.

loadModuleConfig (line 406) receives only bmadDir and has no visibility into which modules are selected. Meanwhile, options (which contains selectedModules) is available at this call site. If loadModuleConfig is updated to be module-aware (per the earlier comment), it will need options or the module list forwarded here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/cli/installers/lib/ide/github-copilot.js` around lines 405 - 406,
generateCopilotInstructions currently calls loadModuleConfig(bmadDir) without
passing options, so per-module config cannot be resolved; update the call in
generateCopilotInstructions to forward the options (or at least
options.selectedModules) to loadModuleConfig so that loadModuleConfig(bmadDir,
options) (or loadModuleConfig(bmadDir, selectedModules)) can use the selected
modules when resolving config—adjust the loadModuleConfig signature and any
callers accordingly to accept and use the extra parameter.

488-494: "Core workflows" line (491) is now redundant when workflowDefsLine already points at core/workflows/.

When no non-core modules are installed, line 458 sets workflowDefsLine to core/workflows/, and line 491 lists core workflows at the same path again. Users will see two bullets referencing the same directory. Consider either removing the static "Core workflows" bullet or adjusting workflowDefsLine to exclude core in the else branch (replacing it with a no-op or omitting it).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/cli/installers/lib/ide/github-copilot.js` around lines 488 - 494, The
static "Core workflows" bullet duplicates the path already output via
workflowDefsLine when no non-core modules are installed; update the logic that
sets workflowDefsLine (the else branch where workflowDefsLine is assigned) to
avoid duplication by setting workflowDefsLine to an empty string or null when it
would otherwise point to `${bmad}/core/workflows/`, and then ensure downstream
rendering skips empty/null workflowDefsLine (or alternately remove the static "-
**Core workflows**" bullet and rely solely on
workflowDefsLine/agentDefsLine/moduleConfigLine); locate the assignment to
workflowDefsLine and the static bullet text near the variables agentDefsLine,
workflowDefsLine, moduleConfigLine and bmad to implement the change.

331-361: createTechWriterPromptContent is fragile — it hardcodes a fixed set of commands and assumes a rigid agent path structure.

The command map (lines 334-340) is a static dictionary that must be manually synchronized with any changes in the tech-writer agent's menu. If a custom module adds or renames tech-writer commands, this function silently skips them (returns null). There's no warning when an unrecognized tech-writer entry is encountered. At minimum, a log/warn for skipped entries would help diagnose silent failures during installation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/cli/installers/lib/ide/github-copilot.js` around lines 331 - 361, The
createTechWriterPromptContent function currently uses a hardcoded
techWriterCommands map and silently returns null when entry.name isn't found;
update it to log a warning before returning null (e.g., emit this.logger.warn or
console.warn including entry.name and configModule) so skipped/unknown
tech-writer commands are visible, and optionally fall back to generating a
generic prompt using entry.name when cmd is missing; modify the block around
const cmd = techWriterCommands[entry.name]; if (!cmd) return null; to emit the
warning (and/or create a generic cmd object) while still using
escapeYamlSingleQuote, getToolsForFile, configModule and this.bmadFolderName
downstream.

251-253: No validation on entry.module / artifact.module values used in path construction.

Module names sourced from CSV (entry.module) and artifacts (artifact.module) are interpolated directly into path strings without any sanitization. While these typically come from controlled sources, a malformed or adversarial CSV entry (e.g., module: ../../etc) would produce an invalid or misleading path in the generated prompt. This pattern repeats at lines 252, 349, and 383.

A lightweight guard (e.g., stripping path separators or validating against a known module list) would harden this.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/cli/installers/lib/ide/github-copilot.js` around lines 251 - 253, The
code interpolates entry.module and artifact.module directly into paths (see
configModule, configLine and other uses) which can produce unsafe/malformed
paths; sanitize or validate these values before use by stripping path separators
and traversal tokens (e.g., remove characters like ../, ./, path.sep) or,
better, validate against a whitelist of known module names and fallback to
'core' if invalid, and apply the same guard to every occurrence that uses
entry.module or artifact.module (including the other spots mentioned) so
generated paths use only safe, verified module names.

446-467: Asymmetric handling of core paths across the three generated lines creates inconsistency and duplication.

Three issues in this block:

  1. agentDefsLine (line 448) includes core as a suffix (and \…/core/agents/` (core)), but workflowDefsLine` (line 456) omits core entirely. Core workflows only appear later on line 491 under a separate "Core workflows" bullet. The two lines should follow the same pattern.

  2. When only core is installed, workflowDefsLine (line 458) references core/workflows/ under "Workflow definitions", and line 491 lists the same path under "Core workflows" — a duplication in the generated markdown.

  3. moduleConfigLine (line 466) says "(no non-core modules installed)" which is odd wording for a user-facing document. Since line 494 already lists core/config.yaml under "Core configuration", this line could simply be omitted when no non-core modules exist.

Suggested approach
     // Build workflow definitions line
     let workflowDefsLine;
     if (nonCoreModules.length > 0) {
-      workflowDefsLine = `- **Workflow definitions**: ${moduleWorkflowPaths} (organized by phase)`;
+      workflowDefsLine = `- **Workflow definitions**: ${moduleWorkflowPaths} and \`${bmad}/core/workflows/\` (core)`;
     } 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)`;
+      moduleConfigLine = '';
     }

Then conditionally include moduleConfigLine in the template only if non-empty.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/cli/installers/lib/ide/github-copilot.js` around lines 446 - 467, The
three generated lines are inconsistent and cause duplication; make them
consistent by: (1) build workflowDefsLine exactly like agentDefsLine — when
nonCoreModules.length > 0 set workflowDefsLine = `- **Workflow definitions**:
${moduleWorkflowPaths} and \`${bmad}/core/workflows/\` (core)`, otherwise set
workflowDefsLine to an empty string (so core-only cases don't duplicate the core
path elsewhere); (2) set moduleConfigLine to `- **Module configuration**:
${moduleConfigPaths}` when nonCoreModules.length > 0 and to an empty string when
there are no non-core modules; and (3) update the template render to include
agentDefsLine, workflowDefsLine, and moduleConfigLine only if they are non-empty
— use the existing symbols agentDefsLine, workflowDefsLine, moduleConfigLine,
nonCoreModules, moduleAgentPaths, moduleWorkflowPaths, moduleConfigPaths, and
bmad to locate and change the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tools/cli/installers/lib/ide/github-copilot.js`:
- Line 470: Remove the dead declaration primaryModule and update related logic:
replace the hardcoded "bmm" in loadModuleConfig with the actual module variable
used when constructing module paths (use the module name passed to
loadModuleConfig), make workflowDefsLine include "core" consistently (not
exclude it when nonCoreModules exists) to match agentDefsLine, change
moduleConfigLine text so it does not misleadingly say "(no non-core modules
installed)" when core config is listed (show accurate wording or list only
non-core when present), and add validation checks for entry.module,
artifact.module, and items of selectedModules before using them to construct
paths (ensure they are non-empty strings and safe identifiers, and handle/skip
invalid names). Ensure references to loadModuleConfig, workflowDefsLine,
agentDefsLine, moduleConfigLine, entry.module, artifact.module, and
selectedModules are updated accordingly.
- Around line 348-357: The tech-writer agent path is hardcoded to bmm which
conflicts with module-agnostic use: update the code that builds the agent path
(currently using configModule and bmadFolderName) and the techWriterCommands
lookup so the tech-writer agent is resolved per-module (e.g., derive the agent
file path from entry.module/configModule or from a module agent
manifest/registry) instead of always using
"bmm/agents/tech-writer/tech-writer.md"; alternatively, if tech-writer is
intentionally BMM-only, make that explicit by constraining techWriterCommands to
BMM-only and document/enforce that path choice so other modules cannot register
tech-writer commands that would point to a non-existent file.
- Around line 408-412: selectedModules (from options.selectedModules) can
contain duplicates causing nonCoreModules to include duplicate paths;
deduplicate selectedModules before computing installedModules/nonCoreModules
(e.g., replace options.selectedModules usage with a deduplicated array via a Set
or similar) so installedModules and nonCoreModules only contain unique module
names; update the logic around selectedModules, installedModules, and
nonCoreModules to use the deduplicated list so generated markdown no longer
contains repeated path strings.

---

Outside diff comments:
In `@tools/cli/installers/lib/ide/github-copilot.js`:
- Around line 334-340: The techWriterCommands object currently hardcodes
filenames with the `bmm` segment which causes generated prompt filenames to be
incorrect for other modules; update the code that constructs those filenames to
use the actual module name (e.g., `entry.module` or the derived `configModule`)
instead of the fixed `bmm`. Specifically, change the `file` values in
`techWriterCommands` (or the logic that consumes them) to generate names like
`bmad-${entry.module}-write-document` (and similar for update-standards,
mermaid-generate, validate-document, explain-concept) so prompt filenames
reflect the dynamic module being processed.

---

Nitpick comments:
In `@tools/cli/installers/lib/ide/github-copilot.js`:
- Around line 405-406: generateCopilotInstructions currently calls
loadModuleConfig(bmadDir) without passing options, so per-module config cannot
be resolved; update the call in generateCopilotInstructions to forward the
options (or at least options.selectedModules) to loadModuleConfig so that
loadModuleConfig(bmadDir, options) (or loadModuleConfig(bmadDir,
selectedModules)) can use the selected modules when resolving config—adjust the
loadModuleConfig signature and any callers accordingly to accept and use the
extra parameter.
- Around line 488-494: The static "Core workflows" bullet duplicates the path
already output via workflowDefsLine when no non-core modules are installed;
update the logic that sets workflowDefsLine (the else branch where
workflowDefsLine is assigned) to avoid duplication by setting workflowDefsLine
to an empty string or null when it would otherwise point to
`${bmad}/core/workflows/`, and then ensure downstream rendering skips empty/null
workflowDefsLine (or alternately remove the static "- **Core workflows**" bullet
and rely solely on workflowDefsLine/agentDefsLine/moduleConfigLine); locate the
assignment to workflowDefsLine and the static bullet text near the variables
agentDefsLine, workflowDefsLine, moduleConfigLine and bmad to implement the
change.
- Around line 331-361: The createTechWriterPromptContent function currently uses
a hardcoded techWriterCommands map and silently returns null when entry.name
isn't found; update it to log a warning before returning null (e.g., emit
this.logger.warn or console.warn including entry.name and configModule) so
skipped/unknown tech-writer commands are visible, and optionally fall back to
generating a generic prompt using entry.name when cmd is missing; modify the
block around const cmd = techWriterCommands[entry.name]; if (!cmd) return null;
to emit the warning (and/or create a generic cmd object) while still using
escapeYamlSingleQuote, getToolsForFile, configModule and this.bmadFolderName
downstream.
- Around line 251-253: The code interpolates entry.module and artifact.module
directly into paths (see configModule, configLine and other uses) which can
produce unsafe/malformed paths; sanitize or validate these values before use by
stripping path separators and traversal tokens (e.g., remove characters like
../, ./, path.sep) or, better, validate against a whitelist of known module
names and fallback to 'core' if invalid, and apply the same guard to every
occurrence that uses entry.module or artifact.module (including the other spots
mentioned) so generated paths use only safe, verified module names.
- Around line 446-467: The three generated lines are inconsistent and cause
duplication; make them consistent by: (1) build workflowDefsLine exactly like
agentDefsLine — when nonCoreModules.length > 0 set workflowDefsLine = `-
**Workflow definitions**: ${moduleWorkflowPaths} and \`${bmad}/core/workflows/\`
(core)`, otherwise set workflowDefsLine to an empty string (so core-only cases
don't duplicate the core path elsewhere); (2) set moduleConfigLine to `-
**Module configuration**: ${moduleConfigPaths}` when nonCoreModules.length > 0
and to an empty string when there are no non-core modules; and (3) update the
template render to include agentDefsLine, workflowDefsLine, and moduleConfigLine
only if they are non-empty — use the existing symbols agentDefsLine,
workflowDefsLine, moduleConfigLine, nonCoreModules, moduleAgentPaths,
moduleWorkflowPaths, moduleConfigPaths, and bmad to locate and change the logic.

Markus-Ende and others added 3 commits February 19, 2026 20:37
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Hardcoded bmm/config.yaml path in GitHub Copilot installer breaks custom modules

2 participants