Compare commits

...

7 Commits

Author SHA1 Message Date
Dicky Moore bbb89da668
Merge ddd0e8fbf2 into 8265bbf295 2025-12-05 18:55:11 -08:00
Paul Preibisch 8265bbf295
feat(installer): Enhanced TTS injection summary with tracking and documentation (#1037)
## Summary
- Track all files with TTS injection applied during installation
- Display informative summary explaining what TTS injection does
- Show backup location and restore command for recovery

## What is TTS Injection?
TTS (Text-to-Speech) injection adds voice instructions to BMAD agents,
enabling them to speak their responses aloud using AgentVibes.

Example: When you activate the PM agent, it will greet you with
spoken audio like "Hey! I'm your Project Manager. How can I help?"

## Changes
- **installer.js**: Track files in `processAgentFiles()`, `buildStandaloneAgents()`,
  and `rebuildAgentFiles()` when TTS markers are processed
- **compiler.js**: Add TTS injection support for custom agent compilation
- **ui.js**: Enhanced installation summary showing:
  - Explanation of what TTS injection is with example
  - List of all files with TTS injection applied (grouped by type)
  - Backup location (~/.bmad-tts-backups/)
  - Restore command for recovery

## Example Output
```
═══════════════════════════════════════════════════
            AgentVibes TTS Injection Summary
═══════════════════════════════════════════════════

What is TTS Injection?

  TTS (Text-to-Speech) injection adds voice instructions to BMAD agents,
  enabling them to speak their responses aloud using AgentVibes.

  Example: When you activate the PM agent, it will greet you with
  spoken audio like "Hey! I'm your Project Manager. How can I help?"

 TTS injection applied to 11 file(s):

  Party Mode (multi-agent conversations):
    • .bmad/core/workflows/party-mode/instructions.md
  Agent TTS (individual agent voices):
    • .bmad/bmm/agents/analyst.md
    • .bmad/bmm/agents/architect.md
    ...

Backups & Recovery:

  Pre-injection backups are stored in:
    ~/.bmad-tts-backups/

  To restore original files (removes TTS instructions):
    bmad-tts-injector.sh --restore /path/to/.bmad
```

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

Co-authored-by: Paul Preibisch <paul@paulpreibisch.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-05 18:54:03 -06:00
Murat K Ozcan f99e192e74
fix: tea ci nvmrc (#1036) 2025-12-05 12:30:20 -06:00
mq-bot ddd0e8fbf2 fix: remind workflows to reload sprint status 2025-11-26 14:06:12 +00:00
mq-bot 9e70b1d41c Merge upstream/main into feature/sprint-status-reload 2025-11-26 13:38:58 +00:00
mq-bot 9a3a46314e fix: remind workflows to reload sprint status 2025-11-24 19:31:35 +00:00
mq-bot a4c394fc78 Ensure workflow launcher loads core workflow 2025-11-24 14:56:30 +00:00
12 changed files with 196 additions and 21 deletions

View File

@ -3,6 +3,8 @@
<critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical> <critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical>
<critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical> <critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical>
<critical>Generate all documents in {document_output_language}</critical> <critical>Generate all documents in {document_output_language}</critical>
<critical>ALWAYS reload {{sprint_status}} (docs/sprint-artifacts/sprint-status.yaml by default) from disk immediately before reading or
updating statuses. Multiple workflows change this file between runs; never rely on a previously loaded copy or cached memory.</critical>
<critical>🔥 YOU ARE AN ADVERSARIAL CODE REVIEWER - Find what's wrong or missing! 🔥</critical> <critical>🔥 YOU ARE AN ADVERSARIAL CODE REVIEWER - Find what's wrong or missing! 🔥</critical>
<critical>Your purpose: Validate story file claims against actual implementation</critical> <critical>Your purpose: Validate story file claims against actual implementation</critical>

View File

@ -2,6 +2,8 @@
<critical>The workflow execution engine is governed by: {project-root}/{bmad_folder}/core/tasks/workflow.xml</critical> <critical>The workflow execution engine is governed by: {project-root}/{bmad_folder}/core/tasks/workflow.xml</critical>
<critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical> <critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical>
<critical>Communicate all responses in {communication_language} and generate all documents in {document_output_language}</critical> <critical>Communicate all responses in {communication_language} and generate all documents in {document_output_language}</critical>
<critical>ALWAYS reload {{sprint_status}} (docs/sprint-artifacts/sprint-status.yaml by default) from disk immediately before reading or
updating statuses. Multiple workflows change this file between runs; never rely on a previously loaded copy or cached memory.</critical>
<critical>🔥 CRITICAL MISSION: You are creating the ULTIMATE story context engine that prevents LLM developer mistakes, omissions or <critical>🔥 CRITICAL MISSION: You are creating the ULTIMATE story context engine that prevents LLM developer mistakes, omissions or
disasters! 🔥</critical> disasters! 🔥</critical>

View File

@ -3,6 +3,8 @@
<critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical> <critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical>
<critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical> <critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical>
<critical>Generate all documents in {document_output_language}</critical> <critical>Generate all documents in {document_output_language}</critical>
<critical>ALWAYS reload {{sprint_status}} (docs/sprint-artifacts/sprint-status.yaml by default) from disk immediately before reading or
updating statuses. Multiple workflows change this file between runs; never rely on a previously loaded copy or cached memory.</critical>
<critical>Only modify the story file in these areas: Tasks/Subtasks checkboxes, Dev Agent Record (Debug Log, Completion Notes), File List, <critical>Only modify the story file in these areas: Tasks/Subtasks checkboxes, Dev Agent Record (Debug Log, Completion Notes), File List,
Change Log, and Status</critical> Change Log, and Status</critical>
<critical>Execute ALL steps in exact order; do NOT skip steps</critical> <critical>Execute ALL steps in exact order; do NOT skip steps</critical>

View File

@ -26,6 +26,7 @@ PARTY MODE PROTOCOL:
- Create natural back-and-forth with user actively participating - Create natural back-and-forth with user actively participating
- Show disagreements, diverse perspectives, authentic team dynamics - Show disagreements, diverse perspectives, authentic team dynamics
</critical> </critical>
<critical>ALWAYS reload {{sprint_status_file}} (docs/sprint-artifacts/sprint-status.yaml by default) from disk immediately before reading or updating statuses. Multiple workflows change this file between runs; never rely on a previously loaded copy or cached memory.</critical>
<workflow> <workflow>

View File

@ -27,10 +27,21 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Determine Node version
id: node-version
run: |
if [ -f .nvmrc ]; then
echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
echo "Using Node from .nvmrc"
else
echo "value=24" >> "$GITHUB_OUTPUT"
echo "Using default Node 24 (current LTS)"
fi
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version-file: ".nvmrc" node-version: ${{ steps.node-version.outputs.value }}
cache: "npm" cache: "npm"
- name: Install dependencies - name: Install dependencies
@ -54,10 +65,21 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Determine Node version
id: node-version
run: |
if [ -f .nvmrc ]; then
echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
echo "Using Node from .nvmrc"
else
echo "value=22" >> "$GITHUB_OUTPUT"
echo "Using default Node 22 (current LTS)"
fi
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version-file: ".nvmrc" node-version: ${{ steps.node-version.outputs.value }}
cache: "npm" cache: "npm"
- name: Cache Playwright browsers - name: Cache Playwright browsers
@ -99,10 +121,21 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Determine Node version
id: node-version
run: |
if [ -f .nvmrc ]; then
echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
echo "Using Node from .nvmrc"
else
echo "value=22" >> "$GITHUB_OUTPUT"
echo "Using default Node 22 (current LTS)"
fi
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version-file: ".nvmrc" node-version: ${{ steps.node-version.outputs.value }}
cache: "npm" cache: "npm"
- name: Cache Playwright browsers - name: Cache Playwright browsers

View File

@ -15,6 +15,8 @@ variables:
npm_config_cache: "$CI_PROJECT_DIR/.npm" npm_config_cache: "$CI_PROJECT_DIR/.npm"
# Playwright browser cache # Playwright browser cache
PLAYWRIGHT_BROWSERS_PATH: "$CI_PROJECT_DIR/.cache/ms-playwright" PLAYWRIGHT_BROWSERS_PATH: "$CI_PROJECT_DIR/.cache/ms-playwright"
# Default Node version when .nvmrc is missing
DEFAULT_NODE_VERSION: "24"
# Caching configuration # Caching configuration
cache: cache:
@ -29,19 +31,32 @@ cache:
# Lint stage - Code quality checks # Lint stage - Code quality checks
lint: lint:
stage: lint stage: lint
image: node:20 image: node:$DEFAULT_NODE_VERSION
script: before_script:
- |
NODE_VERSION=$(cat .nvmrc 2>/dev/null || echo "$DEFAULT_NODE_VERSION")
echo "Using Node $NODE_VERSION"
npm install -g n
n "$NODE_VERSION"
node -v
- npm ci - npm ci
script:
- npm run lint - npm run lint
timeout: 5 minutes timeout: 5 minutes
# Test stage - Parallel execution with sharding # Test stage - Parallel execution with sharding
.test-template: &test-template .test-template: &test-template
stage: test stage: test
image: node:20 image: node:$DEFAULT_NODE_VERSION
needs: needs:
- lint - lint
before_script: before_script:
- |
NODE_VERSION=$(cat .nvmrc 2>/dev/null || echo "$DEFAULT_NODE_VERSION")
echo "Using Node $NODE_VERSION"
npm install -g n
n "$NODE_VERSION"
node -v
- npm ci - npm ci
- npx playwright install --with-deps chromium - npx playwright install --with-deps chromium
artifacts: artifacts:
@ -75,7 +90,7 @@ test:shard-4:
# Burn-in stage - Flaky test detection # Burn-in stage - Flaky test detection
burn-in: burn-in:
stage: burn-in stage: burn-in
image: node:20 image: node:$DEFAULT_NODE_VERSION
needs: needs:
- test:shard-1 - test:shard-1
- test:shard-2 - test:shard-2
@ -86,6 +101,12 @@ burn-in:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_PIPELINE_SOURCE == "schedule"' - if: '$CI_PIPELINE_SOURCE == "schedule"'
before_script: before_script:
- |
NODE_VERSION=$(cat .nvmrc 2>/dev/null || echo "$DEFAULT_NODE_VERSION")
echo "Using Node $NODE_VERSION"
npm install -g n
n "$NODE_VERSION"
node -v
- npm ci - npm ci
- npx playwright install --with-deps chromium - npx playwright install --with-deps chromium
script: script:

View File

@ -61,8 +61,8 @@ Scaffolds a production-ready CI/CD quality pipeline with test execution, burn-in
- Ask user if unable to auto-detect - Ask user if unable to auto-detect
5. **Read Environment Configuration** 5. **Read Environment Configuration**
- Check for `.nvmrc` to determine Node version - Use `.nvmrc` for Node version if present
- Default to Node 20 LTS if not found - If missing, default to a current LTS (Node 24) or newer instead of a fixed old version
- Read `package.json` to identify dependencies (affects caching strategy) - Read `package.json` to identify dependencies (affects caching strategy)
**Halt Condition:** If preflight checks fail, stop immediately and report which requirement failed. **Halt Condition:** If preflight checks fail, stop immediately and report which requirement failed.

View File

@ -51,6 +51,7 @@ class Installer {
this.configCollector = new ConfigCollector(); this.configCollector = new ConfigCollector();
this.ideConfigManager = new IdeConfigManager(); this.ideConfigManager = new IdeConfigManager();
this.installedFiles = []; // Track all installed files this.installedFiles = []; // Track all installed files
this.ttsInjectedFiles = []; // Track files with TTS injection applied
} }
/** /**
@ -146,8 +147,8 @@ class Installer {
content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}'); content = content.replaceAll('{*bmad_folder*}', '{bmad_folder}');
} }
// Process AgentVibes injection points // Process AgentVibes injection points (pass targetPath for tracking)
content = this.processTTSInjectionPoints(content); content = this.processTTSInjectionPoints(content, targetPath);
// Write to target with replaced content // Write to target with replaced content
await fs.ensureDir(path.dirname(targetPath)); await fs.ensureDir(path.dirname(targetPath));
@ -226,10 +227,14 @@ class Installer {
* - src/modules/bmm/agents/*.md (rules sections) * - src/modules/bmm/agents/*.md (rules sections)
* - TTS Hook: .claude/hooks/bmad-speak.sh (in AgentVibes repo) * - TTS Hook: .claude/hooks/bmad-speak.sh (in AgentVibes repo)
*/ */
processTTSInjectionPoints(content) { processTTSInjectionPoints(content, targetPath = null) {
// Check if AgentVibes is enabled (set during installation configuration) // Check if AgentVibes is enabled (set during installation configuration)
const enableAgentVibes = this.enableAgentVibes || false; const enableAgentVibes = this.enableAgentVibes || false;
// Check if content contains any TTS injection markers
const hasPartyMode = content.includes('<!-- TTS_INJECTION:party-mode -->');
const hasAgentTTS = content.includes('<!-- TTS_INJECTION:agent-tts -->');
if (enableAgentVibes) { if (enableAgentVibes) {
// Replace party-mode injection marker with actual TTS call // Replace party-mode injection marker with actual TTS call
// Use single quotes to prevent shell expansion of special chars like ! // Use single quotes to prevent shell expansion of special chars like !
@ -253,6 +258,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
IMPORTANT: Use single quotes as shown - do NOT escape special characters like ! or $ inside single quotes IMPORTANT: Use single quotes as shown - do NOT escape special characters like ! or $ inside single quotes
Run in background (&) to avoid blocking`, Run in background (&) to avoid blocking`,
); );
// Track files that had TTS injection applied
if (targetPath && (hasPartyMode || hasAgentTTS)) {
const injectionType = hasPartyMode ? 'party-mode' : 'agent-tts';
this.ttsInjectedFiles.push({ path: targetPath, type: injectionType });
}
} else { } else {
// Strip injection markers cleanly when AgentVibes is disabled // Strip injection markers cleanly when AgentVibes is disabled
content = content.replaceAll(/<!-- TTS_INJECTION:party-mode -->\n?/g, ''); content = content.replaceAll(/<!-- TTS_INJECTION:party-mode -->\n?/g, '');
@ -1021,6 +1032,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
modules: config.modules, modules: config.modules,
ides: config.ides, ides: config.ides,
customFiles: customFiles.length > 0 ? customFiles : undefined, customFiles: customFiles.length > 0 ? customFiles : undefined,
ttsInjectedFiles: this.enableAgentVibes && this.ttsInjectedFiles.length > 0 ? this.ttsInjectedFiles : undefined,
agentVibesEnabled: this.enableAgentVibes || false,
}); });
// Offer cleanup for legacy files (only for updates, not fresh installs, and only if not skipped) // Offer cleanup for legacy files (only for updates, not fresh installs, and only if not skipped)
@ -1526,13 +1539,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Build YAML + customize to .md // Build YAML + customize to .md
const customizeExists = await fs.pathExists(customizePath); const customizeExists = await fs.pathExists(customizePath);
const xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, { let xmlContent = await this.xmlHandler.buildFromYaml(yamlPath, customizeExists ? customizePath : null, {
includeMetadata: true, includeMetadata: true,
}); });
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir); // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Process TTS injection points (pass targetPath for tracking)
xmlContent = this.processTTSInjectionPoints(xmlContent, mdPath);
// Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline // Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n'; const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
await fs.writeFile(mdPath, content, 'utf8'); await fs.writeFile(mdPath, content, 'utf8');
@ -1628,13 +1644,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
} }
// Build YAML to XML .md // Build YAML to XML .md
const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, { let xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
includeMetadata: true, includeMetadata: true,
}); });
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir); // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Process TTS injection points (pass targetPath for tracking)
xmlContent = this.processTTSInjectionPoints(xmlContent, targetMdPath);
// Write the built .md file with POSIX-compliant final newline // Write the built .md file with POSIX-compliant final newline
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n'; const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
await fs.writeFile(targetMdPath, content, 'utf8'); await fs.writeFile(targetMdPath, content, 'utf8');
@ -1722,13 +1741,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
} }
// Build YAML + customize to .md // Build YAML + customize to .md
const xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, { let xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
includeMetadata: true, includeMetadata: true,
}); });
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir); // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Process TTS injection points (pass targetPath for tracking)
xmlContent = this.processTTSInjectionPoints(xmlContent, targetMdPath);
// Write the rebuilt .md file with POSIX-compliant final newline // Write the rebuilt .md file with POSIX-compliant final newline
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n'; const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
await fs.writeFile(targetMdPath, content, 'utf8'); await fs.writeFile(targetMdPath, content, 'utf8');

