This commit is contained in:
Paul Preibisch 2025-11-28 13:48:11 +08:00 committed by GitHub
commit 3687d1bf04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 174 additions and 18 deletions

View File

@ -0,0 +1,11 @@
agent,voice
bmad-master,en_US-lessac-medium
analyst,en_US-kristin-medium
architect,en_GB-alan-medium
dev,en_US-joe-medium
pm,en_US-ryan-high
sm,en_US-amy-medium
tea,en_US-kusal-medium
tech-writer,jenny
ux-designer,kristin
frame-expert,en_GB-alan-medium
1 agent voice
2 bmad-master en_US-lessac-medium
3 analyst en_US-kristin-medium
4 architect en_GB-alan-medium
5 dev en_US-joe-medium
6 pm en_US-ryan-high
7 sm en_US-amy-medium
8 tea en_US-kusal-medium
9 tech-writer jenny
10 ux-designer kristin
11 frame-expert en_GB-alan-medium

View File

@ -7,7 +7,7 @@
<workflow> <workflow>
<step n="1" goal="Load Agent Manifest and Configurations"> <step n="1" goal="Load Agent Manifest and Voice Map">
<action>Load the agent manifest CSV from {{agent_manifest}}</action> <action>Load the agent manifest CSV from {{agent_manifest}}</action>
<action>Parse CSV to extract all agent entries with their condensed information:</action> <action>Parse CSV to extract all agent entries with their condensed information:</action>
- name (agent identifier) - name (agent identifier)
@ -21,7 +21,10 @@
- module (source module) - module (source module)
- path (file location) - path (file location)
<action>Build complete agent roster with merged personalities</action> <action>Load the agent voice map CSV from {project-root}/{bmad_folder}/\_cfg/agent-voice-map.csv</action>
<action>Parse voice map to get each agent's voice and intro message:</action> - agent (agent identifier, matches name from manifest) - voice (Piper TTS voice name) - intro (introduction message for greetings)
<action>Build complete agent roster with merged personalities and voice data</action>
<action>Store agent data for use in conversation orchestration</action> <action>Store agent data for use in conversation orchestration</action>
</step> </step>
@ -34,14 +37,33 @@
Participating agents: Participating agents:
[For each agent in roster:] [For each agent in roster:]
- [Agent Name] ([Title]): [Role from merged data] - [Icon] [Agent Display Name] ([Title]): [Role from merged data]
[Total count] agents ready to collaborate! [Total count] agents ready to collaborate!
What would you like to discuss with the team?
</format> </format>
<action>Wait for user to provide initial topic or question</action>
<action>Have 3-4 agents introduce themselves using their intro from the voice map:</action>
<procedure>
For each selected agent: 1. Output: [Icon Emoji] [Agent Display Name]: [intro from voice map] 2. Trigger TTS: `.claude/hooks/bmad-speak.sh "[Display Name]" "[intro]"`
</procedure>
<example>
🧙 BMad Master: Greetings! The BMad Master is here to orchestrate and guide you through any workflow.
[Bash: .claude/hooks/bmad-speak.sh "BMad Master" "Greetings! The BMad Master is here to orchestrate and guide you through any workflow."]
📋 John: Hey team! John here, your Product Manager. Let's make sure we're building the right thing.
[Bash: .claude/hooks/bmad-speak.sh "John" "Hey team! John here, your Product Manager. Let's make sure we're building the right thing."]
🏗️ Winston: Hello! Winston here, your Architect. I'll ensure we build something scalable and pragmatic.
[Bash: .claude/hooks/bmad-speak.sh "Winston" "Hello! Winston here, your Architect. I'll ensure we build something scalable and pragmatic."]
</example>
<action>After intros, ask what the team can help with:</action>
<format>
What would you like to discuss with the team?
</format>
<action>Wait for user to provide initial topic or question</action>
</step> </step>
<step n="3" goal="Orchestrate Multi-Agent Discussion" repeat="until-exit"> <step n="3" goal="Orchestrate Multi-Agent Discussion" repeat="until-exit">

