diff --git a/src/core/workflows/party-mode/instructions.md b/src/core/workflows/party-mode/instructions.md index 3ca8e052..06e6c3ec 100644 --- a/src/core/workflows/party-mode/instructions.md +++ b/src/core/workflows/party-mode/instructions.md @@ -32,7 +32,7 @@ Participating agents: [For each agent in roster:] - - [Agent Name] ([Title]): [Role from merged data] + - [Icon Emoji] [Agent Name] ([Title]): [Role from merged data] [Total count] agents ready to collaborate! @@ -94,13 +94,20 @@ - Present each agent's contribution clearly: + For each agent response, output text THEN trigger their voice: + + + 1. Output the agent's text in format: [Icon Emoji] [Agent Name]: [dialogue] + 2. + 3. Repeat for each agent in the response + + - [Agent Name]: [Their response in their voice/style] + [Icon Emoji] [Agent Name]: [Their response in their voice/style] - [Another Agent]: [Their response, potentially referencing the first] + [Icon Emoji] [Another Agent]: [Their response, potentially referencing the first] - [Third Agent if selected]: [Their contribution] + [Icon Emoji] [Third Agent if selected]: [Their contribution] Maintain spacing between agents for readability diff --git a/src/utility/models/fragments/activation-rules.xml b/src/utility/models/fragments/activation-rules.xml index 8fdd9852..4835e834 100644 --- a/src/utility/models/fragments/activation-rules.xml +++ b/src/utility/models/fragments/activation-rules.xml @@ -1,5 +1,6 @@ - ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style + - Stay in character until exit selected - Menu triggers use asterisk (*) - NOT markdown, display exactly as shown - Number all lists, use letters for sub-options diff --git a/tools/cli/commands/install.js b/tools/cli/commands/install.js index d2706ee6..8d5fd369 100644 --- a/tools/cli/commands/install.js +++ b/tools/cli/commands/install.js @@ -59,6 +59,18 @@ module.exports = { console.log(chalk.cyan('BMAD Core and Selected Modules have been installed to:'), chalk.bold(result.path)); console.log(chalk.yellow('\nThank you for helping test the early release version of the new BMad Core and BMad Method!')); console.log(chalk.cyan('Stable Beta coming soon - please read the full readme.md and linked documentation to get started!')); + + // Display post-installation steps if needed + if (result.needsAgentVibes) { + console.log(chalk.magenta('\nšŸ“‹ Post-Installation Steps')); + console.log(chalk.cyan('To complete AgentVibes TTS setup, run:')); + console.log(chalk.green(`\n cd ${result.projectDir}`)); + console.log(chalk.green(' npx agentvibes install\n')); + console.log(chalk.dim('This will set up TTS with your choice of:')); + console.log(chalk.dim(' • ElevenLabs AI (150+ premium voices)')); + console.log(chalk.dim(' • Piper TTS (50+ free voices)\n')); + } + process.exit(0); } } catch (error) { diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 95a33bd2..5aba6462 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -1,3 +1,23 @@ +/** + * File: tools/cli/installers/lib/core/installer.js + * + * BMAD Method - Business Model Agile Development Method + * Repository: https://github.com/paulpreibisch/BMAD-METHOD + * + * Copyright (c) 2025 Paul Preibisch + * Licensed under the Apache License, Version 2.0 + * + * --- + * + * @fileoverview Core BMAD installation orchestrator with AgentVibes injection point support + * @context Manages complete BMAD installation flow including core agents, modules, IDE configs, and optional TTS integration + * @architecture Orchestrator pattern - coordinates Detector, ModuleManager, IdeManager, and file operations to build complete BMAD installation + * @dependencies fs-extra, ora, chalk, detector.js, module-manager.js, ide-manager.js, config.js + * @entrypoints Called by install.js command via installer.install(config) + * @patterns Injection point processing (AgentVibes), placeholder replacement ({bmad_folder}), module dependency resolution + * @related GitHub AgentVibes#34 (injection points), ui.js (user prompts), copyFileWithPlaceholderReplacement() + */ + const path = require('node:path'); const fs = require('fs-extra'); const chalk = require('chalk'); @@ -69,10 +89,41 @@ class Installer { } /** - * Copy a file and replace {bmad_folder} placeholder with actual folder name - * @param {string} sourcePath - Source file path - * @param {string} targetPath - Target file path - * @param {string} bmadFolderName - The bmad folder name to use for replacement + * @function copyFileWithPlaceholderReplacement + * @intent Copy files from BMAD source to installation directory with dynamic content transformation + * @why Enables installation-time customization: {bmad_folder} replacement + optional AgentVibes TTS injection + * @param {string} sourcePath - Absolute path to source file in BMAD repository + * @param {string} targetPath - Absolute path to destination file in user's project + * @param {string} bmadFolderName - User's chosen bmad folder name (default: 'bmad') + * @returns {Promise} Resolves when file copy and transformation complete + * @sideeffects Writes transformed file to targetPath, creates parent directories if needed + * @edgecases Binary files bypass transformation, falls back to raw copy if UTF-8 read fails + * @calledby installCore(), installModule(), IDE installers during file vendoring + * @calls processTTSInjectionPoints(), fs.readFile(), fs.writeFile(), fs.copy() + * + * AI NOTE: This is the core transformation pipeline for ALL BMAD installation file copies. + * It performs two transformations in sequence: + * 1. {bmad_folder} → user's custom folder name (e.g., ".bmad" or "bmad") + * 2. → TTS bash calls (if enabled) OR stripped (if disabled) + * + * The injection point processing enables loose coupling between BMAD and TTS providers: + * - BMAD source contains injection markers (not actual TTS code) + * - At install-time, markers are replaced OR removed based on user preference + * - Result: Clean installs for users without TTS, working TTS for users with it + * + * PATTERN: Adding New Injection Points + * ===================================== + * 1. Add HTML comment marker in BMAD source file: + * + * + * 2. Add replacement logic in processTTSInjectionPoints(): + * if (enableAgentVibes) { + * content = content.replace(//g, 'actual code'); + * } else { + * content = content.replace(/\n?/g, ''); + * } + * + * 3. Document marker in instructions.md (if applicable) */ async copyFileWithPlaceholderReplacement(sourcePath, targetPath, bmadFolderName) { // List of text file extensions that should have placeholder replacement @@ -90,6 +141,9 @@ class Installer { content = content.replaceAll('{bmad_folder}', bmadFolderName); } + // Process AgentVibes injection points + content = this.processTTSInjectionPoints(content); + // Write to target with replaced content await fs.ensureDir(path.dirname(targetPath)); await fs.writeFile(targetPath, content, 'utf8'); @@ -103,6 +157,104 @@ class Installer { } } + /** + * @function processTTSInjectionPoints + * @intent Transform TTS injection markers based on user's installation choice + * @why Enables optional TTS integration without tight coupling between BMAD and TTS providers + * @param {string} content - Raw file content containing potential injection markers + * @returns {string} Transformed content with markers replaced (if enabled) or stripped (if disabled) + * @sideeffects None - pure transformation function + * @edgecases Returns content unchanged if no markers present, safe to call on all files + * @calledby copyFileWithPlaceholderReplacement() during every file copy operation + * @calls String.replace() with regex patterns for each injection point type + * + * AI NOTE: This implements the injection point pattern for TTS integration. + * Key architectural decisions: + * + * 1. **Why Injection Points vs Direct Integration?** + * - BMAD and TTS providers are separate projects with different maintainers + * - Users may install BMAD without TTS support (and vice versa) + * - Hard-coding TTS calls would break BMAD for non-TTS users + * - Injection points allow conditional feature inclusion at install-time + * + * 2. **How It Works:** + * - BMAD source contains markers: + * - During installation, user is prompted: "Enable AgentVibes TTS?" + * - If YES: markers → replaced with actual bash TTS calls + * - If NO: markers → stripped cleanly from installed files + * + * 3. **State Management:** + * - this.enableAgentVibes set in install() method from config.enableAgentVibes + * - config.enableAgentVibes comes from ui.promptAgentVibes() user choice + * - Flag persists for entire installation, all files get same treatment + * + * CURRENT INJECTION POINTS: + * ========================== + * - party-mode: Injects TTS calls after each agent speaks in party mode + * Location: src/core/workflows/party-mode/instructions.md + * Marker: + * Replacement: Bash call to .claude/hooks/bmad-speak.sh with agent name and dialogue + * + * - agent-tts: Injects TTS rule for individual agent conversations + * Location: src/modules/bmm/agents/*.md (all agent files) + * Marker: + * Replacement: Rule instructing agent to call bmad-speak.sh with agent ID and response + * + * ADDING NEW INJECTION POINTS: + * ============================= + * 1. Add new case in this function: + * content = content.replace( + * //g, + * `code to inject when enabled` + * ); + * + * 2. Add marker to BMAD source file at injection location + * + * 3. Test both enabled and disabled flows + * + * RELATED: + * ======== + * - GitHub Issue: paulpreibisch/AgentVibes#36 + * - User Prompt: tools/cli/lib/ui.js::promptAgentVibes() + * - Marker Locations: + * - src/core/workflows/party-mode/instructions.md:101 + * - src/modules/bmm/agents/*.md (rules sections) + * - TTS Hook: .claude/hooks/bmad-speak.sh (in AgentVibes repo) + */ + processTTSInjectionPoints(content) { + // Check if AgentVibes is enabled (set during installation configuration) + const enableAgentVibes = this.enableAgentVibes || false; + + if (enableAgentVibes) { + // Replace party-mode injection marker with actual TTS call + // Use single quotes to prevent shell expansion of special chars like ! + content = content.replaceAll( + '', + `If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: + - Use Bash tool: \`.claude/hooks/bmad-speak.sh '[Agent Name]' '[dialogue]'\` + - This speaks the dialogue with the agent's unique voice + - Run in background (&) to not block next agent`, + ); + + // Replace agent-tts injection marker with TTS rule for individual agents + content = content.replaceAll( + '', + `- 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 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`, + ); + } else { + // Strip injection markers cleanly when AgentVibes is disabled + content = content.replaceAll(/\n?/g, ''); + content = content.replaceAll(/\n?/g, ''); + } + + return content; + } + /** * Collect Tool/IDE configurations after module configuration * @param {string} projectDir - Project directory @@ -269,6 +421,9 @@ class Installer { const bmadFolderName = moduleConfigs.core && moduleConfigs.core.bmad_folder ? moduleConfigs.core.bmad_folder : 'bmad'; this.bmadFolderName = bmadFolderName; // Store for use in other methods + // Store AgentVibes configuration for injection point processing + this.enableAgentVibes = config.enableAgentVibes || false; + // Set bmad folder name on module manager and IDE manager for placeholder replacement this.moduleManager.setBmadFolderName(bmadFolderName); this.ideManager.setBmadFolderName(bmadFolderName); @@ -850,7 +1005,14 @@ class Installer { customFiles: customFiles.length > 0 ? customFiles : undefined, }); - return { success: true, path: bmadDir, modules: config.modules, ides: config.ides }; + return { + success: true, + path: bmadDir, + modules: config.modules, + ides: config.ides, + needsAgentVibes: this.enableAgentVibes && !config.agentVibesInstalled, + projectDir: projectDir, + }; } catch (error) { spinner.fail('Installation failed'); throw error; diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index 323fdaaf..3cdd48d4 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -1,3 +1,23 @@ +/** + * File: tools/cli/lib/ui.js + * + * BMAD Method - Business Model Agile Development Method + * Repository: https://github.com/paulpreibisch/BMAD-METHOD + * + * Copyright (c) 2025 Paul Preibisch + * Licensed under the Apache License, Version 2.0 + * + * --- + * + * @fileoverview Interactive installation prompts and user input collection for BMAD CLI + * @context Guides users through installation configuration including core settings, modules, IDEs, and optional AgentVibes TTS + * @architecture Facade pattern - presents unified installation flow, delegates to Detector/ConfigCollector/IdeManager for specifics + * @dependencies inquirer (prompts), chalk (formatting), detector.js (existing installation detection) + * @entrypoints Called by install.js command via ui.promptInstall(), returns complete configuration object + * @patterns Progressive disclosure (prompts in order), early IDE selection (Windows compat), AgentVibes auto-detection + * @related installer.js (consumes config), AgentVibes#34 (TTS integration), promptAgentVibes() + */ + const chalk = require('chalk'); const inquirer = require('inquirer'); const path = require('node:path'); @@ -99,6 +119,9 @@ 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); @@ -114,6 +137,8 @@ class UI { ides: toolSelection.ides, skipIde: toolSelection.skipIde, coreConfig: coreConfig, // Pass collected core config to installer + enableAgentVibes: agentVibesConfig.enabled, // AgentVibes TTS integration + agentVibesInstalled: agentVibesConfig.alreadyInstalled, }; } @@ -603,6 +628,140 @@ class UI { // Resolve to the absolute path relative to the current working directory return path.resolve(expanded); } + + /** + * @function promptAgentVibes + * @intent Ask user if they want AgentVibes TTS integration during BMAD installation + * @why Enables optional voice features without forcing TTS on users who don't want it + * @param {string} projectDir - Absolute path to user's project directory + * @returns {Promise} Configuration object: { enabled: boolean, alreadyInstalled: boolean } + * @sideeffects None - pure user input collection, no files written + * @edgecases Shows warning if user enables TTS but AgentVibes not detected + * @calledby promptInstall() during installation flow, after core config, before IDE selection + * @calls checkAgentVibesInstalled(), inquirer.prompt(), chalk.green/yellow/dim() + * + * AI NOTE: This prompt is strategically positioned in installation flow: + * - AFTER core config (bmad_folder, user_name, etc) + * - BEFORE IDE selection (which can hang on Windows/PowerShell) + * + * Flow Logic: + * 1. Auto-detect if AgentVibes already installed (checks for hook files) + * 2. Show detection status to user (green checkmark or gray "not detected") + * 3. Prompt: "Enable AgentVibes TTS?" (defaults to true if detected) + * 4. If user says YES but AgentVibes NOT installed: + * → Show warning with installation link (graceful degradation) + * 5. Return config to promptInstall(), which passes to installer.install() + * + * State Flow: + * promptAgentVibes() → { enabled, alreadyInstalled } + * ↓ + * promptInstall() → config.enableAgentVibes + * ↓ + * installer.install() → this.enableAgentVibes + * ↓ + * processTTSInjectionPoints() → injects OR strips markers + * + * RELATED: + * ======== + * - Detection: checkAgentVibesInstalled() - looks for bmad-speak.sh and play-tts.sh + * - Processing: installer.js::processTTSInjectionPoints() + * - Markers: src/core/workflows/party-mode/instructions.md:101, src/modules/bmm/agents/*.md + * - GitHub Issue: paulpreibisch/AgentVibes#36 + */ + async promptAgentVibes(projectDir) { + CLIUtils.displaySection('šŸŽ¤ Voice Features', 'Enable TTS for multi-agent conversations'); + + // Check if AgentVibes is already installed + const agentVibesInstalled = await this.checkAgentVibesInstalled(projectDir); + + if (agentVibesInstalled) { + console.log(chalk.green(' āœ“ AgentVibes detected')); + } else { + console.log(chalk.dim(' AgentVibes not detected')); + } + + const answers = await inquirer.prompt([ + { + type: 'confirm', + name: 'enableTts', + message: 'Enable AgentVibes TTS? (Agents speak with unique voices in party mode)', + default: true, // Default to yes - recommended for best experience + }, + ]); + + 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')); + } + + return { + enabled: answers.enableTts, + alreadyInstalled: agentVibesInstalled, + }; + } + + /** + * @function checkAgentVibesInstalled + * @intent Detect if AgentVibes TTS hooks are present in user's project + * @why Allows auto-enabling TTS and showing helpful installation guidance + * @param {string} projectDir - Absolute path to user's project directory + * @returns {Promise} true if both required AgentVibes hooks exist, false otherwise + * @sideeffects None - read-only file existence checks + * @edgecases Returns false if either hook missing (both required for functional TTS) + * @calledby promptAgentVibes() to determine default value and show detection status + * @calls fs.pathExists() twice (bmad-speak.sh, play-tts.sh) + * + * AI NOTE: This checks for the MINIMUM viable AgentVibes installation. + * + * Required Files: + * =============== + * 1. .claude/hooks/bmad-speak.sh + * - Maps agent display names → agent IDs → voice profiles + * - Calls play-tts.sh with agent's assigned voice + * - Created by AgentVibes installer + * + * 2. .claude/hooks/play-tts.sh + * - Core TTS router (ElevenLabs or Piper) + * - Provider-agnostic interface + * - Required by bmad-speak.sh + * + * Why Both Required: + * ================== + * - bmad-speak.sh alone: No TTS backend + * - play-tts.sh alone: No BMAD agent voice mapping + * - Both together: Full party mode TTS integration + * + * Detection Strategy: + * =================== + * We use simple file existence (not version checks) because: + * - Fast and reliable + * - Works across all AgentVibes versions + * - User will discover version issues when TTS runs (fail-fast) + * + * PATTERN: Adding New Detection Criteria + * ======================================= + * If future AgentVibes features require additional files: + * 1. Add new pathExists check to this function + * 2. Update documentation in promptAgentVibes() + * 3. Consider: should missing file prevent detection or just log warning? + * + * RELATED: + * ======== + * - AgentVibes Installer: creates these hooks + * - bmad-speak.sh: calls play-tts.sh with agent voices + * - Party Mode: uses bmad-speak.sh for agent dialogue + */ + async checkAgentVibesInstalled(projectDir) { + const fs = require('fs-extra'); + const path = require('node:path'); + + // Check for AgentVibes hook files + const hookPath = path.join(projectDir, '.claude', 'hooks', 'bmad-speak.sh'); + const playTtsPath = path.join(projectDir, '.claude', 'hooks', 'play-tts.sh'); + + return (await fs.pathExists(hookPath)) && (await fs.pathExists(playTtsPath)); + } } module.exports = { UI };