fix bmb workflow paths

This commit is contained in:
Brian Madison
2025-12-10 20:50:24 +09:00
parent 45a97b070a
commit 446a0359ab
336 changed files with 1414 additions and 1509 deletions

View File

@@ -696,15 +696,6 @@ class ConfigCollector {
}
}
// Special handling for bmad_folder: detect existing folder name
if (moduleName === 'core' && key === 'bmad_folder' && !existingValue && this.currentProjectDir) {
// Try to detect the existing BMAD folder name
const detectedFolder = await this.detectExistingBmadFolder(this.currentProjectDir);
if (detectedFolder) {
existingValue = detectedFolder;
}
}
// Special handling for user_name: default to system user
if (moduleName === 'core' && key === 'user_name' && !existingValue) {
item.default = this.getDefaultUsername();

View File

@@ -14,7 +14,7 @@
* @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete BMAD installation
* @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js
* @entrypoints Called by install.js command via installer.install(config)
* @patterns Injection point processing (AgentVibes), placeholder replacement ({bmad_folder}), module dependency resolution
* @patterns Injection point processing (AgentVibes), placeholder replacement (.bmad), module dependency resolution
* @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement()
*/
@@ -67,7 +67,7 @@ class Installer {
// Check if project directory exists
if (!(await fs.pathExists(projectDir))) {
// Project doesn't exist yet, return default
return path.join(projectDir, 'bmad');
return path.join(projectDir, '.bmad');
}
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
@@ -89,13 +89,13 @@ class Installer {
// No V6+ installation found, return default
// This will be used for new installations
return path.join(projectDir, 'bmad');
return path.join(projectDir, '.bmad');
}
/**
* @function copyFileWithPlaceholderReplacement
* @intent Copy files from BMAD source to installation directory with dynamic content transformation
* @why Enables installation-time customization: {bmad_folder} replacement + optional AgentVibes TTS injection
* @why Enables installation-time customization: .bmad replacement + optional AgentVibes TTS injection
* @param {string} sourcePath - Absolute path to source file in BMAD repository
* @param {string} targetPath - Absolute path to destination file in user's project
* @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad')
@@ -105,11 +105,6 @@ class Installer {
* @calledby installCore(), installModule(), IDE installers during file vendoring
* @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy()
*
* AI NOTE: This is the core transformation pipeline for ALL BMAD installation file copies.
* It performs two transformations in sequence:
* 1. {bmad_folder} → user's custom folder name (e.g., ".bmad" or "bmad")
* 2. <!-- TTS_INJECTION:* --> → TTS bash calls (if enabled) OR stripped (if disabled)
*
* The injection point processing enables loose coupling between BMAD and TTS providers:
* - BMAD source contains injection markers (not actual TTS code)
* - At install-time, markers are replaced OR removed based on user preference
@@ -140,16 +135,6 @@ class Installer {
// Read the file content
let content = await fs.readFile(sourcePath, 'utf8');
// Replace {bmad_folder} placeholder with actual folder name
if (content.includes('{bmad_folder}')) {
content = content.replaceAll('{bmad_folder}', bmadFolderName);
}
// Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
if (content.includes('{*bmad_folder*}')) {
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
}
// Process AgentVibes injection points (pass targetPath for tracking)
content = this.processTTSInjectionPoints(content, targetPath);
@@ -487,8 +472,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
});
}
// Get bmad_folder from config (default to 'bmad' for backwards compatibility)
const bmadFolderName = moduleConfigs.core && moduleConfigs.core.bmad_folder ? moduleConfigs.core.bmad_folder : 'bmad';
// Always use .bmad as the folder name
const bmadFolderName = '.bmad';
this.bmadFolderName = bmadFolderName; // Store for use in other methods
// Store AgentVibes configuration for injection point processing
@@ -507,7 +492,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Resolve target directory (path.resolve handles platform differences)
const projectDir = path.resolve(config.directory);
// Check if bmad_folder has changed from existing installation (only if project dir exists)
let existingBmadDir = null;
let existingBmadFolderName = null;
@@ -516,54 +500,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
existingBmadFolderName = path.basename(existingBmadDir);
}
const targetBmadDir = path.join(projectDir, bmadFolderName);
// If bmad_folder changed during update/upgrade, back up old folder and do fresh install
if (existingBmadDir && (await fs.pathExists(existingBmadDir)) && existingBmadFolderName !== bmadFolderName) {
spinner.stop();
console.log(chalk.yellow(`\n⚠️ bmad_folder has changed: ${existingBmadFolderName}${bmadFolderName}`));
console.log(chalk.yellow('This will result in a fresh installation to the new folder.'));
const inquirer = require('inquirer');
const { confirmFreshInstall } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmFreshInstall',
message: chalk.cyan('Proceed with fresh install? (Your old folder will be backed up)'),
default: true,
},
]);
if (!confirmFreshInstall) {
console.log(chalk.yellow('Installation cancelled.'));
return { success: false, cancelled: true };
}
spinner.start('Backing up existing installation...');
// Find a unique backup name
let backupDir = `${existingBmadDir}-bak`;
let counter = 1;
while (await fs.pathExists(backupDir)) {
backupDir = `${existingBmadDir}-bak-${counter}`;
counter++;
}
// Rename the old folder to backup
await fs.move(existingBmadDir, backupDir);
spinner.succeed(`Backed up ${existingBmadFolderName}${path.basename(backupDir)}`);
console.log(chalk.cyan('\n📋 Important:'));
console.log(chalk.dim(` - Your old installation has been backed up to: ${path.basename(backupDir)}`));
console.log(chalk.dim(` - If you had custom agents or configurations, copy them from:`));
console.log(chalk.dim(` ${path.basename(backupDir)}/_cfg/`));
console.log(chalk.dim(` - To the new location:`));
console.log(chalk.dim(` ${bmadFolderName}/_cfg/`));
console.log('');
spinner.start('Starting fresh installation...');
}
// Create a project directory if it doesn't exist (user already confirmed)
if (!(await fs.pathExists(projectDir))) {
spinner.text = 'Creating installation directory...';
@@ -1932,8 +1868,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Replace {bmad_folder} with actual folder name
xmlContent = xmlContent.replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
// Replace .bmad with actual folder name
xmlContent = xmlContent.replaceAll('.bmad', this.bmadFolderName || 'bmad');
// Replace {agent_sidecar_folder} if configured
const coreConfig = this.configCollector.collectedConfig.core || {};
@@ -1980,7 +1916,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Resolve path variables
const resolvedSidecarFolder = agentSidecarFolder
.replaceAll('{project-root}', projectDir)
.replaceAll('{bmad_folder}', this.bmadFolderName || 'bmad');
.replaceAll('.bmad', this.bmadFolderName || 'bmad');
// Create sidecar directory for this agent
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
@@ -2674,7 +2610,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
lastModified: new Date().toISOString(),
};
// Check if bmad_folder has changed
const existingBmadFolderName = path.basename(bmadDir);
const newBmadFolderName = this.configCollector.collectedConfig.core?.bmad_folder || existingBmadFolderName;
@@ -3272,7 +3207,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const agentSidecarFolder = config.coreConfig?.agent_sidecar_folder;
// Resolve path variables
const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('{bmad_folder}', bmadDir);
const resolvedSidecarFolder = agentSidecarFolder.replaceAll('{project-root}', projectDir).replaceAll('.bmad', bmadDir);
// Create sidecar directory for this agent
const agentSidecarDir = path.join(resolvedSidecarFolder, finalAgentName);

View File

@@ -23,7 +23,7 @@ class ManifestGenerator {
/**
* Generate all manifests for the installation
* @param {string} bmadDir - BMAD installation directory
* @param {string} bmadDir - .bmad
* @param {Array} selectedModules - Selected modules for installation
* @param {Array} installedFiles - All installed files (optional, for hash tracking)
*/

View File

@@ -255,7 +255,6 @@ class CustomHandler {
let content = await fs.readFile(sourcePath, 'utf8');
// Replace placeholders
content = content.replaceAll('{bmad_folder}', config.bmad_folder || 'bmad');
content = content.replaceAll('{user_name}', config.user_name || 'User');
content = content.replaceAll('{communication_language}', config.communication_language || 'English');
content = content.replaceAll('{output_folder}', config.output_folder || 'docs');
@@ -321,7 +320,6 @@ class CustomHandler {
if (await fs.pathExists(genericTemplatePath)) {
// Copy with placeholder replacement
let templateContent = await fs.readFile(genericTemplatePath, 'utf8');
templateContent = templateContent.replaceAll('{bmad_folder}', config.bmad_folder || 'bmad');
await fs.writeFile(customizePath, templateContent, 'utf8');
console.log(chalk.dim(` Created customize: custom-${agentName}.customize.yaml`));
}
@@ -332,7 +330,6 @@ class CustomHandler {
// Replace placeholders in the compiled content
let processedXml = xml;
processedXml = processedXml.replaceAll('{bmad_folder}', config.bmad_folder || 'bmad');
processedXml = processedXml.replaceAll('{user_name}', config.user_name || 'User');
processedXml = processedXml.replaceAll('{communication_language}', config.communication_language || 'English');
processedXml = processedXml.replaceAll('{output_folder}', config.output_folder || 'docs');
@@ -358,7 +355,7 @@ class CustomHandler {
const projectDir = path.dirname(bmadDir);
const resolvedSidecarFolder = config.agent_sidecar_folder
.replaceAll('{project-root}', projectDir)
.replaceAll('{bmad_folder}', path.basename(bmadDir));
.replaceAll('.bmad', path.basename(bmadDir));
// Create sidecar directory for this agent
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);

View File

@@ -527,26 +527,26 @@ class BaseIdeSetup {
}
/**
* Write file with content (replaces {bmad_folder} placeholder)
* Write file with content (replaces .bmad placeholder)
* @param {string} filePath - File path
* @param {string} content - File content
*/
async writeFile(filePath, content) {
// Replace {bmad_folder} placeholder if present
if (typeof content === 'string' && content.includes('{bmad_folder}')) {
content = content.replaceAll('{bmad_folder}', this.bmadFolderName);
// Replace .bmad placeholder if present
if (typeof content === 'string' && content.includes('.bmad')) {
content = content.replaceAll('.bmad', this.bmadFolderName);
}
// Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
if (typeof content === 'string' && content.includes('{*bmad_folder*}')) {
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
// Replace escape sequence .bmad with literal .bmad
if (typeof content === 'string' && content.includes('.bmad')) {
content = content.replaceAll('.bmad', '.bmad');
}
await this.ensureDir(path.dirname(filePath));
await fs.writeFile(filePath, content, 'utf8');
}
/**
* Copy file from source to destination (replaces {bmad_folder} placeholder in text files)
* Copy file from source to destination (replaces .bmad placeholder in text files)
* @param {string} source - Source file path
* @param {string} dest - Destination file path
*/
@@ -563,14 +563,14 @@ class BaseIdeSetup {
// Read the file content
let content = await fs.readFile(source, 'utf8');
// Replace {bmad_folder} placeholder with actual folder name
if (content.includes('{bmad_folder}')) {
content = content.replaceAll('{bmad_folder}', this.bmadFolderName);
// Replace .bmad placeholder with actual folder name
if (content.includes('.bmad')) {
content = content.replaceAll('.bmad', this.bmadFolderName);
}
// Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
if (content.includes('{*bmad_folder*}')) {
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
// Replace escape sequence .bmad with literal .bmad
if (content.includes('.bmad')) {
content = content.replaceAll('.bmad', '.bmad');
}
// Write to dest with replaced content

View File

@@ -174,8 +174,8 @@ ${contentWithoutFrontmatter}
// Note: {user_name} and other {config_values} are left as-is for runtime substitution by Gemini
const tomlContent = template
.replaceAll('{{title}}', title)
.replaceAll('{{*bmad_folder*}}', '{bmad_folder}')
.replaceAll('{{bmad_folder}}', this.bmadFolderName)
.replaceAll('{.bmad}', '.bmad')
.replaceAll('{.bmad}', this.bmadFolderName)
.replaceAll('{{module}}', agent.module)
.replaceAll('{{name}}', agent.name);
@@ -196,8 +196,8 @@ ${contentWithoutFrontmatter}
// Replace template variables
const tomlContent = template
.replaceAll('{{taskName}}', taskName)
.replaceAll('{{*bmad_folder*}}', '{bmad_folder}')
.replaceAll('{{bmad_folder}}', this.bmadFolderName)
.replaceAll('{.bmad}', '.bmad')
.replaceAll('{.bmad}', this.bmadFolderName)
.replaceAll('{{module}}', task.module)
.replaceAll('{{filename}}', task.filename);

View File

@@ -65,8 +65,8 @@ class AgentCommandGenerator {
.replaceAll('{{module}}', agent.module)
.replaceAll('{{path}}', agentPathInModule)
.replaceAll('{{description}}', agent.description || `${agent.name} agent`)
.replaceAll('{bmad_folder}', this.bmadFolderName)
.replaceAll('{*bmad_folder*}', '{bmad_folder}');
.replaceAll('.bmad', this.bmadFolderName)
.replaceAll('.bmad', '.bmad');
}
/**

View File

@@ -109,7 +109,7 @@ class WorkflowCommandGenerator {
// Convert source path to installed path
// From: /Users/.../src/modules/bmm/workflows/.../workflow.yaml
// To: {project-root}/{bmad_folder}/bmm/workflows/.../workflow.yaml
// To: {project-root}/.bmad/bmm/workflows/.../workflow.yaml
let workflowPath = workflow.path;
// Extract the relative path from source
@@ -131,8 +131,8 @@ class WorkflowCommandGenerator {
.replaceAll('{{module}}', workflow.module)
.replaceAll('{{description}}', workflow.description)
.replaceAll('{{workflow_path}}', workflowPath)
.replaceAll('{bmad_folder}', this.bmadFolderName)
.replaceAll('{*bmad_folder*}', '{bmad_folder}');
.replaceAll('.bmad', this.bmadFolderName)
.replaceAll('.bmad', '.bmad');
}
/**

View File

@@ -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_folder}/{{module}}/agents/{{path}}
1. LOAD the FULL agent file from @.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

View File

@@ -3,12 +3,12 @@ prompt = """
CRITICAL: You are now the BMad '{{title}}' agent.
PRE-FLIGHT CHECKLIST:
1. [ ] IMMEDIATE ACTION: Load and parse @{{bmad_folder}}/{{module}}/config.yaml - store ALL config values in memory for use throughout the session.
2. [ ] IMMEDIATE ACTION: Read and internalize the full agent definition at @{{bmad_folder}}/{{module}}/agents/{{name}}.md.
1. [ ] IMMEDIATE ACTION: Load and parse @{.bmad}/{{module}}/config.yaml - store ALL config values in memory for use throughout the session.
2. [ ] IMMEDIATE ACTION: Read and internalize the full agent definition at @{.bmad}/{{module}}/agents/{{name}}.md.
3. [ ] CONFIRM: The user's name from config is {user_name}.
Only after all checks are complete, greet the user by name and display the menu.
Acknowledge this checklist is complete in your first response.
AGENT DEFINITION: @{{bmad_folder}}/{{module}}/agents/{{name}}.md
AGENT DEFINITION: @{.bmad}/{{module}}/agents/{{name}}.md
"""

View File

@@ -3,10 +3,10 @@ prompt = """
Execute the following BMad Method task workflow:
PRE-FLIGHT CHECKLIST:
1. [ ] IMMEDIATE ACTION: Load and parse @{{bmad_folder}}/{{module}}/config.yaml.
2. [ ] IMMEDIATE ACTION: Read and load the task definition at @{{bmad_folder}}/{{module}}/tasks/{{filename}}.
1. [ ] IMMEDIATE ACTION: Load and parse @{.bmad}/{{module}}/config.yaml.
2. [ ] IMMEDIATE ACTION: Read and load the task definition at @{.bmad}/{{module}}/tasks/{{filename}}.
Follow all instructions and complete the task as defined.
TASK DEFINITION: @{{bmad_folder}}/{{module}}/tasks/{{filename}}
TASK DEFINITION: @{.bmad}/{{module}}/tasks/{{filename}}
"""

View File

@@ -5,7 +5,7 @@ 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_folder}/core/tasks/workflow.xml
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}}
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

View File

@@ -47,7 +47,7 @@ class ModuleManager {
}
/**
* Copy a file and replace {bmad_folder} placeholder with actual folder name
* Copy a file and replace .bmad placeholder with actual folder name
* @param {string} sourcePath - Source file path
* @param {string} targetPath - Target file path
*/
@@ -62,14 +62,14 @@ class ModuleManager {
// Read the file content
let content = await fs.readFile(sourcePath, 'utf8');
// Replace escape sequence {*bmad_folder*} with literal {bmad_folder}
if (content.includes('{*bmad_folder*}')) {
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
// Replace escape sequence .bmad with literal .bmad
if (content.includes('.bmad')) {
content = content.replaceAll('.bmad', '.bmad');
}
// Replace {bmad_folder} placeholder with actual folder name
if (content.includes('{bmad_folder}')) {
content = content.replaceAll('{bmad_folder}', this.bmadFolderName);
// Replace .bmad placeholder with actual folder name
if (content.includes('.bmad')) {
content = content.replaceAll('.bmad', this.bmadFolderName);
}
// Write to target with replaced content
@@ -695,8 +695,8 @@ class ModuleManager {
// IMPORTANT: Replace escape sequence and placeholder BEFORE parsing YAML
// Otherwise parsing will fail on the placeholder
yamlContent = yamlContent.replaceAll('{*bmad_folder*}', '{bmad_folder}');
yamlContent = yamlContent.replaceAll('{bmad_folder}', this.bmadFolderName);
yamlContent = yamlContent.replaceAll('.bmad', '.bmad');
yamlContent = yamlContent.replaceAll('.bmad', this.bmadFolderName);
try {
// First check if web_bundle exists by parsing
@@ -853,9 +853,9 @@ class ModuleManager {
// Compile with customizations if any
const { xml } = compileAgent(yamlContent, {}, agentName, relativePath, { config: this.coreConfig });
// Replace {bmad_folder} placeholder if needed
if (xml.includes('{bmad_folder}') && this.bmadFolderName) {
const processedXml = xml.replaceAll('{bmad_folder}', this.bmadFolderName);
// Replace .bmad placeholder if needed
if (xml.includes('.bmad') && this.bmadFolderName) {
const processedXml = xml.replaceAll('.bmad', this.bmadFolderName);
await fs.writeFile(targetMdPath, processedXml, 'utf8');
} else {
await fs.writeFile(targetMdPath, xml, 'utf8');
@@ -872,7 +872,7 @@ class ModuleManager {
const projectDir = path.dirname(bmadDir);
const resolvedSidecarFolder = agentSidecarFolder
.replaceAll('{project-root}', projectDir)
.replaceAll('{bmad_folder}', path.basename(bmadDir));
.replaceAll('.bmad', path.basename(bmadDir));
// Create sidecar directory for this agent
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
@@ -1030,10 +1030,10 @@ class ModuleManager {
const installWorkflowPath = item['workflow-install']; // Where to copy TO
// Parse SOURCE workflow path
// Handle both {bmad_folder} placeholder and hardcoded 'bmad'
// Example: {project-root}/{bmad_folder}/bmm/workflows/4-implementation/create-story/workflow.yaml
// Handle both .bmad placeholder and hardcoded 'bmad'
// Example: {project-root}/.bmad/bmm/workflows/4-implementation/create-story/workflow.yaml
// Or: {project-root}/bmad/bmm/workflows/4-implementation/create-story/workflow.yaml
const sourceMatch = sourceWorkflowPath.match(/\{project-root\}\/(?:\{bmad_folder\}|bmad)\/([^/]+)\/workflows\/(.+)/);
const sourceMatch = sourceWorkflowPath.match(/\{project-root\}\/(?:\.bmad)\/([^/]+)\/workflows\/(.+)/);
if (!sourceMatch) {
console.warn(chalk.yellow(` Could not parse workflow path: ${sourceWorkflowPath}`));
continue;
@@ -1042,9 +1042,9 @@ class ModuleManager {
const [, sourceModule, sourceWorkflowSubPath] = sourceMatch;
// Parse INSTALL workflow path
// Handle both {bmad_folder} placeholder and hardcoded 'bmad'
// Example: {project-root}/{bmad_folder}/bmgd/workflows/4-production/create-story/workflow.yaml
const installMatch = installWorkflowPath.match(/\{project-root\}\/(?:\{bmad_folder\}|bmad)\/([^/]+)\/workflows\/(.+)/);
// Handle.bmad
// Example: {project-root}/.bmad/bmgd/workflows/4-production/create-story/workflow.yaml
const installMatch = installWorkflowPath.match(/\{project-root\}\/(\.bmad)\/([^/]+)\/workflows\/(.+)/);
if (!installMatch) {
console.warn(chalk.yellow(` Could not parse workflow-install path: ${installWorkflowPath}`));
continue;
@@ -1096,9 +1096,9 @@ class ModuleManager {
async updateWorkflowConfigSource(workflowYamlPath, newModuleName) {
let yamlContent = await fs.readFile(workflowYamlPath, 'utf8');
// Replace config_source: "{project-root}/{bmad_folder}/OLD_MODULE/config.yaml"
// with config_source: "{project-root}/{bmad_folder}/NEW_MODULE/config.yaml"
// Note: At this point {bmad_folder} has already been replaced with actual folder name
// Replace config_source: "{project-root}/.bmad/OLD_MODULE/config.yaml"
// with config_source: "{project-root}/.bmad/NEW_MODULE/config.yaml"
// Note: At this point .bmad has already been replaced with actual folder name
const configSourcePattern = /config_source:\s*["']?\{project-root\}\/[^/]+\/[^/]+\/config\.yaml["']?/g;
const newConfigSource = `config_source: "{project-root}/${this.bmadFolderName}/${newModuleName}/config.yaml"`;