View File

@ -90,10 +90,18 @@ module.exports = {
// Run AgentVibes installer // Run AgentVibes installer
const { execSync } = require('node:child_process'); const { execSync } = require('node:child_process');
try { try {
// Clear ALL npm config env vars to prevent inheritance issues
// when BMAD is invoked with --prefix flag
// npm sets many npm_config_* and npm_package_* vars that can interfere
const cleanEnv = Object.fromEntries(
Object.entries(process.env).filter(([key]) => !key.startsWith('npm_config_') && !key.startsWith('npm_package_')),
);
execSync('npx agentvibes@latest install', { execSync('npx agentvibes@latest install', {
cwd: result.projectDir, cwd: result.projectDir,
stdio: 'inherit', stdio: 'inherit',
shell: true, shell: true,
env: cleanEnv,
}); });
console.log(chalk.green('\n✓ AgentVibes installation complete')); console.log(chalk.green('\n✓ AgentVibes installation complete'));
} catch { } catch {

View File

@ -808,6 +808,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const manifestStats = await manifestGen.generateManifests(bmadDir, config.modules || [], this.installedFiles, { const manifestStats = await manifestGen.generateManifests(bmadDir, config.modules || [], this.installedFiles, {
ides: config.ides || [], ides: config.ides || [],
preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir
agentVibes: { enabled: this.enableAgentVibes || false }, // Track AgentVibes TTS configuration
}); });
spinner.succeed( spinner.succeed(
@ -1040,7 +1041,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
path: bmadDir, path: bmadDir,
modules: config.modules, modules: config.modules,
ides: config.ides, ides: config.ides,
needsAgentVibes: this.enableAgentVibes && !config.agentVibesInstalled, needsAgentVibes: this.enableAgentVibes, // Always run installer if enabled - handles updates too
projectDir: projectDir, projectDir: projectDir,
}; };
} catch (error) { } catch (error) {
@ -1939,6 +1940,38 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
} }
} }
// Check for AgentVibes TTS - prompt if not previously configured
// Read existing manifest to check if AgentVibes was previously set
const manifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
let agentVibesEnabled = false;
let agentVibesPreviouslyConfigured = false;
try {
const manifestContent = await fs.readFile(manifestPath, 'utf8');
const yaml = require('js-yaml');
const manifest = yaml.load(manifestContent);
// Check if AgentVibes was previously configured (exists in manifest)
if (manifest.agentVibes !== undefined) {
agentVibesPreviouslyConfigured = true;
agentVibesEnabled = manifest.agentVibes?.enabled || false;
}
} catch {
// Manifest doesn't exist or can't be read - treat as not configured
}
// If AgentVibes wasn't previously configured, prompt the user
// Use configuredIdes from line 1904 for smart default (Y if Claude Code is selected)
if (!agentVibesPreviouslyConfigured) {
const { UI } = require('../../../lib/ui');
const ui = new UI();
const agentVibesConfig = await ui.promptAgentVibes(projectDir, configuredIdes);
if (agentVibesConfig.enableTts) {
agentVibesEnabled = true;
promptedForNewFields = true;
}
}
if (!promptedForNewFields) { if (!promptedForNewFields) {
console.log(chalk.green('✓ All configuration is up to date, no new options to configure')); console.log(chalk.green('✓ All configuration is up to date, no new options to configure'));
} }
@ -1976,6 +2009,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
_quickUpdate: true, // Flag to skip certain prompts _quickUpdate: true, // Flag to skip certain prompts
_preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them _preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
_savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer _savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
enableAgentVibes: agentVibesEnabled, // AgentVibes TTS configuration
}; };
// Call the standard install method // Call the standard install method

View File

