diff --git a/.bmad/_cfg/agent-voice-map.csv b/.bmad/_cfg/agent-voice-map.csv new file mode 100644 index 00000000..18a20fe4 --- /dev/null +++ b/.bmad/_cfg/agent-voice-map.csv @@ -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 diff --git a/src/core/workflows/party-mode/instructions.md b/src/core/workflows/party-mode/instructions.md index 3289f0a7..959f5632 100644 --- a/src/core/workflows/party-mode/instructions.md +++ b/src/core/workflows/party-mode/instructions.md @@ -7,7 +7,7 @@ - + Load the agent manifest CSV from {{agent_manifest}} Parse CSV to extract all agent entries with their condensed information: - name (agent identifier) @@ -21,7 +21,10 @@ - module (source module) - path (file location) -Build complete agent roster with merged personalities +Load the agent voice map CSV from {project-root}/{bmad_folder}/\_cfg/agent-voice-map.csv +Parse voice map to get each agent's voice and intro message: - agent (agent identifier, matches name from manifest) - voice (Piper TTS voice name) - intro (introduction message for greetings) + +Build complete agent roster with merged personalities and voice data Store agent data for use in conversation orchestration @@ -34,14 +37,33 @@ Participating agents: [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! - What would you like to discuss with the team? - - Wait for user to provide initial topic or question + +Have 3-4 agents introduce themselves using their intro from the voice map: + +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]"` + + +šŸ§™ 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."] + + + +After intros, ask what the team can help with: + +What would you like to discuss with the team? + +Wait for user to provide initial topic or question diff --git a/tools/cli/commands/install.js b/tools/cli/commands/install.js index e7725338..3a603fd9 100644 --- a/tools/cli/commands/install.js +++ b/tools/cli/commands/install.js @@ -90,10 +90,18 @@ module.exports = { // Run AgentVibes installer const { execSync } = require('node:child_process'); 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', { cwd: result.projectDir, stdio: 'inherit', shell: true, + env: cleanEnv, }); console.log(chalk.green('\nāœ“ AgentVibes installation complete')); } catch { diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 6e6fbab9..99e3497f 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -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, { ides: config.ides || [], preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir + agentVibes: { enabled: this.enableAgentVibes || false }, // Track AgentVibes TTS configuration }); spinner.succeed( @@ -1040,7 +1041,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: path: bmadDir, modules: config.modules, ides: config.ides, - needsAgentVibes: this.enableAgentVibes && !config.agentVibesInstalled, + needsAgentVibes: this.enableAgentVibes, // Always run installer if enabled - handles updates too projectDir: projectDir, }; } 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) { 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 _preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them _savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer + enableAgentVibes: agentVibesEnabled, // AgentVibes TTS configuration }; // Call the standard install method diff --git a/tools/cli/installers/lib/core/manifest-generator.js b/tools/cli/installers/lib/core/manifest-generator.js index 1dbb8ea6..1b3be9ad 100644 --- a/tools/cli/installers/lib/core/manifest-generator.js +++ b/tools/cli/installers/lib/core/manifest-generator.js @@ -54,6 +54,9 @@ class ManifestGenerator { // Filter out any undefined/null values from IDE list this.selectedIdes = resolvedIdes.filter((ide) => ide && typeof ide === 'string'); + // Store AgentVibes configuration for manifest + this.agentVibes = options.agentVibes || null; + // Collect workflow data await this.collectWorkflows(selectedModules); @@ -71,6 +74,7 @@ class ManifestGenerator { await this.writeMainManifest(cfgDir), await this.writeWorkflowManifest(cfgDir), await this.writeAgentManifest(cfgDir), + await this.writeVoiceMap(cfgDir), await this.writeTaskManifest(cfgDir), await this.writeToolManifest(cfgDir), await this.writeFilesManifest(cfgDir), @@ -437,6 +441,7 @@ class ManifestGenerator { }, modules: this.modules, ides: this.selectedIdes, + agentVibes: this.agentVibes, // Track AgentVibes TTS configuration }; const yamlStr = yaml.dump(manifest, { @@ -572,6 +577,80 @@ class ManifestGenerator { 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 * @returns {string} Path to the manifest file diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index 32e8dfc0..34ee9bbf 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -119,13 +119,14 @@ class UI { const moduleChoices = await this.getModuleChoices(installedModuleIds); 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) // This allows text-based prompts to complete before the checkbox prompt 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 return { @@ -704,7 +705,7 @@ class UI { * - Markers: src/core/workflows/party-mode/instructions.md:101, src/modules/bmm/agents/*.md * - GitHub Issue: paulpreibisch/AgentVibes#36 */ - async promptAgentVibes(projectDir) { + async promptAgentVibes(projectDir, selectedIdes = []) { CLIUtils.displaySection('šŸŽ¤ Voice Features', 'Enable TTS for multi-agent conversations'); // Check if AgentVibes is already installed @@ -716,20 +717,21 @@ class UI { 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([ { type: 'confirm', name: 'enableTts', - message: 'Enable Agents to Speak Out loud (powered by Agent Vibes? Claude Code only currently)', - default: false, // Default to yes - recommended for best experience + message: 'Enable Agents to Speak Out loud (powered by AgentVibes, Claude Code only)', + default: defaultValue, }, ]); - if (answers.enableTts && !agentVibesInstalled) { - console.log(chalk.yellow('\n āš ļø AgentVibes not installed')); - console.log(chalk.dim(' Install AgentVibes separately to enable TTS:')); - console.log(chalk.dim(' https://github.com/paulpreibisch/AgentVibes\n')); - } + // Note: AgentVibes installer runs at end of BMAD install if enabled and not already installed + // No need to show warning here - the installer will handle it return { enabled: answers.enableTts,