View File

@ -125,12 +125,15 @@ class WorkflowCommandGenerator {
} }
} }
const coreWorkflowPath = `${this.bmadFolderName}/core/tasks/workflow.xml`;
// Replace template variables // Replace template variables
return template return template
.replaceAll('{{name}}', workflow.name) .replaceAll('{{name}}', workflow.name)
.replaceAll('{{module}}', workflow.module) .replaceAll('{{module}}', workflow.module)
.replaceAll('{{description}}', workflow.description) .replaceAll('{{description}}', workflow.description)
.replaceAll('{{workflow_path}}', workflowPath) .replaceAll('{{workflow_path}}', workflowPath)
.replaceAll('{{core_workflow_path}}', coreWorkflowPath)
.replaceAll('{bmad_folder}', this.bmadFolderName) .replaceAll('{bmad_folder}', this.bmadFolderName)
.replaceAll('{*bmad_folder*}', '{bmad_folder}'); .replaceAll('{*bmad_folder*}', '{bmad_folder}');
} }

View File

@ -5,7 +5,7 @@ description: '{{description}}'
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded: IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
<steps CRITICAL="TRUE"> <steps CRITICAL="TRUE">
1. Always LOAD the FULL @{bmad_folder}/core/tasks/workflow.xml 1. Always LOAD the FULL @{{core_workflow_path}}
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config @{{workflow_path}} 2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config @{{workflow_path}}
3. Pass the yaml path {{workflow_path}} as 'workflow-config' parameter to the workflow.xml instructions 3. Pass the yaml path {{workflow_path}} as 'workflow-config' parameter to the workflow.xml instructions
4. Follow workflow.xml instructions EXACTLY as written to process and follow the specific workflow config and its instructions 4. Follow workflow.xml instructions EXACTLY as written to process and follow the specific workflow config and its instructions