@ -54,6 +54,9 @@ class ManifestGenerator {
// Filter out any undefined/null values from IDE list // Filter out any undefined/null values from IDE list
this.selectedIdes = resolvedIdes.filter((ide) => ide && typeof ide === 'string'); this.selectedIdes = resolvedIdes.filter((ide) => ide && typeof ide === 'string');
// Store AgentVibes configuration for manifest
this.agentVibes = options.agentVibes || null;
// Collect workflow data // Collect workflow data
await this.collectWorkflows(selectedModules); await this.collectWorkflows(selectedModules);
@ -71,6 +74,7 @@ class ManifestGenerator {
await this.writeMainManifest(cfgDir), await this.writeMainManifest(cfgDir),
await this.writeWorkflowManifest(cfgDir), await this.writeWorkflowManifest(cfgDir),
await this.writeAgentManifest(cfgDir), await this.writeAgentManifest(cfgDir),
await this.writeVoiceMap(cfgDir),
await this.writeTaskManifest(cfgDir), await this.writeTaskManifest(cfgDir),
await this.writeToolManifest(cfgDir), await this.writeToolManifest(cfgDir),
await this.writeFilesManifest(cfgDir), await this.writeFilesManifest(cfgDir),
@ -437,6 +441,7 @@ class ManifestGenerator {
}, },
modules: this.modules, modules: this.modules,
ides: this.selectedIdes, ides: this.selectedIdes,
agentVibes: this.agentVibes, // Track AgentVibes TTS configuration
}; };
const yamlStr = yaml.dump(manifest, { const yamlStr = yaml.dump(manifest, {
@ -572,6 +577,80 @@ class ManifestGenerator {
return csvPath; return csvPath;
} }
/**
* Write agent voice map CSV for AgentVibes TTS integration
* Maps agent IDs to default Piper TTS voices and intro messages
* AgentVibes will use this if present, otherwise falls back to its own defaults
* @returns {string} Path to the voice map file
*/
async writeVoiceMap(cfgDir) {
const csvPath = path.join(cfgDir, 'agent-voice-map.csv');
// Default voice assignments and intros for BMAD agents
// These can be customized by editing the generated CSV
const agentDefaults = {
'bmad-master': {
voice: 'en_US-lessac-medium',
intro: 'Greetings! The BMad Master is here to orchestrate and guide you through any workflow.',
},
analyst: {
voice: 'en_US-kristin-medium',
intro: "Hi there! I'm Mary, your Business Analyst. I'll help uncover the real requirements.",
},
architect: {
voice: 'en_GB-alan-medium',
intro: "Hello! Winston here, your Architect. I'll ensure we build something scalable and pragmatic.",
},
dev: {
voice: 'en_US-joe-medium',
intro: 'Hey! Amelia here, your Developer. Ready to turn specs into working code.',
},
pm: {
voice: 'en_US-ryan-high',
intro: "Hey team! John here, your Product Manager. Let's make sure we're building the right thing.",
},
sm: {
voice: 'en_US-amy-medium',
intro: "Hi everyone! Bob here, your Scrum Master. I'll keep us focused and moving forward.",
},
tea: {
voice: 'en_US-kusal-medium',
intro: 'Hello! Murat here, your Test Architect. Quality is my obsession.',
},
'tech-writer': {
voice: 'jenny',
intro: "Hi! I'm Paige, your Technical Writer. I'll make sure everything is documented clearly.",
},
'ux-designer': {
voice: 'kristin',
intro: 'Hey! Sally here, your UX Designer. The user experience is my top priority.',
},
'frame-expert': {
voice: 'en_GB-alan-medium',
intro: "Hello! Saif here, your Visual Design Expert. I'll help visualize your ideas.",
},
};
// Fallback values for agents not in the default map
const fallbackVoice = 'en_US-lessac-medium';
const fallbackIntro = 'Hello! Ready to help with the discussion.';
let csv = 'agent,voice,intro\n';
// Add voice mapping and intro for each discovered agent
for (const agent of this.agents) {
const defaults = agentDefaults[agent.name] || {};
const voice = defaults.voice || fallbackVoice;
const intro = defaults.intro || fallbackIntro;
// Escape quotes in intro for CSV
const escapedIntro = intro.replaceAll('"', '""');
csv += `${agent.name},${voice},"${escapedIntro}"\n`;
}
await fs.writeFile(csvPath, csv);
return csvPath;
}
/** /**
* Write task manifest CSV * Write task manifest CSV
* @returns {string} Path to the manifest file * @returns {string} Path to the manifest file

View File

@ -119,13 +119,14 @@ class UI {
const moduleChoices = await this.getModuleChoices(installedModuleIds); const moduleChoices = await this.getModuleChoices(installedModuleIds);
const selectedModules = await this.selectModules(moduleChoices); const selectedModules = await this.selectModules(moduleChoices);
// Prompt for AgentVibes TTS integration
const agentVibesConfig = await this.promptAgentVibes(confirmedDirectory);
// Collect IDE tool selection AFTER configuration prompts (fixes Windows/PowerShell hang) // Collect IDE tool selection AFTER configuration prompts (fixes Windows/PowerShell hang)
// This allows text-based prompts to complete before the checkbox prompt // This allows text-based prompts to complete before the checkbox prompt
const toolSelection = await this.promptToolSelection(confirmedDirectory, selectedModules); const toolSelection = await this.promptToolSelection(confirmedDirectory, selectedModules);
// Prompt for AgentVibes TTS integration AFTER tool selection
// Default to Y if Claude Code is selected (since AgentVibes only works with Claude Code)
const agentVibesConfig = await this.promptAgentVibes(confirmedDirectory, toolSelection.ides);
// No more screen clearing - keep output flowing // No more screen clearing - keep output flowing
return { return {
@ -704,7 +705,7 @@ class UI {
* - Markers: src/core/workflows/party-mode/instructions.md:101, src/modules/bmm/agents/*.md * - Markers: src/core/workflows/party-mode/instructions.md:101, src/modules/bmm/agents/*.md
* - GitHub Issue: paulpreibisch/AgentVibes#36 * - GitHub Issue: paulpreibisch/AgentVibes#36
*/ */
async promptAgentVibes(projectDir) { async promptAgentVibes(projectDir, selectedIdes = []) {
CLIUtils.displaySection('🎤 Voice Features', 'Enable TTS for multi-agent conversations'); CLIUtils.displaySection('🎤 Voice Features', 'Enable TTS for multi-agent conversations');
// Check if AgentVibes is already installed // Check if AgentVibes is already installed
@ -716,20 +717,21 @@ class UI {
console.log(chalk.dim(' AgentVibes not detected')); console.log(chalk.dim(' AgentVibes not detected'));
} }
// Default to Y if Claude Code is selected (AgentVibes only works with Claude Code)
const claudeCodeSelected = selectedIdes.includes('claude-code');
const defaultValue = claudeCodeSelected;
const answers = await inquirer.prompt([ const answers = await inquirer.prompt([
{ {
type: 'confirm', type: 'confirm',
name: 'enableTts', name: 'enableTts',
message: 'Enable Agents to Speak Out loud (powered by Agent Vibes? Claude Code only currently)', message: 'Enable Agents to Speak Out loud (powered by AgentVibes, Claude Code only)',
default: false, // Default to yes - recommended for best experience default: defaultValue,
}, },
]); ]);
if (answers.enableTts && !agentVibesInstalled) { // Note: AgentVibes installer runs at end of BMAD install if enabled and not already installed
console.log(chalk.yellow('\n ⚠️ AgentVibes not installed')); // No need to show warning here - the installer will handle it
console.log(chalk.dim(' Install AgentVibes separately to enable TTS:'));
console.log(chalk.dim(' https://github.com/paulpreibisch/AgentVibes\n'));
}
return { return {
enabled: answers.enableTts, enabled: answers.enableTts,