_cfg -> _config

This commit is contained in:
Brian Madison
2025-12-13 19:41:09 +08:00
parent ac5fa5c23f
commit ae9851acab
37 changed files with 192 additions and 130 deletions

View File

@@ -99,7 +99,7 @@ async function buildAgent(projectDir, agentName) {
// Build the standalone agent
console.log(chalk.cyan(` Building standalone agent ${agentName}...`));
const customizePath = path.join(projectDir, '_bmad', '_cfg', 'agents', `${agentName}.customize.yaml`);
const customizePath = path.join(projectDir, '_bmad', '_config', 'agents', `${agentName}.customize.yaml`);
const customizeExists = await fs.pathExists(customizePath);
await builder.buildAgent(standaloneYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });
@@ -125,7 +125,7 @@ async function buildAgent(projectDir, agentName) {
// Build the agent
console.log(chalk.cyan(` Building ${agentName}...`));
const customizePath = path.join(projectDir, '.claude', '_cfg', 'agents', `${agentName}.customize.yaml`);
const customizePath = path.join(projectDir, '.claude', '_config', 'agents', `${agentName}.customize.yaml`);
const customizeExists = await fs.pathExists(customizePath);
await builder.buildAgent(agentYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });
@@ -177,7 +177,7 @@ async function buildAllAgents(projectDir) {
console.log(chalk.cyan(` Building standalone agent ${agentName}...`));
const customizePath = path.join(projectDir, '_bmad', '_cfg', 'agents', `${agentName}.customize.yaml`);
const customizePath = path.join(projectDir, '_bmad', '_config', 'agents', `${agentName}.customize.yaml`);
const customizeExists = await fs.pathExists(customizePath);
await builder.buildAgent(agentYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });
@@ -213,7 +213,7 @@ async function buildAllAgents(projectDir) {
console.log(chalk.cyan(` Building ${agentName}...`));
const customizePath = path.join(projectDir, '.claude', '_cfg', 'agents', `${agentName}.customize.yaml`);
const customizePath = path.join(projectDir, '.claude', '_config', 'agents', `${agentName}.customize.yaml`);
const customizeExists = await fs.pathExists(customizePath);
await builder.buildAgent(agentYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });

View File

