diff --git a/src/core/agents/bmad-master.agent.yaml b/src/core/agents/bmad-master.agent.yaml index bba8be22..83e4286f 100644 --- a/src/core/agents/bmad-master.agent.yaml +++ b/src/core/agents/bmad-master.agent.yaml @@ -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" diff --git a/src/modules/bmm/agents/analyst.agent.yaml b/src/modules/bmm/agents/analyst.agent.yaml index eb0bc7c4..cf00d663 100644 --- a/src/modules/bmm/agents/analyst.agent.yaml +++ b/src/modules/bmm/agents/analyst.agent.yaml @@ -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" diff --git a/src/modules/bmm/agents/architect.agent.yaml b/src/modules/bmm/agents/architect.agent.yaml index 07d9ad3a..3b0a676c 100644 --- a/src/modules/bmm/agents/architect.agent.yaml +++ b/src/modules/bmm/agents/architect.agent.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" diff --git a/src/modules/bmm/agents/dev.agent.yaml b/src/modules/bmm/agents/dev.agent.yaml index 3e3fdc2d..fe0ef415 100644 --- a/src/modules/bmm/agents/dev.agent.yaml +++ b/src/modules/bmm/agents/dev.agent.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" diff --git a/src/modules/bmm/agents/pm.agent.yaml b/src/modules/bmm/agents/pm.agent.yaml index 40dcf7d0..39fe7120 100644 --- a/src/modules/bmm/agents/pm.agent.yaml +++ b/src/modules/bmm/agents/pm.agent.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" diff --git a/src/modules/bmm/agents/sm.agent.yaml b/src/modules/bmm/agents/sm.agent.yaml index 8be3ee66..77dbe60a 100644 --- a/src/modules/bmm/agents/sm.agent.yaml +++ b/src/modules/bmm/agents/sm.agent.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" diff --git a/src/modules/bmm/agents/tea.agent.yaml b/src/modules/bmm/agents/tea.agent.yaml index df18b836..dfaed39f 100644 --- a/src/modules/bmm/agents/tea.agent.yaml +++ b/src/modules/bmm/agents/tea.agent.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" diff --git a/src/modules/bmm/agents/tech-writer.agent.yaml b/src/modules/bmm/agents/tech-writer.agent.yaml index 6911c581..af791ead 100644 --- a/src/modules/bmm/agents/tech-writer.agent.yaml +++ b/src/modules/bmm/agents/tech-writer.agent.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" diff --git a/src/modules/bmm/agents/ux-designer.agent.yaml b/src/modules/bmm/agents/ux-designer.agent.yaml index 04ba4c86..777f2c19 100644 --- a/src/modules/bmm/agents/ux-designer.agent.yaml +++ b/src/modules/bmm/agents/ux-designer.agent.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" diff --git a/tools/cli/installers/lib/core/manifest-generator.js b/tools/cli/installers/lib/core/manifest-generator.js index 141fb45a..924f357f 100644 --- a/tools/cli/installers/lib/core/manifest-generator.js +++ b/tools/cli/installers/lib/core/manifest-generator.js @@ -269,6 +269,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, @@ -280,6 +295,7 @@ class ManifestGenerator { principles: principlesMatch ? cleanForCSV(principlesMatch[1]) : '', module: moduleName, path: installPath, + tts: ttsData, // Add TTS data from YAML }); // Add to files list @@ -595,62 +611,48 @@ class ManifestGenerator { 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.", - }, + // 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', + elevenlabs: 'piper', // ElevenLabs not used, fallback to piper + macos: 'mac', }; - // Fallback values for agents not in the default map - const fallbackVoice = 'en_US-lessac-medium'; + 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) { - const defaults = agentDefaults[agent.name] || {}; - const voice = defaults.voice || fallbackVoice; - const intro = defaults.intro || fallbackIntro; + 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`; diff --git a/tools/schema/agent.js b/tools/schema/agent.js index 99438f6a..dcebd08c 100644 --- a/tools/schema/agent.js +++ b/tools/schema/agent.js @@ -126,6 +126,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(), @@ -195,6 +196,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({