mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
folder rename from .bmad to _bmad
This commit is contained in:
@@ -7,13 +7,13 @@ const { getSourcePath } = require('./project-root');
|
||||
*/
|
||||
class ActivationBuilder {
|
||||
constructor() {
|
||||
this.fragmentsDir = getSourcePath('utility', 'models', 'fragments');
|
||||
this.agentComponents = getSourcePath('utility', 'agent-components');
|
||||
this.fragmentCache = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a fragment file
|
||||
* @param {string} fragmentName - Name of fragment file (e.g., 'activation-init.xml')
|
||||
* @param {string} fragmentName - Name of fragment file (e.g., 'activation-init.txt')
|
||||
* @returns {string} Fragment content
|
||||
*/
|
||||
async loadFragment(fragmentName) {
|
||||
@@ -22,7 +22,7 @@ class ActivationBuilder {
|
||||
return this.fragmentCache.get(fragmentName);
|
||||
}
|
||||
|
||||
const fragmentPath = path.join(this.fragmentsDir, fragmentName);
|
||||
const fragmentPath = path.join(this.agentComponents, fragmentName);
|
||||
|
||||
if (!(await fs.pathExists(fragmentPath))) {
|
||||
throw new Error(`Fragment not found: ${fragmentName}`);
|
||||
@@ -49,7 +49,7 @@ class ActivationBuilder {
|
||||
activation += this.indent(steps, 2) + '\n';
|
||||
|
||||
// 2. Build menu handlers section with dynamic handlers
|
||||
const menuHandlers = await this.loadFragment('menu-handlers.xml');
|
||||
const menuHandlers = await this.loadFragment('menu-handlers.txt');
|
||||
|
||||
// Build handlers (load only needed handlers)
|
||||
const handlers = await this.buildHandlers(profile);
|
||||
@@ -63,11 +63,8 @@ class ActivationBuilder {
|
||||
|
||||
activation += '\n' + this.indent(processedHandlers, 2) + '\n';
|
||||
|
||||
// 3. Include rules (skip for web bundles as they're in web-bundle-activation-steps.xml)
|
||||
if (!forWebBundle) {
|
||||
const rules = await this.loadFragment('activation-rules.xml');
|
||||
activation += this.indent(rules, 2) + '\n';
|
||||
}
|
||||
const rules = await this.loadFragment('activation-rules.txt');
|
||||
activation += this.indent(rules, 2) + '\n';
|
||||
|
||||
activation += '</activation>';
|
||||
|
||||
@@ -83,7 +80,7 @@ class ActivationBuilder {
|
||||
const handlerFragments = [];
|
||||
|
||||
for (const attrType of profile.usedAttributes) {
|
||||
const fragmentName = `handler-${attrType}.xml`;
|
||||
const fragmentName = `handler-${attrType}.txt`;
|
||||
try {
|
||||
const handler = await this.loadFragment(fragmentName);
|
||||
handlerFragments.push(handler);
|
||||
@@ -103,9 +100,7 @@ class ActivationBuilder {
|
||||
* @returns {string} Steps XML
|
||||
*/
|
||||
async buildSteps(metadata = {}, agentSpecificActions = [], forWebBundle = false) {
|
||||
// Use web-specific fragment for web bundles, standard fragment otherwise
|
||||
const fragmentName = forWebBundle ? 'web-bundle-activation-steps.xml' : 'activation-steps.xml';
|
||||
const stepsTemplate = await this.loadFragment(fragmentName);
|
||||
const stepsTemplate = await this.loadFragment('activation-steps.txt');
|
||||
|
||||
// Extract basename from agent ID (e.g., "bmad/bmm/agents/pm.md" → "pm")
|
||||
const agentBasename = metadata.id ? metadata.id.split('/').pop().replace('.md', '') : metadata.name || 'agent';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const { escapeXml } = require('../../lib/xml-utils');
|
||||
|
||||
const AgentPartyGenerator = {
|
||||
/**
|
||||
@@ -47,9 +48,9 @@ const AgentPartyGenerator = {
|
||||
for (const agent of agents) {
|
||||
xmlContent += ` <agent id="${agent.id}" name="${agent.name}" title="${agent.title || ''}" icon="${agent.icon || ''}">
|
||||
<persona>
|
||||
<role>${this.escapeXml(agent.role || '')}</role>
|
||||
<identity>${this.escapeXml(agent.identity || '')}</identity>
|
||||
<communication_style>${this.escapeXml(agent.communicationStyle || '')}</communication_style>
|
||||
<role>${escapeXml(agent.role || '')}</role>
|
||||
<identity>${escapeXml(agent.identity || '')}</identity>
|
||||
<communication_style>${escapeXml(agent.communicationStyle || '')}</communication_style>
|
||||
<principles>${agent.principles || ''}</principles>
|
||||
</persona>
|
||||
</agent>\n`;
|
||||
@@ -124,19 +125,6 @@ const AgentPartyGenerator = {
|
||||
return match ? match[1] : '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Escape XML special characters
|
||||
*/
|
||||
escapeXml(text) {
|
||||
if (!text) return '';
|
||||
return text
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll("'", ''');
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply config overrides to agent details
|
||||
* @param {Object} details - Original agent details
|
||||
|
||||
@@ -8,22 +8,7 @@ const yaml = require('yaml');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { processAgentYaml, extractInstallConfig, stripInstallConfig, getDefaultValues } = require('./template-engine');
|
||||
|
||||
// Use existing BMAD builder if available
|
||||
let YamlXmlBuilder;
|
||||
try {
|
||||
YamlXmlBuilder = require('../../lib/yaml-xml-builder').YamlXmlBuilder;
|
||||
} catch {
|
||||
YamlXmlBuilder = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape XML special characters
|
||||
*/
|
||||
function escapeXml(text) {
|
||||
if (!text) return '';
|
||||
return text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
|
||||
}
|
||||
const { escapeXml } = require('../../../lib/xml-utils');
|
||||
|
||||
/**
|
||||
* Build frontmatter for agent
|
||||
|
||||
@@ -17,7 +17,7 @@ const { extractInstallConfig, getDefaultValues } = require('./template-engine');
|
||||
*/
|
||||
function findBmadConfig(startPath = process.cwd()) {
|
||||
// Look for common BMAD folder names
|
||||
const possibleNames = ['.bmad', 'bmad', '.bmad-method'];
|
||||
const possibleNames = ['_bmad'];
|
||||
|
||||
for (const name of possibleNames) {
|
||||
const configPath = path.join(startPath, name, 'bmb', 'config.yaml');
|
||||
@@ -42,7 +42,7 @@ function findBmadConfig(startPath = process.cwd()) {
|
||||
* @returns {string} Resolved path
|
||||
*/
|
||||
function resolvePath(pathStr, context) {
|
||||
return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}', context.bmadFolder);
|
||||
return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}', context_bmadFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +273,7 @@ function installAgent(agentInfo, answers, targetPath, options = {}) {
|
||||
// Resolve path variables
|
||||
const resolvedSidecarFolder = agentSidecarFolder
|
||||
.replaceAll('{project-root}', options.projectRoot || process.cwd())
|
||||
.replaceAll('.bmad', options.bmadFolder || '.bmad');
|
||||
.replaceAll('_bmad', options_bmadFolder || '_bmad');
|
||||
|
||||
// Create sidecar directory for this agent
|
||||
const agentSidecarDir = path.join(resolvedSidecarFolder, agentFolderName);
|
||||
@@ -407,7 +407,7 @@ function detectBmadProject(targetPath) {
|
||||
|
||||
// Walk up directory tree looking for BMAD installation
|
||||
while (checkPath !== root) {
|
||||
const possibleNames = ['.bmad'];
|
||||
const possibleNames = ['_bmad'];
|
||||
for (const name of possibleNames) {
|
||||
const bmadFolder = path.join(checkPath, name);
|
||||
const cfgFolder = path.join(bmadFolder, '_cfg');
|
||||
@@ -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', '_cfg', 'manifest.yaml');
|
||||
let installedIdes = ['claude-code']; // Default to Claude Code if no manifest
|
||||
|
||||
if (fs.existsSync(manifestPath)) {
|
||||
|
||||
@@ -136,7 +136,7 @@ class UI {
|
||||
// Create the bmad directory based on core config
|
||||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const bmadFolderName = '.bmad';
|
||||
const bmadFolderName = '_bmad';
|
||||
const bmadDir = path.join(confirmedDirectory, bmadFolderName);
|
||||
|
||||
await fs.ensureDir(bmadDir);
|
||||
@@ -535,7 +535,7 @@ class UI {
|
||||
// Show backup info and restore command
|
||||
console.log('\n' + chalk.white.bold('Backups & Recovery:\n'));
|
||||
console.log(chalk.dim(' Pre-injection backups are stored in:'));
|
||||
console.log(chalk.cyan(' ~/.bmad-tts-backups/\n'));
|
||||
console.log(chalk.cyan(' ~/_bmad-tts-backups/\n'));
|
||||
console.log(chalk.dim(' To restore original files (removes TTS instructions):'));
|
||||
console.log(chalk.cyan(` bmad-tts-injector.sh --restore ${result.path}\n`));
|
||||
|
||||
|
||||
@@ -44,18 +44,7 @@ class XmlHandler {
|
||||
* @returns {Object} Parsed activation block
|
||||
*/
|
||||
async loadActivationTemplate() {
|
||||
const templatePath = getSourcePath('utility', 'models', 'agent-activation-ide.xml');
|
||||
|
||||
try {
|
||||
const xmlContent = await fs.readFile(templatePath, 'utf8');
|
||||
|
||||
// Parse the XML directly (file is now pure XML)
|
||||
const parsed = await this.parser.parseStringPromise(xmlContent);
|
||||
return parsed.activation;
|
||||
} catch (error) {
|
||||
console.error('Failed to load activation template:', error);
|
||||
return null;
|
||||
}
|
||||
console.error('Failed to load activation template:', error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,51 +125,10 @@ class XmlHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple string-based injection (fallback method for legacy XML agents)
|
||||
* This preserves formatting better than XML parsing
|
||||
* TODO: DELETE THIS METHOD
|
||||
*/
|
||||
injectActivationSimple(agentContent, metadata = {}) {
|
||||
// Check if already has activation
|
||||
if (agentContent.includes('<activation')) {
|
||||
return agentContent;
|
||||
}
|
||||
|
||||
// Load template file
|
||||
const templatePath = getSourcePath('utility', 'models', 'agent-activation-ide.xml');
|
||||
|
||||
try {
|
||||
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
||||
|
||||
// The file is now pure XML, use it directly with proper indentation
|
||||
// Add 2 spaces of indentation for insertion into agent
|
||||
let activationBlock = templateContent
|
||||
.split('\n')
|
||||
.map((line) => (line ? ' ' + line : ''))
|
||||
.join('\n');
|
||||
|
||||
// Replace {agent-filename} with actual filename if metadata provided
|
||||
if (metadata.module && metadata.name) {
|
||||
const agentFilename = `${metadata.module}-${metadata.name}.md`;
|
||||
activationBlock = activationBlock.replace('{agent-filename}', agentFilename);
|
||||
}
|
||||
|
||||
// Find where to insert (after <agent> tag)
|
||||
const agentMatch = agentContent.match(/(<agent[^>]*>)/);
|
||||
if (!agentMatch) {
|
||||
return agentContent;
|
||||
}
|
||||
|
||||
const insertPos = agentMatch.index + agentMatch[0].length;
|
||||
|
||||
// Insert the activation block
|
||||
const before = agentContent.slice(0, insertPos);
|
||||
const after = agentContent.slice(insertPos);
|
||||
|
||||
return before + '\n' + activationBlock + after;
|
||||
} catch (error) {
|
||||
console.error('Error in simple injection:', error);
|
||||
return agentContent;
|
||||
}
|
||||
console.error('Error in simple injection:', error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ const path = require('node:path');
|
||||
const crypto = require('node:crypto');
|
||||
const { AgentAnalyzer } = require('./agent-analyzer');
|
||||
const { ActivationBuilder } = require('./activation-builder');
|
||||
const { escapeXml } = require('../../lib/xml-utils');
|
||||
|
||||
/**
|
||||
* Converts agent YAML files to XML format with smart activation injection
|
||||
@@ -241,15 +242,15 @@ class YamlXmlBuilder {
|
||||
let xml = ' <persona>\n';
|
||||
|
||||
if (persona.role) {
|
||||
xml += ` <role>${this.escapeXml(persona.role)}</role>\n`;
|
||||
xml += ` <role>${escapeXml(persona.role)}</role>\n`;
|
||||
}
|
||||
|
||||
if (persona.identity) {
|
||||
xml += ` <identity>${this.escapeXml(persona.identity)}</identity>\n`;
|
||||
xml += ` <identity>${escapeXml(persona.identity)}</identity>\n`;
|
||||
}
|
||||
|
||||
if (persona.communication_style) {
|
||||
xml += ` <communication_style>${this.escapeXml(persona.communication_style)}</communication_style>\n`;
|
||||
xml += ` <communication_style>${escapeXml(persona.communication_style)}</communication_style>\n`;
|
||||
}
|
||||
|
||||
if (persona.principles) {
|
||||
@@ -260,7 +261,7 @@ class YamlXmlBuilder {
|
||||
} else {
|
||||
principlesText = persona.principles;
|
||||
}
|
||||
xml += ` <principles>${this.escapeXml(principlesText)}</principles>\n`;
|
||||
xml += ` <principles>${escapeXml(principlesText)}</principles>\n`;
|
||||
}
|
||||
|
||||
xml += ' </persona>\n';
|
||||
@@ -277,7 +278,7 @@ class YamlXmlBuilder {
|
||||
let xml = ' <memories>\n';
|
||||
|
||||
for (const memory of memories) {
|
||||
xml += ` <memory>${this.escapeXml(memory)}</memory>\n`;
|
||||
xml += ` <memory>${escapeXml(memory)}</memory>\n`;
|
||||
}
|
||||
|
||||
xml += ' </memories>\n';
|
||||
@@ -314,7 +315,7 @@ class YamlXmlBuilder {
|
||||
for (const prompt of promptsArray) {
|
||||
xml += ` <prompt id="${prompt.id || ''}">\n`;
|
||||
xml += ` <content>\n`;
|
||||
xml += `${this.escapeXml(prompt.content || '')}\n`;
|
||||
xml += `${escapeXml(prompt.content || '')}\n`;
|
||||
xml += ` </content>\n`;
|
||||
xml += ` </prompt>\n`;
|
||||
}
|
||||
@@ -351,7 +352,7 @@ class YamlXmlBuilder {
|
||||
|
||||
// Handle multi format menu items with nested handlers
|
||||
if (item.multi && item.triggers && Array.isArray(item.triggers)) {
|
||||
xml += ` <item type="multi">${this.escapeXml(item.multi)}\n`;
|
||||
xml += ` <item type="multi">${escapeXml(item.multi)}\n`;
|
||||
xml += this.buildNestedHandlers(item.triggers);
|
||||
xml += ` </item>\n`;
|
||||
}
|
||||
@@ -381,7 +382,7 @@ class YamlXmlBuilder {
|
||||
if (item.data) attrs.push(`data="${item.data}"`);
|
||||
if (item.action) attrs.push(`action="${item.action}"`);
|
||||
|
||||
xml += ` <item ${attrs.join(' ')}>${this.escapeXml(item.description || '')}</item>\n`;
|
||||
xml += ` <item ${attrs.join(' ')}>${escapeXml(item.description || '')}</item>\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,7 +413,7 @@ class YamlXmlBuilder {
|
||||
|
||||
// For nested handlers in multi items, we don't need cmd attribute
|
||||
// The match attribute will handle fuzzy matching
|
||||
const attrs = [`match="${this.escapeXml(execData.description || '')}"`];
|
||||
const attrs = [`match="${escapeXml(execData.description || '')}"`];
|
||||
|
||||
// Add handler attributes based on exec data
|
||||
if (execData.route) attrs.push(`exec="${execData.route}"`);
|
||||
@@ -482,19 +483,6 @@ class YamlXmlBuilder {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape XML special characters
|
||||
*/
|
||||
escapeXml(text) {
|
||||
if (!text) return '';
|
||||
return text
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll("'", ''');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate file hash for build tracking
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user