const xml2js = require('xml2js'); const fs = require('fs-extra'); const path = require('node:path'); const { getProjectRoot, getSourcePath } = require('./project-root'); /** * XML utility functions for BMAD installer */ class XmlHandler { constructor() { this.parser = new xml2js.Parser({ preserveChildrenOrder: true, explicitChildren: true, explicitArray: false, trim: false, normalizeTags: false, attrkey: '$', charkey: '_', }); this.builder = new xml2js.Builder({ renderOpts: { pretty: true, indent: ' ', newline: '\n', }, xmldec: { version: '1.0', encoding: 'utf8', standalone: false, }, headless: true, // Don't add XML declaration attrkey: '$', charkey: '_', }); } /** * Load and parse the activation template * @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; } } /** * Inject activation block into agent XML content * @param {string} agentContent - The agent file content * @param {Object} metadata - Metadata containing module and name * @returns {string} Modified content with activation block */ async injectActivation(agentContent, metadata = {}) { try { // Check if already has activation if (agentContent.includes(' { if (line.trim() === '') return line; if (line.startsWith(' (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 tag) const agentMatch = agentContent.match(/(]*>)/); 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; } } } module.exports = { XmlHandler };