mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-17 09:45:25 +00:00
feat(installer): Enhanced TTS injection summary with tracking and documentation (#1037)
## Summary
- Track all files with TTS injection applied during installation
- Display informative summary explaining what TTS injection does
- Show backup location and restore command for recovery
## What is TTS Injection?
TTS (Text-to-Speech) injection adds voice instructions to BMAD agents,
enabling them to speak their responses aloud using AgentVibes.
Example: When you activate the PM agent, it will greet you with
spoken audio like "Hey! I'm your Project Manager. How can I help?"
## Changes
- **installer.js**: Track files in `processAgentFiles()`, `buildStandaloneAgents()`,
and `rebuildAgentFiles()` when TTS markers are processed
- **compiler.js**: Add TTS injection support for custom agent compilation
- **ui.js**: Enhanced installation summary showing:
- Explanation of what TTS injection is with example
- List of all files with TTS injection applied (grouped by type)
- Backup location (~/.bmad-tts-backups/)
- Restore command for recovery
## Example Output
```
═══════════════════════════════════════════════════
AgentVibes TTS Injection Summary
═══════════════════════════════════════════════════
What is TTS Injection?
TTS (Text-to-Speech) injection adds voice instructions to BMAD agents,
enabling them to speak their responses aloud using AgentVibes.
Example: When you activate the PM agent, it will greet you with
spoken audio like "Hey! I'm your Project Manager. How can I help?"
✅ TTS injection applied to 11 file(s):
Party Mode (multi-agent conversations):
• .bmad/core/workflows/party-mode/instructions.md
Agent TTS (individual agent voices):
• .bmad/bmm/agents/analyst.md
• .bmad/bmm/agents/architect.md
...
Backups & Recovery:
Pre-injection backups are stored in:
~/.bmad-tts-backups/
To restore original files (removes TTS instructions):
bmad-tts-injector.sh --restore /path/to/.bmad
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Paul Preibisch <paul@paulpreibisch.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f99e192e74
commit
8265bbf295
@ -51,6 +51,7 @@ class Installer {
|
||||
this.configCollector = new ConfigCollector();
|
||||
this.ideConfigManager = new IdeConfigManager();
|
||||
this.installedFiles = []; // Track all installed files
|
||||
this.ttsInjectedFiles = []; // Track files with TTS injection applied
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,8 +147,8 @@ class Installer {
|
||||
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
|
||||
}
|
||||
|
||||
// Process AgentVibes injection points
|
||||
content = this.processTTSInjectionPoints(content);
|
||||
// Process AgentVibes injection points (pass targetPath for tracking)
|
||||
content = this.processTTSInjectionPoints(content, targetPath);
|
||||
|
||||
// Write to target with replaced content
|
||||
await fs.ensureDir(path.dirname(targetPath));
|
||||
@ -226,10 +227,14 @@ class Installer {
|
||||
* - src/modules/bmm/agents/*.md (rules sections)
|
||||
* - TTS Hook: .claude/hooks/bmad-speak.sh (in AgentVibes repo)
|
||||
*/
|
||||
processTTSInjectionPoints(content) {
|
||||
processTTSInjectionPoints(content, targetPath = null) {
|
||||
// Check if AgentVibes is enabled (set during installation configuration)
|
||||
const enableAgentVibes = this.enableAgentVibes || false;
|
||||
|
||||
// Check if content contains any TTS injection markers
|
||||
const hasPartyMode = content.includes('<!-- TTS_INJECTION:party-mode -->');
|
||||
const hasAgentTTS = content.includes('<!-- TTS_INJECTION:agent-tts -->');
|
||||
|
||||
if (enableAgentVibes) {
|
||||
// Replace party-mode injection marker with actual TTS call
|
||||
// Use single quotes to prevent shell expansion of special chars like !
|
||||
@ -253,6 +258,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
IMPORTANT: Use single quotes as shown - do NOT escape special characters like ! or $ inside single quotes
|
||||
Run in background (&) to avoid blocking`,
|
||||
);
|
||||
|
||||
// Track files that had TTS injection applied
|
||||
if (targetPath && (hasPartyMode || hasAgentTTS)) {
|
||||
const injectionType = hasPartyMode ? 'party-mode' : 'agent-tts';
|
||||
this.ttsInjectedFiles.push({ path: targetPath, type: injectionType });
|
||||
}
|
||||
} else {
|
||||
// Strip injection markers cleanly when AgentVibes is disabled
|
||||
content = content.replaceAll(/<!-- TTS_INJECTION:party-mode -->\n?/g, '');
|
||||
@ -1021,6 +1032,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
modules: config.modules,
|
||||
ides: config.ides,
|
||||
customFiles: customFiles.length > 0 ? customFiles : undefined,
|
||||
ttsInjectedFiles: this.enableAgentVibes && this.ttsInjectedFiles.length > 0 ? this.ttsInjectedFiles : undefined,
|
||||
agentVibesEnabled: this.enableAgentVibes || false,
|
||||
});
|
||||
|
||||
// Offer cleanup for legacy files (only for updates, not fresh installs, and only if not skipped)
|
||||
@ -1526,13 +1539,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
|
||||
// Build YAML + customize to .md
|
||||
const customizeExists = await fs.pathExists(customizePath);
|
||||
const xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
|
||||
let xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
|
||||
includeMetadata: true,
|
||||
});
|
||||
|
||||
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
||||
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
||||
|
||||
// Process TTS injection points (pass targetPath for tracking)
|
||||
xmlContent = this.processTTSInjectionPoints(xmlContent, mdPath);
|
||||
|
||||
// Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
|
||||
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
||||
await fs.writeFile(mdPath, content, 'utf8');
|
||||
@ -1628,13 +1644,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
}
|
||||
|
||||
// Build YAML to XML .md
|
||||
const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
||||
let xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
||||
includeMetadata: true,
|
||||
});
|
||||
|
||||
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
||||
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
||||
|
||||
// Process TTS injection points (pass targetPath for tracking)
|
||||
xmlContent = this.processTTSInjectionPoints(xmlContent, targetMdPath);
|
||||
|
||||
// Write the built .md file with POSIX-compliant final newline
|
||||
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
||||
await fs.writeFile(targetMdPath, content, 'utf8');
|
||||
@ -1722,13 +1741,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
}
|
||||
|
||||
// Build YAML + customize to .md
|
||||
const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
||||
let xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
||||
includeMetadata: true,
|
||||
});
|
||||
|
||||
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
||||
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
||||
|
||||
// Process TTS injection points (pass targetPath for tracking)
|
||||
xmlContent = this.processTTSInjectionPoints(xmlContent, targetMdPath);
|
||||
|
||||
// Write the rebuilt .md file with POSIX-compliant final newline
|
||||
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
||||
await fs.writeFile(targetMdPath, content, 'utf8');
|
||||
|
||||
@ -482,10 +482,39 @@ function compileAgent(yamlContent, answers = {}, agentName = '', targetPath = ''
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process TTS injection markers in content
|
||||
* @param {string} content - Content to process
|
||||
* @param {boolean} enableAgentVibes - Whether AgentVibes is enabled
|
||||
* @returns {Object} { content: string, hadInjection: boolean }
|
||||
*/
|
||||
function processTTSInjectionPoints(content, enableAgentVibes) {
|
||||
const hasAgentTTS = content.includes('<!-- TTS_INJECTION:agent-tts -->');
|
||||
|
||||
if (enableAgentVibes && hasAgentTTS) {
|
||||
// Replace agent-tts injection marker with TTS rule
|
||||
content = content.replaceAll(
|
||||
'<!-- TTS_INJECTION:agent-tts -->',
|
||||
`- When responding to user messages, speak your responses using TTS:
|
||||
Call: \`.claude/hooks/bmad-speak.sh '{agent-id}' '{response-text}'\` after each response
|
||||
Replace {agent-id} with YOUR agent ID from <agent id="..."> tag at top of this file
|
||||
Replace {response-text} with the text you just output to the user
|
||||
IMPORTANT: Use single quotes as shown - do NOT escape special characters like ! or $ inside single quotes
|
||||
Run in background (&) to avoid blocking`,
|
||||
);
|
||||
return { content, hadInjection: true };
|
||||
} else if (!enableAgentVibes && hasAgentTTS) {
|
||||
// Strip injection markers when disabled
|
||||
content = content.replaceAll(/<!-- TTS_INJECTION:agent-tts -->\n?/g, '');
|
||||
}
|
||||
|
||||
return { content, hadInjection: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile agent file to .md
|
||||
* @param {string} yamlPath - Path to agent YAML file
|
||||
* @param {Object} options - { answers: {}, outputPath: string }
|
||||
* @param {Object} options - { answers: {}, outputPath: string, enableAgentVibes: boolean }
|
||||
* @returns {Object} Compilation result
|
||||
*/
|
||||
function compileAgentFile(yamlPath, options = {}) {
|
||||
@ -501,13 +530,24 @@ function compileAgentFile(yamlPath, options = {}) {
|
||||
outputPath = path.join(dir, `${basename}.md`);
|
||||
}
|
||||
|
||||
// Process TTS injection points if enableAgentVibes option is provided
|
||||
let xml = result.xml;
|
||||
let ttsInjected = false;
|
||||
if (options.enableAgentVibes !== undefined) {
|
||||
const ttsResult = processTTSInjectionPoints(xml, options.enableAgentVibes);
|
||||
xml = ttsResult.content;
|
||||
ttsInjected = ttsResult.hadInjection;
|
||||
}
|
||||
|
||||
// Write compiled XML
|
||||
fs.writeFileSync(outputPath, result.xml, 'utf8');
|
||||
fs.writeFileSync(outputPath, xml, 'utf8');
|
||||
|
||||
return {
|
||||
...result,
|
||||
xml,
|
||||
outputPath,
|
||||
sourcePath: yamlPath,
|
||||
ttsInjected,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -363,11 +363,60 @@ class UI {
|
||||
`🔧 Tools Configured: ${result.ides?.length > 0 ? result.ides.join(', ') : 'none'}`,
|
||||
];
|
||||
|
||||
// Add AgentVibes TTS info if enabled
|
||||
if (result.agentVibesEnabled) {
|
||||
summary.push(`🎤 AgentVibes TTS: Enabled`);
|
||||
}
|
||||
|
||||
CLIUtils.displayBox(summary.join('\n\n'), {
|
||||
borderColor: 'green',
|
||||
borderStyle: 'round',
|
||||
});
|
||||
|
||||
// Display TTS injection details if present
|
||||
if (result.ttsInjectedFiles && result.ttsInjectedFiles.length > 0) {
|
||||
console.log('\n' + chalk.cyan.bold('═══════════════════════════════════════════════════'));
|
||||
console.log(chalk.cyan.bold(' AgentVibes TTS Injection Summary'));
|
||||
console.log(chalk.cyan.bold('═══════════════════════════════════════════════════\n'));
|
||||
|
||||
// Explain what TTS injection is
|
||||
console.log(chalk.white.bold('What is TTS Injection?\n'));
|
||||
console.log(chalk.dim(' TTS (Text-to-Speech) injection adds voice instructions to BMAD agents,'));
|
||||
console.log(chalk.dim(' enabling them to speak their responses aloud using AgentVibes.\n'));
|
||||
console.log(chalk.dim(' Example: When you activate the PM agent, it will greet you with'));
|
||||
console.log(chalk.dim(' spoken audio like "Hey! I\'m your Project Manager. How can I help?"\n'));
|
||||
|
||||
console.log(chalk.green(`✅ TTS injection applied to ${result.ttsInjectedFiles.length} file(s):\n`));
|
||||
|
||||
// Group by type
|
||||
const partyModeFiles = result.ttsInjectedFiles.filter((f) => f.type === 'party-mode');
|
||||
const agentTTSFiles = result.ttsInjectedFiles.filter((f) => f.type === 'agent-tts');
|
||||
|
||||
if (partyModeFiles.length > 0) {
|
||||
console.log(chalk.yellow(' Party Mode (multi-agent conversations):'));
|
||||
for (const file of partyModeFiles) {
|
||||
console.log(chalk.dim(` • ${file.path}`));
|
||||
}
|
||||
}
|
||||
|
||||
if (agentTTSFiles.length > 0) {
|
||||
console.log(chalk.yellow(' Agent TTS (individual agent voices):'));
|
||||
for (const file of agentTTSFiles) {
|
||||
console.log(chalk.dim(` • ${file.path}`));
|
||||
}
|
||||
}
|
||||
|
||||
// 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.dim(' To restore original files (removes TTS instructions):'));
|
||||
console.log(chalk.cyan(` bmad-tts-injector.sh --restore ${result.path}\n`));
|
||||
|
||||
console.log(chalk.cyan('💡 BMAD agents will now speak when activated!'));
|
||||
console.log(chalk.dim(' Ensure AgentVibes is installed: https://agentvibes.org'));
|
||||
}
|
||||
|
||||
console.log('\n' + chalk.green.bold('✨ BMAD is ready to use!'));
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user