Compare commits
5 Commits
44f06c4bbb
...
56c0aa0993
| Author | SHA1 | Date |
|---|---|---|
|
|
56c0aa0993 | |
|
|
8265bbf295 | |
|
|
f99e192e74 | |
|
|
4d48b0dbe1 | |
|
|
10dc25f43d |
|
|
@ -0,0 +1,256 @@
|
||||||
|
# BMad Method PR #1: Ring of Fire (ROF) Sessions
|
||||||
|
|
||||||
|
**Feature Type**: Core workflow enhancement
|
||||||
|
**Status**: Draft for community review
|
||||||
|
**Origin**: tellingCube project (masemIT e.U.)
|
||||||
|
**Author**: Mario Semper (@sempre)
|
||||||
|
**Date**: 2025-11-23
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Ring of Fire (ROF) Sessions** enable multi-agent collaborative sessions that run in parallel to the user's main workflow, allowing users to delegate complex multi-perspective analysis while continuing other work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
Current BMad Method requires **sequential agent interaction**. When users need multiple agents to collaborate on a complex topic, they must:
|
||||||
|
- Manually orchestrate each agent conversation
|
||||||
|
- Stay in the loop for every exchange
|
||||||
|
- Wait for sequential responses before proceeding
|
||||||
|
- Context-switch constantly between tasks
|
||||||
|
|
||||||
|
This creates **bottlenecks** and prevents **parallel work streams**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proposed Solution: Ring of Fire Sessions
|
||||||
|
|
||||||
|
A new command pattern that enables **scoped multi-agent collaboration sessions** that run while the user continues other work.
|
||||||
|
|
||||||
|
### Command Syntax
|
||||||
|
|
||||||
|
```bash
|
||||||
|
*rof "<topic>" --agents <agent-list> [--report brief|detailed|live]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
*rof "API Refactoring Strategy" --agents dev,architect,qa --report brief
|
||||||
|
```
|
||||||
|
|
||||||
|
**What happens**:
|
||||||
|
1. Dev, Architect, and QA agents enter a collaborative session
|
||||||
|
2. They analyze the topic together (code review, design discussion, testing concerns)
|
||||||
|
3. When agents need tool access (read files, run commands), they request user approval
|
||||||
|
4. User continues working on other tasks in parallel
|
||||||
|
5. Session ends with consolidated report (brief: just recommendations, detailed: full transcript)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
### 1. User-Controlled Scope
|
||||||
|
- **Small**: 2 agents, 5-minute quick discussion
|
||||||
|
- **Large**: 10 agents, 2-hour deep analysis
|
||||||
|
- User decides granularity based on complexity
|
||||||
|
|
||||||
|
### 2. Approval-Gated Tool Access
|
||||||
|
- Agents can **discuss** freely within the session
|
||||||
|
- When agents need **tools** (read files, execute commands, make changes), they:
|
||||||
|
- Pause the session
|
||||||
|
- Request user approval
|
||||||
|
- Resume after user decision
|
||||||
|
|
||||||
|
**Why**: Maintains user control, prevents runaway agent actions
|
||||||
|
|
||||||
|
### 3. Flexible Reporting
|
||||||
|
|
||||||
|
| Mode | Description | Use Case |
|
||||||
|
|------|-------------|----------|
|
||||||
|
| `brief` | Final recommendations only | "Just tell me what to do" |
|
||||||
|
| `detailed` | Full transcript + recommendations | "Show me the reasoning" |
|
||||||
|
| `live` | Real-time updates as agents discuss | "I want to observe" |
|
||||||
|
|
||||||
|
**Default**: `brief` with Q&A available
|
||||||
|
|
||||||
|
### 4. Parallel Workflows
|
||||||
|
- User works on **Task A** while ROF session tackles **Task B**
|
||||||
|
- No context-switching overhead
|
||||||
|
- Efficient use of time
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
### 1. Architecture Reviews
|
||||||
|
```bash
|
||||||
|
*rof "Evaluate microservices vs monolith for new feature" --agents architect,dev,qa
|
||||||
|
```
|
||||||
|
**Agents collaborate on**: Design trade-offs, implementation complexity, testing implications
|
||||||
|
|
||||||
|
### 2. Code Refactoring
|
||||||
|
```bash
|
||||||
|
*rof "Refactor authentication module" --agents dev,architect --report detailed
|
||||||
|
```
|
||||||
|
**Agents collaborate on**: Current code analysis, refactoring approach, migration strategy
|
||||||
|
|
||||||
|
### 3. Feature Planning
|
||||||
|
```bash
|
||||||
|
*rof "Plan user notifications feature" --agents pm,ux,dev --report brief
|
||||||
|
```
|
||||||
|
**Agents collaborate on**: Requirements, UX flow, technical feasibility, timeline
|
||||||
|
|
||||||
|
### 4. Quality Gates
|
||||||
|
```bash
|
||||||
|
*rof "Investigate test failures in CI/CD" --agents qa,dev --report live
|
||||||
|
```
|
||||||
|
**Agents collaborate on**: Root cause analysis, fix recommendations, regression prevention
|
||||||
|
|
||||||
|
### 5. Documentation Sprints
|
||||||
|
```bash
|
||||||
|
*rof "Document API endpoints" --agents dev,pm,ux
|
||||||
|
```
|
||||||
|
**Agents collaborate on**: Technical accuracy, user-friendly examples, completeness
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Experience Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
User->>River: *rof "Topic" --agents dev,architect
|
||||||
|
River->>Dev: Join ROF session
|
||||||
|
River->>Architect: Join ROF session
|
||||||
|
River->>User: Session started, continue your work
|
||||||
|
|
||||||
|
Dev->>Architect: Discuss approach
|
||||||
|
Architect->>Dev: Suggest alternatives
|
||||||
|
|
||||||
|
Dev->>User: Need to read auth.ts - approve?
|
||||||
|
User->>Dev: Approved
|
||||||
|
Dev->>Architect: After reading file...
|
||||||
|
|
||||||
|
Architect->>Dev: Recommendation
|
||||||
|
Dev->>River: Session complete
|
||||||
|
River->>User: Brief report: [Recommendations]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Considerations
|
||||||
|
|
||||||
|
### Technical Requirements
|
||||||
|
- **Session state management**: Track active ROF sessions, participating agents
|
||||||
|
- **Agent context sharing**: Agents share knowledge within session scope
|
||||||
|
- **User approval workflow**: Clear prompt for tool requests
|
||||||
|
- **Report generation**: Brief/detailed/live output formatting
|
||||||
|
- **Workflow integration**: Link ROF findings to existing workflow plans/todos
|
||||||
|
|
||||||
|
### Open Questions for Community
|
||||||
|
|
||||||
|
1. **Integration**: Core BMad feature or plugin/extension?
|
||||||
|
2. **Concurrency**: How to handle file conflicts if multiple agents want to edit?
|
||||||
|
3. **Cost Model**: Guidance for LLM call budgeting with multiple agents?
|
||||||
|
4. **Session Limits**: Recommended max agents/duration?
|
||||||
|
5. **Agent Communication**: Free-form discussion or structured turn-taking?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Real-World Validation
|
||||||
|
|
||||||
|
**Origin Project**: tellingCube (BI dashboard, masemIT e.U.)
|
||||||
|
|
||||||
|
**Validation Scenario**:
|
||||||
|
- **Topic**: "Next steps for tellingCube after validation test"
|
||||||
|
- **Agents**: River (orchestrator), Mary (analyst), Winston (architect)
|
||||||
|
- **Report Mode**: Brief
|
||||||
|
- **Outcome**: Successfully analyzed post-validation roadmap with 3 scenarios (GO/CHANGE/NO-GO), delivered consolidated recommendations in 5 minutes
|
||||||
|
|
||||||
|
**User Feedback (Mario Semper)**:
|
||||||
|
> "This is exactly what I needed - I wanted multiple perspectives without having to orchestrate every conversation. The brief report gave me actionable next steps immediately."
|
||||||
|
|
||||||
|
**Documentation**: `docs/_masemIT/readme.md` in tellingCube repository
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proposed Documentation Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.bmad-core/
|
||||||
|
features/
|
||||||
|
ring-of-fire.md # Feature specification
|
||||||
|
|
||||||
|
docs/
|
||||||
|
guides/
|
||||||
|
using-rof-sessions.md # User guide with examples
|
||||||
|
|
||||||
|
architecture/
|
||||||
|
agent-collaboration.md # Technical design
|
||||||
|
rof-session-management.md # State handling approach
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
✅ **Unlocks parallel workflows** - User productivity gains
|
||||||
|
✅ **Reduces context-switching** - Cognitive load reduction
|
||||||
|
✅ **Enables complex analysis** - Multi-perspective insights
|
||||||
|
✅ **Maintains user control** - Approval gates for tools
|
||||||
|
✅ **Scales flexibly** - From quick checks to deep dives
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comparison to Existing Patterns
|
||||||
|
|
||||||
|
| Feature | Standard Agent Use | ROF Session |
|
||||||
|
|---------|-------------------|-------------|
|
||||||
|
| Agent collaboration | Sequential (one at a time) | Parallel (multiple simultaneously) |
|
||||||
|
| User involvement | Required for every exchange | Only for approvals |
|
||||||
|
| Parallel work | No (user waits) | Yes (user continues tasks) |
|
||||||
|
| Output | Chat transcript | Consolidated report |
|
||||||
|
| Use case | Single-perspective tasks | Multi-perspective analysis |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Community feedback** on approach and open questions
|
||||||
|
2. **Technical design** refinement (state management, agent communication)
|
||||||
|
3. **Prototype implementation** in BMad core or as extension
|
||||||
|
4. **Beta testing** with real projects (beyond tellingCube)
|
||||||
|
5. **Documentation** completion with examples
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
### Alt 1: "Breakout Session"
|
||||||
|
- **Pros**: Clear meeting metaphor
|
||||||
|
- **Cons**: Less evocative, doesn't convey "continuous collaborative space"
|
||||||
|
|
||||||
|
### Alt 2: "Agent Huddle"
|
||||||
|
- **Pros**: Short, casual
|
||||||
|
- **Cons**: Implies quick/informal only
|
||||||
|
|
||||||
|
### Alt 3: "Lagerfeuer" (original German name)
|
||||||
|
- **Pros**: Warm, campfire metaphor
|
||||||
|
- **Cons**: Poor i18n, hard to pronounce/remember for non-German speakers
|
||||||
|
|
||||||
|
**Chosen**: **Ring of Fire** - evokes continuous collaboration circle, internationally understood, memorable, shortcut "ROF" works well
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- **Source Project**: tellingCube (https://github.com/masemIT/telling-cube) [if public]
|
||||||
|
- **Documentation**: `docs/_masemIT/readme.md`
|
||||||
|
- **Discussion**: [Link to BMad community discussion if applicable]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Contribution ready for review.** Feedback welcome! 🔥
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue