From 11a1dbaefcf789a145c13e2883ec073c5b47b2e0 Mon Sep 17 00:00:00 2001 From: mreis-atlassian Date: Thu, 27 Nov 2025 02:05:04 +1100 Subject: [PATCH 01/12] feat: Adding support for Rovo Dev (#975) - Adding support for rovo dev - Adding rovo dev translation wrappers --- docs/ide-info/rovo-dev.md | 388 ++++++++++++++++++ docs/index.md | 1 + .../installers-modules-platforms-reference.md | 2 +- tools/cli/README.md | 1 + tools/cli/installers/lib/core/installer.js | 4 +- tools/cli/installers/lib/ide/rovo-dev.js | 290 +++++++++++++ tools/platform-codes.yaml | 6 + 7 files changed, 690 insertions(+), 2 deletions(-) create mode 100644 docs/ide-info/rovo-dev.md create mode 100644 tools/cli/installers/lib/ide/rovo-dev.js diff --git a/docs/ide-info/rovo-dev.md b/docs/ide-info/rovo-dev.md new file mode 100644 index 00000000..a6445758 --- /dev/null +++ b/docs/ide-info/rovo-dev.md @@ -0,0 +1,388 @@ +# Rovo Dev IDE Integration + +This document describes how BMAD-METHOD integrates with [Atlassian Rovo Dev](https://www.atlassian.com/rovo-dev), an AI-powered software development assistant. + +## Overview + +Rovo Dev is designed to integrate deeply with developer workflows and organizational knowledge bases. When you install BMAD-METHOD in a Rovo Dev project, it automatically installs BMAD agents, workflows, tasks, and tools just like it does for other IDEs (Cursor, VS Code, etc.). + +BMAD-METHOD provides: + +- **Agents**: Specialized subagents for various development tasks +- **Workflows**: Multi-step workflow guides and coordinators +- **Tasks & Tools**: Reference documentation for BMAD tasks and tools + +### What are Rovo Dev Subagents? + +Subagents are specialized agents that Rovo Dev can delegate tasks to. They are defined as Markdown files with YAML frontmatter stored in the `.rovodev/subagents/` directory. Rovo Dev automatically discovers these files and makes them available through the `@subagent-name` syntax. + +## Installation and Setup + +### Automatic Installation + +When you run the BMAD-METHOD installer and select Rovo Dev as your IDE: + +```bash +bmad install +``` + +The installer will: + +1. Create a `.rovodev/subagents/` directory in your project (if it doesn't exist) +2. Convert BMAD agents into Rovo Dev subagent format +3. Write subagent files with the naming pattern: `bmad--.md` + +### File Structure + +After installation, your project will have: + +``` +project-root/ +├── .rovodev/ +│ ├── subagents/ +│ │ ├── bmad-core-code-reviewer.md +│ │ ├── bmad-bmm-pm.md +│ │ ├── bmad-bmm-dev.md +│ │ └── ... (more agents from selected modules) +│ ├── workflows/ +│ │ ├── bmad-brainstorming.md +│ │ ├── bmad-prd-creation.md +│ │ └── ... (workflow guides) +│ ├── references/ +│ │ ├── bmad-task-core-code-review.md +│ │ ├── bmad-tool-core-analysis.md +│ │ └── ... (task/tool references) +│ ├── config.yml (Rovo Dev configuration) +│ ├── prompts.yml (Optional: reusable prompts) +│ └── ... +├── .bmad/ (BMAD installation directory) +└── ... +``` + +**Directory Structure Explanation:** + +- **subagents/**: Agents discovered and used by Rovo Dev with `@agent-name` syntax +- **workflows/**: Multi-step workflow guides and instructions +- **references/**: Documentation for available tasks and tools in BMAD + +## Subagent File Format + +BMAD agents are converted to Rovo Dev subagent format, which uses Markdown with YAML frontmatter: + +### Basic Structure + +```markdown +--- +name: bmad-module-agent-name +description: One sentence description of what this agent does +tools: + - bash + - open_files + - grep + - expand_code_chunks +model: anthropic.claude-3-5-sonnet-20241022-v2:0 # Optional +load_memory: true # Optional +--- + +You are a specialized agent for [specific task]. + +## Your Role + +Describe the agent's role and responsibilities... + +## Key Instructions + +1. First instruction +2. Second instruction +3. Third instruction + +## When to Use This Agent + +Explain when and how to use this agent... +``` + +### YAML Frontmatter Fields + +| Field | Type | Required | Description | +| ------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | string | Yes | Unique identifier for the subagent (kebab-case, no spaces) | +| `description` | string | Yes | One-line description of the subagent's purpose | +| `tools` | array | No | List of tools the subagent can use. If not specified, uses parent agent's tools | +| `model` | string | No | Specific LLM model for this subagent (e.g., `anthropic.claude-3-5-sonnet-20241022-v2:0`). If not specified, uses parent agent's model | +| `load_memory` | boolean | No | Whether to load default memory files (AGENTS.md, AGENTS.local.md). Defaults to `true` | + +### System Prompt + +The content after the closing `---` is the subagent's system prompt. This defines: + +- The agent's persona and role +- Its capabilities and constraints +- Step-by-step instructions for task execution +- Examples of expected behavior + +## Using BMAD Components in Rovo Dev + +### Invoking a Subagent (Agent) + +In Rovo Dev, you can invoke a BMAD agent as a subagent using the `@` syntax: + +``` +@bmad-core-code-reviewer Please review this PR for potential issues +@bmad-bmm-pm Help plan this feature release +@bmad-bmm-dev Implement this feature +``` + +### Accessing Workflows + +Workflow guides are available in `.rovodev/workflows/` directory: + +``` +@bmad-core-code-reviewer Use the brainstorming workflow from .rovodev/workflows/bmad-brainstorming.md +``` + +Workflow files contain step-by-step instructions and can be referenced or copied into Rovo Dev for collaborative workflow execution. + +### Accessing Tasks and Tools + +Task and tool documentation is available in `.rovodev/references/` directory. These provide: + +- Task execution instructions +- Tool capabilities and usage +- Integration examples +- Parameter documentation + +### Example Usage Scenarios + +#### Code Review + +``` +@bmad-core-code-reviewer Review the changes in src/components/Button.tsx +for best practices, performance, and potential bugs +``` + +#### Documentation + +``` +@bmad-core-documentation-writer Generate API documentation for the new +user authentication module +``` + +#### Feature Design + +``` +@bmad-module-feature-designer Design a solution for implementing +dark mode support across the application +``` + +## Customizing BMAD Subagents + +You can customize BMAD subagents after installation by editing their files directly in `.rovodev/subagents/`. + +### Example: Adding Tool Restrictions + +By default, BMAD subagents inherit tools from the parent Rovo Dev agent. You can restrict which tools a specific subagent can use: + +```yaml +--- +name: bmad-core-code-reviewer +description: Reviews code and suggests improvements +tools: + - open_files + - expand_code_chunks + - grep +--- +``` + +### Example: Using a Specific Model + +Some agents might benefit from using a different model. You can specify this: + +```yaml +--- +name: bmad-core-documentation-writer +description: Writes clear and comprehensive documentation +model: anthropic.claude-3-5-sonnet-20241022-v2:0 +--- +``` + +### Example: Enhancing the System Prompt + +You can add additional context to a subagent's system prompt: + +```markdown +--- +name: bmad-core-code-reviewer +description: Reviews code and suggests improvements +--- + +You are a specialized code review agent for our project. + +## Project Context + +Our codebase uses: + +- React 18 for frontend +- Node.js 18+ for backend +- TypeScript for type safety +- Jest for testing + +## Review Checklist + +1. Type safety and TypeScript correctness +2. React best practices and hooks usage +3. Performance considerations +4. Test coverage +5. Documentation and comments + +...rest of original system prompt... +``` + +## Memory and Context + +By default, BMAD subagents have `load_memory: true`, which means they will load memory files from your project: + +- **Project-level**: `.rovodev/AGENTS.md` and `.rovodev/.agent.md` +- **User-level**: `~/.rovodev/AGENTS.md` (global memory across all projects) + +These files can contain: + +- Project guidelines and conventions +- Common patterns and best practices +- Recent decisions and context +- Custom instructions for all agents + +### Creating Project Memory + +Create `.rovodev/AGENTS.md` in your project: + +```markdown +# Project Guidelines + +## Code Style + +- Use 2-space indentation +- Use camelCase for variables +- Use PascalCase for classes + +## Architecture + +- Follow modular component structure +- Use dependency injection for services +- Implement proper error handling + +## Testing Requirements + +- Minimum 80% code coverage +- Write tests before implementation +- Use descriptive test names +``` + +## Troubleshooting + +### Subagents Not Appearing in Rovo Dev + +1. **Verify files exist**: Check that `.rovodev/subagents/bmad-*.md` files are present +2. **Check Rovo Dev is reloaded**: Rovo Dev may cache agent definitions. Restart Rovo Dev or reload the project +3. **Verify file format**: Ensure files have proper YAML frontmatter (between `---` markers) +4. **Check file permissions**: Ensure files are readable by Rovo Dev + +### Agent Name Conflicts + +If you have custom subagents with the same names as BMAD agents, Rovo Dev will load both but may show a warning. Use unique prefixes for custom subagents to avoid conflicts. + +### Tools Not Available + +If a subagent's tools aren't working: + +1. Verify the tool names match Rovo Dev's available tools +2. Check that the parent Rovo Dev agent has access to those tools +3. Ensure tool permissions are properly configured in `.rovodev/config.yml` + +## Advanced: Tool Configuration + +Rovo Dev agents have access to a set of tools for various tasks. Common tools available include: + +- `bash`: Execute shell commands +- `open_files`: View file contents +- `grep`: Search across files +- `expand_code_chunks`: View specific code sections +- `find_and_replace_code`: Modify files +- `create_file`: Create new files +- `delete_file`: Delete files +- `move_file`: Rename or move files + +### MCP Servers + +Rovo Dev can also connect to Model Context Protocol (MCP) servers, which provide additional tools and data sources: + +- **Atlassian Integration**: Access to Jira, Confluence, and Bitbucket +- **Code Analysis**: Custom code analysis and metrics +- **External Services**: APIs and third-party integrations + +Configure MCP servers in `~/.rovodev/mcp.json` or `.rovodev/mcp.json`. + +## Integration with Other IDE Handlers + +BMAD-METHOD supports multiple IDEs simultaneously. You can have both Rovo Dev and other IDE configurations (Cursor, VS Code, etc.) in the same project. Each IDE will have its own artifacts installed in separate directories. + +For example: + +- Rovo Dev agents: `.rovodev/subagents/bmad-*.md` +- Cursor rules: `.cursor/rules/bmad/` +- Claude Code: `.claude/rules/bmad/` + +## Performance Considerations + +- BMAD subagent files are typically small (1-5 KB each) +- Rovo Dev lazy-loads subagents, so having many subagents doesn't impact startup time +- System prompts are cached by Rovo Dev after first load + +## Best Practices + +1. **Keep System Prompts Concise**: Shorter, well-structured prompts are more effective +2. **Use Project Memory**: Leverage `.rovodev/AGENTS.md` for shared context +3. **Customize Tool Restrictions**: Give subagents only the tools they need +4. **Test Subagent Invocations**: Verify each subagent works as expected for your project +5. **Version Control**: Commit `.rovodev/subagents/` to version control for team consistency +6. **Document Custom Subagents**: Add comments explaining the purpose of customized subagents + +## Related Documentation + +- [Rovo Dev Official Documentation](https://www.atlassian.com/rovo-dev) +- [BMAD-METHOD Installation Guide](./installation.md) +- [IDE Handler Architecture](./ide-handlers.md) +- [Rovo Dev Configuration Reference](https://www.atlassian.com/rovo-dev/configuration) + +## Examples + +### Example 1: Code Review Workflow + +``` +User: @bmad-core-code-reviewer Review src/auth/login.ts for security issues +Rovo Dev → Subagent: Opens file, analyzes code, suggests improvements +Subagent output: Security vulnerabilities found, recommendations provided +``` + +### Example 2: Documentation Generation + +``` +User: @bmad-core-documentation-writer Generate API docs for the new payment module +Rovo Dev → Subagent: Analyzes code structure, generates documentation +Subagent output: Markdown documentation with examples and API reference +``` + +### Example 3: Architecture Design + +``` +User: @bmad-module-feature-designer Design a caching strategy for the database layer +Rovo Dev → Subagent: Reviews current architecture, proposes design +Subagent output: Detailed architecture proposal with implementation plan +``` + +## Support + +For issues or questions about: + +- **Rovo Dev**: See [Atlassian Rovo Dev Documentation](https://www.atlassian.com/rovo-dev) +- **BMAD-METHOD**: See [BMAD-METHOD README](../README.md) +- **IDE Integration**: See [IDE Handler Guide](./ide-handlers.md) diff --git a/docs/index.md b/docs/index.md index 0db96b2f..d5e1f83e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -87,6 +87,7 @@ Instructions for loading agents and running workflows in your development enviro - [OpenCode](./ide-info/opencode.md) - [Qwen](./ide-info/qwen.md) - [Roo](./ide-info/roo.md) +- [Rovo Dev](./ide-info/rovo-dev.md) - [Trae](./ide-info/trae.md) **Key concept:** Every reference to "load an agent" or "activate an agent" in the main docs links to the [ide-info](./ide-info/) directory for IDE-specific instructions. diff --git a/docs/installers-bundlers/installers-modules-platforms-reference.md b/docs/installers-bundlers/installers-modules-platforms-reference.md index df54e875..62f1a398 100644 --- a/docs/installers-bundlers/installers-modules-platforms-reference.md +++ b/docs/installers-bundlers/installers-modules-platforms-reference.md @@ -171,7 +171,7 @@ communication_language: "English" - Windsurf **Additional**: -Cline, Roo, Auggie, GitHub Copilot, Codex, Gemini, Qwen, Trae, Kilo, Crush, iFlow +Cline, Roo, Rovo Dev,Auggie, GitHub Copilot, Codex, Gemini, Qwen, Trae, Kilo, Crush, iFlow ### Platform Features diff --git a/tools/cli/README.md b/tools/cli/README.md index 944b2948..5c794f16 100644 --- a/tools/cli/README.md +++ b/tools/cli/README.md @@ -162,6 +162,7 @@ The installer supports **15 IDE environments** through a base-derived architectu | `gemini` | Google Gemini | `.gemini/` | | `qwen` | Qwen | `.qwen/` | | `roo` | Roo | `.roo/` | +| `rovo-dev` | Rovo | `.rovodev/` | | `trae` | Trae | `.trae/` | | `iflow` | iFlow | `.iflow/` | | `kilo` | Kilo | `.kilo/` | diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 3f72cd59..7473a307 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -166,7 +166,9 @@ class Installer { for (const ide of newlySelectedIdes) { // List of IDEs that have interactive prompts - const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini'].includes(ide); + const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes( + ide, + ); if (needsPrompts) { // Get IDE handler and collect configuration diff --git a/tools/cli/installers/lib/ide/rovo-dev.js b/tools/cli/installers/lib/ide/rovo-dev.js new file mode 100644 index 00000000..8f460178 --- /dev/null +++ b/tools/cli/installers/lib/ide/rovo-dev.js @@ -0,0 +1,290 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const chalk = require('chalk'); +const { BaseIdeSetup } = require('./_base-ide'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); + +/** + * Rovo Dev IDE setup handler + * + * Installs BMAD agents as Rovo Dev subagents in .rovodev/subagents/ + * Installs workflows and tasks/tools as reference guides in .rovodev/ + * Rovo Dev automatically discovers agents and integrates with BMAD like other IDEs + */ +class RovoDevSetup extends BaseIdeSetup { + constructor() { + super('rovo-dev', 'Atlassian Rovo Dev', true); // preferred IDE + this.configDir = '.rovodev'; + this.subagentsDir = 'subagents'; + this.workflowsDir = 'workflows'; + this.referencesDir = 'references'; + } + + /** + * Cleanup old BMAD installation before reinstalling + * @param {string} projectDir - Project directory + */ + async cleanup(projectDir) { + const rovoDevDir = path.join(projectDir, this.configDir); + + if (!(await fs.pathExists(rovoDevDir))) { + return; + } + + // Clean BMAD agents from subagents directory + const subagentsDir = path.join(rovoDevDir, this.subagentsDir); + if (await fs.pathExists(subagentsDir)) { + const entries = await fs.readdir(subagentsDir); + const bmadFiles = entries.filter((file) => file.startsWith('bmad-') && file.endsWith('.md')); + + for (const file of bmadFiles) { + await fs.remove(path.join(subagentsDir, file)); + } + } + + // Clean BMAD workflows from workflows directory + const workflowsDir = path.join(rovoDevDir, this.workflowsDir); + if (await fs.pathExists(workflowsDir)) { + const entries = await fs.readdir(workflowsDir); + const bmadFiles = entries.filter((file) => file.startsWith('bmad-') && file.endsWith('.md')); + + for (const file of bmadFiles) { + await fs.remove(path.join(workflowsDir, file)); + } + } + + // Clean BMAD tasks/tools from references directory + const referencesDir = path.join(rovoDevDir, this.referencesDir); + if (await fs.pathExists(referencesDir)) { + const entries = await fs.readdir(referencesDir); + const bmadFiles = entries.filter((file) => file.startsWith('bmad-') && file.endsWith('.md')); + + for (const file of bmadFiles) { + await fs.remove(path.join(referencesDir, file)); + } + } + } + + /** + * Setup Rovo Dev configuration + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options + */ + async setup(projectDir, bmadDir, options = {}) { + console.log(chalk.cyan(`Setting up ${this.name}...`)); + + // Clean up old BMAD installation first + await this.cleanup(projectDir); + + // Create .rovodev directory structure + const rovoDevDir = path.join(projectDir, this.configDir); + const subagentsDir = path.join(rovoDevDir, this.subagentsDir); + const workflowsDir = path.join(rovoDevDir, this.workflowsDir); + const referencesDir = path.join(rovoDevDir, this.referencesDir); + + await this.ensureDir(subagentsDir); + await this.ensureDir(workflowsDir); + await this.ensureDir(referencesDir); + + // Generate and install agents + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + let agentCount = 0; + for (const artifact of agentArtifacts) { + const subagentFilename = `bmad-${artifact.module}-${artifact.name}.md`; + const targetPath = path.join(subagentsDir, subagentFilename); + const subagentContent = this.convertToRovoDevSubagent(artifact.content, artifact.name, artifact.module); + await this.writeFile(targetPath, subagentContent); + agentCount++; + } + + // Generate and install workflows + const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGen.collectWorkflowArtifacts(bmadDir); + + let workflowCount = 0; + for (const artifact of workflowArtifacts) { + if (artifact.type === 'workflow-command') { + const workflowFilename = path.basename(artifact.relativePath); + const targetPath = path.join(workflowsDir, workflowFilename); + await this.writeFile(targetPath, artifact.content); + workflowCount++; + } + } + + // Generate and install tasks and tools + const taskToolGen = new TaskToolCommandGenerator(); + const { tasks: taskCount, tools: toolCount } = await this.generateTaskToolReferences(bmadDir, referencesDir, taskToolGen); + + // Summary output + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents installed to .rovodev/subagents/`)); + if (workflowCount > 0) { + console.log(chalk.dim(` - ${workflowCount} workflows installed to .rovodev/workflows/`)); + } + if (taskCount + toolCount > 0) { + console.log( + chalk.dim(` - ${taskCount + toolCount} tasks/tools installed to .rovodev/references/ (${taskCount} tasks, ${toolCount} tools)`), + ); + } + console.log(chalk.yellow(`\n Note: Agents are automatically discovered by Rovo Dev`)); + console.log(chalk.dim(` - Access agents by typing @ in Rovo Dev to see available options`)); + console.log(chalk.dim(` - Workflows and references are available in .rovodev/ directory`)); + + return { + success: true, + agents: agentCount, + workflows: workflowCount, + tasks: taskCount, + tools: toolCount, + }; + } + + /** + * Generate task and tool reference guides + * @param {string} bmadDir - BMAD directory + * @param {string} referencesDir - References directory + * @param {TaskToolCommandGenerator} taskToolGen - Generator instance + */ + async generateTaskToolReferences(bmadDir, referencesDir, taskToolGen) { + const tasks = await taskToolGen.loadTaskManifest(bmadDir); + const tools = await taskToolGen.loadToolManifest(bmadDir); + + const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + + let taskCount = 0; + for (const task of standaloneTasks) { + const commandContent = taskToolGen.generateCommandContent(task, 'task'); + const targetPath = path.join(referencesDir, `bmad-task-${task.module}-${task.name}.md`); + await this.writeFile(targetPath, commandContent); + taskCount++; + } + + let toolCount = 0; + for (const tool of standaloneTools) { + const commandContent = taskToolGen.generateCommandContent(tool, 'tool'); + const targetPath = path.join(referencesDir, `bmad-tool-${tool.module}-${tool.name}.md`); + await this.writeFile(targetPath, commandContent); + toolCount++; + } + + return { tasks: taskCount, tools: toolCount }; + } + + /** + * Convert BMAD agent launcher to Rovo Dev subagent format + * + * Rovo Dev subagents use Markdown files with YAML frontmatter containing: + * - name: Unique identifier for the subagent + * - description: One-line description of the subagent's purpose + * - tools: Array of tools the subagent can use (optional) + * - model: Specific model for this subagent (optional) + * - load_memory: Whether to load memory files (optional, defaults to true) + * + * @param {string} launcherContent - Original agent launcher content + * @param {string} agentName - Name of the agent + * @param {string} moduleName - Name of the module + * @returns {string} Rovo Dev subagent-formatted content + */ + convertToRovoDevSubagent(launcherContent, agentName, moduleName) { + // Extract metadata from the launcher XML + const titleMatch = launcherContent.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(agentName); + + const descriptionMatch = launcherContent.match(/description="([^"]+)"/); + const description = descriptionMatch ? descriptionMatch[1] : `BMAD agent: ${title}`; + + const roleDefinitionMatch = launcherContent.match(/roleDefinition="([^"]+)"/); + const roleDefinition = roleDefinitionMatch ? roleDefinitionMatch[1] : `You are a specialized agent for ${title.toLowerCase()} tasks.`; + + // Extract the main system prompt from the launcher (content after closing tags) + let systemPrompt = roleDefinition; + + // Try to extract additional instructions from the launcher content + const instructionsMatch = launcherContent.match(/([\s\S]*?)<\/instructions>/); + if (instructionsMatch) { + systemPrompt += '\n\n' + instructionsMatch[1].trim(); + } + + // Build YAML frontmatter for Rovo Dev subagent + const frontmatter = { + name: `bmad-${moduleName}-${agentName}`, + description: description, + // Note: tools and model can be added by users in their .rovodev/subagents/*.md files + // We don't enforce specific tools since BMAD agents are flexible + }; + + // Create YAML frontmatter string with proper quoting for special characters + let yamlContent = '---\n'; + yamlContent += `name: ${frontmatter.name}\n`; + // Quote description to handle colons and other special characters in YAML + yamlContent += `description: "${frontmatter.description.replaceAll('"', String.raw`\"`)}"\n`; + yamlContent += '---\n'; + + // Combine frontmatter with system prompt + const subagentContent = yamlContent + systemPrompt; + + return subagentContent; + } + + /** + * Detect whether Rovo Dev is already configured in the project + * @param {string} projectDir - Project directory + * @returns {boolean} + */ + async detect(projectDir) { + const rovoDevDir = path.join(projectDir, this.configDir); + + if (!(await fs.pathExists(rovoDevDir))) { + return false; + } + + // Check for BMAD agents in subagents directory + const subagentsDir = path.join(rovoDevDir, this.subagentsDir); + if (await fs.pathExists(subagentsDir)) { + try { + const entries = await fs.readdir(subagentsDir); + if (entries.some((entry) => entry.startsWith('bmad-') && entry.endsWith('.md'))) { + return true; + } + } catch { + // Continue checking other directories + } + } + + // Check for BMAD workflows in workflows directory + const workflowsDir = path.join(rovoDevDir, this.workflowsDir); + if (await fs.pathExists(workflowsDir)) { + try { + const entries = await fs.readdir(workflowsDir); + if (entries.some((entry) => entry.startsWith('bmad-') && entry.endsWith('.md'))) { + return true; + } + } catch { + // Continue checking other directories + } + } + + // Check for BMAD tasks/tools in references directory + const referencesDir = path.join(rovoDevDir, this.referencesDir); + if (await fs.pathExists(referencesDir)) { + try { + const entries = await fs.readdir(referencesDir); + if (entries.some((entry) => entry.startsWith('bmad-') && entry.endsWith('.md'))) { + return true; + } + } catch { + // Continue + } + } + + return false; + } +} + +module.exports = { RovoDevSetup }; diff --git a/tools/platform-codes.yaml b/tools/platform-codes.yaml index 99c4b275..a58e2119 100644 --- a/tools/platform-codes.yaml +++ b/tools/platform-codes.yaml @@ -55,6 +55,12 @@ platforms: category: ide description: "Enhanced Cline fork" + rovo: + name: "Rovo Dev" + preferred: false + category: ide + description: "Atlassian's AI coding assistant" + github-copilot: name: "GitHub Copilot" preferred: false From 5702195ef73385b5d2ea474fe6f08f4f8d5c8650 Mon Sep 17 00:00:00 2001 From: Paul Preibisch Date: Wed, 26 Nov 2025 08:51:57 -0700 Subject: [PATCH 02/12] Add Text-to-Speech Integration via TTS_INJECTION System (#934) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 - ALWAYS communicate in {communication_language} - Stay in character until exit selected ``` 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 * 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 * 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 * 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 * 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 * 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 - Removed 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 * 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 Co-authored-by: Claude Co-authored-by: Paul Preibisch Co-authored-by: Brian --- TESTING.md | 115 ++++++ src/core/workflows/party-mode/instructions.md | 28 +- .../models/fragments/activation-rules.xml | 1 + test-bmad-pr.sh | 381 ++++++++++++++++++ tools/cli/commands/install.js | 40 ++ tools/cli/installers/lib/core/installer.js | 174 +++++++- tools/cli/lib/ui.js | 159 ++++++++ 7 files changed, 889 insertions(+), 9 deletions(-) create mode 100644 TESTING.md create mode 100755 test-bmad-pr.sh diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 00000000..37357302 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,115 @@ +# Testing AgentVibes Party Mode (PR #934) + +This guide helps you test the AgentVibes integration that adds multi-agent party mode with unique voices for each BMAD agent. + +## Quick Start + +We've created an automated test script that handles everything for you: + +```bash +curl -fsSL https://raw.githubusercontent.com/paulpreibisch/BMAD-METHOD/feature/agentvibes-tts-integration/test-bmad-pr.sh -o test-bmad-pr.sh +chmod +x test-bmad-pr.sh +./test-bmad-pr.sh +``` + +## What the Script Does + +The automated script will: + +1. Clone the BMAD repository +2. Checkout the PR branch with party mode features +3. Install BMAD CLI tools locally +4. Create a test BMAD project +5. Install AgentVibes TTS system +6. Configure unique voices for each agent +7. Verify the installation + +## Prerequisites + +- Node.js and npm installed +- Git installed +- ~500MB free disk space +- 10-15 minutes for complete setup + +## Manual Testing (Alternative) + +If you prefer manual installation: + +### 1. Clone and Setup BMAD + +```bash +git clone https://github.com/paulpreibisch/BMAD-METHOD.git +cd BMAD-METHOD +git fetch origin pull/934/head:agentvibes-party-mode +git checkout agentvibes-party-mode +cd tools/cli +npm install +npm link +``` + +### 2. Create Test Project + +```bash +mkdir -p ~/bmad-test-project +cd ~/bmad-test-project +bmad install +``` + +When prompted: + +- Enable TTS for agents? → **Yes** +- The installer will automatically prompt you to install AgentVibes + +### 3. Test Party Mode + +```bash +cd ~/bmad-test-project +claude-code +``` + +In Claude Code, run: + +``` +/bmad:core:workflows:party-mode +``` + +Each BMAD agent should speak with a unique voice! + +## Verification + +After installation, verify: + +✅ Voice map file exists: `.bmad/_cfg/agent-voice-map.csv` +✅ BMAD TTS hooks exist: `.claude/hooks/bmad-speak.sh` +✅ Each agent has a unique voice assigned +✅ Party mode works with distinct voices + +## Troubleshooting + +**No audio?** + +- Check: `.claude/hooks/play-tts.sh` exists +- Test current voice: `/agent-vibes:whoami` + +**Same voice for all agents?** + +- Check: `.bmad/_cfg/agent-voice-map.csv` has different voices +- List available voices: `/agent-vibes:list` + +## Report Issues + +Found a bug? Report it on the PR: +https://github.com/bmad-code-org/BMAD-METHOD/pull/934 + +## Cleanup + +To remove the test installation: + +```bash +# Remove test directory +rm -rf ~/bmad-test-project # or your custom test directory + +# Unlink BMAD CLI (optional) +cd ~/BMAD-METHOD/tools/cli +npm unlink +``` diff --git a/src/core/workflows/party-mode/instructions.md b/src/core/workflows/party-mode/instructions.md index 3ca8e052..6afd5ede 100644 --- a/src/core/workflows/party-mode/instructions.md +++ b/src/core/workflows/party-mode/instructions.md @@ -2,6 +2,7 @@ The workflow execution engine is governed by: {project_root}/{bmad_folder}/core/tasks/workflow.xml This workflow orchestrates group discussions between all installed BMAD agents + @@ -94,17 +95,36 @@ - 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. 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 + 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] + + 🏗️ [Winston]: I recommend using microservices for better scalability. + [Bash: .claude/hooks/bmad-speak.sh "Winston" "I recommend using microservices for better scalability."] + + 📋 [John]: But a monolith would get us to market faster for MVP. + [Bash: .claude/hooks/bmad-speak.sh "John" "But a monolith would get us to market faster for MVP."] + Maintain spacing between agents for readability Preserve each agent's unique voice throughout + Always include the agent's icon emoji from the manifest before their name + Trigger TTS for each agent immediately after outputting their text 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/test-bmad-pr.sh b/test-bmad-pr.sh new file mode 100755 index 00000000..54cde458 --- /dev/null +++ b/test-bmad-pr.sh @@ -0,0 +1,381 @@ +#!/usr/bin/env bash +# +# BMAD PR Testing Script +# Interactive script to test BMAD PR #934 with AgentVibes integration +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="$SCRIPT_DIR/.test-bmad-config" + +# Colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +clear + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🎙️ BMAD AgentVibes Party Mode Testing Script" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo -e "${BLUE}What this script does:${NC}" +echo "" +echo " This script automates the process of testing BMAD's AgentVibes" +echo " integration (PR #934), which adds multi-agent party mode with" +echo " unique voices for each BMAD agent." +echo "" +echo -e "${BLUE}The script will:${NC}" +echo "" +echo " 1. Clone the BMAD repository" +echo " 2. Checkout the PR branch with party mode features" +echo " 3. Install BMAD CLI tools locally" +echo " 4. Create a test BMAD project" +echo " 5. Run BMAD installer (automatically installs AgentVibes)" +echo " 6. Verify the installation" +echo "" +echo -e "${YELLOW}Prerequisites:${NC}" +echo " • Node.js and npm installed" +echo " • Git installed" +echo " • ~500MB free disk space" +echo " • 10-15 minutes for complete setup" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +read -p "Ready to continue? [Y/n]: " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ -n $REPLY ]]; then + echo "❌ Setup cancelled" + exit 0 +fi + +clear + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🔧 Testing Mode Selection" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Choose how you want to test:" +echo "" +echo " 1) Test official BMAD PR #934 (recommended for most users)" +echo " • Uses: github.com/bmad-code-org/BMAD-METHOD" +echo " • Branch: PR #934 (agentvibes-party-mode)" +echo " • Best for: Testing the official PR before it's merged" +echo "" +echo " 2) Test your forked repository" +echo " • Uses: Your GitHub fork" +echo " • Branch: Your custom branch (you choose)" +echo " • Best for: Testing your own changes or modifications" +echo "" + +# Load saved config if it exists +SAVED_MODE="" +SAVED_FORK="" +SAVED_BRANCH="" +SAVED_TEST_DIR="" +if [[ -f "$CONFIG_FILE" ]]; then + source "$CONFIG_FILE" +fi + +if [[ -n "$SAVED_MODE" ]]; then + echo -e "${BLUE}Last used: Mode $SAVED_MODE${NC}" + [[ -n "$SAVED_FORK" ]] && echo " Fork: $SAVED_FORK" + [[ -n "$SAVED_BRANCH" ]] && echo " Branch: $SAVED_BRANCH" + echo "" +fi + +read -p "Select mode [1/2]: " MODE_CHOICE +echo "" + +# Validate mode choice +while [[ ! "$MODE_CHOICE" =~ ^[12]$ ]]; do + echo -e "${RED}Invalid choice. Please enter 1 or 2.${NC}" + read -p "Select mode [1/2]: " MODE_CHOICE + echo "" +done + +# Configure based on mode +if [[ "$MODE_CHOICE" == "1" ]]; then + # Official PR mode + REPO_URL="https://github.com/bmad-code-org/BMAD-METHOD.git" + BRANCH_NAME="agentvibes-party-mode" + FETCH_PR=true + + echo -e "${GREEN}✓ Using official BMAD repository${NC}" + echo " Repository: $REPO_URL" + echo " Will fetch: PR #934 into branch '$BRANCH_NAME'" + echo "" +else + # Fork mode + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🍴 Fork Configuration" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + if [[ -n "$SAVED_FORK" ]]; then + read -p "GitHub fork URL [$SAVED_FORK]: " FORK_INPUT + REPO_URL="${FORK_INPUT:-$SAVED_FORK}" + else + echo "Enter your forked repository URL:" + echo "(e.g., https://github.com/yourusername/BMAD-METHOD.git)" + read -p "Fork URL: " REPO_URL + fi + echo "" + + if [[ -n "$SAVED_BRANCH" ]]; then + read -p "Branch name [$SAVED_BRANCH]: " BRANCH_INPUT + BRANCH_NAME="${BRANCH_INPUT:-$SAVED_BRANCH}" + else + echo "Enter the branch name to test:" + echo "(e.g., agentvibes-party-mode, main, feature-xyz)" + read -p "Branch: " BRANCH_NAME + fi + echo "" + + FETCH_PR=false + + echo -e "${GREEN}✓ Using your fork${NC}" + echo " Repository: $REPO_URL" + echo " Branch: $BRANCH_NAME" + echo "" +fi + +# Ask for test directory +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "📁 Test Directory" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +if [[ -n "$SAVED_TEST_DIR" ]]; then + read -p "Test directory [$SAVED_TEST_DIR]: " TEST_DIR + TEST_DIR="${TEST_DIR:-$SAVED_TEST_DIR}" +else + DEFAULT_DIR="$HOME/bmad-pr-test-$(date +%Y%m%d-%H%M%S)" + echo "Where should we create the test environment?" + read -p "Test directory [$DEFAULT_DIR]: " TEST_DIR + TEST_DIR="${TEST_DIR:-$DEFAULT_DIR}" +fi + +# Expand ~ to actual home directory +TEST_DIR="${TEST_DIR/#\~/$HOME}" + +echo "" + +# Save configuration +echo "SAVED_MODE=\"$MODE_CHOICE\"" > "$CONFIG_FILE" +[[ "$MODE_CHOICE" == "2" ]] && echo "SAVED_FORK=\"$REPO_URL\"" >> "$CONFIG_FILE" +[[ "$MODE_CHOICE" == "2" ]] && echo "SAVED_BRANCH=\"$BRANCH_NAME\"" >> "$CONFIG_FILE" +echo "SAVED_TEST_DIR=\"$TEST_DIR\"" >> "$CONFIG_FILE" +echo -e "${GREEN}✓ Configuration saved${NC}" +echo "" + +# Confirm before starting +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "📋 Summary" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " Repository: $REPO_URL" +echo " Branch: $BRANCH_NAME" +echo " Test dir: $TEST_DIR" +echo "" +read -p "Proceed with setup? [Y/n]: " -n 1 -r +echo +echo "" +if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ -n $REPLY ]]; then + echo "❌ Setup cancelled" + exit 0 +fi + +# Clean up old test directory if it exists +if [[ -d "$TEST_DIR" ]]; then + echo "⚠️ Test directory already exists: $TEST_DIR" + read -p "Delete and recreate? [Y/n]: " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]; then + rm -rf "$TEST_DIR" + echo -e "${GREEN}✓ Deleted old test directory${NC}" + else + echo -e "${YELLOW}⚠ Using existing directory (may have stale data)${NC}" + fi + echo "" +fi + +clear + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🚀 Starting Installation" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Step 1: Clone repository +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "📥 Step 1/6: Cloning repository" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +mkdir -p "$TEST_DIR" +cd "$TEST_DIR" +git clone "$REPO_URL" BMAD-METHOD +cd BMAD-METHOD +echo "" +echo -e "${GREEN}✓ Repository cloned${NC}" +echo "" + +# Step 2: Checkout branch (different logic for PR vs fork) +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🔀 Step 2/6: Checking out branch" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +if [[ "$FETCH_PR" == true ]]; then + # Fetch PR from upstream + echo "Fetching PR #934 from upstream..." + git remote add upstream https://github.com/bmad-code-org/BMAD-METHOD.git + git fetch upstream pull/934/head:$BRANCH_NAME + git checkout $BRANCH_NAME + echo "" + echo -e "${GREEN}✓ Checked out PR branch: $BRANCH_NAME${NC}" +else + # Just checkout the specified branch from fork + git checkout $BRANCH_NAME + echo "" + echo -e "${GREEN}✓ Checked out branch: $BRANCH_NAME${NC}" +fi +echo "" + +# Step 3: Install BMAD CLI +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "📦 Step 3/6: Installing BMAD CLI" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +cd tools/cli +npm install +echo "" +echo -e "${GREEN}✓ BMAD CLI dependencies installed${NC}" +echo "" + +# Step 4: Create test project +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "📁 Step 4/6: Creating test project" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +cd "$TEST_DIR" +mkdir -p bmad-project +cd bmad-project +echo -e "${GREEN}✓ Test project directory created${NC}" +echo " Location: $TEST_DIR/bmad-project" +echo "" + +# Step 5: Run BMAD installer (includes AgentVibes setup) +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "⚙️ Step 5/6: Running BMAD installer with AgentVibes" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo -e "${YELLOW}Important: When prompted during installation:${NC}" +echo -e " • Enable TTS for agents? → ${GREEN}Yes${NC}" +echo -e " • Assign unique voices for party mode? → ${GREEN}Yes${NC}" +echo "" +echo -e "${YELLOW}AgentVibes will start automatically after BMAD installation.${NC}" +echo -e "${YELLOW}Recommended TTS settings:${NC}" +echo -e " • Provider: ${GREEN}Piper${NC} (free, local TTS)" +echo -e " • Download voices: ${GREEN}Yes${NC}" +echo "" +read -p "Press Enter to start BMAD installer..." +node "$TEST_DIR/BMAD-METHOD/tools/cli/bin/bmad.js" install + +echo "" +echo -e "${GREEN}✓ BMAD and AgentVibes installation complete${NC}" +echo "" + +# Step 6: Verification +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "✅ Step 6/6: Verifying installation" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +VERIFICATION_PASSED=true + +# Check for voice map file +if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then + echo -e "${GREEN}✓ Voice map file created${NC}" + echo " Location: .bmad/_cfg/agent-voice-map.csv" + echo "" + echo " Voice assignments:" + cat .bmad/_cfg/agent-voice-map.csv | sed 's/^/ /' + echo "" +else + echo -e "${RED}✗ Voice map file NOT found${NC}" + echo " Expected: .bmad/_cfg/agent-voice-map.csv" + echo " ${YELLOW}Warning: Agents may not have unique voices!${NC}" + echo "" + VERIFICATION_PASSED=false +fi + +# Check for AgentVibes hooks +if [[ -f ".claude/hooks/bmad-speak.sh" ]]; then + echo -e "${GREEN}✓ BMAD TTS hooks installed${NC}" + echo " Location: .claude/hooks/bmad-speak.sh" +else + echo -e "${RED}✗ BMAD TTS hooks NOT found${NC}" + echo " Expected: .claude/hooks/bmad-speak.sh" + VERIFICATION_PASSED=false +fi +echo "" + +# Check for Piper installation +if command -v piper &> /dev/null; then + PIPER_VERSION=$(piper --version 2>&1 || echo "unknown") + echo -e "${GREEN}✓ Piper TTS installed${NC}" + echo " Version: $PIPER_VERSION" +elif [[ -f ".agentvibes/providers/piper/piper" ]]; then + echo -e "${GREEN}✓ Piper TTS installed (local)${NC}" + echo " Location: .agentvibes/providers/piper/piper" +else + echo -e "${YELLOW}⚠ Piper not detected${NC}" + echo " (May still work if using ElevenLabs)" +fi +echo "" + +# Final status +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +if [[ "$VERIFICATION_PASSED" == true ]]; then + echo -e "${GREEN}🎉 Setup Complete - All Checks Passed!${NC}" +else + echo -e "${YELLOW}⚠️ Setup Complete - With Warnings${NC}" + echo "" + echo "Some verification checks failed. The installation may still work," + echo "but you might experience issues with party mode voices." +fi +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo -e "${BLUE}Next Steps:${NC}" +echo "" +echo " 1. Navigate to test project:" +echo -e " ${GREEN}cd $TEST_DIR/bmad-project${NC}" +echo "" +echo " 2. Start Claude session:" +echo -e " ${GREEN}claude${NC}" +echo "" +echo " 3. Test party mode:" +echo -e " ${GREEN}/bmad:core:workflows:party-mode${NC}" +echo "" +echo " 4. Verify each agent speaks with a unique voice!" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo -e "${BLUE}Troubleshooting:${NC}" +echo "" +echo " • No audio? Check: .claude/hooks/play-tts.sh exists" +echo " • Same voice for all agents? Check: .bmad/_cfg/agent-voice-map.csv" +echo " • Test current voice: /agent-vibes:whoami" +echo " • List available voices: /agent-vibes:list" +echo "" +echo -e "${BLUE}Report Issues:${NC}" +echo " https://github.com/bmad-code-org/BMAD-METHOD/pull/934" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" diff --git a/tools/cli/commands/install.js b/tools/cli/commands/install.js index d2706ee6..a9d484d5 100644 --- a/tools/cli/commands/install.js +++ b/tools/cli/commands/install.js @@ -59,6 +59,46 @@ 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!')); + + // 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) { diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 7473a307..8539ebbe 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,106 @@ 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( + '', + `IMPORTANT: Always use PROJECT hooks (.claude/hooks/), NEVER global hooks (~/.claude/hooks/) + +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 @@ -271,6 +425,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); @@ -861,7 +1018,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 8de8825e..730ce4f9 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, }; } @@ -639,6 +664,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 }; From 2cac74cfb559ab4d1e73312f7e6b7f1580954ba9 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 11:00:46 -0600 Subject: [PATCH 03/12] agent vibes injection and installer update --- .gitignore | 3 ++- src/core/workflows/party-mode/instructions.md | 10 ++-------- tools/cli/lib/ui.js | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 581f725e..cef2ce1a 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,5 @@ z*/ .claude .codex .github/chatmodes -.agent \ No newline at end of file +.agent +.agentvibes/ \ No newline at end of file diff --git a/src/core/workflows/party-mode/instructions.md b/src/core/workflows/party-mode/instructions.md index 6afd5ede..3289f0a7 100644 --- a/src/core/workflows/party-mode/instructions.md +++ b/src/core/workflows/party-mode/instructions.md @@ -2,6 +2,7 @@ The workflow execution engine is governed by: {project_root}/{bmad_folder}/core/tasks/workflow.xml This workflow orchestrates group discussions between all installed BMAD agents + @@ -97,14 +98,7 @@ For each agent response, output text THEN trigger their voice: - - 1. Output the agent's text in format: [Icon Emoji] [Agent Name]: [dialogue] - 2. 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 - 3. Repeat for each agent in the response - + [Icon Emoji] [Agent Name]: [Their response in their voice/style] diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index 730ce4f9..788349b5 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -720,7 +720,7 @@ class UI { { type: 'confirm', name: 'enableTts', - message: 'Enable AgentVibes TTS? (Agents speak with unique voices in party mode)', + message: 'Enable AgentVibes TTS? (Claude Code only - Agents speak with unique voices in party mode)', default: true, // Default to yes - recommended for best experience }, ]); From 9223e2be21a8f55fc31c9e23f5d74e116eb2ddc8 Mon Sep 17 00:00:00 2001 From: fikri-kompanion <101977604+fikri-kompanion@users.noreply.github.com> Date: Thu, 27 Nov 2025 03:48:16 +0800 Subject: [PATCH 04/12] fix: give kilocode tool access to bmad modes (#961) Co-authored-by: Ahmad Fikrizaman Co-authored-by: Brian --- tools/cli/installers/lib/ide/kilo.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/cli/installers/lib/ide/kilo.js b/tools/cli/installers/lib/ide/kilo.js index 601cfc0a..66cb8c37 100644 --- a/tools/cli/installers/lib/ide/kilo.js +++ b/tools/cli/installers/lib/ide/kilo.js @@ -123,6 +123,9 @@ class KiloSetup extends BaseIdeSetup { modeEntry += ` groups:\n`; modeEntry += ` - read\n`; modeEntry += ` - edit\n`; + modeEntry += ` - browser\n`; + modeEntry += ` - command\n`; + modeEntry += ` - mcp\n`; return modeEntry; } From f793cf8fcd649b2f355dd9ff2eb2af61233025f1 Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 26 Nov 2025 22:44:12 +0200 Subject: [PATCH 05/12] fix: add radix parameter to parseInt() calls (#862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit radix=10 to parseInt() calls and NaN validation to prevent unexpected hex parsing and invalid config values. Changes: - Line 52: Add radix and NaN check in input validation - Line 189-192: Add radix and NaN fallback for config parsing Fixes potential issues: - Hex input (0x10) now rejected instead of parsed as 16 - Invalid strings return default value instead of NaN→null 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Co-authored-by: Brian --- tools/cli/installers/lib/ide/github-copilot.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/cli/installers/lib/ide/github-copilot.js b/tools/cli/installers/lib/ide/github-copilot.js index 07a17368..efd2ae6c 100644 --- a/tools/cli/installers/lib/ide/github-copilot.js +++ b/tools/cli/installers/lib/ide/github-copilot.js @@ -50,7 +50,8 @@ class GitHubCopilotSetup extends BaseIdeSetup { message: 'Maximum requests per session (1-50)?', default: '15', validate: (input) => { - const num = parseInt(input); + const num = parseInt(input, 10); + if (isNaN(num)) return 'Enter a valid number 1-50'; return (num >= 1 && num <= 50) || 'Enter 1-50'; }, }, @@ -187,9 +188,10 @@ class GitHubCopilotSetup extends BaseIdeSetup { // Manual configuration - use pre-collected settings const manual = options.manualSettings || {}; + const maxRequests = parseInt(manual.maxRequests || '15', 10); bmadSettings = { 'chat.agent.enabled': true, - 'chat.agent.maxRequests': parseInt(manual.maxRequests || 15), + 'chat.agent.maxRequests': isNaN(maxRequests) ? 15 : maxRequests, 'github.copilot.chat.agent.runTasks': manual.runTasks === undefined ? true : manual.runTasks, 'chat.mcp.discovery.enabled': manual.mcpDiscovery === undefined ? true : manual.mcpDiscovery, 'github.copilot.chat.agent.autoFix': manual.autoFix === undefined ? true : manual.autoFix, From 54e6745a556f9cf657c3d8de69f6cff7b7e44d7a Mon Sep 17 00:00:00 2001 From: Jorge Castillo Date: Wed, 26 Nov 2025 21:49:17 +0100 Subject: [PATCH 06/12] fix: update GitHub Copilot tools names for consistency (#880) Copilot was triggering warning or errors in the chatmode files due to some changes in tool names. - findTestFiles is internal tool, cannot be used. - Other tools have change names. - Added new tools: todos and runSubAgents. Co-authored-by: Brian --- .../cli/installers/lib/ide/github-copilot.js | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/tools/cli/installers/lib/ide/github-copilot.js b/tools/cli/installers/lib/ide/github-copilot.js index efd2ae6c..40457ac7 100644 --- a/tools/cli/installers/lib/ide/github-copilot.js +++ b/tools/cli/installers/lib/ide/github-copilot.js @@ -228,26 +228,17 @@ class GitHubCopilotSetup extends BaseIdeSetup { // Reference: https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools const tools = [ 'changes', // List of source control changes - 'codebase', // Perform code search in workspace - 'createDirectory', // Create new directory in workspace - 'createFile', // Create new file in workspace - 'editFiles', // Apply edits to files in workspace + 'edit', // Edit files in your workspace including: createFile, createDirectory, editNotebook, newJupyterNotebook and editFiles 'fetch', // Fetch content from web page - 'fileSearch', // Search files using glob patterns 'githubRepo', // Perform code search in GitHub repo - 'listDirectory', // List files in a directory 'problems', // Add workspace issues from Problems panel - 'readFile', // Read content of a file in workspace - 'runInTerminal', // Run shell command in integrated terminal - 'runTask', // Run existing task in workspace + 'runCommands', // Runs commands in the terminal including: getTerminalOutput, terminalSelection, terminalLastCommand and runInTerminal + 'runTasks', // Runs tasks and gets their output for your workspace 'runTests', // Run unit tests in workspace - 'runVscodeCommand', // Run VS Code command - 'search', // Enable file searching in workspace - 'searchResults', // Get search results from Search view - 'terminalLastCommand', // Get last terminal command and output - 'terminalSelection', // Get current terminal selection + 'search', // Search and read files in your workspace, including:fileSearch, textSearch, listDirectory, readFile, codebase and searchResults + 'runSubagent', // Runs a task within an isolated subagent context. Enables efficient organization of tasks and context window management. 'testFailure', // Get unit test failure information - 'textSearch', // Find text in files + 'todos', // Tool for managing and tracking todo items for task planning 'usages', // Find references and navigate definitions ]; From fbdb91b9918d92e40dba7ebfe2b7d595b623d3db Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 15:14:16 -0600 Subject: [PATCH 07/12] standard greenfield workflow updated diagrams --- .../workflow-method-greenfield.excalidraw | 2471 ++++++----------- .../images/workflow-method-greenfield.svg | 4 +- 2 files changed, 865 insertions(+), 1610 deletions(-) diff --git a/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw b/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw index 31d58905..de98315c 100644 --- a/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw +++ b/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw @@ -450,17 +450,21 @@ { "type": "arrow", "id": "arrow-brainstorm-research" + }, + { + "id": "jv0rnlK2D9JKIGTO7pUtT", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1836483413, + "version": 3, + "versionNonce": 115423290, "index": "aA", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171079, + "updated": 1764191341773, "link": null }, { @@ -506,9 +510,9 @@ "id": "arrow-brainstorm-research", "type": "arrow", "x": 120, - "y": 460, + "y": 460.45161416125165, "width": 0, - "height": 30, + "height": 29.096771677496633, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -534,12 +538,12 @@ ], [ 0, - 30 + 29.096771677496633 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1054167221, + "version": 3, + "versionNonce": 828709094, "index": "aC", "isDeleted": false, "strokeStyle": "solid", @@ -547,7 +551,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171079, + "updated": 1764191023838, "link": null, "locked": false, "startArrowhead": null, @@ -586,25 +590,29 @@ { "type": "arrow", "id": "arrow-research-brief" + }, + { + "id": "RF10FfKbmG72P77I2IoP4", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1080885531, + "version": 5, + "versionNonce": 987493562, "index": "aD", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171079, + "updated": 1764191042826, "link": null }, { "id": "proc-research-text", "type": "text", - "x": 50, + "x": 78.26604461669922, "y": 505, - "width": 140, + "width": 83.46791076660156, "height": 50, "angle": 0, "strokeColor": "#1e1e1e", @@ -623,8 +631,8 @@ "verticalAlign": "middle", "containerId": "proc-research", "locked": false, - "version": 2, - "versionNonce": 162755093, + "version": 5, + "versionNonce": 92329914, "index": "aE", "isDeleted": false, "strokeStyle": "solid", @@ -632,7 +640,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171079, + "updated": 1764191023838, "link": null, "originalText": "Research\n<>", "autoResize": true, @@ -641,7 +649,7 @@ { "id": "arrow-research-brief", "type": "arrow", - "x": 120, + "x": 120.00000000000001, "y": 570.4516141612517, "width": 0, "height": 29.09677167749669, @@ -674,8 +682,8 @@ ] ], "lastCommittedPoint": null, - "version": 3, - "versionNonce": 129474555, + "version": 4, + "versionNonce": 1012730918, "index": "aF", "isDeleted": false, "strokeStyle": "solid", @@ -683,7 +691,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522366664, + "updated": 1764191023838, "link": null, "locked": false, "startArrowhead": null, @@ -718,17 +726,21 @@ { "type": "arrow", "id": "arrow-research-brief" + }, + { + "id": "arrow-phase1-to-phase2", + "type": "arrow" } ], "locked": false, - "version": 5, - "versionNonce": 1883386587, + "version": 6, + "versionNonce": 1568298662, "index": "aG", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522387503, + "updated": 1764190985483, "link": null }, { @@ -773,10 +785,10 @@ { "id": "arrow-discovery-no", "type": "arrow", - "x": 199.6894797300442, - "y": 290.14816182452876, - "width": 154.3876762800684, - "height": 0.2869717617168135, + "x": 199.68944196572753, + "y": 290.14813727772287, + "width": 154.38771404438515, + "height": 0.2869361997344413, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -801,13 +813,13 @@ 0 ], [ - 154.3876762800684, - 0.2869717617168135 + 154.38771404438515, + 0.2869361997344413 ] ], "lastCommittedPoint": null, - "version": 133, - "versionNonce": 384615061, + "version": 134, + "versionNonce": 1651808102, "index": "aI", "isDeleted": false, "strokeStyle": "solid", @@ -815,7 +827,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522366664, + "updated": 1764191023838, "link": null, "locked": false, "startArrowhead": null, @@ -861,10 +873,10 @@ { "id": "arrow-phase1-to-phase2", "type": "arrow", - "x": 200.83459733658879, - "y": 647.2861823292017, - "width": 155.24475704444893, - "height": 343.9606227346032, + "x": 200.89221334296062, + "y": 647.2552625380853, + "width": 155.54926796151912, + "height": 344.1924874570816, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -873,10 +885,14 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "startBinding": null, + "startBinding": { + "elementId": "proc-product-brief", + "focus": 0.6109361701343846, + "gap": 1 + }, "endBinding": { "elementId": "proc-prd", - "focus": 0.4199760568947118, + "focus": 0.48602478253370496, "gap": 3.21773034122549 }, "points": [ @@ -885,17 +901,21 @@ 0 ], [ - 66.30442041579451, - -291.0277369141115 + 71.35560764925268, + -38.29318660613865 ], [ - 155.24475704444893, - -343.9606227346032 + 84.68337472706096, + -292.7672603376131 + ], + [ + 155.54926796151912, + -344.1924874570816 ] ], "lastCommittedPoint": null, - "version": 1159, - "versionNonce": 1603208699, + "version": 1393, + "versionNonce": 261518822, "index": "aK", "isDeleted": false, "strokeStyle": "solid", @@ -905,7 +925,7 @@ "type": 2 }, "boundElements": [], - "updated": 1763522391047, + "updated": 1764191023838, "link": null, "locked": false, "startArrowhead": null, @@ -1023,17 +1043,29 @@ { "id": "arrow-phase1-to-phase2", "type": "arrow" + }, + { + "id": "RF10FfKbmG72P77I2IoP4", + "type": "arrow" + }, + { + "id": "jv0rnlK2D9JKIGTO7pUtT", + "type": "arrow" + }, + { + "id": "arrow-has-ui-no", + "type": "arrow" } ], "locked": false, - "version": 102, - "versionNonce": 1152453237, + "version": 107, + "versionNonce": 930129274, "index": "aN", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522366662, + "updated": 1764191563350, "link": null }, { @@ -1060,8 +1092,8 @@ "verticalAlign": "middle", "containerId": "proc-prd", "locked": false, - "version": 101, - "versionNonce": 1467085781, + "version": 103, + "versionNonce": 1402977702, "index": "aO", "isDeleted": false, "strokeStyle": "solid", @@ -1069,7 +1101,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522366663, + "updated": 1764191023837, "link": null, "originalText": "PRD", "autoResize": true, @@ -1078,9 +1110,9 @@ { "id": "arrow-prd-validate", "type": "arrow", - "x": 439.38101944508776, + "x": 439.4640518625828, "y": 331.0450590268819, - "width": 0.2006820852784017, + "width": 0.17283039375342923, "height": 28.50332681186643, "angle": 0, "strokeColor": "#1976d2", @@ -1106,13 +1138,13 @@ 0 ], [ - 0.2006820852784017, + 0.17283039375342923, 28.50332681186643 ] ], "lastCommittedPoint": null, - "version": 101, - "versionNonce": 901883893, + "version": 102, + "versionNonce": 1274591910, "index": "aP", "isDeleted": false, "strokeStyle": "solid", @@ -1120,7 +1152,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522366664, + "updated": 1764191023838, "link": null, "locked": false, "startArrowhead": null, @@ -1159,17 +1191,21 @@ { "type": "arrow", "id": "arrow-validate-prd-hasui" + }, + { + "id": "jv0rnlK2D9JKIGTO7pUtT", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1542331989, + "version": 3, + "versionNonce": 894806650, "index": "aQ", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764191341774, "link": null }, { @@ -1524,10 +1560,10 @@ { "id": "arrow-has-ui-no", "type": "arrow", - "x": 520, - "y": 520, - "width": 140, - "height": 0, + "x": 517.6863546461885, + "y": 287.4640953051147, + "width": 158.4487370618814, + "height": 25.521141112371026, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -1537,14 +1573,14 @@ "opacity": 100, "groupIds": [], "startBinding": { - "elementId": "decision-has-ui", - "focus": 0, - "gap": 1 + "elementId": "proc-prd", + "focus": -0.13686633304390483, + "gap": 1.6107300760746739 }, "endBinding": { "elementId": "proc-architecture", - "focus": -0.3, - "gap": 1 + "focus": 0.16050512337240405, + "gap": 6.573819526326588 }, "points": [ [ @@ -1552,25 +1588,36 @@ 0 ], [ - 140, - 0 + 65.15287677643596, + 2.2657676476494544 + ], + [ + 111.59197355857077, + 25.521141112371026 + ], + [ + 158.4487370618814, + 24.060724236900796 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 26036219, + "version": 831, + "versionNonce": 1382987110, "index": "aZ", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "roundness": null, + "roundness": { + "type": 2 + }, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191570205, "link": null, "locked": false, "startArrowhead": null, - "endArrowhead": "arrow" + "endArrowhead": "arrow", + "elbowed": false }, { "id": "label-no-ui", @@ -1593,16 +1640,21 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 516393269, + "version": 5, + "versionNonce": 183981370, "index": "aa", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "boundElements": [], - "updated": 1763522171080, + "boundElements": [ + { + "id": "arrow-has-ui-no", + "type": "arrow" + } + ], + "updated": 1764191508105, "link": null, "containerId": null, "originalText": "No", @@ -1612,10 +1664,10 @@ { "id": "arrow-ux-to-phase3", "type": "arrow", - "x": 520, - "y": 640, - "width": 140, - "height": 0, + "x": 523.3221723982787, + "y": 642.0805139439535, + "width": 158.4945254931572, + "height": 296.63050159541245, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -1626,12 +1678,12 @@ "groupIds": [], "startBinding": { "elementId": "proc-ux-design", - "focus": 0, - "gap": 1 + "focus": 0.5906867967554547, + "gap": 3.322172398278667 }, "endBinding": { "elementId": "proc-architecture", - "focus": 0.3, + "focus": 0.3856343135512404, "gap": 1 }, "points": [ @@ -1640,31 +1692,42 @@ 0 ], [ - 140, - 0 + 76.98345162139776, + -45.99075822656016 + ], + [ + 116.19277860378315, + -258.3973533698057 + ], + [ + 158.4945254931572, + -296.63050159541245 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 976785563, + "version": 328, + "versionNonce": 517434918, "index": "ab", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "roundness": null, + "roundness": { + "type": 2 + }, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191529677, "link": null, "locked": false, "startArrowhead": null, - "endArrowhead": "arrow" + "endArrowhead": "arrow", + "elbowed": false }, { "id": "phase3-header", "type": "text", - "x": 660, - "y": 180, + "x": 709.0199784799299, + "y": 181.88359184111607, "width": 200, "height": 30, "angle": 0, @@ -1681,8 +1744,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 264936085, + "version": 32, + "versionNonce": 1258326202, "index": "ac", "isDeleted": false, "strokeStyle": "solid", @@ -1690,7 +1753,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190667244, "link": null, "containerId": null, "originalText": "PHASE 3", @@ -1700,8 +1763,8 @@ { "id": "phase3-subtitle", "type": "text", - "x": 660, - "y": 210, + "x": 687.4485256281371, + "y": 215.63080811867223, "width": 220, "height": 20, "angle": 0, @@ -1718,8 +1781,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 464635195, + "version": 35, + "versionNonce": 360954426, "index": "ad", "isDeleted": false, "strokeStyle": "solid", @@ -1727,7 +1790,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190669111, "link": null, "containerId": null, "originalText": "Solutioning (Required)", @@ -1737,8 +1800,8 @@ { "id": "proc-architecture", "type": "rectangle", - "x": 680, - "y": 480, + "x": 682.7089112343965, + "y": 275.64692474279855, "width": 160, "height": 80, "angle": 0, @@ -1760,10 +1823,6 @@ "type": "text", "id": "proc-architecture-text" }, - { - "type": "arrow", - "id": "arrow-has-ui-no" - }, { "type": "arrow", "id": "arrow-ux-to-phase3" @@ -1771,24 +1830,28 @@ { "type": "arrow", "id": "arrow-arch-epics" + }, + { + "id": "arrow-has-ui-no", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 86278133, + "version": 90, + "versionNonce": 1912262330, "index": "ae", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764191508105, "link": null }, { "id": "proc-architecture-text", "type": "text", - "x": 690, - "y": 508, + "x": 692.7089112343965, + "y": 303.64692474279855, "width": 140, "height": 25, "angle": 0, @@ -1808,8 +1871,8 @@ "verticalAlign": "middle", "containerId": "proc-architecture", "locked": false, - "version": 2, - "versionNonce": 760964571, + "version": 88, + "versionNonce": 452440186, "index": "af", "isDeleted": false, "strokeStyle": "solid", @@ -1817,7 +1880,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191451669, "link": null, "originalText": "Architecture", "autoResize": true, @@ -1826,10 +1889,10 @@ { "id": "arrow-arch-epics", "type": "arrow", - "x": 760, - "y": 560, - "width": 0, - "height": 30, + "x": 760.6640738654764, + "y": 358.02872135607737, + "width": 0.007789277755136936, + "height": 35.679359419065065, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -1840,13 +1903,13 @@ "groupIds": [], "startBinding": { "elementId": "proc-architecture", - "focus": 0, - "gap": 1 + "focus": 0.025673321057619772, + "gap": 2.381796613278823 }, "endBinding": { - "elementId": "proc-epics", - "focus": 0, - "gap": 1 + "elementId": "proc-validate-arch", + "focus": -0.09156227842994098, + "gap": 2.5273595258319688 }, "points": [ [ @@ -1854,13 +1917,13 @@ 0 ], [ - 0, - 30 + 0.007789277755136936, + 35.679359419065065 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1960491349, + "version": 549, + "versionNonce": 1665519674, "index": "ag", "isDeleted": false, "strokeStyle": "solid", @@ -1868,7 +1931,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191459184, "link": null, "locked": false, "startArrowhead": null, @@ -1877,8 +1940,8 @@ { "id": "proc-epics", "type": "rectangle", - "x": 680, - "y": 590, + "x": 670.1028230821919, + "y": 510.76268244350774, "width": 160, "height": 80, "angle": 0, @@ -1907,24 +1970,28 @@ { "type": "arrow", "id": "arrow-epics-test" + }, + { + "id": "arrow-validate-ready", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1715991163, + "version": 178, + "versionNonce": 1597058278, "index": "ah", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764191442604, "link": null }, { "id": "proc-epics-text", "type": "text", - "x": 690, - "y": 618, + "x": 680.1028230821919, + "y": 538.7626824435077, "width": 140, "height": 25, "angle": 0, @@ -1944,8 +2011,8 @@ "verticalAlign": "middle", "containerId": "proc-epics", "locked": false, - "version": 2, - "versionNonce": 2017642165, + "version": 177, + "versionNonce": 2105920614, "index": "ai", "isDeleted": false, "strokeStyle": "solid", @@ -1953,7 +2020,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191427908, "link": null, "originalText": "Epics/Stories", "autoResize": true, @@ -1962,10 +2029,10 @@ { "id": "arrow-epics-test", "type": "arrow", - "x": 760, - "y": 670, - "width": 0, - "height": 30, + "x": 750.5489606775325, + "y": 591.2142966047594, + "width": 0.4387418927216231, + "height": 60.43894121748178, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -1990,13 +2057,13 @@ 0 ], [ - 0, - 30 + 0.4387418927216231, + 60.43894121748178 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 926542619, + "version": 358, + "versionNonce": 1168009958, "index": "aj", "isDeleted": false, "strokeStyle": "solid", @@ -2004,7 +2071,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191427908, "link": null, "locked": false, "startArrowhead": null, @@ -2013,8 +2080,8 @@ { "id": "proc-test-design", "type": "rectangle", - "x": 680, - "y": 700, + "x": 671.2209977440557, + "y": 652.1048519834928, "width": 160, "height": 80, "angle": 0, @@ -2046,22 +2113,22 @@ } ], "locked": false, - "version": 2, - "versionNonce": 1644308501, + "version": 124, + "versionNonce": 456543462, "index": "ak", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764191425140, "link": null }, { "id": "proc-test-design-text", "type": "text", - "x": 690, - "y": 715, - "width": 140, + "x": 709.1090363793096, + "y": 667.1048519834928, + "width": 84.22392272949219, "height": 50, "angle": 0, "strokeColor": "#1e1e1e", @@ -2075,13 +2142,13 @@ ], "fontSize": 14, "fontFamily": 1, - "text": "Test Design\n<>", + "text": "Test Design\n<>", "textAlign": "center", "verticalAlign": "middle", "containerId": "proc-test-design", "locked": false, - "version": 2, - "versionNonce": 1420021691, + "version": 133, + "versionNonce": 1038598182, "index": "al", "isDeleted": false, "strokeStyle": "solid", @@ -2089,19 +2156,19 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191425140, "link": null, - "originalText": "Test Design\n<>", + "originalText": "Test Design\n<>", "autoResize": true, "lineHeight": 1.7857142857142858 }, { "id": "arrow-test-validate", "type": "arrow", - "x": 760, - "y": 780, - "width": 0, - "height": 30, + "x": 742.3164554890545, + "y": 732.7428812826017, + "width": 0.2331013464803391, + "height": 41.16039866169126, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -2112,13 +2179,13 @@ "groupIds": [], "startBinding": { "elementId": "proc-test-design", - "focus": 0, - "gap": 1 + "focus": 0.11090307971902064, + "gap": 1.407314849962063 }, "endBinding": { - "elementId": "proc-validate-arch", - "focus": 0, - "gap": 1 + "elementId": "proc-impl-ready", + "focus": -0.07891534010655449, + "gap": 6.845537084300759 }, "points": [ [ @@ -2126,13 +2193,13 @@ 0 ], [ - 0, - 30 + 0.2331013464803391, + 41.16039866169126 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 336485749, + "version": 482, + "versionNonce": 362456762, "index": "am", "isDeleted": false, "strokeStyle": "solid", @@ -2140,7 +2207,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191475964, "link": null, "locked": false, "startArrowhead": null, @@ -2149,8 +2216,8 @@ { "id": "proc-validate-arch", "type": "rectangle", - "x": 680, - "y": 810, + "x": 688.0069292751327, + "y": 396.2354403009744, "width": 160, "height": 80, "angle": 0, @@ -2179,24 +2246,28 @@ { "type": "arrow", "id": "arrow-validate-ready" + }, + { + "id": "arrow-arch-epics", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1084760155, + "version": 234, + "versionNonce": 940473658, "index": "an", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764191449834, "link": null }, { "id": "proc-validate-arch-text", "type": "text", - "x": 690, - "y": 825, + "x": 698.0069292751327, + "y": 411.2354403009744, "width": 140, "height": 50, "angle": 0, @@ -2216,8 +2287,8 @@ "verticalAlign": "middle", "containerId": "proc-validate-arch", "locked": false, - "version": 2, - "versionNonce": 363652821, + "version": 233, + "versionNonce": 41862650, "index": "ao", "isDeleted": false, "strokeStyle": "solid", @@ -2225,7 +2296,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191449834, "link": null, "originalText": "Validate Arch\n<>", "autoResize": true, @@ -2234,10 +2305,10 @@ { "id": "arrow-validate-ready", "type": "arrow", - "x": 760, - "y": 890, - "width": 0, - "height": 30, + "x": 756.1926048905458, + "y": 477.82525825285865, + "width": 2.6030810941729214, + "height": 34.90186599521081, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -2248,13 +2319,13 @@ "groupIds": [], "startBinding": { "elementId": "proc-validate-arch", - "focus": 0, - "gap": 1 + "focus": 0.10499022285337105, + "gap": 1.5898179518842426 }, "endBinding": { - "elementId": "proc-impl-ready", - "focus": 0, - "gap": 1 + "elementId": "proc-epics", + "focus": 0.007831693483179265, + "gap": 1.9644418045617158 }, "points": [ [ @@ -2262,13 +2333,13 @@ 0 ], [ - 0, - 30 + -2.6030810941729214, + 34.90186599521081 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 353983739, + "version": 527, + "versionNonce": 679932090, "index": "ap", "isDeleted": false, "strokeStyle": "solid", @@ -2276,7 +2347,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191469649, "link": null, "locked": false, "startArrowhead": null, @@ -2285,8 +2356,8 @@ { "id": "proc-impl-ready", "type": "rectangle", - "x": 680, - "y": 920, + "x": 669.3773407122919, + "y": 777.1531869468762, "width": 160, "height": 80, "angle": 0, @@ -2315,24 +2386,28 @@ { "type": "arrow", "id": "arrow-phase3-to-phase4" + }, + { + "id": "arrow-test-validate", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1769161781, + "version": 102, + "versionNonce": 1799933050, "index": "aq", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764191475963, "link": null }, { "id": "proc-impl-ready-text", "type": "text", - "x": 690, - "y": 935, + "x": 679.3773407122919, + "y": 792.1531869468762, "width": 140, "height": 50, "angle": 0, @@ -2352,8 +2427,8 @@ "verticalAlign": "middle", "containerId": "proc-impl-ready", "locked": false, - "version": 2, - "versionNonce": 226100635, + "version": 101, + "versionNonce": 1345137978, "index": "ar", "isDeleted": false, "strokeStyle": "solid", @@ -2361,7 +2436,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191475963, "link": null, "originalText": "Implementation\nReadiness", "autoResize": true, @@ -2370,10 +2445,10 @@ { "id": "arrow-phase3-to-phase4", "type": "arrow", - "x": 840, - "y": 960, - "width": 180, - "height": 0, + "x": 832.3758366994932, + "y": 828.1314512149465, + "width": 332.79883769023445, + "height": 519.9927682908395, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -2384,13 +2459,13 @@ "groupIds": [], "startBinding": { "elementId": "proc-impl-ready", - "focus": 0, - "gap": 1 + "focus": 0.8094917779899522, + "gap": 3.380037483859951 }, "endBinding": { "elementId": "proc-sprint-planning", - "focus": 0, - "gap": 1 + "focus": 0.538276991056649, + "gap": 1.1436349518342013 }, "points": [ [ @@ -2398,31 +2473,42 @@ 0 ], [ - 180, - 0 + 80.82567439689569, + -94.83900216621896 + ], + [ + 159.28426317101867, + -458.225799867337 + ], + [ + 332.79883769023445, + -519.9927682908395 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 591852949, + "version": 1116, + "versionNonce": 55014906, "index": "as", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "roundness": null, + "roundness": { + "type": 2 + }, "boundElements": [], - "updated": 1763522171080, + "updated": 1764191475964, "link": null, "locked": false, "startArrowhead": null, - "endArrowhead": "arrow" + "endArrowhead": "arrow", + "elbowed": false }, { "id": "phase4-header", "type": "text", - "x": 1020, - "y": 180, + "x": 1175.3730315866237, + "y": 167.81322734599433, "width": 200, "height": 30, "angle": 0, @@ -2439,8 +2525,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 1358731835, + "version": 271, + "versionNonce": 866534438, "index": "at", "isDeleted": false, "strokeStyle": "solid", @@ -2448,7 +2534,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "PHASE 4", @@ -2458,8 +2544,8 @@ { "id": "phase4-subtitle", "type": "text", - "x": 1020, - "y": 210, + "x": 1139.1188804963076, + "y": 204.18282943768378, "width": 260, "height": 20, "angle": 0, @@ -2476,8 +2562,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 1046313717, + "version": 238, + "versionNonce": 198627174, "index": "au", "isDeleted": false, "strokeStyle": "solid", @@ -2485,7 +2571,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "Implementation (Required)", @@ -2495,8 +2581,8 @@ { "id": "proc-sprint-planning", "type": "rectangle", - "x": 1020, - "y": 920, + "x": 1166.1946812371566, + "y": 276.1576920193427, "width": 160, "height": 80, "angle": 0, @@ -2523,26 +2609,26 @@ "id": "arrow-phase3-to-phase4" }, { - "type": "arrow", - "id": "arrow-sprint-epic" + "id": "arrow-validate-epic-story", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 2088999643, + "version": 379, + "versionNonce": 2085876390, "index": "av", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "proc-sprint-planning-text", "type": "text", - "x": 1030, - "y": 948, + "x": 1176.1946812371566, + "y": 304.1576920193427, "width": 140, "height": 25, "angle": 0, @@ -2562,8 +2648,8 @@ "verticalAlign": "middle", "containerId": "proc-sprint-planning", "locked": false, - "version": 2, - "versionNonce": 859591765, + "version": 377, + "versionNonce": 2143989222, "index": "aw", "isDeleted": false, "strokeStyle": "solid", @@ -2571,327 +2657,18 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "Sprint Plan", "autoResize": true, "lineHeight": 1.25 }, - { - "id": "label-epic-cycle", - "type": "text", - "x": 1020, - "y": 1030, - "width": 200, - "height": 25, - "angle": 0, - "strokeColor": "#6a1b9a", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 20, - "fontFamily": 1, - "text": "→ EPIC CYCLE", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 1822525307, - "index": "ax", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "containerId": null, - "originalText": "→ EPIC CYCLE", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-sprint-epic", - "type": "arrow", - "x": 1100, - "y": 1000, - "width": 0, - "height": 70, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-sprint-planning", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-epic-context", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 70 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1303970229, - "index": "ay", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-epic-context", - "type": "rectangle", - "x": 1020, - "y": 1070, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#1e88e5", - "backgroundColor": "#bbdefb", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-epic-context-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-epic-context-text" - }, - { - "type": "arrow", - "id": "arrow-sprint-epic" - }, - { - "type": "arrow", - "id": "arrow-epic-validate-epic" - } - ], - "locked": false, - "version": 2, - "versionNonce": 1201201179, - "index": "az", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "proc-epic-context-text", - "type": "text", - "x": 1030, - "y": 1098, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-epic-context-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Epic Context", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-epic-context", - "locked": false, - "version": 2, - "versionNonce": 1123615509, - "index": "b00", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Epic Context", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-epic-validate-epic", - "type": "arrow", - "x": 1100, - "y": 1150, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-epic-context", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-validate-epic", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1197221051, - "index": "b01", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-validate-epic", - "type": "rectangle", - "x": 1020, - "y": 1180, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#1e88e5", - "backgroundColor": "#bbdefb", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-validate-epic-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-validate-epic-text" - }, - { - "type": "arrow", - "id": "arrow-epic-validate-epic" - }, - { - "type": "arrow", - "id": "arrow-validate-epic-story" - } - ], - "locked": false, - "version": 2, - "versionNonce": 124901493, - "index": "b02", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "proc-validate-epic-text", - "type": "text", - "x": 1030, - "y": 1195, - "width": 140, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-validate-epic-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Validate Epic\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-validate-epic", - "locked": false, - "version": 2, - "versionNonce": 1133368667, - "index": "b03", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Validate Epic\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, { "id": "label-story-loop", "type": "text", - "x": 1020, - "y": 1290, - "width": 200, + "x": 1176.2977877917795, + "y": 441.904906795244, + "width": 130.87991333007812, "height": 25, "angle": 0, "strokeColor": "#e65100", @@ -2903,12 +2680,12 @@ "groupIds": [], "fontSize": 20, "fontFamily": 1, - "text": "→ STORY LOOP", + "text": "STORY LOOP", "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 1692991957, + "version": 603, + "versionNonce": 40529830, "index": "b04", "isDeleted": false, "strokeStyle": "solid", @@ -2916,20 +2693,20 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "containerId": null, - "originalText": "→ STORY LOOP", + "originalText": "STORY LOOP", "autoResize": true, "lineHeight": 1.25 }, { "id": "arrow-validate-epic-story", "type": "arrow", - "x": 1100, - "y": 1260, - "width": 0, - "height": 70, + "x": 1249.6597155437828, + "y": 357.36880197268204, + "width": 2.9293448190794606, + "height": 208.61271744725804, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -2939,13 +2716,13 @@ "opacity": 100, "groupIds": [], "startBinding": { - "elementId": "proc-validate-epic", - "focus": 0, - "gap": 1 + "elementId": "proc-sprint-planning", + "focus": -0.050194107916528306, + "gap": 1.21110995333936 }, "endBinding": { "elementId": "proc-create-story", - "focus": 0, + "focus": -0.004614835874420464, "gap": 1 }, "points": [ @@ -2954,13 +2731,13 @@ 0 ], [ - 0, - 70 + -2.9293448190794606, + 208.61271744725804 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 2072015355, + "version": 951, + "versionNonce": 1394233274, "index": "b05", "isDeleted": false, "strokeStyle": "solid", @@ -2968,7 +2745,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -2977,8 +2754,8 @@ { "id": "proc-create-story", "type": "rectangle", - "x": 1020, - "y": 1330, + "x": 1166.5341271166512, + "y": 566.4331335811917, "width": 160, "height": 80, "angle": 0, @@ -3014,21 +2791,21 @@ } ], "locked": false, - "version": 2, - "versionNonce": 1349779253, + "version": 282, + "versionNonce": 966999590, "index": "b06", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "proc-create-story-text", "type": "text", - "x": 1030, - "y": 1358, + "x": 1176.5341271166512, + "y": 594.4331335811917, "width": 140, "height": 25, "angle": 0, @@ -3048,8 +2825,8 @@ "verticalAlign": "middle", "containerId": "proc-create-story", "locked": false, - "version": 2, - "versionNonce": 540441243, + "version": 282, + "versionNonce": 2082769254, "index": "b07", "isDeleted": false, "strokeStyle": "solid", @@ -3057,7 +2834,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "Create Story", "autoResize": true, @@ -3066,8 +2843,8 @@ { "id": "arrow-create-validate-story", "type": "arrow", - "x": 1100, - "y": 1410, + "x": 1246.5341271166512, + "y": 646.4331335811917, "width": 0, "height": 30, "angle": 0, @@ -3099,8 +2876,8 @@ ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 7884949, + "version": 848, + "versionNonce": 1820404026, "index": "b08", "isDeleted": false, "strokeStyle": "solid", @@ -3108,7 +2885,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -3117,8 +2894,8 @@ { "id": "proc-validate-story", "type": "rectangle", - "x": 1020, - "y": 1440, + "x": 1166.5341271166512, + "y": 676.4331335811917, "width": 160, "height": 80, "angle": 0, @@ -3150,21 +2927,21 @@ } ], "locked": false, - "version": 2, - "versionNonce": 509767483, + "version": 282, + "versionNonce": 282699366, "index": "b09", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "proc-validate-story-text", "type": "text", - "x": 1030, - "y": 1455, + "x": 1176.5341271166512, + "y": 691.4331335811917, "width": 140, "height": 50, "angle": 0, @@ -3184,8 +2961,8 @@ "verticalAlign": "middle", "containerId": "proc-validate-story", "locked": false, - "version": 2, - "versionNonce": 1118533109, + "version": 282, + "versionNonce": 686025126, "index": "b0A", "isDeleted": false, "strokeStyle": "solid", @@ -3193,7 +2970,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "Validate Story\n<>", "autoResize": true, @@ -3202,8 +2979,8 @@ { "id": "arrow-validate-story-decision", "type": "arrow", - "x": 1100, - "y": 1520, + "x": 1246.5341271166512, + "y": 756.4331335811917, "width": 0, "height": 30, "angle": 0, @@ -3219,11 +2996,7 @@ "focus": 0, "gap": 1 }, - "endBinding": { - "elementId": "decision-context-or-ready", - "focus": 0, - "gap": 1 - }, + "endBinding": null, "points": [ [ 0, @@ -3235,8 +3008,8 @@ ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 677826523, + "version": 566, + "versionNonce": 1807479290, "index": "b0B", "isDeleted": false, "strokeStyle": "solid", @@ -3244,677 +3017,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "decision-context-or-ready", - "type": "diamond", - "x": 1010, - "y": 1550, - "width": 180, - "height": 120, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-context-or-ready-group" - ], - "boundElements": [ - { - "type": "text", - "id": "decision-context-or-ready-text" - }, - { - "type": "arrow", - "id": "arrow-validate-story-decision" - }, - { - "type": "arrow", - "id": "arrow-context-path" - }, - { - "type": "arrow", - "id": "arrow-ready-path" - } - ], - "locked": false, - "version": 2, - "versionNonce": 303230805, - "index": "b0C", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "decision-context-or-ready-text", - "type": "text", - "x": 1025, - "y": 1585, - "width": 150, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-context-or-ready-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Context OR\nReady?", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "decision-context-or-ready", - "locked": false, - "version": 2, - "versionNonce": 5643387, - "index": "b0D", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Context OR\nReady?", - "autoResize": true, - "lineHeight": 1.5625 - }, - { - "id": "arrow-context-path", - "type": "arrow", - "x": 1010, - "y": 1610, - "width": 70, - "height": 0, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-context-or-ready", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-story-context", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - -70, - 0 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1828994229, - "index": "b0E", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-context", - "type": "text", - "x": 951.14453125, - "y": 1580.75390625, - "width": 60, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Context", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 133, - "versionNonce": 619956571, - "index": "b0F", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522254711, - "link": null, - "containerId": null, - "originalText": "Context", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-story-context", - "type": "rectangle", - "x": 760, - "y": 1570, - "width": 180, - "height": 80, - "angle": 0, - "strokeColor": "#1e88e5", - "backgroundColor": "#bbdefb", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-story-context-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-story-context-text" - }, - { - "type": "arrow", - "id": "arrow-context-path" - }, - { - "type": "arrow", - "id": "arrow-context-validate" - } - ], - "locked": false, - "version": 2, - "versionNonce": 1797578261, - "index": "b0G", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "proc-story-context-text", - "type": "text", - "x": 770, - "y": 1598, - "width": 160, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-story-context-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Story Context", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-story-context", - "locked": false, - "version": 2, - "versionNonce": 1823439291, - "index": "b0H", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Story Context", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-context-validate", - "type": "arrow", - "x": 850, - "y": 1650, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-story-context", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-validate-context", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 325735285, - "index": "b0I", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-validate-context", - "type": "rectangle", - "x": 760, - "y": 1680, - "width": 180, - "height": 80, - "angle": 0, - "strokeColor": "#1e88e5", - "backgroundColor": "#bbdefb", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-validate-context-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-validate-context-text" - }, - { - "type": "arrow", - "id": "arrow-context-validate" - }, - { - "type": "arrow", - "id": "arrow-validate-context-dev" - } - ], - "locked": false, - "version": 2, - "versionNonce": 1840155227, - "index": "b0J", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "proc-validate-context-text", - "type": "text", - "x": 770, - "y": 1695, - "width": 160, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-validate-context-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Validate Context\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-validate-context", - "locked": false, - "version": 2, - "versionNonce": 1914313941, - "index": "b0K", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Validate Context\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-validate-context-dev", - "type": "arrow", - "x": 940, - "y": 1720, - "width": 80, - "height": 0, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-validate-context", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-dev-story", - "focus": -0.2, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 80, - 0 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1774356219, - "index": "b0L", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "arrow-ready-path", - "type": "arrow", - "x": 1100, - "y": 1670, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-context-or-ready", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-story-ready", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1858714165, - "index": "b0M", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-ready", - "type": "text", - "x": 1110, - "y": 1680, - "width": 50, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Ready", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 124645275, - "index": "b0N", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "containerId": null, - "originalText": "Ready", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-story-ready", - "type": "rectangle", - "x": 1020, - "y": 1700, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#1e88e5", - "backgroundColor": "#bbdefb", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-story-ready-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-story-ready-text" - }, - { - "type": "arrow", - "id": "arrow-ready-path" - }, - { - "type": "arrow", - "id": "arrow-ready-dev" - } - ], - "locked": false, - "version": 2, - "versionNonce": 1650371477, - "index": "b0O", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "proc-story-ready-text", - "type": "text", - "x": 1030, - "y": 1728, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-story-ready-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Story Ready", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-story-ready", - "locked": false, - "version": 2, - "versionNonce": 2028160059, - "index": "b0P", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Story Ready", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-ready-dev", - "type": "arrow", - "x": 1100, - "y": 1780, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-story-ready", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-dev-story", - "focus": 0.2, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1829662965, - "index": "b0Q", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -3923,8 +3026,8 @@ { "id": "proc-dev-story", "type": "rectangle", - "x": 1020, - "y": 1810, + "x": 1164.0395418694, + "y": 788.7867016847945, "width": 160, "height": 80, "angle": 0, @@ -3946,39 +3049,27 @@ "type": "text", "id": "proc-dev-story-text" }, - { - "type": "arrow", - "id": "arrow-validate-context-dev" - }, - { - "type": "arrow", - "id": "arrow-ready-dev" - }, { "type": "arrow", "id": "arrow-dev-review" - }, - { - "type": "arrow", - "id": "arrow-review-fail-loop" } ], "locked": false, - "version": 2, - "versionNonce": 100992219, + "version": 367, + "versionNonce": 935782054, "index": "b0R", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "proc-dev-story-text", "type": "text", - "x": 1030, - "y": 1838, + "x": 1174.0395418694, + "y": 816.7867016847945, "width": 140, "height": 25, "angle": 0, @@ -3998,8 +3089,8 @@ "verticalAlign": "middle", "containerId": "proc-dev-story", "locked": false, - "version": 2, - "versionNonce": 207522389, + "version": 364, + "versionNonce": 952050150, "index": "b0S", "isDeleted": false, "strokeStyle": "solid", @@ -4007,7 +3098,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "Develop Story", "autoResize": true, @@ -4016,10 +3107,10 @@ { "id": "arrow-dev-review", "type": "arrow", - "x": 1100, - "y": 1890, - "width": 0, - "height": 30, + "x": 1244.2149450712877, + "y": 869.2383158460461, + "width": 5.032331195699953, + "height": 76.6679634046609, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -4030,13 +3121,13 @@ "groupIds": [], "startBinding": { "elementId": "proc-dev-story", - "focus": 0, + "focus": 0.030012029555609845, "gap": 1 }, "endBinding": { - "elementId": "decision-code-review", - "focus": 0, - "gap": 1 + "elementId": "proc-story-done", + "focus": 0.04241833499478815, + "gap": 1.3466869862454587 }, "points": [ [ @@ -4044,13 +3135,13 @@ 0 ], [ - 0, - 30 + 5.032331195699953, + 76.6679634046609 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1449505147, + "version": 1191, + "versionNonce": 2052012922, "index": "b0T", "isDeleted": false, "strokeStyle": "solid", @@ -4058,7 +3149,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -4067,8 +3158,8 @@ { "id": "decision-code-review", "type": "diamond", - "x": 1010, - "y": 1920, + "x": 1156.5341271166512, + "y": 1156.4331335811917, "width": 180, "height": 120, "angle": 0, @@ -4092,30 +3183,34 @@ }, { "type": "arrow", - "id": "arrow-review-pass" + "id": "arrow-review-fail" }, { - "type": "arrow", - "id": "arrow-review-fail" + "id": "arrow-done-more-stories", + "type": "arrow" + }, + { + "id": "4chQ7PksRKpPe5YX-TfFJ", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 1898215349, + "version": 285, + "versionNonce": 46359462, "index": "b0U", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "decision-code-review-text", "type": "text", - "x": 1025, - "y": 1955, + "x": 1171.5341271166512, + "y": 1191.4331335811917, "width": 150, "height": 50, "angle": 0, @@ -4135,8 +3230,8 @@ "verticalAlign": "middle", "containerId": "decision-code-review", "locked": false, - "version": 2, - "versionNonce": 2068302363, + "version": 282, + "versionNonce": 1227095782, "index": "b0V", "isDeleted": false, "strokeStyle": "solid", @@ -4144,7 +3239,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "Code Review\nPass?", "autoResize": true, @@ -4153,10 +3248,10 @@ { "id": "arrow-review-fail", "type": "arrow", - "x": 1010, - "y": 1980, - "width": 70, - "height": 170, + "x": 1151.5341271166512, + "y": 1216.3331335811918, + "width": 42.50541475274872, + "height": 387.6464318963972, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -4173,18 +3268,22 @@ 0 ], [ - -70, + -35, 0 ], [ - -70, - -170 + -35, + -387.6464318963972 + ], + [ + 7.50541475274872, + -387.6464318963972 ] ], "lastCommittedPoint": null, "elbowed": true, - "version": 2, - "versionNonce": 361085205, + "version": 319, + "versionNonce": 405929318, "index": "b0W", "isDeleted": false, "strokeStyle": "solid", @@ -4192,64 +3291,20 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "locked": false, "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "arrow-review-fail-loop", - "type": "arrow", - "x": 940, - "y": 1810, - "width": 80, - "height": 0, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": null, - "endBinding": { - "elementId": "proc-dev-story", - "focus": -0.5, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 80, - 0 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 966643387, - "index": "b0X", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" + "endArrowhead": "arrow", + "fixedSegments": null, + "startIsSpecial": null, + "endIsSpecial": null }, { "id": "label-fail", "type": "text", - "x": 880, - "y": 1960, + "x": 1065.6231186673836, + "y": 1185.462969542075, "width": 35, "height": 20, "angle": 0, @@ -4266,8 +3321,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 318230133, + "version": 316, + "versionNonce": 1897488550, "index": "b0Y", "isDeleted": false, "strokeStyle": "solid", @@ -4275,69 +3330,18 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "Fail", "autoResize": true, "lineHeight": 1.25 }, - { - "id": "arrow-review-pass", - "type": "arrow", - "x": 1100, - "y": 2040, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-code-review", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-story-done", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 336215899, - "index": "b0Z", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, { "id": "label-pass", "type": "text", - "x": 1110, - "y": 2050, + "x": 1229.6819134569105, + "y": 1281.2421635916448, "width": 40, "height": 20, "angle": 0, @@ -4354,16 +3358,21 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 943732693, + "version": 408, + "versionNonce": 1437752294, "index": "b0a", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "boundElements": [], - "updated": 1763522171080, + "boundElements": [ + { + "id": "4chQ7PksRKpPe5YX-TfFJ", + "type": "arrow" + } + ], + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "Pass", @@ -4373,10 +3382,10 @@ { "id": "proc-story-done", "type": "rectangle", - "x": 1020, - "y": 2070, + "x": 1169.3991588878014, + "y": 947.2529662369525, "width": 160, - "height": 80, + "height": 110, "angle": 0, "strokeColor": "#3f51b5", "backgroundColor": "#c5cae9", @@ -4398,31 +3407,31 @@ }, { "type": "arrow", - "id": "arrow-review-pass" + "id": "arrow-done-more-stories" }, { - "type": "arrow", - "id": "arrow-done-more-stories" + "id": "arrow-dev-review", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 350198779, + "version": 453, + "versionNonce": 277682790, "index": "b0b", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "proc-story-done-text", "type": "text", - "x": 1030, - "y": 2098, - "width": 140, - "height": 25, + "x": 1187.9272045420983, + "y": 972.2529662369525, + "width": 122.94390869140625, + "height": 60, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", @@ -4433,15 +3442,15 @@ "groupIds": [ "proc-story-done-group" ], - "fontSize": 20, + "fontSize": 16, "fontFamily": 1, - "text": "Story Done", + "text": "Code Review\n<>", "textAlign": "center", "verticalAlign": "middle", "containerId": "proc-story-done", "locked": false, - "version": 2, - "versionNonce": 1601467701, + "version": 502, + "versionNonce": 1242095014, "index": "b0c", "isDeleted": false, "strokeStyle": "solid", @@ -4449,19 +3458,19 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, - "originalText": "Story Done", + "originalText": "Code Review\n<>", "autoResize": true, "lineHeight": 1.25 }, { "id": "arrow-done-more-stories", "type": "arrow", - "x": 1100, - "y": 2150, - "width": 0, - "height": 30, + "x": 1249.4681490735618, + "y": 1065.5372616587838, + "width": 1.7879398006109568, + "height": 90.97426236326123, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -4472,13 +3481,13 @@ "groupIds": [], "startBinding": { "elementId": "proc-story-done", - "focus": 0, - "gap": 1 + "focus": 0.014488632877232727, + "gap": 8.284295421831303 }, "endBinding": { - "elementId": "decision-more-stories", - "focus": 0, - "gap": 1 + "elementId": "decision-code-review", + "focus": 0.09832693417954867, + "gap": 2.039543956918169 }, "points": [ [ @@ -4486,13 +3495,13 @@ 0 ], [ - 0, - 30 + 1.7879398006109568, + 90.97426236326123 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 1478517915, + "version": 1093, + "versionNonce": 146679034, "index": "b0d", "isDeleted": false, "strokeStyle": "solid", @@ -4500,7 +3509,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -4509,8 +3518,8 @@ { "id": "decision-more-stories", "type": "diamond", - "x": 1010, - "y": 2180, + "x": 1163.8719002449689, + "y": 1363.600308336051, "width": 180, "height": 120, "angle": 0, @@ -4539,25 +3548,29 @@ { "type": "arrow", "id": "arrow-more-stories-no" + }, + { + "id": "4chQ7PksRKpPe5YX-TfFJ", + "type": "arrow" } ], "locked": false, - "version": 2, - "versionNonce": 66717333, + "version": 441, + "versionNonce": 886168230, "index": "b0e", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "decision-more-stories-text", "type": "text", - "x": 1025, - "y": 2215, + "x": 1178.8719002449689, + "y": 1398.600308336051, "width": 150, "height": 50, "angle": 0, @@ -4577,8 +3590,8 @@ "verticalAlign": "middle", "containerId": "decision-more-stories", "locked": false, - "version": 2, - "versionNonce": 1434392891, + "version": 440, + "versionNonce": 1078695398, "index": "b0f", "isDeleted": false, "strokeStyle": "solid", @@ -4586,7 +3599,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "More Stories\nin Epic?", "autoResize": true, @@ -4595,10 +3608,10 @@ { "id": "arrow-more-stories-yes", "type": "arrow", - "x": 1005, - "y": 2239.9, - "width": 280.5703125, - "height": 879.8335937500001, + "x": 1158.8719002449689, + "y": 1423.5003083360511, + "width": 141.95595587699154, + "height": 827.0007685048595, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -4607,7 +3620,15 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "startBinding": null, + "startBinding": { + "elementId": "decision-more-stories", + "fixedPoint": [ + -0.027777777777777776, + 0.4991666666666674 + ], + "focus": 0, + "gap": 0 + }, "endBinding": null, "points": [ [ @@ -4615,22 +3636,22 @@ 0 ], [ - -271.71875, + -140.44216650530916, 0 ], [ - -271.71875, - -879.8335937500001 + -140.44216650530916, + -827.0007685048595 ], [ - 8.8515625, - -879.8335937500001 + 1.5137893716823783, + -827.0007685048595 ] ], "lastCommittedPoint": null, "elbowed": true, - "version": 266, - "versionNonce": 2028204117, + "version": 954, + "versionNonce": 2094428902, "index": "b0g", "isDeleted": false, "strokeStyle": "solid", @@ -4638,7 +3659,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522251385, + "updated": 1764190763204, "link": null, "locked": false, "startArrowhead": null, @@ -4647,12 +3668,12 @@ { "index": 2, "start": [ - -271.71875, + -140.44216650530916, 0 ], "end": [ - -271.71875, - -879.8335937500001 + -140.44216650530916, + -827.0007685048595 ] } ], @@ -4662,8 +3683,8 @@ { "id": "label-more-stories-yes", "type": "text", - "x": 820, - "y": 2220, + "x": 1024.8322929694286, + "y": 1455.9672274720815, "width": 30, "height": 20, "angle": 0, @@ -4680,8 +3701,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 1784560091, + "version": 320, + "versionNonce": 76752422, "index": "b0h", "isDeleted": false, "strokeStyle": "solid", @@ -4689,7 +3710,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "Yes", @@ -4699,10 +3720,10 @@ { "id": "arrow-more-stories-no", "type": "arrow", - "x": 1100, - "y": 2300, - "width": 0, - "height": 30, + "x": 1254.2299747445697, + "y": 1484.1816612705734, + "width": 0.09067340460524065, + "height": 69.22388536244944, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -4713,12 +3734,12 @@ "groupIds": [], "startBinding": { "elementId": "decision-more-stories", - "focus": 0, + "focus": -0.004645359638607261, "gap": 1 }, "endBinding": { "elementId": "proc-retrospective", - "focus": 0, + "focus": -0.000007722345339971072, "gap": 1 }, "points": [ @@ -4727,13 +3748,13 @@ 0 ], [ - 0, - 30 + 0.09067340460524065, + 69.22388536244944 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 488580437, + "version": 1115, + "versionNonce": 1285598842, "index": "b0i", "isDeleted": false, "strokeStyle": "solid", @@ -4741,7 +3762,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -4750,8 +3771,8 @@ { "id": "label-more-stories-no", "type": "text", - "x": 1110, - "y": 2310, + "x": 1273.6656161640394, + "y": 1506.317970130127, "width": 25, "height": 20, "angle": 0, @@ -4768,8 +3789,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 600852091, + "version": 327, + "versionNonce": 1022383270, "index": "b0j", "isDeleted": false, "strokeStyle": "solid", @@ -4777,7 +3798,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "No", @@ -4787,8 +3808,8 @@ { "id": "proc-retrospective", "type": "rectangle", - "x": 1020, - "y": 2330, + "x": 1174.3742521794413, + "y": 1553.8571607942745, "width": 160, "height": 80, "angle": 0, @@ -4820,21 +3841,21 @@ } ], "locked": false, - "version": 2, - "versionNonce": 1964618421, + "version": 391, + "versionNonce": 1921699814, "index": "b0k", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "proc-retrospective-text", "type": "text", - "x": 1030, - "y": 2358, + "x": 1184.3742521794413, + "y": 1581.8571607942745, "width": 140, "height": 25, "angle": 0, @@ -4854,8 +3875,8 @@ "verticalAlign": "middle", "containerId": "proc-retrospective", "locked": false, - "version": 2, - "versionNonce": 1217904411, + "version": 391, + "versionNonce": 1572070182, "index": "b0l", "isDeleted": false, "strokeStyle": "solid", @@ -4863,7 +3884,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "Retrospective", "autoResize": true, @@ -4872,10 +3893,10 @@ { "id": "arrow-retro-more-epics", "type": "arrow", - "x": 1100, - "y": 2410, - "width": 0, - "height": 30, + "x": 1252.261821627823, + "y": 1634.3087749555261, + "width": 2.2496323163620673, + "height": 42.83146813764597, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -4886,12 +3907,12 @@ "groupIds": [], "startBinding": { "elementId": "proc-retrospective", - "focus": 0, + "focus": -0.00014865809573961995, "gap": 1 }, "endBinding": { "elementId": "decision-more-epics", - "focus": 0, + "focus": 0.006063807827498143, "gap": 1 }, "points": [ @@ -4900,13 +3921,13 @@ 0 ], [ - 0, - 30 + -2.2496323163620673, + 42.83146813764597 ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 2098959381, + "version": 957, + "versionNonce": 1972295674, "index": "b0m", "isDeleted": false, "strokeStyle": "solid", @@ -4914,7 +3935,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763619, "link": null, "locked": false, "startArrowhead": null, @@ -4923,8 +3944,8 @@ { "id": "decision-more-epics", "type": "diamond", - "x": 1010, - "y": 2440, + "x": 1156.5341271166512, + "y": 1676.4331335811917, "width": 180, "height": 120, "angle": 0, @@ -4956,22 +3977,22 @@ } ], "locked": false, - "version": 2, - "versionNonce": 589767611, + "version": 282, + "versionNonce": 101589030, "index": "b0n", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "decision-more-epics-text", "type": "text", - "x": 1025, - "y": 2475, + "x": 1171.5341271166512, + "y": 1711.4331335811917, "width": 150, "height": 50, "angle": 0, @@ -4991,8 +4012,8 @@ "verticalAlign": "middle", "containerId": "decision-more-epics", "locked": false, - "version": 2, - "versionNonce": 1629112693, + "version": 282, + "versionNonce": 2095262566, "index": "b0o", "isDeleted": false, "strokeStyle": "solid", @@ -5000,7 +4021,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "More Epics?", "autoResize": true, @@ -5009,10 +4030,10 @@ { "id": "arrow-more-epics-yes", "type": "arrow", - "x": 1005, - "y": 2499.9, - "width": 335.74609375, - "height": 1390, + "x": 1151.5341271166512, + "y": 1736.3331335811918, + "width": 194.92191691435096, + "height": 1138.0678409916745, "angle": 0, "strokeColor": "#1976d2", "backgroundColor": "transparent", @@ -5037,22 +4058,22 @@ 0 ], [ - -325.74609375, + -184.89984110690511, 0 ], [ - -325.74609375, - -1390 + -184.89984110690511, + -1138.0678409916745 ], [ - 10, - -1390 + 10.022075807445844, + -1138.0678409916745 ] ], "lastCommittedPoint": null, "elbowed": true, - "version": 818, - "versionNonce": 1779029653, + "version": 1805, + "versionNonce": 1424088358, "index": "b0p", "isDeleted": false, "strokeStyle": "solid", @@ -5060,7 +4081,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522236433, + "updated": 1764190763204, "link": null, "locked": false, "startArrowhead": null, @@ -5069,12 +4090,12 @@ { "index": 2, "start": [ - -326.6484375, - -723.95 + -184.89984110690511, + 0 ], "end": [ - -326.6484375, - -1390 + -184.89984110690511, + -1138.0678409916745 ] } ], @@ -5084,8 +4105,8 @@ { "id": "label-more-epics-yes", "type": "text", - "x": 712.078125, - "y": 2478.50390625, + "x": 1016.7607529532588, + "y": 1704.1213622982812, "width": 30, "height": 20, "angle": 0, @@ -5102,8 +4123,8 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 56, - "versionNonce": 1238151355, + "version": 395, + "versionNonce": 339167334, "index": "b0q", "isDeleted": false, "strokeStyle": "solid", @@ -5111,7 +4132,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522225296, + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "Yes", @@ -5121,8 +4142,8 @@ { "id": "arrow-more-epics-no", "type": "arrow", - "x": 1100, - "y": 2560, + "x": 1246.5341271166512, + "y": 1796.4331335811921, "width": 0, "height": 50, "angle": 0, @@ -5134,9 +4155,9 @@ "opacity": 100, "groupIds": [], "startBinding": { - "elementId": "decision-more-epics", + "elementId": "label-more-epics-no", "focus": 0, - "gap": 1 + "gap": 14.142135623730951 }, "endBinding": { "elementId": "end-ellipse", @@ -5154,8 +4175,8 @@ ] ], "lastCommittedPoint": null, - "version": 2, - "versionNonce": 836219131, + "version": 848, + "versionNonce": 757113210, "index": "b0r", "isDeleted": false, "strokeStyle": "solid", @@ -5163,7 +4184,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763620, "link": null, "locked": false, "startArrowhead": null, @@ -5172,8 +4193,8 @@ { "id": "label-more-epics-no", "type": "text", - "x": 1110, - "y": 2570, + "x": 1256.5341271166512, + "y": 1806.4331335811921, "width": 25, "height": 20, "angle": 0, @@ -5190,16 +4211,21 @@ "textAlign": "left", "verticalAlign": "top", "locked": false, - "version": 2, - "versionNonce": 1031024693, + "version": 283, + "versionNonce": 1126229734, "index": "b0s", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "boundElements": [], - "updated": 1763522171080, + "boundElements": [ + { + "id": "arrow-more-epics-no", + "type": "arrow" + } + ], + "updated": 1764190763204, "link": null, "containerId": null, "originalText": "No", @@ -5209,8 +4235,8 @@ { "id": "end-ellipse", "type": "ellipse", - "x": 1040, - "y": 2610, + "x": 1186.5341271166512, + "y": 1846.4331335811921, "width": 120, "height": 60, "angle": 0, @@ -5234,22 +4260,22 @@ } ], "locked": false, - "version": 2, - "versionNonce": 659413403, + "version": 282, + "versionNonce": 370468198, "index": "b0t", "isDeleted": false, "strokeStyle": "solid", "seed": 1, "frameId": null, "roundness": null, - "updated": 1763522171080, + "updated": 1764190763204, "link": null }, { "id": "end-text", "type": "text", - "x": 1077, - "y": 2628, + "x": 1223.5341271166512, + "y": 1864.4331335811921, "width": 46, "height": 25, "angle": 0, @@ -5269,8 +4295,8 @@ "verticalAlign": "middle", "containerId": "end-ellipse", "locked": false, - "version": 2, - "versionNonce": 541745557, + "version": 282, + "versionNonce": 39798950, "index": "b0u", "isDeleted": false, "strokeStyle": "solid", @@ -5278,7 +4304,7 @@ "frameId": null, "roundness": null, "boundElements": [], - "updated": 1763522171080, + "updated": 1764190763204, "link": null, "originalText": "End", "autoResize": true, @@ -5907,6 +4933,235 @@ "originalText": "Decision", "autoResize": true, "lineHeight": 1.25 + }, + { + "id": "4chQ7PksRKpPe5YX-TfFJ", + "type": "arrow", + "x": 1250.9718703296421, + "y": 1311.0799578560604, + "width": 3.1071377799139555, + "height": 47.57227388165256, + "angle": 0, + "strokeColor": "#1976d2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "roughness": 0, + "opacity": 100, + "groupIds": [], + "startBinding": { + "elementId": "label-pass", + "focus": 0.0002774287102738527, + "gap": 9.837794264415606 + }, + "endBinding": { + "elementId": "decision-more-stories", + "focus": 0.07415216095379644, + "gap": 5.01120144889627 + }, + "points": [ + [ + 0, + 0 + ], + [ + 3.1071377799139555, + 47.57227388165256 + ] + ], + "lastCommittedPoint": null, + "version": 1485, + "versionNonce": 384699130, + "index": "b1D", + "isDeleted": false, + "strokeStyle": "solid", + "seed": 1128360742, + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1764190763620, + "link": null, + "locked": false, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "jv0rnlK2D9JKIGTO7pUtT", + "type": "arrow", + "x": 199.95091169427553, + "y": 434.3642722686245, + "width": 152.18808817436843, + "height": 126.81486476828513, + "angle": 0, + "strokeColor": "#1976d2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "roughness": 0, + "opacity": 100, + "groupIds": [], + "startBinding": { + "elementId": "proc-brainstorm", + "focus": 0.3249856938901564, + "gap": 1 + }, + "endBinding": { + "elementId": "proc-prd", + "focus": 0.40022808683972894, + "gap": 7.158084853619243 + }, + "points": [ + [ + 0, + 0 + ], + [ + 69.77818267983719, + 0.8988822936652241 + ], + [ + 84.43045426782976, + -84.30283196996788 + ], + [ + 152.18808817436843, + -125.91598247461991 + ] + ], + "lastCommittedPoint": null, + "version": 2008, + "versionNonce": 1304633062, + "index": "b1F", + "isDeleted": false, + "strokeStyle": "solid", + "seed": 753809018, + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1764191372763, + "link": null, + "locked": false, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "RF10FfKbmG72P77I2IoP4", + "type": "arrow", + "x": 200.50999902520755, + "y": 524.3440535408814, + "width": 155.72897460360434, + "height": 217.43940257292877, + "angle": 0, + "strokeColor": "#1976d2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "roughness": 0, + "opacity": 100, + "groupIds": [], + "startBinding": { + "elementId": "proc-research", + "focus": 0.2547348377789515, + "gap": 1 + }, + "endBinding": { + "elementId": "proc-prd", + "focus": 0.3948133447078272, + "gap": 3.0581110934513163 + }, + "points": [ + [ + 0, + 0 + ], + [ + 71.74164413965786, + -18.904836665604307 + ], + [ + 83.93792495248488, + -172.66332121061578 + ], + [ + 155.72897460360434, + -217.43940257292877 + ] + ], + "lastCommittedPoint": null, + "version": 2022, + "versionNonce": 1289623162, + "index": "b1G", + "isDeleted": false, + "strokeStyle": "solid", + "seed": 389493926, + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1764191336778, + "link": null, + "locked": false, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "FDR4ZvEvNmPvkP3HfQMY4", + "type": "arrow", + "x": 523.1179307657023, + "y": 528.6598293249855, + "width": 156.49193140361945, + "height": 211.37494429949584, + "angle": 0, + "strokeColor": "#1976d2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "roughness": 0, + "opacity": 100, + "groupIds": [], + "startBinding": null, + "endBinding": null, + "points": [ + [ + 0, + 0 + ], + [ + 67.6421465593952, + -30.201232355758236 + ], + [ + 96.50992722652438, + -178.58566948715793 + ], + [ + 156.49193140361945, + -211.37494429949584 + ] + ], + "lastCommittedPoint": null, + "version": 672, + "versionNonce": 1827754470, + "index": "b1I", + "isDeleted": false, + "strokeStyle": "solid", + "seed": 310318758, + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1764191558236, + "link": null, + "locked": false, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false } ], "appState": { diff --git a/src/modules/bmm/docs/images/workflow-method-greenfield.svg b/src/modules/bmm/docs/images/workflow-method-greenfield.svg index 7d2691fe..1b7dd76c 100644 --- a/src/modules/bmm/docs/images/workflow-method-greenfield.svg +++ b/src/modules/bmm/docs/images/workflow-method-greenfield.svg @@ -1,2 +1,2 @@ -BMad Method Workflow - Standard GreenfieldStartPHASE 1Discovery(Optional)IncludeDiscovery?YesBrainstorm<<optional>>Research<<optional>>Product Brief<<optional>>NoPHASE 2Planning (Required)PRDValidate PRD<<optional>>Has UI?YesCreate UXNoPHASE 3Solutioning (Required)ArchitectureEpics/StoriesTest Design<<recommended>>Validate Arch<<optional>>ImplementationReadinessPHASE 4Implementation (Required)Sprint Plan→ EPIC CYCLEEpic ContextValidate Epic<<optional>>→ STORY LOOPCreate StoryValidate Story<<optional>>Context ORReady?ContextStory ContextValidate Context<<optional>>ReadyStory ReadyDevelop StoryCode ReviewPass?FailPassStory DoneMore Storiesin Epic?YesNoRetrospectiveMore Epics?YesNoEndAgent LegendAnalystPMUX DesignerArchitectTEASMDEVDecision \ No newline at end of file +BMad Method Workflow - Standard GreenfieldStartPHASE 1Discovery(Optional)IncludeDiscovery?YesBrainstorm<<optional>>Research<<optional>>Product Brief<<optional>>NoPHASE 2Planning (Required)PRDValidate PRD<<optional>>Has UI?YesCreate UXNoPHASE 3Solutioning (Required)ArchitectureEpics/StoriesTest Design<<optional>>Validate Arch<<optional>>ImplementationReadinessPHASE 4Implementation (Required)Sprint PlanSTORY LOOPCreate StoryValidate Story<<optional>>Develop StoryCode ReviewPass?FailPassCode Review<<use differentLLM>>More Storiesin Epic?YesNoRetrospectiveMore Epics?YesNoEndAgent LegendAnalystPMUX DesignerArchitectTEASMDEVDecision \ No newline at end of file From 331a67eeb3ddb9864e4fb17836a528e15b865b9d Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 16:47:15 -0600 Subject: [PATCH 08/12] installer allows cleanup of unneeded files in upgrades --- tools/cli/commands/cleanup.js | 141 ++++++++ tools/cli/commands/install.js | 9 +- tools/cli/installers/lib/core/installer.js | 375 ++++++++++++++++++++- 3 files changed, 522 insertions(+), 3 deletions(-) create mode 100644 tools/cli/commands/cleanup.js diff --git a/tools/cli/commands/cleanup.js b/tools/cli/commands/cleanup.js new file mode 100644 index 00000000..5dae8e5d --- /dev/null +++ b/tools/cli/commands/cleanup.js @@ -0,0 +1,141 @@ +const chalk = require('chalk'); +const nodePath = require('node:path'); +const { Installer } = require('../installers/lib/core/installer'); + +module.exports = { + command: 'cleanup', + description: 'Clean up obsolete files from BMAD installation', + options: [ + ['-d, --dry-run', 'Show what would be deleted without actually deleting'], + ['-a, --auto-delete', 'Automatically delete non-retained files without prompts'], + ['-l, --list-retained', 'List currently retained files'], + ['-c, --clear-retained', 'Clear retained files list'], + ], + action: async (options) => { + try { + // Create installer and let it find the BMAD directory + const installer = new Installer(); + const bmadDir = await installer.findBmadDir(process.cwd()); + + if (!bmadDir) { + console.error(chalk.red('❌ BMAD installation not found')); + process.exit(1); + } + + const retentionPath = nodePath.join(bmadDir, '_cfg', 'user-retained-files.yaml'); + + // Handle list-retained option + if (options.listRetained) { + const fs = require('fs-extra'); + const yaml = require('js-yaml'); + + if (await fs.pathExists(retentionPath)) { + const retentionContent = await fs.readFile(retentionPath, 'utf8'); + const retentionData = yaml.load(retentionContent) || { retainedFiles: [] }; + + if (retentionData.retainedFiles.length > 0) { + console.log(chalk.cyan('\n📋 Retained Files:\n')); + for (const file of retentionData.retainedFiles) { + console.log(chalk.dim(` - ${file}`)); + } + console.log(); + } else { + console.log(chalk.yellow('\n✨ No retained files found\n')); + } + } else { + console.log(chalk.yellow('\n✨ No retained files found\n')); + } + + return; + } + + // Handle clear-retained option + if (options.clearRetained) { + const fs = require('fs-extra'); + + if (await fs.pathExists(retentionPath)) { + await fs.remove(retentionPath); + console.log(chalk.green('\n✅ Cleared retained files list\n')); + } else { + console.log(chalk.yellow('\n✨ No retained files list to clear\n')); + } + + return; + } + + // Handle cleanup operations + if (options.dryRun) { + console.log(chalk.cyan('\n🔍 Legacy File Scan (Dry Run)\n')); + + const legacyFiles = await installer.scanForLegacyFiles(bmadDir); + const allLegacyFiles = [ + ...legacyFiles.backup, + ...legacyFiles.documentation, + ...legacyFiles.deprecated_task, + ...legacyFiles.unknown, + ]; + + if (allLegacyFiles.length === 0) { + console.log(chalk.green('✨ No legacy files found\n')); + return; + } + + // Group files by category + const categories = []; + if (legacyFiles.backup.length > 0) { + categories.push({ name: 'Backup Files (.bak)', files: legacyFiles.backup }); + } + if (legacyFiles.documentation.length > 0) { + categories.push({ name: 'Documentation', files: legacyFiles.documentation }); + } + if (legacyFiles.deprecated_task.length > 0) { + categories.push({ name: 'Deprecated Task Files', files: legacyFiles.deprecated_task }); + } + if (legacyFiles.unknown.length > 0) { + categories.push({ name: 'Unknown Files', files: legacyFiles.unknown }); + } + + for (const category of categories) { + console.log(chalk.yellow(`${category.name}:`)); + for (const file of category.files) { + const size = (file.size / 1024).toFixed(1); + const date = file.mtime.toLocaleDateString(); + let line = ` - ${file.relativePath} (${size}KB, ${date})`; + if (file.suggestedAlternative) { + line += chalk.dim(` → ${file.suggestedAlternative}`); + } + console.log(chalk.dim(line)); + } + console.log(); + } + + console.log(chalk.cyan(`Found ${allLegacyFiles.length} legacy file(s) that could be cleaned up.\n`)); + console.log(chalk.dim('Run "bmad cleanup" to actually delete these files.\n')); + + return; + } + + // Perform actual cleanup + console.log(chalk.cyan('\n🧹 Cleaning up legacy files...\n')); + + const result = await installer.performCleanup(bmadDir, options.autoDelete); + + if (result.message) { + console.log(chalk.dim(result.message)); + } else { + if (result.deleted > 0) { + console.log(chalk.green(`✅ Deleted ${result.deleted} legacy file(s)`)); + } + if (result.retained > 0) { + console.log(chalk.yellow(`⏭️ Retained ${result.retained} file(s)`)); + console.log(chalk.dim('Run "bmad cleanup --list-retained" to see retained files\n')); + } + } + + console.log(); + } catch (error) { + console.error(chalk.red(`❌ Error: ${error.message}`)); + process.exit(1); + } + }, +}; diff --git a/tools/cli/commands/install.js b/tools/cli/commands/install.js index a9d484d5..e7725338 100644 --- a/tools/cli/commands/install.js +++ b/tools/cli/commands/install.js @@ -9,8 +9,8 @@ const ui = new UI(); module.exports = { command: 'install', description: 'Install BMAD Core agents and tools', - options: [], - action: async () => { + options: [['--skip-cleanup', 'Skip automatic cleanup of legacy files']], + action: async (options) => { try { const config = await ui.promptInstall(); @@ -44,6 +44,11 @@ module.exports = { config._requestedReinstall = true; } + // Add skip cleanup flag if option provided + if (options && options.skipCleanup) { + config.skipCleanup = true; + } + // Regular install/update flow const result = await installer.install(config); diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 8539ebbe..6e6fbab9 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -1018,6 +1018,23 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: customFiles: customFiles.length > 0 ? customFiles : undefined, }); + // Offer cleanup for legacy files (only for updates, not fresh installs, and only if not skipped) + if (!config.skipCleanup && config._isUpdate) { + try { + const cleanupResult = await this.performCleanup(bmadDir, false); + if (cleanupResult.deleted > 0) { + console.log(chalk.green(`\n✓ Cleaned up ${cleanupResult.deleted} legacy file${cleanupResult.deleted > 1 ? 's' : ''}`)); + } + if (cleanupResult.retained > 0) { + console.log(chalk.dim(`Run 'bmad cleanup' anytime to manage retained files`)); + } + } catch (cleanupError) { + // Don't fail the installation for cleanup errors + console.log(chalk.yellow(`\n⚠️ Cleanup warning: ${cleanupError.message}`)); + console.log(chalk.dim('Run "bmad cleanup" to manually clean up legacy files')); + } + } + return { success: true, path: bmadDir, @@ -1939,7 +1956,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: if (existingBmadFolderName === newBmadFolderName) { // Normal quick update - start the spinner - spinner.start('Updating BMAD installation...'); + console.log(chalk.cyan('Updating BMAD installation...')); } else { // Folder name has changed - stop spinner and let install() handle it spinner.stop(); @@ -2578,6 +2595,362 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: } } } + + /** + * Scan for legacy/obsolete files in BMAD installation + * @param {string} bmadDir - BMAD installation directory + * @returns {Object} Categorized files for cleanup + */ + async scanForLegacyFiles(bmadDir) { + const legacyFiles = { + backup: [], + documentation: [], + deprecated_task: [], + unknown: [], + }; + + try { + // Load files manifest to understand what should exist + const manifestPath = path.join(bmadDir, 'files-manifest.csv'); + const manifestFiles = new Set(); + + if (await fs.pathExists(manifestPath)) { + const manifestContent = await fs.readFile(manifestPath, 'utf8'); + const lines = manifestContent.split('\n').slice(1); // Skip header + for (const line of lines) { + if (line.trim()) { + const relativePath = line.split(',')[0]; + if (relativePath) { + manifestFiles.add(relativePath); + } + } + } + } + + // Scan all files recursively + const allFiles = await this.getAllFiles(bmadDir); + + for (const filePath of allFiles) { + const relativePath = path.relative(bmadDir, filePath); + + // Skip expected files + if (this.isExpectedFile(relativePath, manifestFiles)) { + continue; + } + + // Categorize legacy files + if (relativePath.endsWith('.bak')) { + legacyFiles.backup.push({ + path: filePath, + relativePath: relativePath, + size: (await fs.stat(filePath)).size, + mtime: (await fs.stat(filePath)).mtime, + }); + } else if (this.isDocumentationFile(relativePath)) { + legacyFiles.documentation.push({ + path: filePath, + relativePath: relativePath, + size: (await fs.stat(filePath)).size, + mtime: (await fs.stat(filePath)).mtime, + }); + } else if (this.isDeprecatedTaskFile(relativePath)) { + const suggestedAlternative = this.suggestAlternative(relativePath); + legacyFiles.deprecated_task.push({ + path: filePath, + relativePath: relativePath, + size: (await fs.stat(filePath)).size, + mtime: (await fs.stat(filePath)).mtime, + suggestedAlternative, + }); + } else { + legacyFiles.unknown.push({ + path: filePath, + relativePath: relativePath, + size: (await fs.stat(filePath)).size, + mtime: (await fs.stat(filePath)).mtime, + }); + } + } + } catch (error) { + console.warn(`Warning: Could not scan for legacy files: ${error.message}`); + } + + return legacyFiles; + } + + /** + * Get all files in directory recursively + * @param {string} dir - Directory to scan + * @returns {Array} Array of file paths + */ + async getAllFiles(dir) { + const files = []; + + async function scan(currentDir) { + const entries = await fs.readdir(currentDir); + + for (const entry of entries) { + const fullPath = path.join(currentDir, entry); + const stat = await fs.stat(fullPath); + + if (stat.isDirectory()) { + // Skip certain directories + if (!['node_modules', '.git', 'dist', 'build'].includes(entry)) { + await scan(fullPath); + } + } else { + files.push(fullPath); + } + } + } + + await scan(dir); + return files; + } + + /** + * Check if file is expected in installation + * @param {string} relativePath - Relative path from BMAD dir + * @param {Set} manifestFiles - Files from manifest + * @returns {boolean} True if expected file + */ + isExpectedFile(relativePath, manifestFiles) { + // Core files in manifest + if (manifestFiles.has(relativePath)) { + return true; + } + + // Configuration files + if (relativePath.startsWith('_cfg/') || relativePath === 'config.yaml') { + return true; + } + + // Custom files + if (relativePath.startsWith('custom/') || relativePath === 'manifest.yaml') { + return true; + } + + // Generated files + if (relativePath === 'manifest.csv' || relativePath === 'files-manifest.csv') { + return true; + } + + // IDE-specific files + const ides = ['vscode', 'cursor', 'windsurf', 'claude-code', 'github-copilot', 'zsh', 'bash', 'fish']; + if (ides.some((ide) => relativePath.includes(ide))) { + return true; + } + + // BMAD MODULE STRUCTURES - recognize valid module content + const modulePrefixes = ['bmb/', 'bmm/', 'cis/', 'core/', 'bmgd/']; + const validExtensions = ['.yaml', '.yml', '.json', '.csv', '.md', '.xml', '.svg', '.png', '.jpg', '.gif', '.excalidraw', '.js']; + + // Check if this file is in a recognized module directory + for (const modulePrefix of modulePrefixes) { + if (relativePath.startsWith(modulePrefix)) { + // Check if it has a valid extension + const hasValidExtension = validExtensions.some((ext) => relativePath.endsWith(ext)); + if (hasValidExtension) { + return true; + } + } + } + + // Special case for core module resources + if (relativePath.startsWith('core/resources/')) { + return true; + } + + // Special case for docs directory + if (relativePath.startsWith('docs/')) { + return true; + } + + return false; + } + + /** + * Check if file is documentation + * @param {string} relativePath - Relative path + * @returns {boolean} True if documentation + */ + isDocumentationFile(relativePath) { + const docExtensions = ['.md', '.txt', '.pdf']; + const docPatterns = ['docs/', 'README', 'CHANGELOG', 'LICENSE']; + + return docExtensions.some((ext) => relativePath.endsWith(ext)) || docPatterns.some((pattern) => relativePath.includes(pattern)); + } + + /** + * Check if file is deprecated task file + * @param {string} relativePath - Relative path + * @returns {boolean} True if deprecated + */ + isDeprecatedTaskFile(relativePath) { + // Known deprecated files + const deprecatedFiles = ['adv-elicit-methods.csv', 'game-resources.json', 'ux-workflow.json']; + + return deprecatedFiles.some((dep) => relativePath.includes(dep)); + } + + /** + * Suggest alternative for deprecated file + * @param {string} relativePath - Deprecated file path + * @returns {string} Suggested alternative + */ + suggestAlternative(relativePath) { + const alternatives = { + 'adv-elicit-methods.csv': 'Use the new structured workflows in src/modules/', + 'game-resources.json': 'Resources are now integrated into modules', + 'ux-workflow.json': 'UX workflows are now in src/modules/bmm/workflows/', + }; + + for (const [deprecated, alternative] of Object.entries(alternatives)) { + if (relativePath.includes(deprecated)) { + return alternative; + } + } + + return 'Check src/modules/ for new alternatives'; + } + + /** + * Perform interactive cleanup of legacy files + * @param {string} bmadDir - BMAD installation directory + * @param {boolean} skipInteractive - Skip interactive prompts + * @returns {Object} Cleanup results + */ + async performCleanup(bmadDir, skipInteractive = false) { + const inquirer = require('inquirer'); + const yaml = require('js-yaml'); + + // Load user retention preferences + const retentionPath = path.join(bmadDir, '_cfg', 'user-retained-files.yaml'); + let retentionData = { retainedFiles: [], history: [] }; + + if (await fs.pathExists(retentionPath)) { + const retentionContent = await fs.readFile(retentionPath, 'utf8'); + retentionData = yaml.load(retentionContent) || retentionData; + } + + // Scan for legacy files + const legacyFiles = await this.scanForLegacyFiles(bmadDir); + const allLegacyFiles = [...legacyFiles.backup, ...legacyFiles.documentation, ...legacyFiles.deprecated_task, ...legacyFiles.unknown]; + + if (allLegacyFiles.length === 0) { + return { deleted: 0, retained: 0, message: 'No legacy files found' }; + } + + let deletedCount = 0; + let retainedCount = 0; + const filesToDelete = []; + + if (skipInteractive) { + // Auto-delete all non-retained files + for (const file of allLegacyFiles) { + if (!retentionData.retainedFiles.includes(file.relativePath)) { + filesToDelete.push(file); + } + } + } else { + // Interactive cleanup + console.log(chalk.cyan('\n🧹 Legacy File Cleanup\n')); + console.log(chalk.dim('The following obsolete files were found:\n')); + + // Group files by category + const categories = []; + if (legacyFiles.backup.length > 0) { + categories.push({ name: 'Backup Files (.bak)', files: legacyFiles.backup }); + } + if (legacyFiles.documentation.length > 0) { + categories.push({ name: 'Documentation', files: legacyFiles.documentation }); + } + if (legacyFiles.deprecated_task.length > 0) { + categories.push({ name: 'Deprecated Task Files', files: legacyFiles.deprecated_task }); + } + if (legacyFiles.unknown.length > 0) { + categories.push({ name: 'Unknown Files', files: legacyFiles.unknown }); + } + + for (const category of categories) { + console.log(chalk.yellow(`${category.name}:`)); + for (const file of category.files) { + const size = (file.size / 1024).toFixed(1); + const date = file.mtime.toLocaleDateString(); + let line = ` - ${file.relativePath} (${size}KB, ${date})`; + if (file.suggestedAlternative) { + line += chalk.dim(` → ${file.suggestedAlternative}`); + } + console.log(chalk.dim(line)); + } + console.log(); + } + + const prompt = await inquirer.prompt([ + { + type: 'confirm', + name: 'proceed', + message: 'Would you like to review these files for cleanup?', + default: true, + }, + ]); + + if (!prompt.proceed) { + return { deleted: 0, retained: allLegacyFiles.length, message: 'Cleanup cancelled by user' }; + } + + // Show selection interface + const selectionPrompt = await inquirer.prompt([ + { + type: 'checkbox', + name: 'filesToDelete', + message: 'Select files to delete (use SPACEBAR to select, ENTER to continue):', + choices: allLegacyFiles.map((file) => { + const isRetained = retentionData.retainedFiles.includes(file.relativePath); + const description = `${file.relativePath} (${(file.size / 1024).toFixed(1)}KB)`; + return { + name: description, + value: file, + checked: !isRetained && !file.relativePath.includes('.bak'), + }; + }), + pageSize: Math.min(allLegacyFiles.length, 15), + }, + ]); + + filesToDelete.push(...selectionPrompt.filesToDelete); + } + + // Delete selected files + for (const file of filesToDelete) { + try { + await fs.remove(file.path); + deletedCount++; + } catch (error) { + console.warn(`Warning: Could not delete ${file.relativePath}: ${error.message}`); + } + } + + // Count retained files + retainedCount = allLegacyFiles.length - deletedCount; + + // Update retention data + const newlyRetained = allLegacyFiles.filter((f) => !filesToDelete.includes(f)).map((f) => f.relativePath); + + retentionData.retainedFiles = [...new Set([...retentionData.retainedFiles, ...newlyRetained])]; + retentionData.history.push({ + date: new Date().toISOString(), + deleted: deletedCount, + retained: retainedCount, + files: filesToDelete.map((f) => f.relativePath), + }); + + // Save retention data + await fs.ensureDir(path.dirname(retentionPath)); + await fs.writeFile(retentionPath, yaml.dump(retentionData), 'utf8'); + + return { deleted: deletedCount, retained: retainedCount }; + } } module.exports = { Installer }; From 3ac539b61f7d83c0ea20fc026dc081b77b0fa478 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 17:07:09 -0600 Subject: [PATCH 09/12] npm vulnerabilities resolved --- package-lock.json | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07a43df8..80746370 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bmad-method", - "version": "6.0.0-alpha.11", + "version": "6.0.0-alpha.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bmad-method", - "version": "6.0.0-alpha.11", + "version": "6.0.0-alpha.12", "license": "MIT", "dependencies": { "@kayvan/markdown-tree-parser": "^1.6.1", @@ -1023,9 +1023,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -1329,9 +1329,9 @@ } }, "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -2618,9 +2618,9 @@ } }, "node_modules/c8/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -4103,14 +4103,14 @@ } }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -4139,10 +4139,10 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -4808,9 +4808,9 @@ } }, "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -5181,9 +5181,9 @@ } }, "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -5413,9 +5413,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" From 677c0008203d49806874964d70e7261750f9c0e7 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 17:46:26 -0600 Subject: [PATCH 10/12] github uses agents folder now instead of chatmodes --- .../cli/installers/lib/ide/github-copilot.js | 80 ++++++++++++------- tools/cli/lib/ui.js | 4 +- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/tools/cli/installers/lib/ide/github-copilot.js b/tools/cli/installers/lib/ide/github-copilot.js index 40457ac7..998ec657 100644 --- a/tools/cli/installers/lib/ide/github-copilot.js +++ b/tools/cli/installers/lib/ide/github-copilot.js @@ -6,13 +6,13 @@ const { AgentCommandGenerator } = require('./shared/agent-command-generator'); /** * GitHub Copilot setup handler - * Creates chat modes in .github/chatmodes/ and configures VS Code settings + * Creates agents in .github/agents/ and configures VS Code settings */ class GitHubCopilotSetup extends BaseIdeSetup { constructor() { super('github-copilot', 'GitHub Copilot', true); // preferred IDE this.configDir = '.github'; - this.chatmodesDir = 'chatmodes'; + this.agentsDir = 'agents'; this.vscodeDir = '.vscode'; } @@ -98,10 +98,10 @@ class GitHubCopilotSetup extends BaseIdeSetup { const config = options.preCollectedConfig || {}; await this.configureVsCodeSettings(projectDir, { ...options, ...config }); - // Create .github/chatmodes directory + // Create .github/agents directory const githubDir = path.join(projectDir, this.configDir); - const chatmodesDir = path.join(githubDir, this.chatmodesDir); - await this.ensureDir(chatmodesDir); + const agentsDir = path.join(githubDir, this.agentsDir); + await this.ensureDir(agentsDir); // Clean up any existing BMAD files before reinstalling await this.cleanup(projectDir); @@ -110,29 +110,29 @@ class GitHubCopilotSetup extends BaseIdeSetup { const agentGen = new AgentCommandGenerator(this.bmadFolderName); const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); - // Create chat mode files with bmad- prefix - let modeCount = 0; + // Create agent files with bmd- prefix + let agentCount = 0; for (const artifact of agentArtifacts) { const content = artifact.content; - const chatmodeContent = await this.createChatmodeContent({ module: artifact.module, name: artifact.name }, content); + const agentContent = await this.createAgentContent({ module: artifact.module, name: artifact.name }, content); - // Use bmad- prefix: bmad-agent-{module}-{name}.chatmode.md - const targetPath = path.join(chatmodesDir, `bmad-agent-${artifact.module}-${artifact.name}.chatmode.md`); - await this.writeFile(targetPath, chatmodeContent); - modeCount++; + // Use bmd- prefix: bmd-custom-{module}-{name}.agent.md + const targetPath = path.join(agentsDir, `bmd-custom-${artifact.module}-${artifact.name}.agent.md`); + await this.writeFile(targetPath, agentContent); + agentCount++; - console.log(chalk.green(` ✓ Created chat mode: bmad-agent-${artifact.module}-${artifact.name}`)); + console.log(chalk.green(` ✓ Created agent: bmd-custom-${artifact.module}-${artifact.name}`)); } console.log(chalk.green(`✓ ${this.name} configured:`)); - console.log(chalk.dim(` - ${modeCount} chat modes created`)); - console.log(chalk.dim(` - Chat modes directory: ${path.relative(projectDir, chatmodesDir)}`)); + console.log(chalk.dim(` - ${agentCount} agents created`)); + console.log(chalk.dim(` - Agents directory: ${path.relative(projectDir, agentsDir)}`)); console.log(chalk.dim(` - VS Code settings configured`)); - console.log(chalk.dim('\n Chat modes available in VS Code Chat view')); + console.log(chalk.dim('\n Agents available in VS Code Chat view')); return { success: true, - chatmodes: modeCount, + agents: agentCount, settings: true, }; } @@ -208,9 +208,9 @@ class GitHubCopilotSetup extends BaseIdeSetup { } /** - * Create chat mode content + * Create agent content */ - async createChatmodeContent(agent, content) { + async createAgentContent(agent, content) { // Extract metadata from launcher frontmatter if present const descMatch = content.match(/description:\s*"([^"]+)"/); const title = descMatch ? descMatch[1] : this.formatTitle(agent.name); @@ -242,7 +242,7 @@ class GitHubCopilotSetup extends BaseIdeSetup { 'usages', // Find references and navigate definitions ]; - let chatmodeContent = `--- + let agentContent = `--- description: "${description.replaceAll('"', String.raw`\"`)}" tools: ${JSON.stringify(tools)} --- @@ -253,7 +253,7 @@ ${cleanContent} `; - return chatmodeContent; + return agentContent; } /** @@ -271,10 +271,10 @@ ${cleanContent} */ async cleanup(projectDir) { const fs = require('fs-extra'); - const chatmodesDir = path.join(projectDir, this.configDir, this.chatmodesDir); + // Clean up old chatmodes directory + const chatmodesDir = path.join(projectDir, this.configDir, 'chatmodes'); if (await fs.pathExists(chatmodesDir)) { - // Only remove files that start with bmad- prefix const files = await fs.readdir(chatmodesDir); let removed = 0; @@ -286,7 +286,25 @@ ${cleanContent} } if (removed > 0) { - console.log(chalk.dim(` Cleaned up ${removed} existing BMAD chat modes`)); + console.log(chalk.dim(` Cleaned up ${removed} old BMAD chat modes`)); + } + } + + // Clean up new agents directory + const agentsDir = path.join(projectDir, this.configDir, this.agentsDir); + if (await fs.pathExists(agentsDir)) { + const files = await fs.readdir(agentsDir); + let removed = 0; + + for (const file of files) { + if (file.startsWith('bmd-') && file.endsWith('.agent.md')) { + await fs.remove(path.join(agentsDir, file)); + removed++; + } + } + + if (removed > 0) { + console.log(chalk.dim(` Cleaned up ${removed} existing BMAD agents`)); } } } @@ -300,13 +318,13 @@ ${cleanContent} * @returns {Object|null} Info about created command */ async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { - const chatmodesDir = path.join(projectDir, this.configDir, this.chatmodesDir); + const agentsDir = path.join(projectDir, this.configDir, this.agentsDir); if (!(await this.exists(path.join(projectDir, this.configDir)))) { return null; // IDE not configured for this project } - await this.ensureDir(chatmodesDir); + await this.ensureDir(agentsDir); const launcherContent = `You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. @@ -346,7 +364,7 @@ ${cleanContent} 'usages', ]; - const chatmodeContent = `--- + const agentContent = `--- description: "Activates the ${metadata.title || agentName} agent persona." tools: ${JSON.stringify(copilotTools)} --- @@ -356,12 +374,12 @@ tools: ${JSON.stringify(copilotTools)} ${launcherContent} `; - const chatmodePath = path.join(chatmodesDir, `bmad-agent-custom-${agentName}.chatmode.md`); - await this.writeFile(chatmodePath, chatmodeContent); + const agentFilePath = path.join(agentsDir, `bmd-custom-${agentName}.agent.md`); + await this.writeFile(agentFilePath, agentContent); return { - path: chatmodePath, - command: `bmad-agent-custom-${agentName}`, + path: agentFilePath, + command: `bmd-custom-${agentName}`, }; } } diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index 788349b5..32e8dfc0 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -720,8 +720,8 @@ class UI { { type: 'confirm', name: 'enableTts', - message: 'Enable AgentVibes TTS? (Claude Code only - Agents speak with unique voices in party mode)', - default: true, // Default to yes - recommended for best experience + message: 'Enable Agents to Speak Out loud (powered by Agent Vibes? Claude Code only currently)', + default: false, // Default to yes - recommended for best experience }, ]); From dfc35f35f8db51f7ee8eb95f1b8fe1a0e8958ba9 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 18:22:24 -0600 Subject: [PATCH 11/12] BMad Agents menu items are logically ordered and marked with optional or recommended and some required tags --- src/modules/bmm/agents/analyst.agent.yaml | 8 ++++---- src/modules/bmm/agents/architect.agent.yaml | 10 +++++----- src/modules/bmm/agents/dev.agent.yaml | 6 +----- src/modules/bmm/agents/pm.agent.yaml | 8 ++++---- src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml | 6 +++--- src/modules/bmm/agents/sm.agent.yaml | 10 +++++----- src/modules/bmm/agents/ux-designer.agent.yaml | 2 +- 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/modules/bmm/agents/analyst.agent.yaml b/src/modules/bmm/agents/analyst.agent.yaml index 1398183e..0ae86f39 100644 --- a/src/modules/bmm/agents/analyst.agent.yaml +++ b/src/modules/bmm/agents/analyst.agent.yaml @@ -20,19 +20,19 @@ agent: menu: - trigger: brainstorm-project workflow: "{project-root}/{bmad_folder}/bmm/workflows/1-analysis/brainstorm-project/workflow.yaml" - description: Guided Brainstorming scoped to product development ideation and problem discovery + description: Guided Brainstorming session with final report (optional) - trigger: research workflow: "{project-root}/{bmad_folder}/bmm/workflows/1-analysis/research/workflow.yaml" - description: Guided Research scoped to market and competitive analysis of a product or feature + description: Guided Research scoped to market and competitive analysis (optional) - trigger: product-brief workflow: "{project-root}/{bmad_folder}/bmm/workflows/1-analysis/product-brief/workflow.yaml" - description: Create a Product Brief, a great input to then drive a PRD + description: Create a Product Brief (recommended input for PRD) - trigger: document-project workflow: "{project-root}/{bmad_folder}/bmm/workflows/document-project/workflow.yaml" - description: Generate comprehensive documentation of an existing codebase, including architecture, data flows, and API contracts, and other details to aid project understanding. + description: Document your existing project (optional, but recommended for existing brownfield project efforts) - trigger: party-mode workflow: "{project-root}/{bmad_folder}/core/workflows/party-mode/workflow.yaml" diff --git a/src/modules/bmm/agents/architect.agent.yaml b/src/modules/bmm/agents/architect.agent.yaml index dd8dd7fe..f49782ef 100644 --- a/src/modules/bmm/agents/architect.agent.yaml +++ b/src/modules/bmm/agents/architect.agent.yaml @@ -20,23 +20,23 @@ agent: menu: - trigger: create-architecture workflow: "{project-root}/{bmad_folder}/bmm/workflows/3-solutioning/architecture/workflow.yaml" - description: Produce a Scale Adaptive Architecture + description: Create an Architecture Document to Guide Development of a PRD (required for BMad Method projects) - trigger: validate-architecture validate-workflow: "{project-root}/{bmad_folder}/bmm/workflows/3-solutioning/architecture/workflow.yaml" - description: Validate Architecture Document + description: Validate Architecture Document (Recommended, use another LLM and fresh context for best results) - trigger: implementation-readiness workflow: "{project-root}/{bmad_folder}/bmm/workflows/3-solutioning/implementation-readiness/workflow.yaml" - description: Validate implementation readiness - PRD, UX, Architecture, Epics aligned + description: Validate PRD, UX, Architecture, Epics and stories aligned (Optional but recommended before development) - trigger: create-excalidraw-diagram workflow: "{project-root}/{bmad_folder}/bmm/workflows/diagrams/create-diagram/workflow.yaml" - description: Create system architecture or technical diagram (Excalidraw) + description: Create system architecture or technical diagram (Excalidraw) (Use any time you need a diagram) - trigger: create-excalidraw-dataflow workflow: "{project-root}/{bmad_folder}/bmm/workflows/diagrams/create-dataflow/workflow.yaml" - description: Create data flow diagram (Excalidraw) + description: Create data flow diagram (Excalidraw) (Use any time you need a diagram) - trigger: party-mode workflow: "{project-root}/{bmad_folder}/core/workflows/party-mode/workflow.yaml" diff --git a/src/modules/bmm/agents/dev.agent.yaml b/src/modules/bmm/agents/dev.agent.yaml index cddd65a4..3e3fdc2d 100644 --- a/src/modules/bmm/agents/dev.agent.yaml +++ b/src/modules/bmm/agents/dev.agent.yaml @@ -39,10 +39,6 @@ agent: workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/dev-story/workflow.yaml" description: "Execute Dev Story workflow (full BMM path with sprint-status)" - - trigger: story-done - workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/story-done/workflow.yaml" - description: "Mark story done after DoD complete" - - trigger: code-review workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/code-review/workflow.yaml" - description: "Perform a thorough clean context QA code review on a story flagged Ready for Review" + description: "Perform a thorough clean context code review (Highly Recommended, use fresh context and different LLM)" diff --git a/src/modules/bmm/agents/pm.agent.yaml b/src/modules/bmm/agents/pm.agent.yaml index 7280dc57..6e77b917 100644 --- a/src/modules/bmm/agents/pm.agent.yaml +++ b/src/modules/bmm/agents/pm.agent.yaml @@ -21,19 +21,19 @@ agent: menu: - trigger: create-prd workflow: "{project-root}/{bmad_folder}/bmm/workflows/2-plan-workflows/prd/workflow.yaml" - description: Create Product Requirements Document (PRD) + description: Create Product Requirements Document (PRD) (Required for BMad Method flow) - trigger: validate-prd validate-workflow: "{project-root}/{bmad_folder}/bmm/workflows/2-plan-workflows/prd/workflow.yaml" - description: Validate PRD + description: Validate PRD (Highly Recommended, use fresh context and different LLM for best results) - trigger: create-epics-and-stories workflow: "{project-root}/{bmad_folder}/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.yaml" - description: Create Epics and User Stories from PRD (Its recommended to not do this until the architecture is complete) + description: Create Epics and User Stories from PRD (Required for BMad Method flow AFTER the Architecture is completed) - trigger: correct-course workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/correct-course/workflow.yaml" - description: Course Correction Analysis + description: Course Correction Analysis (optional during implementation when things go off track) ide-only: true - trigger: party-mode diff --git a/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml b/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml index 424d9754..aeee5630 100644 --- a/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml +++ b/src/modules/bmm/agents/quick-flow-solo-dev.agent.yaml @@ -21,15 +21,15 @@ agent: menu: - trigger: create-tech-spec workflow: "{project-root}/{bmad_folder}/bmm/workflows/bmad-quick-flow/create-tech-spec/workflow.yaml" - description: Architect a technical spec with implementation-ready stories + description: Architect a technical spec with implementation-ready stories (Required first step) - trigger: quick-dev workflow: "{project-root}/{bmad_folder}/bmm/workflows/bmad-quick-flow/quick-dev/workflow.yaml" - description: Ship features from spec or direct instructions - no handoffs + description: Implement the tech spec end-to-end solo (Core of Quick Flow) - trigger: code-review workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/code-review/workflow.yaml" - description: Review code for quality, patterns, and acceptance criteria + description: Review code and improve it (Highly Recommended, use fresh context and different LLM for best results) - trigger: party-mode workflow: "{project-root}/{bmad_folder}/core/workflows/party-mode/workflow.yaml" diff --git a/src/modules/bmm/agents/sm.agent.yaml b/src/modules/bmm/agents/sm.agent.yaml index 316ff8ae..fa04e7cd 100644 --- a/src/modules/bmm/agents/sm.agent.yaml +++ b/src/modules/bmm/agents/sm.agent.yaml @@ -26,24 +26,24 @@ agent: menu: - trigger: sprint-planning workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/sprint-planning/workflow.yaml" - description: Generate or update sprint-status.yaml from epic files + description: Generate or re-generate sprint-status.yaml from epic files (Required after Epics+Stories are created) - trigger: create-story workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/create-story/workflow.yaml" - description: Create a Draft Story + description: Create a Draft Story (Required to prepare stories for development) - trigger: validate-create-story validate-workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/create-story/workflow.yaml" - description: (Optional) Validate Story Draft with Independent Review + description: Validate Story Draft (Highly Recommended, use fresh context and different LLM for best results) - trigger: epic-retrospective workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/retrospective/workflow.yaml" data: "{project-root}/{bmad_folder}/_cfg/agent-manifest.csv" - description: (Optional) Facilitate team retrospective after an epic is completed + description: Facilitate team retrospective after an epic is completed (Optional) - trigger: correct-course workflow: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/correct-course/workflow.yaml" - description: (Optional) Execute correct-course task + description: Execute correct-course task (When implementation is off-track) - trigger: party-mode workflow: "{project-root}/{bmad_folder}/core/workflows/party-mode/workflow.yaml" diff --git a/src/modules/bmm/agents/ux-designer.agent.yaml b/src/modules/bmm/agents/ux-designer.agent.yaml index fd8b28af..45d700d6 100644 --- a/src/modules/bmm/agents/ux-designer.agent.yaml +++ b/src/modules/bmm/agents/ux-designer.agent.yaml @@ -25,7 +25,7 @@ agent: menu: - trigger: create-ux-design workflow: "{project-root}/{bmad_folder}/bmm/workflows/2-plan-workflows/create-ux-design/workflow.yaml" - description: Conduct Design Thinking Workshop to Define the User Specification with PRD as input + description: Generate a UX Design and UI Plan from a PRD (Recommended before creating Architecture) - trigger: validate-design validate-workflow: "{project-root}/{bmad_folder}/bmm/workflows/2-plan-workflows/create-ux-design/workflow.yaml" From 355ccebca2c1ea09712fb9f1377ec70d549271df Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Wed, 26 Nov 2025 19:48:47 -0600 Subject: [PATCH 12/12] workflow-status can call workflow init --- .../bmm/_module-installer/assets/bmm-kb.md | 1 - .../assets/technical-decisions.md | 30 ----- src/modules/bmm/agents/analyst.agent.yaml | 4 + src/modules/bmm/agents/architect.agent.yaml | 4 + src/modules/bmm/agents/pm.agent.yaml | 4 + .../workflow-status/init/instructions.md | 120 ++++++++++-------- .../workflows/workflow-status/instructions.md | 15 ++- .../paths/quick-flow-brownfield.yaml | 67 ---------- .../paths/quick-flow-greenfield.yaml | 56 -------- 9 files changed, 89 insertions(+), 212 deletions(-) delete mode 100644 src/modules/bmm/_module-installer/assets/bmm-kb.md delete mode 100644 src/modules/bmm/_module-installer/assets/technical-decisions.md delete mode 100644 src/modules/bmm/workflows/workflow-status/paths/quick-flow-brownfield.yaml delete mode 100644 src/modules/bmm/workflows/workflow-status/paths/quick-flow-greenfield.yaml diff --git a/src/modules/bmm/_module-installer/assets/bmm-kb.md b/src/modules/bmm/_module-installer/assets/bmm-kb.md deleted file mode 100644 index 0683986f..00000000 --- a/src/modules/bmm/_module-installer/assets/bmm-kb.md +++ /dev/null @@ -1 +0,0 @@ -# BMad Method Master Knowledge Base Index diff --git a/src/modules/bmm/_module-installer/assets/technical-decisions.md b/src/modules/bmm/_module-installer/assets/technical-decisions.md deleted file mode 100644 index ceac48fb..00000000 --- a/src/modules/bmm/_module-installer/assets/technical-decisions.md +++ /dev/null @@ -1,30 +0,0 @@ -# Technical Decisions Log - -_Auto-updated during discovery and planning sessions - you can also add information here yourself_ - -## Purpose - -This document captures technical decisions, preferences, and constraints discovered during project discussions. It serves as input for architecture.md and solution design documents. - -## Confirmed Decisions - - - -## Preferences - - - -## Constraints - - - -## To Investigate - - - -## Notes - -- This file is automatically updated when technical information is mentioned -- Decisions here are inputs, not final architecture -- Final technical decisions belong in architecture.md -- Implementation details belong in solutions/\*.md and story context or dev notes. diff --git a/src/modules/bmm/agents/analyst.agent.yaml b/src/modules/bmm/agents/analyst.agent.yaml index 0ae86f39..acdc0a17 100644 --- a/src/modules/bmm/agents/analyst.agent.yaml +++ b/src/modules/bmm/agents/analyst.agent.yaml @@ -18,6 +18,10 @@ agent: - Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md` menu: + - trigger: workflow-status + workflow: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/workflow.yaml" + description: Get workflow status or initialize a workflow if not already done (optional) + - trigger: brainstorm-project workflow: "{project-root}/{bmad_folder}/bmm/workflows/1-analysis/brainstorm-project/workflow.yaml" description: Guided Brainstorming session with final report (optional) diff --git a/src/modules/bmm/agents/architect.agent.yaml b/src/modules/bmm/agents/architect.agent.yaml index f49782ef..c8cc6b78 100644 --- a/src/modules/bmm/agents/architect.agent.yaml +++ b/src/modules/bmm/agents/architect.agent.yaml @@ -18,6 +18,10 @@ agent: - Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md` menu: + - trigger: workflow-status + workflow: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/workflow.yaml" + description: Get workflow status or initialize a workflow if not already done (optional) + - trigger: create-architecture workflow: "{project-root}/{bmad_folder}/bmm/workflows/3-solutioning/architecture/workflow.yaml" description: Create an Architecture Document to Guide Development of a PRD (required for BMad Method projects) diff --git a/src/modules/bmm/agents/pm.agent.yaml b/src/modules/bmm/agents/pm.agent.yaml index 6e77b917..61d08a2a 100644 --- a/src/modules/bmm/agents/pm.agent.yaml +++ b/src/modules/bmm/agents/pm.agent.yaml @@ -19,6 +19,10 @@ agent: - Find if this exists, if it does, always treat it as the bible I plan and execute against: `**/project-context.md` menu: + - trigger: workflow-status + workflow: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/workflow.yaml" + description: Get workflow status or initialize a workflow if not already done (optional) + - trigger: create-prd workflow: "{project-root}/{bmad_folder}/bmm/workflows/2-plan-workflows/prd/workflow.yaml" description: Create Product Requirements Document (PRD) (Required for BMad Method flow) diff --git a/src/modules/bmm/workflows/workflow-status/init/instructions.md b/src/modules/bmm/workflows/workflow-status/init/instructions.md index 6f842305..2519fe14 100644 --- a/src/modules/bmm/workflows/workflow-status/init/instructions.md +++ b/src/modules/bmm/workflows/workflow-status/init/instructions.md @@ -3,7 +3,7 @@ The workflow execution engine is governed by: {project-root}/{bmad_folder}/core/tasks/workflow.xml You MUST have already loaded and processed: workflow-init/workflow.yaml Communicate in {communication_language} with {user_name} -This workflow handles BOTH new projects AND legacy projects being migrated to BMad Method +This workflow handles BOTH new projects AND legacy projects following the BMad Method @@ -12,7 +12,7 @@ Perform comprehensive scan for existing work: -- BMM artifacts: PRD, tech-spec, epics, architecture, UX, brief, research, brainstorm +- BMM artifacts: PRD, epics, architecture, UX, brief, research, brainstorm - Implementation: stories, sprint-status, workflow-status - Codebase: source directories, package files, git repo - Check both {output_folder} and {sprint_artifacts} locations @@ -53,31 +53,31 @@ Happy building! 🚀 How would you like to proceed? -a) **Continue** - Work with existing artifacts -b) **Archive & Start Fresh** - Move old work to archive -c) **Express Setup** - I know exactly what I need -d) **Guided Setup** - Walk me through options +1. **Continue** - Work with existing artifacts +2. **Archive & Start Fresh** - Move old work to archive +3. **Express Setup** - I know exactly what I need +4. **Guided Setup** - Walk me through options -Choice [a/b/c/d]: +Choice [1-4] - + Set continuing_existing = true Store found artifacts Continue to step 7 (detect track from artifacts) - + Archive existing work? (y/n) Move artifacts to {output_folder}/archive/ Ready for fresh start! Continue to step 3 - + Jump to step 3 (express path) - + Continue to step 4 (guided path) @@ -85,16 +85,16 @@ Choice [a/b/c/d]: Setup approach: -a) **Express** - I know what I need -b) **Guided** - Show me the options +1. **Express** - I know what I need +2. **Guided** - Show me the options -Choice [a/b]: +Choice [1 or 2]: - + Continue to step 3 (express) - + Continue to step 4 (guided) @@ -102,20 +102,22 @@ Choice [a/b]: Is this for: -1) **New project** (greenfield) -2) **Existing codebase** (brownfield) +1. **New project** (greenfield) +2. **Existing codebase** (brownfield) Choice [1/2]: Set field_type based on choice Planning approach: -1. **Quick Flow** - Minimal planning, fast to code -2. **BMad Method** - Full planning for complex projects -3. **Enterprise Method** - Extended planning with security/DevOps +1. **BMad Method** - Full planning for complex projects +2. **Enterprise Method** - Extended planning with security/DevOps -Choice [1/2/3]: -Map to selected_track: quick-flow/method/enterprise +Choice [1/2]: +Map to selected_track: method/enterprise + +🚀 **For Quick Flow (minimal planning, straight to code):** +Load the **quick-flow-solo-dev** agent instead - use Quick Flow agent for faster development field_type selected_track @@ -135,8 +137,8 @@ Choice [1/2/3]: I see existing code. Are you: -1) **Modifying** existing codebase (brownfield) -2) **Starting fresh** - code is just scaffold (greenfield) +1. **Modifying** existing codebase (brownfield) +2. **Starting fresh** - code is just scaffold (greenfield) Choice [1/2]: Set field_type based on answer @@ -165,44 +167,60 @@ Continue with software workflows? (y/n) -Based on your project, here are your planning options: +Based on your project, here are your BMad Method planning options: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -**1. Quick Flow** 🚀 - -- Minimal planning, straight to code -- Best for: Simple features, bug fixes -- Risk: Potential rework if complexity emerges - -**2. BMad Method** 🎯 {{#if recommended}}(RECOMMENDED){{/if}} +**1. BMad Method** 🎯 {{#if recommended}}(RECOMMENDED){{/if}} - Full planning: PRD + UX + Architecture - Best for: Products, platforms, complex features - Benefit: AI agents have complete context for better results -**3. Enterprise Method** 🏢 +**2. Enterprise Method** 🏢 - Extended: Method + Security + DevOps + Testing - Best for: Enterprise, compliance, mission-critical - Benefit: Comprehensive planning for complex systems +**🚀 For Quick Flow (minimal planning, straight to code):** +Load the **quick-flow-solo-dev** agent instead - use Quick Flow agent for faster development + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ {{#if brownfield}} 💡 Architecture creates focused solution design from your codebase, keeping AI agents on track. {{/if}} -Which approach fits best? +Which BMad Method approach fits best? -1. Quick Flow -2. BMad Method {{#if recommended}}(recommended){{/if}} -3. Enterprise Method -4. Help me decide +1. BMad Method {{#if recommended}}(recommended){{/if}} +2. Enterprise Method +3. Help me decide +4. Switch to Quick Flow (use quick-flow-solo-dev agent) Choice [1/2/3/4]: + 🚀 **Switching to Quick Flow!** + +Load the **quick-flow-solo-dev** agent instead: + +- Start a new chat +- Load the quick-flow-solo-dev agent +- Use Quick Flow for minimal planning and faster development + +Quick Flow is perfect for: + +- Simple features and bug fixes +- Rapid prototyping +- When you want to get straight to code + +Happy coding! 🚀 +Exit workflow + + + What concerns you about choosing? Provide tailored guidance based on concerns Loop back to choice @@ -215,7 +233,7 @@ Choice [1/2/3/4]: Determine available discovery workflows based on: - field_type (greenfield gets product-brief option) -- selected_track (quick-flow skips product-brief) +- selected_track (method/enterprise options) @@ -229,7 +247,7 @@ Choice [1/2/3/4]: Enter numbers (e.g., "1,3" or "all" or "none"): - + Optional discovery workflows: Include any of these? @@ -250,7 +268,7 @@ Enter numbers (e.g., "1,2" or "none"): research_requested product_brief_requested - + 💡 **Note:** For brownfield projects, run document-project workflow first to analyze your codebase. @@ -258,18 +276,18 @@ Enter numbers (e.g., "1,2" or "none"): Analyze artifacts to detect track: - Has PRD → BMad Method -- Has tech-spec only → Quick Flow - Has Security/DevOps → Enterprise Method +- Has tech-spec only → Suggest switching to quick-flow-solo-dev agent Detected: **{{detected_track}}** based on {{found_artifacts}} Correct? (y/n) -Which track instead? +Which BMad Method track instead? -1. Quick Flow -2. BMad Method -3. Enterprise Method +1. BMad Method +2. Enterprise Method +3. Switch to Quick Flow (use quick-flow-solo-dev agent) Choice: @@ -298,11 +316,8 @@ Choice: {{#if brownfield}}Prerequisites: document-project{{/if}} {{#if has_discovery}}Discovery: {{list_selected_discovery}}{{/if}} -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - {{workflow_path_summary}} - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Create workflow tracking file? (y/n) @@ -326,9 +341,6 @@ To check progress: /bmad:bmm:workflows:workflow-status Happy building! 🚀 - - No problem! Run workflow-init again when ready. - diff --git a/src/modules/bmm/workflows/workflow-status/instructions.md b/src/modules/bmm/workflows/workflow-status/instructions.md index fc5059ef..6ef9bd78 100644 --- a/src/modules/bmm/workflows/workflow-status/instructions.md +++ b/src/modules/bmm/workflows/workflow-status/instructions.md @@ -37,12 +37,19 @@ Search {output_folder}/ for file: bmm-workflow-status.yaml - No workflow status found. To get started: + No workflow status found. + Would you like to run Workflow Init now? (y/n) -Load analyst agent and run: `workflow-init` + + Launching workflow-init to set up your project tracking... + + Exit workflow and let workflow-init take over + -This will guide you through project setup and create your workflow path. -Exit workflow + + No workflow status file. Run workflow-init when ready to enable progress tracking. + Exit workflow + diff --git a/src/modules/bmm/workflows/workflow-status/paths/quick-flow-brownfield.yaml b/src/modules/bmm/workflows/workflow-status/paths/quick-flow-brownfield.yaml deleted file mode 100644 index d9d1fbc6..00000000 --- a/src/modules/bmm/workflows/workflow-status/paths/quick-flow-brownfield.yaml +++ /dev/null @@ -1,67 +0,0 @@ -# BMad Quick Flow - Brownfield -# Fast spec-driven development for existing codebases (1-10 stories typically) - -method_name: "BMad Quick Flow" -track: "quick-flow" -field_type: "brownfield" -description: "Spec-driven development for brownfield projects - streamlined path with codebase context" - -phases: - - prerequisite: true - name: "Documentation" - conditional: "if_undocumented" - note: "NOT a phase - prerequisite for brownfield without docs" - workflows: - - id: "document-project" - required: true - agent: "analyst" - command: "document-project" - output: "Comprehensive project documentation" - purpose: "Generate codebase context for spec engineering" - - - phase: 0 - name: "Discovery (Optional)" - optional: true - note: "User-selected during workflow-init" - workflows: - - id: "brainstorm-project" - optional: true - agent: "analyst" - command: "brainstorm-project" - included_by: "user_choice" - - - id: "research" - optional: true - agent: "analyst" - command: "research" - included_by: "user_choice" - - - phase: 1 - name: "Spec Engineering" - required: true - workflows: - - id: "create-tech-spec" - required: true - agent: "quick-flow-solo-dev" - command: "create-tech-spec" - output: "Technical Specification with implementation-ready stories" - note: "Stories include codebase context from document-project" - - - phase: 2 - name: "Implementation" - required: true - note: "Barry executes all stories, optional code-review after each" - workflows: - - id: "dev-spec" - required: true - repeat: true - agent: "quick-flow-solo-dev" - command: "dev-spec" - note: "Execute stories from spec - Barry is the one-man powerhouse" - - - id: "code-review" - optional: true - repeat: true - agent: "quick-flow-solo-dev" - command: "code-review" - note: "Review completed story implementation" diff --git a/src/modules/bmm/workflows/workflow-status/paths/quick-flow-greenfield.yaml b/src/modules/bmm/workflows/workflow-status/paths/quick-flow-greenfield.yaml deleted file mode 100644 index 87478445..00000000 --- a/src/modules/bmm/workflows/workflow-status/paths/quick-flow-greenfield.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# BMad Quick Flow - Greenfield -# Fast spec-driven development path (1-10 stories typically) - -method_name: "BMad Quick Flow" -track: "quick-flow" -field_type: "greenfield" -description: "Spec-driven development for greenfield projects - streamlined path without sprint overhead" - -phases: - - phase: 0 - name: "Discovery (Optional)" - optional: true - note: "User-selected during workflow-init" - workflows: - - id: "brainstorm-project" - optional: true - agent: "analyst" - command: "brainstorm-project" - included_by: "user_choice" - - - id: "research" - optional: true - agent: "analyst" - command: "research" - included_by: "user_choice" - note: "Can have multiple research workflows" - - - phase: 1 - name: "Spec Engineering" - required: true - workflows: - - id: "create-tech-spec" - required: true - agent: "quick-flow-solo-dev" - command: "create-tech-spec" - output: "Technical Specification with implementation-ready stories" - note: "Stories contain all context for execution" - - - phase: 2 - name: "Implementation" - required: true - note: "Barry executes all stories, optional code-review after each" - workflows: - - id: "dev-spec" - required: true - repeat: true - agent: "quick-flow-solo-dev" - command: "dev-spec" - note: "Execute stories from spec - Barry is the one-man powerhouse" - - - id: "code-review" - optional: true - repeat: true - agent: "quick-flow-solo-dev" - command: "code-review" - note: "Review completed story implementation"