@@ -15,7 +15,7 @@ class ConfigCollector {
/**
* Find the bmad installation directory in a project
* V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
* V6+ installations can use ANY folder name but ALWAYS have _config/manifest.yaml
* @param {string} projectDir - Project directory
* @returns {Promise<string>} Path to bmad directory
*/
@@ -26,13 +26,13 @@ class ConfigCollector {
return path.join(projectDir, 'bmad');
}
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
// V6+ strategy: Look for ANY directory with _config/manifest.yaml
// This is the definitive marker of a V6+ installation
try {
const entries = await fs.readdir(projectDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
const manifestPath = path.join(projectDir, entry.name, '_config', 'manifest.yaml');
if (await fs.pathExists(manifestPath)) {
// Found a V6+ installation
return path.join(projectDir, entry.name);
@@ -59,12 +59,12 @@ class ConfigCollector {
return null;
}
// Look for ANY directory with _cfg/manifest.yaml
// Look for ANY directory with _config/manifest.yaml
try {
const entries = await fs.readdir(projectDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
const manifestPath = path.join(projectDir, entry.name, '_config', 'manifest.yaml');
if (await fs.pathExists(manifestPath)) {
// Found a V6+ installation, return just the folder name
return entry.name;

View File

@@ -1,6 +1,6 @@
/**
* Custom Module Source Cache
* Caches custom module sources under _cfg/custom/ to ensure they're never lost
* Caches custom module sources under _config/custom/ to ensure they're never lost
* and can be checked into source control
*/
@@ -11,7 +11,7 @@ const crypto = require('node:crypto');
class CustomModuleCache {
constructor(bmadDir) {
this.bmadDir = bmadDir;
this.customCacheDir = path.join(bmadDir, '_cfg', 'custom');
this.customCacheDir = path.join(bmadDir, '_config', 'custom');
this.manifestPath = path.join(this.customCacheDir, 'cache-manifest.yaml');
}

View File

@@ -92,7 +92,7 @@ class Detector {
// Fallback: scan directory for modules (legacy installations without manifest)
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_cfg') {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_config') {
const modulePath = path.join(bmadDir, entry.name);
const moduleConfigPath = path.join(modulePath, 'config.yaml');
@@ -205,7 +205,7 @@ class Detector {
/**
* Detect legacy BMAD v4 footprints (case-sensitive path checks)
* V4 used _bmad-method as default folder name
* V6+ uses configurable folder names and ALWAYS has _cfg/manifest.yaml with installation.version
* V6+ uses configurable folder names and ALWAYS has _config/manifest.yaml with installation.version
* @param {string} projectDir - Project directory to check
* @returns {{ hasLegacyV4: boolean, offenders: string[] }}
*/
@@ -232,7 +232,7 @@ class Detector {
// Helper: check if a directory is a V6+ installation
const isV6Installation = async (dirPath) => {
const manifestPath = path.join(dirPath, '_cfg', 'manifest.yaml');
const manifestPath = path.join(dirPath, '_config', 'manifest.yaml');
if (!(await fs.pathExists(manifestPath))) {
return false;
}
@@ -250,7 +250,7 @@ class Detector {
const offenders = [];
// Strategy:
// 1. First scan for ANY V6+ installation (_cfg/manifest.yaml)
// 1. First scan for ANY V6+ installation (_config/manifest.yaml)
// 2. If V6+ found → don't flag anything (user is already on V6+)
// 3. If NO V6+ found → flag folders with "bmad" in name as potential V4 legacy
@@ -271,7 +271,7 @@ class Detector {
continue; // Skip empty folders
}
// Check if it's a V6+ installation by looking for _cfg/manifest.yaml
// Check if it's a V6+ installation by looking for _config/manifest.yaml
// This works for ANY folder name (not just bmad-prefixed)
const isV6 = await isV6Installation(fullPath);

View File

@@ -4,7 +4,7 @@ const yaml = require('yaml');
/**
* Manages IDE configuration persistence
* Saves and loads IDE-specific configurations to/from bmad/_cfg/ides/
* Saves and loads IDE-specific configurations to/from bmad/_config/ides/
*/
class IdeConfigManager {
constructor() {}
@@ -15,7 +15,7 @@ class IdeConfigManager {
* @returns {string} Path to IDE config directory
*/
getIdeConfigDir(bmadDir) {
return path.join(bmadDir, '_cfg', 'ides');
return path.join(bmadDir, '_config', 'ides');
}
/**

View File

@@ -38,27 +38,40 @@ class Installer {
/**
* Find the bmad installation directory in a project
* V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
* V6+ installations can use ANY folder name but ALWAYS have _config/manifest.yaml
* Also checks for legacy _cfg folder for migration
* @param {string} projectDir - Project directory
* @returns {Promise<string>} Path to bmad directory
* @returns {Promise<Object>} { bmadDir: string, hasLegacyCfg: boolean }
*/
async findBmadDir(projectDir) {
// Check if project directory exists
if (!(await fs.pathExists(projectDir))) {
// Project doesn't exist yet, return default
return path.join(projectDir, '_bmad');
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
}
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
// This is the definitive marker of a V6+ installation
// V6+ strategy: Look for ANY directory with _config/manifest.yaml or legacy _cfg/manifest.yaml
let bmadDir = null;
let hasLegacyCfg = false;
try {
const entries = await fs.readdir(projectDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
const bmadPath = path.join(projectDir, entry.name);
// Check for current _config folder
const manifestPath = path.join(bmadPath, '_config', 'manifest.yaml');
if (await fs.pathExists(manifestPath)) {
// Found a V6+ installation
return path.join(projectDir, entry.name);
// Found a V6+ installation with current _config folder
return { bmadDir: bmadPath, hasLegacyCfg: false };
}
// Check for legacy _cfg folder
const legacyManifestPath = path.join(bmadPath, '_cfg', 'manifest.yaml');
if (await fs.pathExists(legacyManifestPath)) {
bmadDir = bmadPath;
hasLegacyCfg = true;
}
}
}
@@ -66,9 +79,14 @@ class Installer {
// Ignore errors, fall through to default
}
// If we found a bmad directory (with or without legacy _cfg)
if (bmadDir) {
return { bmadDir, hasLegacyCfg };
}
// No V6+ installation found, return default
// This will be used for new installations
return path.join(projectDir, '_bmad');
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
}
/**
@@ -473,10 +491,13 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
let existingBmadDir = null;
let existingBmadFolderName = null;
let hasLegacyCfg = false;
if (await fs.pathExists(projectDir)) {
existingBmadDir = await this.findBmadDir(projectDir);
const result = await this.findBmadDir(projectDir);
existingBmadDir = result.bmadDir;
existingBmadFolderName = path.basename(existingBmadDir);
hasLegacyCfg = result.hasLegacyCfg;
}
// Create a project directory if it doesn't exist (user already confirmed)
@@ -501,6 +522,44 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const bmadDir = path.join(projectDir, bmadFolderName);
// Check for legacy _cfg folder and prompt for rename
if (hasLegacyCfg && !config._quickUpdate) {
spinner.stop();
console.log(chalk.yellow('\n⚠ Legacy configuration folder detected'));
console.log(chalk.dim(` Found: ${path.join(bmadDir, '_cfg')}`));
console.log(chalk.dim(' The configuration folder has been renamed from "_cfg" to "_config"'));
const inquirer = require('inquirer');
const { shouldRename } = await inquirer.prompt([
{
type: 'confirm',
name: 'shouldRename',
message: 'Would you like the installer to rename "_cfg" to "_config" for you?',
default: true,
},
]);
if (!shouldRename) {
console.log(chalk.red('\n❌ Installation cancelled'));
console.log(chalk.dim('You must manually rename the "_cfg" folder to "_config" before proceeding.'));
return { success: false, cancelled: true };
}
// Perform the rename
spinner.start('Renaming configuration folder...');
try {
const oldCfgPath = path.join(bmadDir, '_cfg');
const newCfgPath = path.join(bmadDir, '_config');
await fs.move(oldCfgPath, newCfgPath);
spinner.succeed('Configuration folder renamed successfully');
} catch (error) {
spinner.fail('Failed to rename configuration folder');
console.error(chalk.red(`Error: ${error.message}`));
return { success: false, error: error.message };
}
}
// Check existing installation
spinner.text = 'Checking for existing installation...';
const existingInstall = await this.detector.detect(bmadDir);
@@ -834,8 +893,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
customInfo = config._customModuleSources.get(moduleName);
isCustomModule = true;
// Check if this is a cached module (source path starts with _cfg)
if (customInfo.sourcePath && (customInfo.sourcePath.startsWith('_cfg') || customInfo.sourcePath.includes('_cfg/custom'))) {
// Check if this is a cached module (source path starts with _config)
if (
customInfo.sourcePath &&
(customInfo.sourcePath.startsWith('_config') || customInfo.sourcePath.includes('_config/custom'))
) {
useCache = true;
// Make sure we have the right path structure
if (!customInfo.path) {
@@ -939,9 +1001,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
if (finalCustomContent && finalCustomContent.cachedModules) {
sourcePath = finalCustomContent.cachedModules.find((m) => m.id === moduleName)?.relativePath;
} else {
// During update, the sourcePath is already cache-relative if it starts with _cfg
// During update, the sourcePath is already cache-relative if it starts with _config
sourcePath =
customInfo.sourcePath && customInfo.sourcePath.startsWith('_cfg')
customInfo.sourcePath && customInfo.sourcePath.startsWith('_config')
? customInfo.sourcePath
: path.relative(bmadDir, customInfo.path || customInfo.sourcePath);
}
@@ -1061,7 +1123,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Customize templates are now created in processAgentFiles when building YAML agents
// Pre-register manifest files that will be created (except files-manifest.csv to avoid recursion)
const cfgDir = path.join(bmadDir, '_cfg');
const cfgDir = path.join(bmadDir, '_config');
this.installedFiles.push(
path.join(cfgDir, 'manifest.yaml'),
path.join(cfgDir, 'workflow-manifest.csv'),
@@ -1329,7 +1391,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
try {
const projectDir = path.resolve(config.directory);
const bmadDir = await this.findBmadDir(projectDir);
const { bmadDir } = await this.findBmadDir(projectDir);
const existingInstall = await this.detector.detect(bmadDir);
if (!existingInstall.installed) {
@@ -1414,7 +1476,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
*/
async getStatus(directory) {
const projectDir = path.resolve(directory);
const bmadDir = await this.findBmadDir(projectDir);
const { bmadDir } = await this.findBmadDir(projectDir);
return await this.detector.detect(bmadDir);
}
@@ -1430,7 +1492,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
*/
async uninstall(directory) {
const projectDir = path.resolve(directory);
const bmadDir = await this.findBmadDir(projectDir);
const { bmadDir } = await this.findBmadDir(projectDir);
if (await fs.pathExists(bmadDir)) {
await fs.remove(bmadDir);
@@ -1447,9 +1509,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
*/
async createDirectoryStructure(bmadDir) {
await fs.ensureDir(bmadDir);
await fs.ensureDir(path.join(bmadDir, '_cfg'));
await fs.ensureDir(path.join(bmadDir, '_cfg', 'agents'));
await fs.ensureDir(path.join(bmadDir, '_cfg', 'custom'));
await fs.ensureDir(path.join(bmadDir, '_config'));
await fs.ensureDir(path.join(bmadDir, '_config', 'agents'));
await fs.ensureDir(path.join(bmadDir, '_config', 'custom'));
}
/**
@@ -1466,7 +1528,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Get all installed module directories
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
const installedModules = entries
.filter((entry) => entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs')
.filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs')
.map((entry) => entry.name);
// Generate config.yaml for each installed module
@@ -1826,9 +1888,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Determine project directory (parent of bmad/ directory)
const bmadDir = path.dirname(modulePath);
const projectDir = path.dirname(bmadDir);
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
// Ensure _cfg/agents directory exists
// Ensure _config/agents directory exists
await fs.ensureDir(cfgAgentsDir);
// Get all agent files
@@ -1917,7 +1979,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
*/
async buildStandaloneAgents(bmadDir, projectDir) {
const standaloneAgentsPath = path.join(bmadDir, 'agents');
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
// Check if standalone agents directory exists
if (!(await fs.pathExists(standaloneAgentsPath))) {
@@ -2018,7 +2080,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Determine project directory (parent of bmad/ directory)
const bmadDir = path.dirname(modulePath);
const projectDir = path.dirname(bmadDir);
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
const targetAgentsPath = path.join(modulePath, 'agents');
// Ensure target directory exists
@@ -2143,7 +2205,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
async compileAgents(config) {
try {
const projectDir = path.resolve(config.directory);
const bmadDir = await this.findBmadDir(projectDir);
const { bmadDir } = await this.findBmadDir(projectDir);
// Check if bmad directory exists
if (!(await fs.pathExists(bmadDir))) {
@@ -2151,7 +2213,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
}
// Get installed modules from manifest
const manifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
let installedModules = [];
let manifest = null;
if (await fs.pathExists(manifestPath)) {
@@ -2181,7 +2243,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== '_cfg' && entry.name !== 'docs') {
if (entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs') {
const modulePath = path.join(bmadDir, entry.name);
// Special handling for standalone agents in bmad/agents/ directory
@@ -2264,7 +2326,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
try {
const projectDir = path.resolve(config.directory);
const bmadDir = await this.findBmadDir(projectDir);
const { bmadDir } = await this.findBmadDir(projectDir);
// Check if bmad directory exists
if (!(await fs.pathExists(bmadDir))) {
@@ -2287,8 +2349,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Ensure we have an absolute sourcePath
let absoluteSourcePath = customModule.sourcePath;
// Check if sourcePath is a cache-relative path (starts with _cfg/)
if (absoluteSourcePath && absoluteSourcePath.startsWith('_cfg')) {
// Check if sourcePath is a cache-relative path (starts with _config/)
if (absoluteSourcePath && absoluteSourcePath.startsWith('_config')) {
// Convert cache-relative path to absolute path
absoluteSourcePath = path.join(bmadDir, absoluteSourcePath);
}
@@ -2695,7 +2757,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
* @returns {Array} Array of file entries from files-manifest.csv
*/
async readFilesManifest(bmadDir) {
const filesManifestPath = path.join(bmadDir, '_cfg', 'files-manifest.csv');
const filesManifestPath = path.join(bmadDir, '_config', 'files-manifest.csv');
if (!(await fs.pathExists(filesManifestPath))) {
return [];
}
@@ -2798,12 +2860,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const relativePath = path.relative(bmadDir, fullPath);
const fileName = path.basename(fullPath);
// Skip _cfg directory EXCEPT for modified agent customizations
if (relativePath.startsWith('_cfg/') || relativePath.startsWith('_cfg\\')) {
// Skip _config directory EXCEPT for modified agent customizations
if (relativePath.startsWith('_config/') || relativePath.startsWith('_config\\')) {
// Special handling for .customize.yaml files - only preserve if modified
if (relativePath.includes('/agents/') && fileName.endsWith('.customize.yaml')) {
// Check if the customization file has been modified from manifest
const manifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
if (await fs.pathExists(manifestPath)) {
const crypto = require('node:crypto');
const currentContent = await fs.readFile(fullPath, 'utf8');
@@ -2824,7 +2886,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
}
// Skip config.yaml files - these are regenerated on each install/update
// Users should use _cfg/agents/ override files instead
// Users should use _config/agents/ override files instead
if (fileName === 'config.yaml') {
continue;
}
@@ -2832,8 +2894,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
if (!fileInfo) {
// File not in manifest = custom file
// EXCEPT: Agent .md files in module folders are generated files, not custom
// Only treat .md files under _cfg/agents/ as custom
if (!(fileName.endsWith('.md') && relativePath.includes('/agents/') && !relativePath.startsWith('_cfg/'))) {
// Only treat .md files under _config/agents/ as custom
if (!(fileName.endsWith('.md') && relativePath.includes('/agents/') && !relativePath.startsWith('_config/'))) {
customFiles.push(fullPath);
}
} else if (manifestHasHashes && fileInfo.hash) {
@@ -2866,7 +2928,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
* @param {Object} userInfo - User information including name and language
*/
async createAgentConfigs(bmadDir, userInfo = null) {
const agentConfigDir = path.join(bmadDir, '_cfg', 'agents');
const agentConfigDir = path.join(bmadDir, '_config', 'agents');
await fs.ensureDir(agentConfigDir);
// Get all agents from all modules
@@ -2876,7 +2938,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Check modules for agents (including core)
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== '_cfg') {
if (entry.isDirectory() && entry.name !== '_config') {
const moduleAgentsPath = path.join(bmadDir, entry.name, 'agents');
if (await fs.pathExists(moduleAgentsPath)) {
const agentFiles = await fs.readdir(moduleAgentsPath);
@@ -2994,7 +3056,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
* @param {Array} agentDetails - Array of agent details
*/
async generateAgentManifest(bmadDir, agentDetails) {
const manifestPath = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
const manifestPath = path.join(bmadDir, '_config', 'agent-manifest.csv');
await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
}

View File

@@ -28,8 +28,8 @@ class ManifestGenerator {
* @param {Array} installedFiles - All installed files (optional, for hash tracking)
*/
async generateManifests(bmadDir, selectedModules, installedFiles = [], options = {}) {
// Create _cfg directory if it doesn't exist
const cfgDir = path.join(bmadDir, '_cfg');
// Create _config directory if it doesn't exist
const cfgDir = path.join(bmadDir, '_config');
await fs.ensureDir(cfgDir);
// Store modules list (all modules including preserved ones)
@@ -902,7 +902,7 @@ class ManifestGenerator {
for (const entry of entries) {
// Skip if not a directory or is a special directory
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === '_cfg') {
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === '_config') {
continue;
}

View File

@@ -10,10 +10,10 @@ class Manifest {
* @param {Array} installedFiles - List of installed files (no longer used, files tracked in files-manifest.csv)
*/
async create(bmadDir, data, installedFiles = []) {
const manifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
const yaml = require('yaml');
// Ensure _cfg directory exists
// Ensure _config directory exists
await fs.ensureDir(path.dirname(manifestPath));
// Structure the manifest data
@@ -46,7 +46,7 @@ class Manifest {
* @returns {Object|null} Manifest data or null if not found
*/
async read(bmadDir) {
const yamlPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
const yamlPath = path.join(bmadDir, '_config', 'manifest.yaml');
const yaml = require('yaml');
if (await fs.pathExists(yamlPath)) {
@@ -97,7 +97,7 @@ class Manifest {
ides: manifest.ides || [],
};
const manifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
await fs.ensureDir(path.dirname(manifestPath));
const yamlContent = yaml.stringify(manifestData, {

View File

@@ -306,7 +306,7 @@ class CustomHandler {
const targetMdPath = path.join(targetDir, `${agentName}.md`);
// Use the actual bmadDir if available (for when installing to temp dir)
const actualBmadDir = config._bmadDir || bmadDir;
const customizePath = path.join(actualBmadDir, '_cfg', 'agents', `custom-${agentName}.customize.yaml`);
const customizePath = path.join(actualBmadDir, '_config', 'agents', `custom-${agentName}.customize.yaml`);
// Read and compile the YAML
try {

View File

@@ -136,7 +136,7 @@ class BaseIdeSetup {
// Get module agents
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_cfg' && entry.name !== 'agents') {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_config' && entry.name !== 'agents') {
const moduleAgentsPath = path.join(bmadDir, entry.name, 'agents');
if (await fs.pathExists(moduleAgentsPath)) {
const moduleAgents = await this.scanDirectory(moduleAgentsPath, '.md');
@@ -208,7 +208,7 @@ class BaseIdeSetup {
// Get module tasks
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_cfg' && entry.name !== 'agents') {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_config' && entry.name !== 'agents') {
const moduleTasksPath = path.join(bmadDir, entry.name, 'tasks');
if (await fs.pathExists(moduleTasksPath)) {
const moduleTasks = await this.scanDirectoryWithStandalone(moduleTasksPath, ['.md', '.xml']);
@@ -254,7 +254,7 @@ class BaseIdeSetup {
// Get module tools
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_cfg' && entry.name !== 'agents') {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_config' && entry.name !== 'agents') {
const moduleToolsPath = path.join(bmadDir, entry.name, 'tools');
if (await fs.pathExists(moduleToolsPath)) {
const moduleTools = await this.scanDirectoryWithStandalone(moduleToolsPath, ['.md', '.xml']);
@@ -300,7 +300,7 @@ class BaseIdeSetup {
// Get module workflows
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_cfg' && entry.name !== 'agents') {
if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_config' && entry.name !== 'agents') {
const moduleWorkflowsPath = path.join(bmadDir, entry.name, 'workflows');
if (await fs.pathExists(moduleWorkflowsPath)) {
const moduleWorkflows = await this.findWorkflowYamlFiles(moduleWorkflowsPath);
@@ -635,7 +635,7 @@ class BaseIdeSetup {
* @param {Object} agent - Agent information
*/
async createAgentConfig(bmadDir, agent) {
const agentConfigDir = path.join(bmadDir, '_cfg', 'agents');
const agentConfigDir = path.join(bmadDir, '_config', 'agents');
await this.ensureDir(agentConfigDir);
// Load agent config template

View File

@@ -85,7 +85,7 @@ Follow all instructions in the ${type} file exactly as written.
* Load task manifest CSV
*/
async loadTaskManifest(bmadDir) {
const manifestPath = path.join(bmadDir, '_cfg', 'task-manifest.csv');
const manifestPath = path.join(bmadDir, '_config', 'task-manifest.csv');
if (!(await fs.pathExists(manifestPath))) {
return null;
@@ -102,7 +102,7 @@ Follow all instructions in the ${type} file exactly as written.
* Load tool manifest CSV
*/
async loadToolManifest(bmadDir) {
const manifestPath = path.join(bmadDir, '_cfg', 'tool-manifest.csv');
const manifestPath = path.join(bmadDir, '_config', 'tool-manifest.csv');
if (!(await fs.pathExists(manifestPath))) {
return null;

View File

@@ -224,7 +224,7 @@ When running any workflow:
}
async loadWorkflowManifest(bmadDir) {
const manifestPath = path.join(bmadDir, '_cfg', 'workflow-manifest.csv');
const manifestPath = path.join(bmadDir, '_config', 'workflow-manifest.csv');
if (!(await fs.pathExists(manifestPath))) {
return null;

View File

@@ -242,15 +242,15 @@ class ModuleManager {
}
}
// Also check for cached custom modules in _cfg/custom/
// Also check for cached custom modules in _config/custom/
if (this.bmadDir) {
const customCacheDir = path.join(this.bmadDir, '_cfg', 'custom');
const customCacheDir = path.join(this.bmadDir, '_config', 'custom');
if (await fs.pathExists(customCacheDir)) {
const cacheEntries = await fs.readdir(customCacheDir, { withFileTypes: true });
for (const entry of cacheEntries) {
if (entry.isDirectory()) {
const cachePath = path.join(customCacheDir, entry.name);
const moduleInfo = await this.getModuleInfo(cachePath, entry.name, '_cfg/custom');
const moduleInfo = await this.getModuleInfo(cachePath, entry.name, '_config/custom');
if (moduleInfo && !modules.some((m) => m.id === moduleInfo.id) && !customModules.some((m) => m.id === moduleInfo.id)) {
moduleInfo.isCustom = true;
moduleInfo.fromCache = true;
@@ -785,7 +785,7 @@ class ModuleManager {
async compileModuleAgents(sourcePath, targetPath, moduleName, bmadDir) {
const sourceAgentsPath = path.join(sourcePath, 'agents');
const targetAgentsPath = path.join(targetPath, 'agents');
const cfgAgentsDir = path.join(bmadDir, '_cfg', 'agents');
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
// Check if agents directory exists in source
if (!(await fs.pathExists(sourceAgentsPath))) {
@@ -827,7 +827,7 @@ class ModuleManager {
const originalHash = crypto.createHash('sha256').update(customizeContent).digest('hex');
// Store in main manifest
const manifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
let manifestData = {};
if (await fs.pathExists(manifestPath)) {
const manifestContent = await fs.readFile(manifestPath, 'utf8');

View File

@@ -29,7 +29,7 @@ const AgentPartyGenerator = {
let xmlContent = `<!-- Powered by BMAD-CORE™ -->
<!-- Agent Manifest - Generated during BMAD ${forWeb ? 'bundling' : 'installation'} -->
<!-- This file contains a summary of all ${forWeb ? 'bundled' : 'installed'} agents for quick reference -->
<manifest id="bmad/_cfg/agent-manifest.csv" version="1.0" generated="${new Date().toISOString()}">
<manifest id="bmad/_config/agent-manifest.csv" version="1.0" generated="${new Date().toISOString()}">
<description>
Complete roster of ${forWeb ? 'bundled' : 'installed'} BMAD agents with summarized personas for efficient multi-agent orchestration.
Used by party-mode and other multi-agent coordination features.

View File

@@ -410,7 +410,7 @@ function detectBmadProject(targetPath) {
const possibleNames = ['_bmad'];
for (const name of possibleNames) {
const bmadFolder = path.join(checkPath, name);
const cfgFolder = path.join(bmadFolder, '_cfg');
const cfgFolder = path.join(bmadFolder, '_config');
const manifestFile = path.join(cfgFolder, 'agent-manifest.csv');
if (fs.existsSync(manifestFile)) {
@@ -596,16 +596,16 @@ function addToManifest(manifestFile, agentData) {
}
/**
* Save agent source YAML to _cfg/custom/agents/ for reinstallation
* Save agent source YAML to _config/custom/agents/ for reinstallation
* Stores user answers in a top-level saved_answers section (cleaner than overwriting defaults)
* @param {Object} agentInfo - Agent info (path, type, etc.)
* @param {string} cfgFolder - Path to _cfg folder
* @param {string} cfgFolder - Path to _config folder
* @param {string} agentName - Final agent name (e.g., "fred-commit-poet")
* @param {Object} answers - User answers to save for reinstallation
* @returns {Object} Info about saved source
*/
function saveAgentSource(agentInfo, cfgFolder, agentName, answers = {}) {
// Save to _cfg/custom/agents/ instead of _cfg/agents/
// Save to _config/custom/agents/ instead of _config/agents/
const customAgentsCfgDir = path.join(cfgFolder, 'custom', 'agents');
if (!fs.existsSync(customAgentsCfgDir)) {
@@ -689,7 +689,7 @@ function saveAgentSource(agentInfo, cfgFolder, agentName, answers = {}) {
*/
async function createIdeSlashCommands(projectRoot, agentName, agentPath, metadata) {
// Read manifest.yaml to get installed IDEs
const manifestPath = path.join(projectRoot, '_bmad', '_cfg', 'manifest.yaml');
const manifestPath = path.join(projectRoot, '_bmad', '_config', 'manifest.yaml');
let installedIdes = ['claude-code']; // Default to Claude Code if no manifest
if (fs.existsSync(manifestPath)) {

View File

@@ -650,11 +650,11 @@ class UI {
if (stats.isDirectory()) {
const files = await fs.readdir(directory);
if (files.length > 0) {
// Check for any bmad installation (any folder with _cfg/manifest.yaml)
// Check for any bmad installation (any folder with _config/manifest.yaml)
const { Installer } = require('../installers/lib/core/installer');
const installer = new Installer();
const bmadDir = await installer.findBmadDir(directory);
const hasBmadInstall = (await fs.pathExists(bmadDir)) && (await fs.pathExists(path.join(bmadDir, '_cfg', 'manifest.yaml')));
const hasBmadInstall = (await fs.pathExists(bmadDir)) && (await fs.pathExists(path.join(bmadDir, '_config', 'manifest.yaml')));
console.log(
chalk.gray(`Directory exists and contains ${files.length} item(s)`) +

View File

@@ -525,7 +525,7 @@ class YamlXmlBuilder {
} else if (bmadIndex !== -1 && pathParts[bmadIndex + 1]) {
// Path contains /bmad/{module}/
const potentialModule = pathParts[bmadIndex + 1];
// Check if it's a known module, not 'agents' or '_cfg'
// Check if it's a known module, not 'agents' or '_config'
if (['bmm', 'bmb', 'cis', 'core'].includes(potentialModule)) {
module = potentialModule;
}