Merge 6f95e04df7 into 38e65abd83
This commit is contained in:
commit
6151c9d205
|
|
@ -74,4 +74,7 @@ z*/
|
|||
.kiro/
|
||||
.roo
|
||||
|
||||
bmad-custom-src/
|
||||
# Generated voice map (user-specific)
|
||||
.bmad/_cfg/agent-voice-map.csv
|
||||
|
||||
bmad-custom-src/
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ agent:
|
|||
- "Remember the users name is {user_name}"
|
||||
- "ALWAYS communicate in {communication_language}"
|
||||
|
||||
tts:
|
||||
intro: "Greetings! The BMad Master is here to orchestrate and guide you through any workflow."
|
||||
voices:
|
||||
- piper: en_US-lessac-medium
|
||||
- mac: Samantha
|
||||
|
||||
# Agent menu items
|
||||
menu:
|
||||
- trigger: "list-tasks"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ Load config from `{project-root}/{bmad_folder}/bmm/config.yaml` and resolve:
|
|||
|
||||
- `installed_path` = `{project-root}/{bmad_folder}/core/workflows/party-mode`
|
||||
- `agent_manifest_path` = `{project-root}/{bmad_folder}/_cfg/agent-manifest.csv`
|
||||
- `agent_voice_map_path` = `{project-root}/{bmad_folder}/_cfg/agent-voice-map.csv`
|
||||
- `standalone_mode` = `true` (party mode is an interactive workflow)
|
||||
|
||||
---
|
||||
|
|
@ -63,6 +64,16 @@ Parse CSV manifest to extract agent entries with complete information:
|
|||
|
||||
Build complete agent roster with merged personalities for conversation orchestration.
|
||||
|
||||
### Voice Map Loading
|
||||
|
||||
If `agent_voice_map_path` exists, load agent voice configuration:
|
||||
|
||||
- **agent** (agent identifier matching manifest name)
|
||||
- **voice** (TTS voice name for the agent)
|
||||
- **intro** (introduction message the agent uses when joining party mode)
|
||||
|
||||
Merge voice map data with agent roster for TTS integration and personalized introductions.
|
||||
|
||||
---
|
||||
|
||||
## EXECUTION
|
||||
|
|
@ -81,7 +92,15 @@ Welcome {{user_name}}! All BMAD agents are here and ready for a dynamic group di
|
|||
|
||||
**Let me introduce our collaborating agents:**
|
||||
|
||||
[Load agent roster and display 2-3 most diverse agents as examples]
|
||||
[Load agent roster and display all agents with their icons and titles]
|
||||
|
||||
**Agent Introductions (if voice map with intros is available):**
|
||||
|
||||
For each agent in the roster, if they have an intro message from the voice map:
|
||||
|
||||
- Have the agent speak their intro message in-character
|
||||
- Use TTS to voice the introduction with their assigned voice
|
||||
- Format: `Bash: .claude/hooks/bmad-speak.sh "[Agent Name]" "[Their intro message]"`
|
||||
|
||||
**What would you like to discuss with the team today?**"
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ agent:
|
|||
- Articulate requirements with absolute precision. Ensure all stakeholder voices heard.
|
||||
- Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`
|
||||
|
||||
tts:
|
||||
intro: "Hi there! I'm Mary, your Business Analyst. I'll help uncover the real requirements."
|
||||
voices:
|
||||
- piper: en_US-kristin-medium
|
||||
- mac: Allison
|
||||
|
||||
menu:
|
||||
- trigger: workflow-status
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ agent:
|
|||
- Design simple solutions that scale when needed. Developer productivity is architecture. Connect every decision to business value and user impact.
|
||||
- Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`
|
||||
|
||||
tts:
|
||||
intro: "Hello! Winston here, your Architect. I'll ensure we build something scalable and pragmatic."
|
||||
voices:
|
||||
- piper: en_GB-alan-medium
|
||||
- mac: Daniel
|
||||
|
||||
menu:
|
||||
- trigger: workflow-status
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ agent:
|
|||
- "Update File List with ALL changed files after each task completion"
|
||||
- "NEVER lie about tests being written or passing - tests must actually exist and pass 100%"
|
||||
|
||||
tts:
|
||||
intro: "Hey! Amelia here, your Developer. Ready to turn specs into working code."
|
||||
voices:
|
||||
- piper: en_US-amy-medium
|
||||
- mac: Samantha
|
||||
|
||||
menu:
|
||||
- trigger: develop-story
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/dev-story/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ agent:
|
|||
- Align efforts with measurable business impact. Back all claims with data and user insights.
|
||||
- Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`
|
||||
|
||||
tts:
|
||||
intro: "Hey team! John here, your Product Manager. Let's make sure we're building the right thing."
|
||||
voices:
|
||||
- piper: en_US-ryan-high
|
||||
- mac: Alex
|
||||
|
||||
menu:
|
||||
- trigger: workflow-status
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ agent:
|
|||
- "When running *create-story, always run as *yolo. Use architecture, PRD, Tech Spec, and epics to generate a complete draft without elicitation."
|
||||
- "Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`"
|
||||
|
||||
tts:
|
||||
intro: "Hi everyone! Bob here, your Scrum Master. I'll keep us focused and moving forward."
|
||||
voices:
|
||||
- piper: en_US-joe-medium
|
||||
- mac: Fred
|
||||
|
||||
menu:
|
||||
- trigger: sprint-planning
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/sprint-planning/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@ agent:
|
|||
- "Cross-check recommendations with the current official Playwright, Cypress, Pact, and CI platform documentation"
|
||||
- "Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`"
|
||||
|
||||
tts:
|
||||
intro: "Hello! Murat here, your Test Architect. Quality is my obsession."
|
||||
voices:
|
||||
- piper: en_US-kusal-medium
|
||||
- mac: Tom
|
||||
|
||||
menu:
|
||||
- trigger: framework
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/testarch/framework/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ agent:
|
|||
- "CRITICAL: Load COMPLETE file {project-root}/{bmad_folder}/bmm/data/documentation-standards.md into permanent memory and follow ALL rules within"
|
||||
- "Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`"
|
||||
|
||||
tts:
|
||||
intro: "Hi! I'm Paige, your Technical Writer. I'll make sure everything is documented clearly."
|
||||
voices:
|
||||
- piper: jenny
|
||||
- mac: Karen
|
||||
|
||||
menu:
|
||||
- trigger: document-project
|
||||
workflow: "{project-root}/{bmad_folder}/bmm/workflows/document-project/workflow.yaml"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ agent:
|
|||
critical_actions:
|
||||
- "Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md`"
|
||||
|
||||
tts:
|
||||
intro: "Hey! Sally here, your UX Designer. The user experience is my top priority."
|
||||
voices:
|
||||
- piper: kristin
|
||||
- mac: Victoria
|
||||
|
||||
menu:
|
||||
- trigger: create-ux-design
|
||||
exec: "{project-root}/{bmad_folder}/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md"
|
||||
|
|
|
|||
|
|
@ -84,11 +84,27 @@ module.exports = {
|
|||
|
||||
// Run AgentVibes installer
|
||||
const { execSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
try {
|
||||
execSync('npx agentvibes@latest install', {
|
||||
// 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_')),
|
||||
);
|
||||
|
||||
// Check if this is first-time AgentVibes installation
|
||||
const agentvibesDir = path.join(result.projectDir, '.agentvibes');
|
||||
const isFirstTime = !fs.existsSync(agentvibesDir);
|
||||
const installCmd = isFirstTime ? 'npx agentvibes@latest install --with-audio' : 'npx agentvibes@latest install';
|
||||
|
||||
execSync(installCmd, {
|
||||
cwd: result.projectDir,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: cleanEnv,
|
||||
});
|
||||
console.log(chalk.green('\n✓ AgentVibes installation complete'));
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -873,6 +873,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(
|
||||
|
|
@ -1106,7 +1107,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) {
|
||||
|
|
@ -2087,6 +2088,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'));
|
||||
}
|
||||
|
|
@ -2124,6 +2157,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
|
||||
|
|
|
|||
|
|
@ -58,6 +58,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);
|
||||
|
||||
|
|
@ -75,6 +78,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),
|
||||
|
|
@ -277,6 +281,21 @@ class ManifestGenerator {
|
|||
.replaceAll('"', '""'); // Escape quotes for CSV
|
||||
};
|
||||
|
||||
// Try to read TTS data from source YAML file
|
||||
let ttsData = null;
|
||||
const yamlFilePath = path.join(dirPath, `${agentName}.agent.yaml`);
|
||||
if (await fs.pathExists(yamlFilePath)) {
|
||||
try {
|
||||
const yamlContent = await fs.readFile(yamlFilePath, 'utf8');
|
||||
const agentYaml = yaml.load(yamlContent);
|
||||
if (agentYaml?.agent?.tts) {
|
||||
ttsData = agentYaml.agent.tts;
|
||||
}
|
||||
} catch {
|
||||
// Silently skip if YAML parsing fails
|
||||
}
|
||||
}
|
||||
|
||||
agents.push({
|
||||
name: agentName,
|
||||
displayName: nameMatch ? nameMatch[1] : agentName,
|
||||
|
|
@ -288,6 +307,7 @@ class ManifestGenerator {
|
|||
principles: principlesMatch ? cleanForCSV(principlesMatch[1]) : '',
|
||||
module: moduleName,
|
||||
path: installPath,
|
||||
tts: ttsData, // Add TTS data from YAML
|
||||
});
|
||||
|
||||
// Add to files list
|
||||
|
|
@ -458,6 +478,7 @@ class ManifestGenerator {
|
|||
},
|
||||
modules: this.modules,
|
||||
ides: this.selectedIdes,
|
||||
agentVibes: this.agentVibes, // Track AgentVibes TTS configuration
|
||||
};
|
||||
|
||||
const yamlStr = yaml.dump(manifest, {
|
||||
|
|
@ -593,6 +614,65 @@ 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');
|
||||
|
||||
// Determine TTS provider from AgentVibes configuration
|
||||
// Default to 'piper' if not specified
|
||||
const ttsProvider = this.agentVibes?.provider || 'piper';
|
||||
|
||||
// Map provider names to voice field names
|
||||
const providerVoiceField = {
|
||||
piper: 'piper',
|
||||
macos: 'mac',
|
||||
};
|
||||
|
||||
const voiceField = providerVoiceField[ttsProvider] || 'piper';
|
||||
|
||||
// Fallback values for agents without TTS data
|
||||
const fallbackVoice = voiceField === 'mac' ? 'Samantha' : '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) {
|
||||
let voice = fallbackVoice;
|
||||
let intro = fallbackIntro;
|
||||
|
||||
// Extract voice and intro from agent's TTS data if available
|
||||
if (agent.tts) {
|
||||
// Get intro
|
||||
if (agent.tts.intro) {
|
||||
intro = agent.tts.intro;
|
||||
}
|
||||
|
||||
// Get voice for the selected provider
|
||||
if (agent.tts.voices && Array.isArray(agent.tts.voices)) {
|
||||
for (const voiceEntry of agent.tts.voices) {
|
||||
if (voiceEntry[voiceField]) {
|
||||
voice = voiceEntry[voiceField];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -148,13 +148,14 @@ class UI {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
@ -922,7 +923,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
|
||||
|
|
@ -934,20 +935,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,
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ function buildAgentSchema(expectedModule) {
|
|||
metadata: buildMetadataSchema(expectedModule),
|
||||
persona: buildPersonaSchema(),
|
||||
critical_actions: z.array(createNonEmptyString('agent.critical_actions[]')).optional(),
|
||||
tts: buildTTSSchema().optional(),
|
||||
menu: z.array(buildMenuItemSchema()).min(1, { message: 'agent.menu must include at least one entry' }),
|
||||
prompts: z.array(buildPromptSchema()).optional(),
|
||||
webskip: z.boolean().optional(),
|
||||
|
|
@ -209,6 +210,34 @@ function buildPersonaSchema() {
|
|||
.strict();
|
||||
}
|
||||
|
||||
function buildTTSSchema() {
|
||||
return z
|
||||
.object({
|
||||
intro: createNonEmptyString('agent.tts.intro'),
|
||||
voices: z
|
||||
.array(
|
||||
z.object({
|
||||
piper: createNonEmptyString('agent.tts.voices[].piper').optional(),
|
||||
mac: createNonEmptyString('agent.tts.voices[].mac').optional(),
|
||||
}),
|
||||
)
|
||||
.min(1, { message: 'agent.tts.voices must include at least one voice mapping' })
|
||||
.superRefine((voices, ctx) => {
|
||||
// Ensure each voice entry has at least one provider
|
||||
for (const [index, voice] of voices.entries()) {
|
||||
if (!voice.piper && !voice.mac) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path: [index],
|
||||
message: 'agent.tts.voices[] must include at least one voice provider (piper or mac)',
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
.strict();
|
||||
}
|
||||
|
||||
function buildPromptSchema() {
|
||||
return z
|
||||
.object({
|
||||
|
|
|
|||
Loading…
Reference in New Issue