View File

@ -482,10 +482,39 @@ function compileAgent(yamlContent, answers = {}, agentName = '', targetPath = ''
}; };
} }
/**
* Process TTS injection markers in content
* @param {string} content - Content to process
* @param {boolean} enableAgentVibes - Whether AgentVibes is enabled
* @returns {Object} { content: string, hadInjection: boolean }
*/
function processTTSInjectionPoints(content, enableAgentVibes) {
const hasAgentTTS = content.includes('<!-- TTS_INJECTION:agent-tts -->');
if (enableAgentVibes && hasAgentTTS) {
// Replace agent-tts injection marker with TTS rule
content = content.replaceAll(
'<!-- TTS_INJECTION:agent-tts -->',
`- 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 <agent id="..."> 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`,
);
return { content, hadInjection: true };
} else if (!enableAgentVibes && hasAgentTTS) {
// Strip injection markers when disabled
content = content.replaceAll(/<!-- TTS_INJECTION:agent-tts -->\n?/g, '');
}
return { content, hadInjection: false };
}
/** /**
* Compile agent file to .md * Compile agent file to .md
* @param {string} yamlPath - Path to agent YAML file * @param {string} yamlPath - Path to agent YAML file
* @param {Object} options - { answers: {}, outputPath: string } * @param {Object} options - { answers: {}, outputPath: string, enableAgentVibes: boolean }
* @returns {Object} Compilation result * @returns {Object} Compilation result
*/ */
function compileAgentFile(yamlPath, options = {}) { function compileAgentFile(yamlPath, options = {}) {
@ -501,13 +530,24 @@ function compileAgentFile(yamlPath, options = {}) {
outputPath = path.join(dir, `${basename}.md`); outputPath = path.join(dir, `${basename}.md`);
} }
// Process TTS injection points if enableAgentVibes option is provided
let xml = result.xml;
let ttsInjected = false;
if (options.enableAgentVibes !== undefined) {
const ttsResult = processTTSInjectionPoints(xml, options.enableAgentVibes);
xml = ttsResult.content;
ttsInjected = ttsResult.hadInjection;
}
// Write compiled XML // Write compiled XML
fs.writeFileSync(outputPath, result.xml, 'utf8'); fs.writeFileSync(outputPath, xml, 'utf8');
return { return {
...result, ...result,
xml,
outputPath, outputPath,
sourcePath: yamlPath, sourcePath: yamlPath,
ttsInjected,
}; };
} }

