folder rename from .bmad to _bmad

This commit is contained in:
Brian Madison
2025-12-13 16:22:34 +08:00
parent 0c873638ab
commit 25c79e3fe5
375 changed files with 1421 additions and 2745 deletions

View File

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

View File

@@ -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('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&apos;');
},
/**
* Apply config overrides to agent details
* @param {Object} details - Original agent details

View File

@@ -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('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
}
const { escapeXml } = require('../../../lib/xml-utils');
/**
* Build frontmatter for agent

View File

@@ -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)) {

View File

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

View File

@@ -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);
}
/**

View File

@@ -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('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&apos;');
}
/**
* Calculate file hash for build tracking
*/