Paul Preibisch 5702195ef7
Add Text-to-Speech Integration via TTS_INJECTION System (#934)
* feat: Add provider-agnostic TTS integration via injection point system

Implements comprehensive Text-to-Speech integration for BMAD agents using a generic
TTS_INJECTION marker system. When AgentVibes (or any compatible TTS provider) is
installed, all BMAD agents can speak their responses with unique AI voices.

## Key Features

**Provider-Agnostic Architecture**
- Uses generic `TTS_INJECTION` markers instead of vendor-specific naming
- Future-proof for multiple TTS providers beyond AgentVibes
- Clean separation - BMAD stays TTS-agnostic, providers handle injection

**Installation Flow**
- BMAD → AgentVibes: TTS instructions injected when AgentVibes detects existing BMAD installation
- AgentVibes → BMAD: TTS instructions injected during BMAD installation when AgentVibes detected
- User must manually create voice assignment file when AgentVibes installs first (documented limitation)

**Party Mode Voice Support**
- Each agent speaks with unique assigned voice in multi-agent discussions
- PM, Architect, Developer, Analyst, UX Designer, etc. - all with distinct voices

**Zero Breaking Changes**
- Fully backward compatible - works without any TTS provider
- `TTS_INJECTION` markers are benign HTML comments if not processed
- No changes to existing agent behavior or non-TTS workflows

## Implementation Details

**Files Modified:**
- `tools/cli/installers/lib/core/installer.js` - TTS injection processing logic
- `tools/cli/lib/ui.js` - AgentVibes detection and installation prompts
- `tools/cli/commands/install.js` - Post-install guidance for AgentVibes setup
- `src/utility/models/fragments/activation-rules.xml` - TTS_INJECTION marker for agents
- `src/core/workflows/party-mode/instructions.md` - TTS_INJECTION marker for party mode

**Injection Point System:**
```xml
<rules>
  - ALWAYS communicate in {communication_language}
  <!-- TTS_INJECTION:agent-tts -->
  - Stay in character until exit selected
</rules>
```

When AgentVibes is detected, the installer replaces this marker with:
```
- When responding to user messages, speak your responses using TTS:
  Call: `.claude/hooks/bmad-speak.sh '{agent-id}' '{response-text}'` after each response
  IMPORTANT: Use single quotes - do NOT escape special characters like ! or $
```

**Special Character Handling:**
- Explicit guidance to use single quotes without escaping
- Prevents "backslash exclamation" artifacts in speech

**User Experience:**
```
User: "How should we architect this feature?"
Architect: [Text response] + 🔊 [Professional voice explains architecture]
```

Party Mode:
```
PM (John): "I'll focus on user value..." 🔊 [Male professional voice]
UX Designer (Sara): "From a user perspective..." 🔊 [Female voice]
Architect (Marcus): "The technical approach..." 🔊 [Male technical voice]
```

## Testing

**Unit Tests:**  62/62 passing
- 49/49 schema validation tests
- 13/13 installation component tests

**Integration Testing:**
-  BMAD → AgentVibes (automatic injection)
-  AgentVibes → BMAD (automatic injection)
-  No TTS provider (markers remain as comments)

## Documentation

Comprehensive testing guide created with:
- Both installation scenario walkthroughs
- Verification commands and expected outputs
- Troubleshooting guidance

## Known Limitations

**AgentVibes → BMAD Installation Order:**
When AgentVibes installs first, voice assignment file must be created manually:
```bash
mkdir -p .bmad/_cfg
cat > .bmad/_cfg/agent-voice-map.csv << 'EOF'
agent_id,voice_name
pm,en_US-ryan-high
architect,en_US-danny-low
dev,en_US-joe-medium
EOF
```

This limitation exists to prevent false legacy v4 detection warnings from BMAD installer.

**Recommended:** Install BMAD first, then AgentVibes for automatic voice assignment.

## Related Work

**Companion Implementation:**
- Repository: paulpreibisch/AgentVibes
- Commits: 6 commits implementing injection processing and voice routing
- Features: Retroactive injection, file path extraction, escape stripping

**GitHub Issues:**
- paulpreibisch/AgentVibes#36 - BMAD agent ID support

## Breaking Changes

None. Feature is opt-in and requires separate TTS provider installation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Enforce project hooks over global hooks in party mode

before, claude would sometimes favor global agent vibes hooks over project specific

* feat: Automate AgentVibes installer invocation after BMAD install

Instead of showing manual installation instructions, the installer now:
- Prompts "Press Enter to start AgentVibes installer..."
- Automatically runs npx agentvibes@latest install
- Handles errors gracefully with fallback instructions

This provides a seamless installation flow matching the test script's
interactive approach.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: Add automated testing script and guide for PR #934

Added comprehensive testing tools for AgentVibes party mode integration:

- test-bmad-pr.sh: Fully automated installation and verification script
  - Interactive mode selection (official PR or custom fork)
  - Automatic BMAD CLI setup and linking
  - AgentVibes installation with guided prompts
  - Built-in verification checks for voice maps and hooks
  - Saved configuration for quick re-testing

- TESTING.md: Complete testing documentation
  - Quick start with one-line npx command
  - Manual installation alternative
  - Troubleshooting guide
  - Cleanup instructions

Testers can now run a single command to test the full AgentVibes integration
without needing to understand the complex setup process.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Add shell: true to npx execSync to prevent permission denied error

The execSync call for 'npx agentvibes@latest install' was failing with
'Permission denied' because the shell was trying to execute 'agentvibes@latest'
directly instead of passing it as an argument to npx.

Adding shell: true ensures the command runs in a proper shell context
where npx can correctly interpret the @latest version syntax.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Remove duplicate AgentVibes installation step from test script

The test script was calling AgentVibes installer twice:
1. BMAD installer now automatically runs AgentVibes (new feature)
2. Test script had a separate Step 6 that also ran AgentVibes

This caused the installer to run twice, with the second call failing
because it was already installed.

Changes:
- Removed redundant Step 6 (AgentVibes installation)
- Updated Step 5 to indicate it includes AgentVibes
- Updated step numbers from 7 to 6 throughout
- Added guidance that AgentVibes runs automatically

Now the flow is cleaner: BMAD installer handles everything!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Address bmadcode review - preserve variables and move TTS logic to injection

Fixes requested changes from PR review:

1. Preserve {bmad_folder} variable placeholder
   - Changed: {project_root}/.bmad/core/tasks/workflow.xml
   - To: {project_root}/{bmad_folder}/core/tasks/workflow.xml
   - Allows users to choose custom BMAD folder names during installation

2. Move TTS-specific hook guidance to injection system
   - Removed hardcoded hook enforcement from source files
   - Added hook guidance to processTTSInjectionPoints() in installer.js
   - Now only appears when AgentVibes is installed (via TTS_INJECTION)

3. Maintain TTS-agnostic source architecture
   - Source files remain clean of TTS-specific instructions
   - TTS details injected at install-time only when needed
   - Preserves provider-agnostic design principle

Changes made:
- src/core/workflows/party-mode/instructions.md
  - Reverted .bmad to {bmad_folder} variable
  - Replaced hardcoded hook guidance with <!-- TTS_INJECTION:party-mode -->
  - Removed <note> about play-tts.sh hook location

- tools/cli/installers/lib/core/installer.js
  - Added hook enforcement to party-mode injection replacement
  - Guidance now injected only when enableAgentVibes is true

Addresses review comments from bmadcode:
- "needs to remain the variable. it will get set in the file at the install destination."
- "items like this we will need to inject if user is using claude and TTS"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Change 'claude-code' to 'claude' in test script instructions

The correct command to start Claude is 'claude', not 'claude-code'.
Updated line 362-363 in test-bmad-pr.sh to show the correct command.

* fix: Remove npm link from test script to avoid global namespace pollution

- Removed 'npm link' command that was installing BMAD globally
- Changed 'bmad install' to direct node execution using local clone
- Updated success message to reflect no global installation

This keeps testing fully isolated and prevents conflicts with:
- Existing BMAD installations
- Future official BMAD installs
- Orphaned symlinks when test directory is deleted

The test script now runs completely self-contained without modifying
the user's global npm environment.

---------

Co-authored-by: Claude Code <claude@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Paul Preibisch <paul@paulpreibisch.com>
Co-authored-by: Brian <bmadcode@gmail.com>
2025-11-26 09:51:57 -06:00

120 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const chalk = require('chalk');
const path = require('node:path');
const { Installer } = require('../installers/lib/core/installer');
const { UI } = require('../lib/ui');
const installer = new Installer();
const ui = new UI();
module.exports = {
command: 'install',
description: 'Install BMAD Core agents and tools',
options: [],
action: async () => {
try {
const config = await ui.promptInstall();
// Handle cancel
if (config.actionType === 'cancel') {
console.log(chalk.yellow('Installation cancelled.'));
process.exit(0);
return;
}
// Handle agent compilation separately
if (config.actionType === 'compile') {
const result = await installer.compileAgents(config);
console.log(chalk.green('\n✨ Agent compilation complete!'));
console.log(chalk.cyan(`Rebuilt ${result.agentCount} agents and ${result.taskCount} tasks`));
process.exit(0);
return;
}
// Handle quick update separately
if (config.actionType === 'quick-update') {
const result = await installer.quickUpdate(config);
console.log(chalk.green('\n✨ Quick update complete!'));
console.log(chalk.cyan(`Updated ${result.moduleCount} modules with preserved settings`));
process.exit(0);
return;
}
// Handle reinstall by setting force flag
if (config.actionType === 'reinstall') {
config._requestedReinstall = true;
}
// Regular install/update flow
const result = await installer.install(config);
// Check if installation was cancelled
if (result && result.cancelled) {
process.exit(0);
return;
}
// Check if installation succeeded
if (result && result.success) {
console.log(chalk.green('\n✨ Installation complete!'));
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!'));
// Run AgentVibes installer if needed
if (result.needsAgentVibes) {
console.log(chalk.magenta('\n🎙 AgentVibes TTS Setup'));
console.log(chalk.cyan('AgentVibes provides voice synthesis for BMAD agents with:'));
console.log(chalk.dim(' • ElevenLabs AI (150+ premium voices)'));
console.log(chalk.dim(' • Piper TTS (50+ free voices)\n'));
const readline = require('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
await new Promise((resolve) => {
rl.question(chalk.green('Press Enter to start AgentVibes installer...'), () => {
rl.close();
resolve();
});
});
console.log('');
// Run AgentVibes installer
const { execSync } = require('node:child_process');
try {
execSync('npx agentvibes@latest install', {
cwd: result.projectDir,
stdio: 'inherit',
shell: true,
});
console.log(chalk.green('\n✓ AgentVibes installation complete'));
} catch {
console.log(chalk.yellow('\n⚠ AgentVibes installation was interrupted or failed'));
console.log(chalk.cyan('You can run it manually later with:'));
console.log(chalk.green(` cd ${result.projectDir}`));
console.log(chalk.green(' npx agentvibes install\n'));
}
}
process.exit(0);
}
} catch (error) {
// Check if error has a complete formatted message
if (error.fullMessage) {
console.error(error.fullMessage);
if (error.stack) {
console.error('\n' + chalk.dim(error.stack));
}
} else {
// Generic error handling for all other errors
console.error(chalk.red('Installation failed:'), error.message);
console.error(chalk.dim(error.stack));
}
process.exit(1);
}
},
};