View File

@ -363,11 +363,60 @@ class UI {
`🔧 Tools Configured: ${result.ides?.length > 0 ? result.ides.join(', ') : 'none'}`, `🔧 Tools Configured: ${result.ides?.length > 0 ? result.ides.join(', ') : 'none'}`,
]; ];
// Add AgentVibes TTS info if enabled
if (result.agentVibesEnabled) {
summary.push(`🎤 AgentVibes TTS: Enabled`);
}
CLIUtils.displayBox(summary.join('\n\n'), { CLIUtils.displayBox(summary.join('\n\n'), {
borderColor: 'green', borderColor: 'green',
borderStyle: 'round', borderStyle: 'round',
}); });
// Display TTS injection details if present
if (result.ttsInjectedFiles && result.ttsInjectedFiles.length > 0) {
console.log('\n' + chalk.cyan.bold('═══════════════════════════════════════════════════'));
console.log(chalk.cyan.bold(' AgentVibes TTS Injection Summary'));
console.log(chalk.cyan.bold('═══════════════════════════════════════════════════\n'));
// Explain what TTS injection is
console.log(chalk.white.bold('What is TTS Injection?\n'));
console.log(chalk.dim(' TTS (Text-to-Speech) injection adds voice instructions to BMAD agents,'));
console.log(chalk.dim(' enabling them to speak their responses aloud using AgentVibes.\n'));
console.log(chalk.dim(' Example: When you activate the PM agent, it will greet you with'));
console.log(chalk.dim(' spoken audio like "Hey! I\'m your Project Manager. How can I help?"\n'));
console.log(chalk.green(`✅ TTS injection applied to ${result.ttsInjectedFiles.length} file(s):\n`));
// Group by type
const partyModeFiles = result.ttsInjectedFiles.filter((f) => f.type === 'party-mode');
const agentTTSFiles = result.ttsInjectedFiles.filter((f) => f.type === 'agent-tts');
if (partyModeFiles.length > 0) {
console.log(chalk.yellow(' Party Mode (multi-agent conversations):'));
for (const file of partyModeFiles) {
console.log(chalk.dim(`${file.path}`));
}
}
if (agentTTSFiles.length > 0) {
console.log(chalk.yellow(' Agent TTS (individual agent voices):'));
for (const file of agentTTSFiles) {
console.log(chalk.dim(`${file.path}`));
}
}
// Show backup info and restore command
console.log('\n' + chalk.white.bold('Backups & Recovery:\n'));
console.log(chalk.dim(' Pre-injection backups are stored in:'));
console.log(chalk.cyan(' ~/.bmad-tts-backups/\n'));
console.log(chalk.dim(' To restore original files (removes TTS instructions):'));
console.log(chalk.cyan(` bmad-tts-injector.sh --restore ${result.path}\n`));
console.log(chalk.cyan('💡 BMAD agents will now speak when activated!'));
console.log(chalk.dim(' Ensure AgentVibes is installed: https://agentvibes.org'));
}
console.log('\n' + chalk.green.bold('✨ BMAD is ready to use!')); console.log('\n' + chalk.green.bold('✨ BMAD is ready to use!'));
} }