diff --git a/docs/explanation/agents/barry-quick-flow.md b/docs/_archive/explanation/agents/barry-quick-flow.md similarity index 100% rename from docs/explanation/agents/barry-quick-flow.md rename to docs/_archive/explanation/agents/barry-quick-flow.md diff --git a/docs/explanation/agents/index.md b/docs/_archive/explanation/agents/index.md similarity index 100% rename from docs/explanation/agents/index.md rename to docs/_archive/explanation/agents/index.md diff --git a/docs/explanation/core-concepts/index.md b/docs/_archive/explanation/core-concepts/index.md similarity index 100% rename from docs/explanation/core-concepts/index.md rename to docs/_archive/explanation/core-concepts/index.md diff --git a/docs/explanation/core-concepts/what-are-agents.md b/docs/_archive/explanation/core-concepts/what-are-agents.md similarity index 100% rename from docs/explanation/core-concepts/what-are-agents.md rename to docs/_archive/explanation/core-concepts/what-are-agents.md diff --git a/docs/explanation/core/index.md b/docs/_archive/explanation/core/index.md similarity index 100% rename from docs/explanation/core/index.md rename to docs/_archive/explanation/core/index.md diff --git a/docs/explanation/features/web-bundles.md b/docs/_archive/explanation/features/web-bundles.md similarity index 100% rename from docs/explanation/features/web-bundles.md rename to docs/_archive/explanation/features/web-bundles.md diff --git a/docs/how-to/workflows/bmgd-quick-flow.md b/docs/_archive/how-to-workflows/bmgd-quick-flow.md similarity index 100% rename from docs/how-to/workflows/bmgd-quick-flow.md rename to docs/_archive/how-to-workflows/bmgd-quick-flow.md diff --git a/docs/how-to/workflows/conduct-research.md b/docs/_archive/how-to-workflows/conduct-research.md similarity index 100% rename from docs/how-to/workflows/conduct-research.md rename to docs/_archive/how-to-workflows/conduct-research.md diff --git a/docs/how-to/workflows/create-architecture.md b/docs/_archive/how-to-workflows/create-architecture.md similarity index 100% rename from docs/how-to/workflows/create-architecture.md rename to docs/_archive/how-to-workflows/create-architecture.md diff --git a/docs/how-to/workflows/create-epics-and-stories.md b/docs/_archive/how-to-workflows/create-epics-and-stories.md similarity index 100% rename from docs/how-to/workflows/create-epics-and-stories.md rename to docs/_archive/how-to-workflows/create-epics-and-stories.md diff --git a/docs/how-to/workflows/create-prd.md b/docs/_archive/how-to-workflows/create-prd.md similarity index 100% rename from docs/how-to/workflows/create-prd.md rename to docs/_archive/how-to-workflows/create-prd.md diff --git a/docs/how-to/workflows/create-product-brief.md b/docs/_archive/how-to-workflows/create-product-brief.md similarity index 100% rename from docs/how-to/workflows/create-product-brief.md rename to docs/_archive/how-to-workflows/create-product-brief.md diff --git a/docs/how-to/workflows/create-story.md b/docs/_archive/how-to-workflows/create-story.md similarity index 100% rename from docs/how-to/workflows/create-story.md rename to docs/_archive/how-to-workflows/create-story.md diff --git a/docs/how-to/workflows/create-ux-design.md b/docs/_archive/how-to-workflows/create-ux-design.md similarity index 100% rename from docs/how-to/workflows/create-ux-design.md rename to docs/_archive/how-to-workflows/create-ux-design.md diff --git a/docs/how-to/workflows/implement-story.md b/docs/_archive/how-to-workflows/implement-story.md similarity index 100% rename from docs/how-to/workflows/implement-story.md rename to docs/_archive/how-to-workflows/implement-story.md diff --git a/docs/how-to/workflows/quick-spec.md b/docs/_archive/how-to-workflows/quick-spec.md similarity index 100% rename from docs/how-to/workflows/quick-spec.md rename to docs/_archive/how-to-workflows/quick-spec.md diff --git a/docs/how-to/workflows/run-brainstorming-session.md b/docs/_archive/how-to-workflows/run-brainstorming-session.md similarity index 100% rename from docs/how-to/workflows/run-brainstorming-session.md rename to docs/_archive/how-to-workflows/run-brainstorming-session.md diff --git a/docs/how-to/workflows/run-code-review.md b/docs/_archive/how-to-workflows/run-code-review.md similarity index 100% rename from docs/how-to/workflows/run-code-review.md rename to docs/_archive/how-to-workflows/run-code-review.md diff --git a/docs/how-to/workflows/run-implementation-readiness.md b/docs/_archive/how-to-workflows/run-implementation-readiness.md similarity index 100% rename from docs/how-to/workflows/run-implementation-readiness.md rename to docs/_archive/how-to-workflows/run-implementation-readiness.md diff --git a/docs/how-to/workflows/run-sprint-planning.md b/docs/_archive/how-to-workflows/run-sprint-planning.md similarity index 100% rename from docs/how-to/workflows/run-sprint-planning.md rename to docs/_archive/how-to-workflows/run-sprint-planning.md diff --git a/docs/how-to/workflows/setup-party-mode.md b/docs/_archive/how-to-workflows/setup-party-mode.md similarity index 100% rename from docs/how-to/workflows/setup-party-mode.md rename to docs/_archive/how-to-workflows/setup-party-mode.md diff --git a/docs/reference/workflows/core-workflows.md b/docs/_archive/reference/workflows/core-workflows.md similarity index 100% rename from docs/reference/workflows/core-workflows.md rename to docs/_archive/reference/workflows/core-workflows.md diff --git a/docs/reference/workflows/document-project.md b/docs/_archive/reference/workflows/document-project.md similarity index 100% rename from docs/reference/workflows/document-project.md rename to docs/_archive/reference/workflows/document-project.md diff --git a/docs/reference/workflows/index.md b/docs/_archive/reference/workflows/index.md similarity index 100% rename from docs/reference/workflows/index.md rename to docs/_archive/reference/workflows/index.md diff --git a/docs/explanation/bmm/index.md b/docs/_basement/explanation/bmm.md similarity index 100% rename from docs/explanation/bmm/index.md rename to docs/_basement/explanation/bmm.md diff --git a/docs/explanation/core-concepts/agent-roles.md b/docs/_basement/explanation/core-concepts/agent-roles.md similarity index 100% rename from docs/explanation/core-concepts/agent-roles.md rename to docs/_basement/explanation/core-concepts/agent-roles.md diff --git a/docs/explanation/core-concepts/what-are-modules.md b/docs/_basement/explanation/core-concepts/what-are-modules.md similarity index 100% rename from docs/explanation/core-concepts/what-are-modules.md rename to docs/_basement/explanation/core-concepts/what-are-modules.md diff --git a/docs/explanation/core-concepts/what-are-workflows.md b/docs/_basement/explanation/core-concepts/what-are-workflows.md similarity index 100% rename from docs/explanation/core-concepts/what-are-workflows.md rename to docs/_basement/explanation/core-concepts/what-are-workflows.md diff --git a/docs/explanation/philosophy/facilitation-over-generation.md b/docs/_basement/explanation/facilitation-over-generation.md similarity index 100% rename from docs/explanation/philosophy/facilitation-over-generation.md rename to docs/_basement/explanation/facilitation-over-generation.md diff --git a/docs/explanation/faq/brownfield-faq.md b/docs/_basement/explanation/faq/brownfield-faq.md similarity index 100% rename from docs/explanation/faq/brownfield-faq.md rename to docs/_basement/explanation/faq/brownfield-faq.md diff --git a/docs/explanation/faq/getting-started-faq.md b/docs/_basement/explanation/faq/getting-started-faq.md similarity index 100% rename from docs/explanation/faq/getting-started-faq.md rename to docs/_basement/explanation/faq/getting-started-faq.md diff --git a/docs/explanation/faq/implementation-faq.md b/docs/_basement/explanation/faq/implementation-faq.md similarity index 100% rename from docs/explanation/faq/implementation-faq.md rename to docs/_basement/explanation/faq/implementation-faq.md diff --git a/docs/explanation/faq/index.md b/docs/_basement/explanation/faq/index.md similarity index 100% rename from docs/explanation/faq/index.md rename to docs/_basement/explanation/faq/index.md diff --git a/docs/explanation/faq/levels-and-tracks-faq.md b/docs/_basement/explanation/faq/levels-and-tracks-faq.md similarity index 100% rename from docs/explanation/faq/levels-and-tracks-faq.md rename to docs/_basement/explanation/faq/levels-and-tracks-faq.md diff --git a/docs/explanation/faq/planning-faq.md b/docs/_basement/explanation/faq/planning-faq.md similarity index 100% rename from docs/explanation/faq/planning-faq.md rename to docs/_basement/explanation/faq/planning-faq.md diff --git a/docs/explanation/faq/tools-faq.md b/docs/_basement/explanation/faq/tools-faq.md similarity index 100% rename from docs/explanation/faq/tools-faq.md rename to docs/_basement/explanation/faq/tools-faq.md diff --git a/docs/explanation/faq/workflows-faq.md b/docs/_basement/explanation/faq/workflows-faq.md similarity index 100% rename from docs/explanation/faq/workflows-faq.md rename to docs/_basement/explanation/faq/workflows-faq.md diff --git a/docs/explanation/architecture/four-phases.md b/docs/_basement/explanation/four-phases.md similarity index 100% rename from docs/explanation/architecture/four-phases.md rename to docs/_basement/explanation/four-phases.md diff --git a/docs/explanation/architecture/preventing-agent-conflicts.md b/docs/_basement/explanation/preventing-agent-conflicts.md similarity index 100% rename from docs/explanation/architecture/preventing-agent-conflicts.md rename to docs/_basement/explanation/preventing-agent-conflicts.md diff --git a/docs/explanation/architecture/why-solutioning-matters.md b/docs/_basement/explanation/why-solutioning-matters.md similarity index 100% rename from docs/explanation/architecture/why-solutioning-matters.md rename to docs/_basement/explanation/why-solutioning-matters.md diff --git a/docs/how-to/brownfield/add-feature-to-existing.md b/docs/_basement/how-to/brownfield/add-feature-to-existing.md similarity index 100% rename from docs/how-to/brownfield/add-feature-to-existing.md rename to docs/_basement/how-to/brownfield/add-feature-to-existing.md diff --git a/docs/how-to/brownfield/document-existing-project.md b/docs/_basement/how-to/brownfield/document-existing-project.md similarity index 100% rename from docs/how-to/brownfield/document-existing-project.md rename to docs/_basement/how-to/brownfield/document-existing-project.md diff --git a/docs/how-to/brownfield/index.md b/docs/_basement/how-to/brownfield/index.md similarity index 100% rename from docs/how-to/brownfield/index.md rename to docs/_basement/how-to/brownfield/index.md diff --git a/docs/how-to/brownfield/quick-fix-in-brownfield.md b/docs/_basement/how-to/brownfield/quick-fix-in-brownfield.md similarity index 100% rename from docs/how-to/brownfield/quick-fix-in-brownfield.md rename to docs/_basement/how-to/brownfield/quick-fix-in-brownfield.md diff --git a/docs/how-to/customization/customize-agents.md b/docs/_basement/how-to/customization/customize-agents.md similarity index 100% rename from docs/how-to/customization/customize-agents.md rename to docs/_basement/how-to/customization/customize-agents.md diff --git a/docs/how-to/customization/shard-large-documents.md b/docs/_basement/how-to/customization/shard-large-documents.md similarity index 100% rename from docs/how-to/customization/shard-large-documents.md rename to docs/_basement/how-to/customization/shard-large-documents.md diff --git a/docs/how-to/installation/install-custom-modules.md b/docs/_basement/how-to/installation/install-custom-modules.md similarity index 100% rename from docs/how-to/installation/install-custom-modules.md rename to docs/_basement/how-to/installation/install-custom-modules.md diff --git a/docs/how-to/installation/upgrade-to-v6.md b/docs/_basement/how-to/installation/upgrade-to-v6.md similarity index 100% rename from docs/how-to/installation/upgrade-to-v6.md rename to docs/_basement/how-to/installation/upgrade-to-v6.md diff --git a/docs/reference/agents/index.md b/docs/_basement/reference/agents.md similarity index 100% rename from docs/reference/agents/index.md rename to docs/_basement/reference/agents.md diff --git a/docs/reference/configuration/core-tasks.md b/docs/_basement/reference/configuration/core-tasks.md similarity index 100% rename from docs/reference/configuration/core-tasks.md rename to docs/_basement/reference/configuration/core-tasks.md diff --git a/docs/reference/configuration/global-config.md b/docs/_basement/reference/configuration/global-config.md similarity index 100% rename from docs/reference/configuration/global-config.md rename to docs/_basement/reference/configuration/global-config.md diff --git a/docs/downloads.md b/docs/downloads.md index 0db386cf..7f9a4133 100644 --- a/docs/downloads.md +++ b/docs/downloads.md @@ -6,19 +6,21 @@ Download BMad Method resources for offline use, AI training, or integration. ## Source Bundles +Download these from the `downloads/` folder on the documentation site. + | File | Description | |------|-------------| -| **[bmad-sources.zip](/downloads/bmad-sources.zip)** | Complete BMad source files | -| **[bmad-prompts.zip](/downloads/bmad-prompts.zip)** | Agent and workflow prompts only | +| `bmad-sources.zip` | Complete BMad source files | +| `bmad-prompts.zip` | Agent and workflow prompts only | ## LLM-Optimized Files -These files are designed for AI consumption - perfect for loading into Claude, ChatGPT, or any LLM context window. +These files are designed for AI consumption - perfect for loading into Claude, ChatGPT, or any LLM context window. See [API Access](#api-access) below for URLs. | File | Description | Use Case | |------|-------------|----------| -| **[llms.txt](/llms.txt)** | Documentation index with summaries | Quick overview, navigation | -| **[llms-full.txt](/llms-full.txt)** | Complete documentation concatenated | Full context loading | +| `llms.txt` | Documentation index with summaries | Quick overview, navigation | +| `llms-full.txt` | Complete documentation concatenated | Full context loading | ### Using with LLMs @@ -41,12 +43,12 @@ docs = requests.get("https://bmad-code-org.github.io/BMAD-METHOD/llms-full.txt") ## Installation Options -### NPM (Recommended) - ```bash npx bmad-method@alpha install ``` +[More details](/docs/how-to/install-bmad.md) + ## Version Information - **Current Version:** See [CHANGELOG](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/CHANGELOG.md) diff --git a/docs/explanation/advanced-elicitation.md b/docs/explanation/advanced-elicitation.md new file mode 100644 index 00000000..c7ca5bcf --- /dev/null +++ b/docs/explanation/advanced-elicitation.md @@ -0,0 +1,24 @@ +--- +title: "Advanced Elicitation" +description: Push the LLM to rethink its work using structured reasoning methods +--- + +Make the LLM reconsider what it just generated. You pick a reasoning method, it applies that method to its own output, you decide whether to keep the improvements. + +Dozens of methods are built in - things like First Principles, Red Team vs Blue Team, Pre-mortem Analysis, Socratic Questioning, and more. + +## When to Use It + +- After a workflow generates content and you want alternatives +- When output seems okay but you suspect there's more depth +- To stress-test assumptions or find weaknesses +- For high-stakes content where rethinking helps + +Workflows offer advanced elicitation at decision points - after the LLM has generated something, you'll be asked if you want to run it. + +## How It Works + +1. LLM suggests 5 relevant methods for your content +2. You pick one (or reshuffle for different options) +3. Method is applied, improvements shown +4. Accept or discard, repeat or continue diff --git a/docs/explanation/adversarial-review.md b/docs/explanation/adversarial-review.md new file mode 100644 index 00000000..4543616e --- /dev/null +++ b/docs/explanation/adversarial-review.md @@ -0,0 +1,57 @@ +--- +title: "Adversarial Review" +description: Forced reasoning technique that prevents lazy "looks good" reviews +--- + +Force deeper analysis by requiring problems to be found. + +## What is Adversarial Review? + +A review technique where the reviewer *must* find issues. No "looks good" allowed. The reviewer adopts a cynical stance - assume problems exist and find them. + +This isn't about being negative. It's about forcing genuine analysis instead of a cursory glance that rubber-stamps whatever was submitted. + +**The core rule:** You must find issues. Zero findings triggers a halt - re-analyze or explain why. + +## Why It Works + +Normal reviews suffer from confirmation bias. You skim the work, nothing jumps out, you approve it. The "find problems" mandate breaks this pattern: + +- **Forces thoroughness** - Can't approve until you've looked hard enough to find issues +- **Catches missing things** - "What's not here?" becomes a natural question +- **Improves signal quality** - Findings are specific and actionable, not vague concerns +- **Information asymmetry** - Run reviews with fresh context (no access to original reasoning) so you evaluate the artifact, not the intent + +## Where It's Used + +Adversarial review appears throughout BMAD workflows - code review, implementation readiness checks, spec validation, and others. Sometimes it's a required step, sometimes optional (like advanced elicitation or party mode). The pattern adapts to whatever artifact needs scrutiny. + +## Human Filtering Required + +Because the AI is *instructed* to find problems, it will find problems - even when they don't exist. Expect false positives: nitpicks dressed as issues, misunderstandings of intent, or outright hallucinated concerns. + +**You decide what's real.** Review each finding, dismiss the noise, fix what matters. + +## Example + +Instead of: + +> "The authentication implementation looks reasonable. Approved." + +An adversarial review produces: + +> 1. **HIGH** - `login.ts:47` - No rate limiting on failed attempts +> 2. **HIGH** - Session token stored in localStorage (XSS vulnerable) +> 3. **MEDIUM** - Password validation happens client-side only +> 4. **MEDIUM** - No audit logging for failed login attempts +> 5. **LOW** - Magic number `3600` should be `SESSION_TIMEOUT_SECONDS` + +The first review might miss a security vulnerability. The second caught four. + +## Iteration and Diminishing Returns + +After addressing findings, consider running it again. A second pass usually catches more. A third isn't always useless either. But each pass takes time, and eventually you hit diminishing returns - just nitpicks and false findings. + +:::tip[Better Reviews] +Assume problems exist. Look for what's missing, not just what's wrong. +::: diff --git a/docs/explanation/brainstorming.md b/docs/explanation/brainstorming.md new file mode 100644 index 00000000..f4301b2f --- /dev/null +++ b/docs/explanation/brainstorming.md @@ -0,0 +1,31 @@ +--- +title: "Brainstorming" +description: Interactive creative sessions using 60+ proven ideation techniques +--- + +Unlock your creativity through guided exploration. + +## What is Brainstorming? + +Run `brainstorming` and you've got a creative facilitator pulling ideas out of you - not generating them for you. The AI acts as coach and guide, using proven techniques to create conditions where your best thinking emerges. + +**Good for:** + +- Breaking through creative blocks +- Generating product or feature ideas +- Exploring problems from new angles +- Developing raw concepts into action plans + +## How It Works + +1. **Setup** - Define topic, goals, constraints +2. **Choose approach** - Pick techniques yourself, get AI recommendations, go random, or follow a progressive flow +3. **Facilitation** - Work through techniques with probing questions and collaborative coaching +4. **Organize** - Ideas grouped into themes and prioritized +5. **Action** - Top ideas get next steps and success metrics + +Everything gets captured in a session document you can reference later or share with stakeholders. + +:::note[Your Ideas] +Every idea comes from you. The workflow creates conditions for insight - you're the source. +::: diff --git a/docs/explanation/features/advanced-elicitation.md b/docs/explanation/features/advanced-elicitation.md deleted file mode 100644 index 0185977f..00000000 --- a/docs/explanation/features/advanced-elicitation.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: "Advanced Elicitation" ---- - -Push the LLM to rethink its work through 50+ reasoning methods — essentially, LLM brainstorming. - -Advanced Elicitation is the inverse of Brainstorming. Instead of pulling ideas out of you, the LLM applies sophisticated reasoning techniques to re-examine and enhance content it has just generated. It's the LLM brainstorming with itself to find better approaches, uncover hidden issues, and discover improvements it missed on the first pass. - -## When to Use It - -- After a workflow generates a section of content and you want to explore alternatives -- When the LLM's initial output seems adequate but you suspect there's more depth available -- For high-stakes content where multiple perspectives would strengthen the result -- To stress-test assumptions, explore edge cases, or find weaknesses in generated plans -- When you want the LLM to "think again" but with structured reasoning methods - -## How It Works - -### 1. Context Analysis -The LLM analyzes the current content, understanding its type, complexity, stakeholder needs, risk level, and creative potential. - -### 2. Smart Method Selection -Based on context, 5 methods are intelligently selected from a library of 50+ techniques and presented to you: - -| Option | Description | -| ----------------- | ---------------------------------------- | -| **1-5** | Apply the selected method to the content | -| **[r] Reshuffle** | Get 5 new methods selected randomly | -| **[a] List All** | Browse the complete method library | -| **[x] Proceed** | Continue with enhanced content | - -### 3. Method Execution & Iteration -- The selected method is applied to the current content -- Improvements are shown for your review -- You choose whether to apply changes or discard them -- The menu re-appears for additional elicitations -- Each method builds on previous enhancements - -### 4. Party Mode Integration (Optional) -If Party Mode is active, BMad agents participate randomly in the elicitation process, adding their unique perspectives to the methods. - -## Method Categories - -| Category | Focus | Example Methods | -| ----------------- | ----------------------------------- | -------------------------------------------------------------- | -| **Core** | Foundational reasoning techniques | First Principles Analysis, 5 Whys, Socratic Questioning | -| **Collaboration** | Multiple perspectives and synthesis | Stakeholder Round Table, Expert Panel Review, Debate Club | -| **Advanced** | Complex reasoning frameworks | Tree of Thoughts, Graph of Thoughts, Self-Consistency | -| **Competitive** | Adversarial stress-testing | Red Team vs Blue Team, Shark Tank Pitch, Code Review Gauntlet | -| **Technical** | Architecture and code quality | Decision Records, Rubber Duck Debugging, Algorithm Olympics | -| **Creative** | Innovation and lateral thinking | SCAMPER, Reverse Engineering, Random Input Stimulus | -| **Research** | Evidence-based analysis | Literature Review Personas, Thesis Defense, Comparative Matrix | -| **Risk** | Risk identification and mitigation | Pre-mortem Analysis, Failure Mode Analysis, Chaos Monkey | -| **Learning** | Understanding verification | Feynman Technique, Active Recall Testing | -| **Philosophical** | Conceptual clarity | Occam's Razor, Ethical Dilemmas | -| **Retrospective** | Reflection and lessons | Hindsight Reflection, Lessons Learned Extraction | - -## Key Features - -- **50+ reasoning methods** — Spanning core logic to advanced multi-step reasoning frameworks -- **Smart context selection** — Methods chosen based on content type, complexity, and stakeholder needs -- **Iterative enhancement** — Each method builds on previous improvements -- **User control** — Accept or discard each enhancement before proceeding -- **Party Mode integration** — Agents can participate when Party Mode is active - -## Workflow Integration - -Advanced Elicitation is a core workflow designed to be invoked by other workflows during content generation: - -| Parameter | Description | -| ---------------------- | --------------------------------------------------------- | -| **Content to enhance** | The current section content that was just generated | -| **Context type** | The kind of content being created (spec, code, doc, etc.) | -| **Enhancement goals** | What the calling workflow wants to improve | - -### Integration Flow - -When called from a workflow: -1. Receives the current section content that was just generated -2. Applies elicitation methods iteratively to enhance that content -3. Returns the enhanced version when user selects 'x' to proceed -4. The enhanced content replaces the original section in the output document - -### Example - -A specification generation workflow could invoke Advanced Elicitation after producing each major section (requirements, architecture, implementation plan). The workflow would pass the generated section, and Advanced Elicitation would offer methods like "Stakeholder Round Table" to gather diverse perspectives on requirements, or "Red Team vs Blue Team" to stress-test the architecture for vulnerabilities. - -## Advanced Elicitation vs. Brainstorming - -| | **Advanced Elicitation** | **Brainstorming** | -| ------------ | ------------------------------------------------- | --------------------------------------------- | -| **Source** | LLM generates ideas through structured reasoning | User provides ideas, AI coaches them out | -| **Purpose** | Rethink and improve LLM's own output | Unlock user's creativity | -| **Methods** | 50+ reasoning and analysis techniques | 60+ ideation and creativity techniques | -| **Best for** | Enhancing generated content, finding alternatives | Breaking through blocks, generating new ideas | diff --git a/docs/explanation/features/brainstorming-techniques.md b/docs/explanation/features/brainstorming-techniques.md deleted file mode 100644 index 704ddb3a..00000000 --- a/docs/explanation/features/brainstorming-techniques.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: "Brainstorming" ---- - -Facilitate structured creative sessions using 60+ proven ideation techniques. - -The Brainstorming workflow is an interactive facilitation system that helps you unlock your own creativity. The AI acts as coach, guide, and creative partner — using proven techniques to draw out ideas and insights that are already within you. - -:::note[Important] -Every idea comes from you. The workflow creates the conditions for your best thinking to emerge through guided exploration, but you are the source. -::: - -## When to Use It - -- Breaking through creative blocks on a specific challenge -- Generating innovative ideas for products, features, or solutions -- Exploring a problem from completely new angles -- Systematically developing ideas from raw concepts to actionable plans -- Team ideation (with collaborative techniques) or personal creative exploration - -## How It Works - -### 1. Session Setup -Define your topic, goals, and any constraints. - -### 2. Choose Your Approach - -| Approach | Description | -|----------|-------------| -| **User-Selected** | Browse the full technique library and pick what appeals to you | -| **AI-Recommended** | Get customized technique suggestions based on your goals | -| **Random Selection** | Discover unexpected methods through serendipitous technique combinations | -| **Progressive Flow** | Journey systematically from expansive exploration to focused action planning | - -### 3. Interactive Facilitation -Work through techniques with true collaborative coaching. The AI asks probing questions, builds on your ideas, and helps you think deeper—but your ideas are the source. - -### 4. Idea Organization -All your generated ideas are organized into themes and prioritized. - -### 5. Action Planning -Top ideas get concrete next steps, resource requirements, and success metrics. - -## What You Get - -A comprehensive session document that captures the entire journey: - -- Topic, goals, and session parameters -- Each technique used and how it was applied -- Your contributions and the ideas you generated -- Thematic organization connecting related insights -- Prioritized ideas with action plans -- Session highlights and key breakthroughs - -This document becomes a permanent record of your creative process — valuable for future reference, sharing with stakeholders, or continuing the session later. - -## Technique Categories - -| Category | Focus | -|----------|-------| -| **Collaborative** | Team dynamics and inclusive participation | -| **Creative** | Breakthrough thinking and paradigm shifts | -| **Deep** | Root cause analysis and strategic insight | -| **Structured** | Organized frameworks and systematic exploration | -| **Theatrical** | Playful, radical perspectives | -| **Wild** | Boundary-pushing, extreme thinking | -| **Biomimetic** | Nature-inspired solutions | -| **Quantum** | Quantum principles for innovation | -| **Cultural** | Traditional knowledge and cross-cultural approaches | -| **Introspective Delight** | Inner wisdom and authentic exploration | - -## Key Features - -- **Interactive coaching** — Pulls ideas *out* of you, doesn't generate them for you -- **On-demand loading** — Techniques loaded from a comprehensive library as needed -- **Session preservation** — Every step, insight, and action plan is documented -- **Continuation support** — Pause sessions and return later, or extend with additional techniques - -## Workflow Integration - -Brainstorming is a core workflow designed to be invoked and configured by other modules. When called from another workflow, it accepts contextual parameters: - -| Parameter | Description | -|-----------|-------------| -| **Topic focus** | What the brainstorming should help discover or solve | -| **Guardrails** | Constraints, boundaries, or must-avoid areas | -| **Output goals** | What the final output needs to accomplish for the calling workflow | -| **Context files** | Project-specific guidance to inform technique selection | - -### Example - -When creating a new module in the BMad Builder workflow, Brainstorming can be invoked with guardrails around the module's purpose and a goal to discover key features, user needs, or architectural considerations. The session becomes focused on producing exactly what the module creation workflow needs. diff --git a/docs/explanation/features/party-mode.md b/docs/explanation/features/party-mode.md deleted file mode 100644 index 1ff6d5cf..00000000 --- a/docs/explanation/features/party-mode.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: "Party Mode: Multi-Agent Collaboration" ---- - -Get all your AI agents in one conversation. - -## What is Party Mode? - -Ever wanted to gather your entire AI team in one room and see what happens? That's party mode. - -Type `/bmad:core:workflows:party-mode` (or `*party-mode` from any agent or at key workflow junctions when asked), and suddenly you've got **all your AI agents** in one conversation. PM, Architect, DEV, UX Designer and more that you can choose from. - -**Why it's useful:** - -- **After complex workflows** - Debrief with the whole team about what worked, what didn't -- **Big decisions with tradeoffs** - Get technical, creative, and strategic perspectives simultaneously -- **Brainstorming sessions** - Watch ideas evolve through cross-pollination -- **When things go wrong** - Call out failures, watch agents defend their decisions, let them debate whose fault it was (oddly therapeutic) -- **Sprint retrospectives** - Party mode powers the retrospective workflow -- **Sprint planning** - Multi-agent collaboration for planning sessions - -## How It Works - -**The basics:** - -1. Party mode reads `_bmad/_config/agent-manifest.csv` -2. Loads ALL installed agents (already includes your customizations from install) -3. BMad Master orchestrates - picks 2-3 relevant agents per message based on topic -4. Agents respond in character, can agree/disagree/build on each other's ideas -5. Discussion continues until you type "exit" or reach natural conclusion - -**That's it.** No complex merging, no runtime magic. Just agents talking. - -## Quick Start - -```bash -/bmad:core:workflows:party-mode - -*party-mode - - -/bmad:core:workflows:party-mode and include also in the party Santa Clause and Einstein - -Ask questions, respond to agents, direct the conversation - -Type: exit -``` - -## Fun Examples - -### Example 1: Calling Out Bad Architecture - -**You:** "Okay team, our last sprint was a disaster. The authentication service went down three times. Who screwed this up?" - -**Architect (Winston):** "The design was sound - distributed authentication with proper fallbacks. If implementation deviated from the spec..." - -**DEV (Amelia):** "I followed the architecture doc exactly. The spec didn't account for concurrent session invalidation race conditions." - -**PM (John):** "Both of you missed the bigger issue - we didn't validate session management requirements in the PRD. This is on me for not catching it." - -**TEA (Murat):** "And I should've caught this in integration tests. The test scenarios didn't cover concurrent invalidation." - -_(Watch them debate whose fault it really was - it's therapeutic)_ - -### Example 2: Creative Brainstorming - -**You:** "How do we make onboarding feel magical instead of boring?" - -**UX Designer (Sally):** "Start with progressive disclosure - reveal features as users need them, not all at once in a tutorial." - -**Storyteller (CIS):** "What if onboarding was a story? Each step reveals a character's journey - the user IS the hero." - -**Innovation Strategist (CIS):** "Take it further - gamify with unlockable achievements. But make them meaningful, not arbitrary badges." - -**Game Designer:** "Building on that - what if the first 'quest' is actually solving a real user problem? They learn by doing something valuable." - -_(Ideas cross-pollinate and evolve)_ - -### Example 3: Technical Decision - -**You:** "Monolith or microservices for MVP?" - -**Architect:** "Start monolith. Microservices add complexity you don't need at 1000 users." - -**PM:** "Agree. Time to market matters more than theoretical scalability." - -**DEV:** "Monolith with clear module boundaries. We can extract services later if needed." - -**Innovation Strategist:** "Contrarian take - if your differentiator IS scalability, build for it now. Otherwise Architect's right." - -_(Multiple perspectives reveal the right answer)_ - -:::tip[Better Decisions] -Better decisions through diverse perspectives. Welcome to party mode. -::: diff --git a/docs/explanation/features/quick-flow.md b/docs/explanation/features/quick-flow.md deleted file mode 100644 index 29857ef2..00000000 --- a/docs/explanation/features/quick-flow.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: "Quick Spec Flow" -description: Understanding Quick Spec Flow for rapid development in BMad Method ---- - -Quick Spec Flow is a streamlined alternative to the full BMad Method for Quick Flow track projects. Instead of going through Product Brief → PRD → Architecture, you go straight to a context-aware technical specification and start coding. - -- **Perfect for:** Bug fixes, small features, rapid prototyping, and quick enhancements -- **Time to implementation:** Minutes, not hours - -## When to Use Quick Flow - -### Use Quick Flow when: - -- Single bug fix or small enhancement -- Small feature with clear scope (typically 1-15 stories) -- Rapid prototyping or experimentation -- Adding to existing brownfield codebase -- You know exactly what you want to build - -### Use BMad Method or Enterprise when: - -- Building new products or major features -- Need stakeholder alignment -- Complex multi-team coordination -- Requires extensive planning and architecture - -:::tip[Not Sure?] -Run `workflow-init` to get a recommendation based on your project's needs. -::: - -## Quick Flow Overview - -```mermaid -flowchart TD - START[Step 1: Run Tech-Spec Workflow] - DETECT[Detects project stack] - ANALYZE[Analyzes brownfield codebase] - TEST[Detects test frameworks] - CONFIRM[Confirms conventions] - GENERATE[Generates context-rich tech-spec] - STORIES[Creates ready-to-implement stories] - IMPL[Step 2: Implement with DEV Agent] - DONE[DONE!] - - START --> DETECT - DETECT --> ANALYZE - ANALYZE --> TEST - TEST --> CONFIRM - CONFIRM --> GENERATE - GENERATE --> STORIES - STORIES --> IMPL - IMPL --> DONE - - style START fill:#bfb,stroke:#333,stroke-width:2px - style IMPL fill:#bbf,stroke:#333,stroke-width:2px - style DONE fill:#f9f,stroke:#333,stroke-width:3px -``` - -## What Makes It Quick - -- No Product Brief needed -- No PRD needed -- No Architecture doc needed -- Auto-detects your stack -- Auto-analyzes brownfield code -- Auto-validates quality -- Story context optional (tech-spec is comprehensive) - -## Smart Context Discovery - -Quick Spec Flow automatically discovers and uses: - -### Existing Documentation -- Product briefs (if they exist) -- Research documents -- `document-project` output (brownfield codebase map) - -### Project Stack -- **Node.js:** package.json → frameworks, dependencies, scripts -- **Python:** requirements.txt, pyproject.toml → packages, tools -- **Ruby:** Gemfile → gems and versions -- **Java:** pom.xml, build.gradle → Maven/Gradle dependencies -- **Go:** go.mod → modules -- **Rust:** Cargo.toml → crates - -### Brownfield Code Patterns -- Directory structure and organization -- Existing code patterns (class-based, functional, MVC) -- Naming conventions -- Test frameworks and patterns -- Code style configurations - -### Convention Confirmation - -Quick Spec Flow detects your conventions and **asks for confirmation**: - -``` -I've detected these conventions in your codebase: - -Code Style: -- ESLint with Airbnb config -- Prettier with single quotes - -Test Patterns: -- Jest test framework -- .test.js file naming - -Should I follow these existing conventions? (yes/no) -``` - -**You decide:** Conform to existing patterns or establish new standards! - -## Auto-Validation - -Quick Spec Flow **automatically validates** everything: - -- Context gathering completeness -- Definitiveness (no "use X or Y" statements) -- Brownfield integration quality -- Stack alignment -- Implementation readiness - -## Comparison: Quick Flow vs Full BMM - -| Aspect | Quick Flow Track | BMad Method/Enterprise Tracks | -| --------------------- | ---------------------------- | ---------------------------------- | -| **Setup** | None (standalone) | workflow-init recommended | -| **Planning Docs** | tech-spec.md only | Product Brief → PRD → Architecture | -| **Time to Code** | Minutes | Hours to days | -| **Best For** | Bug fixes, small features | New products, major features | -| **Context Discovery** | Automatic | Manual + guided | -| **Validation** | Auto-validates everything | Manual validation steps | -| **Brownfield** | Auto-analyzes and conforms | Manual documentation required | - -## When to Graduate to BMad Method - -Start with Quick Flow, but switch to BMad Method when: - -- Project grows beyond initial scope -- Multiple teams need coordination -- Stakeholders need formal documentation -- Product vision is unclear -- Architectural decisions need deep analysis -- Compliance/regulatory requirements exist - -:::tip[Transition Tip] -You can always run `workflow-init` later to transition from Quick Flow to BMad Method. -::: diff --git a/docs/explanation/party-mode.md b/docs/explanation/party-mode.md new file mode 100644 index 00000000..5e9dbaa9 --- /dev/null +++ b/docs/explanation/party-mode.md @@ -0,0 +1,57 @@ +--- +title: "Party Mode" +description: Multi-agent collaboration - get all your AI agents in one conversation +--- + +Get all your AI agents in one conversation. + +## What is Party Mode? + +Run `party-mode` and you've got your whole AI team in one room - PM, Architect, Dev, UX Designer, whoever you need. BMad Master orchestrates, picking relevant agents per message. Agents respond in character, agree, disagree, and build on each other's ideas. + +The conversation continues as long as you want. Ask follow-ups, push back on answers, redirect the discussion - it's a real back-and-forth with your agents until you're done. + +**Good for:** + +- Big decisions with tradeoffs +- Brainstorming sessions +- Post-mortems when things go wrong +- Sprint retrospectives and planning + +## Examples + +### Calling Out Bad Architecture + +**You:** "Okay team, our last sprint was a disaster. The authentication service went down three times. Who screwed this up?" + +**Architect:** "The design was sound - distributed authentication with proper fallbacks. If implementation deviated from the spec..." + +**Dev:** "I followed the architecture doc exactly. The spec didn't account for concurrent session invalidation race conditions." + +**PM:** "Both of you missed the bigger issue - we didn't validate session management requirements in the PRD. This is on me for not catching it." + +**TEA:** "And I should've caught this in integration tests. The test scenarios didn't cover concurrent invalidation." + +### Creative Brainstorming + +**You:** "How do we make onboarding feel magical instead of boring?" + +**UX Designer:** "Start with progressive disclosure - reveal features as users need them, not all at once in a tutorial." + +**Storyteller:** "What if onboarding was a story? Each step reveals a character's journey - the user IS the hero." + +**Game Designer:** "Building on that - what if the first 'quest' is actually solving a real user problem? They learn by doing something valuable." + +### Technical Decision + +**You:** "Monolith or microservices for MVP?" + +**Architect:** "Start monolith. Microservices add complexity you don't need at 1000 users." + +**PM:** "Agree. Time to market matters more than theoretical scalability." + +**Dev:** "Monolith with clear module boundaries. We can extract services later if needed." + +:::tip[Better Decisions] +Better decisions through diverse perspectives. Welcome to party mode. +::: diff --git a/docs/explanation/quick-flow.md b/docs/explanation/quick-flow.md new file mode 100644 index 00000000..9b6e4ea7 --- /dev/null +++ b/docs/explanation/quick-flow.md @@ -0,0 +1,27 @@ +--- +title: "Quick Flow" +description: Fast-track for small changes - skip the full methodology +--- + +Quick Flow is for when you don't need the full BMad Method. Skip Product Brief, PRD, and Architecture - go straight to implementation. + +## How It Works + +1. **Run `quick-spec`** — generates a focused tech-spec +2. **Run `quick-dev`** — implements it + +That's it. + +## When to Use It + +- Bug fixes +- Refactoring +- Small features +- Prototyping + +## When to Use Full BMad Method Instead + +- New products +- Major features +- Multiple teams involved +- Stakeholder alignment needed diff --git a/docs/how-to/customization/index.md b/docs/how-to/customization/index.md deleted file mode 100644 index a792104c..00000000 --- a/docs/how-to/customization/index.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "BMad Customization" ---- - -Personalize agents and workflows to match your needs. - -## Guides - -| Guide | Description | -|-------|-------------| -| **[Agent Customization](/docs/how-to/customization/customize-agents.md)** | Modify agent behavior without editing core files | - -## Overview - -BMad provides two main customization approaches: - -### Agent Customization - -Modify any agent's persona, name, capabilities, or menu items using `.customize.yaml` files in `_bmad/_config/agents/`. Your customizations persist through updates. - -### Workflow Customization - -Replace or extend workflow steps to create tailored processes. (Coming soon) diff --git a/docs/how-to/install-bmad.md b/docs/how-to/install-bmad.md new file mode 100644 index 00000000..126f029f --- /dev/null +++ b/docs/how-to/install-bmad.md @@ -0,0 +1,89 @@ +--- +title: "How to Install BMad" +description: Step-by-step guide to installing BMad in your project +--- + +Use the `npx bmad-method install` command to set up BMad in your project with your choice of modules and AI tools. + +## When to Use This + +- Starting a new project with BMad +- Adding BMad to an existing codebase +- Update the existing BMad Installation + +:::note[Prerequisites] +- **Node.js** 20+ (required for the installer) +- **Git** (recommended) +- **AI tool** (Claude Code, Cursor, Windsurf, or similar) +::: + +## Steps + +### 1. Run the Installer + +```bash +npx bmad-method@alpha install +``` + +### 2. Choose Installation Location + +The installer will ask where to install BMad files: + +- Current directory (recommended for new projects if you created the directory yourself and ran from within the directory) +- Custom path + +### 3. Select Your AI Tools + +Pick which AI tools you use: + +- Claude Code +- Cursor +- Windsurf +- Others + +Each tool has its own way of integrating commands. The installer creates tiny prompt files to activate workflows and agents — it just puts them where your tool expects to find them. + +### 4. Choose Modules + +The installer shows available modules. Select whichever ones you need — most users just want **BMad Method** (the software development module). + +### 5. Follow the Prompts + +The installer guides you through the rest — custom content, settings, etc. + +## What You Get + +``` +your-project/ +├── _bmad/ +│ ├── bmm/ # Your selected modules +│ │ └── config.yaml # Module settings (if you ever need to change them) +│ ├── core/ # Required core module +│ └── ... +├── _bmad-output/ # Generated artifacts +└── .claude/ # Claude Code commands (if using Claude Code) +``` + +## Verify Installation + +Run the `help` workflow (`/bmad-help` on most platforms) to verify everything works and see what to do next. + +## Living on the Edge + +**Latest pre-release (alpha/beta):** +```bash +npx bmad-method@alpha install +``` + +**Latest from main branch:** +```bash +npx github:bmad-code-org/BMAD-METHOD install +``` + +Use these if you want the newest features before they're officially released. Things might break. + +## Troubleshooting + +**Installer throws an error** — Copy-paste the output into your AI assistant and let it figure it out. + +**Installer worked but something doesn't work later** — Your AI needs BMad context to help. See [How to Get Answers About BMad](/docs/how-to/get-answers-about-bmad.md) for how to point your AI at the right sources. diff --git a/docs/how-to/installation/index.md b/docs/how-to/installation/index.md deleted file mode 100644 index e3a3e29d..00000000 --- a/docs/how-to/installation/index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "Installation Guides" -description: How to install and upgrade BMad Method ---- - -How-to guides for installing and configuring the BMad Method. - -| Guide | Description | -|-------|-------------| -| [Install BMad](/docs/how-to/installation/install-bmad.md) | Step-by-step installation instructions | -| [Install Custom Modules](/docs/how-to/installation/install-custom-modules.md) | Add custom agents, workflows, and modules | -| [Upgrade to v6](/docs/how-to/installation/upgrade-to-v6.md) | Migrate from BMad v4 to v6 | diff --git a/docs/how-to/installation/install-bmad.md b/docs/how-to/installation/install-bmad.md deleted file mode 100644 index 5d1ae33a..00000000 --- a/docs/how-to/installation/install-bmad.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: "How to Install BMad" -description: Step-by-step guide to installing BMad in your project ---- - -Use the `npx bmad-method install` command to set up BMad in your project with your choice of modules and AI tools. - -## When to Use This - -- Starting a new project with BMad -- Adding BMad to an existing codebase -- Update the existing BMad Installation - -:::note[Prerequisites] -- **Node.js** 20+ (required for the installer) -- **Git** (recommended) -- **AI-powered IDE** (Claude Code, Cursor, Windsurf, or similar) -::: - -## Steps - -### 1. Run the Installer - -```bash -npx bmad-method install -``` - -### 2. Choose Installation Location - -The installer will ask where to install BMad files: - -- Current directory (recommended for new projects if you created the directory yourself and ran from within the directory) -- Custom path - -### 3. Select Your AI Tools - -Choose which AI tools you'll be using: - -- Claude Code -- Cursor -- Windsurf -- Many others to choose from - -The installer configures BMad for your selected tools by setting up commands that will call the ui. - -### 4. Choose Modules - -Select which modules to install: - -| Module | Purpose | -| -------- | ----------------------------------------- | -| **BMM** | Core methodology for software development | -| **BMGD** | Game development workflows | -| **CIS** | Creative intelligence and facilitation | -| **BMB** | Building custom agents and workflows | - -### 5. Add Custom Content (Optional) - -If you have custom agents, workflows, or modules, point to their location and the installer will integrate them. - -### 6. Configure Settings - -For each module, either accept recommended defaults (faster) or customize settings (more control). - -## What You Get - -``` -your-project/ -├── _bmad/ -│ ├── bmm/ # Method module -│ │ ├── agents/ # Agent files -│ │ ├── workflows/ # Workflow files -│ │ └── config.yaml # Module config -│ ├── core/ # Core utilities -│ └── ... -├── _bmad-output/ # Generated artifacts -└── .claude/ # IDE configuration -``` - -## Verify Installation - -1. Check the `_bmad/` directory exists -2. Load an agent in your AI tool -3. Run `/workflow-init` which will autocomplete to the full command to see available commands - -## Configuration - -Edit `_bmad/[module]/config.yaml` to customize. For example these could be changed: - -```yaml -output_folder: ./_bmad-output -user_name: Your Name -communication_language: english -``` - -## Troubleshooting - -**"Command not found: npx"** — Install Node.js 20+: -```bash -brew install node -``` - -**"Permission denied"** — Check npm permissions: -```bash -npm config set prefix ~/.npm-global -``` - -**Installer hangs** — Try running with verbose output: -```bash -npx bmad-method install --verbose -``` diff --git a/docs/index.md b/docs/index.md index f0a0ae46..3dcf5cd0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,16 +10,10 @@ If you're comfortable working with AI coding assistants like Claude, Cursor, or ## New Here? Start with a Tutorial -The fastest way to understand BMad is to try it. Choose a tutorial to walk through your first project in about 10 minutes. +The fastest way to understand BMad is to try it. -- **[Get Started with BMad](/docs/tutorials/getting-started/getting-started-bmadv6.md)** — Latest features, still in active development -- **[Workflow Guide](/workflow-guide)** — A simple visual overview of the various BMad tracks that get you going quickly. - -:::tip[Already familiar with AI-assisted development?] -Feel free to skip around. Use the sidebar to jump to any topic, or check out [What Are Agents?](/docs/explanation/core-concepts/what-are-agents.md) to understand how BMad organizes its AI personas. -::: - ---- +- **[Get Started with BMad](/docs/tutorials/getting-started.md)** — Install and understand how BMad works +- **[Workflow Map](/docs/reference/workflow-map.md)** — Visual overview of BMM phases, workflows, and context management. ## How to Use These Docs @@ -59,6 +53,4 @@ Get help, share what you're building, or contribute to BMad: ## Next Step -Ready to dive in? Pick a tutorial and start building. - -- **[Get Started with BMad](/docs/tutorials/getting-started/getting-started-bmadv6.md)** — Explore the latest features +Ready to dive in? **[Get Started with BMad](/docs/tutorials/getting-started.md)** and build your first project. diff --git a/docs/reference/workflow-map.md b/docs/reference/workflow-map.md new file mode 100644 index 00000000..d32d8a32 --- /dev/null +++ b/docs/reference/workflow-map.md @@ -0,0 +1,74 @@ +--- +title: "Workflow Map" +description: Visual reference for BMad Method workflow phases and outputs +--- + +BMAD is a context management system. AI agents work best with clear, structured context. The BMM workflow builds that context progressively - each phase produces documents that inform the next, so agents always know what to build and why. + +![BMad Method Workflow Map](/img/workflow-map.png) + +## Phase 1: Analysis (Optional) + +Explore the problem space and validate ideas before committing to planning. + +| Workflow | Purpose | Produces | +|----------|---------|----------| +| `research` | Validate market, technical, or domain assumptions | Research findings | +| `create-product-brief` | Capture strategic vision | `product-brief.md` | + +## Phase 2: Planning + +Define what to build and for whom. + +| Workflow | Purpose | Produces | +|----------|---------|----------| +| `prd` | Define requirements (FRs/NFRs) | `PRD.md` | +| `create-ux-design` | Design user experience (when UX matters) | `ux-spec.md` | + +## Phase 3: Solutioning + +Decide how to build it and break work into stories. + +| Workflow | Purpose | Produces | +|----------|---------|----------| +| `create-architecture` | Make technical decisions explicit | `architecture.md` with ADRs | +| `create-epics-and-stories` | Break requirements into implementable work | Epic files with stories | +| `check-implementation-readiness` | Gate check before implementation | PASS/CONCERNS/FAIL decision | + +## Phase 4: Implementation + +Build it, one story at a time. + +| Workflow | Purpose | Produces | +|----------|---------|----------| +| `sprint-planning` | Initialize tracking (once per project) | `sprint-status.yaml` | +| `create-story` | Prepare next story for implementation | `story-[slug].md` | +| `dev-story` | Implement the story | Working code + tests | +| `code-review` | Validate implementation quality | Approved or changes requested | +| `correct-course` | Handle significant mid-sprint changes | Updated plan or re-routing | +| `retrospective` | Review after epic completion | Lessons learned | + +## Quick Flow (Parallel Track) + +Skip phases 1-3 for small, well-understood work. + +| Workflow | Purpose | Produces | +|----------|---------|----------| +| `quick-spec` | Define an ad-hoc change | `tech-spec.md` (story file for small changes) | +| `quick-dev` | Implement from spec or direct instructions | Working code + tests | + +## Context Management + +Each document becomes context for the next phase. The PRD tells the architect what constraints matter. The architecture tells the dev agent which patterns to follow. Story files give focused, complete context for implementation. Without this structure, agents make inconsistent decisions. + +For brownfield projects, `document-project` creates or updates `project-context.md` - what exists in the codebase and the rules all implementation workflows must observe. Run it just before Phase 4, and again when something significant changes - structure, architecture, or those rules. You can also edit `project-context.md` by hand. + +All implementation workflows load `project-context.md` if it exists. Additional context per workflow: + +| Workflow | Also Loads | +|----------|------------| +| `create-story` | epics, PRD, architecture, UX | +| `dev-story` | story file | +| `code-review` | architecture, story file | +| `quick-spec` | planning docs (if exist) | +| `quick-dev` | tech-spec | diff --git a/docs/explanation/tea/engagement-models.md b/docs/tea/explanation/engagement-models.md similarity index 75% rename from docs/explanation/tea/engagement-models.md rename to docs/tea/explanation/engagement-models.md index 17dfd543..856883d1 100644 --- a/docs/explanation/tea/engagement-models.md +++ b/docs/tea/explanation/engagement-models.md @@ -13,7 +13,7 @@ TEA is optional and flexible. There are five valid ways to engage with TEA - cho 1. **No TEA** - Skip all TEA workflows, use existing testing approach 2. **TEA Solo** - Use TEA standalone without BMad Method -3. **TEA Lite** - Beginner approach using just `*automate` +3. **TEA Lite** - Beginner approach using just `automate` 4. **TEA Integrated (Greenfield)** - Full BMad Method integration from scratch 5. **TEA Integrated (Brownfield)** - Full BMad Method integration with existing code @@ -84,10 +84,10 @@ Decision: Skip TEA, keep what works **Typical Sequence:** ``` -1. *test-design (system or epic) -2. *atdd or *automate -3. *test-review (optional) -4. *trace (coverage + gate decision) +1. `test-design` (system or epic) +2. `atdd` or `automate` +3. `test-review` (optional) +4. `trace` (coverage + gate decision) ``` **You Bring:** @@ -96,14 +96,14 @@ Decision: Skip TEA, keep what works - Project context **TEA Provides:** -- Risk-based test planning (`*test-design`) -- Test generation (`*atdd`, `*automate`) -- Quality review (`*test-review`) -- Coverage traceability (`*trace`) +- Risk-based test planning (`test-design`) +- Test generation (`atdd`, `automate`) +- Quality review (`test-review`) +- Coverage traceability (`trace`) **Optional:** -- Framework setup (`*framework`) if needed -- CI configuration (`*ci`) if needed +- Framework setup (`framework`) if needed +- CI configuration (`ci`) if needed **Example:** ``` @@ -114,10 +114,10 @@ Your project: Workflow: 1. Export stories from Jira -2. Run *test-design on epic -3. Run *atdd for each story +2. Run `test-design` on epic +3. Run `atdd` for each story 4. Implement features -5. Run *trace for coverage +5. Run `trace` for coverage ``` **Verdict:** Best for teams wanting TEA benefits without BMad Method commitment. @@ -126,7 +126,7 @@ Workflow: ### Model 3: TEA Lite -**What:** Beginner approach using just `*automate` to test existing features. +**What:** Beginner approach using just `automate` to test existing features. **When to Use:** - Learning TEA fundamentals @@ -136,9 +136,9 @@ Workflow: **Workflow:** ``` -1. *framework (setup test infrastructure) -2. *test-design (optional, risk assessment) -3. *automate (generate tests for existing features) +1. `framework` (setup test infrastructure) +2. `test-design` (optional, risk assessment) +3. `automate` (generate tests for existing features) 4. Run tests (they pass immediately) ``` @@ -150,8 +150,8 @@ Beginner developer: - 30 minutes available Steps: -1. Run *framework -2. Run *automate on TodoMVC demo +1. Run `framework` +2. Run `automate` on TodoMVC demo 3. Tests generated and passing 4. Learn TEA basics ``` @@ -186,29 +186,29 @@ Steps: **Phase 2: Planning** - PM creates PRD with NFRs -- (Optional) TEA runs `*nfr-assess` (Enterprise only) +- (Optional) TEA runs `nfr-assess` (Enterprise only) **Phase 3: Solutioning** - Architect creates architecture -- TEA runs `*test-design` (system-level) → testability review -- TEA runs `*framework` → test infrastructure -- TEA runs `*ci` → CI/CD pipeline -- Architect runs `*implementation-readiness` (fed by test design) +- TEA runs `test-design` (system-level) → testability review +- TEA runs `framework` → test infrastructure +- TEA runs `ci` → CI/CD pipeline +- Architect runs `implementation-readiness` (fed by test design) **Phase 4: Implementation (Per Epic)** -- SM runs `*sprint-planning` -- TEA runs `*test-design` (epic-level) → risk assessment for THIS epic +- SM runs `sprint-planning` +- TEA runs `test-design` (epic-level) → risk assessment for THIS epic - SM creates stories -- (Optional) TEA runs `*atdd` → failing tests before dev +- (Optional) TEA runs `atdd` → failing tests before dev - DEV implements story -- TEA runs `*automate` → expand coverage -- (Optional) TEA runs `*test-review` → quality audit -- TEA runs `*trace` Phase 1 → refresh coverage +- TEA runs `automate` → expand coverage +- (Optional) TEA runs `test-review` → quality audit +- TEA runs `trace` Phase 1 → refresh coverage **Release Gate:** -- (Optional) TEA runs `*test-review` → final audit -- (Optional) TEA runs `*nfr-assess` → validate NFRs -- TEA runs `*trace` Phase 2 → gate decision (PASS/CONCERNS/FAIL/WAIVED) +- (Optional) TEA runs `test-review` → final audit +- (Optional) TEA runs `nfr-assess` → validate NFRs +- TEA runs `trace` Phase 2 → gate decision (PASS/CONCERNS/FAIL/WAIVED) **What You Get:** - Complete quality operating model @@ -249,30 +249,30 @@ Workflow: **Phase 0: Documentation (if needed)** ``` -- Run *document-project +- Run `document-project` - Create baseline documentation ``` **Phase 2: Planning** ``` -- TEA runs *trace Phase 1 → establish coverage baseline +- TEA runs `trace` Phase 1 → establish coverage baseline - PM creates PRD (with existing system context) ``` **Phase 3: Solutioning** ``` - Architect creates architecture (with brownfield constraints) -- TEA runs *test-design (system-level) → testability review -- TEA runs *framework (only if modernizing test infra) -- TEA runs *ci (update existing CI or create new) +- TEA runs `test-design` (system-level) → testability review +- TEA runs `framework` (only if modernizing test infra) +- TEA runs `ci` (update existing CI or create new) ``` **Phase 4: Implementation** ``` -- TEA runs *test-design (epic-level) → focus on REGRESSION HOTSPOTS +- TEA runs `test-design` (epic-level) → focus on REGRESSION HOTSPOTS - Per story: ATDD → dev → automate -- TEA runs *test-review → improve legacy test quality -- TEA runs *trace Phase 1 → track coverage improvement +- TEA runs `test-review` → improve legacy test quality +- TEA runs `trace` Phase 1 → track coverage improvement ``` **Brownfield-Specific:** @@ -289,10 +289,10 @@ Legacy e-commerce platform: - Want to improve quality Workflow: -1. Phase 2: *trace baseline → 30% coverage -2. Phase 3: *test-design → identify regression risks +1. Phase 2: `trace` baseline → 30% coverage +2. Phase 3: `test-design` → identify regression risks 3. Phase 4: Fix top 20 flaky tests + add tests for new checkout -4. Gate: *trace → 60% coverage (2x improvement) +4. Gate: `trace` → 60% coverage (2x improvement) ``` **Verdict:** Best for incrementally improving legacy systems. @@ -309,7 +309,7 @@ flowchart TD Start([Choose TEA Model]) --> BMad{Using
BMad Method?} BMad -->|No| NonBMad{Project Type?} - NonBMad -->|Learning| Lite[TEA Lite
Just *automate
30 min tutorial] + NonBMad -->|Learning| Lite[TEA Lite
Just automate
30 min tutorial] NonBMad -->|Serious Project| Solo[TEA Solo
Standalone workflows
Full capabilities] BMad -->|Yes| WantTEA{Want TEA?} @@ -374,18 +374,18 @@ flowchart TD ``` Week 1: TEA Lite -- Run *framework -- Run *automate +- Run `framework` +- Run `automate` - Learn basics Week 2: Expand to TEA Solo -- Add *test-design -- Use *atdd for new features -- Add *test-review +- Add `test-design` +- Use `atdd` for new features +- Add `test-review` Week 3: Continue expanding -- Add *trace for coverage -- Setup *ci +- Add `trace` for coverage +- Setup `ci` - Full TEA Solo workflow ``` @@ -435,8 +435,8 @@ Testing: Manual only Decision: Start with TEA Lite Result: -- Run *framework (Playwright setup) -- Run *automate (20 tests generated) +- Run `framework` (Playwright setup) +- Run `automate` (20 tests generated) - Learning TEA basics ``` @@ -447,9 +447,9 @@ Testing: Automated tests exist Decision: Expand to TEA Solo Result: -- Add *test-design (risk assessment) -- Add *atdd (TDD workflow) -- Add *test-review (quality audits) +- Add `test-design` (risk assessment) +- Add `atdd` (TDD workflow) +- Add `test-review` (quality audits) ``` **Month 6:** TEA Integrated @@ -477,26 +477,26 @@ Result: **Phase 2:** ``` -- *trace baseline → 45% coverage (lots of gaps) +- `trace` baseline → 45% coverage (lots of gaps) - Document current state ``` **Phase 3:** ``` -- *test-design (system) → identify regression hotspots -- *framework → modernize test infrastructure -- *ci → add selective testing +- `test-design` (system) → identify regression hotspots +- `framework` → modernize test infrastructure +- `ci` → add selective testing ``` **Phase 4:** ``` Per epic: -- *test-design → focus on regression + new features +- `test-design` → focus on regression + new features - Fix top 10 flaky tests -- *atdd for new features -- *automate for coverage expansion -- *test-review → track quality improvement -- *trace → compare to baseline +- `atdd` for new features +- `automate` for coverage expansion +- `test-review` → track quality improvement +- `trace` → compare to baseline ``` **Result after 6 months:** @@ -520,9 +520,9 @@ Per epic: ``` Client project 1 (Scrum): - Import Jira stories -- Run *test-design -- Generate tests with *atdd/*automate -- Deliver quality report with *test-review +- Run `test-design` +- Generate tests with `atdd`/`automate` +- Deliver quality report with `test-review` Client project 2 (Kanban): - Import requirements from Notion @@ -581,11 +581,11 @@ Client project 3 (Ad-hoc): **When:** Outgrow beginner approach, need more workflows. **Steps:** -1. Continue using `*framework` and `*automate` -2. Add `*test-design` for planning -3. Add `*atdd` for TDD workflow -4. Add `*test-review` for quality audits -5. Add `*trace` for coverage tracking +1. Continue using `framework` and `automate` +2. Add `test-design` for planning +3. Add `atdd` for TDD workflow +4. Add `test-review` for quality audits +5. Add `trace` for coverage tracking **Timeline:** 2-4 weeks of gradual expansion @@ -620,13 +620,13 @@ Client project 3 (Ad-hoc): ``` Phase 1 (Week 1-2): TEA Lite -- Learn with *automate on demo app +- Learn with `automate` on demo app - Understand TEA fundamentals - Low commitment Phase 2 (Week 3-4): Evaluate -- Try *test-design (planning) -- Try *atdd (TDD) +- Try `test-design` (planning) +- Try `atdd` (TDD) - See if value justifies investment Phase 3 (Month 2+): Decide @@ -664,46 +664,46 @@ Non-critical features (UI tweaks): ## Technical Implementation Each model uses different TEA workflows. See: -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Model details -- [TEA Command Reference](/docs/reference/tea/commands.md) - Workflow reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - Setup options +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Model details +- [TEA Command Reference](/docs/tea/reference/commands.md) - Workflow reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - Setup options ## Related Concepts **Core TEA Concepts:** -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Risk assessment in different models -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Quality across all models -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - Consistent patterns across models +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Risk assessment in different models +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Quality across all models +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - Consistent patterns across models **Technical Patterns:** -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Infrastructure in different models -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Reliability in all models +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Infrastructure in different models +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Reliability in all models **Overview:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - 5 engagement models with cheat sheets -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - Design philosophy +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - 5 engagement models with cheat sheets +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - Design philosophy ## Practical Guides **Getting Started:** -- [TEA Lite Quickstart Tutorial](/docs/tutorials/getting-started/tea-lite-quickstart.md) - Model 3: TEA Lite +- [TEA Lite Quickstart Tutorial](/docs/tea/tutorials/tea-lite-quickstart.md) - Model 3: TEA Lite **Use-Case Guides:** -- [Using TEA with Existing Tests](/docs/how-to/brownfield/use-tea-with-existing-tests.md) - Model 5: Brownfield -- [Running TEA for Enterprise](/docs/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise integration +- [Using TEA with Existing Tests](/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md) - Model 5: Brownfield +- [Running TEA for Enterprise](/docs/tea/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise integration **All Workflow Guides:** -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Used in TEA Solo and Integrated -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Used in TEA Solo and Integrated +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - All workflows explained -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config per model -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - TEA Lite, TEA Solo, TEA Integrated terms +- [TEA Command Reference](/docs/tea/reference/commands.md) - All workflows explained +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config per model +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - TEA Lite, TEA Solo, TEA Integrated terms --- diff --git a/docs/explanation/tea/fixture-architecture.md b/docs/tea/explanation/fixture-architecture.md similarity index 88% rename from docs/explanation/tea/fixture-architecture.md rename to docs/tea/explanation/fixture-architecture.md index 6b58d64f..36d47c29 100644 --- a/docs/explanation/tea/fixture-architecture.md +++ b/docs/tea/explanation/fixture-architecture.md @@ -232,7 +232,7 @@ test('should update profile', async ({ apiRequest, authToken, log }) => { }); ``` -**Note:** This example uses the vanilla pure function signature (`url`, `data`). Playwright Utils uses different parameter names (`path`, `body`). See [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) for the utilities API. +**Note:** This example uses the vanilla pure function signature (`url`, `data`). Playwright Utils uses different parameter names (`path`, `body`). See [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) for the utilities API. **Note:** `authToken` requires auth-session fixture setup with provider configuration. See [auth-session documentation](https://seontechnologies.github.io/playwright-utils/auth-session.html). @@ -246,7 +246,7 @@ test('should update profile', async ({ apiRequest, authToken, log }) => { ### TEA Generates This Pattern -When you run `*framework` with `tea_use_playwright_utils: true`: +When you run `framework` with `tea_use_playwright_utils: true`: **TEA scaffolds:** ``` @@ -265,7 +265,7 @@ tests/ ### TEA Reviews Against This Pattern -When you run `*test-review`: +When you run `test-review`: **TEA checks:** - Are utilities pure functions? ✓ @@ -385,8 +385,8 @@ export const test = mergeTests(apiRequestTest, authSessionTest, logTest); ## Technical Implementation For detailed fixture architecture patterns, see the knowledge base: -- [Knowledge Base Index - Architecture & Fixtures](/docs/reference/tea/knowledge-base.md) -- [Complete Knowledge Base Index](/docs/reference/tea/knowledge-base.md) +- [Knowledge Base Index - Architecture & Fixtures](/docs/tea/reference/knowledge-base.md) +- [Complete Knowledge Base Index](/docs/tea/reference/knowledge-base.md) ## When to Use This Pattern @@ -425,32 +425,32 @@ function createTestUser(name: string) { ## Related Concepts **Core TEA Concepts:** -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Quality standards fixtures enforce -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - Fixture patterns in knowledge base +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Quality standards fixtures enforce +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - Fixture patterns in knowledge base **Technical Patterns:** -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Network fixtures explained -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Fixture complexity matches risk +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Network fixtures explained +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Fixture complexity matches risk **Overview:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Fixture architecture in workflows -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - Why fixtures matter +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Fixture architecture in workflows +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - Why fixtures matter ## Practical Guides **Setup Guides:** -- [How to Set Up Test Framework](/docs/how-to/workflows/setup-test-framework.md) - TEA scaffolds fixtures -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Production-ready fixtures +- [How to Set Up Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) - TEA scaffolds fixtures +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Production-ready fixtures **Workflow Guides:** -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Using fixtures in tests -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Fixture composition examples +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Using fixtures in tests +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Fixture composition examples ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - *framework command -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Fixture architecture fragments -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - Fixture architecture term +- [TEA Command Reference](/docs/tea/reference/commands.md) - `framework` command +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Fixture architecture fragments +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - Fixture architecture term --- diff --git a/docs/explanation/tea/knowledge-base-system.md b/docs/tea/explanation/knowledge-base-system.md similarity index 83% rename from docs/explanation/tea/knowledge-base-system.md rename to docs/tea/explanation/knowledge-base-system.md index 8264808e..4d5e69e3 100644 --- a/docs/explanation/tea/knowledge-base-system.md +++ b/docs/tea/explanation/knowledge-base-system.md @@ -91,7 +91,7 @@ fixture-architecture,Fixture Architecture,Composable fixture patterns,fixtures;a **2. Workflow Loads Relevant Fragments** -When user runs `*atdd`: +When user runs `atdd`: ``` TEA reads tea-index.csv Identifies fragments needed for ATDD: @@ -107,7 +107,7 @@ Generates tests following these patterns **3. Consistent Output** -Every time `*atdd` runs: +Every time `atdd` runs: - Same fragments loaded - Same patterns applied - Same quality standards @@ -120,7 +120,7 @@ Every time `*atdd` runs: ```mermaid %%{init: {'theme':'base', 'themeVariables': { 'fontSize':'14px'}}}%% flowchart TD - User([User: *atdd]) --> Workflow[TEA Workflow
Triggered] + User([User: atdd]) --> Workflow[TEA Workflow
Triggered] Workflow --> Read[Read Manifest
tea-index.csv] Read --> Identify{Identify Relevant
Fragments for ATDD} @@ -257,12 +257,12 @@ await page.waitForTimeout(3000); | Workflow | Fragments Loaded | Purpose | |----------|-----------------|---------| -| `*framework` | fixture-architecture, playwright-config, fixtures-composition | Infrastructure patterns | -| `*test-design` | test-quality, test-priorities-matrix, risk-governance | Planning standards | -| `*atdd` | test-quality, component-tdd, network-first, data-factories | TDD patterns | -| `*automate` | test-quality, test-levels-framework, selector-resilience | Comprehensive generation | -| `*test-review` | All quality/resilience/debugging fragments | Full audit patterns | -| `*ci` | ci-burn-in, burn-in, selective-testing | CI/CD optimization | +| `framework` | fixture-architecture, playwright-config, fixtures-composition | Infrastructure patterns | +| `test-design` | test-quality, test-priorities-matrix, risk-governance | Planning standards | +| `atdd` | test-quality, component-tdd, network-first, data-factories | TDD patterns | +| `automate` | test-quality, test-levels-framework, selector-resilience | Comprehensive generation | +| `test-review` | All quality/resilience/debugging fragments | Full audit patterns | +| `ci` | ci-burn-in, burn-in, selective-testing | CI/CD optimization | **Benefit:** Only load what's needed (focused context, no bloat). @@ -271,7 +271,7 @@ await page.waitForTimeout(3000); TEA doesn't load all 33 fragments at once: ``` -User runs: *atdd for authentication feature +User runs: atdd for authentication feature TEA analyzes context: - Feature type: Authentication @@ -298,7 +298,7 @@ Result: 5 relevant fragments loaded, 28 skipped **Without Knowledge Base (Vanilla Playwright, Random Quality):** ``` -Session 1: User runs *atdd +Session 1: User runs atdd AI: [Guesses patterns from general knowledge] Generated: @@ -309,7 +309,7 @@ test('api test', async ({ request }) => { // Random quality }); -Session 2: User runs *atdd (different day) +Session 2: User runs atdd (different day) AI: [Different random patterns] Generated: @@ -324,7 +324,7 @@ Result: Inconsistent quality, random patterns **With Knowledge Base (TEA + Playwright Utils):** ``` -Session 1: User runs *atdd +Session 1: User runs atdd TEA: [Loads test-quality.md, network-first.md, api-request.md from tea-index.csv] Generated: @@ -340,7 +340,7 @@ test('should fetch users', async ({ apiRequest }) => { expect(body).toBeInstanceOf(Array); }); -Session 2: User runs *atdd (different day) +Session 2: User runs atdd (different day) TEA: [Loads same fragments from tea-index.csv] Generated: Identical pattern, same quality @@ -356,10 +356,10 @@ Result: Systematic quality, established patterns (ALWAYS uses apiRequest utility **Without Knowledge Base:** ``` -*test-review session 1: +test-review session 1: "This test looks okay" [50 issues missed] -*test-review session 2: +test-review session 2: "This test has some issues" [Different issues flagged] Result: Inconsistent feedback @@ -367,11 +367,11 @@ Result: Inconsistent feedback **With Knowledge Base:** ``` -*test-review session 1: +test-review session 1: [Loads all quality fragments] Flags: 12 hard waits, 5 conditionals (based on test-quality.md) -*test-review session 2: +test-review session 2: [Loads same fragments] Flags: Same issues with same explanations @@ -430,12 +430,12 @@ Result: Consistent, reliable feedback ### 2. Onboarding **Before:** New team member reads 20 documents, asks 50 questions -**After:** New team member runs `*atdd`, sees patterns in generated code, learns by example +**After:** New team member runs `atdd`, sees patterns in generated code, learns by example ### 3. Quality Gates **Before:** "Is this test good?" → subjective opinion -**After:** "*test-review" → objective score against knowledge base +**After:** `test-review` → objective score against knowledge base ### 4. Pattern Evolution @@ -513,41 +513,41 @@ test('job completion', async ({ apiRequest, recurse }) => { ## Technical Implementation For details on the knowledge base index, see: -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) -- [TEA Configuration](/docs/reference/tea/configuration.md) +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) +- [TEA Configuration](/docs/tea/reference/configuration.md) ## Related Concepts **Core TEA Concepts:** -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Standards in knowledge base -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Risk patterns in knowledge base -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - Knowledge base across all models +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Standards in knowledge base +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Risk patterns in knowledge base +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - Knowledge base across all models **Technical Patterns:** -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Fixture patterns in knowledge base -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Network patterns in knowledge base +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Fixture patterns in knowledge base +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Network patterns in knowledge base **Overview:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Knowledge base in workflows -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Foundation: Context engineering philosophy** (why knowledge base solves AI test problems) +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Knowledge base in workflows +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Foundation: Context engineering philosophy** (why knowledge base solves AI test problems) ## Practical Guides **All Workflow Guides Use Knowledge Base:** -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) **Integration:** -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - PW-Utils in knowledge base +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - PW-Utils in knowledge base ## Reference -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Complete fragment index -- [TEA Command Reference](/docs/reference/tea/commands.md) - Which workflows load which fragments -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config affects fragment loading -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - Context engineering, knowledge fragment terms +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Complete fragment index +- [TEA Command Reference](/docs/tea/reference/commands.md) - Which workflows load which fragments +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config affects fragment loading +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - Context engineering, knowledge fragment terms --- diff --git a/docs/explanation/tea/network-first-patterns.md b/docs/tea/explanation/network-first-patterns.md similarity index 93% rename from docs/explanation/tea/network-first-patterns.md rename to docs/tea/explanation/network-first-patterns.md index 4be84dbb..8c1a460e 100644 --- a/docs/explanation/tea/network-first-patterns.md +++ b/docs/tea/explanation/network-first-patterns.md @@ -232,7 +232,7 @@ sequenceDiagram **Vanilla Playwright:** ```typescript -// When you run *atdd or *automate, TEA generates: +// When you run `atdd` or `automate`, TEA generates: test('should create user', async ({ page }) => { // TEA automatically includes network wait @@ -287,7 +287,7 @@ test('should create user', async ({ page, interceptNetworkCall }) => { ### TEA Reviews for Hard Waits -When you run `*test-review`: +When you run `test-review`: ```markdown ## Critical Issue: Hard Wait Detected @@ -807,46 +807,46 @@ test('test 2', async ({ page, interceptNetworkCall }) => { - Glob pattern matching (`**/api/**`) - Consistent API across all tests -See [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md#intercept-network-call) for setup. +See [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md#intercept-network-call) for setup. ## Technical Implementation For detailed network-first patterns, see the knowledge base: -- [Knowledge Base Index - Network & Reliability](/docs/reference/tea/knowledge-base.md) -- [Complete Knowledge Base Index](/docs/reference/tea/knowledge-base.md) +- [Knowledge Base Index - Network & Reliability](/docs/tea/reference/knowledge-base.md) +- [Complete Knowledge Base Index](/docs/tea/reference/knowledge-base.md) ## Related Concepts **Core TEA Concepts:** -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Determinism requires network-first -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - High-risk features need reliable tests +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Determinism requires network-first +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - High-risk features need reliable tests **Technical Patterns:** -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Network utilities as fixtures -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - Network patterns in knowledge base +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Network utilities as fixtures +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - Network patterns in knowledge base **Overview:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Network-first in workflows -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - Why flakiness matters +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Network-first in workflows +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - Why flakiness matters ## Practical Guides **Workflow Guides:** -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Review for hard waits -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Generate network-first tests -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Expand with network patterns +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Review for hard waits +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Generate network-first tests +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Expand with network patterns **Use-Case Guides:** -- [Using TEA with Existing Tests](/docs/how-to/brownfield/use-tea-with-existing-tests.md) - Fix flaky legacy tests +- [Using TEA with Existing Tests](/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md) - Fix flaky legacy tests **Customization:** -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Network utilities (recorder, interceptor, error monitor) +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Network utilities (recorder, interceptor, error monitor) ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - All workflows use network-first -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Network-first fragment -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - Network-first pattern term +- [TEA Command Reference](/docs/tea/reference/commands.md) - All workflows use network-first +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Network-first fragment +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - Network-first pattern term --- diff --git a/docs/explanation/tea/risk-based-testing.md b/docs/tea/explanation/risk-based-testing.md similarity index 91% rename from docs/explanation/tea/risk-based-testing.md rename to docs/tea/explanation/risk-based-testing.md index 5a386a03..5b45d8a7 100644 --- a/docs/explanation/tea/risk-based-testing.md +++ b/docs/tea/explanation/risk-based-testing.md @@ -250,7 +250,7 @@ Risk scores inform test priorities (but aren't the only factor): - **Test Levels:** E2E smoke test only - **Example:** Theme customization, experimental features -**Note:** Priorities consider risk scores plus business context (usage frequency, user impact, etc.). See [Test Priorities Matrix](/docs/reference/tea/knowledge-base.md#quality-standards) for complete criteria. +**Note:** Priorities consider risk scores plus business context (usage frequency, user impact, etc.). See [Test Priorities Matrix](/docs/tea/reference/knowledge-base.md#quality-standards) for complete criteria. ### 3. Mitigation Plans @@ -459,12 +459,12 @@ describe('User profile - High Value (P1)', () => { ## Technical Implementation For detailed risk governance patterns, see the knowledge base: -- [Knowledge Base Index - Risk & Gates](/docs/reference/tea/knowledge-base.md) -- [TEA Command Reference - *test-design](/docs/reference/tea/commands.md#test-design) +- [Knowledge Base Index - Risk & Gates](/docs/tea/reference/knowledge-base.md) +- [TEA Command Reference - `test-design`](/docs/tea/reference/commands.md#test-design) ### Risk Scoring Matrix -TEA uses this framework in `*test-design`: +TEA uses this framework in `test-design`: ``` Impact @@ -553,33 +553,33 @@ flowchart TD ## Related Concepts **Core TEA Concepts:** -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Quality complements risk assessment -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - When risk-based testing matters most -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - How risk patterns are loaded +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Quality complements risk assessment +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - When risk-based testing matters most +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - How risk patterns are loaded **Technical Patterns:** -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Building risk-appropriate test infrastructure -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Quality patterns for high-risk features +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Building risk-appropriate test infrastructure +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Quality patterns for high-risk features **Overview:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Risk assessment in TEA lifecycle -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - Design philosophy +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Risk assessment in TEA lifecycle +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - Design philosophy ## Practical Guides **Workflow Guides:** -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Apply risk scoring -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) - Gate decisions based on risk -- [How to Run NFR Assessment](/docs/how-to/workflows/run-nfr-assess.md) - NFR risk assessment +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Apply risk scoring +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) - Gate decisions based on risk +- [How to Run NFR Assessment](/docs/tea/how-to/workflows/run-nfr-assess.md) - NFR risk assessment **Use-Case Guides:** -- [Running TEA for Enterprise](/docs/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise risk management +- [Running TEA for Enterprise](/docs/tea/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise risk management ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - `*test-design`, `*nfr-assess`, `*trace` -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Risk governance fragments -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - Risk-based testing term +- [TEA Command Reference](/docs/tea/reference/commands.md) - `test-design`, `nfr-assess`, `trace` +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Risk governance fragments +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - Risk-based testing term --- diff --git a/docs/explanation/features/tea-overview.md b/docs/tea/explanation/tea-overview.md similarity index 53% rename from docs/explanation/features/tea-overview.md rename to docs/tea/explanation/tea-overview.md index 7ff65ab2..41c8e834 100644 --- a/docs/explanation/features/tea-overview.md +++ b/docs/tea/explanation/tea-overview.md @@ -7,7 +7,7 @@ description: Understanding the Test Architect (TEA) agent and its role in BMad M The Test Architect (TEA) is a specialized agent focused on quality strategy, test automation, and release gates in BMad Method projects. :::tip[Design Philosophy] -TEA was built to solve AI-generated tests that rot in review. For the problem statement and design principles, see [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md). For setup, see [Setup Test Framework](/docs/how-to/workflows/setup-test-framework.md). +TEA was built to solve AI-generated tests that rot in review. For the problem statement and design principles, see [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md). For setup, see [Setup Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md). ::: ## Overview @@ -25,31 +25,31 @@ BMad does not mandate TEA. There are five valid ways to use it (or skip it). Pic 2. **TEA Solo (Standalone)** - Use TEA on a non-BMad project. Bring your own requirements, acceptance criteria, and environments. - - Typical sequence: `*test-design` (system or epic) -> `*atdd` and/or `*automate` -> optional `*test-review` -> `*trace` for coverage and gate decisions. - - Run `*framework` or `*ci` only if you want TEA to scaffold the harness or pipeline; they work best after you decide the stack/architecture. + - Typical sequence: `test-design` (system or epic) -> `atdd` and/or `automate` -> optional `test-review` -> `trace` for coverage and gate decisions. + - Run `framework` or `ci` only if you want TEA to scaffold the harness or pipeline; they work best after you decide the stack/architecture. **TEA Lite (Beginner Approach):** - - Simplest way to use TEA - just use `*automate` to test existing features. + - Simplest way to use TEA - just use `automate` to test existing features. - Perfect for learning TEA fundamentals in 30 minutes. - - See [TEA Lite Quickstart Tutorial](/docs/tutorials/getting-started/tea-lite-quickstart.md). + - See [TEA Lite Quickstart Tutorial](/docs/tea/tutorials/tea-lite-quickstart.md). 3. **Integrated: Greenfield - BMad Method (Simple/Standard Work)** - - Phase 3: system-level `*test-design`, then `*framework` and `*ci`. - - Phase 4: per-epic `*test-design`, optional `*atdd`, then `*automate` and optional `*test-review`. - - Gate (Phase 2): `*trace`. + - Phase 3: system-level `test-design`, then `framework` and `ci`. + - Phase 4: per-epic `test-design`, optional `atdd`, then `automate` and optional `test-review`. + - Gate (Phase 2): `trace`. 4. **Integrated: Brownfield - BMad Method or Enterprise (Simple or Complex)** - - Phase 2: baseline `*trace`. - - Phase 3: system-level `*test-design`, then `*framework` and `*ci`. - - Phase 4: per-epic `*test-design` focused on regression and integration risks. - - Gate (Phase 2): `*trace`; `*nfr-assess` (if not done earlier). - - For brownfield BMad Method, follow the same flow with `*nfr-assess` optional. + - Phase 2: baseline `trace`. + - Phase 3: system-level `test-design`, then `framework` and `ci`. + - Phase 4: per-epic `test-design` focused on regression and integration risks. + - Gate (Phase 2): `trace`; `nfr-assess` (if not done earlier). + - For brownfield BMad Method, follow the same flow with `nfr-assess` optional. 5. **Integrated: Greenfield - Enterprise Method (Enterprise/Compliance Work)** - - Phase 2: `*nfr-assess`. - - Phase 3: system-level `*test-design`, then `*framework` and `*ci`. - - Phase 4: per-epic `*test-design`, plus `*atdd`/`*automate`/`*test-review`. - - Gate (Phase 2): `*trace`; archive artifacts as needed. + - Phase 2: `nfr-assess`. + - Phase 3: system-level `test-design`, then `framework` and `ci`. + - Phase 4: per-epic `test-design`, plus `atdd`/`automate`/`test-review`. + - Gate (Phase 2): `trace`; archive artifacts as needed. If you are unsure, default to the integrated path for your track and adjust later. @@ -57,24 +57,24 @@ If you are unsure, default to the integrated path for your track and adjust late | Command | Primary Outputs | Notes | With Playwright MCP Enhancements | | -------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `*framework` | Playwright/Cypress scaffold, `.env.example`, `.nvmrc`, sample specs | Use when no production-ready harness exists | - | -| `*ci` | CI workflow, selective test scripts, secrets checklist | Platform-aware (GitHub Actions default) | - | -| `*test-design` | Combined risk assessment, mitigation plan, and coverage strategy | Risk scoring + optional exploratory mode | **+ Exploratory**: Interactive UI discovery with browser automation (uncover actual functionality) | -| `*atdd` | Failing acceptance tests + implementation checklist | TDD red phase + optional recording mode | **+ Recording**: UI selectors verified with live browser; API tests benefit from trace analysis | -| `*automate` | Prioritized specs, fixtures, README/script updates, DoD summary | Optional healing/recording, avoid duplicate coverage | **+ Healing**: Visual debugging + trace analysis for test fixes; **+ Recording**: Verified selectors (UI) + network inspection (API) | -| `*test-review` | Test quality review report with 0-100 score, violations, fixes | Reviews tests against knowledge base patterns | - | -| `*nfr-assess` | NFR assessment report with actions | Focus on security/performance/reliability | - | -| `*trace` | Phase 1: Coverage matrix, recommendations. Phase 2: Gate decision (PASS/CONCERNS/FAIL/WAIVED) | Two-phase workflow: traceability + gate decision | - | +| `framework` | Playwright/Cypress scaffold, `.env.example`, `.nvmrc`, sample specs | Use when no production-ready harness exists | - | +| `ci` | CI workflow, selective test scripts, secrets checklist | Platform-aware (GitHub Actions default) | - | +| `test-design` | Combined risk assessment, mitigation plan, and coverage strategy | Risk scoring + optional exploratory mode | **+ Exploratory**: Interactive UI discovery with browser automation (uncover actual functionality) | +| `atdd` | Failing acceptance tests + implementation checklist | TDD red phase + optional recording mode | **+ Recording**: UI selectors verified with live browser; API tests benefit from trace analysis | +| `automate` | Prioritized specs, fixtures, README/script updates, DoD summary | Optional healing/recording, avoid duplicate coverage | **+ Healing**: Visual debugging + trace analysis for test fixes; **+ Recording**: Verified selectors (UI) + network inspection (API) | +| `test-review` | Test quality review report with 0-100 score, violations, fixes | Reviews tests against knowledge base patterns | - | +| `nfr-assess` | NFR assessment report with actions | Focus on security/performance/reliability | - | +| `trace` | Phase 1: Coverage matrix, recommendations. Phase 2: Gate decision (PASS/CONCERNS/FAIL/WAIVED) | Two-phase workflow: traceability + gate decision | - | ## TEA Workflow Lifecycle **Phase Numbering Note:** BMad uses a 4-phase methodology with optional Phase 1 and a documentation prerequisite: -- **Documentation** (Optional for brownfield): Prerequisite using `*document-project` -- **Phase 1** (Optional): Discovery/Analysis (`*brainstorm`, `*research`, `*product-brief`) -- **Phase 2** (Required): Planning (`*prd` creates PRD with FRs/NFRs) -- **Phase 3** (Track-dependent): Solutioning (`*architecture` → `*test-design` (system-level) → `*create-epics-and-stories` → TEA: `*framework`, `*ci` → `*implementation-readiness`) -- **Phase 4** (Required): Implementation (`*sprint-planning` → per-epic: `*test-design` → per-story: dev workflows) +- **Documentation** (Optional for brownfield): Prerequisite using `document-project` +- **Phase 1** (Optional): Discovery/Analysis (`brainstorm`, `research`, `product-brief`) +- **Phase 2** (Required): Planning (`prd` creates PRD with FRs/NFRs) +- **Phase 3** (Track-dependent): Solutioning (`architecture` → `test-design` (system-level) → `create-epics-and-stories` → TEA: `framework`, `ci` → `implementation-readiness`) +- **Phase 4** (Required): Implementation (`sprint-planning` → per-epic: `test-design` → per-story: dev workflows) TEA integrates into the BMad development lifecycle during Solutioning (Phase 3) and Implementation (Phase 4): @@ -82,21 +82,21 @@ TEA integrates into the BMad development lifecycle during Solutioning (Phase 3) %%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#fff','primaryTextColor':'#000','primaryBorderColor':'#000','lineColor':'#000','secondaryColor':'#fff','tertiaryColor':'#fff','fontSize':'16px','fontFamily':'arial'}}}%% graph TB subgraph Phase2["Phase 2: PLANNING"] - PM["PM: *prd (creates PRD with FRs/NFRs)"] + PM["PM: prd (creates PRD with FRs/NFRs)"] PlanNote["Business requirements phase"] - NFR2["TEA: *nfr-assess (optional, enterprise)"] + NFR2["TEA: nfr-assess (optional, enterprise)"] PM -.-> NFR2 NFR2 -.-> PlanNote PM -.-> PlanNote end subgraph Phase3["Phase 3: SOLUTIONING"] - Architecture["Architect: *architecture"] - EpicsStories["PM/Architect: *create-epics-and-stories"] - TestDesignSys["TEA: *test-design (system-level)"] - Framework["TEA: *framework (optional if needed)"] - CI["TEA: *ci (optional if needed)"] - GateCheck["Architect: *implementation-readiness"] + Architecture["Architect: architecture"] + EpicsStories["PM/Architect: create-epics-and-stories"] + TestDesignSys["TEA: test-design (system-level)"] + Framework["TEA: framework (optional if needed)"] + CI["TEA: ci (optional if needed)"] + GateCheck["Architect: implementation-readiness"] Architecture --> EpicsStories Architecture --> TestDesignSys TestDesignSys --> Framework @@ -108,14 +108,14 @@ graph TB end subgraph Phase4["Phase 4: IMPLEMENTATION - Per Epic Cycle"] - SprintPlan["SM: *sprint-planning"] - TestDesign["TEA: *test-design (per epic)"] - CreateStory["SM: *create-story"] - ATDD["TEA: *atdd (optional, before dev)"] + SprintPlan["SM: sprint-planning"] + TestDesign["TEA: test-design (per epic)"] + CreateStory["SM: create-story"] + ATDD["TEA: atdd (optional, before dev)"] DevImpl["DEV: implements story"] - Automate["TEA: *automate"] - TestReview1["TEA: *test-review (optional)"] - Trace1["TEA: *trace (refresh coverage)"] + Automate["TEA: automate"] + TestReview1["TEA: test-review (optional)"] + Trace1["TEA: trace (refresh coverage)"] SprintPlan --> TestDesign TestDesign --> CreateStory @@ -130,9 +130,9 @@ graph TB end subgraph Gate["EPIC/RELEASE GATE"] - NFR["TEA: *nfr-assess (if not done earlier)"] - TestReview2["TEA: *test-review (final audit, optional)"] - TraceGate["TEA: *trace - Phase 2: Gate"] + NFR["TEA: nfr-assess (if not done earlier)"] + TestReview2["TEA: test-review (final audit, optional)"] + TraceGate["TEA: trace - Phase 2: Gate"] GateDecision{"Gate Decision"} NFR --> TestReview2 @@ -158,14 +158,14 @@ graph TB style Waived fill:#9c27b0,stroke:#4a148c,stroke-width:3px,color:#000 ``` -**TEA workflows:** `*framework` and `*ci` run once in Phase 3 after architecture. `*test-design` is **dual-mode**: +**TEA workflows:** `framework` and `ci` run once in Phase 3 after architecture. `test-design` is **dual-mode**: - **System-level (Phase 3):** Run immediately after architecture/ADR drafting to produce TWO documents: `test-design-architecture.md` (for Architecture/Dev teams: testability gaps, ASRs, NFR requirements) + `test-design-qa.md` (for QA team: test execution recipe, coverage plan, Sprint 0 setup). Feeds the implementation-readiness gate. - **Epic-level (Phase 4):** Run per-epic to produce `test-design-epic-N.md` (risk, priorities, coverage plan). The Quick Flow track skips Phases 1 and 3. BMad Method and Enterprise use all phases based on project needs. -When an ADR or architecture draft is produced, run `*test-design` in **system-level** mode before the implementation-readiness gate. This ensures the ADR has an attached testability review and ADR → test mapping. Keep the test-design updated if ADRs change. +When an ADR or architecture draft is produced, run `test-design` in **system-level** mode before the implementation-readiness gate. This ensures the ADR has an attached testability review and ADR → test mapping. Keep the test-design updated if ADRs change. ## Why TEA Is Different from Other BMM Agents @@ -176,11 +176,11 @@ TEA spans multiple phases (Phase 3, Phase 4, and the release gate). Most BMM age | Phase | TEA Workflows | Frequency | Purpose | | ----------- | --------------------------------------------------------- | ---------------- | ------------------------------------------------------- | | **Phase 2** | (none) | - | Planning phase - PM defines requirements | -| **Phase 3** | \*test-design (system-level), \*framework, \*ci | Once per project | System testability review and test infrastructure setup | -| **Phase 4** | \*test-design, \*atdd, \*automate, \*test-review, \*trace | Per epic/story | Test planning per epic, then per-story testing | -| **Release** | \*nfr-assess, \*trace (Phase 2: gate) | Per epic/release | Go/no-go decision | +| **Phase 3** | `test-design` (system-level), `framework`, `ci` | Once per project | System testability review and test infrastructure setup | +| **Phase 4** | `test-design`, `atdd`, `automate`, `test-review`, `trace` | Per epic/story | Test planning per epic, then per-story testing | +| **Release** | `nfr-assess`, `trace` (Phase 2: gate) | Per epic/release | Go/no-go decision | -**Note**: `*trace` is a two-phase workflow: Phase 1 (traceability) + Phase 2 (gate decision). This reduces cognitive load while maintaining natural workflow. +**Note**: `trace` is a two-phase workflow: Phase 1 (traceability) + Phase 2 (gate decision). This reduces cognitive load while maintaining natural workflow. ### Why TEA Requires Its Own Knowledge Base @@ -211,19 +211,19 @@ These cheat sheets map TEA workflows to the **BMad Method and Enterprise tracks* | Workflow Stage | Test Architect | Dev / Team | Outputs | | -------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------- | -| **Phase 1**: Discovery | - | Analyst `*product-brief` (optional) | `product-brief.md` | -| **Phase 2**: Planning | - | PM `*prd` (creates PRD with FRs/NFRs) | PRD with functional/non-functional requirements | -| **Phase 3**: Solutioning | Run `*framework`, `*ci` AFTER architecture and epic creation | Architect `*architecture`, `*create-epics-and-stories`, `*implementation-readiness` | Architecture, epics/stories, test scaffold, CI pipeline | -| **Phase 4**: Sprint Start | - | SM `*sprint-planning` | Sprint status file with all epics and stories | -| **Phase 4**: Epic Planning | Run `*test-design` for THIS epic (per-epic test plan) | Review epic scope | `test-design-epic-N.md` with risk assessment and test plan | -| **Phase 4**: Story Dev | (Optional) `*atdd` before dev, then `*automate` after | SM `*create-story`, DEV implements | Tests, story implementation | -| **Phase 4**: Story Review | Execute `*test-review` (optional), re-run `*trace` | Address recommendations, update code/tests | Quality report, refreshed coverage matrix | -| **Phase 4**: Release Gate | (Optional) `*test-review` for final audit, Run `*trace` (Phase 2) | Confirm Definition of Done, share release notes | Quality audit, Gate YAML + release summary | +| **Phase 1**: Discovery | - | Analyst `product-brief` (optional) | `product-brief.md` | +| **Phase 2**: Planning | - | PM `prd` (creates PRD with FRs/NFRs) | PRD with functional/non-functional requirements | +| **Phase 3**: Solutioning | Run `framework`, `ci` AFTER architecture and epic creation | Architect `architecture`, `create-epics-and-stories`, `implementation-readiness` | Architecture, epics/stories, test scaffold, CI pipeline | +| **Phase 4**: Sprint Start | - | SM `sprint-planning` | Sprint status file with all epics and stories | +| **Phase 4**: Epic Planning | Run `test-design` for THIS epic (per-epic test plan) | Review epic scope | `test-design-epic-N.md` with risk assessment and test plan | +| **Phase 4**: Story Dev | (Optional) `atdd` before dev, then `automate` after | SM `create-story`, DEV implements | Tests, story implementation | +| **Phase 4**: Story Review | Execute `test-review` (optional), re-run `trace` | Address recommendations, update code/tests | Quality report, refreshed coverage matrix | +| **Phase 4**: Release Gate | (Optional) `test-review` for final audit, Run `trace` (Phase 2) | Confirm Definition of Done, share release notes | Quality audit, Gate YAML + release summary | **Key notes:** -- Run `*framework` and `*ci` once in Phase 3 after architecture. -- Run `*test-design` per epic in Phase 4; use `*atdd` before dev when helpful. -- Use `*trace` for gate decisions; `*test-review` is an optional audit. +- Run `framework` and `ci` once in Phase 3 after architecture. +- Run `test-design` per epic in Phase 4; use `atdd` before dev when helpful. +- Use `trace` for gate decisions; `test-review` is an optional audit. ### Brownfield - BMad Method or Enterprise (Simple or Complex) @@ -233,26 +233,26 @@ These cheat sheets map TEA workflows to the **BMad Method and Enterprise tracks* **🔄 Brownfield Deltas from Greenfield:** - ➕ Documentation (Prerequisite) - Document existing codebase if undocumented -- ➕ Phase 2: `*trace` - Baseline existing test coverage before planning -- 🔄 Phase 4: `*test-design` - Focus on regression hotspots and brownfield risks -- 🔄 Phase 4: Story Review - May include `*nfr-assess` if not done earlier +- ➕ Phase 2: `trace` - Baseline existing test coverage before planning +- 🔄 Phase 4: `test-design` - Focus on regression hotspots and brownfield risks +- 🔄 Phase 4: Story Review - May include `nfr-assess` if not done earlier | Workflow Stage | Test Architect | Dev / Team | Outputs | | --------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| **Documentation**: Prerequisite ➕ | - | Analyst `*document-project` (if undocumented) | Comprehensive project documentation | -| **Phase 1**: Discovery | - | Analyst/PM/Architect rerun planning workflows | Updated planning artifacts in `{output_folder}` | -| **Phase 2**: Planning | Run ➕ `*trace` (baseline coverage) | PM `*prd` (creates PRD with FRs/NFRs) | PRD with FRs/NFRs, ➕ coverage baseline | -| **Phase 3**: Solutioning | Run `*framework`, `*ci` AFTER architecture and epic creation | Architect `*architecture`, `*create-epics-and-stories`, `*implementation-readiness` | Architecture, epics/stories, test framework, CI pipeline | -| **Phase 4**: Sprint Start | - | SM `*sprint-planning` | Sprint status file with all epics and stories | -| **Phase 4**: Epic Planning | Run `*test-design` for THIS epic 🔄 (regression hotspots) | Review epic scope and brownfield risks | `test-design-epic-N.md` with brownfield risk assessment and mitigation | -| **Phase 4**: Story Dev | (Optional) `*atdd` before dev, then `*automate` after | SM `*create-story`, DEV implements | Tests, story implementation | -| **Phase 4**: Story Review | Apply `*test-review` (optional), re-run `*trace`, ➕ `*nfr-assess` if needed | Resolve gaps, update docs/tests | Quality report, refreshed coverage matrix, NFR report | -| **Phase 4**: Release Gate | (Optional) `*test-review` for final audit, Run `*trace` (Phase 2) | Capture sign-offs, share release notes | Quality audit, Gate YAML + release summary | +| **Documentation**: Prerequisite ➕ | - | Analyst `document-project` (if undocumented) | Comprehensive project documentation | +| **Phase 1**: Discovery | - | Analyst/PM/Architect rerun planning workflows | Updated planning artifacts in `{output_folder}` | +| **Phase 2**: Planning | Run ➕ `trace` (baseline coverage) | PM `prd` (creates PRD with FRs/NFRs) | PRD with FRs/NFRs, ➕ coverage baseline | +| **Phase 3**: Solutioning | Run `framework`, `ci` AFTER architecture and epic creation | Architect `architecture`, `create-epics-and-stories`, `implementation-readiness` | Architecture, epics/stories, test framework, CI pipeline | +| **Phase 4**: Sprint Start | - | SM `sprint-planning` | Sprint status file with all epics and stories | +| **Phase 4**: Epic Planning | Run `test-design` for THIS epic 🔄 (regression hotspots) | Review epic scope and brownfield risks | `test-design-epic-N.md` with brownfield risk assessment and mitigation | +| **Phase 4**: Story Dev | (Optional) `atdd` before dev, then `automate` after | SM `create-story`, DEV implements | Tests, story implementation | +| **Phase 4**: Story Review | Apply `test-review` (optional), re-run `trace`, ➕ `nfr-assess` if needed | Resolve gaps, update docs/tests | Quality report, refreshed coverage matrix, NFR report | +| **Phase 4**: Release Gate | (Optional) `test-review` for final audit, Run `trace` (Phase 2) | Capture sign-offs, share release notes | Quality audit, Gate YAML + release summary | **Key notes:** -- Start with `*trace` in Phase 2 to baseline coverage. -- Focus `*test-design` on regression hotspots and integration risk. -- Run `*nfr-assess` before the gate if it wasn't done earlier. +- Start with `trace` in Phase 2 to baseline coverage. +- Focus `test-design` on regression hotspots and integration risk. +- Run `nfr-assess` before the gate if it wasn't done earlier. ### Greenfield - Enterprise Method (Enterprise/Compliance Work) @@ -261,54 +261,54 @@ These cheat sheets map TEA workflows to the **BMad Method and Enterprise tracks* **🏢 Enterprise Deltas from BMad Method:** -- ➕ Phase 1: `*research` - Domain and compliance research (recommended) -- ➕ Phase 2: `*nfr-assess` - Capture NFR requirements early (security/performance/reliability) -- 🔄 Phase 4: `*test-design` - Enterprise focus (compliance, security architecture alignment) +- ➕ Phase 1: `research` - Domain and compliance research (recommended) +- ➕ Phase 2: `nfr-assess` - Capture NFR requirements early (security/performance/reliability) +- 🔄 Phase 4: `test-design` - Enterprise focus (compliance, security architecture alignment) - 📦 Release Gate - Archive artifacts and compliance evidence for audits | Workflow Stage | Test Architect | Dev / Team | Outputs | | -------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| **Phase 1**: Discovery | - | Analyst ➕ `*research`, `*product-brief` | Domain research, compliance analysis, product brief | -| **Phase 2**: Planning | Run ➕ `*nfr-assess` | PM `*prd` (creates PRD with FRs/NFRs), UX `*create-ux-design` | Enterprise PRD with FRs/NFRs, UX design, ➕ NFR documentation | -| **Phase 3**: Solutioning | Run `*framework`, `*ci` AFTER architecture and epic creation | Architect `*architecture`, `*create-epics-and-stories`, `*implementation-readiness` | Architecture, epics/stories, test framework, CI pipeline | -| **Phase 4**: Sprint Start | - | SM `*sprint-planning` | Sprint plan with all epics | -| **Phase 4**: Epic Planning | Run `*test-design` for THIS epic 🔄 (compliance focus) | Review epic scope and compliance requirements | `test-design-epic-N.md` with security/performance/compliance focus | -| **Phase 4**: Story Dev | (Optional) `*atdd`, `*automate`, `*test-review`, `*trace` per story | SM `*create-story`, DEV implements | Tests, fixtures, quality reports, coverage matrices | -| **Phase 4**: Release Gate | Final `*test-review` audit, Run `*trace` (Phase 2), 📦 archive artifacts | Capture sign-offs, 📦 compliance evidence | Quality audit, updated assessments, gate YAML, 📦 audit trail | +| **Phase 1**: Discovery | - | Analyst ➕ `research`, `product-brief` | Domain research, compliance analysis, product brief | +| **Phase 2**: Planning | Run ➕ `nfr-assess` | PM `prd` (creates PRD with FRs/NFRs), UX `create-ux-design` | Enterprise PRD with FRs/NFRs, UX design, ➕ NFR documentation | +| **Phase 3**: Solutioning | Run `framework`, `ci` AFTER architecture and epic creation | Architect `architecture`, `create-epics-and-stories`, `implementation-readiness` | Architecture, epics/stories, test framework, CI pipeline | +| **Phase 4**: Sprint Start | - | SM `sprint-planning` | Sprint plan with all epics | +| **Phase 4**: Epic Planning | Run `test-design` for THIS epic 🔄 (compliance focus) | Review epic scope and compliance requirements | `test-design-epic-N.md` with security/performance/compliance focus | +| **Phase 4**: Story Dev | (Optional) `atdd`, `automate`, `test-review`, `trace` per story | SM `create-story`, DEV implements | Tests, fixtures, quality reports, coverage matrices | +| **Phase 4**: Release Gate | Final `test-review` audit, Run `trace` (Phase 2), 📦 archive artifacts | Capture sign-offs, 📦 compliance evidence | Quality audit, updated assessments, gate YAML, 📦 audit trail | **Key notes:** -- Run `*nfr-assess` early in Phase 2. -- `*test-design` emphasizes compliance, security, and performance alignment. +- Run `nfr-assess` early in Phase 2. +- `test-design` emphasizes compliance, security, and performance alignment. - Archive artifacts at the release gate for audits. **Related how-to guides:** -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) -- [How to Set Up a Test Framework](/docs/how-to/workflows/setup-test-framework.md) -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) -- [How to Set Up CI Pipeline](/docs/how-to/workflows/setup-ci.md) -- [How to Run NFR Assessment](/docs/how-to/workflows/run-nfr-assess.md) -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) +- [How to Set Up a Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) +- [How to Set Up CI Pipeline](/docs/tea/how-to/workflows/setup-ci.md) +- [How to Run NFR Assessment](/docs/tea/how-to/workflows/run-nfr-assess.md) +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) ## Deep Dive Concepts Want to understand TEA principles and patterns in depth? **Core Principles:** -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Probability × impact scoring, P0-P3 priorities -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Definition of Done, determinism, isolation -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - Context engineering with tea-index.csv +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Probability × impact scoring, P0-P3 priorities +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Definition of Done, determinism, isolation +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - Context engineering with tea-index.csv **Technical Patterns:** -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Pure function → fixture → composition -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Eliminating flakiness with intercept-before-navigate +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Pure function → fixture → composition +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Eliminating flakiness with intercept-before-navigate **Engagement & Strategy:** -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - TEA Lite, TEA Solo, TEA Integrated (5 models explained) +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - TEA Lite, TEA Solo, TEA Integrated (5 models explained) **Philosophy:** -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Start here to understand WHY TEA exists** - The problem with AI-generated tests and TEA's three-part solution +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Start here to understand WHY TEA exists** - The problem with AI-generated tests and TEA's three-part solution ## Optional Integrations @@ -318,7 +318,7 @@ Production-ready fixtures and utilities that enhance TEA workflows. - Install: `npm install -D @seontechnologies/playwright-utils` > Note: Playwright Utils is enabled via the installer. Only set `tea_use_playwright_utils` in `_bmad/bmm/config.yaml` if you need to override the installer choice. -- Impacts: `*framework`, `*atdd`, `*automate`, `*test-review`, `*ci` +- Impacts: `framework`, `atdd`, `automate`, `test-review`, `ci` - Utilities include: api-request, auth-session, network-recorder, intercept-network-call, recurse, log, file-utils, burn-in, network-error-monitor, fixtures-composition ### Playwright MCP Enhancements @@ -347,8 +347,8 @@ Live browser verification for test design and automation. } ``` -- Helps `*test-design` validate actual UI behavior. -- Helps `*atdd` and `*automate` verify selectors against the live DOM. +- Helps `test-design` validate actual UI behavior. +- Helps `atdd` and `automate` verify selectors against the live DOM. - Enhances healing with `browser_snapshot`, console, network, and locator tools. **To disable**: set `tea_use_mcp_enhancements: false` in `_bmad/bmm/config.yaml` or remove MCPs from IDE config. @@ -360,51 +360,51 @@ Live browser verification for test design and automation. ### Start Here **New to TEA? Start with the tutorial:** -- [TEA Lite Quickstart Tutorial](/docs/tutorials/getting-started/tea-lite-quickstart.md) - 30-minute beginner guide using TodoMVC +- [TEA Lite Quickstart Tutorial](/docs/tea/tutorials/tea-lite-quickstart.md) - 30-minute beginner guide using TodoMVC ### Workflow Guides (Task-Oriented) **All 8 TEA workflows with step-by-step instructions:** -1. [How to Set Up a Test Framework with TEA](/docs/how-to/workflows/setup-test-framework.md) - Scaffold Playwright or Cypress -2. [How to Set Up CI Pipeline with TEA](/docs/how-to/workflows/setup-ci.md) - Configure CI/CD with selective testing -3. [How to Run Test Design with TEA](/docs/how-to/workflows/run-test-design.md) - Risk-based test planning (system or epic) -4. [How to Run ATDD with TEA](/docs/how-to/workflows/run-atdd.md) - Generate failing tests before implementation -5. [How to Run Automate with TEA](/docs/how-to/workflows/run-automate.md) - Expand test coverage after implementation -6. [How to Run Test Review with TEA](/docs/how-to/workflows/run-test-review.md) - Audit test quality (0-100 scoring) -7. [How to Run NFR Assessment with TEA](/docs/how-to/workflows/run-nfr-assess.md) - Validate non-functional requirements -8. [How to Run Trace with TEA](/docs/how-to/workflows/run-trace.md) - Coverage traceability + gate decisions +1. [How to Set Up a Test Framework with TEA](/docs/tea/how-to/workflows/setup-test-framework.md) - Scaffold Playwright or Cypress +2. [How to Set Up CI Pipeline with TEA](/docs/tea/how-to/workflows/setup-ci.md) - Configure CI/CD with selective testing +3. [How to Run Test Design with TEA](/docs/tea/how-to/workflows/run-test-design.md) - Risk-based test planning (system or epic) +4. [How to Run ATDD with TEA](/docs/tea/how-to/workflows/run-atdd.md) - Generate failing tests before implementation +5. [How to Run Automate with TEA](/docs/tea/how-to/workflows/run-automate.md) - Expand test coverage after implementation +6. [How to Run Test Review with TEA](/docs/tea/how-to/workflows/run-test-review.md) - Audit test quality (0-100 scoring) +7. [How to Run NFR Assessment with TEA](/docs/tea/how-to/workflows/run-nfr-assess.md) - Validate non-functional requirements +8. [How to Run Trace with TEA](/docs/tea/how-to/workflows/run-trace.md) - Coverage traceability + gate decisions ### Customization & Integration **Optional enhancements to TEA workflows:** -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Production-ready fixtures and 9 utilities -- [Enable TEA MCP Enhancements](/docs/how-to/customization/enable-tea-mcp-enhancements.md) - Live browser verification, visual debugging +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Production-ready fixtures and 9 utilities +- [Enable TEA MCP Enhancements](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md) - Live browser verification, visual debugging ### Use-Case Guides **Specialized guidance for specific contexts:** -- [Using TEA with Existing Tests (Brownfield)](/docs/how-to/brownfield/use-tea-with-existing-tests.md) - Incremental improvement, regression hotspots, baseline coverage -- [Running TEA for Enterprise](/docs/how-to/brownfield/use-tea-for-enterprise.md) - Compliance, NFR assessment, audit trails, SOC 2/HIPAA +- [Using TEA with Existing Tests (Brownfield)](/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md) - Incremental improvement, regression hotspots, baseline coverage +- [Running TEA for Enterprise](/docs/tea/how-to/brownfield/use-tea-for-enterprise.md) - Compliance, NFR assessment, audit trails, SOC 2/HIPAA ### Concept Deep Dives (Understanding-Oriented) **Understand the principles and patterns:** -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Probability × impact scoring, P0-P3 priorities, mitigation strategies -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Definition of Done, determinism, isolation, explicit assertions -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Pure function → fixture → composition pattern -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Intercept-before-navigate, eliminating flakiness -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - Context engineering with tea-index.csv, 33 fragments -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - TEA Lite, TEA Solo, TEA Integrated (5 models explained) +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Probability × impact scoring, P0-P3 priorities, mitigation strategies +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Definition of Done, determinism, isolation, explicit assertions +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Pure function → fixture → composition pattern +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Intercept-before-navigate, eliminating flakiness +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - Context engineering with tea-index.csv, 33 fragments +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - TEA Lite, TEA Solo, TEA Integrated (5 models explained) ### Philosophy & Design **Why TEA exists and how it works:** -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Start here to understand WHY** - The problem with AI-generated tests and TEA's three-part solution +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Start here to understand WHY** - The problem with AI-generated tests and TEA's three-part solution ### Reference (Quick Lookup) **Factual information for quick reference:** -- [TEA Command Reference](/docs/reference/tea/commands.md) - All 8 workflows: inputs, outputs, phases, frequency -- [TEA Configuration Reference](/docs/reference/tea/configuration.md) - Config options, file locations, setup examples -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - 33 fragments categorized and explained -- [Glossary - TEA Section](/docs/reference/glossary/index.md#test-architect-tea-concepts) - 20 TEA-specific terms defined +- [TEA Command Reference](/docs/tea/reference/commands.md) - All 8 workflows: inputs, outputs, phases, frequency +- [TEA Configuration Reference](/docs/tea/reference/configuration.md) - Config options, file locations, setup examples +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - 33 fragments categorized and explained +- [Glossary - TEA Section](/docs/tea/glossary/index.md#test-architect-tea-concepts) - 20 TEA-specific terms defined diff --git a/docs/explanation/tea/test-quality-standards.md b/docs/tea/explanation/test-quality-standards.md similarity index 93% rename from docs/explanation/tea/test-quality-standards.md rename to docs/tea/explanation/test-quality-standards.md index da1fdae4..987c603e 100644 --- a/docs/explanation/tea/test-quality-standards.md +++ b/docs/tea/explanation/test-quality-standards.md @@ -513,7 +513,7 @@ test('fast test', async ({ page, interceptNetworkCall }) => { ## TEA's Quality Scoring -TEA reviews tests against these standards in `*test-review`: +TEA reviews tests against these standards in `test-review`: ### Scoring Categories (100 points total) @@ -725,7 +725,7 @@ test('should create user with valid data', async ({ apiRequest }) => { ## How TEA Enforces Standards -### During Test Generation (`*atdd`, `*automate`) +### During Test Generation (`atdd`, `automate`) TEA generates tests following standards by default: @@ -755,7 +755,7 @@ test('should submit contact form', async ({ page }) => { }); ``` -### During Test Review (*test-review) +### During Test Review (`test-review`) TEA audits tests and flags violations: @@ -800,7 +800,7 @@ When is a test "done"? **Code Review DoD:** - [ ] Test quality score > 80 -- [ ] No critical issues from `*test-review` +- [ ] No critical issues from `test-review` - [ ] Follows project patterns (fixtures, selectors) - [ ] Test reviewed by team member @@ -866,41 +866,41 @@ test('should work with optional button', async ({ page }) => { ## Technical Implementation For detailed test quality patterns, see: -- [Test Quality Fragment](/docs/reference/tea/knowledge-base.md#quality-standards) -- [Test Levels Framework Fragment](/docs/reference/tea/knowledge-base.md#quality-standards) -- [Complete Knowledge Base Index](/docs/reference/tea/knowledge-base.md) +- [Test Quality Fragment](/docs/tea/reference/knowledge-base.md#quality-standards) +- [Test Levels Framework Fragment](/docs/tea/reference/knowledge-base.md#quality-standards) +- [Complete Knowledge Base Index](/docs/tea/reference/knowledge-base.md) ## Related Concepts **Core TEA Concepts:** -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Quality scales with risk -- [Knowledge Base System](/docs/explanation/tea/knowledge-base-system.md) - How standards are enforced -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - Quality in different models +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Quality scales with risk +- [Knowledge Base System](/docs/tea/explanation/knowledge-base-system.md) - How standards are enforced +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - Quality in different models **Technical Patterns:** -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Determinism explained -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Isolation through fixtures +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Determinism explained +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Isolation through fixtures **Overview:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Quality standards in lifecycle -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - Why quality matters +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Quality standards in lifecycle +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - Why quality matters ## Practical Guides **Workflow Guides:** -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Audit against these standards -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Generate quality tests -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Expand with quality +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Audit against these standards +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Generate quality tests +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Expand with quality **Use-Case Guides:** -- [Using TEA with Existing Tests](/docs/how-to/brownfield/use-tea-with-existing-tests.md) - Improve legacy quality -- [Running TEA for Enterprise](/docs/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise quality thresholds +- [Using TEA with Existing Tests](/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md) - Improve legacy quality +- [Running TEA for Enterprise](/docs/tea/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise quality thresholds ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - *test-review command -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Test quality fragment -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - TEA terminology +- [TEA Command Reference](/docs/tea/reference/commands.md) - `test-review` command +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Test quality fragment +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - TEA terminology --- diff --git a/docs/explanation/philosophy/testing-as-engineering.md b/docs/tea/explanation/testing-as-engineering.md similarity index 89% rename from docs/explanation/philosophy/testing-as-engineering.md rename to docs/tea/explanation/testing-as-engineering.md index 7b8db5a4..8e2655e4 100644 --- a/docs/explanation/philosophy/testing-as-engineering.md +++ b/docs/tea/explanation/testing-as-engineering.md @@ -55,14 +55,14 @@ A quality operating model packaged as eight executable workflows spanning test d | Workflow | Purpose | |----------|---------| -| `*test-design` | Risk-based test planning per epic | -| `*framework` | Scaffold production-ready test infrastructure | -| `*ci` | CI pipeline with selective testing | -| `*atdd` | Acceptance test-driven development | -| `*automate` | Prioritized test automation | -| `*test-review` | Test quality audits (0-100 score) | -| `*nfr-assess` | Non-functional requirements assessment | -| `*trace` | Coverage traceability and gate decisions | +| `test-design` | Risk-based test planning per epic | +| `framework` | Scaffold production-ready test infrastructure | +| `ci` | CI pipeline with selective testing | +| `atdd` | Acceptance test-driven development | +| `automate` | Prioritized test automation | +| `test-review` | Test quality audits (0-100 score) | +| `nfr-assess` | Non-functional requirements assessment | +| `trace` | Coverage traceability and gate decisions | :::tip[Key Insight] TEA doesn't just generate tests—it provides a complete quality operating model with workflows for planning, execution, and release gates. @@ -105,8 +105,8 @@ The three-part stack addresses each gap: | Gap | Solution | |-----|----------| | No standards | Playwright-Utils provides production-ready patterns | -| No planning | TEA `*test-design` workflow creates risk-based test plans | +| No planning | TEA `test-design` creates risk-based test plans | | No verification | Playwright MCPs validate against live applications | -| No review | TEA `*test-review` audits quality with scoring | +| No review | TEA `test-review` audits quality with scoring | This approach is sometimes called *context engineering*—loading domain-specific standards into AI context automatically rather than relying on prompts alone. TEA's `tea-index.csv` manifest loads relevant knowledge fragments so the AI doesn't relearn testing patterns each session. diff --git a/docs/reference/glossary/index.md b/docs/tea/glossary/index.md similarity index 96% rename from docs/reference/glossary/index.md rename to docs/tea/glossary/index.md index 10624030..3d48d83c 100644 --- a/docs/reference/glossary/index.md +++ b/docs/tea/glossary/index.md @@ -101,7 +101,7 @@ Terminology reference for the BMad Method. | **Story** | Single unit of implementable work with clear acceptance criteria, typically 2-8 hours of effort. Grouped into epics. | | **Story Context** | Implementation guidance embedded in story files during create-story, referencing existing patterns and approaches. | | **Story File** | Markdown file containing story description, acceptance criteria, technical notes, and testing requirements. | -| **Track Selection** | Automatic analysis by workflow-init suggesting appropriate track based on complexity indicators. User can override. | +| **Track Selection** | Automatic analysis by `bmad-help` suggesting appropriate track based on complexity indicators. User can override. | ## Game Development Terms @@ -141,7 +141,7 @@ Terminology reference for the BMad Method. | **System-Level Test Design** | Test planning at architecture level (Phase 3) focusing on testability review, ADR mapping, and test infrastructure needs. | | **tea-index.csv** | Manifest file tracking all knowledge fragments, their descriptions, tags, and which workflows load them. | | **TEA Integrated** | Full BMad Method integration with TEA workflows across all phases (Phase 2, 3, 4, and Release Gate). | -| **TEA Lite** | Beginner approach using just `*automate` workflow to test existing features (simplest way to use TEA). | +| **TEA Lite** | Beginner approach using just `automate` to test existing features (simplest way to use TEA). | | **TEA Solo** | Standalone engagement model using TEA without full BMad Method integration (bring your own requirements). | | **Test Priorities** | Classification system for test importance: P0 (critical path), P1 (high value), P2 (medium value), P3 (low value). | @@ -149,10 +149,10 @@ Terminology reference for the BMad Method. ## See Also -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Complete TEA capabilities -- [TEA Knowledge Base](/docs/reference/tea/knowledge-base.md) - Fragment index -- [TEA Command Reference](/docs/reference/tea/commands.md) - Workflow reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config options +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Complete TEA capabilities +- [TEA Knowledge Base](/docs/tea/reference/knowledge-base.md) - Fragment index +- [TEA Command Reference](/docs/tea/reference/commands.md) - Workflow reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config options --- diff --git a/docs/how-to/brownfield/use-tea-for-enterprise.md b/docs/tea/how-to/brownfield/use-tea-for-enterprise.md similarity index 87% rename from docs/how-to/brownfield/use-tea-for-enterprise.md rename to docs/tea/how-to/brownfield/use-tea-for-enterprise.md index b8397715..9ca7add2 100644 --- a/docs/how-to/brownfield/use-tea-for-enterprise.md +++ b/docs/tea/how-to/brownfield/use-tea-for-enterprise.md @@ -24,7 +24,7 @@ Use TEA on enterprise projects with compliance, security, audit, and regulatory ## Enterprise-Specific TEA Workflows -### NFR Assessment (*nfr-assess) +### NFR Assessment (`nfr-assess`) **Purpose:** Validate non-functional requirements with evidence. @@ -38,7 +38,7 @@ Use TEA on enterprise projects with compliance, security, audit, and regulatory **Example:** ``` -*nfr-assess +nfr-assess Categories: Security, Performance, Reliability, Maintainability @@ -56,7 +56,7 @@ Evidence: **Output:** NFR assessment with PASS/CONCERNS/FAIL for each category. -### Trace with Audit Evidence (*trace) +### Trace with Audit Evidence (`trace`) **Purpose:** Requirements traceability with audit trail. @@ -69,7 +69,7 @@ Evidence: **Example:** ``` -*trace Phase 1 +trace Phase 1 Requirements: PRD.md (with compliance requirements) Test location: tests/ @@ -83,7 +83,7 @@ Output: traceability-matrix.md with: **For Release Gate:** ``` -*trace Phase 2 +trace Phase 2 Generate gate-decision-{gate_type}-{story_id}.md with: - Evidence references @@ -92,7 +92,7 @@ Generate gate-decision-{gate_type}-{story_id}.md with: - Decision rationale ``` -### Test Design with Compliance Focus (*test-design) +### Test Design with Compliance Focus (`test-design`) **Purpose:** Risk assessment with compliance and security focus. @@ -105,7 +105,7 @@ Generate gate-decision-{gate_type}-{story_id}.md with: **Example:** ``` -*test-design +test-design Mode: System-level @@ -126,7 +126,7 @@ Output: TWO documents (system-level): **Research compliance requirements:** ``` -Analyst: *research +Analyst: research Topics: - Industry compliance (SOC 2, HIPAA, GDPR) @@ -138,7 +138,7 @@ Topics: **1. Define NFRs early:** ``` -PM: *prd +PM: prd Include in PRD: - Security requirements (authentication, encryption) @@ -149,7 +149,7 @@ Include in PRD: **2. Assess NFRs:** ``` -TEA: *nfr-assess +TEA: nfr-assess Categories: All (Security, Performance, Reliability, Maintainability) @@ -161,7 +161,7 @@ Output: nfr-assessment.md **3. Baseline (brownfield only):** ``` -TEA: *trace Phase 1 +TEA: trace Phase 1 Establish baseline coverage before new work ``` @@ -170,9 +170,9 @@ Establish baseline coverage before new work **1. Architecture with testability review:** ``` -Architect: *architecture +Architect: architecture -TEA: *test-design (system-level) +TEA: test-design (system-level) Focus: - Security architecture testability @@ -182,7 +182,7 @@ Focus: **2. Test infrastructure:** ``` -TEA: *framework +TEA: framework Requirements: - Separate test environments (dev, staging, prod-mirror) @@ -192,7 +192,7 @@ Requirements: **3. CI/CD with compliance:** ``` -TEA: *ci +TEA: ci Requirements: - Secrets management (Vault, AWS Secrets Manager) @@ -205,21 +205,21 @@ Requirements: **Per epic:** ``` -1. TEA: *test-design (epic-level) +1. TEA: test-design (epic-level) Focus: Compliance, security, performance for THIS epic -2. TEA: *atdd (optional) +2. TEA: atdd (optional) Generate tests including security/compliance scenarios 3. DEV: Implement story -4. TEA: *automate +4. TEA: automate Expand coverage including compliance edge cases -5. TEA: *test-review +5. TEA: test-review Audit quality (score >80 per epic, rises to >85 at release) -6. TEA: *trace Phase 1 +6. TEA: trace Phase 1 Refresh coverage, verify compliance requirements tested ``` @@ -227,7 +227,7 @@ Requirements: **1. Final NFR assessment:** ``` -TEA: *nfr-assess +TEA: nfr-assess All categories (if not done earlier) Latest evidence (performance tests, security scans) @@ -235,7 +235,7 @@ Latest evidence (performance tests, security scans) **2. Final quality audit:** ``` -TEA: *test-review tests/ +TEA: test-review tests/ Full suite review Quality target: >85 for enterprise @@ -243,7 +243,7 @@ Quality target: >85 for enterprise **3. Gate decision:** ``` -TEA: *trace Phase 2 +TEA: trace Phase 2 Evidence required: - traceability-matrix.md (from Phase 1) @@ -374,7 +374,7 @@ compliance/ **Priority 1:** Security requirements ``` 1. Document all security requirements -2. Generate security tests with *atdd +2. Generate security tests with `atdd` 3. Run security test suite 4. Pass security audit BEFORE moving forward ``` @@ -495,30 +495,30 @@ testWithAuth('admin can access admin endpoint', async ({ apiRequest, authToken, ## Related Guides **Workflow Guides:** -- [How to Run NFR Assessment](/docs/how-to/workflows/run-nfr-assess.md) - Deep dive on NFRs -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) - Gate decisions with evidence -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Quality audits -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Compliance-focused planning +- [How to Run NFR Assessment](/docs/tea/how-to/workflows/run-nfr-assess.md) - Deep dive on NFRs +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) - Gate decisions with evidence +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Quality audits +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Compliance-focused planning **Use-Case Guides:** -- [Using TEA with Existing Tests](/docs/how-to/brownfield/use-tea-with-existing-tests.md) - Brownfield patterns +- [Using TEA with Existing Tests](/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md) - Brownfield patterns **Customization:** -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Production-ready utilities +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Production-ready utilities ## Understanding the Concepts -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - Enterprise model explained -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Probability × impact scoring -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Enterprise quality thresholds -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Complete TEA lifecycle +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - Enterprise model explained +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Probability × impact scoring +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Enterprise quality thresholds +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Complete TEA lifecycle ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - All 8 workflows -- [TEA Configuration](/docs/reference/tea/configuration.md) - Enterprise config options -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Testing patterns -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - TEA terminology +- [TEA Command Reference](/docs/tea/reference/commands.md) - All 8 workflows +- [TEA Configuration](/docs/tea/reference/configuration.md) - Enterprise config options +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Testing patterns +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - TEA terminology --- diff --git a/docs/how-to/brownfield/use-tea-with-existing-tests.md b/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md similarity index 81% rename from docs/how-to/brownfield/use-tea-with-existing-tests.md rename to docs/tea/how-to/brownfield/use-tea-with-existing-tests.md index b2590373..0f05a9ad 100644 --- a/docs/how-to/brownfield/use-tea-with-existing-tests.md +++ b/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md @@ -22,7 +22,7 @@ Use TEA on brownfield projects (existing codebases with legacy tests) to establi - Existing codebase with tests (even if incomplete or low quality) - Tests run successfully (or at least can be executed) -**Note:** If your codebase is completely undocumented, run `*document-project` first to create baseline documentation. +**Note:** If your codebase is completely undocumented, run `document-project` first to create baseline documentation. ## Brownfield Strategy @@ -30,12 +30,12 @@ Use TEA on brownfield projects (existing codebases with legacy tests) to establi Understand what you have before changing anything. -#### Step 1: Baseline Coverage with *trace +#### Step 1: Baseline Coverage with `trace` -Run `*trace` Phase 1 to map existing tests to requirements: +Run `trace` Phase 1 to map existing tests to requirements: ``` -*trace +trace ``` **Select:** Phase 1 (Requirements Traceability) @@ -68,12 +68,12 @@ Run `*trace` Phase 1 to map existing tests to requirements: This baseline becomes your improvement target. -#### Step 2: Quality Audit with *test-review +#### Step 2: Quality Audit with `test-review` -Run `*test-review` on existing tests: +Run `test-review` on existing tests: ``` -*test-review tests/ +test-review tests/ ``` **Output:** `test-review.md` with quality score and issues. @@ -113,7 +113,7 @@ Goal: Get P0 coverage to 100% Actions: 1. Identify P0 requirements with no tests (from trace) -2. Run *automate to generate tests for missing P0 scenarios +2. Run `automate` to generate tests for missing P0 scenarios 3. Fix critical quality issues in P0 tests (from test-review) ``` @@ -183,7 +183,7 @@ test('checkout completes', async ({ page, interceptNetworkCall }) => { - Glob pattern matching (`**/api/checkout`) - Cleaner, more maintainable code -**For automatic error detection,** use `network-error-monitor` fixture separately. See [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md#network-error-monitor). +**For automatic error detection,** use `network-error-monitor` fixture separately. See [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md#network-error-monitor). **Priority 3: P1 Requirements** ``` @@ -228,11 +228,11 @@ Apply TEA workflows to new work while improving legacy tests. **Use full TEA workflow:** ``` -1. *test-design (epic-level) - Plan tests for new feature -2. *atdd - Generate failing tests first (TDD) +1. `test-design` (epic-level) - Plan tests for new feature +2. `atdd` - Generate failing tests first (TDD) 3. Implement feature -4. *automate - Expand coverage -5. *test-review - Ensure quality +4. `automate` - Expand coverage +5. `test-review` - Ensure quality ``` **Benefits:** @@ -247,7 +247,7 @@ Apply TEA workflows to new work while improving legacy tests. 1. Reproduce bug with failing test 2. Fix bug 3. Verify test passes -4. Run *test-review on regression test +4. Run `test-review` on regression test 5. Add to regression test suite ``` @@ -255,10 +255,10 @@ Apply TEA workflows to new work while improving legacy tests. **Before refactoring:** ``` -1. Run *trace - Baseline coverage +1. Run `trace` - Baseline coverage 2. Note current coverage % 3. Refactor code -4. Run *trace - Verify coverage maintained +4. Run `trace` - Verify coverage maintained 5. No coverage should decrease ``` @@ -375,7 +375,7 @@ test.skip('flaky test - needs fixing', async ({ page }) => { **Week 1:** `tests/auth/` ``` -1. Run *test-review on auth tests +1. Run `test-review` on auth tests 2. Fix critical issues 3. Re-review 4. Mark directory as "modernized" @@ -425,7 +425,7 @@ Same process **Solution:** ``` -1. Run *trace - TEA analyzes tests and maps to requirements +1. Run `trace` - TEA analyzes tests and maps to requirements 2. Review traceability matrix 3. Document findings 4. Use as baseline for improvement @@ -463,7 +463,7 @@ Incremental changes = lower risk 3. Commit documentation for team ``` -**Note:** `*framework` is for new test setup, not existing tests. For brownfield, document what you have. +**Note:** `framework` is for new test setup, not existing tests. For brownfield, document what you have. ### "Tests Take Hours to Run" @@ -480,7 +480,7 @@ Before: 4 hours sequential After: 15 minutes with sharding + selective testing ``` -**How `*ci` helps:** +**How `ci` helps:** - Scaffolds CI configuration with parallel sharding examples - Provides selective testing script templates - Documents burn-in and optimization strategies @@ -489,7 +489,7 @@ After: 15 minutes with sharding + selective testing **With Playwright Utils burn-in:** - Smart selective testing based on git diff - Volume control (run percentage of affected tests) -- See [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md#burn-in) +- See [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md#burn-in) ### "We Have Tests But They Always Fail" @@ -497,7 +497,7 @@ After: 15 minutes with sharding + selective testing **Solution:** ``` -1. Run *test-review to identify flakiness patterns +1. Run `test-review` to identify flakiness patterns 2. Fix top 5 flaky tests (biggest impact) 3. Quarantine remaining flaky tests 4. Re-enable as you fix them @@ -511,66 +511,66 @@ Don't let perfect be the enemy of good **1. Documentation (if needed):** ``` -*document-project +document-project ``` **2. Baseline (Phase 2):** ``` -*trace Phase 1 - Establish coverage baseline -*test-review - Establish quality baseline +trace Phase 1 - Establish coverage baseline +test-review - Establish quality baseline ``` **3. Planning (Phase 2-3):** ``` -*prd - Document requirements (if missing) -*architecture - Document architecture (if missing) -*test-design (system-level) - Testability review +prd - Document requirements (if missing) +architecture - Document architecture (if missing) +test-design (system-level) - Testability review ``` **4. Infrastructure (Phase 3):** ``` -*framework - Modernize test framework (if needed) -*ci - Setup or improve CI/CD +framework - Modernize test framework (if needed) +ci - Setup or improve CI/CD ``` **5. Per Epic (Phase 4):** ``` -*test-design (epic-level) - Focus on regression hotspots -*automate - Add missing tests -*test-review - Ensure quality -*trace Phase 1 - Refresh coverage +test-design (epic-level) - Focus on regression hotspots +automate - Add missing tests +test-review - Ensure quality +trace Phase 1 - Refresh coverage ``` **6. Release Gate:** ``` -*nfr-assess - Validate NFRs (if enterprise) -*trace Phase 2 - Gate decision +nfr-assess - Validate NFRs (if enterprise) +trace Phase 2 - Gate decision ``` ## Related Guides **Workflow Guides:** -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) - Baseline coverage analysis -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Quality audit -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Fill coverage gaps -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Risk assessment +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) - Baseline coverage analysis +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Quality audit +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Fill coverage gaps +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Risk assessment **Customization:** -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Modernize tests with utilities +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Modernize tests with utilities ## Understanding the Concepts -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - Brownfield model explained -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - What makes tests good -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Fix flakiness -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Prioritize improvements +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - Brownfield model explained +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - What makes tests good +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Fix flakiness +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Prioritize improvements ## Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) - All 8 workflows -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config options -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Testing patterns -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - TEA terminology +- [TEA Command Reference](/docs/tea/reference/commands.md) - All 8 workflows +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config options +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Testing patterns +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - TEA terminology --- diff --git a/docs/how-to/customization/enable-tea-mcp-enhancements.md b/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md similarity index 85% rename from docs/how-to/customization/enable-tea-mcp-enhancements.md rename to docs/tea/how-to/customization/enable-tea-mcp-enhancements.md index 830ee22f..331578a1 100644 --- a/docs/how-to/customization/enable-tea-mcp-enhancements.md +++ b/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md @@ -19,14 +19,14 @@ MCP (Model Context Protocol) servers enable AI agents to interact with live brow ## When to Use This **For UI Testing:** -- Want exploratory mode in `*test-design` (browser-based UI discovery) -- Want recording mode in `*atdd` or `*automate` (verify selectors with live browser) -- Want healing mode in `*automate` (fix tests with visual debugging) +- Want exploratory mode in `test-design` (browser-based UI discovery) +- Want recording mode in `atdd` or `automate` (verify selectors with live browser) +- Want healing mode in `automate` (fix tests with visual debugging) - Need accurate selectors from actual DOM - Debugging complex UI interactions **For API Testing:** -- Want healing mode in `*automate` (analyze failures with trace data) +- Want healing mode in `automate` (analyze failures with trace data) - Need to debug test failures (network responses, request/response data, timing) - Want to inspect trace files (network traffic, errors, race conditions) @@ -100,7 +100,7 @@ Add to your IDE's MCP configuration: } ``` -See [TEA Overview](/docs/explanation/features/tea-overview.md#playwright-mcp-enhancements) for IDE-specific config locations. +See [TEA Overview](/docs/tea/explanation/tea-overview.md#playwright-mcp-enhancements) for IDE-specific config locations. ### 2. Enable in BMAD @@ -117,7 +117,7 @@ Ensure your MCP servers are running in your IDE. ## How MCP Enhances TEA Workflows -### *test-design: Exploratory Mode +### test-design: Exploratory Mode **Without MCP:** - TEA infers UI functionality from documentation @@ -148,7 +148,7 @@ I'll design tests for these interactions." - Discovers functionality you might not describe - Validates test scenarios are possible -### *atdd: Recording Mode +### atdd: Recording Mode **Without MCP:** - TEA generates selectors from best practices @@ -187,7 +187,7 @@ I'll validate these in tests." - API: Validated request/response patterns from trace - Both: Tests work on first run -### *automate: Healing + Recording Modes +### automate: Healing + Recording Modes **Without MCP:** - TEA analyzes test code only @@ -227,7 +227,7 @@ API: [Analyzes network traffic, validates request/response patterns] ### Exploratory Mode Example ``` -User: Run *test-design +User: Run test-design TEA: What feature are you designing tests for? @@ -252,7 +252,7 @@ Test design will cover all these scenarios. ### Recording Mode Example ``` -User: Run *atdd for user registration feature +User: Run atdd for user registration feature TEA: [With MCP] Let me verify the registration form selectors [Opens browser at /register] @@ -271,7 +271,7 @@ Generating tests with verified selectors... ### Healing Mode Example ``` -User: Run *automate with healing mode +User: Run automate with healing mode TEA: [With MCP] Running tests and checking for failures [Runs test suite] @@ -398,26 +398,26 @@ tea_use_mcp_enhancements: false ## Related Guides **Getting Started:** -- [TEA Lite Quickstart Tutorial](/docs/tutorials/getting-started/tea-lite-quickstart.md) - Learn TEA basics first +- [TEA Lite Quickstart Tutorial](/docs/tea/tutorials/tea-lite-quickstart.md) - Learn TEA basics first **Workflow Guides (MCP-Enhanced):** -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Exploratory mode with browser -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Recording mode for accurate selectors -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Healing mode for debugging +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Exploratory mode with browser +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Recording mode for accurate selectors +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Healing mode for debugging **Other Customization:** -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Production-ready utilities +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Production-ready utilities ## Understanding the Concepts -- [TEA Overview](/docs/explanation/features/tea-overview.md) - MCP enhancements in lifecycle -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - When to use MCP enhancements +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - MCP enhancements in lifecycle +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - When to use MCP enhancements ## Reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - tea_use_mcp_enhancements option -- [TEA Command Reference](/docs/reference/tea/commands.md) - MCP-enhanced workflows -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - MCP Enhancements term +- [TEA Configuration](/docs/tea/reference/configuration.md) - tea_use_mcp_enhancements option +- [TEA Command Reference](/docs/tea/reference/commands.md) - MCP-enhanced workflows +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - MCP Enhancements term --- diff --git a/docs/how-to/customization/integrate-playwright-utils.md b/docs/tea/how-to/customization/integrate-playwright-utils.md similarity index 94% rename from docs/how-to/customization/integrate-playwright-utils.md rename to docs/tea/how-to/customization/integrate-playwright-utils.md index 32d6fcff..12c9203f 100644 --- a/docs/how-to/customization/integrate-playwright-utils.md +++ b/docs/tea/how-to/customization/integrate-playwright-utils.md @@ -82,7 +82,7 @@ tea_use_playwright_utils: true ## What Changes When Enabled -### *framework Workflow +### `framework` Workflow **Vanilla Playwright:** ```typescript @@ -133,7 +133,7 @@ test('api test', async ({ apiRequest, log }) => { }); ``` -### `*atdd` and `*automate` Workflows +### `atdd` and `automate` Workflows **Without Playwright Utils:** ```typescript @@ -160,7 +160,7 @@ test('should fetch profile', async ({ apiRequest }) => { }); ``` -### *test-review Workflow +### `test-review` Workflow **Without Playwright Utils:** Reviews against generic Playwright patterns @@ -172,7 +172,7 @@ Reviews against playwright-utils best practices: - Network-first patterns - Structured logging -### *ci Workflow +### `ci` Workflow **Without Playwright Utils:** - Parallel sharding @@ -783,29 +783,29 @@ expect(status).toBe(200); ## Related Guides **Getting Started:** -- [TEA Lite Quickstart Tutorial](/docs/tutorials/getting-started/tea-lite-quickstart.md) - Learn TEA basics -- [How to Set Up Test Framework](/docs/how-to/workflows/setup-test-framework.md) - Initial framework setup +- [TEA Lite Quickstart Tutorial](/docs/tea/tutorials/tea-lite-quickstart.md) - Learn TEA basics +- [How to Set Up Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) - Initial framework setup **Workflow Guides:** -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Generate tests with utilities -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Expand coverage with utilities -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Review against PW-Utils patterns +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Generate tests with utilities +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Expand coverage with utilities +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Review against PW-Utils patterns **Other Customization:** -- [Enable MCP Enhancements](/docs/how-to/customization/enable-tea-mcp-enhancements.md) - Live browser verification +- [Enable MCP Enhancements](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md) - Live browser verification ## Understanding the Concepts -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Why Playwright Utils matters** (part of TEA's three-part solution) -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Pure function → fixture pattern -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Network utilities explained -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Patterns PW-Utils enforces +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Why Playwright Utils matters** (part of TEA's three-part solution) +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Pure function → fixture pattern +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Network utilities explained +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Patterns PW-Utils enforces ## Reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - tea_use_playwright_utils option -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Playwright Utils fragments -- [Glossary](/docs/reference/glossary/index.md#test-architect-tea-concepts) - Playwright Utils term +- [TEA Configuration](/docs/tea/reference/configuration.md) - tea_use_playwright_utils option +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Playwright Utils fragments +- [Glossary](/docs/tea/glossary/index.md#test-architect-tea-concepts) - Playwright Utils term - [Official PW-Utils Docs](https://seontechnologies.github.io/playwright-utils/) - Complete API reference --- diff --git a/docs/how-to/workflows/run-atdd.md b/docs/tea/how-to/workflows/run-atdd.md similarity index 87% rename from docs/how-to/workflows/run-atdd.md rename to docs/tea/how-to/workflows/run-atdd.md index 9b55ecb3..c3112176 100644 --- a/docs/how-to/workflows/run-atdd.md +++ b/docs/tea/how-to/workflows/run-atdd.md @@ -5,7 +5,7 @@ description: Generate failing acceptance tests before implementation using TEA's # How to Run ATDD with TEA -Use TEA's `*atdd` workflow to generate failing acceptance tests BEFORE implementation. This is the TDD (Test-Driven Development) red phase - tests fail first, guide development, then pass. +Use TEA's `atdd` workflow to generate failing acceptance tests BEFORE implementation. This is the TDD (Test-Driven Development) red phase - tests fail first, guide development, then pass. ## When to Use This @@ -15,14 +15,14 @@ Use TEA's `*atdd` workflow to generate failing acceptance tests BEFORE implement - You're practicing acceptance test-driven development **Don't use this if:** -- Feature already exists (use `*automate` instead) +- Feature already exists (use `automate` instead) - You want tests that pass immediately ## Prerequisites - BMad Method installed - TEA agent available -- Test framework setup complete (run `*framework` if needed) +- Test framework setup complete (run `framework` if needed) - Story or feature defined with acceptance criteria **Note:** This guide uses Playwright examples. If using Cypress, commands and syntax will differ (e.g., `cy.get()` instead of `page.locator()`). @@ -34,13 +34,13 @@ Use TEA's `*atdd` workflow to generate failing acceptance tests BEFORE implement Start a fresh chat and load TEA: ``` -*tea +tea ``` ### 2. Run the ATDD Workflow ``` -*atdd +atdd ``` ### 3. Provide Context @@ -80,7 +80,7 @@ And changes are not saved **Reference Documents** (optional): - Point to your story file - Reference PRD or tech spec -- Link to test design (if you ran `*test-design` first) +- Link to test design (if you ran `test-design` first) ### 4. Specify Test Levels @@ -367,20 +367,20 @@ Running 6 tests using 1 worker ### Start with Test Design -Run `*test-design` before `*atdd` for better results: +Run `test-design` before `atdd` for better results: ``` -*test-design # Risk assessment and priorities -*atdd # Generate tests based on design +test-design # Risk assessment and priorities +atdd # Generate tests based on design ``` ### MCP Enhancements (Optional) -If you have MCP servers configured (`tea_use_mcp_enhancements: true`), TEA can use them during `*atdd`. +If you have MCP servers configured (`tea_use_mcp_enhancements: true`), TEA can use them during `atdd`. **Note:** ATDD is for features that don't exist yet, so recording mode (verify selectors with live UI) only applies if you have skeleton/mockup UI already implemented. For typical ATDD (no UI yet), TEA infers selectors from best practices. -See [Enable MCP Enhancements](/docs/how-to/customization/enable-tea-mcp-enhancements.md) for setup. +See [Enable MCP Enhancements](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md) for setup. ### Focus on P0/P1 Scenarios @@ -391,15 +391,15 @@ Generate tests for: - P0: Critical path (happy path) - P1: High value (validation, errors) -Skip P2/P3 for now - add later with *automate +Skip P2/P3 for now - add later with automate ``` ### API Tests First, E2E Later Recommended order: -1. Generate API tests with `*atdd` +1. Generate API tests with `atdd` 2. Implement backend (make API tests pass) -3. Generate E2E tests with `*atdd` (or `*automate`) +3. Generate E2E tests with `atdd` (or `automate`) 4. Implement frontend (make E2E tests pass) This "outside-in" approach is faster and more reliable. @@ -415,21 +415,21 @@ Don't modify these patterns - they prevent flakiness! ## Related Guides -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Plan before generating -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Tests for existing features -- [How to Set Up Test Framework](/docs/how-to/workflows/setup-test-framework.md) - Initial setup +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Plan before generating +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Tests for existing features +- [How to Set Up Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) - Initial setup ## Understanding the Concepts -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Why TEA generates quality tests** (foundational) -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Why P0 vs P3 matters -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - What makes tests good -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Avoiding flakiness +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Why TEA generates quality tests** (foundational) +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Why P0 vs P3 matters +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - What makes tests good +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Avoiding flakiness ## Reference -- [Command: *atdd](/docs/reference/tea/commands.md#atdd) - Full command reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - MCP and Playwright Utils options +- [Command: *atdd](/docs/tea/reference/commands.md#atdd) - Full command reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - MCP and Playwright Utils options --- diff --git a/docs/how-to/workflows/run-automate.md b/docs/tea/how-to/workflows/run-automate.md similarity index 91% rename from docs/how-to/workflows/run-automate.md rename to docs/tea/how-to/workflows/run-automate.md index 0b48f8f1..ad44b27f 100644 --- a/docs/how-to/workflows/run-automate.md +++ b/docs/tea/how-to/workflows/run-automate.md @@ -5,7 +5,7 @@ description: Expand test automation coverage after implementation using TEA's au # How to Run Automate with TEA -Use TEA's `*automate` workflow to generate comprehensive tests for existing features. Unlike `*atdd`, these tests pass immediately because the feature already exists. +Use TEA's `automate` workflow to generate comprehensive tests for existing features. Unlike `*atdd`, these tests pass immediately because the feature already exists. ## When to Use This @@ -16,14 +16,14 @@ Use TEA's `*automate` workflow to generate comprehensive tests for existing feat - Adding tests to legacy code **Don't use this if:** -- Feature doesn't exist yet (use `*atdd` instead) -- Want failing tests to guide development (use `*atdd` for TDD) +- Feature doesn't exist yet (use `atdd` instead) +- Want failing tests to guide development (use `atdd` for TDD) ## Prerequisites - BMad Method installed - TEA agent available -- Test framework setup complete (run `*framework` if needed) +- Test framework setup complete (run `framework` if needed) - Feature implemented and working **Note:** This guide uses Playwright examples. If using Cypress, commands and syntax will differ. @@ -35,13 +35,13 @@ Use TEA's `*automate` workflow to generate comprehensive tests for existing feat Start a fresh chat and load TEA: ``` -*tea +tea ``` ### 2. Run the Automate Workflow ``` -*automate +automate ``` ### 3. Provide Context @@ -501,11 +501,11 @@ TEA supports component testing using framework-appropriate tools: ### Start with Test Design -Run `*test-design` before `*automate` for better results: +Run `test-design` before `automate` for better results: ``` -*test-design # Risk assessment, priorities -*automate # Generate tests based on priorities +test-design # Risk assessment, priorities +automate # Generate tests based on priorities ``` TEA will focus on P0/P1 scenarios and skip low-value tests. @@ -543,12 +543,12 @@ TEA will analyze existing tests and only generate new scenarios. ### MCP Enhancements (Optional) -If you have MCP servers configured (`tea_use_mcp_enhancements: true`), TEA can use them during `*automate` for: +If you have MCP servers configured (`tea_use_mcp_enhancements: true`), TEA can use them during `automate` for: - **Healing mode:** Fix broken selectors, update assertions, enhance with trace analysis - **Recording mode:** Verify selectors with live browser, capture network requests -No prompts - TEA uses MCPs automatically when available. See [Enable MCP Enhancements](/docs/how-to/customization/enable-tea-mcp-enhancements.md) for setup. +No prompts - TEA uses MCPs automatically when available. See [Enable MCP Enhancements](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md) for setup. ### Generate Tests Incrementally @@ -557,20 +557,20 @@ Don't generate all tests at once: **Iteration 1:** ``` Generate P0 tests only (critical path) -Run: *automate +Run: automate ``` **Iteration 2:** ``` Generate P1 tests (high value scenarios) -Run: *automate +Run: automate Tell TEA to avoid P0 coverage ``` **Iteration 3:** ``` Generate P2 tests (if time permits) -Run: *automate +Run: automate ``` This iterative approach: @@ -628,25 +628,25 @@ Generate tests for scenarios NOT covered by those files If you have MCP servers configured, TEA verifies selectors against live browser. Otherwise, TEA generates accessible selectors (`getByRole`, `getByLabel`) by default. -Setup: Answer "Yes" to MCPs in BMad installer + configure MCP servers in your IDE. See [Enable MCP Enhancements](/docs/how-to/customization/enable-tea-mcp-enhancements.md). +Setup: Answer "Yes" to MCPs in BMad installer + configure MCP servers in your IDE. See [Enable MCP Enhancements](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md). ## Related Guides -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Plan before generating -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Failing tests before implementation -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Audit generated quality +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Plan before generating +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Failing tests before implementation +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Audit generated quality ## Understanding the Concepts -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Why TEA generates quality tests** (foundational) -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Why prioritize P0 over P3 -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - What makes tests good -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Reusable test patterns +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Why TEA generates quality tests** (foundational) +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Why prioritize P0 over P3 +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - What makes tests good +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Reusable test patterns ## Reference -- [Command: *automate](/docs/reference/tea/commands.md#automate) - Full command reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - MCP and Playwright Utils options +- [Command: *automate](/docs/tea/reference/commands.md#automate) - Full command reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - MCP and Playwright Utils options --- diff --git a/docs/how-to/workflows/run-nfr-assess.md b/docs/tea/how-to/workflows/run-nfr-assess.md similarity index 95% rename from docs/how-to/workflows/run-nfr-assess.md rename to docs/tea/how-to/workflows/run-nfr-assess.md index 6477aca1..f5bd1ecf 100644 --- a/docs/how-to/workflows/run-nfr-assess.md +++ b/docs/tea/how-to/workflows/run-nfr-assess.md @@ -5,7 +5,7 @@ description: Validate non-functional requirements for security, performance, rel # How to Run NFR Assessment with TEA -Use TEA's `*nfr-assess` workflow to validate non-functional requirements (NFRs) with evidence-based assessment across security, performance, reliability, and maintainability. +Use TEA's `nfr-assess` workflow to validate non-functional requirements (NFRs) with evidence-based assessment across security, performance, reliability, and maintainability. ## When to Use This @@ -37,7 +37,7 @@ Use TEA's `*nfr-assess` workflow to validate non-functional requirements (NFRs) Start a fresh chat and run: ``` -*nfr-assess +nfr-assess ``` This loads TEA and starts the NFR assessment workflow. @@ -463,7 +463,7 @@ If business decides to deploy with current performance: ### Run NFR Assessment Early **Phase 2 (Enterprise):** -Run `*nfr-assess` during planning to: +Run `nfr-assess` during planning to: - Identify NFR requirements early - Plan for performance testing - Budget for security audits @@ -559,7 +559,7 @@ After implementing mitigations: ``` 1. Fix performance issues 2. Run load tests again -3. Run *nfr-assess with new evidence +3. Run nfr-assess with new evidence 4. Verify PASS status ``` @@ -573,7 +573,7 @@ Don't deploy with CONCERNS without mitigation or waiver. ### Pre-Release - [ ] All tests passing - [ ] Test coverage > 80% -- [ ] Run *nfr-assess +- [ ] Run nfr-assess - [ ] NFR status: PASS or WAIVED ### Performance @@ -660,19 +660,19 @@ Assess categories incrementally, not all at once. ## Related Guides -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) - Gate decision complements NFR -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Quality complements NFR -- [Run TEA for Enterprise](/docs/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise workflow +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) - Gate decision complements NFR +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Quality complements NFR +- [Run TEA for Enterprise](/docs/tea/how-to/brownfield/use-tea-for-enterprise.md) - Enterprise workflow ## Understanding the Concepts -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Risk assessment principles -- [TEA Overview](/docs/explanation/features/tea-overview.md) - NFR in release gates +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Risk assessment principles +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - NFR in release gates ## Reference -- [Command: *nfr-assess](/docs/reference/tea/commands.md#nfr-assess) - Full command reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - Enterprise config options +- [Command: *nfr-assess](/docs/tea/reference/commands.md#nfr-assess) - Full command reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - Enterprise config options --- diff --git a/docs/how-to/workflows/run-test-design.md b/docs/tea/how-to/workflows/run-test-design.md similarity index 95% rename from docs/how-to/workflows/run-test-design.md rename to docs/tea/how-to/workflows/run-test-design.md index 64424b65..7b972820 100644 --- a/docs/how-to/workflows/run-test-design.md +++ b/docs/tea/how-to/workflows/run-test-design.md @@ -3,7 +3,7 @@ title: "How to Run Test Design with TEA" description: How to create comprehensive test plans using TEA's test-design workflow --- -Use TEA's `*test-design` workflow to create comprehensive test plans with risk assessment and coverage strategies. +Use TEA's `test-design` workflow to create comprehensive test plans with risk assessment and coverage strategies. ## When to Use This @@ -33,7 +33,7 @@ Start a fresh chat and load the TEA (Test Architect) agent. ### 2. Run the Test Design Workflow ``` -*test-design +test-design ``` ### 3. Specify the Mode @@ -122,7 +122,7 @@ TEA produces two focused documents for system-level mode: - **Run system-level right after architecture** — Early testability review - **Run epic-level at the start of each epic** — Targeted test planning - **Update if ADRs change** — Keep test design aligned -- **Use output to guide other workflows** — Feeds into `*atdd` and `*automate` +- **Use output to guide other workflows** — Feeds into `atdd` and `automate` - **Architecture teams review Architecture doc** — Focus on blockers and mitigation plans - **QA teams use QA doc as implementation guide** — Follow test scenarios and Sprint 0 checklist diff --git a/docs/how-to/workflows/run-test-review.md b/docs/tea/how-to/workflows/run-test-review.md similarity index 92% rename from docs/how-to/workflows/run-test-review.md rename to docs/tea/how-to/workflows/run-test-review.md index 924b12a7..ae538a47 100644 --- a/docs/how-to/workflows/run-test-review.md +++ b/docs/tea/how-to/workflows/run-test-review.md @@ -5,7 +5,7 @@ description: Audit test quality using TEA's comprehensive knowledge base and get # How to Run Test Review with TEA -Use TEA's `*test-review` workflow to audit test quality with objective scoring and actionable feedback. TEA reviews tests against its knowledge base of best practices. +Use TEA's `test-review` workflow to audit test quality with objective scoring and actionable feedback. TEA reviews tests against its knowledge base of best practices. ## When to Use This @@ -30,13 +30,13 @@ Use TEA's `*test-review` workflow to audit test quality with objective scoring a Start a fresh chat and load TEA: ``` -*tea +tea ``` ### 2. Run the Test Review Workflow ``` -*test-review +test-review ``` ### 3. Specify Review Scope @@ -351,17 +351,17 @@ test('should show validation error for expired card', async ({ page }) => { }); 6. Improve test names in `checkout.spec.ts` ### Long-term (Continuous Improvement) -7. Re-run `*test-review` after fixes (target: 85/100) +7. Re-run `test-review` after fixes (target: 85/100) 8. Add performance budgets to CI 9. Document test patterns for team ## Knowledge Base References TEA reviewed against these patterns: -- [test-quality.md](/docs/reference/tea/knowledge-base.md#test-quality) - Execution limits, isolation -- [network-first.md](/docs/reference/tea/knowledge-base.md#network-first) - Deterministic waits -- [timing-debugging.md](/docs/reference/tea/knowledge-base.md#timing-debugging) - Race conditions -- [selector-resilience.md](/docs/reference/tea/knowledge-base.md#selector-resilience) - Robust selectors +- [test-quality.md](/docs/tea/reference/knowledge-base.md#test-quality) - Execution limits, isolation +- [network-first.md](/docs/tea/reference/knowledge-base.md#network-first) - Deterministic waits +- [timing-debugging.md](/docs/tea/reference/knowledge-base.md#timing-debugging) - Race conditions +- [selector-resilience.md](/docs/tea/reference/knowledge-base.md#selector-resilience) - Robust selectors ``` ## Understanding the Scores @@ -445,8 +445,8 @@ Make test review part of release checklist: Always review AI-generated tests: ``` -1. Run *atdd or *automate -2. Run *test-review on generated tests +1. Run atdd or automate +2. Run test-review on generated tests 3. Fix critical issues 4. Commit tests ``` @@ -585,20 +585,20 @@ Don't try to fix everything at once. ## Related Guides -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Generate tests to review -- [How to Run Automate](/docs/how-to/workflows/run-automate.md) - Expand coverage to review -- [How to Run Trace](/docs/how-to/workflows/run-trace.md) - Coverage complements quality +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Generate tests to review +- [How to Run Automate](/docs/tea/how-to/workflows/run-automate.md) - Expand coverage to review +- [How to Run Trace](/docs/tea/how-to/workflows/run-trace.md) - Coverage complements quality ## Understanding the Concepts -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - What makes tests good -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Avoiding flakiness -- [Fixture Architecture](/docs/explanation/tea/fixture-architecture.md) - Reusable patterns +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - What makes tests good +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Avoiding flakiness +- [Fixture Architecture](/docs/tea/explanation/fixture-architecture.md) - Reusable patterns ## Reference -- [Command: *test-review](/docs/reference/tea/commands.md#test-review) - Full command reference -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Patterns TEA reviews against +- [Command: *test-review](/docs/tea/reference/commands.md#test-review) - Full command reference +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Patterns TEA reviews against --- diff --git a/docs/how-to/workflows/run-trace.md b/docs/tea/how-to/workflows/run-trace.md similarity index 93% rename from docs/how-to/workflows/run-trace.md rename to docs/tea/how-to/workflows/run-trace.md index bd352f39..d86c3723 100644 --- a/docs/how-to/workflows/run-trace.md +++ b/docs/tea/how-to/workflows/run-trace.md @@ -5,7 +5,7 @@ description: Map requirements to tests and make quality gate decisions using TEA # How to Run Trace with TEA -Use TEA's `*trace` workflow for requirements traceability and quality gate decisions. This is a two-phase workflow: Phase 1 analyzes coverage, Phase 2 makes the go/no-go decision. +Use TEA's `trace` workflow for requirements traceability and quality gate decisions. This is a two-phase workflow: Phase 1 analyzes coverage, Phase 2 makes the go/no-go decision. ## When to Use This @@ -34,7 +34,7 @@ Use TEA's `*trace` workflow for requirements traceability and quality gate decis ### 1. Run the Trace Workflow ``` -*trace +trace ``` ### 2. Specify Phase @@ -306,7 +306,7 @@ test('should update bio via API', async ({ apiRequest, authToken }) => { }); ``` -**Note:** `authToken` requires auth-session fixture setup. See [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md#auth-session). +**Note:** `authToken` requires auth-session fixture setup. See [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md#auth-session). ### 2. Add Avatar Upload Tests @@ -350,7 +350,7 @@ test('should accept valid image upload', async ({ request }) => { After reviewing traceability: 1. **Fix critical gaps** - Add tests for P0/P1 requirements -2. **Run *test-review** - Ensure new tests meet quality standards +2. **Run `test-review`** - Ensure new tests meet quality standards 3. **Run Phase 2** - Make gate decision after gaps addressed ``` @@ -369,7 +369,7 @@ After Phase 1 coverage analysis is complete, run Phase 2 for the gate decision. ### 7. Run Phase 2 ``` -*trace +trace ``` Select "Phase 2: Quality Gate Decision" @@ -405,12 +405,12 @@ traceability-matrix.md (from Phase 1) **Test Quality (Optional):** ``` -test-review.md (from *test-review) +test-review.md (from test-review) ``` **NFR Assessment (Optional):** ``` -nfr-assessment.md (from *nfr-assess) +nfr-assessment.md (from nfr-assess) ``` ### 10. Review Gate Decision @@ -586,7 +586,7 @@ TEA uses deterministic rules when decision_mode = "deterministic": 1. Add login tests (QA team, 2 days) 2. Fix SQL injection (backend team, 1 day) 3. Re-run security scan (DevOps, 1 hour) -4. Re-run *trace after fixes +4. Re-run trace after fixes **Cannot proceed until all blockers resolved.** ``` @@ -612,15 +612,15 @@ TEA uses deterministic rules when decision_mode = "deterministic": **Phase 3:** ``` After architecture complete: -1. Run *test-design (system-level) -2. Run *trace Phase 1 (baseline) +1. Run test-design (system-level) +2. Run trace Phase 1 (baseline) 3. Use for implementation-readiness gate ``` **Phase 4:** ``` After each epic/story: -1. Run *trace Phase 1 (refresh coverage) +1. Run trace Phase 1 (refresh coverage) 2. Identify gaps 3. Add missing tests ``` @@ -628,8 +628,8 @@ After each epic/story: **Release Gate:** ``` Before deployment: -1. Run *trace Phase 1 (final coverage check) -2. Run *trace Phase 2 (make gate decision) +1. Run trace Phase 1 (final coverage check) +2. Run trace Phase 2 (make gate decision) 3. Get approvals 4. Deploy (if PASS or WAIVED) ``` @@ -639,7 +639,7 @@ Before deployment: **Phase 2:** ``` Before planning new work: -1. Run *trace Phase 1 (establish baseline) +1. Run trace Phase 1 (establish baseline) 2. Understand existing coverage 3. Plan testing strategy ``` @@ -647,7 +647,7 @@ Before planning new work: **Phase 4:** ``` After each epic/story: -1. Run *trace Phase 1 (refresh) +1. Run trace Phase 1 (refresh) 2. Compare to baseline 3. Track coverage improvement ``` @@ -655,8 +655,8 @@ After each epic/story: **Release Gate:** ``` Before deployment: -1. Run *trace Phase 1 (final check) -2. Run *trace Phase 2 (gate decision) +1. Run trace Phase 1 (final check) +2. Run trace Phase 2 (gate decision) 3. Compare to baseline 4. Deploy if coverage maintained or improved ``` @@ -668,10 +668,10 @@ Before deployment: Don't wait until release gate: ``` -After Story 1: *trace Phase 1 (identify gaps early) -After Story 2: *trace Phase 1 (refresh) -After Story 3: *trace Phase 1 (refresh) -Before Release: *trace Phase 1 + Phase 2 (final gate) +After Story 1: trace Phase 1 (identify gaps early) +After Story 2: trace Phase 1 (refresh) +After Story 3: trace Phase 1 (refresh) +Before Release: trace Phase 1 + Phase 2 (final gate) ``` **Benefit:** Catch gaps early when they're cheap to fix. @@ -864,19 +864,19 @@ Result: PARTIAL coverage (3/4 criteria) ## Related Guides -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Provides requirements for traceability -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Quality scores feed gate -- [How to Run NFR Assessment](/docs/how-to/workflows/run-nfr-assess.md) - NFR status feeds gate +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Provides requirements for traceability +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Quality scores feed gate +- [How to Run NFR Assessment](/docs/tea/how-to/workflows/run-nfr-assess.md) - NFR status feeds gate ## Understanding the Concepts -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - Why P0 vs P3 matters -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Gate decisions in context +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - Why P0 vs P3 matters +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Gate decisions in context ## Reference -- [Command: *trace](/docs/reference/tea/commands.md#trace) - Full command reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config options +- [Command: *trace](/docs/tea/reference/commands.md#trace) - Full command reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config options --- diff --git a/docs/how-to/workflows/setup-ci.md b/docs/tea/how-to/workflows/setup-ci.md similarity index 94% rename from docs/how-to/workflows/setup-ci.md rename to docs/tea/how-to/workflows/setup-ci.md index 7f64ae56..2b7defb7 100644 --- a/docs/how-to/workflows/setup-ci.md +++ b/docs/tea/how-to/workflows/setup-ci.md @@ -5,7 +5,7 @@ description: Configure automated test execution with selective testing and burn- # How to Set Up CI Pipeline with TEA -Use TEA's `*ci` workflow to scaffold production-ready CI/CD configuration for automated test execution with selective testing, parallel sharding, and flakiness detection. +Use TEA's `ci` workflow to scaffold production-ready CI/CD configuration for automated test execution with selective testing, parallel sharding, and flakiness detection. ## When to Use This @@ -20,7 +20,7 @@ Use TEA's `*ci` workflow to scaffold production-ready CI/CD configuration for au - BMad Method installed - TEA agent available -- Test framework configured (run `*framework` first) +- Test framework configured (run `framework` first) - Tests written (have something to run in CI) - CI/CD platform access (GitHub Actions, GitLab CI, etc.) @@ -31,13 +31,13 @@ Use TEA's `*ci` workflow to scaffold production-ready CI/CD configuration for au Start a fresh chat and load TEA: ``` -*tea +tea ``` ### 2. Run the CI Workflow ``` -*ci +ci ``` ### 3. Select CI/CD Platform @@ -675,7 +675,7 @@ Speed up CI with caching: **Solution:** 1. Identify flaky tests (check which iteration fails) -2. Fix flaky tests using `*test-review` +2. Fix flaky tests using `test-review` 3. Re-run burn-in on specific files: ```bash npm run test:burn-in tests/flaky.spec.ts @@ -693,19 +693,19 @@ npm run test:burn-in tests/flaky.spec.ts ## Related Guides -- [How to Set Up Test Framework](/docs/how-to/workflows/setup-test-framework.md) - Run first -- [How to Run Test Review](/docs/how-to/workflows/run-test-review.md) - Audit CI tests -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) - Burn-in utility +- [How to Set Up Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) - Run first +- [How to Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) - Audit CI tests +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) - Burn-in utility ## Understanding the Concepts -- [Test Quality Standards](/docs/explanation/tea/test-quality-standards.md) - Why determinism matters -- [Network-First Patterns](/docs/explanation/tea/network-first-patterns.md) - Avoid CI flakiness +- [Test Quality Standards](/docs/tea/explanation/test-quality-standards.md) - Why determinism matters +- [Network-First Patterns](/docs/tea/explanation/network-first-patterns.md) - Avoid CI flakiness ## Reference -- [Command: *ci](/docs/reference/tea/commands.md#ci) - Full command reference -- [TEA Configuration](/docs/reference/tea/configuration.md) - CI-related config options +- [Command: *ci](/docs/tea/reference/commands.md#ci) - Full command reference +- [TEA Configuration](/docs/tea/reference/configuration.md) - CI-related config options --- diff --git a/docs/how-to/workflows/setup-test-framework.md b/docs/tea/how-to/workflows/setup-test-framework.md similarity index 93% rename from docs/how-to/workflows/setup-test-framework.md rename to docs/tea/how-to/workflows/setup-test-framework.md index 4d1dd1e5..c0875929 100644 --- a/docs/how-to/workflows/setup-test-framework.md +++ b/docs/tea/how-to/workflows/setup-test-framework.md @@ -3,7 +3,7 @@ title: "How to Set Up a Test Framework with TEA" description: How to set up a production-ready test framework using TEA --- -Use TEA's `*framework` workflow to scaffold a production-ready test framework for your project. +Use TEA's `framework` workflow to scaffold a production-ready test framework for your project. ## When to Use This @@ -27,7 +27,7 @@ Start a fresh chat and load the TEA (Test Architect) agent. ### 2. Run the Framework Workflow ``` -*framework +framework ``` ### 3. Answer TEA's Questions @@ -87,7 +87,7 @@ Configure in your IDE's MCP settings. - **Run only once per repository** — Framework setup is a one-time operation - **Run after architecture is complete** — Framework aligns with tech stack -- **Follow up with CI setup** — Run `*ci` to configure CI/CD pipeline +- **Follow up with CI setup** — Run `ci` to configure CI/CD pipeline ## Next Steps diff --git a/docs/reference/tea/commands.md b/docs/tea/reference/commands.md similarity index 69% rename from docs/reference/tea/commands.md rename to docs/tea/reference/commands.md index 6180bf13..1866991b 100644 --- a/docs/reference/tea/commands.md +++ b/docs/tea/reference/commands.md @@ -9,18 +9,18 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s ## Quick Index -- [*framework](#framework) - Scaffold test framework -- [*ci](#ci) - Setup CI/CD pipeline -- [*test-design](#test-design) - Risk-based test planning -- [*atdd](#atdd) - Acceptance TDD -- [*automate](#automate) - Test automation -- [*test-review](#test-review) - Quality audit -- [*nfr-assess](#nfr-assess) - NFR assessment -- [*trace](#trace) - Coverage traceability +- [`framework`](#framework) - Scaffold test framework +- [`ci`](#ci) - Setup CI/CD pipeline +- [`test-design`](#test-design) - Risk-based test planning +- [`atdd`](#atdd) - Acceptance TDD +- [`automate`](#automate) - Test automation +- [`test-review`](#test-review) - Quality audit +- [`nfr-assess`](#nfr-assess) - NFR assessment +- [`trace`](#trace) - Coverage traceability --- -## *framework +## framework **Purpose:** Scaffold production-ready test framework (Playwright or Cypress) @@ -37,11 +37,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s - `.env.example`, `.nvmrc` - Sample tests with best practices -**How-To Guide:** [Setup Test Framework](/docs/how-to/workflows/setup-test-framework.md) +**How-To Guide:** [Setup Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) --- -## *ci +## ci **Purpose:** Setup CI/CD pipeline with selective testing and burn-in @@ -59,11 +59,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s - Burn-in loops for flakiness detection - Secrets checklist -**How-To Guide:** [Setup CI Pipeline](/docs/how-to/workflows/setup-ci.md) +**How-To Guide:** [Setup CI Pipeline](/docs/tea/how-to/workflows/setup-ci.md) --- -## *test-design +## test-design **Purpose:** Risk-based test planning with coverage strategy @@ -108,11 +108,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s **MCP Enhancement:** Exploratory mode (live browser UI discovery) -**How-To Guide:** [Run Test Design](/docs/how-to/workflows/run-test-design.md) +**How-To Guide:** [Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) --- -## *atdd +## atdd **Purpose:** Generate failing acceptance tests BEFORE implementation (TDD red phase) @@ -130,11 +130,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s **MCP Enhancement:** Recording mode (for skeleton UI only - rare) -**How-To Guide:** [Run ATDD](/docs/how-to/workflows/run-atdd.md) +**How-To Guide:** [Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) --- -## *automate +## automate **Purpose:** Expand test coverage after implementation @@ -152,11 +152,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s **MCP Enhancement:** Healing + Recording modes (fix tests, verify selectors) -**How-To Guide:** [Run Automate](/docs/how-to/workflows/run-automate.md) +**How-To Guide:** [Run Automate](/docs/tea/how-to/workflows/run-automate.md) --- -## *test-review +## test-review **Purpose:** Audit test quality with 0-100 scoring @@ -180,11 +180,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s - Structure: 10 points - Performance: 10 points -**How-To Guide:** [Run Test Review](/docs/how-to/workflows/run-test-review.md) +**How-To Guide:** [Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) --- -## *nfr-assess +## nfr-assess **Purpose:** Validate non-functional requirements with evidence @@ -202,11 +202,11 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s - Mitigation plans - Gate decision inputs -**How-To Guide:** [Run NFR Assessment](/docs/how-to/workflows/run-nfr-assess.md) +**How-To Guide:** [Run NFR Assessment](/docs/tea/how-to/workflows/run-nfr-assess.md) --- -## *trace +## trace **Purpose:** Requirements traceability + quality gate decision @@ -232,7 +232,7 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s - P1 coverage: ≥90% for PASS, 80-89% for CONCERNS, <80% FAIL - Overall coverage: ≥80% required -**How-To Guide:** [Run Trace](/docs/how-to/workflows/run-trace.md) +**How-To Guide:** [Run Trace](/docs/tea/how-to/workflows/run-trace.md) --- @@ -240,36 +240,36 @@ Quick reference for all 8 TEA (Test Architect) workflows. For detailed step-by-s | Command | Phase | Frequency | Primary Output | |---------|-------|-----------|----------------| -| `*framework` | 3 | Once | Test infrastructure | -| `*ci` | 3 | Once | CI/CD pipeline | -| `*test-design` | 3, 4 | System + per epic | Test design doc | -| `*atdd` | 4 | Per story (optional) | Failing tests | -| `*automate` | 4 | Per story | Passing tests | -| `*test-review` | 4, Gate | Per epic/release | Quality report | -| `*nfr-assess` | 2, Gate | Per release | NFR assessment | -| `*trace` | 2, 4, Gate | Baseline + refresh + gate | Coverage matrix + decision | +| `framework` | 3 | Once | Test infrastructure | +| `ci` | 3 | Once | CI/CD pipeline | +| `test-design` | 3, 4 | System + per epic | Test design doc | +| `atdd` | 4 | Per story (optional) | Failing tests | +| `automate` | 4 | Per story | Passing tests | +| `test-review` | 4, Gate | Per epic/release | Quality report | +| `nfr-assess` | 2, Gate | Per release | NFR assessment | +| `trace` | 2, 4, Gate | Baseline + refresh + gate | Coverage matrix + decision | --- ## See Also **How-To Guides (Detailed Instructions):** -- [Setup Test Framework](/docs/how-to/workflows/setup-test-framework.md) -- [Setup CI Pipeline](/docs/how-to/workflows/setup-ci.md) -- [Run Test Design](/docs/how-to/workflows/run-test-design.md) -- [Run ATDD](/docs/how-to/workflows/run-atdd.md) -- [Run Automate](/docs/how-to/workflows/run-automate.md) -- [Run Test Review](/docs/how-to/workflows/run-test-review.md) -- [Run NFR Assessment](/docs/how-to/workflows/run-nfr-assess.md) -- [Run Trace](/docs/how-to/workflows/run-trace.md) +- [Setup Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) +- [Setup CI Pipeline](/docs/tea/how-to/workflows/setup-ci.md) +- [Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) +- [Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) +- [Run Automate](/docs/tea/how-to/workflows/run-automate.md) +- [Run Test Review](/docs/tea/how-to/workflows/run-test-review.md) +- [Run NFR Assessment](/docs/tea/how-to/workflows/run-nfr-assess.md) +- [Run Trace](/docs/tea/how-to/workflows/run-trace.md) **Explanation:** -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Complete TEA lifecycle -- [Engagement Models](/docs/explanation/tea/engagement-models.md) - When to use which workflows +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Complete TEA lifecycle +- [Engagement Models](/docs/tea/explanation/engagement-models.md) - When to use which workflows **Reference:** -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config options -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) - Pattern fragments +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config options +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) - Pattern fragments --- diff --git a/docs/reference/tea/configuration.md b/docs/tea/reference/configuration.md similarity index 87% rename from docs/reference/tea/configuration.md rename to docs/tea/reference/configuration.md index ae24d324..2d16c047 100644 --- a/docs/reference/tea/configuration.md +++ b/docs/tea/reference/configuration.md @@ -64,21 +64,21 @@ Enable Playwright Utils integration for production-ready fixtures and utilities. **Installer Prompt:** ``` Are you using playwright-utils (@seontechnologies/playwright-utils) in your project? -You must install packages yourself, or use test architect's *framework command. +You must install packages yourself, or use test architect's `framework` command. ``` **Purpose:** Enables TEA to: -- Include playwright-utils in `*framework` scaffold +- Include playwright-utils in `framework` scaffold - Generate tests using playwright-utils fixtures - Review tests against playwright-utils patterns - Configure CI with burn-in and selective testing utilities **Affects Workflows:** -- `*framework` - Includes playwright-utils imports and fixture examples -- `*atdd` - Uses fixtures like `apiRequest`, `authSession` in generated tests -- `*automate` - Leverages utilities for test patterns -- `*test-review` - Reviews against playwright-utils best practices -- `*ci` - Includes burn-in utility and selective testing +- `framework` - Includes playwright-utils imports and fixture examples +- `atdd` - Uses fixtures like `apiRequest`, `authSession` in generated tests +- `automate` - Leverages utilities for test patterns +- `test-review` - Reviews against playwright-utils best practices +- `ci` - Includes burn-in utility and selective testing **Example (Enable):** ```yaml @@ -96,7 +96,7 @@ npm install -D @seontechnologies/playwright-utils ``` **Related:** -- [Integrate Playwright Utils Guide](/docs/how-to/customization/integrate-playwright-utils.md) +- [Integrate Playwright Utils Guide](/docs/tea/how-to/customization/integrate-playwright-utils.md) - [Playwright Utils on npm](https://www.npmjs.com/package/@seontechnologies/playwright-utils) --- @@ -127,9 +127,9 @@ Would you like to enable MCP enhancements in Test Architect? - Visual debugging and healing **Affects Workflows:** -- `*test-design` - Enables exploratory mode (browser-based UI discovery) -- `*atdd` - Enables recording mode (verify selectors with live browser) -- `*automate` - Enables healing mode (fix tests with visual debugging) +- `test-design` - Enables exploratory mode (browser-based UI discovery) +- `atdd` - Enables recording mode (verify selectors with live browser) +- `automate` - Enables healing mode (fix tests with visual debugging) **MCP Servers Required:** @@ -173,8 +173,8 @@ tea_use_mcp_enhancements: false 3. Browser binaries installed (`npx playwright install`) **Related:** -- [Enable MCP Enhancements Guide](/docs/how-to/customization/enable-tea-mcp-enhancements.md) -- [TEA Overview - MCP Section](/docs/explanation/features/tea-overview.md#playwright-mcp-enhancements) +- [Enable MCP Enhancements Guide](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md) +- [TEA Overview - MCP Section](/docs/tea/explanation/tea-overview.md#playwright-mcp-enhancements) - [Playwright MCP on npm](https://www.npmjs.com/package/@playwright/mcp) --- @@ -197,14 +197,14 @@ output_folder: _bmad-output ``` **TEA Output Files:** -- `test-design-architecture.md` + `test-design-qa.md` (from *test-design system-level - TWO documents) -- `test-design-epic-N.md` (from *test-design epic-level) -- `test-review.md` (from *test-review) -- `traceability-matrix.md` (from *trace Phase 1) -- `gate-decision-{gate_type}-{story_id}.md` (from *trace Phase 2) -- `nfr-assessment.md` (from *nfr-assess) -- `automation-summary.md` (from *automate) -- `atdd-checklist-{story_id}.md` (from *atdd) +- `test-design-architecture.md` + `test-design-qa.md` (from `test-design` system-level - TWO documents) +- `test-design-epic-N.md` (from `test-design` epic-level) +- `test-review.md` (from `test-review`) +- `traceability-matrix.md` (from `trace` Phase 1) +- `gate-decision-{gate_type}-{story_id}.md` (from `trace` Phase 2) +- `nfr-assessment.md` (from `nfr-assess`) +- `automation-summary.md` (from `automate`) +- `atdd-checklist-{story_id}.md` (from `atdd`) --- @@ -561,7 +561,7 @@ tea_use_mcp_enhancements: true # Recommended **Why recommended:** - Playwright Utils: Production-ready fixtures and utilities - MCP enhancements: Live browser verification, visual debugging -- Together: The three-part stack (see [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md)) +- Together: The three-part stack (see [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md)) **Prerequisites:** ```bash @@ -660,18 +660,18 @@ document_output_language: english ## See Also ### How-To Guides -- [Set Up Test Framework](/docs/how-to/workflows/setup-test-framework.md) -- [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) -- [Enable MCP Enhancements](/docs/how-to/customization/enable-tea-mcp-enhancements.md) +- [Set Up Test Framework](/docs/tea/how-to/workflows/setup-test-framework.md) +- [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) +- [Enable MCP Enhancements](/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md) ### Reference -- [TEA Command Reference](/docs/reference/tea/commands.md) -- [Knowledge Base Index](/docs/reference/tea/knowledge-base.md) -- [Glossary](/docs/reference/glossary/index.md) +- [TEA Command Reference](/docs/tea/reference/commands.md) +- [Knowledge Base Index](/docs/tea/reference/knowledge-base.md) +- [Glossary](/docs/tea/glossary/index.md) ### Explanation -- [TEA Overview](/docs/explanation/features/tea-overview.md) -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) +- [TEA Overview](/docs/tea/explanation/tea-overview.md) +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) --- diff --git a/docs/reference/tea/knowledge-base.md b/docs/tea/reference/knowledge-base.md similarity index 92% rename from docs/reference/tea/knowledge-base.md rename to docs/tea/reference/knowledge-base.md index 606ff96e..661e911e 100644 --- a/docs/reference/tea/knowledge-base.md +++ b/docs/tea/reference/knowledge-base.md @@ -19,7 +19,7 @@ Instead of asking AI to "write good tests" every time, TEA: **Example:** ``` -User runs: *test-design +User runs: `test-design` TEA reads tea-index.csv: - Loads: test-quality.md, test-priorities-matrix.md, risk-governance.md @@ -31,7 +31,7 @@ Result: Focused context, consistent quality standards ## How Knowledge Loading Works ### 1. Workflow Trigger -User runs a TEA workflow (e.g., `*test-design`) +User runs a TEA workflow (e.g., `test-design`) ### 2. Manifest Lookup TEA reads `src/bmm/testarch/tea-index.csv`: @@ -60,7 +60,7 @@ Core patterns for test infrastructure and fixture composition. | [playwright-config](../../../src/bmm/testarch/knowledge/playwright-config.md) | Environment switching, timeout standards, artifact outputs | Configuration, environments, CI | | [fixtures-composition](../../../src/bmm/testarch/knowledge/fixtures-composition.md) | mergeTests composition patterns for combining utilities | Fixture merging, utility composition | -**Used in:** `*framework`, `*test-design`, `*atdd`, `*automate`, `*test-review` +**Used in:** `framework`, `test-design`, `atdd`, `automate`, `test-review` --- @@ -74,7 +74,7 @@ Patterns for test data generation, authentication, and setup. | [email-auth](../../../src/bmm/testarch/knowledge/email-auth.md) | Magic link extraction, state preservation, negative flows | Authentication, email testing | | [auth-session](../../../src/bmm/testarch/knowledge/auth-session.md) | Token persistence, multi-user, API/browser authentication | Auth patterns, session management | -**Used in:** `*framework`, `*atdd`, `*automate`, `*test-review` +**Used in:** `framework`, `atdd`, `automate`, `test-review` --- @@ -89,7 +89,7 @@ Network interception, error handling, and reliability patterns. | [error-handling](../../../src/bmm/testarch/knowledge/error-handling.md) | Scoped exception handling, retry validation, telemetry logging | Error patterns, resilience | | [network-error-monitor](../../../src/bmm/testarch/knowledge/network-error-monitor.md) | HTTP 4xx/5xx detection for UI tests | Error detection, monitoring | -**Used in:** `*atdd`, `*automate`, `*test-review` +**Used in:** `atdd`, `automate`, `test-review` --- @@ -103,7 +103,7 @@ CI/CD patterns, burn-in testing, and selective test execution. | [burn-in](../../../src/bmm/testarch/knowledge/burn-in.md) | Smart test selection, git diff for CI optimization | Test selection, performance | | [selective-testing](../../../src/bmm/testarch/knowledge/selective-testing.md) | Tag/grep usage, spec filters, diff-based runs | Test filtering, optimization | -**Used in:** `*ci`, `*test-review` +**Used in:** `ci`, `test-review` --- @@ -119,7 +119,7 @@ Test quality standards, test level selection, and TDD patterns. | [test-healing-patterns](../../../src/bmm/testarch/knowledge/test-healing-patterns.md) | Common failure patterns and automated fixes | Debugging, healing, fixes | | [component-tdd](../../../src/bmm/testarch/knowledge/component-tdd.md) | Red→green→refactor workflow, provider isolation | TDD, component testing | -**Used in:** `*test-design`, `*atdd`, `*automate`, `*test-review`, `*trace` +**Used in:** `test-design`, `atdd`, `automate`, `test-review`, `trace` --- @@ -133,7 +133,7 @@ Risk assessment, governance, and gate decision frameworks. | [probability-impact](../../../src/bmm/testarch/knowledge/probability-impact.md) | Probability × impact scale for scoring matrix | Risk scoring, impact analysis | | [nfr-criteria](../../../src/bmm/testarch/knowledge/nfr-criteria.md) | Security, performance, reliability, maintainability status | NFRs, compliance, enterprise | -**Used in:** `*test-design`, `*nfr-assess`, `*trace` +**Used in:** `test-design`, `nfr-assess`, `trace` --- @@ -147,7 +147,7 @@ Selector resilience, race condition debugging, and visual debugging. | [timing-debugging](../../../src/bmm/testarch/knowledge/timing-debugging.md) | Race condition identification and deterministic fixes | Race conditions, timing issues | | [visual-debugging](../../../src/bmm/testarch/knowledge/visual-debugging.md) | Trace viewer usage, artifact expectations | Debugging, trace viewer, artifacts | -**Used in:** `*atdd`, `*automate`, `*test-review` +**Used in:** `atdd`, `automate`, `test-review` --- @@ -161,7 +161,7 @@ Feature flag testing, contract testing, and API testing patterns. | [contract-testing](../../../src/bmm/testarch/knowledge/contract-testing.md) | Pact publishing, provider verification, resilience | Contract testing, Pact | | [api-testing-patterns](../../../src/bmm/testarch/knowledge/api-testing-patterns.md) | Pure API patterns without browser | API testing, backend testing | -**Used in:** `*test-design`, `*atdd`, `*automate` +**Used in:** `test-design`, `atdd`, `automate` --- @@ -183,7 +183,7 @@ Patterns for using `@seontechnologies/playwright-utils` package (9 utilities). **Note:** `fixtures-composition` is listed under Architecture & Fixtures (general Playwright `mergeTests` pattern, applies to all fixtures). -**Used in:** `*framework` (if `tea_use_playwright_utils: true`), `*atdd`, `*automate`, `*test-review`, `*ci` +**Used in:** `framework` (if `tea_use_playwright_utils: true`), `atdd`, `automate`, `test-review`, `ci` **Official Docs:** @@ -219,7 +219,7 @@ risk-governance,Risk Governance,Risk scoring and gate decisions,risk;governance, Each TEA workflow loads specific fragments: -### *framework +### `framework` **Key Fragments:** - fixture-architecture.md - playwright-config.md @@ -231,7 +231,7 @@ Each TEA workflow loads specific fragments: --- -### *test-design +### `test-design` **Key Fragments:** - test-quality.md - test-priorities-matrix.md @@ -245,7 +245,7 @@ Each TEA workflow loads specific fragments: --- -### *atdd +### `atdd` **Key Fragments:** - test-quality.md - component-tdd.md @@ -262,7 +262,7 @@ Each TEA workflow loads specific fragments: --- -### *automate +### `automate` **Key Fragments:** - test-quality.md - test-levels-framework.md @@ -279,7 +279,7 @@ Each TEA workflow loads specific fragments: --- -### *test-review +### `test-review` **Key Fragments:** - test-quality.md - test-healing-patterns.md @@ -296,7 +296,7 @@ Each TEA workflow loads specific fragments: --- -### *ci +### `ci` **Key Fragments:** - ci-burn-in.md - burn-in.md @@ -307,7 +307,7 @@ Each TEA workflow loads specific fragments: --- -### *nfr-assess +### `nfr-assess` **Key Fragments:** - nfr-criteria.md - risk-governance.md @@ -317,7 +317,7 @@ Each TEA workflow loads specific fragments: --- -### *trace +### `trace` **Key Fragments:** - test-priorities-matrix.md - risk-governance.md @@ -331,9 +331,9 @@ Each TEA workflow loads specific fragments: ## Related -- [TEA Overview](/docs/explanation/features/tea-overview.md) - How knowledge base fits in TEA -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - Context engineering philosophy -- [TEA Command Reference](/docs/reference/tea/commands.md) - Workflows that use fragments +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - How knowledge base fits in TEA +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - Context engineering philosophy +- [TEA Command Reference](/docs/tea/reference/commands.md) - Workflows that use fragments --- diff --git a/docs/tutorials/getting-started/tea-lite-quickstart.md b/docs/tea/tutorials/tea-lite-quickstart.md similarity index 82% rename from docs/tutorials/getting-started/tea-lite-quickstart.md rename to docs/tea/tutorials/tea-lite-quickstart.md index 34c9dda4..b6e7c108 100644 --- a/docs/tutorials/getting-started/tea-lite-quickstart.md +++ b/docs/tea/tutorials/tea-lite-quickstart.md @@ -3,7 +3,7 @@ title: "Getting Started with Test Architect" description: Learn Test Architect fundamentals by generating and running tests for an existing demo app in 30 minutes --- -Welcome! **Test Architect (TEA) Lite** is the simplest way to get started with TEA - just use `*automate` to generate tests for existing features. Perfect for beginners who want to learn TEA fundamentals quickly. +Welcome! **Test Architect (TEA) Lite** is the simplest way to get started with TEA - just use the `automate` workflow (e.g., `/automate` in Claude Code) to generate tests for existing features. Perfect for beginners who want to learn TEA fundamentals quickly. ## What You'll Build @@ -19,14 +19,14 @@ By the end of this 30-minute tutorial, you'll have: ::: :::tip[Quick Path] -Load TEA (`*tea`) → scaffold framework (`*framework`) → create test plan (`*test-design`) → generate tests (`*automate`) → run with `npx playwright test`. +Load TEA (`tea`) → scaffold framework (`framework`) → create test plan (`test-design`) → generate tests (`automate`) → run with `npx playwright test`. ::: ## TEA Approaches Explained Before we start, understand the three ways to use TEA: -- **TEA Lite** (this tutorial): Beginner using just `*automate` to test existing features +- **TEA Lite** (this tutorial): Beginner using just the `automate` workflow to test existing features - **TEA Solo**: Using TEA standalone without full BMad Method integration - **TEA Integrated**: Full BMad Method with all TEA workflows across phases @@ -68,7 +68,7 @@ BMad is now installed! You'll see a `_bmad/` folder in your project. Start a new chat with your AI assistant (Claude, etc.) and type: ``` -*tea +tea ``` This loads the Test Architect agent. You'll see TEA's menu with available workflows. @@ -78,7 +78,7 @@ This loads the Test Architect agent. You'll see TEA's menu with available workfl In your chat, run: ``` -*framework +framework ``` TEA will ask you questions: @@ -120,7 +120,7 @@ Test design is where TEA shines - risk-based planning before writing tests. In your chat with TEA, run: ``` -*test-design +test-design ``` **Q: System-level or epic-level?** @@ -161,7 +161,7 @@ Now the magic happens - TEA generates tests based on your test design. In your chat with TEA, run: ``` -*automate +automate ``` **Q: What are you testing?** @@ -280,7 +280,7 @@ test('should mark todo as complete', async ({ page, apiRequest }) => { - Built-in schema validation - Cleaner, more maintainable code -See [Integrate Playwright Utils](/docs/how-to/customization/integrate-playwright-utils.md) to enable this. +See [Integrate Playwright Utils](/docs/tea/how-to/customization/integrate-playwright-utils.md) to enable this. ## Step 4: Run and Validate (5 minutes) @@ -319,9 +319,9 @@ Opens a beautiful HTML report showing: ### What Just Happened? You used **TEA Lite** to: -1. Scaffold a production-ready test framework (`*framework`) -2. Create a risk-based test plan (`*test-design`) -3. Generate comprehensive tests (`*automate`) +1. Scaffold a production-ready test framework (`framework`) +2. Create a risk-based test plan (`test-design`) +3. Generate comprehensive tests (`automate`) 4. Run tests against an existing application All in 30 minutes! @@ -334,10 +334,10 @@ Congratulations! You've completed the TEA Lite tutorial. You learned: | Command | Purpose | | -------------- | ------------------------------------ | -| `*tea` | Load the TEA agent | -| `*framework` | Scaffold test infrastructure | -| `*test-design` | Risk-based test planning | -| `*automate` | Generate tests for existing features | +| `tea` | Load the TEA agent | +| `framework` | Scaffold test infrastructure | +| `test-design` | Risk-based test planning | +| `automate` | Generate tests for existing features | ### TEA Principles - **Risk-based testing** - Depth scales with impact (P0 vs P3) @@ -346,44 +346,44 @@ Congratulations! You've completed the TEA Lite tutorial. You learned: - **Production-ready from day one** - Not toy examples :::tip[Key Takeaway] -TEA Lite (just `*automate`) is perfect for beginners learning TEA fundamentals, testing existing applications, quick test coverage expansion, and teams wanting fast results. +TEA Lite (just `automate`) is perfect for beginners learning TEA fundamentals, testing existing applications, quick test coverage expansion, and teams wanting fast results. ::: ## Understanding ATDD vs Automate -This tutorial used `*automate` to generate tests for **existing features** (tests pass immediately). +This tutorial used the `automate` workflow to generate tests for **existing features** (tests pass immediately). -**When to use `*automate`:** +**When to use `automate`:** - Feature already exists - Want to add test coverage - Tests should pass on first run -**When to use `*atdd` (Acceptance Test-Driven Development):** +**When to use `atdd` (Acceptance Test-Driven Development):** - Feature doesn't exist yet (Test-Driven Development workflow) - Want failing tests BEFORE implementation - Following red → green → refactor cycle -See [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) for the test-drive development (TDD) approach. +See [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) for the test-drive development (TDD) approach. ## Next Steps ### Level Up Your TEA Skills **How-To Guides** (task-oriented): -- [How to Run Test Design](/docs/how-to/workflows/run-test-design.md) - Deep dive into risk assessment -- [How to Run ATDD](/docs/how-to/workflows/run-atdd.md) - Generate failing tests first (TDD) -- [How to Set Up CI Pipeline](/docs/how-to/workflows/setup-ci.md) - Automate test execution -- [How to Review Test Quality](/docs/how-to/workflows/run-test-review.md) - Audit test quality +- [How to Run Test Design](/docs/tea/how-to/workflows/run-test-design.md) - Deep dive into risk assessment +- [How to Run ATDD](/docs/tea/how-to/workflows/run-atdd.md) - Generate failing tests first (TDD) +- [How to Set Up CI Pipeline](/docs/tea/how-to/workflows/setup-ci.md) - Automate test execution +- [How to Review Test Quality](/docs/tea/how-to/workflows/run-test-review.md) - Audit test quality **Explanation** (understanding-oriented): -- [TEA Overview](/docs/explanation/features/tea-overview.md) - Complete TEA capabilities -- [Testing as Engineering](/docs/explanation/philosophy/testing-as-engineering.md) - **Why TEA exists** (problem + solution) -- [Risk-Based Testing](/docs/explanation/tea/risk-based-testing.md) - How risk scoring works +- [TEA Overview](/docs/tea/explanation/tea-overview.md) - Complete TEA capabilities +- [Testing as Engineering](/docs/tea/explanation/testing-as-engineering.md) - **Why TEA exists** (problem + solution) +- [Risk-Based Testing](/docs/tea/explanation/risk-based-testing.md) - How risk scoring works **Reference** (quick lookup): -- [TEA Command Reference](/docs/reference/tea/commands.md) - All 8 TEA workflows -- [TEA Configuration](/docs/reference/tea/configuration.md) - Config options -- [Glossary](/docs/reference/glossary/index.md) - TEA terminology +- [TEA Command Reference](/docs/tea/reference/commands.md) - All 8 TEA workflows +- [TEA Configuration](/docs/tea/reference/configuration.md) - Config options +- [Glossary](/docs/tea/glossary/index.md) - TEA terminology ### Try TEA Solo @@ -392,14 +392,14 @@ Ready for standalone usage without full BMad Method? Use TEA Solo: - Bring your own requirements - Use on non-BMad projects -See [TEA Overview](/docs/explanation/features/tea-overview.md) for engagement models. +See [TEA Overview](/docs/tea/explanation/tea-overview.md) for engagement models. ### Go Full TEA Integrated Want the complete quality operating model? Try TEA Integrated with BMad Method: - Phase 2: Planning with non-functional requirements (NFR) assessment - Phase 3: Architecture testability review -- Phase 4: Per-epic test design → ATDD → automate +- Phase 4: Per-epic test design → `atdd` → `automate` - Release Gate: Coverage traceability and gate decisions See [BMad Method Documentation](/) for the full workflow. diff --git a/docs/tutorials/getting-started/getting-started-bmadv6.md b/docs/tutorials/getting-started.md similarity index 56% rename from docs/tutorials/getting-started/getting-started-bmadv6.md rename to docs/tutorials/getting-started.md index 70be60aa..bd29888d 100644 --- a/docs/tutorials/getting-started/getting-started-bmadv6.md +++ b/docs/tutorials/getting-started.md @@ -1,12 +1,8 @@ --- -title: "Getting Started with the BMad Method" +title: "Getting Started" description: Install BMad and build your first project --- -**Upgrading from previous versions?** See the [Upgrade Guide](/docs/how-to/installation/upgrade-to-v6.md) instead. - ---- - Build software faster using AI-powered workflows with specialized agents that guide you through planning, architecture, and implementation. ## What You'll Learn @@ -25,10 +21,9 @@ Build software faster using AI-powered workflows with specialized agents that gu :::tip[Quick Path] **Install** → `npx bmad-method@alpha install` -**Initialize** → Load Analyst agent, run `workflow-init` **Plan** → PM creates PRD, Architect creates architecture **Build** → SM manages sprints, DEV implements stories -**Always use fresh chats** for each workflow to avoid context issues. +**Fresh chats** for each workflow to avoid context issues. ::: ## Understanding BMad @@ -42,7 +37,7 @@ BMad helps you build software through guided workflows with specialized AI agent | 3 | Solutioning | Design architecture *(BMad Method/Enterprise only)* | | 4 | Implementation | Build epic by epic, story by story | -**[Open the Interactive Workflow Guide](/workflow-guide)** to explore phases, agents, and outputs for your chosen track. +**[Open the Workflow Map](/docs/reference/workflow-map.md)** to explore phases, workflows, and context management. Based on your project's complexity, BMad offers three planning tracks: @@ -64,68 +59,38 @@ Open a terminal in your project directory and run: npx bmad-method@alpha install ``` -The interactive installer guides you through setup and creates a `_bmad/` folder with all agents and workflows. +When prompted to select modules, choose **BMad Method**. -Verify your installation: +The installer creates two folders: +- `_bmad/` — agents, workflows, tasks, and configuration +- `_bmad-output/` — empty for now, but this is where your artifacts will be saved -``` -your-project/ -├── _bmad/ -│ ├── bmm/ # Method module -│ │ ├── agents/ # Agent files -│ │ ├── workflows/ # Workflow files -│ │ └── config.yaml # Module config -│ └── core/ # Core utilities -├── _bmad-output/ # Generated artifacts (created later) -└── .claude/ # IDE configuration (if using Claude Code) -``` - -:::tip[Troubleshooting] -Having issues? See [Install BMad](/docs/how-to/installation/install-bmad.md) for common solutions. -::: - -Open your AI IDE in the project folder. From here, you can run `/bmad-help` anytime to see what to do next — or ask it a question like `/bmad-help How should I build a web app for XYZ?` - -## Step 1: Initialize Your Project - -Load the **Analyst agent** in your IDE, wait for the menu, then run `workflow-init`. - -:::note[How to Load Agents] -Type `/` in your IDE and use autocomplete. Not sure what's available? Start with `/bmad` to see all agents and workflows. -::: - -The workflow asks you to describe your project, whether it's new or existing, and the general complexity. Based on your description, it recommends a planning track. - -Once you confirm, the workflow creates `bmm-workflow-status.yaml` to track your progress through all phases. +Open your AI IDE in the project folder. Run the `help` workflow (`/bmad-help` on most platforms) to see what to do next — it detects what you've completed and recommends the next step. :::caution[Fresh Chats] Always start a fresh chat for each workflow. This prevents context limitations from causing issues. ::: -## Step 2: Create Your Plan +## Step 1: Create Your Plan -After initialization, work through phases 1-3. **Use fresh chats for each workflow.** - -:::tip[Check Your Status] -Unsure what's next? Load any agent and ask for `workflow-status`. It tells you the next recommended or required workflow. -::: +Work through phases 1-3. **Use fresh chats for each workflow.** ### Phase 1: Analysis (Optional) All workflows in this phase are optional: -- **brainstorm-project** — Guided ideation +- **brainstorming** — Guided ideation - **research** — Market and technical research -- **product-brief** — Recommended foundation document +- **create-product-brief** — Recommended foundation document ### Phase 2: Planning (Required) **For BMad Method and Enterprise tracks:** 1. Load the **PM agent** in a new chat -2. Run the PRD workflow: `*prd` +2. Run the `prd` workflow 3. Output: `PRD.md` **For Quick Flow track:** -- Use `tech-spec` instead of PRD, then skip to implementation +- Use the `quick-spec` workflow instead of PRD, then skip to implementation :::note[UX Design (Optional)] If your project has a user interface, load the **UX-Designer agent** and run the UX design workflow after creating your PRD. @@ -150,10 +115,10 @@ Epics and stories are now created *after* architecture. This produces better qua **Implementation Readiness Check** *(Highly Recommended)* 1. Load the **Architect agent** in a new chat -2. Run `implementation-readiness` +2. Run `check-implementation-readiness` 3. Validates cohesion across all planning documents -## Step 3: Build Your Project +## Step 2: Build Your Project Once planning is complete, move to implementation. **Each workflow should run in a fresh chat.** @@ -169,8 +134,7 @@ For each story, repeat this cycle with fresh chats: | ---- | ----- | -------------- | ------------------------------------- | | 1 | SM | `create-story` | Create story file from epic | | 2 | DEV | `dev-story` | Implement the story | -| 3 | TEA | `automate` | Generate guardrail tests *(optional)* | -| 4 | DEV | `code-review` | Quality validation *(recommended)* | +| 3 | DEV | `code-review` | Quality validation *(recommended)* | After completing all stories in an epic, load the **SM agent** and run `retrospective`. @@ -192,25 +156,23 @@ your-project/ │ ├── PRD.md # Your requirements document │ ├── architecture.md # Technical decisions │ ├── epics/ # Epic and story files -│ ├── bmm-workflow-status.yaml # Phase progress tracking │ └── sprint-status.yaml # Sprint tracking └── ... ``` ## Quick Reference -| Command | Agent | Purpose | -| --------------------------- | --------- | ------------------------------------ | -| `*workflow-init` | Analyst | Initialize a new project | -| `*workflow-status` | Any | Check progress and next steps | -| `*prd` | PM | Create Product Requirements Document | -| `*create-architecture` | Architect | Create architecture document | -| `*create-epics-and-stories` | PM | Break down PRD into epics | -| `*implementation-readiness` | Architect | Validate planning cohesion | -| `*sprint-planning` | SM | Initialize sprint tracking | -| `*create-story` | SM | Create a story file | -| `*dev-story` | DEV | Implement a story | -| `*code-review` | DEV | Review implemented code | +| Workflow | Agent | Purpose | +| ---------------------------------- | --------- | ------------------------------------ | +| `help` | Any | Get guidance on what to do next | +| `prd` | PM | Create Product Requirements Document | +| `create-architecture` | Architect | Create architecture document | +| `create-epics-and-stories` | PM | Break down PRD into epics | +| `check-implementation-readiness` | Architect | Validate planning cohesion | +| `sprint-planning` | SM | Initialize sprint tracking | +| `create-story` | SM | Create a story file | +| `dev-story` | DEV | Implement a story | +| `code-review` | DEV | Review implemented code | ## Common Questions @@ -221,26 +183,23 @@ Only for BMad Method and Enterprise tracks. Quick Flow skips from tech-spec to i Yes. The SM agent has a `correct-course` workflow for handling scope changes. **What if I want to brainstorm first?** -Load the Analyst agent and run `brainstorm-project` before `workflow-init`. +Load the Analyst agent and run `brainstorming` before starting your PRD. -**Can I skip workflow-init and workflow-status?** -Yes, once you learn the flow. Use the Quick Reference to go directly to needed workflows. +**Do I need to follow a strict order?** +Not strictly. Once you learn the flow, you can run workflows directly using the Quick Reference above. ## Getting Help - **During workflows** — Agents guide you with questions and explanations - **Community** — [Discord](https://discord.gg/gk8jAdXWmj) (#bmad-method-help, #report-bugs-and-issues) -- **Documentation** — [BMM Workflow Reference](/docs/reference/workflows/index.md) -- **Video tutorials** — [BMad Code YouTube](https://www.youtube.com/@BMadCode) +- **Stuck?** — Run `help` to see what to do next ## Key Takeaways :::tip[Remember These] -- **Always use fresh chats** — Load agents in new chats for each workflow -- **Let workflow-status guide you** — Ask any agent for status when unsure -- **Track matters** — Quick Flow uses tech-spec; Method/Enterprise need PRD and architecture -- **Tracking is automatic** — Status files update themselves -- **Agents are flexible** — Use menu numbers, shortcuts (`*prd`), or natural language +- **Always use fresh chats** — Start a new chat for each workflow +- **Track matters** — Quick Flow uses quick-spec; Method/Enterprise need PRD and architecture +- **Use `help` when stuck** — It detects your progress and suggests next steps ::: -Ready to start? Install BMad, load the Analyst, run `workflow-init`, and let the agents guide you. +Ready to start? Install BMad and let the agents guide you through your first project. diff --git a/docs/tutorials/getting-started/images/workflow-method-greenfield.excalidraw b/docs/tutorials/getting-started/images/workflow-method-greenfield.excalidraw deleted file mode 100644 index c7acf4f5..00000000 --- a/docs/tutorials/getting-started/images/workflow-method-greenfield.excalidraw +++ /dev/null @@ -1,5034 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", - "elements": [ - { - "id": "title", - "type": "text", - "x": 284.6321356748704, - "y": 20, - "width": 673.7520141601562, - "height": 37.15738334525602, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 29.725906676204815, - "fontFamily": 1, - "text": "BMad Method Workflow - Standard Greenfield", - "textAlign": "center", - "verticalAlign": "top", - "locked": false, - "version": 67, - "versionNonce": 1431078555, - "index": "a0", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522299028, - "link": null, - "containerId": null, - "originalText": "BMad Method Workflow - Standard Greenfield", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "start-ellipse", - "type": "ellipse", - "x": 60, - "y": 80, - "width": 120, - "height": 60, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "#e3f2fd", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "start-group" - ], - "boundElements": [ - { - "type": "text", - "id": "start-text" - }, - { - "type": "arrow", - "id": "arrow-start-discovery" - } - ], - "locked": false, - "version": 2, - "versionNonce": 1364787547, - "index": "a1", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1763522171079, - "link": null - }, - { - "id": "start-text", - "type": "text", - "x": 93, - "y": 98, - "width": 54, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "start-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Start", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "start-ellipse", - "locked": false, - "version": 2, - "versionNonce": 1303811541, - "index": "a2", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "originalText": "Start", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "phase1-header", - "type": "text", - "x": 13.742901708014983, - "y": 180.0057616006372, - "width": 200, - "height": 30, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 24, - "fontFamily": 1, - "text": "PHASE 1", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 18, - "versionNonce": 1987415189, - "index": "a3", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522322404, - "link": null, - "containerId": null, - "originalText": "PHASE 1", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "phase1-subtitle", - "type": "text", - "x": 140.26189010000303, - "y": 168.98316506386624, - "width": 75.31195068359375, - "height": 40, - "angle": 0, - "strokeColor": "#666666", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Discovery\n(Optional)", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 225, - "versionNonce": 1515322069, - "index": "a4", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522324513, - "link": null, - "containerId": null, - "originalText": "Discovery\n(Optional)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-start-discovery", - "type": "arrow", - "x": 120, - "y": 140, - "width": 0, - "height": 100, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "start-ellipse", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "decision-discovery", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 2116462235, - "index": "a5", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "decision-discovery", - "type": "diamond", - "x": 40, - "y": 240, - "width": 160, - "height": 100, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-discovery-group" - ], - "boundElements": [ - { - "type": "text", - "id": "decision-discovery-text" - }, - { - "type": "arrow", - "id": "arrow-start-discovery" - }, - { - "type": "arrow", - "id": "arrow-discovery-yes" - }, - { - "type": "arrow", - "id": "arrow-discovery-no" - } - ], - "locked": false, - "version": 2, - "versionNonce": 1508959381, - "index": "a6", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1763522171079, - "link": null - }, - { - "id": "decision-discovery-text", - "type": "text", - "x": 55, - "y": 265, - "width": 130, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-discovery-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Include\nDiscovery?", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "decision-discovery", - "locked": false, - "version": 2, - "versionNonce": 627907387, - "index": "a7", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "originalText": "Include\nDiscovery?", - "autoResize": true, - "lineHeight": 1.5625 - }, - { - "id": "arrow-discovery-yes", - "type": "arrow", - "x": 120, - "y": 340, - "width": 0, - "height": 40, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-discovery", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-brainstorm", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 40 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 133270005, - "index": "a8", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-yes-discovery", - "type": "text", - "x": 130, - "y": 350, - "width": 30, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Yes", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 1362885595, - "index": "a9", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "containerId": null, - "originalText": "Yes", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-brainstorm", - "type": "rectangle", - "x": 40, - "y": 380, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#00acc1", - "backgroundColor": "#b2ebf2", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-brainstorm-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-brainstorm-text" - }, - { - "type": "arrow", - "id": "arrow-discovery-yes" - }, - { - "type": "arrow", - "id": "arrow-brainstorm-research" - }, - { - "id": "jv0rnlK2D9JKIGTO7pUtT", - "type": "arrow" - } - ], - "locked": false, - "version": 3, - "versionNonce": 115423290, - "index": "aA", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191341773, - "link": null - }, - { - "id": "proc-brainstorm-text", - "type": "text", - "x": 50, - "y": 395, - "width": 140, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-brainstorm-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Brainstorm\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-brainstorm", - "locked": false, - "version": 2, - "versionNonce": 765839483, - "index": "aB", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "originalText": "Brainstorm\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-brainstorm-research", - "type": "arrow", - "x": 120, - "y": 460.45161416125165, - "width": 0, - "height": 29.096771677496633, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-brainstorm", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-research", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 29.096771677496633 - ] - ], - "lastCommittedPoint": null, - "version": 3, - "versionNonce": 828709094, - "index": "aC", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191023838, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-research", - "type": "rectangle", - "x": 40, - "y": 490, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#00acc1", - "backgroundColor": "#b2ebf2", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-research-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-research-text" - }, - { - "type": "arrow", - "id": "arrow-brainstorm-research" - }, - { - "type": "arrow", - "id": "arrow-research-brief" - }, - { - "id": "RF10FfKbmG72P77I2IoP4", - "type": "arrow" - } - ], - "locked": false, - "version": 5, - "versionNonce": 987493562, - "index": "aD", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191042826, - "link": null - }, - { - "id": "proc-research-text", - "type": "text", - "x": 78.26604461669922, - "y": 505, - "width": 83.46791076660156, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-research-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Research\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-research", - "locked": false, - "version": 5, - "versionNonce": 92329914, - "index": "aE", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191023838, - "link": null, - "originalText": "Research\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-research-brief", - "type": "arrow", - "x": 120.00000000000001, - "y": 570.4516141612517, - "width": 0, - "height": 29.09677167749669, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-research", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-product-brief", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 29.09677167749669 - ] - ], - "lastCommittedPoint": null, - "version": 4, - "versionNonce": 1012730918, - "index": "aF", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191023838, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-product-brief", - "type": "rectangle", - "x": 40, - "y": 600, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#00acc1", - "backgroundColor": "#b2ebf2", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-product-brief-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-product-brief-text" - }, - { - "type": "arrow", - "id": "arrow-research-brief" - }, - { - "id": "arrow-phase1-to-phase2", - "type": "arrow" - } - ], - "locked": false, - "version": 6, - "versionNonce": 1568298662, - "index": "aG", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190985483, - "link": null - }, - { - "id": "proc-product-brief-text", - "type": "text", - "x": 72.69404602050781, - "y": 615, - "width": 94.61190795898438, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-product-brief-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Product Brief\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-product-brief", - "locked": false, - "version": 3, - "versionNonce": 1653785435, - "index": "aH", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522366663, - "link": null, - "originalText": "Product Brief\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-discovery-no", - "type": "arrow", - "x": 199.68944196572753, - "y": 290.14813727772287, - "width": 154.38771404438515, - "height": 0.2869361997344413, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-discovery", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-prd", - "focus": 0, - "gap": 5.918648042715176 - }, - "points": [ - [ - 0, - 0 - ], - [ - 154.38771404438515, - 0.2869361997344413 - ] - ], - "lastCommittedPoint": null, - "version": 134, - "versionNonce": 1651808102, - "index": "aI", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191023838, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-no-discovery", - "type": "text", - "x": 220, - "y": 270, - "width": 25, - "height": 20, - "angle": 0, - "strokeColor": "#d32f2f", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "No", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 198980347, - "index": "aJ", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "containerId": null, - "originalText": "No", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-phase1-to-phase2", - "type": "arrow", - "x": 200.89221334296062, - "y": 647.2552625380853, - "width": 155.54926796151912, - "height": 344.1924874570816, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-product-brief", - "focus": 0.6109361701343846, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-prd", - "focus": 0.48602478253370496, - "gap": 3.21773034122549 - }, - "points": [ - [ - 0, - 0 - ], - [ - 71.35560764925268, - -38.29318660613865 - ], - [ - 84.68337472706096, - -292.7672603376131 - ], - [ - 155.54926796151912, - -344.1924874570816 - ] - ], - "lastCommittedPoint": null, - "version": 1393, - "versionNonce": 261518822, - "index": "aK", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1764191023838, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, - { - "id": "phase2-header", - "type": "text", - "x": 340, - "y": 180, - "width": 200, - "height": 30, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 24, - "fontFamily": 1, - "text": "PHASE 2", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 292690843, - "index": "aL", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "containerId": null, - "originalText": "PHASE 2", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "phase2-subtitle", - "type": "text", - "x": 340, - "y": 210, - "width": 200, - "height": 20, - "angle": 0, - "strokeColor": "#666666", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Planning (Required)", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 184762261, - "index": "aM", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171079, - "link": null, - "containerId": null, - "originalText": "Planning (Required)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-prd", - "type": "rectangle", - "x": 359.2970847222632, - "y": 250.5934448656302, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#43a047", - "backgroundColor": "#c8e6c9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-prd-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-prd-text" - }, - { - "type": "arrow", - "id": "arrow-discovery-no" - }, - { - "id": "arrow-phase1-to-phase2", - "type": "arrow" - }, - { - "id": "RF10FfKbmG72P77I2IoP4", - "type": "arrow" - }, - { - "id": "jv0rnlK2D9JKIGTO7pUtT", - "type": "arrow" - }, - { - "id": "arrow-has-ui-no", - "type": "arrow" - }, - { - "id": "arrow-prd-hasui", - "type": "arrow" - } - ], - "locked": false, - "version": 108, - "versionNonce": 930129275, - "index": "aN", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764952855000, - "link": null - }, - { - "id": "proc-prd-text", - "type": "text", - "x": 418.107097539646, - "y": 278.0934448656302, - "width": 42.379974365234375, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-prd-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "PRD", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-prd", - "locked": false, - "version": 103, - "versionNonce": 1402977702, - "index": "aO", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191023837, - "link": null, - "originalText": "PRD", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "decision-has-ui", - "type": "diamond", - "x": 360, - "y": 470, - "width": 160, - "height": 100, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-has-ui-group" - ], - "boundElements": [ - { - "type": "text", - "id": "decision-has-ui-text" - }, - { - "type": "arrow", - "id": "arrow-prd-hasui" - }, - { - "type": "arrow", - "id": "arrow-has-ui-yes" - }, - { - "type": "arrow", - "id": "arrow-has-ui-no" - } - ], - "locked": false, - "version": 3, - "versionNonce": 1003877916, - "index": "aT", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1764952855000, - "link": null - }, - { - "id": "decision-has-ui-text", - "type": "text", - "x": 375, - "y": 495, - "width": 130, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-has-ui-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Has UI?", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "decision-has-ui", - "locked": false, - "version": 2, - "versionNonce": 222317845, - "index": "aU", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Has UI?", - "autoResize": true, - "lineHeight": 3.125 - }, - { - "id": "arrow-has-ui-yes", - "type": "arrow", - "x": 440, - "y": 570, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-has-ui", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-ux-design", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 2, - "versionNonce": 528906939, - "index": "aV", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-yes-ui", - "type": "text", - "x": 450, - "y": 580, - "width": 30, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Yes", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 2, - "versionNonce": 1581245045, - "index": "aW", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "containerId": null, - "originalText": "Yes", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-ux-design", - "type": "rectangle", - "x": 360, - "y": 600, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#8e24aa", - "backgroundColor": "#e1bee7", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-ux-design-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-ux-design-text" - }, - { - "type": "arrow", - "id": "arrow-has-ui-yes" - }, - { - "type": "arrow", - "id": "arrow-ux-to-phase3" - } - ], - "locked": false, - "version": 2, - "versionNonce": 268266331, - "index": "aX", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1763522171080, - "link": null - }, - { - "id": "proc-ux-design-text", - "type": "text", - "x": 370, - "y": 628, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-ux-design-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Create UX", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-ux-design", - "locked": false, - "version": 2, - "versionNonce": 157666261, - "index": "aY", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522171080, - "link": null, - "originalText": "Create UX", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-has-ui-no", - "type": "arrow", - "x": 517.6863546461885, - "y": 287.4640953051147, - "width": 158.4487370618814, - "height": 25.521141112371026, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-prd", - "focus": -0.13686633304390483, - "gap": 1.6107300760746739 - }, - "endBinding": { - "elementId": "proc-architecture", - "focus": 0.16050512337240405, - "gap": 6.573819526326588 - }, - "points": [ - [ - 0, - 0 - ], - [ - 65.15287677643596, - 2.2657676476494544 - ], - [ - 111.59197355857077, - 25.521141112371026 - ], - [ - 158.4487370618814, - 24.060724236900796 - ] - ], - "lastCommittedPoint": null, - "version": 831, - "versionNonce": 1382987110, - "index": "aZ", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1764191570205, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, - { - "id": "label-no-ui", - "type": "text", - "x": 540, - "y": 500, - "width": 25, - "height": 20, - "angle": 0, - "strokeColor": "#d32f2f", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "No", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 5, - "versionNonce": 183981370, - "index": "aa", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "arrow-has-ui-no", - "type": "arrow" - } - ], - "updated": 1764191508105, - "link": null, - "containerId": null, - "originalText": "No", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-ux-to-phase3", - "type": "arrow", - "x": 523.3221723982787, - "y": 642.0805139439535, - "width": 158.4945254931572, - "height": 296.63050159541245, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-ux-design", - "focus": 0.5906867967554547, - "gap": 3.322172398278667 - }, - "endBinding": { - "elementId": "proc-architecture", - "focus": 0.3856343135512404, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 76.98345162139776, - -45.99075822656016 - ], - [ - 116.19277860378315, - -258.3973533698057 - ], - [ - 158.4945254931572, - -296.63050159541245 - ] - ], - "lastCommittedPoint": null, - "version": 328, - "versionNonce": 517434918, - "index": "ab", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1764191529677, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, - { - "id": "phase3-header", - "type": "text", - "x": 709.0199784799299, - "y": 181.88359184111607, - "width": 200, - "height": 30, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 24, - "fontFamily": 1, - "text": "PHASE 3", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 32, - "versionNonce": 1258326202, - "index": "ac", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190667244, - "link": null, - "containerId": null, - "originalText": "PHASE 3", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "phase3-subtitle", - "type": "text", - "x": 687.4485256281371, - "y": 215.63080811867223, - "width": 220, - "height": 20, - "angle": 0, - "strokeColor": "#666666", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Solutioning (Required)", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 35, - "versionNonce": 360954426, - "index": "ad", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190669111, - "link": null, - "containerId": null, - "originalText": "Solutioning (Required)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-architecture", - "type": "rectangle", - "x": 682.7089112343965, - "y": 275.64692474279855, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#f4511e", - "backgroundColor": "#ffccbc", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-architecture-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-architecture-text" - }, - { - "type": "arrow", - "id": "arrow-ux-to-phase3" - }, - { - "type": "arrow", - "id": "arrow-arch-epics" - }, - { - "id": "arrow-has-ui-no", - "type": "arrow" - } - ], - "locked": false, - "version": 90, - "versionNonce": 1912262330, - "index": "ae", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191508105, - "link": null - }, - { - "id": "proc-architecture-text", - "type": "text", - "x": 692.7089112343965, - "y": 303.64692474279855, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-architecture-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Architecture", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-architecture", - "locked": false, - "version": 88, - "versionNonce": 452440186, - "index": "af", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191451669, - "link": null, - "originalText": "Architecture", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-arch-epics", - "type": "arrow", - "x": 760.6640738654764, - "y": 358.02872135607737, - "width": 0.007789277755136936, - "height": 35.679359419065065, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-architecture", - "focus": 0.025673321057619772, - "gap": 2.381796613278823 - }, - "endBinding": { - "elementId": "proc-validate-arch", - "focus": -0.09156227842994098, - "gap": 2.5273595258319688 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0.007789277755136936, - 35.679359419065065 - ] - ], - "lastCommittedPoint": null, - "version": 549, - "versionNonce": 1665519674, - "index": "ag", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191459184, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-epics", - "type": "rectangle", - "x": 670.1028230821919, - "y": 510.76268244350774, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#43a047", - "backgroundColor": "#c8e6c9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-epics-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-epics-text" - }, - { - "type": "arrow", - "id": "arrow-arch-epics" - }, - { - "type": "arrow", - "id": "arrow-epics-test" - }, - { - "id": "arrow-validate-ready", - "type": "arrow" - } - ], - "locked": false, - "version": 178, - "versionNonce": 1597058278, - "index": "ah", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191442604, - "link": null - }, - { - "id": "proc-epics-text", - "type": "text", - "x": 680.1028230821919, - "y": 538.7626824435077, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-epics-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Epics/Stories", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-epics", - "locked": false, - "version": 177, - "versionNonce": 2105920614, - "index": "ai", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191427908, - "link": null, - "originalText": "Epics/Stories", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-epics-test", - "type": "arrow", - "x": 750.5489606775325, - "y": 591.2142966047594, - "width": 0.4387418927216231, - "height": 60.43894121748178, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-epics", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-test-design", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0.4387418927216231, - 60.43894121748178 - ] - ], - "lastCommittedPoint": null, - "version": 358, - "versionNonce": 1168009958, - "index": "aj", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191427908, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-test-design", - "type": "rectangle", - "x": 671.2209977440557, - "y": 652.1048519834928, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#e91e63", - "backgroundColor": "#f8bbd0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-test-design-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-test-design-text" - }, - { - "type": "arrow", - "id": "arrow-epics-test" - }, - { - "type": "arrow", - "id": "arrow-test-validate" - } - ], - "locked": false, - "version": 124, - "versionNonce": 456543462, - "index": "ak", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191425140, - "link": null - }, - { - "id": "proc-test-design-text", - "type": "text", - "x": 709.1090363793096, - "y": 667.1048519834928, - "width": 84.22392272949219, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-test-design-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Test Design\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-test-design", - "locked": false, - "version": 133, - "versionNonce": 1038598182, - "index": "al", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191425140, - "link": null, - "originalText": "Test Design\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-test-validate", - "type": "arrow", - "x": 742.3164554890545, - "y": 732.7428812826017, - "width": 0.2331013464803391, - "height": 41.16039866169126, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-test-design", - "focus": 0.11090307971902064, - "gap": 1.407314849962063 - }, - "endBinding": { - "elementId": "proc-impl-ready", - "focus": -0.07891534010655449, - "gap": 6.845537084300759 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0.2331013464803391, - 41.16039866169126 - ] - ], - "lastCommittedPoint": null, - "version": 482, - "versionNonce": 362456762, - "index": "am", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191475964, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-validate-arch", - "type": "rectangle", - "x": 688.0069292751327, - "y": 396.2354403009744, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#f4511e", - "backgroundColor": "#ffccbc", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-validate-arch-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-validate-arch-text" - }, - { - "type": "arrow", - "id": "arrow-test-validate" - }, - { - "type": "arrow", - "id": "arrow-validate-ready" - }, - { - "id": "arrow-arch-epics", - "type": "arrow" - } - ], - "locked": false, - "version": 234, - "versionNonce": 940473658, - "index": "an", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191449834, - "link": null - }, - { - "id": "proc-validate-arch-text", - "type": "text", - "x": 698.0069292751327, - "y": 411.2354403009744, - "width": 140, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-validate-arch-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Validate Arch\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-validate-arch", - "locked": false, - "version": 233, - "versionNonce": 41862650, - "index": "ao", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191449834, - "link": null, - "originalText": "Validate Arch\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-validate-ready", - "type": "arrow", - "x": 756.1926048905458, - "y": 477.82525825285865, - "width": 2.6030810941729214, - "height": 34.90186599521081, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-validate-arch", - "focus": 0.10499022285337105, - "gap": 1.5898179518842426 - }, - "endBinding": { - "elementId": "proc-epics", - "focus": 0.007831693483179265, - "gap": 1.9644418045617158 - }, - "points": [ - [ - 0, - 0 - ], - [ - -2.6030810941729214, - 34.90186599521081 - ] - ], - "lastCommittedPoint": null, - "version": 527, - "versionNonce": 679932090, - "index": "ap", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191469649, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-impl-ready", - "type": "rectangle", - "x": 669.3773407122919, - "y": 777.1531869468762, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#f4511e", - "backgroundColor": "#ffccbc", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-impl-ready-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-impl-ready-text" - }, - { - "type": "arrow", - "id": "arrow-validate-ready" - }, - { - "type": "arrow", - "id": "arrow-phase3-to-phase4" - }, - { - "id": "arrow-test-validate", - "type": "arrow" - } - ], - "locked": false, - "version": 102, - "versionNonce": 1799933050, - "index": "aq", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764191475963, - "link": null - }, - { - "id": "proc-impl-ready-text", - "type": "text", - "x": 679.3773407122919, - "y": 792.1531869468762, - "width": 140, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-impl-ready-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Implementation\nReadiness", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-impl-ready", - "locked": false, - "version": 101, - "versionNonce": 1345137978, - "index": "ar", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764191475963, - "link": null, - "originalText": "Implementation\nReadiness", - "autoResize": true, - "lineHeight": 1.5625 - }, - { - "id": "arrow-phase3-to-phase4", - "type": "arrow", - "x": 832.3758366994932, - "y": 828.1314512149465, - "width": 332.79883769023445, - "height": 519.9927682908395, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-impl-ready", - "focus": 0.8094917779899522, - "gap": 3.380037483859951 - }, - "endBinding": { - "elementId": "proc-sprint-planning", - "focus": 0.538276991056649, - "gap": 1.1436349518342013 - }, - "points": [ - [ - 0, - 0 - ], - [ - 80.82567439689569, - -94.83900216621896 - ], - [ - 159.28426317101867, - -458.225799867337 - ], - [ - 332.79883769023445, - -519.9927682908395 - ] - ], - "lastCommittedPoint": null, - "version": 1116, - "versionNonce": 55014906, - "index": "as", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1764191475964, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, - { - "id": "phase4-header", - "type": "text", - "x": 1175.3730315866237, - "y": 167.81322734599433, - "width": 200, - "height": 30, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 24, - "fontFamily": 1, - "text": "PHASE 4", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 271, - "versionNonce": 866534438, - "index": "at", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "PHASE 4", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "phase4-subtitle", - "type": "text", - "x": 1139.1188804963076, - "y": 204.18282943768378, - "width": 260, - "height": 20, - "angle": 0, - "strokeColor": "#666666", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Implementation (Required)", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 238, - "versionNonce": 198627174, - "index": "au", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "Implementation (Required)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-sprint-planning", - "type": "rectangle", - "x": 1166.1946812371566, - "y": 276.1576920193427, - "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-sprint-planning-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-sprint-planning-text" - }, - { - "type": "arrow", - "id": "arrow-phase3-to-phase4" - }, - { - "id": "arrow-validate-epic-story", - "type": "arrow" - } - ], - "locked": false, - "version": 379, - "versionNonce": 2085876390, - "index": "av", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "proc-sprint-planning-text", - "type": "text", - "x": 1176.1946812371566, - "y": 304.1576920193427, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-sprint-planning-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Sprint Plan", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-sprint-planning", - "locked": false, - "version": 377, - "versionNonce": 2143989222, - "index": "aw", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Sprint Plan", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "label-story-loop", - "type": "text", - "x": 1176.2977877917795, - "y": 441.904906795244, - "width": 130.87991333007812, - "height": 25, - "angle": 0, - "strokeColor": "#e65100", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 20, - "fontFamily": 1, - "text": "STORY LOOP", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 603, - "versionNonce": 40529830, - "index": "b04", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "STORY LOOP", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-validate-epic-story", - "type": "arrow", - "x": 1249.6597155437828, - "y": 357.36880197268204, - "width": 2.9293448190794606, - "height": 208.61271744725804, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-sprint-planning", - "focus": -0.050194107916528306, - "gap": 1.21110995333936 - }, - "endBinding": { - "elementId": "proc-create-story", - "focus": -0.004614835874420464, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - -2.9293448190794606, - 208.61271744725804 - ] - ], - "lastCommittedPoint": null, - "version": 951, - "versionNonce": 1394233274, - "index": "b05", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-create-story", - "type": "rectangle", - "x": 1166.5341271166512, - "y": 566.4331335811917, - "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-create-story-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-create-story-text" - }, - { - "type": "arrow", - "id": "arrow-validate-epic-story" - }, - { - "type": "arrow", - "id": "arrow-create-validate-story" - }, - { - "type": "arrow", - "id": "arrow-more-stories-yes" - } - ], - "locked": false, - "version": 282, - "versionNonce": 966999590, - "index": "b06", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "proc-create-story-text", - "type": "text", - "x": 1176.5341271166512, - "y": 594.4331335811917, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-create-story-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Create Story", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-create-story", - "locked": false, - "version": 282, - "versionNonce": 2082769254, - "index": "b07", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Create Story", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-create-validate-story", - "type": "arrow", - "x": 1246.5341271166512, - "y": 646.4331335811917, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-create-story", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-validate-story", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 848, - "versionNonce": 1820404026, - "index": "b08", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-validate-story", - "type": "rectangle", - "x": 1166.5341271166512, - "y": 676.4331335811917, - "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-story-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-validate-story-text" - }, - { - "type": "arrow", - "id": "arrow-create-validate-story" - }, - { - "type": "arrow", - "id": "arrow-validate-story-decision" - } - ], - "locked": false, - "version": 282, - "versionNonce": 282699366, - "index": "b09", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "proc-validate-story-text", - "type": "text", - "x": 1176.5341271166512, - "y": 691.4331335811917, - "width": 140, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-validate-story-group" - ], - "fontSize": 14, - "fontFamily": 1, - "text": "Validate Story\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-validate-story", - "locked": false, - "version": 282, - "versionNonce": 686025126, - "index": "b0A", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Validate Story\n<>", - "autoResize": true, - "lineHeight": 1.7857142857142858 - }, - { - "id": "arrow-validate-story-decision", - "type": "arrow", - "x": 1246.5341271166512, - "y": 756.4331335811917, - "width": 0, - "height": 30, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-validate-story", - "focus": 0, - "gap": 1 - }, - "endBinding": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30 - ] - ], - "lastCommittedPoint": null, - "version": 566, - "versionNonce": 1807479290, - "index": "b0B", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "proc-dev-story", - "type": "rectangle", - "x": 1164.0395418694, - "y": 788.7867016847945, - "width": 160, - "height": 80, - "angle": 0, - "strokeColor": "#3f51b5", - "backgroundColor": "#c5cae9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-dev-story-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-dev-story-text" - }, - { - "type": "arrow", - "id": "arrow-dev-review" - } - ], - "locked": false, - "version": 367, - "versionNonce": 935782054, - "index": "b0R", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "proc-dev-story-text", - "type": "text", - "x": 1174.0395418694, - "y": 816.7867016847945, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-dev-story-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Develop Story", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-dev-story", - "locked": false, - "version": 364, - "versionNonce": 952050150, - "index": "b0S", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Develop Story", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-dev-review", - "type": "arrow", - "x": 1244.2149450712877, - "y": 869.2383158460461, - "width": 5.032331195699953, - "height": 76.6679634046609, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-dev-story", - "focus": 0.030012029555609845, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-code-review", - "focus": 0.04241833499478815, - "gap": 1.3466869862454587 - }, - "points": [ - [ - 0, - 0 - ], - [ - 5.032331195699953, - 76.6679634046609 - ] - ], - "lastCommittedPoint": null, - "version": 1191, - "versionNonce": 2052012922, - "index": "b0T", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "decision-code-review", - "type": "diamond", - "x": 1156.5341271166512, - "y": 1156.4331335811917, - "width": 180, - "height": 120, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-code-review-group" - ], - "boundElements": [ - { - "type": "text", - "id": "decision-code-review-text" - }, - { - "type": "arrow", - "id": "arrow-dev-review" - }, - { - "type": "arrow", - "id": "arrow-review-fail" - }, - { - "id": "arrow-done-more-stories", - "type": "arrow" - }, - { - "id": "4chQ7PksRKpPe5YX-TfFJ", - "type": "arrow" - } - ], - "locked": false, - "version": 285, - "versionNonce": 46359462, - "index": "b0U", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "decision-code-review-text", - "type": "text", - "x": 1171.5341271166512, - "y": 1191.4331335811917, - "width": 150, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-code-review-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Code Review\nPass?", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "decision-code-review", - "locked": false, - "version": 282, - "versionNonce": 1227095782, - "index": "b0V", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Code Review\nPass?", - "autoResize": true, - "lineHeight": 1.5625 - }, - { - "id": "arrow-review-fail", - "type": "arrow", - "x": 1151.5341271166512, - "y": 1216.3331335811918, - "width": 42.50541475274872, - "height": 387.6464318963972, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": null, - "endBinding": null, - "points": [ - [ - 0, - 0 - ], - [ - -35, - 0 - ], - [ - -35, - -387.6464318963972 - ], - [ - 7.50541475274872, - -387.6464318963972 - ] - ], - "lastCommittedPoint": null, - "elbowed": true, - "version": 319, - "versionNonce": 405929318, - "index": "b0W", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "label-fail", - "type": "text", - "x": 1065.6231186673836, - "y": 1185.462969542075, - "width": 35, - "height": 20, - "angle": 0, - "strokeColor": "#d32f2f", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Fail", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 316, - "versionNonce": 1897488550, - "index": "b0Y", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "Fail", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "label-pass", - "type": "text", - "x": 1229.6819134569105, - "y": 1281.2421635916448, - "width": 40, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Pass", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 408, - "versionNonce": 1437752294, - "index": "b0a", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "4chQ7PksRKpPe5YX-TfFJ", - "type": "arrow" - } - ], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "Pass", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-code-review", - "type": "rectangle", - "x": 1169.3991588878014, - "y": 947.2529662369525, - "width": 160, - "height": 110, - "angle": 0, - "strokeColor": "#3f51b5", - "backgroundColor": "#c5cae9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "roundness": { - "type": 3, - "value": 8 - }, - "groupIds": [ - "proc-code-review-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-code-review-text" - }, - { - "type": "arrow", - "id": "arrow-done-more-stories" - }, - { - "id": "arrow-dev-review", - "type": "arrow" - } - ], - "locked": false, - "version": 453, - "versionNonce": 277682790, - "index": "b0b", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "proc-code-review-text", - "type": "text", - "x": 1187.9272045420983, - "y": 972.2529662369525, - "width": 122.94390869140625, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-code-review-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Code Review\n<>", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-code-review", - "locked": false, - "version": 502, - "versionNonce": 1242095014, - "index": "b0c", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Code Review\n<>", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-done-more-stories", - "type": "arrow", - "x": 1249.4681490735618, - "y": 1065.5372616587838, - "width": 1.7879398006109568, - "height": 90.97426236326123, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-code-review", - "focus": 0.014488632877232727, - "gap": 8.284295421831303 - }, - "endBinding": { - "elementId": "decision-code-review", - "focus": 0.09832693417954867, - "gap": 2.039543956918169 - }, - "points": [ - [ - 0, - 0 - ], - [ - 1.7879398006109568, - 90.97426236326123 - ] - ], - "lastCommittedPoint": null, - "version": 1093, - "versionNonce": 146679034, - "index": "b0d", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "decision-more-stories", - "type": "diamond", - "x": 1163.8719002449689, - "y": 1363.600308336051, - "width": 180, - "height": 120, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-more-stories-group" - ], - "boundElements": [ - { - "type": "text", - "id": "decision-more-stories-text" - }, - { - "type": "arrow", - "id": "arrow-done-more-stories" - }, - { - "type": "arrow", - "id": "arrow-more-stories-yes" - }, - { - "type": "arrow", - "id": "arrow-more-stories-no" - }, - { - "id": "4chQ7PksRKpPe5YX-TfFJ", - "type": "arrow" - } - ], - "locked": false, - "version": 441, - "versionNonce": 886168230, - "index": "b0e", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "decision-more-stories-text", - "type": "text", - "x": 1178.8719002449689, - "y": 1398.600308336051, - "width": 150, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-more-stories-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "More Stories\nin Epic?", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "decision-more-stories", - "locked": false, - "version": 440, - "versionNonce": 1078695398, - "index": "b0f", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "More Stories\nin Epic?", - "autoResize": true, - "lineHeight": 1.5625 - }, - { - "id": "arrow-more-stories-yes", - "type": "arrow", - "x": 1158.8719002449689, - "y": 1423.5003083360511, - "width": 141.95595587699154, - "height": 827.0007685048595, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-more-stories", - "fixedPoint": [ - -0.027777777777777776, - 0.4991666666666674 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": null, - "points": [ - [ - 0, - 0 - ], - [ - -140.44216650530916, - 0 - ], - [ - -140.44216650530916, - -827.0007685048595 - ], - [ - 1.5137893716823783, - -827.0007685048595 - ] - ], - "lastCommittedPoint": null, - "elbowed": true, - "version": 954, - "versionNonce": 2094428902, - "index": "b0g", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "fixedSegments": [ - { - "index": 2, - "start": [ - -140.44216650530916, - 0 - ], - "end": [ - -140.44216650530916, - -827.0007685048595 - ] - } - ], - "startIsSpecial": false, - "endIsSpecial": false - }, - { - "id": "label-more-stories-yes", - "type": "text", - "x": 1024.8322929694286, - "y": 1455.9672274720815, - "width": 30, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Yes", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 320, - "versionNonce": 76752422, - "index": "b0h", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "Yes", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-more-stories-no", - "type": "arrow", - "x": 1254.2299747445697, - "y": 1484.1816612705734, - "width": 0.09067340460524065, - "height": 69.22388536244944, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-more-stories", - "focus": -0.004645359638607261, - "gap": 1 - }, - "endBinding": { - "elementId": "proc-retrospective", - "focus": -0.000007722345339971072, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0.09067340460524065, - 69.22388536244944 - ] - ], - "lastCommittedPoint": null, - "version": 1115, - "versionNonce": 1285598842, - "index": "b0i", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-more-stories-no", - "type": "text", - "x": 1273.6656161640394, - "y": 1506.317970130127, - "width": 25, - "height": 20, - "angle": 0, - "strokeColor": "#d32f2f", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "No", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 327, - "versionNonce": 1022383270, - "index": "b0j", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "No", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "proc-retrospective", - "type": "rectangle", - "x": 1174.3742521794413, - "y": 1553.8571607942745, - "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-retrospective-group" - ], - "boundElements": [ - { - "type": "text", - "id": "proc-retrospective-text" - }, - { - "type": "arrow", - "id": "arrow-more-stories-no" - }, - { - "type": "arrow", - "id": "arrow-retro-more-epics" - } - ], - "locked": false, - "version": 391, - "versionNonce": 1921699814, - "index": "b0k", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "proc-retrospective-text", - "type": "text", - "x": 1184.3742521794413, - "y": 1581.8571607942745, - "width": 140, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "proc-retrospective-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Retrospective", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "proc-retrospective", - "locked": false, - "version": 391, - "versionNonce": 1572070182, - "index": "b0l", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "Retrospective", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-retro-more-epics", - "type": "arrow", - "x": 1252.261821627823, - "y": 1634.3087749555261, - "width": 2.2496323163620673, - "height": 42.83146813764597, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-retrospective", - "focus": -0.00014865809573961995, - "gap": 1 - }, - "endBinding": { - "elementId": "decision-more-epics", - "focus": 0.006063807827498143, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - -2.2496323163620673, - 42.83146813764597 - ] - ], - "lastCommittedPoint": null, - "version": 957, - "versionNonce": 1972295674, - "index": "b0m", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763619, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "decision-more-epics", - "type": "diamond", - "x": 1156.5341271166512, - "y": 1676.4331335811917, - "width": 180, - "height": 120, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-more-epics-group" - ], - "boundElements": [ - { - "type": "text", - "id": "decision-more-epics-text" - }, - { - "type": "arrow", - "id": "arrow-retro-more-epics" - }, - { - "type": "arrow", - "id": "arrow-more-epics-yes" - }, - { - "type": "arrow", - "id": "arrow-more-epics-no" - } - ], - "locked": false, - "version": 282, - "versionNonce": 101589030, - "index": "b0n", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "decision-more-epics-text", - "type": "text", - "x": 1171.5341271166512, - "y": 1711.4331335811917, - "width": 150, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "decision-more-epics-group" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "More Epics?", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "decision-more-epics", - "locked": false, - "version": 282, - "versionNonce": 2095262566, - "index": "b0o", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "More Epics?", - "autoResize": true, - "lineHeight": 3.125 - }, - { - "id": "arrow-more-epics-yes", - "type": "arrow", - "x": 1151.5341271166512, - "y": 1736.3331335811918, - "width": 194.92191691435096, - "height": 1138.0678409916745, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "decision-more-epics", - "fixedPoint": [ - -0.027777777777777776, - 0.4991666666666674 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": null, - "points": [ - [ - 0, - 0 - ], - [ - -184.89984110690511, - 0 - ], - [ - -184.89984110690511, - -1138.0678409916745 - ], - [ - 10.022075807445844, - -1138.0678409916745 - ] - ], - "lastCommittedPoint": null, - "elbowed": true, - "version": 1805, - "versionNonce": 1424088358, - "index": "b0p", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow", - "fixedSegments": [ - { - "index": 2, - "start": [ - -184.89984110690511, - 0 - ], - "end": [ - -184.89984110690511, - -1138.0678409916745 - ] - } - ], - "startIsSpecial": false, - "endIsSpecial": false - }, - { - "id": "label-more-epics-yes", - "type": "text", - "x": 1016.7607529532588, - "y": 1704.1213622982812, - "width": 30, - "height": 20, - "angle": 0, - "strokeColor": "#2e7d32", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "Yes", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 395, - "versionNonce": 339167334, - "index": "b0q", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "Yes", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "arrow-more-epics-no", - "type": "arrow", - "x": 1246.5341271166512, - "y": 1796.4331335811921, - "width": 0, - "height": 50, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "label-more-epics-no", - "focus": 0, - "gap": 14.142135623730951 - }, - "endBinding": { - "elementId": "end-ellipse", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 50 - ] - ], - "lastCommittedPoint": null, - "version": 848, - "versionNonce": 757113210, - "index": "b0r", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763620, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "label-more-epics-no", - "type": "text", - "x": 1256.5341271166512, - "y": 1806.4331335811921, - "width": 25, - "height": 20, - "angle": 0, - "strokeColor": "#d32f2f", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "fontSize": 16, - "fontFamily": 1, - "text": "No", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 283, - "versionNonce": 1126229734, - "index": "b0s", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "arrow-more-epics-no", - "type": "arrow" - } - ], - "updated": 1764190763204, - "link": null, - "containerId": null, - "originalText": "No", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "end-ellipse", - "type": "ellipse", - "x": 1186.5341271166512, - "y": 1846.4331335811921, - "width": 120, - "height": 60, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "#e3f2fd", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "end-group" - ], - "boundElements": [ - { - "type": "text", - "id": "end-text" - }, - { - "type": "arrow", - "id": "arrow-more-epics-no" - } - ], - "locked": false, - "version": 282, - "versionNonce": 370468198, - "index": "b0t", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "updated": 1764190763204, - "link": null - }, - { - "id": "end-text", - "type": "text", - "x": 1223.5341271166512, - "y": 1864.4331335811921, - "width": 46, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "end-group" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "End", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "end-ellipse", - "locked": false, - "version": 282, - "versionNonce": 39798950, - "index": "b0u", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764190763204, - "link": null, - "originalText": "End", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-box", - "type": "rectangle", - "x": -383.37044904818777, - "y": 130.62309916565027, - "width": 280, - "height": 240, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffffff", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "roundness": { - "type": 3, - "value": 8 - }, - "locked": false, - "version": 240, - "versionNonce": 899126491, - "index": "b0v", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-title", - "type": "text", - "x": -303.37044904818777, - "y": 140.62309916565027, - "width": 120, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 20, - "fontFamily": 1, - "text": "Agent Legend", - "textAlign": "center", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 354828667, - "index": "b0w", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "Agent Legend", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-analyst", - "type": "rectangle", - "x": -373.37044904818777, - "y": 180.62309916565027, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#00acc1", - "backgroundColor": "#b2ebf2", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 863394331, - "index": "b0x", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-analyst-text", - "type": "text", - "x": -343.37044904818777, - "y": 182.62309916565027, - "width": 70, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Analyst", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 226123451, - "index": "b0y", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "Analyst", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-pm", - "type": "rectangle", - "x": -373.37044904818777, - "y": 210.62309916565027, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#43a047", - "backgroundColor": "#c8e6c9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 1574227803, - "index": "b0z", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-pm-text", - "type": "text", - "x": -343.37044904818777, - "y": 212.62309916565027, - "width": 30, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "PM", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 1725443067, - "index": "b10", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "PM", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-ux", - "type": "rectangle", - "x": -373.37044904818777, - "y": 240.62309916565027, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#8e24aa", - "backgroundColor": "#e1bee7", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 2089219227, - "index": "b11", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-ux-text", - "type": "text", - "x": -343.37044904818777, - "y": 242.62309916565027, - "width": 110, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "UX Designer", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 1318299963, - "index": "b12", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "UX Designer", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-architect", - "type": "rectangle", - "x": -373.37044904818777, - "y": 270.6230991656503, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#f4511e", - "backgroundColor": "#ffccbc", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 1918945755, - "index": "b13", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-architect-text", - "type": "text", - "x": -343.37044904818777, - "y": 272.6230991656503, - "width": 80, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Architect", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 755029627, - "index": "b14", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "Architect", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-tea", - "type": "rectangle", - "x": -373.37044904818777, - "y": 300.6230991656503, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#e91e63", - "backgroundColor": "#f8bbd0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 2100711195, - "index": "b15", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-tea-text", - "type": "text", - "x": -343.37044904818777, - "y": 302.6230991656503, - "width": 40, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "TEA", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 1702081467, - "index": "b16", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "TEA", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-sm", - "type": "rectangle", - "x": -373.37044904818777, - "y": 330.6230991656503, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#1e88e5", - "backgroundColor": "#bbdefb", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 1977320539, - "index": "b17", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-sm-text", - "type": "text", - "x": -343.37044904818777, - "y": 332.6230991656503, - "width": 30, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "SM", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 373309691, - "index": "b18", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "SM", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-dev", - "type": "rectangle", - "x": -223.37044904818777, - "y": 180.62309916565027, - "width": 20, - "height": 20, - "angle": 0, - "strokeColor": "#3f51b5", - "backgroundColor": "#c5cae9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 270821787, - "index": "b19", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-dev-text", - "type": "text", - "x": -193.37044904818777, - "y": 182.62309916565027, - "width": 40, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "DEV", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 1488617019, - "index": "b1A", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "originalText": "DEV", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "legend-decision", - "type": "diamond", - "x": -223.37044904818777, - "y": 210.62309916565027, - "width": 30, - "height": 30, - "angle": 0, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 1, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "locked": false, - "version": 187, - "versionNonce": 451215067, - "index": "b1B", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null - }, - { - "id": "legend-decision-text", - "type": "text", - "x": -183.37044904818777, - "y": 217.62309916565027, - "width": 70, - "height": 20, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [ - "FOWhENd6l0IWkDrktqohE" - ], - "fontSize": 16, - "fontFamily": 1, - "text": "Decision", - "textAlign": "left", - "verticalAlign": "top", - "locked": false, - "version": 187, - "versionNonce": 20343675, - "index": "b1C", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1763522286451, - "link": null, - "containerId": null, - "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 - }, - { - "id": "arrow-prd-hasui", - "type": "arrow", - "x": 440, - "y": 330, - "width": 0, - "height": 140, - "angle": 0, - "strokeColor": "#1976d2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "opacity": 100, - "groupIds": [], - "startBinding": { - "elementId": "proc-prd", - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "decision-has-ui", - "focus": 0, - "gap": 1 - }, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 140 - ] - ], - "lastCommittedPoint": null, - "version": 1, - "versionNonce": 1, - "index": "b1J", - "isDeleted": false, - "strokeStyle": "solid", - "seed": 1, - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1764952855000, - "link": null, - "locked": false, - "startArrowhead": null, - "endArrowhead": "arrow" - } - ], - "appState": { - "gridSize": 20, - "gridStep": 5, - "gridModeEnabled": false, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/docs/tutorials/getting-started/images/workflow-method-greenfield.svg b/docs/tutorials/getting-started/images/workflow-method-greenfield.svg deleted file mode 100644 index 6522b695..00000000 --- a/docs/tutorials/getting-started/images/workflow-method-greenfield.svg +++ /dev/null @@ -1,4 +0,0 @@ - - -BMad Method Workflow - Standard GreenfieldStartPHASE 1Discovery(Optional)IncludeDiscovery?YesBrainstorm<<optional>>Research<<optional>>Product Brief<<optional>>NoPHASE 2Planning (Required)PRDHas 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 diff --git a/docs/tutorials/getting-started/images/workflow-overview.jpg b/docs/tutorials/getting-started/images/workflow-overview.jpg deleted file mode 100644 index 3f61b3c5..00000000 Binary files a/docs/tutorials/getting-started/images/workflow-overview.jpg and /dev/null differ diff --git a/docs/tutorials/getting-started/workflow-overview.jpg b/docs/tutorials/getting-started/workflow-overview.jpg deleted file mode 100644 index 3f61b3c5..00000000 Binary files a/docs/tutorials/getting-started/workflow-overview.jpg and /dev/null differ diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md index 7202f883..fccb7da2 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md @@ -3,7 +3,7 @@ name: 'step-01-document-discovery' description: 'Discover and inventory all project documents, handling duplicates and organizing file structure' # Path Definitions -workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness' +workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/implementation-readiness' # File References thisStepFile: './step-01-document-discovery.md' diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md index 0e568963..5dd08705 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md @@ -3,7 +3,7 @@ name: 'step-02-prd-analysis' description: 'Read and analyze PRD to extract all FRs and NFRs for coverage validation' # Path Definitions -workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness' +workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/implementation-readiness' # File References thisStepFile: './step-02-prd-analysis.md' diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md index 1952b6b9..981a5b63 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md @@ -3,7 +3,7 @@ name: 'step-03-epic-coverage-validation' description: 'Validate that all PRD FRs are covered in epics and stories' # Path Definitions -workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness' +workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/implementation-readiness' # File References thisStepFile: './step-03-epic-coverage-validation.md' diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md index 1ad10789..33aad045 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md @@ -3,7 +3,7 @@ name: 'step-04-ux-alignment' description: 'Check for UX document and validate alignment with PRD and Architecture' # Path Definitions -workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness' +workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/implementation-readiness' # File References thisStepFile: './step-04-ux-alignment.md' diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md index d7d3842b..0203cdc1 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md @@ -3,7 +3,7 @@ name: 'step-05-epic-quality-review' description: 'Validate epics and stories against create-epics-and-stories best practices' # Path Definitions -workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness' +workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/implementation-readiness' # File References thisStepFile: './step-05-epic-quality-review.md' diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md index b54cbcbc..cc826ee9 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md @@ -3,7 +3,7 @@ name: 'step-06-final-assessment' description: 'Compile final assessment and polish the readiness report' # Path Definitions -workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness' +workflow_path: '{project-root}/_bmad/bmm/workflows/3-solutioning/implementation-readiness' # File References thisStepFile: './step-06-final-assessment.md' diff --git a/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md b/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md index 105c049d..6f234612 100644 --- a/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +++ b/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md @@ -115,11 +115,11 @@ Saved to: {finalFile} **Next Steps:** -[D] Done - exit workflow -[B] Begin Development - start implementing now (not recommended) [A] Advanced Elicitation - refine further +[R] Adversarial Review - critique of the spec (highly recommended) +[B] Begin Development - start implementing now (not recommended) +[D] Done - exit workflow [P] Party Mode - get expert feedback before dev -[R] Adversarial Review again - critique of the spec (highly recommended) --- @@ -138,9 +138,9 @@ b) **HALT and wait for user selection.** #### Menu Handling Logic: -- IF D: Exit workflow - display final confirmation and path to spec -- IF B: Load and execute `{quick_dev_workflow}` with the final spec file (warn: fresh context is better) - IF A: Read fully and follow: `{advanced_elicitation}` with current spec content, process enhanced insights, ask user "Accept improvements? (y/n)", if yes update spec then redisplay menu, if no keep original then redisplay menu +- IF B: Load and execute `{quick_dev_workflow}` with the final spec file (warn: fresh context is better) +- IF D: Exit workflow - display final confirmation and path to spec - IF P: Read fully and follow: `{party_mode_exec}` with current spec content, process collaborative insights, ask user "Accept changes? (y/n)", if yes update spec then redisplay menu, if no keep original then redisplay menu - IF R: Execute Adversarial Review (see below) - IF Any other comments or queries: respond helpfully then redisplay menu diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 005e1faf..9089775d 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -167,31 +167,50 @@ class Installer { if (newlySelectedIdes.length > 0) { console.log('\n'); // Add spacing before IDE questions - // Use IdeManager to get handlers (supports both config-driven and custom IDEs) - await this.ideManager.ensureInitialized(); - for (const ide of newlySelectedIdes) { - try { - const handler = this.ideManager.handlers.get(ide); + // List of IDEs that have interactive prompts + //TODO: Why is this here, hardcoding this list here is bad, fix me! + const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes( + ide, + ); - if (!handler) { - // IDE not recognized - skip silently - continue; - } + if (needsPrompts) { + // Get IDE handler and collect configuration + try { + // Dynamically load the IDE setup module + const ideModule = require(`../ide/${ide}`); - // Check if this IDE handler has a collectConfiguration method - if (typeof handler.collectConfiguration === 'function') { - console.log(chalk.cyan(`\nConfiguring ${ide}...`)); - ideConfigurations[ide] = await handler.collectConfiguration({ - selectedModules: selectedModules || [], - projectDir, - bmadDir, - }); + // Get the setup class (handle different export formats) + let SetupClass; + const className = + ide + .split('-') + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join('') + 'Setup'; + + if (ideModule[className]) { + SetupClass = ideModule[className]; + } else if (ideModule.default) { + SetupClass = ideModule.default; + } else { + continue; + } + + const ideSetup = new SetupClass(); + + // Check if this IDE has a collectConfiguration method + if (typeof ideSetup.collectConfiguration === 'function') { + console.log(chalk.cyan(`\nConfiguring ${ide}...`)); + ideConfigurations[ide] = await ideSetup.collectConfiguration({ + selectedModules: selectedModules || [], + projectDir, + bmadDir, + }); + } + } catch { + // IDE doesn't have a setup file or collectConfiguration method + console.warn(chalk.yellow(`Warning: Could not load configuration for ${ide}`)); } - // Most config-driven IDEs don't need configuration - silently skip - } catch { - // IDE doesn't have collectConfiguration or had an error - skip - continue; } } } diff --git a/tools/cli/installers/lib/ide/STANDARDIZATION_PLAN.md b/tools/cli/installers/lib/ide/STANDARDIZATION_PLAN.md new file mode 100644 index 00000000..f7116cb5 --- /dev/null +++ b/tools/cli/installers/lib/ide/STANDARDIZATION_PLAN.md @@ -0,0 +1,208 @@ +# IDE Installer Standardization Plan + +## Overview + +Standardize IDE installers to use **flat file naming** with **underscores** (Windows-compatible) and centralize duplicated code in shared utilities. + +**Key Rule: All IDEs use underscore format for Windows compatibility (colons don't work on Windows).** + +## Current State Analysis + +### File Structure Patterns + +| IDE | Current Pattern | Path Format | +|-----|-----------------|-------------| +| **claude-code** | Hierarchical | `.claude/commands/bmad/{module}/agents/{name}.md` | +| **cursor** | Hierarchical | `.cursor/commands/bmad/{module}/agents/{name}.md` | +| **crush** | Hierarchical | `.crush/commands/bmad/{module}/agents/{name}.md` | +| **antigravity** | Flattened (underscores) | `.agent/workflows/bmad_module_agents_name.md` | +| **codex** | Flattened (underscores) | `~/.codex/prompts/bmad_module_agents_name.md` | +| **cline** | Flattened (underscores) | `.clinerules/workflows/bmad_module_type_name.md` | +| **roo** | Flattened (underscores) | `.roo/commands/bmad_module_agent_name.md` | +| **auggie** | Hybrid | `.augment/commands/bmad/agents/{module}-{name}.md` | +| **iflow** | Hybrid | `.iflow/commands/bmad/agents/{module}-{name}.md` | +| **trae** | Different (rules) | `.trae/rules/bmad-agent-{module}-{name}.md` | +| **github-copilot** | Different (agents) | `.github/agents/bmd-custom-{module}-{name}.agent.md` | + +### Shared Generators (in `/shared`) + +1. `agent-command-generator.js` - generates agent launchers +2. `task-tool-command-generator.js` - generates task/tool commands +3. `workflow-command-generator.js` - generates workflow commands + +All currently create artifacts with **nested relative paths** like `{module}/agents/{name}.md` + +### Code Duplication Issues + +1. **Flattening logic** duplicated in multiple IDEs +2. **Agent launcher content creation** duplicated +3. **Path transformation** duplicated + +## Target Standardization + +### For All IDEs (underscore format - Windows-compatible) + +**IDEs affected:** claude-code, cursor, crush, antigravity, codex, cline, roo + +``` +Format: bmad_{module}_{type}_{name}.md + +Examples: +- Agent: bmad_bmm_agents_pm.md +- Agent: bmad_core_agents_dev.md +- Workflow: bmad_bmm_workflows_correct-course.md +- Task: bmad_bmm_tasks_bmad-help.md +- Tool: bmad_core_tools_code-review.md +- Custom: bmad_custom_agents_fred-commit-poet.md +``` + +**Note:** Type segments (agents, workflows, tasks, tools) are filtered out from names: +- `bmm/agents/pm.md` → `bmad_bmm_pm.md` (not `bmad_bmm_agents_pm.md`) + +### For Hybrid IDEs (keep as-is) + +**IDEs affected:** auggie, iflow + +These use `{module}-{name}.md` format within subdirectories - keep as-is. + +### Skip (drastically different) + +**IDEs affected:** trae, github-copilot + +## Implementation Plan + +### Phase 1: Create Shared Utility + +**File:** `shared/path-utils.js` + +```javascript +/** + * Convert hierarchical path to flat underscore-separated name (Windows-compatible) + * @param {string} module - Module name (e.g., 'bmm', 'core') + * @param {string} type - Artifact type ('agents', 'workflows', 'tasks', 'tools') - filtered out + * @param {string} name - Artifact name (e.g., 'pm', 'correct-course') + * @returns {string} Flat filename like 'bmad_bmm_pm.md' + */ +function toUnderscoreName(module, type, name) { + return `bmad_${module}_${name}.md`; +} + +/** + * Convert relative path to flat underscore-separated name (Windows-compatible) + * @param {string} relativePath - Path like 'bmm/agents/pm.md' + * @returns {string} Flat filename like 'bmad_bmm_pm.md' + */ +function toUnderscorePath(relativePath) { + const withoutExt = relativePath.replace('.md', ''); + const parts = withoutExt.split(/[\/\\]/); + // Filter out type segments (agents, workflows, tasks, tools) + const filtered = parts.filter((p) => !TYPE_SEGMENTS.includes(p)); + return `bmad_${filtered.join('_')}.md`; +} + +/** + * Create custom agent underscore name + * @param {string} agentName - Custom agent name + * @returns {string} Flat filename like 'bmad_custom_fred-commit-poet.md' + */ +function customAgentUnderscoreName(agentName) { + return `bmad_custom_${agentName}.md`; +} + +// Backward compatibility aliases +const toColonName = toUnderscoreName; +const toColonPath = toUnderscorePath; +const toDashPath = toUnderscorePath; +const customAgentColonName = customAgentUnderscoreName; +const customAgentDashName = customAgentUnderscoreName; + +module.exports = { + toUnderscoreName, + toUnderscorePath, + customAgentUnderscoreName, + // Backward compatibility + toColonName, + toColonPath, + toDashPath, + customAgentColonName, + customAgentDashName, +}; +``` + +### Phase 2: Update Shared Generators + +**Files to modify:** +- `shared/agent-command-generator.js` +- `shared/task-tool-command-generator.js` +- `shared/workflow-command-generator.js` + +**Changes:** +1. Import path utilities +2. Change `relativePath` to use flat format +3. Add method `writeColonArtifacts()` for folder-based IDEs (uses underscore) +4. Add method `writeDashArtifacts()` for flat IDEs (uses underscore) + +### Phase 3: Update All IDEs + +**Files to modify:** +- `claude-code.js` +- `cursor.js` +- `crush.js` +- `antigravity.js` +- `codex.js` +- `cline.js` +- `roo.js` + +**Changes:** +1. Import utilities from path-utils +2. Change from hierarchical to flat underscore naming +3. Update cleanup to handle flat structure (`startsWith('bmad')`) + +### Phase 4: Update Base Class + +**File:** `_base-ide.js` + +**Changes:** +1. Mark `flattenFilename()` as `@deprecated` +2. Add comment pointing to new path-utils + +## Migration Checklist + +### New Files +- [x] Create `shared/path-utils.js` + +### All IDEs (convert to underscore format) +- [x] Update `shared/agent-command-generator.js` - update for underscore +- [x] Update `shared/task-tool-command-generator.js` - update for underscore +- [x] Update `shared/workflow-command-generator.js` - update for underscore +- [x] Update `claude-code.js` - convert to underscore format +- [x] Update `cursor.js` - convert to underscore format +- [x] Update `crush.js` - convert to underscore format +- [ ] Update `antigravity.js` - use underscore format +- [ ] Update `codex.js` - use underscore format +- [ ] Update `cline.js` - use underscore format +- [ ] Update `roo.js` - use underscore format + +### CSV Command Files +- [x] Update `src/core/module-help.csv` - change colons to underscores +- [x] Update `src/bmm/module-help.csv` - change colons to underscores + +### Base Class +- [ ] Update `_base-ide.js` - add deprecation notice + +### Testing +- [ ] Test claude-code installation +- [ ] Test cursor installation +- [ ] Test crush installation +- [ ] Test antigravity installation +- [ ] Test codex installation +- [ ] Test cline installation +- [ ] Test roo installation + +## Notes + +1. **Filter type segments**: agents, workflows, tasks, tools are filtered out from flat names +2. **Underscore format**: Universal underscore format for Windows compatibility +3. **Custom agents**: Follow the same pattern as regular agents +4. **Backward compatibility**: Old function names kept as aliases +5. **Cleanup**: Will remove old `bmad:` format files on next install diff --git a/tools/cli/installers/lib/ide/_config-driven.js b/tools/cli/installers/lib/ide/_config-driven.js deleted file mode 100644 index f0a0979f..00000000 --- a/tools/cli/installers/lib/ide/_config-driven.js +++ /dev/null @@ -1,446 +0,0 @@ -const path = require('node:path'); -const fs = require('fs-extra'); -const chalk = require('chalk'); -const yaml = require('yaml'); -const { BaseIdeSetup } = require('./_base-ide'); -const { UnifiedInstaller } = require('./shared/unified-installer'); -const { toSuffixBasedName, getArtifactSuffix, customAgentSuffixName } = require('./shared/path-utils'); - -/** - * Load platform codes configuration from platform-codes.yaml - * @returns {Object} Platform configuration object - */ -async function loadPlatformCodes() { - const platformCodesPath = path.join(__dirname, 'platform-codes.yaml'); - - if (!(await fs.pathExists(platformCodesPath))) { - console.warn(chalk.yellow('Warning: platform-codes.yaml not found')); - return { platforms: {} }; - } - - const content = await fs.readFile(platformCodesPath, 'utf8'); - const config = yaml.parse(content); - return config; -} - -/** - * Config-driven IDE setup handler - * - * Reads installer configuration from platform-codes.yaml and uses - * UnifiedInstaller to perform the actual installation. - * - * This eliminates the need for separate installer files for most IDEs. - */ -class ConfigDrivenIdeSetup extends BaseIdeSetup { - /** - * @param {string} platformCode - Platform code (e.g., 'claude-code', 'cursor') - * @param {Object} platformConfig - Platform configuration from platform-codes.yaml - */ - constructor(platformCode, platformConfig) { - super(platformCode, platformConfig.name, platformConfig.preferred); - this.platformConfig = platformConfig; - this.installerConfig = platformConfig.installer || null; - } - - /** - * Setup IDE configuration using config-driven approach - * @param {string} projectDir - Project directory - * @param {string} bmadDir - BMAD installation directory - * @param {Object} options - Setup options - * @returns {Promise} Setup result - */ - async setup(projectDir, bmadDir, options = {}) { - console.log(chalk.cyan(`Setting up ${this.name}...`)); - - if (!this.installerConfig) { - console.warn(chalk.yellow(`No installer configuration found for ${this.name}`)); - return { success: false, reason: 'no-config' }; - } - - // Handle multi-target installations (like github-copilot, opencode) - if (this.installerConfig.targets) { - return this.installToMultipleTargets(projectDir, bmadDir, this.installerConfig.targets, options); - } - - // Handle single-target installations - if (this.installerConfig.target_dir) { - return this.installToTarget(projectDir, bmadDir, this.installerConfig, options); - } - - console.warn(chalk.yellow(`Invalid installer configuration for ${this.name}`)); - return { success: false, reason: 'invalid-config' }; - } - - /** - * Install artifacts to a single target directory - * @param {string} projectDir - Project directory - * @param {string} bmadDir - BMAD installation directory - * @param {Object} targetConfig - Target configuration - * @param {Object} options - Setup options - * @returns {Promise} Setup result - */ - async installToTarget(projectDir, bmadDir, targetConfig, options) { - const targetDir = path.join(projectDir, targetConfig.dir || targetConfig.target_dir); - - // Clean up old BMAD installation first - await this.cleanupTarget(targetDir, targetConfig.file_extension || '.md'); - - // Ensure target directory exists - await this.ensureDir(targetDir); - - // Get frontmatter template from config (defaults to common-yaml.md) - const frontmatterTemplate = targetConfig.frontmatter_template || 'common-yaml.md'; - - // Use the unified installer - const installer = new UnifiedInstaller(this.bmadFolderName); - const counts = await installer.install( - projectDir, - bmadDir, - { - targetDir, - namingStyle: 'suffix-based', - frontmatterTemplate, - fileExtension: targetConfig.file_extension || '.md', - skipExisting: targetConfig.skip_existing || false, - artifactTypes: targetConfig.artifact_types, - }, - options.selectedModules || [], - ); - - console.log(chalk.green(`✓ ${this.name} configured:`)); - console.log(chalk.dim(` - ${counts.agents} agents installed`)); - if (counts.workflows > 0) { - console.log(chalk.dim(` - ${counts.workflows} workflow commands generated`)); - } - if (counts.tasks + counts.tools > 0) { - console.log( - chalk.dim(` - ${counts.tasks + counts.tools} task/tool commands generated (${counts.tasks} tasks, ${counts.tools} tools)`), - ); - } - console.log(chalk.dim(` - Target directory: ${path.relative(projectDir, targetDir)}`)); - - return { - success: true, - agents: counts.agents, - tasks: counts.tasks, - tools: counts.tools, - workflows: counts.workflows, - }; - } - - /** - * Install artifacts to multiple target directories - * @param {string} projectDir - Project directory - * @param {string} bmadDir - BMAD installation directory - * @param {Array} targets - Array of target configurations - * @param {Object} options - Setup options - * @returns {Promise} Setup result - */ - async installToMultipleTargets(projectDir, bmadDir, targets, options) { - const totalCounts = { - agents: 0, - workflows: 0, - tasks: 0, - tools: 0, - total: 0, - }; - - const targetNames = []; - - for (const targetConfig of targets) { - const targetDir = path.join(projectDir, targetConfig.dir); - - // Clean up old BMAD installation first - await this.cleanupTarget(targetDir, targetConfig.file_extension || '.md'); - - // Ensure target directory exists - await this.ensureDir(targetDir); - - // Get frontmatter template from config (defaults to common-yaml.md) - const frontmatterTemplate = targetConfig.frontmatter_template || 'common-yaml.md'; - - // Use the unified installer for this target - const installer = new UnifiedInstaller(this.bmadFolderName); - const counts = await installer.install( - projectDir, - bmadDir, - { - targetDir, - namingStyle: 'suffix-based', - frontmatterTemplate, - fileExtension: targetConfig.file_extension || '.md', - skipExisting: targetConfig.skip_existing || false, - artifactTypes: targetConfig.artifact_types, - }, - options.selectedModules || [], - ); - - // Accumulate counts - totalCounts.agents += counts.agents; - totalCounts.workflows += counts.workflows; - totalCounts.tasks += counts.tasks; - totalCounts.tools += counts.tools; - - targetNames.push(path.relative(projectDir, targetDir)); - } - - totalCounts.total = totalCounts.agents + totalCounts.workflows + totalCounts.tasks + totalCounts.tools; - - console.log(chalk.green(`✓ ${this.name} configured:`)); - console.log(chalk.dim(` - ${totalCounts.agents} agents installed`)); - if (totalCounts.workflows > 0) { - console.log(chalk.dim(` - ${totalCounts.workflows} workflow commands generated`)); - } - if (totalCounts.tasks + totalCounts.tools > 0) { - console.log( - chalk.dim( - ` - ${totalCounts.tasks + totalCounts.tools} task/tool commands generated (${totalCounts.tasks} tasks, ${totalCounts.tools} tools)`, - ), - ); - } - console.log(chalk.dim(` - Target directories: ${targetNames.join(', ')}`)); - - // Handle VS Code settings if needed (for github-copilot) - if (this.installerConfig.has_vscode_settings) { - await this.configureVsCodeSettings(projectDir, options); - } - - return { - success: true, - ...totalCounts, - }; - } - - /** - * Configure VS Code settings for GitHub Copilot - * @param {string} projectDir - Project directory - * @param {Object} options - Setup options - */ - async configureVsCodeSettings(projectDir, options) { - const vscodeDir = path.join(projectDir, '.vscode'); - const settingsPath = path.join(vscodeDir, 'settings.json'); - - await this.ensureDir(vscodeDir); - - // Read existing settings - let existingSettings = {}; - if (await fs.pathExists(settingsPath)) { - try { - const content = await fs.readFile(settingsPath, 'utf8'); - existingSettings = JSON.parse(content); - } catch { - console.warn(chalk.yellow(' Could not parse settings.json, creating new')); - } - } - - // BMAD VS Code settings - const bmadSettings = { - 'chat.agent.enabled': true, - 'chat.agent.maxRequests': 15, - 'github.copilot.chat.agent.runTasks': true, - 'chat.mcp.discovery.enabled': true, - 'github.copilot.chat.agent.autoFix': true, - 'chat.tools.autoApprove': false, - }; - - // Merge settings (existing take precedence) - const mergedSettings = { ...bmadSettings, ...existingSettings }; - - // Write settings - await fs.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2)); - console.log(chalk.dim(` - VS Code settings configured`)); - } - - /** - * Clean up a specific target directory - * @param {string} targetDir - Target directory to clean - * @param {string} [fileExtension='.md'] - File extension to match - */ - async cleanupTarget(targetDir, fileExtension = '.md') { - if (!(await fs.pathExists(targetDir))) { - return; - } - - const entries = await fs.readdir(targetDir); - let removed = 0; - - for (const entry of entries) { - // Remove bmad* files with the matching extension - if (entry.startsWith('bmad') && entry.endsWith(fileExtension)) { - await fs.remove(path.join(targetDir, entry)); - removed++; - } - } - - if (removed > 0) { - console.log(chalk.dim(` Cleaned up ${removed} existing BMAD files`)); - } - } - - /** - * Cleanup IDE configuration - * @param {string} projectDir - Project directory - */ - async cleanup(projectDir) { - if (!this.installerConfig) { - return; - } - - // Handle multi-target cleanup - if (this.installerConfig.targets) { - for (const targetConfig of this.installerConfig.targets) { - const targetDir = path.join(projectDir, targetConfig.dir); - await this.cleanupTarget(targetDir, targetConfig.file_extension || '.md'); - } - return; - } - - // Handle single-target cleanup - if (this.installerConfig.target_dir) { - const targetDir = path.join(projectDir, this.installerConfig.target_dir); - await this.cleanupTarget(targetDir, this.installerConfig.file_extension || '.md'); - } - } - - /** - * Install a custom agent launcher for this IDE - * @param {string} projectDir - Project directory - * @param {string} agentName - Agent name (e.g., "fred-commit-poet") - * @param {string} agentPath - Path to compiled agent (relative to project root) - * @param {Object} metadata - Agent metadata - * @returns {Object|null} Info about created command - */ - async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { - if (!this.installerConfig) { - return null; - } - - // Determine target directory for agents - let targetDir; - let fileExtension = '.md'; - let frontmatterTemplate = 'common-yaml.md'; - - if (this.installerConfig.targets) { - // For multi-target IDEs like github-copilot, find the agents target - const agentsTarget = this.installerConfig.targets.find((t) => t.artifact_types && t.artifact_types.includes('agents')); - if (!agentsTarget) { - return null; // No agents target found - } - targetDir = path.join(projectDir, agentsTarget.dir); - fileExtension = agentsTarget.file_extension || '.md'; - frontmatterTemplate = agentsTarget.frontmatter_template || 'common-yaml.md'; - } else if (this.installerConfig.target_dir) { - targetDir = path.join(projectDir, this.installerConfig.target_dir); - fileExtension = this.installerConfig.file_extension || '.md'; - frontmatterTemplate = this.installerConfig.frontmatter_template || 'common-yaml.md'; - } else { - return null; - } - - if (!(await this.exists(targetDir))) { - return null; - } - - await this.ensureDir(targetDir); - - // Create launcher content using frontmatter template - const launcherContent = await this.createLauncherContent(agentName, agentPath, metadata, frontmatterTemplate); - - // Use suffix-based naming for custom agents - const fileName = customAgentSuffixName(agentName, fileExtension); - const launcherPath = path.join(targetDir, fileName); - await this.writeFile(launcherPath, launcherContent); - - return { - path: launcherPath, - command: fileName.replace(fileExtension, ''), - }; - } - - /** - * Create launcher content using frontmatter template - * @param {string} agentName - Agent name - * @param {string} agentPath - Path to agent file - * @param {Object} metadata - Agent metadata - * @param {string} frontmatterTemplate - Template filename - * @returns {Promise} Launcher content - */ - async createLauncherContent(agentName, agentPath, metadata, frontmatterTemplate) { - const title = metadata.title || this.formatTitle(agentName); - - // Base activation content - const activationContent = `You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. - - -1. LOAD the FULL agent file from @${agentPath} -2. READ its entire contents - this contains the complete agent persona, menu, and instructions -3. FOLLOW every step in the section precisely -4. DISPLAY the welcome/greeting as instructed -5. PRESENT the numbered menu -6. WAIT for user input before proceeding - -`; - - // Load frontmatter template - const { UnifiedInstaller } = require('./shared/unified-installer'); - const installer = new UnifiedInstaller(this.bmadFolderName); - const templateContent = await installer.loadFrontmatterTemplate(frontmatterTemplate); - - if (!templateContent) { - // Fallback to basic YAML - return `--- -name: '${agentName}' -description: '${title} agent' ---- - -${activationContent}`; - } - - // Apply template variables - const variables = { - name: agentName, - title, - displayName: agentName, - description: `Activates the ${title} agent persona.`, - icon: '🤖', - content: activationContent, - tools: JSON.stringify([ - 'changes', - 'edit', - 'fetch', - 'githubRepo', - 'problems', - 'runCommands', - 'runTasks', - 'runTests', - 'search', - 'runSubagent', - 'testFailure', - 'todos', - 'usages', - ]), - }; - - let result = templateContent; - for (const [key, value] of Object.entries(variables)) { - result = result.replaceAll(`{{${key}}}`, value); - } - - // Handle TOML templates specially - if (frontmatterTemplate.includes('toml')) { - const escapedContent = activationContent.replaceAll('"""', String.raw`\"\"\"`); - result = result.replace( - /prompt = """/, - `prompt = """\n**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent!\n\n${escapedContent}`, - ); - return result; - } - - return result + activationContent; - } -} - -module.exports = { - ConfigDrivenIdeSetup, - loadPlatformCodes, -}; diff --git a/tools/cli/installers/lib/ide/antigravity.js b/tools/cli/installers/lib/ide/antigravity.js new file mode 100644 index 00000000..7af2e41b --- /dev/null +++ b/tools/cli/installers/lib/ide/antigravity.js @@ -0,0 +1,474 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { + loadModuleInjectionConfig, + shouldApplyInjection, + filterAgentInstructions, + resolveSubagentFiles, +} = require('./shared/module-injections'); +const { getAgentsFromBmad, getAgentsFromDir } = require('./shared/bmad-artifacts'); +const { toDashPath, customAgentDashName } = require('./shared/path-utils'); +const prompts = require('../../../lib/prompts'); + +/** + * Google Antigravity IDE setup handler + * + * Uses .agent/workflows/ directory for slash commands + */ +class AntigravitySetup extends BaseIdeSetup { + constructor() { + super('antigravity', 'Google Antigravity', true); + this.configDir = '.agent'; + this.workflowsDir = 'workflows'; + } + + /** + * Prompt for subagent installation location + * @returns {Promise} Selected location ('project' or 'user') + */ + async _promptInstallLocation() { + return prompts.select({ + message: 'Where would you like to install Antigravity subagents?', + choices: [ + { name: 'Project level (.agent/agents/)', value: 'project' }, + { name: 'User level (~/.agent/agents/)', value: 'user' }, + ], + default: 'project', + }); + } + + /** + * Collect configuration choices before installation + * @param {Object} options - Configuration options + * @returns {Object} Collected configuration + */ + async collectConfiguration(options = {}) { + // const config = { + // subagentChoices: null, + // installLocation: null, + // }; + + // const sourceModulesPath = getSourcePath('modules'); + // const modules = options.selectedModules || []; + + // for (const moduleName of modules) { + // // Check for Antigravity sub-module injection config in SOURCE directory + // const injectionConfigPath = path.join(sourceModulesPath, moduleName, 'sub-modules', 'antigravity', 'injections.yaml'); + + // if (await this.exists(injectionConfigPath)) { + // const yaml = require('yaml'); + + // try { + // // Load injection configuration + // const configContent = await fs.readFile(injectionConfigPath, 'utf8'); + // const injectionConfig = yaml.parse(configContent); + + // // Ask about subagents if they exist and we haven't asked yet + // if (injectionConfig.subagents && !config.subagentChoices) { + // config.subagentChoices = await this.promptSubagentInstallation(injectionConfig.subagents); + + // if (config.subagentChoices.install !== 'none') { + // config.installLocation = await this._promptInstallLocation(); + // } + // } + // } catch (error) { + // console.log(chalk.yellow(` Warning: Failed to process ${moduleName} features: ${error.message}`)); + // } + // } + // } + + return config; + } + + /** + * Cleanup old BMAD installation before reinstalling + * @param {string} projectDir - Project directory + */ + async cleanup(projectDir) { + const bmadWorkflowsDir = path.join(projectDir, this.configDir, this.workflowsDir, 'bmad'); + + if (await fs.pathExists(bmadWorkflowsDir)) { + await fs.remove(bmadWorkflowsDir); + console.log(chalk.dim(` Removed old BMAD workflows from ${this.name}`)); + } + } + + /** + * Setup Antigravity IDE configuration + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options + */ + async setup(projectDir, bmadDir, options = {}) { + // Store project directory for use in processContent + this.projectDir = projectDir; + + console.log(chalk.cyan(`Setting up ${this.name}...`)); + + // Clean up old BMAD installation first + await this.cleanup(projectDir); + + // Create .agent/workflows directory structure + const agentDir = path.join(projectDir, this.configDir); + const workflowsDir = path.join(agentDir, this.workflowsDir); + const bmadWorkflowsDir = path.join(workflowsDir, 'bmad'); + + await this.ensureDir(bmadWorkflowsDir); + + // Generate agent launchers using AgentCommandGenerator + // This creates small launcher files that reference the actual agents in _bmad/ + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts, counts: agentCounts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Write agent launcher files with FLATTENED naming using shared utility + // Antigravity ignores directory structure, so we flatten to: bmad_module_name.md + // This creates slash commands like /bmad_bmm_dev instead of /dev + const agentCount = await agentGen.writeDashArtifacts(bmadWorkflowsDir, agentArtifacts); + + // Process Antigravity specific injections for installed modules + // Use pre-collected configuration if available, or skip if already configured + if (options.preCollectedConfig && options.preCollectedConfig._alreadyConfigured) { + // IDE is already configured from previous installation, skip prompting + // Just process with default/existing configuration + await this.processModuleInjectionsWithConfig(projectDir, bmadDir, options, {}); + } else if (options.preCollectedConfig) { + await this.processModuleInjectionsWithConfig(projectDir, bmadDir, options, options.preCollectedConfig); + } else { + await this.processModuleInjections(projectDir, bmadDir, options); + } + + // Generate workflow commands from manifest (if it exists) + const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir); + + // Write workflow-command artifacts with FLATTENED naming using shared utility + const workflowCommandCount = await workflowGen.writeDashArtifacts(bmadWorkflowsDir, workflowArtifacts); + + // Generate task and tool commands from manifests (if they exist) + const taskToolGen = new TaskToolCommandGenerator(); + const taskToolResult = await taskToolGen.generateTaskToolCommands(projectDir, bmadDir); + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents installed`)); + if (workflowCommandCount > 0) { + console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated`)); + } + if (taskToolResult.generated > 0) { + console.log( + chalk.dim( + ` - ${taskToolResult.generated} task/tool commands generated (${taskToolResult.tasks} tasks, ${taskToolResult.tools} tools)`, + ), + ); + } + console.log(chalk.dim(` - Workflows directory: ${path.relative(projectDir, bmadWorkflowsDir)}`)); + console.log(chalk.yellow(`\n Note: Antigravity uses flattened slash commands (e.g., /bmad_module_agents_name)`)); + + return { + success: true, + agents: agentCount, + }; + } + + /** + * Read and process file content + */ + async readAndProcess(filePath, metadata) { + const content = await fs.readFile(filePath, 'utf8'); + return this.processContent(content, metadata); + } + + /** + * Override processContent to keep {project-root} placeholder + */ + processContent(content, metadata = {}) { + // Use the base class method WITHOUT projectDir to preserve {project-root} placeholder + return super.processContent(content, metadata); + } + + /** + * Get agents from source modules (not installed location) + */ + async getAgentsFromSource(sourceDir, selectedModules) { + const agents = []; + + // Add core agents + const corePath = getModulePath('core'); + if (await fs.pathExists(path.join(corePath, 'agents'))) { + const coreAgents = await getAgentsFromDir(path.join(corePath, 'agents'), 'core'); + agents.push(...coreAgents); + } + + // Add module agents + for (const moduleName of selectedModules) { + const modulePath = path.join(sourceDir, moduleName); + const agentsPath = path.join(modulePath, 'agents'); + + if (await fs.pathExists(agentsPath)) { + const moduleAgents = await getAgentsFromDir(agentsPath, moduleName); + agents.push(...moduleAgents); + } + } + + return agents; + } + + /** + * Process module injections with pre-collected configuration + */ + async processModuleInjectionsWithConfig(projectDir, bmadDir, options, preCollectedConfig) { + // Get list of installed modules + const modules = options.selectedModules || []; + const { subagentChoices, installLocation } = preCollectedConfig; + + // Get the actual source directory (not the installation directory) + await this.processModuleInjectionsInternal({ + projectDir, + modules, + handler: 'antigravity', + subagentChoices, + installLocation, + interactive: false, + }); + } + + /** + * Process Antigravity specific injections for installed modules + * Looks for injections.yaml in each module's antigravity sub-module + */ + async processModuleInjections(projectDir, bmadDir, options) { + // Get list of installed modules + const modules = options.selectedModules || []; + let subagentChoices = null; + let installLocation = null; + + // Get the actual source directory (not the installation directory) + const { subagentChoices: updatedChoices, installLocation: updatedLocation } = await this.processModuleInjectionsInternal({ + projectDir, + modules, + handler: 'antigravity', + subagentChoices, + installLocation, + interactive: true, + }); + + if (updatedChoices) { + subagentChoices = updatedChoices; + } + if (updatedLocation) { + installLocation = updatedLocation; + } + } + + async processModuleInjectionsInternal({ projectDir, modules, handler, subagentChoices, installLocation, interactive = false }) { + let choices = subagentChoices; + let location = installLocation; + + for (const moduleName of modules) { + const configData = await loadModuleInjectionConfig(handler, moduleName); + + if (!configData) { + continue; + } + + const { config, handlerBaseDir } = configData; + + if (interactive) { + console.log(chalk.cyan(`\nConfiguring ${moduleName} ${handler} features...`)); + } + + // if (interactive && config.subagents && !choices) { + // choices = await this.promptSubagentInstallation(config.subagents); + + // if (choices.install !== 'none') { + // location = await this._promptInstallLocation(); + // } + // } + + if (config.injections && choices && choices.install !== 'none') { + for (const injection of config.injections) { + if (shouldApplyInjection(injection, choices)) { + await this.injectContent(projectDir, injection, choices); + } + } + } + + if (config.subagents && choices && choices.install !== 'none') { + await this.copySelectedSubagents(projectDir, handlerBaseDir, config.subagents, choices, location || 'project'); + } + } + + return { subagentChoices: choices, installLocation: location }; + } + + /** + * Prompt user for subagent installation preferences + */ + async promptSubagentInstallation(subagentConfig) { + // First ask if they want to install subagents + const install = await prompts.select({ + message: 'Would you like to install Antigravity subagents for enhanced functionality?', + choices: [ + { name: 'Yes, install all subagents', value: 'all' }, + { name: 'Yes, let me choose specific subagents', value: 'selective' }, + { name: 'No, skip subagent installation', value: 'none' }, + ], + default: 'all', + }); + + if (install === 'selective') { + // Show list of available subagents with descriptions + const subagentInfo = { + 'market-researcher.md': 'Market research and competitive analysis', + 'requirements-analyst.md': 'Requirements extraction and validation', + 'technical-evaluator.md': 'Technology stack evaluation', + 'epic-optimizer.md': 'Epic and story breakdown optimization', + 'document-reviewer.md': 'Document quality review', + }; + + const selected = await prompts.multiselect({ + message: `Select subagents to install ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`, + choices: subagentConfig.files.map((file) => ({ + name: `${file.replace('.md', '')} - ${subagentInfo[file] || 'Specialized assistant'}`, + value: file, + checked: true, + })), + }); + + return { install: 'selective', selected }; + } + + return { install }; + } + + /** + * Inject content at specified point in file + */ + async injectContent(projectDir, injection, subagentChoices = null) { + const targetPath = path.join(projectDir, injection.file); + + if (await this.exists(targetPath)) { + let content = await fs.readFile(targetPath, 'utf8'); + const marker = ``; + + if (content.includes(marker)) { + let injectionContent = injection.content; + + // Filter content if selective subagents chosen + if (subagentChoices && subagentChoices.install === 'selective' && injection.point === 'pm-agent-instructions') { + injectionContent = filterAgentInstructions(injection.content, subagentChoices.selected); + } + + content = content.replace(marker, injectionContent); + await fs.writeFile(targetPath, content); + console.log(chalk.dim(` Injected: ${injection.point} → ${injection.file}`)); + } + } + } + + /** + * Copy selected subagents to appropriate Antigravity agents directory + */ + async copySelectedSubagents(projectDir, handlerBaseDir, subagentConfig, choices, location) { + const os = require('node:os'); + + // Determine target directory based on user choice + let targetDir; + if (location === 'user') { + targetDir = path.join(os.homedir(), '.agent', 'agents'); + console.log(chalk.dim(` Installing subagents globally to: ~/.agent/agents/`)); + } else { + targetDir = path.join(projectDir, '.agent', 'agents'); + console.log(chalk.dim(` Installing subagents to project: .agent/agents/`)); + } + + // Ensure target directory exists + await this.ensureDir(targetDir); + + const resolvedFiles = await resolveSubagentFiles(handlerBaseDir, subagentConfig, choices); + + let copiedCount = 0; + for (const resolved of resolvedFiles) { + try { + const sourcePath = resolved.absolutePath; + + const subFolder = path.dirname(resolved.relativePath); + let targetPath; + if (subFolder && subFolder !== '.') { + const targetSubDir = path.join(targetDir, subFolder); + await this.ensureDir(targetSubDir); + targetPath = path.join(targetSubDir, path.basename(resolved.file)); + } else { + targetPath = path.join(targetDir, path.basename(resolved.file)); + } + + await fs.copyFile(sourcePath, targetPath); + console.log(chalk.green(` ✓ Installed: ${subFolder === '.' ? '' : `${subFolder}/`}${path.basename(resolved.file, '.md')}`)); + copiedCount++; + } catch (error) { + console.log(chalk.yellow(` ⚠ Error copying ${resolved.file}: ${error.message}`)); + } + } + + if (copiedCount > 0) { + console.log(chalk.dim(` Total subagents installed: ${copiedCount}`)); + } + } + + /** + * Install a custom agent launcher for Antigravity + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + // Create .agent/workflows/bmad directory structure (same as regular agents) + const agentDir = path.join(projectDir, this.configDir); + const workflowsDir = path.join(agentDir, this.workflowsDir); + const bmadWorkflowsDir = path.join(workflowsDir, 'bmad'); + + await fs.ensureDir(bmadWorkflowsDir); + + // Create custom agent launcher with same pattern as regular agents + const launcherContent = `name: '${agentName}' +description: '${agentName} agent' +usage: | + Custom BMAD agent: ${agentName} + + Launch with: /${agentName} + + You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. EXECUTE as ${agentName} with full persona adoption + + +--- + +⚠️ **IMPORTANT**: Run @${agentPath} to load the complete agent before using this launcher!`; + + // Use underscore format: bmad_custom_fred-commit-poet.md + const fileName = customAgentDashName(agentName); + const launcherPath = path.join(bmadWorkflowsDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'antigravity', + path: path.relative(projectDir, launcherPath), + command: `/${fileName.replace('.md', '')}`, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { AntigravitySetup }; diff --git a/tools/cli/installers/lib/ide/auggie.js b/tools/cli/installers/lib/ide/auggie.js new file mode 100644 index 00000000..04e08788 --- /dev/null +++ b/tools/cli/installers/lib/ide/auggie.js @@ -0,0 +1,244 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); + +/** + * Auggie CLI setup handler + * Installs to project directory (.augment/commands) + */ +class AuggieSetup extends BaseIdeSetup { + constructor() { + super('auggie', 'Auggie CLI'); + this.detectionPaths = ['.augment']; + } + + /** + * Setup Auggie CLI 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}...`)); + + // Always use project directory + const location = path.join(projectDir, '.augment', 'commands'); + + // Clean up old BMAD installation first + await this.cleanup(projectDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Get tasks, tools, and workflows (ALL workflows now generate commands) + const tasks = await this.getTasks(bmadDir, true); + const tools = await this.getTools(bmadDir, true); + + // Get ALL workflows using the new workflow command generator + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + + // Convert workflow artifacts to expected format + const workflows = workflowArtifacts + .filter((artifact) => artifact.type === 'workflow-command') + .map((artifact) => ({ + module: artifact.module, + name: path.basename(artifact.relativePath, '.md'), + path: artifact.sourcePath, + content: artifact.content, + })); + + const bmadCommandsDir = path.join(location, 'bmad'); + const agentsDir = path.join(bmadCommandsDir, 'agents'); + const tasksDir = path.join(bmadCommandsDir, 'tasks'); + const toolsDir = path.join(bmadCommandsDir, 'tools'); + const workflowsDir = path.join(bmadCommandsDir, 'workflows'); + + await this.ensureDir(agentsDir); + await this.ensureDir(tasksDir); + await this.ensureDir(toolsDir); + await this.ensureDir(workflowsDir); + + // Install agent launchers + for (const artifact of agentArtifacts) { + const targetPath = path.join(agentsDir, `${artifact.module}-${artifact.name}.md`); + await this.writeFile(targetPath, artifact.content); + } + + // Install tasks + for (const task of tasks) { + const content = await this.readFile(task.path); + const commandContent = this.createTaskCommand(task, content); + + const targetPath = path.join(tasksDir, `${task.module}-${task.name}.md`); + await this.writeFile(targetPath, commandContent); + } + + // Install tools + for (const tool of tools) { + const content = await this.readFile(tool.path); + const commandContent = this.createToolCommand(tool, content); + + const targetPath = path.join(toolsDir, `${tool.module}-${tool.name}.md`); + await this.writeFile(targetPath, commandContent); + } + + // Install workflows (already generated commands) + for (const workflow of workflows) { + // Use the pre-generated workflow command content + const targetPath = path.join(workflowsDir, `${workflow.module}-${workflow.name}.md`); + await this.writeFile(targetPath, workflow.content); + } + + const totalInstalled = agentArtifacts.length + tasks.length + tools.length + workflows.length; + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentArtifacts.length} agents installed`)); + console.log(chalk.dim(` - ${tasks.length} tasks installed`)); + console.log(chalk.dim(` - ${tools.length} tools installed`)); + console.log(chalk.dim(` - ${workflows.length} workflows installed`)); + console.log(chalk.dim(` - Location: ${path.relative(projectDir, location)}`)); + console.log(chalk.yellow(`\n 💡 Tip: Add 'model: gpt-4o' to command frontmatter to specify AI model`)); + + return { + success: true, + agents: agentArtifacts.length, + tasks: tasks.length, + tools: tools.length, + workflows: workflows.length, + }; + } + + /** + * Create task command content + */ + createTaskCommand(task, content) { + const nameMatch = content.match(/name="([^"]+)"/); + const taskName = nameMatch ? nameMatch[1] : this.formatTitle(task.name); + + return `--- +description: "Execute the ${taskName} task" +--- + +# ${taskName} Task + +${content} + +## Module +BMAD ${task.module.toUpperCase()} module +`; + } + + /** + * Create tool command content + */ + createToolCommand(tool, content) { + const nameMatch = content.match(/name="([^"]+)"/); + const toolName = nameMatch ? nameMatch[1] : this.formatTitle(tool.name); + + return `--- +description: "Use the ${toolName} tool" +--- + +# ${toolName} Tool + +${content} + +## Module +BMAD ${tool.module.toUpperCase()} module +`; + } + + /** + * Create workflow command content + */ + createWorkflowCommand(workflow, content) { + const description = workflow.description || `Execute the ${workflow.name} workflow`; + + return `--- +description: "${description}" +--- + +# ${workflow.name} Workflow + +${content} + +## Module +BMAD ${workflow.module.toUpperCase()} module +`; + } + + /** + * Cleanup Auggie configuration + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + + // Only clean up project directory + const location = path.join(projectDir, '.augment', 'commands'); + const bmadDir = path.join(location, 'bmad'); + + if (await fs.pathExists(bmadDir)) { + await fs.remove(bmadDir); + console.log(chalk.dim(` Removed old BMAD commands`)); + } + } + + /** + * Install a custom agent launcher for Auggie + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + // Auggie uses .augment/commands directory + const location = path.join(projectDir, '.augment', 'commands'); + const bmadCommandsDir = path.join(location, 'bmad'); + const agentsDir = path.join(bmadCommandsDir, 'agents'); + + // Create .augment/commands/bmad/agents directory if it doesn't exist + await fs.ensureDir(agentsDir); + + // Create custom agent launcher + const launcherContent = `--- +description: "Use the ${agentName} custom agent" +--- + +# ${agentName} Custom Agent + +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this command to activate ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +## Module +BMAD Custom agent +`; + + const fileName = `custom-${agentName.toLowerCase()}.md`; + const launcherPath = path.join(agentsDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'auggie', + path: path.relative(projectDir, launcherPath), + command: agentName, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { AuggieSetup }; diff --git a/tools/cli/installers/lib/ide/claude-code.js b/tools/cli/installers/lib/ide/claude-code.js new file mode 100644 index 00000000..cf7dedcd --- /dev/null +++ b/tools/cli/installers/lib/ide/claude-code.js @@ -0,0 +1,506 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { + loadModuleInjectionConfig, + shouldApplyInjection, + filterAgentInstructions, + resolveSubagentFiles, +} = require('./shared/module-injections'); +const { getAgentsFromBmad, getAgentsFromDir } = require('./shared/bmad-artifacts'); +const { customAgentColonName } = require('./shared/path-utils'); +const prompts = require('../../../lib/prompts'); + +/** + * Claude Code IDE setup handler + */ +class ClaudeCodeSetup extends BaseIdeSetup { + constructor() { + super('claude-code', 'Claude Code', true); // preferred IDE + this.configDir = '.claude'; + this.commandsDir = 'commands'; + this.agentsDir = 'agents'; + } + + /** + * Prompt for subagent installation location + * @returns {Promise} Selected location ('project' or 'user') + */ + async promptInstallLocation() { + return prompts.select({ + message: 'Where would you like to install Claude Code subagents?', + choices: [ + { name: 'Project level (.claude/agents/)', value: 'project' }, + { name: 'User level (~/.claude/agents/)', value: 'user' }, + ], + default: 'project', + }); + } + + // /** + // * Collect configuration choices before installation + // * @param {Object} options - Configuration options + // * @returns {Object} Collected configuration + // */ + // async collectConfiguration(options = {}) { + // const config = { + // subagentChoices: null, + // installLocation: null, + // }; + + // const sourceModulesPath = getSourcePath('modules'); + // const modules = options.selectedModules || []; + + // for (const moduleName of modules) { + // // Check for Claude Code sub-module injection config in SOURCE directory + // const injectionConfigPath = path.join(sourceModulesPath, moduleName, 'sub-modules', 'claude-code', 'injections.yaml'); + + // if (await this.exists(injectionConfigPath)) { + // const yaml = require('yaml'); + + // try { + // // Load injection configuration + // const configContent = await fs.readFile(injectionConfigPath, 'utf8'); + // const injectionConfig = yaml.parse(configContent); + + // // Ask about subagents if they exist and we haven't asked yet + // if (injectionConfig.subagents && !config.subagentChoices) { + // config.subagentChoices = await this.promptSubagentInstallation(injectionConfig.subagents); + + // if (config.subagentChoices.install !== 'none') { + // config.installLocation = await this.promptInstallLocation(); + // } + // } + // } catch (error) { + // console.log(chalk.yellow(` Warning: Failed to process ${moduleName} features: ${error.message}`)); + // } + // } + // } + + // return config; + // } + + /** + * Cleanup old BMAD installation before reinstalling + * @param {string} projectDir - Project directory + */ + async cleanup(projectDir) { + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + // Remove any bmad* files from the commands directory (cleans up old bmad: and bmad- formats) + if (await fs.pathExists(commandsDir)) { + const entries = await fs.readdir(commandsDir); + let removedCount = 0; + for (const entry of entries) { + if (entry.startsWith('bmad')) { + await fs.remove(path.join(commandsDir, entry)); + removedCount++; + } + } + // Also remove legacy bmad folder if it exists + const bmadFolder = path.join(commandsDir, 'bmad'); + if (await fs.pathExists(bmadFolder)) { + await fs.remove(bmadFolder); + console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`)); + } + } + } + + /** + * Clean up legacy folder structure (module/type/name.md) if it exists + * This can be called after migration to remove old nested directories + * @param {string} projectDir - Project directory + */ + async cleanupLegacyFolders(projectDir) { + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + if (!(await fs.pathExists(commandsDir))) { + return; + } + + // Remove legacy bmad folder if it exists + const bmadFolder = path.join(commandsDir, 'bmad'); + if (await fs.pathExists(bmadFolder)) { + await fs.remove(bmadFolder); + console.log(chalk.dim(` Removed legacy bmad folder from ${this.name}`)); + } + } + + /** + * Setup Claude Code IDE configuration + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options + */ + async setup(projectDir, bmadDir, options = {}) { + // Store project directory for use in processContent + this.projectDir = projectDir; + + console.log(chalk.cyan(`Setting up ${this.name}...`)); + + // Clean up old BMAD installation first + await this.cleanup(projectDir); + + // Create .claude/commands directory structure + const claudeDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(claudeDir, this.commandsDir); + await this.ensureDir(commandsDir); + + // Use underscore format: files written directly to commands dir (no bmad subfolder) + // Creates: .claude/commands/bmad_bmm_pm.md + + // Generate agent launchers using AgentCommandGenerator + // This creates small launcher files that reference the actual agents in _bmad/ + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts, counts: agentCounts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Write agent launcher files using flat underscore naming + // Creates files like: bmad_bmm_pm.md + const agentCount = await agentGen.writeColonArtifacts(commandsDir, agentArtifacts); + + // Process Claude Code specific injections for installed modules + // Use pre-collected configuration if available, or skip if already configured + if (options.preCollectedConfig && options.preCollectedConfig._alreadyConfigured) { + // IDE is already configured from previous installation, skip prompting + // Just process with default/existing configuration + await this.processModuleInjectionsWithConfig(projectDir, bmadDir, options, {}); + } else if (options.preCollectedConfig) { + await this.processModuleInjectionsWithConfig(projectDir, bmadDir, options, options.preCollectedConfig); + } else { + await this.processModuleInjections(projectDir, bmadDir, options); + } + + // Skip CLAUDE.md creation - let user manage their own CLAUDE.md file + // await this.createClaudeConfig(projectDir, modules); + + // Generate workflow commands from manifest (if it exists) + const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir); + + // Write workflow-command artifacts using flat underscore naming + // Creates files like: bmad_bmm_correct-course.md + const workflowCommandCount = await workflowGen.writeColonArtifacts(commandsDir, workflowArtifacts); + + // Generate task and tool commands from manifests (if they exist) + const taskToolGen = new TaskToolCommandGenerator(); + const taskToolResult = await taskToolGen.generateColonTaskToolCommands(projectDir, bmadDir, commandsDir); + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents installed`)); + if (workflowCommandCount > 0) { + console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated`)); + } + if (taskToolResult.generated > 0) { + console.log( + chalk.dim( + ` - ${taskToolResult.generated} task/tool commands generated (${taskToolResult.tasks} tasks, ${taskToolResult.tools} tools)`, + ), + ); + } + console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, commandsDir)}`)); + + return { + success: true, + agents: agentCount, + }; + } + + // Method removed - CLAUDE.md file management left to user + + /** + * Read and process file content + */ + async readAndProcess(filePath, metadata) { + const content = await fs.readFile(filePath, 'utf8'); + return this.processContent(content, metadata); + } + + /** + * Override processContent to keep {project-root} placeholder + */ + processContent(content, metadata = {}) { + // Use the base class method WITHOUT projectDir to preserve {project-root} placeholder + return super.processContent(content, metadata); + } + + /** + * Get agents from source modules (not installed location) + */ + async getAgentsFromSource(sourceDir, selectedModules) { + const agents = []; + + // Add core agents + const corePath = getModulePath('core'); + if (await fs.pathExists(path.join(corePath, 'agents'))) { + const coreAgents = await getAgentsFromDir(path.join(corePath, 'agents'), 'core'); + agents.push(...coreAgents); + } + + // Add module agents + for (const moduleName of selectedModules) { + const modulePath = path.join(sourceDir, moduleName); + const agentsPath = path.join(modulePath, 'agents'); + + if (await fs.pathExists(agentsPath)) { + const moduleAgents = await getAgentsFromDir(agentsPath, moduleName); + agents.push(...moduleAgents); + } + } + + return agents; + } + + /** + * Process module injections with pre-collected configuration + */ + async processModuleInjectionsWithConfig(projectDir, bmadDir, options, preCollectedConfig) { + // Get list of installed modules + const modules = options.selectedModules || []; + const { subagentChoices, installLocation } = preCollectedConfig; + + // Get the actual source directory (not the installation directory) + await this.processModuleInjectionsInternal({ + projectDir, + modules, + handler: 'claude-code', + subagentChoices, + installLocation, + interactive: false, + }); + } + + /** + * Process Claude Code specific injections for installed modules + * Looks for injections.yaml in each module's claude-code sub-module + */ + async processModuleInjections(projectDir, bmadDir, options) { + // Get list of installed modules + const modules = options.selectedModules || []; + let subagentChoices = null; + let installLocation = null; + + // Get the actual source directory (not the installation directory) + const { subagentChoices: updatedChoices, installLocation: updatedLocation } = await this.processModuleInjectionsInternal({ + projectDir, + modules, + handler: 'claude-code', + subagentChoices, + installLocation, + interactive: true, + }); + + if (updatedChoices) { + subagentChoices = updatedChoices; + } + if (updatedLocation) { + installLocation = updatedLocation; + } + } + + async processModuleInjectionsInternal({ projectDir, modules, handler, subagentChoices, installLocation, interactive = false }) { + let choices = subagentChoices; + let location = installLocation; + + for (const moduleName of modules) { + const configData = await loadModuleInjectionConfig(handler, moduleName); + + if (!configData) { + continue; + } + + const { config, handlerBaseDir } = configData; + + if (interactive) { + console.log(chalk.cyan(`\nConfiguring ${moduleName} ${handler.replace('-', ' ')} features...`)); + } + + if (interactive && config.subagents && !choices) { + // choices = await this.promptSubagentInstallation(config.subagents); + // if (choices.install !== 'none') { + // location = await this.promptInstallLocation(); + // } + } + + if (config.injections && choices && choices.install !== 'none') { + for (const injection of config.injections) { + if (shouldApplyInjection(injection, choices)) { + await this.injectContent(projectDir, injection, choices); + } + } + } + + if (config.subagents && choices && choices.install !== 'none') { + await this.copySelectedSubagents(projectDir, handlerBaseDir, config.subagents, choices, location || 'project'); + } + } + + return { subagentChoices: choices, installLocation: location }; + } + + /** + * Prompt user for subagent installation preferences + */ + async promptSubagentInstallation(subagentConfig) { + // First ask if they want to install subagents + const install = await prompts.select({ + message: 'Would you like to install Claude Code subagents for enhanced functionality?', + choices: [ + { name: 'Yes, install all subagents', value: 'all' }, + { name: 'Yes, let me choose specific subagents', value: 'selective' }, + { name: 'No, skip subagent installation', value: 'none' }, + ], + default: 'all', + }); + + if (install === 'selective') { + // Show list of available subagents with descriptions + const subagentInfo = { + 'market-researcher.md': 'Market research and competitive analysis', + 'requirements-analyst.md': 'Requirements extraction and validation', + 'technical-evaluator.md': 'Technology stack evaluation', + 'epic-optimizer.md': 'Epic and story breakdown optimization', + 'document-reviewer.md': 'Document quality review', + }; + + const selected = await prompts.multiselect({ + message: `Select subagents to install ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`, + options: subagentConfig.files.map((file) => ({ + label: `${file.replace('.md', '')} - ${subagentInfo[file] || 'Specialized assistant'}`, + value: file, + })), + initialValues: subagentConfig.files, + }); + + return { install: 'selective', selected }; + } + + return { install }; + } + + /** + * Inject content at specified point in file + */ + async injectContent(projectDir, injection, subagentChoices = null) { + const targetPath = path.join(projectDir, injection.file); + + if (await this.exists(targetPath)) { + let content = await fs.readFile(targetPath, 'utf8'); + const marker = ``; + + if (content.includes(marker)) { + let injectionContent = injection.content; + + // Filter content if selective subagents chosen + if (subagentChoices && subagentChoices.install === 'selective' && injection.point === 'pm-agent-instructions') { + injectionContent = filterAgentInstructions(injection.content, subagentChoices.selected); + } + + content = content.replace(marker, injectionContent); + await fs.writeFile(targetPath, content); + console.log(chalk.dim(` Injected: ${injection.point} → ${injection.file}`)); + } + } + } + + /** + * Copy selected subagents to appropriate Claude agents directory + */ + async copySelectedSubagents(projectDir, handlerBaseDir, subagentConfig, choices, location) { + const os = require('node:os'); + + // Determine target directory based on user choice + let targetDir; + if (location === 'user') { + targetDir = path.join(os.homedir(), '.claude', 'agents'); + console.log(chalk.dim(` Installing subagents globally to: ~/.claude/agents/`)); + } else { + targetDir = path.join(projectDir, '.claude', 'agents'); + console.log(chalk.dim(` Installing subagents to project: .claude/agents/`)); + } + + // Ensure target directory exists + await this.ensureDir(targetDir); + + const resolvedFiles = await resolveSubagentFiles(handlerBaseDir, subagentConfig, choices); + + let copiedCount = 0; + for (const resolved of resolvedFiles) { + try { + const sourcePath = resolved.absolutePath; + + const subFolder = path.dirname(resolved.relativePath); + let targetPath; + if (subFolder && subFolder !== '.') { + const targetSubDir = path.join(targetDir, subFolder); + await this.ensureDir(targetSubDir); + targetPath = path.join(targetSubDir, path.basename(resolved.file)); + } else { + targetPath = path.join(targetDir, path.basename(resolved.file)); + } + + await fs.copyFile(sourcePath, targetPath); + console.log(chalk.green(` ✓ Installed: ${subFolder === '.' ? '' : `${subFolder}/`}${path.basename(resolved.file, '.md')}`)); + copiedCount++; + } catch (error) { + console.log(chalk.yellow(` ⚠ Error copying ${resolved.file}: ${error.message}`)); + } + } + + if (copiedCount > 0) { + console.log(chalk.dim(` Total subagents installed: ${copiedCount}`)); + } + } + + /** + * Install a custom agent launcher for Claude Code + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created command + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + if (!(await this.exists(path.join(projectDir, this.configDir)))) { + return null; // IDE not configured for this project + } + + await this.ensureDir(commandsDir); + + const launcherContent = `--- +name: '${agentName}' +description: '${agentName} agent' +--- + +You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. + + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + +`; + + // Use underscore format: bmad_custom_fred-commit-poet.md + // Written directly to commands dir (no bmad subfolder) + const launcherName = customAgentColonName(agentName); + const launcherPath = path.join(commandsDir, launcherName); + await this.writeFile(launcherPath, launcherContent); + + return { + path: launcherPath, + command: `/${launcherName.replace('.md', '')}`, + }; + } +} + +module.exports = { ClaudeCodeSetup }; diff --git a/tools/cli/installers/lib/ide/cline.js b/tools/cli/installers/lib/ide/cline.js new file mode 100644 index 00000000..f2109d88 --- /dev/null +++ b/tools/cli/installers/lib/ide/cline.js @@ -0,0 +1,272 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const chalk = require('chalk'); +const { BaseIdeSetup } = require('./_base-ide'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { getAgentsFromBmad, getTasksFromBmad } = require('./shared/bmad-artifacts'); +const { toDashPath, customAgentDashName } = require('./shared/path-utils'); + +/** + * Cline IDE setup handler + * Installs BMAD artifacts to .clinerules/workflows with flattened naming + */ +class ClineSetup extends BaseIdeSetup { + constructor() { + super('cline', 'Cline', false); + this.configDir = '.clinerules'; + this.workflowsDir = 'workflows'; + } + + /** + * Setup Cline IDE 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}...`)); + + // Create .clinerules/workflows directory + const clineDir = path.join(projectDir, this.configDir); + const workflowsDir = path.join(clineDir, this.workflowsDir); + + await this.ensureDir(workflowsDir); + + // Clear old BMAD files + await this.clearOldBmadFiles(workflowsDir); + + // Collect all artifacts + const { artifacts, counts } = await this.collectClineArtifacts(projectDir, bmadDir, options); + + // Write flattened files + const written = await this.flattenAndWriteArtifacts(artifacts, workflowsDir); + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${counts.agents} agents installed`)); + console.log(chalk.dim(` - ${counts.tasks} tasks installed`)); + console.log(chalk.dim(` - ${counts.workflows} workflow commands installed`)); + if (counts.workflowLaunchers > 0) { + console.log(chalk.dim(` - ${counts.workflowLaunchers} workflow launchers installed`)); + } + console.log(chalk.dim(` - ${written} files written to ${path.relative(projectDir, workflowsDir)}`)); + + // Usage instructions + console.log(chalk.yellow('\n ⚠️ How to Use Cline Workflows')); + console.log(chalk.cyan(' BMAD workflows are available as slash commands in Cline')); + console.log(chalk.dim(' Usage:')); + console.log(chalk.dim(' - Type / to see available commands')); + console.log(chalk.dim(' - All BMAD items start with "bmad_"')); + console.log(chalk.dim(' - Example: /bmad_bmm_pm')); + + return { + success: true, + agents: counts.agents, + tasks: counts.tasks, + workflows: counts.workflows, + workflowLaunchers: counts.workflowLaunchers, + written, + }; + } + + /** + * Detect Cline installation by checking for .clinerules/workflows directory + */ + async detect(projectDir) { + const workflowsDir = path.join(projectDir, this.configDir, this.workflowsDir); + + if (!(await fs.pathExists(workflowsDir))) { + return false; + } + + const entries = await fs.readdir(workflowsDir); + return entries.some((entry) => entry.startsWith('bmad')); + } + + /** + * Collect all artifacts for Cline export + */ + async collectClineArtifacts(projectDir, bmadDir, options = {}) { + const selectedModules = options.selectedModules || []; + const artifacts = []; + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules); + + // Process agent launchers with project-specific paths + for (const agentArtifact of agentArtifacts) { + const content = agentArtifact.content; + + artifacts.push({ + type: 'agent', + module: agentArtifact.module, + sourcePath: agentArtifact.sourcePath, + relativePath: agentArtifact.relativePath, + content, + }); + } + + // Get tasks + const tasks = await getTasksFromBmad(bmadDir, selectedModules); + for (const task of tasks) { + const content = await this.readAndProcessWithProject( + task.path, + { + module: task.module, + name: task.name, + }, + projectDir, + ); + + artifacts.push({ + type: 'task', + module: task.module, + sourcePath: task.path, + relativePath: path.join(task.module, 'tasks', `${task.name}.md`), + content, + }); + } + + // Get workflows + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + artifacts.push(...workflowArtifacts); + + return { + artifacts, + counts: { + agents: agentArtifacts.length, + tasks: tasks.length, + workflows: workflowCounts.commands, + workflowLaunchers: workflowCounts.launchers, + }, + }; + } + + /** + * Flatten file path to bmad_module_type_name.md format + * Uses shared toDashPath utility + */ + flattenFilename(relativePath) { + return toDashPath(relativePath); + } + + /** + * Write all artifacts with flattened names + */ + async flattenAndWriteArtifacts(artifacts, destDir) { + let written = 0; + + for (const artifact of artifacts) { + const flattenedName = this.flattenFilename(artifact.relativePath); + const targetPath = path.join(destDir, flattenedName); + await fs.writeFile(targetPath, artifact.content); + written++; + } + + return written; + } + + /** + * Clear old BMAD files from the workflows directory + */ + async clearOldBmadFiles(destDir) { + if (!(await fs.pathExists(destDir))) { + return; + } + + const entries = await fs.readdir(destDir); + + for (const entry of entries) { + if (!entry.startsWith('bmad')) { + continue; + } + + const entryPath = path.join(destDir, entry); + const stat = await fs.stat(entryPath); + if (stat.isFile()) { + await fs.remove(entryPath); + } else if (stat.isDirectory()) { + await fs.remove(entryPath); + } + } + } + + /** + * Read and process file with project-specific paths + */ + async readAndProcessWithProject(filePath, metadata, projectDir) { + const content = await fs.readFile(filePath, 'utf8'); + return super.processContent(content, metadata, projectDir); + } + + /** + * Cleanup Cline configuration + */ + async cleanup(projectDir) { + const workflowsDir = path.join(projectDir, this.configDir, this.workflowsDir); + await this.clearOldBmadFiles(workflowsDir); + console.log(chalk.dim(`Removed ${this.name} BMAD configuration`)); + } + + /** + * Install a custom agent launcher for Cline + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const clineDir = path.join(projectDir, this.configDir); + const workflowsDir = path.join(clineDir, this.workflowsDir); + + // Create .clinerules/workflows directory if it doesn't exist + await fs.ensureDir(workflowsDir); + + // Create custom agent launcher workflow + const launcherContent = `name: ${agentName} +description: Custom BMAD agent: ${agentName} + +# ${agentName} Custom Agent + +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this workflow as ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +--- + +*Generated by BMAD Method*`; + + // Use underscore format: bmad_custom_fred-commit-poet.md + const fileName = customAgentDashName(agentName); + const launcherPath = path.join(workflowsDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'cline', + path: path.relative(projectDir, launcherPath), + command: fileName.replace('.md', ''), + type: 'custom-agent-launcher', + }; + } + + /** + * Utility: Ensure directory exists + */ + async ensureDir(dirPath) { + await fs.ensureDir(dirPath); + } +} + +module.exports = { ClineSetup }; diff --git a/tools/cli/installers/lib/ide/codex.js b/tools/cli/installers/lib/ide/codex.js index 3040d056..b632d4b7 100644 --- a/tools/cli/installers/lib/ide/codex.js +++ b/tools/cli/installers/lib/ide/codex.js @@ -2,85 +2,28 @@ const path = require('node:path'); const fs = require('fs-extra'); const os = require('node:os'); const chalk = require('chalk'); -const { ConfigDrivenIdeSetup } = require('./_config-driven'); -const { getSourcePath } = require('../../../lib/project-root'); +const { BaseIdeSetup } = require('./_base-ide'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { getTasksFromBmad } = require('./shared/bmad-artifacts'); +const { toDashPath, customAgentDashName } = require('./shared/path-utils'); const prompts = require('../../../lib/prompts'); /** * Codex setup handler (CLI mode) - * - * Extends config-driven setup with Codex-specific features: - * - Install location choice (global vs project-specific) - * - Configuration prompts - * - Detailed setup instructions */ -class CodexSetup extends ConfigDrivenIdeSetup { +class CodexSetup extends BaseIdeSetup { constructor() { - // Initialize with codex platform config - const platformConfig = { - name: 'Codex', - preferred: false, - installer: { - target_dir: '.codex/prompts', - frontmatter_template: 'none', // Codex uses no frontmatter - }, - }; - super('codex', platformConfig); + super('codex', 'Codex', true); // preferred IDE } /** - * Get the Codex agent command activation header from central template - * @returns {string} The activation header text + * Collect configuration choices before installation + * @param {Object} options - Configuration options + * @returns {Object} Collected configuration */ - async getAgentCommandHeader() { - const headerPath = getSourcePath('tools/cli/installers/lib/ide/templates', 'codex-agent-command-template.md'); - return await fs.readFile(headerPath, 'utf8'); - } - - /** - * Override setup to add install location choice and instructions - */ - async setup(projectDir, bmadDir, options = {}) { - console.log(chalk.cyan(`Setting up ${this.name}...`)); - - // Collect install location choice - const installLocation = options.preCollectedConfig?.installLocation || (await this.collectInstallLocation()); - - // Determine destination directory - const destDir = this.getCodexPromptDir(projectDir, installLocation); - await fs.ensureDir(destDir); - await this.clearOldBmadFiles(destDir); - - // Use unified installer with custom destination - const { UnifiedInstaller, NamingStyle } = require('./shared/unified-installer'); - const installer = new UnifiedInstaller(this.bmadFolderName); - const counts = await installer.install( - projectDir, - bmadDir, - { - targetDir: destDir, - namingStyle: NamingStyle.FLAT_DASH, - frontmatterTemplate: 'none', // Codex uses no frontmatter - }, - options.selectedModules || [], - ); - - // Show results and instructions - this.printResults(counts, destDir, installLocation); - - return { - success: true, - mode: 'cli', - ...counts, - destination: destDir, - installLocation, - }; - } - - /** - * Collect install location choice from user - */ - async collectInstallLocation() { + async collectConfiguration(options = {}) { let confirmed = false; let installLocation = 'global'; @@ -89,17 +32,18 @@ class CodexSetup extends ConfigDrivenIdeSetup { message: 'Where would you like to install Codex CLI prompts?', choices: [ { - name: 'Global - Simple for single project (~/codex/prompts, references THIS project only)', + name: 'Global - Simple for single project ' + '(~/.codex/prompts, but references THIS project only)', value: 'global', }, { - name: `Project-specific - Recommended for real work (requires CODEX_HOME=/.codex)`, + name: `Project-specific - Recommended for real work (requires CODEX_HOME=${path.sep}.codex)`, value: 'project', }, ], default: 'global', }); + // Display detailed instructions for the chosen option console.log(''); if (installLocation === 'project') { console.log(this.getProjectSpecificInstructions()); @@ -107,6 +51,7 @@ class CodexSetup extends ConfigDrivenIdeSetup { console.log(this.getGlobalInstructions()); } + // Confirm the choice confirmed = await prompts.confirm({ message: 'Proceed with this installation option?', default: true, @@ -121,144 +66,88 @@ class CodexSetup extends ConfigDrivenIdeSetup { } /** - * Get Codex prompts directory based on location choice + * Setup Codex configuration + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options */ - getCodexPromptDir(projectDir = null, location = 'global') { - if (location === 'project' && projectDir) { - return path.join(projectDir, '.codex', 'prompts'); - } - return path.join(os.homedir(), '.codex', 'prompts'); - } + async setup(projectDir, bmadDir, options = {}) { + console.log(chalk.cyan(`Setting up ${this.name}...`)); - /** - * Print results and instructions - */ - printResults(counts, destDir, installLocation) { - console.log(chalk.green(`✓ Codex configured:`)); + // Always use CLI mode + const mode = 'cli'; + + // Get installation location from pre-collected config or default to global + const installLocation = options.preCollectedConfig?.installLocation || 'global'; + + const { artifacts, counts } = await this.collectClaudeArtifacts(projectDir, bmadDir, options); + + const destDir = this.getCodexPromptDir(projectDir, installLocation); + await fs.ensureDir(destDir); + await this.clearOldBmadFiles(destDir); + + // Collect artifacts and write using underscore format + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + const agentCount = await agentGen.writeDashArtifacts(destDir, agentArtifacts); + + const tasks = await getTasksFromBmad(bmadDir, options.selectedModules || []); + const taskArtifacts = []; + for (const task of tasks) { + const content = await this.readAndProcessWithProject( + task.path, + { + module: task.module, + name: task.name, + }, + projectDir, + ); + taskArtifacts.push({ + type: 'task', + module: task.module, + sourcePath: task.path, + relativePath: path.join(task.module, 'tasks', `${task.name}.md`), + content, + }); + } + + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + const workflowCount = await workflowGenerator.writeDashArtifacts(destDir, workflowArtifacts); + + // Also write tasks using underscore format + const ttGen = new TaskToolCommandGenerator(); + const tasksWritten = await ttGen.writeDashArtifacts(destDir, taskArtifacts); + + const written = agentCount + workflowCount + tasksWritten; + + console.log(chalk.green(`✓ ${this.name} configured:`)); console.log(chalk.dim(` - Mode: CLI`)); - console.log(chalk.dim(` - Location: ${installLocation}`)); - console.log(chalk.dim(` - ${counts.agents} agents installed`)); - if (counts.workflows > 0) { - console.log(chalk.dim(` - ${counts.workflows} workflow commands generated`)); + console.log(chalk.dim(` - ${counts.agents} agents exported`)); + console.log(chalk.dim(` - ${counts.tasks} tasks exported`)); + console.log(chalk.dim(` - ${counts.workflows} workflow commands exported`)); + if (counts.workflowLaunchers > 0) { + console.log(chalk.dim(` - ${counts.workflowLaunchers} workflow launchers exported`)); } - if (counts.tasks + counts.tools > 0) { - console.log(chalk.dim(` - ${counts.tasks + counts.tools} task/tool commands (${counts.tasks} tasks, ${counts.tools} tools)`)); - } - console.log(chalk.dim(` - ${counts.total} files written`)); + console.log(chalk.dim(` - ${written} Codex prompt files written`)); console.log(chalk.dim(` - Destination: ${destDir}`)); - // Show setup instructions if project-specific - if (installLocation === 'project') { - console.log(''); - console.log(chalk.yellow(' Next steps:')); - console.log(chalk.dim(this.getProjectSpecificNextSteps())); - } + return { + success: true, + mode, + artifacts, + counts, + destination: destDir, + written, + installLocation, + }; } /** - * Get instructions for global installation - */ - getGlobalInstructions() { - const lines = [ - '', - chalk.bold.cyan('═'.repeat(70)), - chalk.bold.yellow(' IMPORTANT: Codex Configuration'), - chalk.bold.cyan('═'.repeat(70)), - '', - chalk.white(' /prompts installed globally to your HOME DIRECTORY.'), - '', - chalk.yellow(' ⚠️ These prompts reference a specific _bmad path'), - chalk.dim(" To use with other projects, you'd need to copy the _bmad dir"), - '', - chalk.green(' ✓ You can now use /commands in Codex CLI'), - chalk.dim(' Example: /bmad-bmm-pm'), - chalk.dim(' Type / to see all available commands'), - '', - chalk.bold.cyan('═'.repeat(70)), - '', - ]; - return lines.join('\n'); - } - - /** - * Get instructions for project-specific installation - */ - getProjectSpecificInstructions() { - const isWindows = os.platform() === 'win32'; - - const commonLines = [ - '', - chalk.bold.cyan('═'.repeat(70)), - chalk.bold.yellow(' Project-Specific Codex Configuration'), - chalk.bold.cyan('═'.repeat(70)), - '', - chalk.white(' Prompts will be installed to: ') + chalk.cyan('/.codex/prompts'), - '', - chalk.bold.yellow(' ⚠️ REQUIRED: You must set CODEX_HOME to use these prompts'), - '', - ]; - - const windowsLines = [ - chalk.bold(' Create a codex.cmd file in your project root:'), - '', - chalk.green(' @echo off'), - chalk.green(' set CODEX_HOME=%~dp0.codex'), - chalk.green(' codex %*'), - '', - chalk.dim(String.raw` Then run: .\codex instead of codex`), - chalk.dim(' (The %~dp0 gets the directory of the .cmd file)'), - ]; - - const unixLines = [ - chalk.bold(' Add this alias to your ~/.bashrc or ~/.zshrc:'), - '', - chalk.green(' alias codex=\'CODEX_HOME="$PWD/.codex" codex\''), - '', - chalk.dim(' After adding, run: source ~/.bashrc (or source ~/.zshrc)'), - chalk.dim(' (The $PWD uses your current working directory)'), - ]; - - return [...commonLines, ...(isWindows ? windowsLines : unixLines)].join('\n'); - } - - /** - * Get next steps for project-specific installation - */ - getProjectSpecificNextSteps() { - const isWindows = os.platform() === 'win32'; - if (isWindows) { - return `Create codex.cmd in project root with:\n set CODEX_HOME=%~dp0.codex\n codex %*`; - } - return `Add to ~/.bashrc or ~/.zshrc:\n alias codex='CODEX_HOME="$PWD/.codex" codex'`; - } - - /** - * Clear old BMAD files from destination - */ - async clearOldBmadFiles(destDir) { - if (!(await fs.pathExists(destDir))) { - return; - } - - const entries = await fs.readdir(destDir); - for (const entry of entries) { - if (!entry.startsWith('bmad')) { - continue; - } - const entryPath = path.join(destDir, entry); - const stat = await fs.stat(entryPath); - if (stat.isFile()) { - await fs.remove(entryPath); - } else if (stat.isDirectory()) { - await fs.remove(entryPath); - } - } - } - - /** - * Detect Codex installation (checks both global and project locations) + * Detect Codex installation by checking for BMAD prompt exports */ async detect(projectDir) { + // Check both global and project-specific locations const globalDir = this.getCodexPromptDir(null, 'global'); const projectDir_local = projectDir || process.cwd(); const projectSpecificDir = this.getCodexPromptDir(projectDir_local, 'project'); @@ -283,9 +172,193 @@ class CodexSetup extends ConfigDrivenIdeSetup { } /** - * Cleanup Codex configuration (both global and project-specific) + * Collect Claude-style artifacts for Codex export. + * Returns the normalized artifact list for further processing. + */ + async collectClaudeArtifacts(projectDir, bmadDir, options = {}) { + const selectedModules = options.selectedModules || []; + const artifacts = []; + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules); + + for (const artifact of agentArtifacts) { + artifacts.push({ + type: 'agent', + module: artifact.module, + sourcePath: artifact.sourcePath, + relativePath: artifact.relativePath, + content: artifact.content, + }); + } + + const tasks = await getTasksFromBmad(bmadDir, selectedModules); + for (const task of tasks) { + const content = await this.readAndProcessWithProject( + task.path, + { + module: task.module, + name: task.name, + }, + projectDir, + ); + + artifacts.push({ + type: 'task', + module: task.module, + sourcePath: task.path, + relativePath: path.join(task.module, 'tasks', `${task.name}.md`), + content, + }); + } + + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + artifacts.push(...workflowArtifacts); + + return { + artifacts, + counts: { + agents: agentArtifacts.length, + tasks: tasks.length, + workflows: workflowCounts.commands, + workflowLaunchers: workflowCounts.launchers, + }, + }; + } + + getCodexPromptDir(projectDir = null, location = 'global') { + if (location === 'project' && projectDir) { + return path.join(projectDir, '.codex', 'prompts'); + } + return path.join(os.homedir(), '.codex', 'prompts'); + } + + async flattenAndWriteArtifacts(artifacts, destDir) { + let written = 0; + + for (const artifact of artifacts) { + const flattenedName = this.flattenFilename(artifact.relativePath); + const targetPath = path.join(destDir, flattenedName); + await fs.writeFile(targetPath, artifact.content); + written++; + } + + return written; + } + + async clearOldBmadFiles(destDir) { + if (!(await fs.pathExists(destDir))) { + return; + } + + const entries = await fs.readdir(destDir); + + for (const entry of entries) { + if (!entry.startsWith('bmad')) { + continue; + } + + const entryPath = path.join(destDir, entry); + const stat = await fs.stat(entryPath); + if (stat.isFile()) { + await fs.remove(entryPath); + } else if (stat.isDirectory()) { + await fs.remove(entryPath); + } + } + } + + async readAndProcessWithProject(filePath, metadata, projectDir) { + const content = await fs.readFile(filePath, 'utf8'); + return super.processContent(content, metadata, projectDir); + } + + /** + * Get instructions for global installation + * @returns {string} Instructions text + */ + getGlobalInstructions(destDir) { + const lines = [ + '', + chalk.bold.cyan('═'.repeat(70)), + chalk.bold.yellow(' IMPORTANT: Codex Configuration'), + chalk.bold.cyan('═'.repeat(70)), + '', + chalk.white(' /prompts installed globally to your HOME DIRECTORY.'), + '', + chalk.yellow(' ⚠️ These prompts reference a specific _bmad path'), + chalk.dim(" To use with other projects, you'd need to copy the _bmad dir"), + '', + chalk.green(' ✓ You can now use /commands in Codex CLI'), + chalk.dim(' Example: /bmad_bmm_pm'), + chalk.dim(' Type / to see all available commands'), + '', + chalk.bold.cyan('═'.repeat(70)), + '', + ]; + return lines.join('\n'); + } + + /** + * Get instructions for project-specific installation + * @param {string} projectDir - Optional project directory + * @param {string} destDir - Optional destination directory + * @returns {string} Instructions text + */ + getProjectSpecificInstructions(projectDir = null, destDir = null) { + const isWindows = os.platform() === 'win32'; + + const commonLines = [ + '', + chalk.bold.cyan('═'.repeat(70)), + chalk.bold.yellow(' Project-Specific Codex Configuration'), + chalk.bold.cyan('═'.repeat(70)), + '', + chalk.white(' Prompts will be installed to: ') + chalk.cyan(destDir || '/.codex/prompts'), + '', + chalk.bold.yellow(' ⚠️ REQUIRED: You must set CODEX_HOME to use these prompts'), + '', + ]; + + const windowsLines = [ + chalk.bold(' Create a codex.cmd file in your project root:'), + '', + chalk.green(' @echo off'), + chalk.green(' set CODEX_HOME=%~dp0.codex'), + chalk.green(' codex %*'), + '', + chalk.dim(String.raw` Then run: .\codex instead of codex`), + chalk.dim(' (The %~dp0 gets the directory of the .cmd file)'), + ]; + + const unixLines = [ + chalk.bold(' Add this alias to your ~/.bashrc or ~/.zshrc:'), + '', + chalk.green(' alias codex=\'CODEX_HOME="$PWD/.codex" codex\''), + '', + chalk.dim(' After adding, run: source ~/.bashrc (or source ~/.zshrc)'), + chalk.dim(' (The $PWD uses your current working directory)'), + ]; + const closingLines = [ + '', + chalk.dim(' This tells Codex CLI to use prompts from this project instead of ~/.codex'), + '', + chalk.bold.cyan('═'.repeat(70)), + '', + ]; + + const lines = [...commonLines, ...(isWindows ? windowsLines : unixLines), ...closingLines]; + + return lines.join('\n'); + } + + /** + * Cleanup Codex configuration */ async cleanup(projectDir = null) { + // Clean both global and project-specific locations const globalDir = this.getCodexPromptDir(null, 'global'); await this.clearOldBmadFiles(globalDir); @@ -297,30 +370,37 @@ class CodexSetup extends ConfigDrivenIdeSetup { /** * Install a custom agent launcher for Codex + * @param {string} projectDir - Project directory (not used, Codex installs to home) + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created command */ async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { const destDir = this.getCodexPromptDir(projectDir, 'project'); await fs.ensureDir(destDir); - // Load the custom agent launcher template - const templatePath = getSourcePath('tools/cli/installers/lib/ide/templates', 'codex-custom-agent-template.md'); - let templateContent = await fs.readFile(templatePath, 'utf8'); + const launcherContent = `--- +name: '${agentName}' +description: '${agentName} agent' +--- - // Get activation header - const activationHeader = await this.getAgentCommandHeader(); +You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. - // Replace placeholders - const relativePath = `_bmad/${agentPath}`; - templateContent = templateContent - .replaceAll('{{name}}', agentName) - .replaceAll('{{description}}', `${agentName} agent`) - .replaceAll('{{activationHeader}}', activationHeader) - .replaceAll('{{relativePath}}', relativePath); + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + +`; - const { customAgentDashName } = require('./shared/path-utils'); + // Use underscore format: bmad_custom_fred-commit-poet.md const fileName = customAgentDashName(agentName); const launcherPath = path.join(destDir, fileName); - await fs.writeFile(launcherPath, templateContent, 'utf8'); + await fs.writeFile(launcherPath, launcherContent, 'utf8'); return { path: path.relative(projectDir, launcherPath), diff --git a/tools/cli/installers/lib/ide/crush.js b/tools/cli/installers/lib/ide/crush.js new file mode 100644 index 00000000..b9312d67 --- /dev/null +++ b/tools/cli/installers/lib/ide/crush.js @@ -0,0 +1,149 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { customAgentColonName } = require('./shared/path-utils'); + +/** + * Crush IDE setup handler + * Creates commands in .crush/commands/ directory structure using flat colon naming + */ +class CrushSetup extends BaseIdeSetup { + constructor() { + super('crush', 'Crush'); + this.configDir = '.crush'; + this.commandsDir = 'commands'; + } + + /** + * Setup Crush IDE 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 .crush/commands directory + const crushDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(crushDir, this.commandsDir); + await this.ensureDir(commandsDir); + + // Use underscore format: files written directly to commands dir (no bmad subfolder) + // Creates: .crush/commands/bmad_bmm_pm.md + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Write agent launcher files using flat underscore naming + // Creates files like: bmad_bmm_pm.md + const agentCount = await agentGen.writeColonArtifacts(commandsDir, agentArtifacts); + + // Get ALL workflows using the new workflow command generator + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + + // Write workflow-command artifacts using flat underscore naming + // Creates files like: bmad_bmm_correct-course.md + const workflowCount = await workflowGenerator.writeColonArtifacts(commandsDir, workflowArtifacts); + + // Generate task and tool commands using flat underscore naming + const taskToolGen = new TaskToolCommandGenerator(); + const taskToolResult = await taskToolGen.generateColonTaskToolCommands(projectDir, bmadDir, commandsDir); + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agent commands created`)); + console.log(chalk.dim(` - ${taskToolResult.tasks} task commands created`)); + console.log(chalk.dim(` - ${taskToolResult.tools} tool commands created`)); + console.log(chalk.dim(` - ${workflowCount} workflow commands created`)); + console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, commandsDir)}`)); + console.log(chalk.dim('\n Commands can be accessed via Crush command palette')); + + return { + success: true, + agents: agentCount, + tasks: taskToolResult.tasks || 0, + tools: taskToolResult.tools || 0, + workflows: workflowCount, + }; + } + + /** + * Cleanup Crush configuration + */ + async cleanup(projectDir) { + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + // Remove any bmad* files from the commands directory (cleans up old bmad: and bmad- formats) + if (await fs.pathExists(commandsDir)) { + const entries = await fs.readdir(commandsDir); + for (const entry of entries) { + if (entry.startsWith('bmad')) { + await fs.remove(path.join(commandsDir, entry)); + } + } + } + // Also remove legacy bmad folder if it exists + const bmadFolder = path.join(commandsDir, 'bmad'); + if (await fs.pathExists(bmadFolder)) { + await fs.remove(bmadFolder); + console.log(chalk.dim(`Removed BMAD commands from Crush`)); + } + } + + /** + * Install a custom agent launcher for Crush + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + // Create .crush/commands directory if it doesn't exist + await fs.ensureDir(commandsDir); + + // Create custom agent launcher + const launcherContent = `# ${agentName} Custom Agent + +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this command to activate ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +--- + +*Generated by BMAD Method*`; + + // Use underscore format: bmad_custom_fred-commit-poet.md + // Written directly to commands dir (no bmad subfolder) + const launcherName = customAgentColonName(agentName); + const launcherPath = path.join(commandsDir, launcherName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'crush', + path: path.relative(projectDir, launcherPath), + command: launcherName.replace('.md', ''), + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { CrushSetup }; diff --git a/tools/cli/installers/lib/ide/cursor.js b/tools/cli/installers/lib/ide/cursor.js new file mode 100644 index 00000000..771bba72 --- /dev/null +++ b/tools/cli/installers/lib/ide/cursor.js @@ -0,0 +1,160 @@ +const path = require('node:path'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { customAgentColonName } = require('./shared/path-utils'); + +/** + * Cursor IDE setup handler + */ +class CursorSetup extends BaseIdeSetup { + constructor() { + super('cursor', 'Cursor', true); // preferred IDE + this.configDir = '.cursor'; + this.rulesDir = 'rules'; + this.commandsDir = 'commands'; + } + + /** + * Cleanup old BMAD installation before reinstalling + * @param {string} projectDir - Project directory + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + // Remove any bmad* files from the commands directory (cleans up old bmad: and bmad- formats) + if (await fs.pathExists(commandsDir)) { + const entries = await fs.readdir(commandsDir); + for (const entry of entries) { + if (entry.startsWith('bmad')) { + await fs.remove(path.join(commandsDir, entry)); + } + } + } + // Also remove legacy bmad folder if it exists + const bmadFolder = path.join(commandsDir, 'bmad'); + if (await fs.pathExists(bmadFolder)) { + await fs.remove(bmadFolder); + console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`)); + } + } + + /** + * Setup Cursor IDE 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 .cursor/commands directory structure + const cursorDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(cursorDir, this.commandsDir); + await this.ensureDir(commandsDir); + + // Use underscore format: files written directly to commands dir (no bmad subfolder) + // Creates: .cursor/commands/bmad_bmm_pm.md + + // Generate agent launchers using AgentCommandGenerator + // This creates small launcher files that reference the actual agents in _bmad/ + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts, counts: agentCounts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Write agent launcher files using flat underscore naming + // Creates files like: bmad_bmm_pm.md + const agentCount = await agentGen.writeColonArtifacts(commandsDir, agentArtifacts); + + // Generate workflow commands from manifest (if it exists) + const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir); + + // Write workflow-command artifacts using flat underscore naming + // Creates files like: bmad_bmm_correct-course.md + const workflowCommandCount = await workflowGen.writeColonArtifacts(commandsDir, workflowArtifacts); + + // Generate task and tool commands from manifests (if they exist) + const taskToolGen = new TaskToolCommandGenerator(); + const taskToolResult = await taskToolGen.generateColonTaskToolCommands(projectDir, bmadDir, commandsDir); + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents installed`)); + if (workflowCommandCount > 0) { + console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated`)); + } + if (taskToolResult.generated > 0) { + console.log( + chalk.dim( + ` - ${taskToolResult.generated} task/tool commands generated (${taskToolResult.tasks} tasks, ${taskToolResult.tools} tools)`, + ), + ); + } + console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, commandsDir)}`)); + + return { + success: true, + agents: agentCount, + tasks: taskToolResult.tasks || 0, + tools: taskToolResult.tools || 0, + workflows: workflowCommandCount, + }; + } + + /** + * Install a custom agent launcher for Cursor + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created command + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + if (!(await this.exists(path.join(projectDir, this.configDir)))) { + return null; // IDE not configured for this project + } + + await this.ensureDir(commandsDir); + + 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. + + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + +`; + + // Cursor uses YAML frontmatter matching Claude Code format + const commandContent = `--- +name: '${agentName}' +description: '${agentName} agent' +--- + +${launcherContent} +`; + + // Use underscore format: bmad_custom_fred-commit-poet.md + // Written directly to commands dir (no bmad subfolder) + const launcherName = customAgentColonName(agentName); + const launcherPath = path.join(commandsDir, launcherName); + await this.writeFile(launcherPath, commandContent); + + return { + path: launcherPath, + command: `/${launcherName.replace('.md', '')}`, + }; + } +} + +module.exports = { CursorSetup }; diff --git a/tools/cli/installers/lib/ide/gemini.js b/tools/cli/installers/lib/ide/gemini.js new file mode 100644 index 00000000..a1673573 --- /dev/null +++ b/tools/cli/installers/lib/ide/gemini.js @@ -0,0 +1,301 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const yaml = require('yaml'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); + +/** + * Gemini CLI setup handler + * Creates TOML files in .gemini/commands/ structure + */ +class GeminiSetup extends BaseIdeSetup { + constructor() { + super('gemini', 'Gemini CLI', false); + this.configDir = '.gemini'; + this.commandsDir = 'commands'; + this.agentTemplatePath = path.join(__dirname, 'templates', 'gemini-agent-command.toml'); + this.taskTemplatePath = path.join(__dirname, 'templates', 'gemini-task-command.toml'); + } + + /** + * Load config values from bmad installation + * @param {string} bmadDir - BMAD installation directory + * @returns {Object} Config values + */ + async loadConfigValues(bmadDir) { + const configValues = { + user_name: 'User', // Default fallback + }; + + // Try to load core config.yaml + const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml'); + if (await fs.pathExists(coreConfigPath)) { + try { + const configContent = await fs.readFile(coreConfigPath, 'utf8'); + const config = yaml.parse(configContent); + + if (config.user_name) { + configValues.user_name = config.user_name; + } + } catch (error) { + console.warn(chalk.yellow(` Warning: Could not load config values: ${error.message}`)); + } + } + + return configValues; + } + + /** + * Setup Gemini CLI 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}...`)); + + // Create .gemini/commands directory (flat structure with bmad- prefix) + const geminiDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(geminiDir, this.commandsDir); + + await this.ensureDir(commandsDir); + + // Clean up any existing BMAD files before reinstalling + await this.cleanup(projectDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Get tasks and workflows (ALL workflows now generate commands) + const tasks = await this.getTasks(bmadDir); + + // Get ALL workflows using the new workflow command generator + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + + // Install agents as TOML files with bmad- prefix (flat structure) + let agentCount = 0; + for (const artifact of agentArtifacts) { + const tomlContent = await this.createAgentLauncherToml(artifact); + + // Flat structure: bmad-agent-{module}-{name}.toml + const tomlPath = path.join(commandsDir, `bmad-agent-${artifact.module}-${artifact.name}.toml`); + await this.writeFile(tomlPath, tomlContent); + agentCount++; + + console.log(chalk.green(` ✓ Added agent: /bmad_agents_${artifact.module}_${artifact.name}`)); + } + + // Install tasks as TOML files with bmad- prefix (flat structure) + let taskCount = 0; + for (const task of tasks) { + const content = await this.readFile(task.path); + const tomlContent = await this.createTaskToml(task, content); + + // Flat structure: bmad-task-{module}-{name}.toml + const tomlPath = path.join(commandsDir, `bmad-task-${task.module}-${task.name}.toml`); + await this.writeFile(tomlPath, tomlContent); + taskCount++; + + console.log(chalk.green(` ✓ Added task: /bmad_tasks_${task.module}_${task.name}`)); + } + + // Install workflows as TOML files with bmad- prefix (flat structure) + let workflowCount = 0; + for (const artifact of workflowArtifacts) { + if (artifact.type === 'workflow-command') { + // Create TOML wrapper around workflow command content + const tomlContent = await this.createWorkflowToml(artifact); + + // Flat structure: bmad-workflow-{module}-{name}.toml + const workflowName = path.basename(artifact.relativePath, '.md'); + const tomlPath = path.join(commandsDir, `bmad-workflow-${artifact.module}-${workflowName}.toml`); + await this.writeFile(tomlPath, tomlContent); + workflowCount++; + + console.log(chalk.green(` ✓ Added workflow: /bmad_workflows_${artifact.module}_${workflowName}`)); + } + } + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents configured`)); + console.log(chalk.dim(` - ${taskCount} tasks configured`)); + console.log(chalk.dim(` - ${workflowCount} workflows configured`)); + console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, commandsDir)}`)); + console.log(chalk.dim(` - Agent activation: /bmad_agents_{agent-name}`)); + console.log(chalk.dim(` - Task activation: /bmad_tasks_{task-name}`)); + console.log(chalk.dim(` - Workflow activation: /bmad_workflows_{workflow-name}`)); + + return { + success: true, + agents: agentCount, + tasks: taskCount, + workflows: workflowCount, + }; + } + + /** + * Create agent launcher TOML content from artifact + */ + async createAgentLauncherToml(artifact) { + // Strip frontmatter from launcher content + const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; + const contentWithoutFrontmatter = artifact.content.replace(frontmatterRegex, '').trim(); + + // Extract title from launcher frontmatter + const titleMatch = artifact.content.match(/description:\s*"([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(artifact.name); + + // Create TOML wrapper around launcher content (without frontmatter) + const description = `BMAD ${artifact.module.toUpperCase()} Agent: ${title}`; + + return `description = "${description}" +prompt = """ +${contentWithoutFrontmatter} +""" +`; + } + + /** + * Create agent TOML content using template + */ + async createAgentToml(agent, content) { + // Extract metadata + const titleMatch = content.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name); + + // Load template + const template = await fs.readFile(this.agentTemplatePath, 'utf8'); + + // Replace template variables + // Note: {user_name} and other {config_values} are left as-is for runtime substitution by Gemini + const tomlContent = template + .replaceAll('{{title}}', title) + .replaceAll('{_bmad}', '_bmad') + .replaceAll('{_bmad}', this.bmadFolderName) + .replaceAll('{{module}}', agent.module) + .replaceAll('{{name}}', agent.name); + + return tomlContent; + } + + /** + * Create task TOML content using template + */ + async createTaskToml(task, content) { + // Extract task name from XML if available + const nameMatch = content.match(/([^<]+)<\/name>/); + const taskName = nameMatch ? nameMatch[1] : this.formatTitle(task.name); + + // Load template + const template = await fs.readFile(this.taskTemplatePath, 'utf8'); + + // Replace template variables + const tomlContent = template + .replaceAll('{{taskName}}', taskName) + .replaceAll('{_bmad}', '_bmad') + .replaceAll('{_bmad}', this.bmadFolderName) + .replaceAll('{{module}}', task.module) + .replaceAll('{{filename}}', task.filename); + + return tomlContent; + } + + /** + * Create workflow TOML content from artifact + */ + async createWorkflowToml(artifact) { + // Extract description from artifact content + const descriptionMatch = artifact.content.match(/description:\s*"([^"]+)"/); + const description = descriptionMatch + ? descriptionMatch[1] + : `BMAD ${artifact.module.toUpperCase()} Workflow: ${path.basename(artifact.relativePath, '.md')}`; + + // Strip frontmatter from command content + const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; + const contentWithoutFrontmatter = artifact.content.replace(frontmatterRegex, '').trim(); + + return `description = "${description}" +prompt = """ +${contentWithoutFrontmatter} +""" +`; + } + + /** + * Cleanup Gemini configuration - surgically remove only BMAD files + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + if (await fs.pathExists(commandsDir)) { + // Remove any bmad* files (cleans up old bmad- and bmad: formats) + const files = await fs.readdir(commandsDir); + let removed = 0; + + for (const file of files) { + if (file.startsWith('bmad') && file.endsWith('.toml')) { + await fs.remove(path.join(commandsDir, file)); + removed++; + } + } + + if (removed > 0) { + console.log(chalk.dim(` Cleaned up ${removed} existing BMAD files`)); + } + } + } + + /** + * Install a custom agent launcher for Gemini + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const geminiDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(geminiDir, this.commandsDir); + + // Create .gemini/commands directory if it doesn't exist + await fs.ensureDir(commandsDir); + + // Create custom agent launcher in TOML format + const launcherContent = `description = "Custom BMAD Agent: ${agentName}" +prompt = """ +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this command to activate ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +--- + +*Generated by BMAD Method* +"""`; + + const fileName = `bmad-custom-${agentName.toLowerCase()}.toml`; + const launcherPath = path.join(commandsDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'gemini', + path: path.relative(projectDir, launcherPath), + command: agentName, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { GeminiSetup }; diff --git a/tools/cli/installers/lib/ide/github-copilot.js b/tools/cli/installers/lib/ide/github-copilot.js new file mode 100644 index 00000000..a7c6c925 --- /dev/null +++ b/tools/cli/installers/lib/ide/github-copilot.js @@ -0,0 +1,383 @@ +const path = require('node:path'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const prompts = require('../../../lib/prompts'); + +/** + * GitHub Copilot setup handler + * 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.agentsDir = 'agents'; + this.vscodeDir = '.vscode'; + } + + /** + * Collect configuration choices before installation + * @param {Object} options - Configuration options + * @returns {Object} Collected configuration + */ + async collectConfiguration(options = {}) { + const config = {}; + + console.log('\n' + chalk.blue(' 🔧 VS Code Settings Configuration')); + console.log(chalk.dim(' GitHub Copilot works best with specific settings\n')); + + config.vsCodeConfig = await prompts.select({ + message: 'How would you like to configure VS Code settings?', + choices: [ + { name: 'Use recommended defaults (fastest)', value: 'defaults' }, + { name: 'Configure each setting manually', value: 'manual' }, + { name: 'Skip settings configuration', value: 'skip' }, + ], + default: 'defaults', + }); + + if (config.vsCodeConfig === 'manual') { + config.manualSettings = await prompts.prompt([ + { + type: 'input', + name: 'maxRequests', + message: 'Maximum requests per session (1-50)?', + default: '15', + validate: (input) => { + const num = parseInt(input, 10); + if (isNaN(num)) return 'Enter a valid number 1-50'; + if (num < 1 || num > 50) return 'Enter a number between 1-50'; + return true; + }, + }, + { + type: 'confirm', + name: 'runTasks', + message: 'Allow running workspace tasks?', + default: true, + }, + { + type: 'confirm', + name: 'mcpDiscovery', + message: 'Enable MCP server discovery?', + default: true, + }, + { + type: 'confirm', + name: 'autoFix', + message: 'Enable automatic error fixing?', + default: true, + }, + { + type: 'confirm', + name: 'autoApprove', + message: 'Auto-approve tools (less secure)?', + default: false, + }, + ]); + } + + return config; + } + + /** + * Setup GitHub Copilot 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}...`)); + + // Configure VS Code settings using pre-collected config if available + const config = options.preCollectedConfig || {}; + await this.configureVsCodeSettings(projectDir, { ...options, ...config }); + + // Create .github/agents directory + const githubDir = path.join(projectDir, this.configDir); + const agentsDir = path.join(githubDir, this.agentsDir); + await this.ensureDir(agentsDir); + + // Clean up any existing BMAD files before reinstalling + await this.cleanup(projectDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Create agent files with bmd- prefix + let agentCount = 0; + for (const artifact of agentArtifacts) { + const content = artifact.content; + const agentContent = await this.createAgentContent({ module: artifact.module, name: artifact.name }, content); + + // 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 agent: bmd-custom-${artifact.module}-${artifact.name}`)); + } + + console.log(chalk.green(`✓ ${this.name} configured:`)); + 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 Agents available in VS Code Chat view')); + + return { + success: true, + agents: agentCount, + settings: true, + }; + } + + /** + * Configure VS Code settings for GitHub Copilot + */ + async configureVsCodeSettings(projectDir, options) { + const fs = require('fs-extra'); + const vscodeDir = path.join(projectDir, this.vscodeDir); + const settingsPath = path.join(vscodeDir, 'settings.json'); + + await this.ensureDir(vscodeDir); + + // Read existing settings + let existingSettings = {}; + if (await fs.pathExists(settingsPath)) { + try { + const content = await fs.readFile(settingsPath, 'utf8'); + existingSettings = JSON.parse(content); + console.log(chalk.yellow(' Found existing .vscode/settings.json')); + } catch { + console.warn(chalk.yellow(' Could not parse settings.json, creating new')); + } + } + + // Use pre-collected configuration or skip if not available + let configChoice = options.vsCodeConfig; + if (!configChoice) { + // If no pre-collected config, skip configuration + console.log(chalk.yellow(' ⚠ No configuration collected, skipping VS Code settings')); + return; + } + + if (configChoice === 'skip') { + console.log(chalk.yellow(' ⚠ Skipping VS Code settings')); + return; + } + + let bmadSettings = {}; + + if (configChoice === 'defaults') { + bmadSettings = { + 'chat.agent.enabled': true, + 'chat.agent.maxRequests': 15, + 'github.copilot.chat.agent.runTasks': true, + 'chat.mcp.discovery.enabled': true, + 'github.copilot.chat.agent.autoFix': true, + 'chat.tools.autoApprove': false, + }; + console.log(chalk.green(' ✓ Using recommended defaults')); + } else { + // 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': 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, + 'chat.tools.autoApprove': manual.autoApprove || false, + }; + } + + // Merge settings (existing take precedence) + const mergedSettings = { ...bmadSettings, ...existingSettings }; + + // Write settings + await fs.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2)); + console.log(chalk.green(' ✓ VS Code settings configured')); + } + + /** + * Create 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); + + const description = `Activates the ${title} agent persona.`; + + // Strip any existing frontmatter from the content + const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; + let cleanContent = content; + if (frontmatterRegex.test(content)) { + cleanContent = content.replace(frontmatterRegex, '').trim(); + } + + // Available GitHub Copilot tools (November 2025 - Official VS Code Documentation) + // Reference: https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools + const tools = [ + 'changes', // List of source control changes + 'edit', // Edit files in your workspace including: createFile, createDirectory, editNotebook, newJupyterNotebook and editFiles + 'fetch', // Fetch content from web page + 'githubRepo', // Perform code search in GitHub repo + 'problems', // Add workspace issues from Problems panel + '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 + '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 + 'todos', // Tool for managing and tracking todo items for task planning + 'usages', // Find references and navigate definitions + ]; + + let agentContent = `--- +description: "${description.replaceAll('"', String.raw`\"`)}" +tools: ${JSON.stringify(tools)} +--- + +# ${title} Agent + +${cleanContent} + +`; + + return agentContent; + } + + /** + * Format name as title + */ + formatTitle(name) { + return name + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } + + /** + * Cleanup GitHub Copilot configuration - surgically remove only BMAD files + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + + // Clean up old chatmodes directory + const chatmodesDir = path.join(projectDir, this.configDir, 'chatmodes'); + if (await fs.pathExists(chatmodesDir)) { + const files = await fs.readdir(chatmodesDir); + let removed = 0; + + for (const file of files) { + if (file.startsWith('bmad') && file.endsWith('.chatmode.md')) { + await fs.remove(path.join(chatmodesDir, file)); + removed++; + } + } + + if (removed > 0) { + 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`)); + } + } + } + + /** + * Install a custom agent launcher for GitHub Copilot + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created command + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + 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(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. + + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + +`; + + // GitHub Copilot needs specific tools in frontmatter + const copilotTools = [ + 'changes', + 'codebase', + 'createDirectory', + 'createFile', + 'editFiles', + 'fetch', + 'fileSearch', + 'githubRepo', + 'listDirectory', + 'problems', + 'readFile', + 'runInTerminal', + 'runTask', + 'runTests', + 'runVscodeCommand', + 'search', + 'searchResults', + 'terminalLastCommand', + 'terminalSelection', + 'testFailure', + 'textSearch', + 'usages', + ]; + + const agentContent = `--- +description: "Activates the ${metadata.title || agentName} agent persona." +tools: ${JSON.stringify(copilotTools)} +--- + +# ${metadata.title || agentName} Agent + +${launcherContent} +`; + + const agentFilePath = path.join(agentsDir, `bmd-custom-${agentName}.agent.md`); + await this.writeFile(agentFilePath, agentContent); + + return { + path: agentFilePath, + command: `bmd-custom-${agentName}`, + }; + } +} + +module.exports = { GitHubCopilotSetup }; diff --git a/tools/cli/installers/lib/ide/iflow.js b/tools/cli/installers/lib/ide/iflow.js new file mode 100644 index 00000000..bbe6d470 --- /dev/null +++ b/tools/cli/installers/lib/ide/iflow.js @@ -0,0 +1,191 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); + +/** + * iFlow CLI setup handler + * Creates commands in .iflow/commands/ directory structure + */ +class IFlowSetup extends BaseIdeSetup { + constructor() { + super('iflow', 'iFlow CLI'); + this.configDir = '.iflow'; + this.commandsDir = 'commands'; + } + + /** + * Setup iFlow CLI 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}...`)); + + // Create .iflow/commands/bmad directory structure + const iflowDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(iflowDir, this.commandsDir, 'bmad'); + const agentsDir = path.join(commandsDir, 'agents'); + const tasksDir = path.join(commandsDir, 'tasks'); + const workflowsDir = path.join(commandsDir, 'workflows'); + + await this.ensureDir(agentsDir); + await this.ensureDir(tasksDir); + await this.ensureDir(workflowsDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Setup agents as commands + let agentCount = 0; + for (const artifact of agentArtifacts) { + const commandContent = await this.createAgentCommand(artifact); + + const targetPath = path.join(agentsDir, `${artifact.module}-${artifact.name}.md`); + await this.writeFile(targetPath, commandContent); + agentCount++; + } + + // Get tasks and workflows (ALL workflows now generate commands) + const tasks = await this.getTasks(bmadDir); + + // Get ALL workflows using the new workflow command generator + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + + // Setup tasks as commands + let taskCount = 0; + for (const task of tasks) { + const content = await this.readFile(task.path); + const commandContent = this.createTaskCommand(task, content); + + const targetPath = path.join(tasksDir, `${task.module}-${task.name}.md`); + await this.writeFile(targetPath, commandContent); + taskCount++; + } + + // Setup workflows as commands (already generated) + let workflowCount = 0; + for (const artifact of workflowArtifacts) { + if (artifact.type === 'workflow-command') { + const targetPath = path.join(workflowsDir, `${artifact.module}-${path.basename(artifact.relativePath, '.md')}.md`); + await this.writeFile(targetPath, artifact.content); + workflowCount++; + } + } + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agent commands created`)); + console.log(chalk.dim(` - ${taskCount} task commands created`)); + console.log(chalk.dim(` - ${workflowCount} workflow commands created`)); + console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, commandsDir)}`)); + + return { + success: true, + agents: agentCount, + tasks: taskCount, + workflows: workflowCount, + }; + } + + /** + * Create agent command content + */ + async createAgentCommand(artifact) { + // The launcher content is already complete - just return it as-is + return artifact.content; + } + + /** + * Create task command content + */ + createTaskCommand(task, content) { + // Extract task name + const nameMatch = content.match(/([^<]+)<\/name>/); + const taskName = nameMatch ? nameMatch[1] : this.formatTitle(task.name); + + let commandContent = `# /task-${task.name} Command + +When this command is used, execute the following task: + +## ${taskName} Task + +${content} + +## Usage + +This command executes the ${taskName} task from the BMAD ${task.module.toUpperCase()} module. + +## Module + +Part of the BMAD ${task.module.toUpperCase()} module. +`; + + return commandContent; + } + + /** + * Cleanup iFlow configuration + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const bmadCommandsDir = path.join(projectDir, this.configDir, this.commandsDir, 'bmad'); + + if (await fs.pathExists(bmadCommandsDir)) { + await fs.remove(bmadCommandsDir); + console.log(chalk.dim(`Removed BMAD commands from iFlow CLI`)); + } + } + + /** + * Install a custom agent launcher for iFlow + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const iflowDir = path.join(projectDir, this.configDir); + const bmadCommandsDir = path.join(iflowDir, this.commandsDir, 'bmad'); + + // Create .iflow/commands/bmad directory if it doesn't exist + await fs.ensureDir(bmadCommandsDir); + + // Create custom agent launcher + const launcherContent = `# ${agentName} Custom Agent + +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this command to activate ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +--- + +*Generated by BMAD Method*`; + + const fileName = `custom-${agentName.toLowerCase()}.md`; + const launcherPath = path.join(bmadCommandsDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'iflow', + path: path.relative(projectDir, launcherPath), + command: agentName, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { IFlowSetup }; diff --git a/tools/cli/installers/lib/ide/kilo.js b/tools/cli/installers/lib/ide/kilo.js index 1f6cf82f..45e38021 100644 --- a/tools/cli/installers/lib/ide/kilo.js +++ b/tools/cli/installers/lib/ide/kilo.js @@ -115,20 +115,18 @@ class KiloSetup extends BaseIdeSetup { // Build mode entry (KiloCode uses same schema as Roo) const slug = `bmad-${artifact.module}-${artifact.name}`; - const modeEntry = ` - slug: ${slug} - name: '${icon} ${title}' - roleDefinition: ${roleDefinition} - whenToUse: ${whenToUse} - customInstructions: | - ${activationHeader.trim()} - Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode - groups: - - read - - edit - - browser - - command - - mcp -`; + let modeEntry = ` - slug: ${slug}\n`; + modeEntry += ` name: '${icon} ${title}'\n`; + modeEntry += ` roleDefinition: ${roleDefinition}\n`; + modeEntry += ` whenToUse: ${whenToUse}\n`; + modeEntry += ` customInstructions: |\n`; + modeEntry += ` ${activationHeader} Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`; + modeEntry += ` groups:\n`; + modeEntry += ` - read\n`; + modeEntry += ` - edit\n`; + modeEntry += ` - browser\n`; + modeEntry += ` - command\n`; + modeEntry += ` - mcp\n`; return modeEntry; } diff --git a/tools/cli/installers/lib/ide/manager.js b/tools/cli/installers/lib/ide/manager.js index b31f571e..97462746 100644 --- a/tools/cli/installers/lib/ide/manager.js +++ b/tools/cli/installers/lib/ide/manager.js @@ -1,36 +1,16 @@ const fs = require('fs-extra'); const path = require('node:path'); const chalk = require('chalk'); -const yaml = require('yaml'); -const { ConfigDrivenIdeSetup, loadPlatformCodes } = require('./_config-driven'); /** * IDE Manager - handles IDE-specific setup * Dynamically discovers and loads IDE handlers - * - * NEW: Loads config-driven handlers from platform-codes.yaml - * Custom installer files (like kilo.js, kiro-cli.js) are still supported - * for IDEs with truly unique requirements. */ class IdeManager { constructor() { this.handlers = new Map(); - this.platformConfig = null; + this.loadHandlers(); this.bmadFolderName = 'bmad'; // Default, can be overridden - this._initialized = false; - // Load custom handlers synchronously - this.loadCustomInstallerFiles(__dirname); - } - - /** - * Ensure handlers are initialized (loads config-driven handlers) - * Call this before using handlers if needed - */ - async ensureInitialized() { - if (!this._initialized) { - await this.loadConfigDrivenHandlers(); - this._initialized = true; - } } /** @@ -48,28 +28,15 @@ class IdeManager { } /** - * Dynamically load all IDE handlers - * - * Loading order: - * 1. Load custom installer files (kilo.js, kiro-cli.js) for IDEs with unique requirements - * 2. Load config-driven handlers from platform-codes.yaml for all other IDEs - * @deprecated Use ensureInitialized() instead + * Dynamically load all IDE handlers from directory */ - async loadHandlers() { - await this.ensureInitialized(); - } + loadHandlers() { + const ideDir = __dirname; - /** - * Load custom installer files (for IDEs with truly unique requirements) - * Synchronous version for constructor - * @param {string} ideDir - IDE handlers directory - */ - loadCustomInstallerFiles(ideDir) { try { // Get all JS files in the IDE directory const files = fs.readdirSync(ideDir).filter((file) => { - // Skip base class, manager, config-driven, utility files (starting with _) - // Also skip shared directory and generator files + // Skip base class, manager, utility files (starting with _), and helper modules return ( file.endsWith('.js') && !file.startsWith('_') && @@ -107,64 +74,15 @@ class IdeManager { } } } catch (error) { - console.error(chalk.red('Failed to load custom IDE handlers:'), error.message); - } - } - - /** - * Load config-driven handlers from platform-codes.yaml - * Async version called by ensureInitialized() - */ - async loadConfigDrivenHandlers() { - try { - // Load platform-codes.yaml configuration - this.platformConfig = await loadPlatformCodes(); - - // Create config-driven handlers for platforms with installer config - if (this.platformConfig.platforms) { - for (const [platformCode, platformInfo] of Object.entries(this.platformConfig.platforms)) { - // Skip if custom handler already exists - if (this.handlers.has(platformCode)) { - continue; - } - - // Skip if no installer config - if (!platformInfo.installer) { - continue; - } - - try { - const handler = new ConfigDrivenIdeSetup(platformCode, platformInfo); - handler.setBmadFolderName(this.bmadFolderName); - this.handlers.set(platformCode, handler); - } catch (error) { - console.warn(chalk.yellow(` Warning: Could not create config-driven handler for ${platformCode}: ${error.message}`)); - } - } - } - - // Log summary - const customCount = [...this.handlers.entries()].filter(([key]) => { - const handler = this.handlers.get(key); - return handler && !(handler instanceof ConfigDrivenIdeSetup); - }).length; - const configCount = [...this.handlers.entries()].filter(([key]) => { - const handler = this.handlers.get(key); - return handler && handler instanceof ConfigDrivenIdeSetup; - }).length; - console.log(chalk.dim(` Loaded ${customCount} custom handlers, ${configCount} config-driven handlers`)); - } catch (error) { - console.error(chalk.red('Failed to load config-driven handlers:'), error.message); + console.error(chalk.red('Failed to load IDE handlers:'), error.message); } } /** * Get all available IDEs with their metadata - * @returns {Promise} Array of IDE information objects + * @returns {Array} Array of IDE information objects */ - async getAvailableIdes() { - await this.ensureInitialized(); - + getAvailableIdes() { const ides = []; for (const [key, handler] of this.handlers) { @@ -195,20 +113,18 @@ class IdeManager { /** * Get preferred IDEs - * @returns {Promise} Array of preferred IDE information + * @returns {Array} Array of preferred IDE information */ - async getPreferredIdes() { - const ides = await this.getAvailableIdes(); - return ides.filter((ide) => ide.preferred); + getPreferredIdes() { + return this.getAvailableIdes().filter((ide) => ide.preferred); } /** * Get non-preferred IDEs - * @returns {Promise} Array of non-preferred IDE information + * @returns {Array} Array of non-preferred IDE information */ - async getOtherIdes() { - const ides = await this.getAvailableIdes(); - return ides.filter((ide) => !ide.preferred); + getOtherIdes() { + return this.getAvailableIdes().filter((ide) => !ide.preferred); } /** @@ -219,8 +135,6 @@ class IdeManager { * @param {Object} options - Setup options */ async setup(ideName, projectDir, bmadDir, options = {}) { - await this.ensureInitialized(); - const handler = this.handlers.get(ideName.toLowerCase()); if (!handler) { diff --git a/tools/cli/installers/lib/ide/opencode.js b/tools/cli/installers/lib/ide/opencode.js new file mode 100644 index 00000000..3ca6aeb4 --- /dev/null +++ b/tools/cli/installers/lib/ide/opencode.js @@ -0,0 +1,257 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const os = require('node:os'); +const chalk = require('chalk'); +const yaml = require('yaml'); +const { BaseIdeSetup } = require('./_base-ide'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); + +/** + * OpenCode IDE setup handler + */ +class OpenCodeSetup extends BaseIdeSetup { + constructor() { + super('opencode', 'OpenCode', true); // Mark as preferred/recommended + this.configDir = '.opencode'; + this.commandsDir = 'command'; + this.agentsDir = 'agent'; + } + + async setup(projectDir, bmadDir, options = {}) { + console.log(chalk.cyan(`Setting up ${this.name}...`)); + + const baseDir = path.join(projectDir, this.configDir); + const commandsBaseDir = path.join(baseDir, this.commandsDir); + const agentsBaseDir = path.join(baseDir, this.agentsDir); + + await this.ensureDir(commandsBaseDir); + await this.ensureDir(agentsBaseDir); + + // Clean up any existing BMAD files before reinstalling + await this.cleanup(projectDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Install primary agents with flat naming: bmad-agent-{module}-{name}.md + // OpenCode agents go in the agent folder (not command folder) + let agentCount = 0; + for (const artifact of agentArtifacts) { + const agentContent = artifact.content; + // Flat structure in agent folder: bmad-agent-{module}-{name}.md + const targetPath = path.join(agentsBaseDir, `bmad-agent-${artifact.module}-${artifact.name}.md`); + await this.writeFile(targetPath, agentContent); + agentCount++; + } + + // Install workflow commands with flat naming: bmad-{module}-{workflow-name} + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + + let workflowCommandCount = 0; + for (const artifact of workflowArtifacts) { + if (artifact.type === 'workflow-command') { + const commandContent = artifact.content; + // Flat structure: bmad-{module}-{workflow-name}.md + // artifact.relativePath is like: bmm/workflows/plan-project.md + const workflowName = path.basename(artifact.relativePath, '.md'); + const targetPath = path.join(commandsBaseDir, `bmad-${artifact.module}-${workflowName}.md`); + await this.writeFile(targetPath, commandContent); + workflowCommandCount++; + } + // Skip workflow launcher READMEs as they're not needed in flat structure + } + + // Install task and tool commands with flat naming + const { tasks, tools } = await this.generateFlatTaskToolCommands(bmadDir, commandsBaseDir); + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents installed to .opencode/agent/`)); + if (workflowCommandCount > 0) { + console.log(chalk.dim(` - ${workflowCommandCount} workflows installed to .opencode/command/`)); + } + if (tasks + tools > 0) { + console.log(chalk.dim(` - ${tasks + tools} tasks/tools installed to .opencode/command/ (${tasks} tasks, ${tools} tools)`)); + } + + return { + success: true, + agents: agentCount, + workflows: workflowCommandCount, + workflowCounts, + }; + } + + /** + * Generate flat task and tool commands for OpenCode + * OpenCode doesn't support nested command directories + */ + async generateFlatTaskToolCommands(bmadDir, commandsBaseDir) { + const taskToolGen = new TaskToolCommandGenerator(); + const tasks = await taskToolGen.loadTaskManifest(bmadDir); + const tools = await taskToolGen.loadToolManifest(bmadDir); + + // Filter to only standalone items + const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + + // Generate command files for tasks with flat naming: bmad-task-{module}-{name}.md + for (const task of standaloneTasks) { + const commandContent = taskToolGen.generateCommandContent(task, 'task'); + const targetPath = path.join(commandsBaseDir, `bmad-task-${task.module}-${task.name}.md`); + await this.writeFile(targetPath, commandContent); + } + + // Generate command files for tools with flat naming: bmad-tool-{module}-{name}.md + for (const tool of standaloneTools) { + const commandContent = taskToolGen.generateCommandContent(tool, 'tool'); + const targetPath = path.join(commandsBaseDir, `bmad-tool-${tool.module}-${tool.name}.md`); + await this.writeFile(targetPath, commandContent); + } + + return { + tasks: standaloneTasks.length, + tools: standaloneTools.length, + }; + } + + async readAndProcess(filePath, metadata) { + const content = await fs.readFile(filePath, 'utf8'); + return this.processContent(content, metadata); + } + + async createAgentContent(content, metadata) { + const { frontmatter = {}, body } = this.parseFrontmatter(content); + + frontmatter.description = + frontmatter.description && String(frontmatter.description).trim().length > 0 + ? frontmatter.description + : `BMAD ${metadata.module} agent: ${metadata.name}`; + + // OpenCode agents use: 'primary' mode for main agents + frontmatter.mode = 'primary'; + + const frontmatterString = this.stringifyFrontmatter(frontmatter); + + // Get the activation header from central template + const activationHeader = await this.getAgentCommandHeader(); + + return `${frontmatterString}\n\n${activationHeader}\n\n${body}`; + } + + parseFrontmatter(content) { + const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/); + if (!match) { + return { data: {}, body: content }; + } + + const body = content.slice(match[0].length); + + let frontmatter = {}; + try { + frontmatter = yaml.parse(match[1]) || {}; + } catch { + frontmatter = {}; + } + + return { frontmatter, body }; + } + + stringifyFrontmatter(frontmatter) { + const yamlText = yaml + .dump(frontmatter, { + indent: 2, + lineWidth: -1, + noRefs: true, + sortKeys: false, + }) + .trimEnd(); + + return `---\n${yamlText}\n---`; + } + + /** + * Cleanup OpenCode configuration - surgically remove only BMAD files + */ + async cleanup(projectDir) { + const agentsDir = path.join(projectDir, this.configDir, this.agentsDir); + const commandsDir = path.join(projectDir, this.configDir, this.commandsDir); + let removed = 0; + + // Clean up agent folder + if (await fs.pathExists(agentsDir)) { + const files = await fs.readdir(agentsDir); + for (const file of files) { + if (file.startsWith('bmad') && file.endsWith('.md')) { + await fs.remove(path.join(agentsDir, file)); + removed++; + } + } + } + + // Clean up command folder + if (await fs.pathExists(commandsDir)) { + const files = await fs.readdir(commandsDir); + for (const file of files) { + if (file.startsWith('bmad') && file.endsWith('.md')) { + await fs.remove(path.join(commandsDir, file)); + removed++; + } + } + } + + if (removed > 0) { + console.log(chalk.dim(` Cleaned up ${removed} existing BMAD files`)); + } + } + + /** + * Install a custom agent launcher for OpenCode + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created command + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + 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(agentsDir); + + const launcherContent = `--- +name: '${agentName}' +description: '${metadata.title || agentName} agent' +mode: 'primary' +--- + +You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. + + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + +`; + + // OpenCode uses flat naming: bmad-agent-custom-{name}.md + const launcherPath = path.join(agentsDir, `bmad-agent-custom-${agentName}.md`); + await this.writeFile(launcherPath, launcherContent); + + return { + path: launcherPath, + command: `bmad-agent-custom-${agentName}`, + }; + } +} + +module.exports = { OpenCodeSetup }; diff --git a/tools/cli/installers/lib/ide/qwen.js b/tools/cli/installers/lib/ide/qwen.js new file mode 100644 index 00000000..7ac72f09 --- /dev/null +++ b/tools/cli/installers/lib/ide/qwen.js @@ -0,0 +1,372 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { getAgentsFromBmad, getTasksFromBmad } = require('./shared/bmad-artifacts'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); + +/** + * Qwen Code setup handler + * Creates TOML command files in .qwen/commands/BMad/ + */ +class QwenSetup extends BaseIdeSetup { + constructor() { + super('qwen', 'Qwen Code'); + this.configDir = '.qwen'; + this.commandsDir = 'commands'; + this.bmadDir = 'bmad'; + } + + /** + * Setup Qwen Code 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}...`)); + + // Create .qwen/commands/BMad directory structure + const qwenDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(qwenDir, this.commandsDir); + const bmadCommandsDir = path.join(commandsDir, this.bmadDir); + + await this.ensureDir(bmadCommandsDir); + + // Update existing settings.json if present + await this.updateSettings(qwenDir); + + // Clean up old configuration if exists + await this.cleanupOldConfig(qwenDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Get tasks, tools, and workflows (standalone only for tools/workflows) + const tasks = await getTasksFromBmad(bmadDir, options.selectedModules || []); + const tools = await this.getTools(bmadDir, true); + const workflows = await this.getWorkflows(bmadDir, true); + + // Create directories for each module (including standalone) + const modules = new Set(); + for (const item of [...agentArtifacts, ...tasks, ...tools, ...workflows]) modules.add(item.module); + + for (const module of modules) { + await this.ensureDir(path.join(bmadCommandsDir, module)); + await this.ensureDir(path.join(bmadCommandsDir, module, 'agents')); + await this.ensureDir(path.join(bmadCommandsDir, module, 'tasks')); + await this.ensureDir(path.join(bmadCommandsDir, module, 'tools')); + await this.ensureDir(path.join(bmadCommandsDir, module, 'workflows')); + } + + // Create TOML files for each agent launcher + let agentCount = 0; + for (const artifact of agentArtifacts) { + // Convert markdown launcher content to TOML format + const tomlContent = this.processAgentLauncherContent(artifact.content, { + module: artifact.module, + name: artifact.name, + }); + + const targetPath = path.join(bmadCommandsDir, artifact.module, 'agents', `${artifact.name}.toml`); + + await this.writeFile(targetPath, tomlContent); + + agentCount++; + console.log(chalk.green(` ✓ Added agent: /bmad_${artifact.module}_agents_${artifact.name}`)); + } + + // Create TOML files for each task + let taskCount = 0; + for (const task of tasks) { + const content = await this.readAndProcess(task.path, { + module: task.module, + name: task.name, + }); + + const targetPath = path.join(bmadCommandsDir, task.module, 'tasks', `${task.name}.toml`); + + await this.writeFile(targetPath, content); + + taskCount++; + console.log(chalk.green(` ✓ Added task: /bmad_${task.module}_tasks_${task.name}`)); + } + + // Create TOML files for each tool + let toolCount = 0; + for (const tool of tools) { + const content = await this.readAndProcess(tool.path, { + module: tool.module, + name: tool.name, + }); + + const targetPath = path.join(bmadCommandsDir, tool.module, 'tools', `${tool.name}.toml`); + + await this.writeFile(targetPath, content); + + toolCount++; + console.log(chalk.green(` ✓ Added tool: /bmad_${tool.module}_tools_${tool.name}`)); + } + + // Create TOML files for each workflow + let workflowCount = 0; + for (const workflow of workflows) { + const content = await this.readAndProcess(workflow.path, { + module: workflow.module, + name: workflow.name, + }); + + const targetPath = path.join(bmadCommandsDir, workflow.module, 'workflows', `${workflow.name}.toml`); + + await this.writeFile(targetPath, content); + + workflowCount++; + console.log(chalk.green(` ✓ Added workflow: /bmad_${workflow.module}_workflows_${workflow.name}`)); + } + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents configured`)); + console.log(chalk.dim(` - ${taskCount} tasks configured`)); + console.log(chalk.dim(` - ${toolCount} tools configured`)); + console.log(chalk.dim(` - ${workflowCount} workflows configured`)); + console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, bmadCommandsDir)}`)); + + return { + success: true, + agents: agentCount, + tasks: taskCount, + tools: toolCount, + workflows: workflowCount, + }; + } + + /** + * Update settings.json to remove old agent references + */ + async updateSettings(qwenDir) { + const fs = require('fs-extra'); + const settingsPath = path.join(qwenDir, 'settings.json'); + + if (await fs.pathExists(settingsPath)) { + try { + const settingsContent = await fs.readFile(settingsPath, 'utf8'); + const settings = JSON.parse(settingsContent); + let updated = false; + + // Remove agent file references from contextFileName + if (settings.contextFileName && Array.isArray(settings.contextFileName)) { + const originalLength = settings.contextFileName.length; + settings.contextFileName = settings.contextFileName.filter( + (fileName) => !fileName.startsWith('agents/') && !fileName.startsWith('bmad-method/'), + ); + + if (settings.contextFileName.length !== originalLength) { + updated = true; + } + } + + if (updated) { + await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2)); + console.log(chalk.green(' ✓ Updated .qwen/settings.json')); + } + } catch (error) { + console.warn(chalk.yellow(' ⚠ Could not update settings.json:'), error.message); + } + } + } + + /** + * Clean up old configuration directories + */ + async cleanupOldConfig(qwenDir) { + const fs = require('fs-extra'); + const agentsDir = path.join(qwenDir, 'agents'); + const bmadMethodDir = path.join(qwenDir, 'bmad-method'); + const bmadDir = path.join(qwenDir, 'bmadDir'); + + if (await fs.pathExists(agentsDir)) { + await fs.remove(agentsDir); + console.log(chalk.green(' ✓ Removed old agents directory')); + } + + if (await fs.pathExists(bmadMethodDir)) { + await fs.remove(bmadMethodDir); + console.log(chalk.green(' ✓ Removed old bmad-method directory')); + } + + if (await fs.pathExists(bmadDir)) { + await fs.remove(bmadDir); + console.log(chalk.green(' ✓ Removed old BMad directory')); + } + } + + /** + * Read and process file content + */ + async readAndProcess(filePath, metadata) { + const fs = require('fs-extra'); + const content = await fs.readFile(filePath, 'utf8'); + return this.processContent(content, metadata); + } + + /** + * Process agent launcher content and convert to TOML format + * @param {string} launcherContent - Launcher markdown content + * @param {Object} metadata - File metadata + * @returns {string} TOML formatted content + */ + processAgentLauncherContent(launcherContent, metadata = {}) { + // Strip frontmatter from launcher content + const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; + const contentWithoutFrontmatter = launcherContent.replace(frontmatterRegex, ''); + + // Extract title for TOML description + const titleMatch = launcherContent.match(/description:\s*"([^"]+)"/); + const title = titleMatch ? titleMatch[1] : metadata.name; + + // Create TOML with launcher content (without frontmatter) + return `description = "BMAD ${metadata.module.toUpperCase()} Agent: ${title}" +prompt = """ +${contentWithoutFrontmatter.trim()} +""" +`; + } + + /** + * Override processContent to add TOML metadata header for Qwen + * @param {string} content - File content + * @param {Object} metadata - File metadata + * @returns {string} Processed content with Qwen template + */ + processContent(content, metadata = {}) { + // First apply base processing (includes activation injection for agents) + let prompt = super.processContent(content, metadata); + + // Determine the type and description based on content + const isAgent = content.includes(' word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } + + /** + * Cleanup Qwen configuration + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const bmadCommandsDir = path.join(projectDir, this.configDir, this.commandsDir, this.bmadDir); + const oldBmadMethodDir = path.join(projectDir, this.configDir, 'bmad-method'); + const oldBMadDir = path.join(projectDir, this.configDir, 'BMad'); + + if (await fs.pathExists(bmadCommandsDir)) { + await fs.remove(bmadCommandsDir); + console.log(chalk.dim(`Removed BMAD configuration from Qwen Code`)); + } + + if (await fs.pathExists(oldBmadMethodDir)) { + await fs.remove(oldBmadMethodDir); + console.log(chalk.dim(`Removed old BMAD configuration from Qwen Code`)); + } + + if (await fs.pathExists(oldBMadDir)) { + await fs.remove(oldBMadDir); + console.log(chalk.dim(`Removed old BMAD configuration from Qwen Code`)); + } + } + + /** + * Install a custom agent launcher for Qwen + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const qwenDir = path.join(projectDir, this.configDir); + const commandsDir = path.join(qwenDir, this.commandsDir); + const bmadCommandsDir = path.join(commandsDir, this.bmadDir); + + // Create .qwen/commands/BMad directory if it doesn't exist + await fs.ensureDir(bmadCommandsDir); + + // Create custom agent launcher in TOML format (same pattern as regular agents) + const launcherContent = `# ${agentName} Custom Agent + +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this command to activate ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +--- + +*Generated by BMAD Method*`; + + // Use Qwen's TOML conversion method + const tomlContent = this.processAgentLauncherContent(launcherContent, { + name: agentName, + module: 'custom', + }); + + const fileName = `custom-${agentName.toLowerCase()}.toml`; + const launcherPath = path.join(bmadCommandsDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, tomlContent, 'utf8'); + + return { + ide: 'qwen', + path: path.relative(projectDir, launcherPath), + command: agentName, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { QwenSetup }; diff --git a/tools/cli/installers/lib/ide/roo.js b/tools/cli/installers/lib/ide/roo.js new file mode 100644 index 00000000..66380464 --- /dev/null +++ b/tools/cli/installers/lib/ide/roo.js @@ -0,0 +1,273 @@ +const path = require('node:path'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { toDashPath, customAgentDashName } = require('./shared/path-utils'); + +/** + * Roo IDE setup handler + * Creates custom commands in .roo/commands directory + */ +class RooSetup extends BaseIdeSetup { + constructor() { + super('roo', 'Roo Code'); + this.configDir = '.roo'; + this.commandsDir = 'commands'; + } + + /** + * Setup Roo IDE 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}...`)); + + // Create .roo/commands directory + const rooCommandsDir = path.join(projectDir, this.configDir, this.commandsDir); + await this.ensureDir(rooCommandsDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + let addedCount = 0; + let skippedCount = 0; + + for (const artifact of agentArtifacts) { + // Use shared toDashPath to get consistent naming: bmad_bmm_name.md + const commandName = toDashPath(artifact.relativePath).replace('.md', ''); + const commandPath = path.join(rooCommandsDir, `${commandName}.md`); + + // Skip if already exists + if (await this.pathExists(commandPath)) { + console.log(chalk.dim(` Skipping ${commandName} - already exists`)); + skippedCount++; + continue; + } + + // artifact.sourcePath contains the full path to the agent file + if (!artifact.sourcePath) { + console.error(`Error: Missing sourcePath for artifact ${artifact.name} from module ${artifact.module}`); + console.error(`Artifact object:`, artifact); + throw new Error(`Missing sourcePath for agent: ${artifact.name}`); + } + + const content = await this.readFile(artifact.sourcePath); + + // Create command file that references the actual _bmad agent + await this.createCommandFile( + { module: artifact.module, name: artifact.name, path: artifact.sourcePath }, + content, + commandPath, + projectDir, + ); + + addedCount++; + console.log(chalk.green(` ✓ Added command: ${commandName}`)); + } + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${addedCount} commands added`)); + if (skippedCount > 0) { + console.log(chalk.dim(` - ${skippedCount} commands skipped (already exist)`)); + } + console.log(chalk.dim(` - Commands directory: ${this.configDir}/${this.commandsDir}/`)); + console.log(chalk.dim(` Commands will be available when you open this project in Roo Code`)); + + return { + success: true, + commands: addedCount, + skipped: skippedCount, + }; + } + + /** + * Create a unified command file for agents + * @param {string} commandPath - Path where to write the command file + * @param {Object} options - Command options + * @param {string} options.name - Display name for the command + * @param {string} options.description - Description for the command + * @param {string} options.agentPath - Path to the agent file (relative to project root) + * @param {string} [options.icon] - Icon emoji (defaults to 🤖) + * @param {string} [options.extraContent] - Additional content to include before activation + */ + async createAgentCommandFile(commandPath, options) { + const { name, description, agentPath, icon = '🤖', extraContent = '' } = options; + + // Build command content with YAML frontmatter + let commandContent = `---\n`; + commandContent += `name: '${icon} ${name}'\n`; + commandContent += `description: '${description}'\n`; + commandContent += `---\n\n`; + + commandContent += `You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.\n\n`; + + // Add any extra content (e.g., warnings for custom agents) + if (extraContent) { + commandContent += `${extraContent}\n\n`; + } + + commandContent += `\n`; + commandContent += `1. LOAD the FULL agent file from @${agentPath}\n`; + commandContent += `2. READ its entire contents - this contains the complete agent persona, menu, and instructions\n`; + commandContent += `3. Execute ALL activation steps exactly as written in the agent file\n`; + commandContent += `4. Follow the agent's persona and menu system precisely\n`; + commandContent += `5. Stay in character throughout the session\n`; + commandContent += `\n`; + + // Write command file + await this.writeFile(commandPath, commandContent); + } + + /** + * Create a command file for an agent + */ + async createCommandFile(agent, content, commandPath, projectDir) { + // Extract metadata from agent content + const titleMatch = content.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name); + + const iconMatch = content.match(/icon="([^"]+)"/); + const icon = iconMatch ? iconMatch[1] : '🤖'; + + const whenToUseMatch = content.match(/whenToUse="([^"]+)"/); + const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`; + + // Get relative path + const relativePath = path.relative(projectDir, agent.path).replaceAll('\\', '/'); + + // Use unified method + await this.createAgentCommandFile(commandPath, { + name: title, + description: whenToUse, + agentPath: relativePath, + icon: icon, + }); + } + + /** + * Format name as title + */ + formatTitle(name) { + return name + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } + + /** + * Cleanup Roo configuration + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const rooCommandsDir = path.join(projectDir, this.configDir, this.commandsDir); + + if (await fs.pathExists(rooCommandsDir)) { + const files = await fs.readdir(rooCommandsDir); + let removedCount = 0; + + for (const file of files) { + if (file.startsWith('bmad') && file.endsWith('.md')) { + await fs.remove(path.join(rooCommandsDir, file)); + removedCount++; + } + } + + if (removedCount > 0) { + console.log(chalk.dim(`Removed ${removedCount} BMAD commands from .roo/commands/`)); + } + } + + // Also clean up old .roomodes file if it exists + const roomodesPath = path.join(projectDir, '.roomodes'); + if (await fs.pathExists(roomodesPath)) { + const content = await fs.readFile(roomodesPath, 'utf8'); + + // Remove BMAD modes only + const lines = content.split('\n'); + const filteredLines = []; + let skipMode = false; + let removedCount = 0; + + for (const line of lines) { + if (/^\s*- slug: bmad/.test(line)) { + skipMode = true; + removedCount++; + } else if (skipMode && /^\s*- slug: /.test(line)) { + skipMode = false; + } + + if (!skipMode) { + filteredLines.push(line); + } + } + + // Write back filtered content + await fs.writeFile(roomodesPath, filteredLines.join('\n')); + if (removedCount > 0) { + console.log(chalk.dim(`Removed ${removedCount} BMAD modes from legacy .roomodes file`)); + } + } + } + + /** + * Install a custom agent launcher for Roo + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata (unused, kept for compatibility) + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const rooCommandsDir = path.join(projectDir, this.configDir, this.commandsDir); + await this.ensureDir(rooCommandsDir); + + // Use underscore format: bmad_custom_fred-commit-poet.md + const commandName = customAgentDashName(agentName).replace('.md', ''); + const commandPath = path.join(rooCommandsDir, `${commandName}.md`); + + // Check if command already exists + if (await this.pathExists(commandPath)) { + return { + ide: 'roo', + path: path.join(this.configDir, this.commandsDir, `${commandName}.md`), + command: commandName, + type: 'custom-agent-launcher', + alreadyExists: true, + }; + } + + // Read the custom agent file to extract metadata (same as regular agents) + const fullAgentPath = path.join(projectDir, agentPath); + const content = await this.readFile(fullAgentPath); + + // Extract metadata from agent content + const titleMatch = content.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(agentName); + + const iconMatch = content.match(/icon="([^"]+)"/); + const icon = iconMatch ? iconMatch[1] : '🤖'; + + const whenToUseMatch = content.match(/whenToUse="([^"]+)"/); + const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`; + + // Use unified method without extra content (clean) + await this.createAgentCommandFile(commandPath, { + name: title, + description: whenToUse, + agentPath: agentPath, + icon: icon, + }); + + return { + ide: 'roo', + path: path.join(this.configDir, this.commandsDir, `${commandName}.md`), + command: commandName, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { RooSetup }; 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..d329e1ad --- /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', false); + 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/cli/installers/lib/ide/shared/agent-command-generator.js b/tools/cli/installers/lib/ide/shared/agent-command-generator.js index 6e2ca31b..29319af8 100644 --- a/tools/cli/installers/lib/ide/shared/agent-command-generator.js +++ b/tools/cli/installers/lib/ide/shared/agent-command-generator.js @@ -1,5 +1,6 @@ const path = require('node:path'); const fs = require('fs-extra'); +const chalk = require('chalk'); const { toColonPath, toDashPath, customAgentColonName, customAgentDashName } = require('./path-utils'); /** @@ -32,10 +33,8 @@ class AgentCommandGenerator { const agentPathInModule = agent.relativePath || `${agent.name}.md`; artifacts.push({ type: 'agent-launcher', - name: agent.name, - displayName: agent.displayName || agent.name, - description: agent.description, module: agent.module, + name: agent.name, relativePath: path.join(agent.module, 'agents', agentPathInModule), content: launcherContent, sourcePath: agent.path, @@ -66,8 +65,9 @@ class AgentCommandGenerator { .replaceAll('{{name}}', agent.name) .replaceAll('{{module}}', agent.module) .replaceAll('{{path}}', agentPathInModule) - .replaceAll('{{relativePath}}', path.join(agent.module, 'agents', agentPathInModule)) - .replaceAll('{{description}}', agent.description || `${agent.name} agent`); + .replaceAll('{{description}}', agent.description || `${agent.name} agent`) + .replaceAll('_bmad', this.bmadFolderName) + .replaceAll('_bmad', '_bmad'); } /** @@ -109,7 +109,7 @@ class AgentCommandGenerator { // Convert relativePath to underscore format: bmm/agents/pm.md → bmad_bmm_pm.md const flatName = toColonPath(artifact.relativePath); const launcherPath = path.join(baseCommandsDir, flatName); - await fs.ensureDir(baseCommandsDir); + await fs.ensureDir(path.dirname(launcherPath)); await fs.writeFile(launcherPath, artifact.content); writtenCount++; } @@ -119,8 +119,8 @@ class AgentCommandGenerator { } /** - * Write agent launcher artifacts using dash format - * Creates flat files like: bmad-bmm-agent-pm.md + * Write agent launcher artifacts using underscore format (Windows-compatible) + * Creates flat files like: bmad_bmm_pm.md * * @param {string} baseCommandsDir - Base commands directory for the IDE * @param {Array} artifacts - Agent launcher artifacts @@ -131,10 +131,10 @@ class AgentCommandGenerator { for (const artifact of artifacts) { if (artifact.type === 'agent-launcher') { - // Convert relativePath to dash format: bmm/agents/pm.md → bmad-bmm-agent-pm.md + // Convert relativePath to underscore format: bmm/agents/pm.md → bmad_bmm_pm.md const flatName = toDashPath(artifact.relativePath); const launcherPath = path.join(baseCommandsDir, flatName); - await fs.ensureDir(baseCommandsDir); + await fs.ensureDir(path.dirname(launcherPath)); await fs.writeFile(launcherPath, artifact.content); writtenCount++; } diff --git a/tools/cli/installers/lib/ide/shared/bmad-artifacts.js b/tools/cli/installers/lib/ide/shared/bmad-artifacts.js index fbe76099..eb190589 100644 --- a/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +++ b/tools/cli/installers/lib/ide/shared/bmad-artifacts.js @@ -44,26 +44,9 @@ async function getAgentsFromBmad(bmadDir, selectedModules = []) { if (content.includes('localskip="true"')) continue; - // Extract description from YAML frontmatter if present - let description = null; - let agentName = file.replace('.md', ''); - const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (frontmatterMatch) { - const descMatch = frontmatterMatch[1].match(/description:\s*"([^"]+)"/); - if (descMatch) { - description = descMatch[1]; - } - const nameMatch = frontmatterMatch[1].match(/name:\s*"([^"]+)"/); - if (nameMatch) { - agentName = nameMatch[1]; - } - } - agents.push({ path: filePath, - name: agentName, - displayName: agentName, - description: description, + name: file.replace('.md', ''), module: 'standalone', // Mark as standalone agent }); } @@ -131,26 +114,9 @@ async function getAgentsFromDir(dirPath, moduleName, relativePath = '') { continue; } - // Extract description from YAML frontmatter if present - let description = null; - const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (frontmatterMatch) { - const descMatch = frontmatterMatch[1].match(/description:\s*"([^"]+)"/); - if (descMatch) { - description = descMatch[1]; - } - // Also extract name from frontmatter if available - const nameMatch = frontmatterMatch[1].match(/name:\s*"([^"]+)"/); - if (nameMatch) { - entry.name = `${nameMatch[1]}.md`; - } - } - agents.push({ path: fullPath, name: entry.name.replace('.md', ''), - displayName: entry.name.replace('.md', ''), - description: description, module: moduleName, relativePath: newRelativePath, // Keep the .md extension for the full path }); diff --git a/tools/cli/installers/lib/ide/shared/path-utils.js b/tools/cli/installers/lib/ide/shared/path-utils.js index 80b632ca..dc564774 100644 --- a/tools/cli/installers/lib/ide/shared/path-utils.js +++ b/tools/cli/installers/lib/ide/shared/path-utils.js @@ -3,24 +3,12 @@ * * Provides utilities to convert hierarchical paths to flat naming conventions. * - Underscore format (bmad_module_name.md) - Windows-compatible universal format - * - Suffix-based format (bmad-module-name.agent.md) - New universal standard */ -// Default file extension for backward compatibility -const DEFAULT_FILE_EXTENSION = '.md'; - // Type segments - agents are included in naming, others are filtered out const TYPE_SEGMENTS = ['workflows', 'tasks', 'tools']; const AGENT_SEGMENT = 'agents'; -/** - * Artifact type to suffix mapping - * Only agents get the .agent suffix; workflows/tasks/tools use standard .md extension - */ -const ARTIFACT_SUFFIXES = { - agent: '.agent', -}; - /** * Convert hierarchical path to flat underscore-separated name * Converts: 'bmm', 'agents', 'pm' → 'bmad_bmm_agent_pm.md' @@ -30,48 +18,36 @@ const ARTIFACT_SUFFIXES = { * @param {string} module - Module name (e.g., 'bmm', 'core') * @param {string} type - Artifact type ('agents', 'workflows', 'tasks', 'tools') * @param {string} name - Artifact name (e.g., 'pm', 'brainstorming') - * @param {string} [fileExtension=DEFAULT_FILE_EXTENSION] - File extension including dot (e.g., '.md', '.toml') * @returns {string} Flat filename like 'bmad_bmm_agent_pm.md' or 'bmad_bmm_correct-course.md' */ -function toUnderscoreName(module, type, name, fileExtension = DEFAULT_FILE_EXTENSION) { +function toUnderscoreName(module, type, name) { const isAgent = type === AGENT_SEGMENT; // For core module, skip the module prefix: use 'bmad_name.md' instead of 'bmad_core_name.md' if (module === 'core') { - return isAgent ? `bmad_agent_${name}${fileExtension}` : `bmad_${name}${fileExtension}`; + return isAgent ? `bmad_agent_${name}.md` : `bmad_${name}.md`; } - return isAgent ? `bmad_${module}_agent_${name}${fileExtension}` : `bmad_${module}_${name}${fileExtension}`; + return isAgent ? `bmad_${module}_agent_${name}.md` : `bmad_${module}_${name}.md`; } /** * Convert relative path to flat underscore-separated name * Converts: 'bmm/agents/pm.md' → 'bmad_bmm_agent_pm.md' * Converts: 'bmm/workflows/correct-course.md' → 'bmad_bmm_correct-course.md' - * Converts: 'bmad_bmb/agents/agent-builder.md' → 'bmad_bmb_agent_agent-builder.md' (bmad prefix already in module) * Converts: 'core/agents/brainstorming.md' → 'bmad_agent_brainstorming.md' (core items skip module prefix) * * @param {string} relativePath - Path like 'bmm/agents/pm.md' - * @param {string} [fileExtension=DEFAULT_FILE_EXTENSION] - File extension including dot (e.g., '.md', '.toml') * @returns {string} Flat filename like 'bmad_bmm_agent_pm.md' or 'bmad_brainstorming.md' */ -function toUnderscorePath(relativePath, fileExtension = DEFAULT_FILE_EXTENSION) { - // Extract extension from relativePath to properly remove it - const extMatch = relativePath.match(/\.[^.]+$/); - const originalExt = extMatch ? extMatch[0] : ''; - const withoutExt = relativePath.replace(originalExt, ''); +function toUnderscorePath(relativePath) { + const withoutExt = relativePath.replace('.md', ''); const parts = withoutExt.split(/[/\\]/); const module = parts[0]; const type = parts[1]; const name = parts.slice(2).join('_'); - const isAgent = type === AGENT_SEGMENT; - // For core module, skip the module prefix: use 'bmad_name.md' instead of 'bmad_core_name.md' - if (module === 'core') { - return isAgent ? `bmad_agent_${name}${fileExtension}` : `bmad_${name}${fileExtension}`; - } - // If module already starts with 'bmad_', don't add another prefix - const prefix = module.startsWith('bmad_') ? '' : 'bmad_'; - return isAgent ? `${prefix}${module}_agent_${name}${fileExtension}` : `${prefix}${module}_${name}${fileExtension}`; + // Use toUnderscoreName for consistency + return toUnderscoreName(module, type, name); } /** @@ -79,11 +55,10 @@ function toUnderscorePath(relativePath, fileExtension = DEFAULT_FILE_EXTENSION) * Creates: 'bmad_custom_fred-commit-poet.md' * * @param {string} agentName - Custom agent name - * @param {string} [fileExtension=DEFAULT_FILE_EXTENSION] - File extension including dot (e.g., '.md', '.toml') * @returns {string} Flat filename like 'bmad_custom_fred-commit-poet.md' */ -function customAgentUnderscoreName(agentName, fileExtension = DEFAULT_FILE_EXTENSION) { - return `bmad_custom_${agentName}${fileExtension}`; +function customAgentUnderscoreName(agentName) { + return `bmad_custom_${agentName}.md`; } /** @@ -159,9 +134,9 @@ function parseUnderscoreName(filename) { } // Backward compatibility aliases (deprecated) -// Note: These now use toDashPath and customAgentDashName which convert underscores to dashes const toColonName = toUnderscoreName; -const toDashName = toUnderscoreName; +const toColonPath = toUnderscorePath; +const toDashPath = toUnderscorePath; const customAgentColonName = customAgentUnderscoreName; const customAgentDashName = customAgentUnderscoreName; const isColonFormat = isUnderscoreFormat; @@ -169,125 +144,7 @@ const isDashFormat = isUnderscoreFormat; const parseColonName = parseUnderscoreName; const parseDashName = parseUnderscoreName; -/** - * Convert relative path to flat colon-separated name (for backward compatibility) - * This is actually the same as underscore format now (underscores in filenames) - * @param {string} relativePath - Path like 'bmm/agents/pm.md' - * @param {string} [fileExtension=DEFAULT_FILE_EXTENSION] - File extension including dot - * @returns {string} Flat filename like 'bmad_bmm_agent_pm.md' - */ -function toColonPath(relativePath, fileExtension = DEFAULT_FILE_EXTENSION) { - return toUnderscorePath(relativePath, fileExtension); -} - -/** - * Convert relative path to flat dash-separated name - * Converts: 'bmm/agents/pm.md' → 'bmad-bmm-agent-pm.md' - * Converts: 'bmm/workflows/correct-course' → 'bmad-bmm-correct-course.md' - * Converts: 'bmad-bmb/agents/agent-builder.md' → 'bmad-bmb-agent-agent-builder.md' (bmad prefix already in module) - * @param {string} relativePath - Path like 'bmm/agents/pm.md' - * @param {string} [fileExtension=DEFAULT_FILE_EXTENSION] - File extension including dot - * @returns {string} Flat filename like 'bmad-bmm-agent-pm.md' - */ -function toDashPath(relativePath, fileExtension = DEFAULT_FILE_EXTENSION) { - // Extract extension from relativePath to properly remove it - const extMatch = relativePath.match(/\.[^.]+$/); - const originalExt = extMatch ? extMatch[0] : ''; - const withoutExt = relativePath.replace(originalExt, ''); - const parts = withoutExt.split(/[/\\]/); - - const module = parts[0]; - const type = parts[1]; - const name = parts.slice(2).join('-'); - - // Use dash naming style - const isAgent = type === AGENT_SEGMENT; - // For core module, skip the module prefix - if (module === 'core') { - return isAgent ? `bmad-agent-${name}${fileExtension}` : `bmad-${name}${fileExtension}`; - } - // If module already starts with 'bmad-', don't add another prefix - const prefix = module.startsWith('bmad-') ? '' : 'bmad-'; - return isAgent ? `${prefix}${module}-agent-${name}${fileExtension}` : `${prefix}${module}-${name}${fileExtension}`; -} - -/** - * Convert relative path to suffix-based name (NEW UNIVERSAL STANDARD) - * Only applies .agent suffix to agents; workflows/tasks/tools get standard .md extension. - * Converts: 'cis/agents/storymaster.md' → 'bmad-cis-storymaster.agent.md' - * Converts: 'bmm/workflows/plan-project.md' → 'bmad-bmm-plan-project.md' - * Converts: 'bmm/tasks/create-story.md' → 'bmad-bmm-create-story.md' - * Converts: 'bmm/tools/file-ops.md' → 'bmad-bmm-file-ops.md' - * Converts: 'core/agents/brainstorming.md' → 'bmad-brainstorming.agent.md' (core items skip module prefix) - * - * @param {string} relativePath - Path like 'cis/agents/storymaster.md' - * @param {string} artifactType - Type of artifact: 'agent', 'workflow', 'task', 'tool' - * @param {string} [fileExtension='.md'] - File extension including dot (e.g., '.md', '.toml') - * @returns {string} Suffix-based filename like 'bmad-cis-storymaster.agent.md' - */ -function toSuffixBasedName(relativePath, artifactType, fileExtension = DEFAULT_FILE_EXTENSION) { - const extMatch = relativePath.match(/\.[^.]+$/); - const originalExt = extMatch ? extMatch[0] : ''; - const withoutExt = relativePath.replace(originalExt, ''); - const parts = withoutExt.split(/[/\\]/); - - const module = parts[0]; - const type = parts[1]; // agents, workflows, tasks, tools - const name = parts.slice(2).join('-'); - - // Only add .agent suffix for agents; workflows/tasks/tools use standard extension - const suffix = artifactType === 'agent' ? ARTIFACT_SUFFIXES.agent : ''; - - // For core module, skip the module prefix (use 'bmad-name.suffix.md') - if (module === 'core') { - return `bmad-${name}${suffix}.${fileExtension.replace('.', '')}`; - } - - // If module already starts with 'bmad-', don't add another prefix - const prefix = module.startsWith('bmad-') ? '' : 'bmad-'; - return `${prefix}${module}-${name}${suffix}.${fileExtension.replace('.', '')}`; -} - -/** - * Get suffix for artifact type - * @param {string} artifactType - Type of artifact: 'agent', 'workflow', 'task', 'tool' - * @returns {string} Suffix like '.agent', '.workflow', etc. - */ -function getArtifactSuffix(artifactType) { - return ARTIFACT_SUFFIXES[artifactType] || ''; -} - -/** - * Parse artifact type from suffix-based filename - * Parses: 'bmad-cis-storymaster.agent.md' → 'agent' - * Returns null for workflows/tasks/tools (no suffix) - * - * @param {string} filename - Suffix-based filename - * @returns {string|null} Artifact type or null if not found - */ -function parseArtifactTypeFromFilename(filename) { - for (const [type, suffix] of Object.entries(ARTIFACT_SUFFIXES)) { - if (filename.includes(`${suffix}.`)) { - return type; - } - } - return null; -} - -/** - * Create custom agent suffix-based name - * Creates: 'bmad-custom-fred-commit-poet.agent.md' - * - * @param {string} agentName - Custom agent name - * @param {string} [fileExtension='.md'] - File extension including dot - * @returns {string} Suffix-based filename like 'bmad-custom-fred-commit-poet.agent.md' - */ -function customAgentSuffixName(agentName, fileExtension = DEFAULT_FILE_EXTENSION) { - return `bmad-custom-${agentName}.agent.${fileExtension.replace('.', '')}`; -} - module.exports = { - DEFAULT_FILE_EXTENSION, toUnderscoreName, toUnderscorePath, customAgentUnderscoreName, @@ -296,7 +153,6 @@ module.exports = { // Backward compatibility aliases toColonName, toColonPath, - toDashName, toDashPath, customAgentColonName, customAgentDashName, @@ -306,10 +162,4 @@ module.exports = { parseDashName, TYPE_SEGMENTS, AGENT_SEGMENT, - // New suffix-based naming functions (UNIVERSAL STANDARD) - ARTIFACT_SUFFIXES, - toSuffixBasedName, - getArtifactSuffix, - parseArtifactTypeFromFilename, - customAgentSuffixName, }; diff --git a/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js b/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js index 45a30206..fd5f45d5 100644 --- a/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +++ b/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js @@ -2,17 +2,85 @@ const path = require('node:path'); const fs = require('fs-extra'); const csv = require('csv-parse/sync'); const chalk = require('chalk'); -const { toColonName, toColonPath, toDashPath, toSuffixBasedName } = require('./path-utils'); +const { toColonName, toColonPath, toDashPath } = require('./path-utils'); /** * Generates command files for standalone tasks and tools */ class TaskToolCommandGenerator { /** - * REMOVED: Old generateTaskToolCommands method that created nested structure. - * This was causing bugs where files were written to wrong directories. - * Use generateColonTaskToolCommands() or generateDashTaskToolCommands() instead. + * Generate task and tool commands from manifest CSVs + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {string} baseCommandsDir - Optional base commands directory (defaults to .claude/commands/bmad) */ + async generateTaskToolCommands(projectDir, bmadDir, baseCommandsDir = null) { + const tasks = await this.loadTaskManifest(bmadDir); + const tools = await this.loadToolManifest(bmadDir); + + // Filter to only standalone items + const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + + // Base commands directory - use provided or default to Claude Code structure + const commandsDir = baseCommandsDir || path.join(projectDir, '.claude', 'commands', 'bmad'); + + let generatedCount = 0; + + // Generate command files for tasks + for (const task of standaloneTasks) { + const moduleTasksDir = path.join(commandsDir, task.module, 'tasks'); + await fs.ensureDir(moduleTasksDir); + + const commandContent = this.generateCommandContent(task, 'task'); + const commandPath = path.join(moduleTasksDir, `${task.name}.md`); + + await fs.writeFile(commandPath, commandContent); + generatedCount++; + } + + // Generate command files for tools + for (const tool of standaloneTools) { + const moduleToolsDir = path.join(commandsDir, tool.module, 'tools'); + await fs.ensureDir(moduleToolsDir); + + const commandContent = this.generateCommandContent(tool, 'tool'); + const commandPath = path.join(moduleToolsDir, `${tool.name}.md`); + + await fs.writeFile(commandPath, commandContent); + generatedCount++; + } + + return { + generated: generatedCount, + tasks: standaloneTasks.length, + tools: standaloneTools.length, + }; + } + + /** + * Generate command content for a task or tool + */ + generateCommandContent(item, type) { + const description = item.description || `Execute ${item.displayName || item.name}`; + + // Convert path to use {project-root} placeholder + let itemPath = item.path; + if (itemPath.startsWith('bmad/')) { + itemPath = `{project-root}/${itemPath}`; + } + + return `--- +description: '${description.replaceAll("'", "''")}' +--- + +# ${item.displayName || item.name} + +LOAD and execute the ${type} at: ${itemPath} + +Follow all instructions in the ${type} file exactly as written. +`; + } /** * Load task manifest CSV @@ -25,16 +93,10 @@ class TaskToolCommandGenerator { } const csvContent = await fs.readFile(manifestPath, 'utf8'); - const tasks = csv.parse(csvContent, { + return csv.parse(csvContent, { columns: true, skip_empty_lines: true, }); - - // Filter out README files - return tasks.filter((task) => { - const nameLower = task.name.toLowerCase(); - return !nameLower.includes('readme') && task.name !== 'README'; - }); } /** @@ -48,16 +110,10 @@ class TaskToolCommandGenerator { } const csvContent = await fs.readFile(manifestPath, 'utf8'); - const tools = csv.parse(csvContent, { + return csv.parse(csvContent, { columns: true, skip_empty_lines: true, }); - - // Filter out README files - return tools.filter((tool) => { - const nameLower = tool.name.toLowerCase(); - return !nameLower.includes('readme') && tool.name !== 'README'; - }); } /** @@ -67,10 +123,9 @@ class TaskToolCommandGenerator { * @param {string} projectDir - Project directory * @param {string} bmadDir - BMAD installation directory * @param {string} baseCommandsDir - Base commands directory for the IDE - * @param {string} [fileExtension='.md'] - File extension including dot (e.g., '.md', '.toml') * @returns {Object} Generation results */ - async generateColonTaskToolCommands(projectDir, bmadDir, baseCommandsDir, fileExtension = '.md') { + async generateColonTaskToolCommands(projectDir, bmadDir, baseCommandsDir) { const tasks = await this.loadTaskManifest(bmadDir); const tools = await this.loadToolManifest(bmadDir); @@ -78,20 +133,14 @@ class TaskToolCommandGenerator { const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : []; const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : []; - // Determine format based on file extension - const format = fileExtension === '.toml' ? 'toml' : 'yaml'; let generatedCount = 0; - // DEBUG: Log parameters - console.log(`[DEBUG generateColonTaskToolCommands] baseCommandsDir: ${baseCommandsDir}, format=${format}`); - // Generate command files for tasks for (const task of standaloneTasks) { - const commandContent = this.generateCommandContent(task, 'task', format); - // Use underscore format: bmad_bmm_name. - const flatName = toColonName(task.module, 'tasks', task.name, fileExtension); + const commandContent = this.generateCommandContent(task, 'task'); + // Use underscore format: bmad_bmm_name.md + const flatName = toColonName(task.module, 'tasks', task.name); const commandPath = path.join(baseCommandsDir, flatName); - console.log(`[DEBUG generateColonTaskToolCommands] Writing task ${task.name} to: ${commandPath}`); await fs.ensureDir(path.dirname(commandPath)); await fs.writeFile(commandPath, commandContent); generatedCount++; @@ -99,9 +148,9 @@ class TaskToolCommandGenerator { // Generate command files for tools for (const tool of standaloneTools) { - const commandContent = this.generateCommandContent(tool, 'tool', format); - // Use underscore format: bmad_bmm_name. - const flatName = toColonName(tool.module, 'tools', tool.name, fileExtension); + const commandContent = this.generateCommandContent(tool, 'tool'); + // Use underscore format: bmad_bmm_name.md + const flatName = toColonName(tool.module, 'tools', tool.name); const commandPath = path.join(baseCommandsDir, flatName); await fs.ensureDir(path.dirname(commandPath)); await fs.writeFile(commandPath, commandContent); @@ -116,16 +165,15 @@ class TaskToolCommandGenerator { } /** - * Generate task and tool commands using dash format - * Creates flat files like: bmad-bmm-bmad-help.md + * Generate task and tool commands using underscore format (Windows-compatible) + * Creates flat files like: bmad_bmm_bmad-help.md * * @param {string} projectDir - Project directory * @param {string} bmadDir - BMAD installation directory * @param {string} baseCommandsDir - Base commands directory for the IDE - * @param {string} [fileExtension='.md'] - File extension including dot (e.g., '.md', '.toml') * @returns {Object} Generation results */ - async generateDashTaskToolCommands(projectDir, bmadDir, baseCommandsDir, fileExtension = '.md') { + async generateDashTaskToolCommands(projectDir, bmadDir, baseCommandsDir) { const tasks = await this.loadTaskManifest(bmadDir); const tools = await this.loadToolManifest(bmadDir); @@ -133,15 +181,13 @@ class TaskToolCommandGenerator { const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : []; const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : []; - // Determine format based on file extension - const format = fileExtension === '.toml' ? 'toml' : 'yaml'; let generatedCount = 0; // Generate command files for tasks for (const task of standaloneTasks) { - const commandContent = this.generateCommandContent(task, 'task', format); - // Use dash format: bmad-bmm-task-name. - const flatName = toDashPath(`${task.module}/tasks/${task.name}.md`, fileExtension); + const commandContent = this.generateCommandContent(task, 'task'); + // Use underscore format: bmad_bmm_name.md + const flatName = toDashPath(`${task.module}/tasks/${task.name}.md`); const commandPath = path.join(baseCommandsDir, flatName); await fs.ensureDir(path.dirname(commandPath)); await fs.writeFile(commandPath, commandContent); @@ -150,9 +196,9 @@ class TaskToolCommandGenerator { // Generate command files for tools for (const tool of standaloneTools) { - const commandContent = this.generateCommandContent(tool, 'tool', format); - // Use dash format: bmad-bmm-tool-name. - const flatName = toDashPath(`${tool.module}/tools/${tool.name}.md`, fileExtension); + const commandContent = this.generateCommandContent(tool, 'tool'); + // Use underscore format: bmad_bmm_name.md + const flatName = toDashPath(`${tool.module}/tools/${tool.name}.md`); const commandPath = path.join(baseCommandsDir, flatName); await fs.ensureDir(path.dirname(commandPath)); await fs.writeFile(commandPath, commandContent); @@ -217,163 +263,6 @@ class TaskToolCommandGenerator { return writtenCount; } - - /** - * Generate task and tool commands using suffix-based format (NEW UNIVERSAL STANDARD) - * Creates flat files like: bmad-bmm-create-story.task.md - * - * @param {string} projectDir - Project directory - * @param {string} bmadDir - BMAD installation directory - * @param {string} baseCommandsDir - Base commands directory for the IDE - * @param {string} [fileExtension='.md'] - File extension including dot (e.g., '.md', '.toml') - * @param {string} [templateContent] - Frontmatter template content (from platform-codes.yaml) - * @param {string} [frontmatterTemplate] - Frontmatter template filename - * @param {boolean} [skipExisting=false] - Skip if file already exists - * @returns {Object} Generation results - */ - async generateSuffixBasedTaskToolCommands( - projectDir, - bmadDir, - baseCommandsDir, - fileExtension = '.md', - templateContent = null, - frontmatterTemplate = 'common-yaml.md', - skipExisting = false, - ) { - const tasks = await this.loadTaskManifest(bmadDir); - const tools = await this.loadToolManifest(bmadDir); - - // Filter to only standalone items - 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 generatedCount = 0; - let skippedCount = 0; - - // Generate command files for tasks - for (const task of standaloneTasks) { - const commandContent = this.generateCommandContent(task, 'task', templateContent, frontmatterTemplate); - // Use suffix-based format: bmad-bmm-create-story.task.md - const relativePath = `${task.module}/tasks/${task.name}.md`; - const suffixName = toSuffixBasedName(relativePath, 'task', fileExtension); - const commandPath = path.join(baseCommandsDir, suffixName); - - // Skip if already exists - if (skipExisting && (await fs.pathExists(commandPath))) { - skippedCount++; - continue; - } - - await fs.ensureDir(baseCommandsDir); - await fs.writeFile(commandPath, commandContent); - generatedCount++; - } - - // Generate command files for tools - for (const tool of standaloneTools) { - const commandContent = this.generateCommandContent(tool, 'tool', templateContent, frontmatterTemplate); - // Use suffix-based format: bmad-bmm-file-ops.tool.md - const relativePath = `${tool.module}/tools/${tool.name}.md`; - const suffixName = toSuffixBasedName(relativePath, 'tool', fileExtension); - const commandPath = path.join(baseCommandsDir, suffixName); - - // Skip if already exists - if (skipExisting && (await fs.pathExists(commandPath))) { - skippedCount++; - continue; - } - - await fs.ensureDir(baseCommandsDir); - await fs.writeFile(commandPath, commandContent); - generatedCount++; - } - - if (skippedCount > 0) { - console.log(chalk.dim(` Skipped ${skippedCount} existing task/tool files`)); - } - - return { - generated: generatedCount, - tasks: standaloneTasks.length, - tools: standaloneTools.length, - }; - } - - /** - * Generate command content for a task or tool - * @param {Object} item - Task or tool item from manifest - * @param {string} type - 'task' or 'tool' - * @param {string|Object|null} [templateOrFormat] - Template content or format string ('yaml'/'toml') for backward compat - * @param {string} [frontmatterTemplate] - Template filename (for format detection) - */ - generateCommandContent(item, type, templateOrFormat = null, frontmatterTemplate = null) { - const description = item.description || `Execute ${item.displayName || item.name}`; - - // Convert path to use {project-root} placeholder - let itemPath = item.path; - if (itemPath.startsWith('bmad/')) { - itemPath = `{project-root}/${itemPath}`; - } - - const content = `# ${item.displayName || item.name} - -LOAD and execute the ${type} at: ${itemPath} - -Follow all instructions in the ${type} file exactly as written. -`; - - // Handle old calling convention: (item, type, format) where format is 'yaml' or 'toml' - if (typeof templateOrFormat === 'string' && (templateOrFormat === 'yaml' || templateOrFormat === 'toml')) { - if (templateOrFormat === 'toml') { - // TOML format - const escapedContent = content.replaceAll('"""', String.raw`\"\"\"`); - return `description = "${description}" -prompt = """ -${escapedContent} -""" -`; - } - // Default YAML format - return `--- -description: '${description.replaceAll("'", "''")}' ---- - -${content}`; - } - - // New calling convention with template content - const templateContent = templateOrFormat; - if (!templateContent || frontmatterTemplate === 'none' || (templateContent === null && frontmatterTemplate === null)) { - // Default YAML - return `--- -description: '${description.replaceAll("'", "''")}' ---- - -${content}`; - } - - // Apply template variables - const variables = { - name: item.name, - displayName: item.displayName || item.name, - description, - content, - icon: '🤖', - }; - - let result = templateContent; - for (const [key, value] of Object.entries(variables)) { - result = result.replaceAll(`{{${key}}}`, value); - } - - // Handle TOML templates specially - if (frontmatterTemplate && frontmatterTemplate.includes('toml')) { - const escapedContent = content.replaceAll('"""', String.raw`\"\"\"`); - result = result.replace(/prompt = """/, `prompt = """\n${escapedContent}`); - } - - return result; - } } module.exports = { TaskToolCommandGenerator }; diff --git a/tools/cli/installers/lib/ide/shared/unified-installer.js b/tools/cli/installers/lib/ide/shared/unified-installer.js deleted file mode 100644 index 3493ea98..00000000 --- a/tools/cli/installers/lib/ide/shared/unified-installer.js +++ /dev/null @@ -1,375 +0,0 @@ -/** - * Unified BMAD Installer for all IDEs - * - * ALL IDE configuration comes from platform-codes.yaml - * NO IDE-specific code in this file - just loads and applies templates - */ - -const path = require('node:path'); -const fs = require('fs-extra'); -const chalk = require('chalk'); -const { AgentCommandGenerator } = require('./agent-command-generator'); -const { WorkflowCommandGenerator } = require('./workflow-command-generator'); -const { TaskToolCommandGenerator } = require('./task-tool-command-generator'); -const { toColonPath, toDashPath, toSuffixBasedName, getArtifactSuffix } = require('./path-utils'); - -/** - * Naming styles - * @deprecated Use 'suffix-based' for all new installations - */ -const NamingStyle = { - FLAT_COLON: 'flat-colon', - FLAT_DASH: 'flat-dash', - NESTED: 'nested', - SUFFIX_BASED: 'suffix-based', -}; - -/** - * Unified installer configuration - * @typedef {Object} UnifiedInstallConfig - * @property {string} targetDir - Full path to target directory - * @property {NamingStyle} namingStyle - How to name files - * @property {string} [frontmatterTemplate] - Frontmatter template filename (from platform-codes.yaml) - * @property {string} [fileExtension='.md'] - File extension including dot - * @property {boolean} includeNestedStructure - For NESTED style, create subdirectories - * @property {Function} [customTemplateFn] - Optional custom template function - */ - -/** - * Unified BMAD Installer - * - * Driven entirely by platform-codes.yaml configuration - * Frontmatter templates are loaded from templates/frontmatter/ directory - */ -class UnifiedInstaller { - constructor(bmadFolderName = 'bmad') { - this.bmadFolderName = bmadFolderName; - this.templateDir = path.join(__dirname, '../templates/frontmatter'); - } - - /** - * Install BMAD artifacts for an IDE - * - * @param {string} projectDir - Project root directory - * @param {string} bmadDir - BMAD installation directory (_bmad) - * @param {UnifiedInstallConfig} config - Installation configuration - * @param {Array} selectedModules - Modules to install - * @returns {Promise} Installation result with counts - */ - async install(projectDir, bmadDir, config, selectedModules = []) { - const { - targetDir, - namingStyle = NamingStyle.SUFFIX_BASED, - frontmatterTemplate = 'common-yaml.md', - fileExtension = '.md', - includeNestedStructure = false, - customTemplateFn = null, - skipExisting = false, - artifactTypes = null, - } = config; - - // Clean up any existing BMAD files in target directory (unless skipExisting) - if (!skipExisting) { - await this.cleanupBmadFiles(targetDir, fileExtension); - } - - // Ensure target directory exists - await fs.ensureDir(targetDir); - - // Count results - const counts = { - agents: 0, - workflows: 0, - tasks: 0, - tools: 0, - total: 0, - }; - - // Check if we should install agents - const installAgents = !artifactTypes || artifactTypes.includes('agents'); - const installWorkflows = !artifactTypes || artifactTypes.includes('workflows'); - const installTasks = !artifactTypes || artifactTypes.includes('tasks'); - const installTools = !artifactTypes || artifactTypes.includes('tools'); - - // Load frontmatter template once (if not 'none') - let templateContent = null; - if (frontmatterTemplate && frontmatterTemplate !== 'none') { - templateContent = await this.loadFrontmatterTemplate(frontmatterTemplate); - } - - // 1. Install Agents - if (installAgents) { - const agentGen = new AgentCommandGenerator(this.bmadFolderName); - const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules); - counts.agents = await this.writeArtifacts( - agentArtifacts, - targetDir, - namingStyle, - templateContent, - frontmatterTemplate, - fileExtension, - customTemplateFn, - 'agent', - skipExisting, - ); - } - - // 2. Install Workflows (filter out README artifacts) - if (installWorkflows) { - const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName); - const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir); - const workflowArtifactsFiltered = workflowArtifacts.filter((a) => { - const name = path.basename(a.relativePath || ''); - return name.toLowerCase() !== 'readme.md' && !name.toLowerCase().startsWith('readme-'); - }); - counts.workflows = await this.writeArtifacts( - workflowArtifactsFiltered, - targetDir, - namingStyle, - templateContent, - frontmatterTemplate, - fileExtension, - customTemplateFn, - 'workflow', - skipExisting, - ); - } - - // 3. Install Tasks and Tools from manifest CSV - if (installTasks || installTools) { - const ttGen = new TaskToolCommandGenerator(); - - // Use suffix-based naming if specified - if (namingStyle === NamingStyle.SUFFIX_BASED) { - const taskToolResult = await ttGen.generateSuffixBasedTaskToolCommands( - projectDir, - bmadDir, - targetDir, - fileExtension, - templateContent, - frontmatterTemplate, - skipExisting, - ); - counts.tasks = taskToolResult.tasks || 0; - counts.tools = taskToolResult.tools || 0; - } else if (namingStyle === NamingStyle.FLAT_DASH) { - const taskToolResult = await ttGen.generateDashTaskToolCommands(projectDir, bmadDir, targetDir, fileExtension); - counts.tasks = taskToolResult.tasks || 0; - counts.tools = taskToolResult.tools || 0; - } else { - const taskToolResult = await ttGen.generateColonTaskToolCommands(projectDir, bmadDir, targetDir, fileExtension); - counts.tasks = taskToolResult.tasks || 0; - counts.tools = taskToolResult.tools || 0; - } - } - - counts.total = counts.agents + counts.workflows + counts.tasks + counts.tools; - - return counts; - } - - /** - * Load frontmatter template from file - * @param {string} templateFile - Template filename - * @returns {Promise} Template content or null if not found - */ - async loadFrontmatterTemplate(templateFile) { - const templatePath = path.join(this.templateDir, templateFile); - try { - return await fs.readFile(templatePath, 'utf8'); - } catch { - console.warn(chalk.yellow(`Warning: Could not load template ${templateFile}, using default`)); - return null; - } - } - - /** - * Apply frontmatter template to content - * @param {Object} artifact - Artifact with metadata - * @param {string} content - Original content - * @param {string} templateContent - Template content - * @param {string} templateFile - Template filename (for special handling) - * @returns {string} Content with frontmatter applied - */ - applyFrontmatterTemplate(artifact, content, templateContent, templateFile) { - if (!templateContent) { - return content; - } - - // Extract existing frontmatter if present - const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; - const contentWithoutFrontmatter = content.replace(frontmatterRegex, '').trim(); - - // Get artifact metadata for template substitution - const name = artifact.name || artifact.displayName || 'workflow'; - const title = this.formatTitle(name); - const iconMatch = content.match(/icon="([^"]+)"/); - const icon = iconMatch ? iconMatch[1] : '🤖'; - - // Use artifact's description if available, otherwise generate fallback - const description = artifact.description || `Activates the ${name} ${artifact.type || 'workflow'}.`; - - // Template variables - const variables = { - name, - title, - displayName: name, - description, - icon, - content: contentWithoutFrontmatter, - - // Special variables for certain templates - autoExecMode: this.getAutoExecMode(artifact), - tools: JSON.stringify(this.getCopilotTools()), - }; - - // Apply template substitutions - let result = templateContent; - for (const [key, value] of Object.entries(variables)) { - result = result.replaceAll(`{{${key}}}`, value); - } - - // Append content after frontmatter (for TOML templates with prompt field) - if (templateFile.includes('toml') && !result.includes('{{content}}')) { - const escapedContent = contentWithoutFrontmatter.replaceAll('"""', String.raw`\"\"\"`); - result = result.replace(/prompt = """/, `prompt = """\n${escapedContent}`); - } - - return result.trim() + '\n\n' + contentWithoutFrontmatter; - } - - /** - * Get auto_execution_mode for Windsurf based on artifact type - */ - getAutoExecMode(artifact) { - if (artifact.type === 'agent') return '3'; - if (artifact.type === 'task' || artifact.type === 'tool') return '2'; - return '1'; // default for workflows - } - - /** - * Get GitHub Copilot tools array - */ - getCopilotTools() { - return [ - 'changes', - 'edit', - 'fetch', - 'githubRepo', - 'problems', - 'runCommands', - 'runTasks', - 'runTests', - 'search', - 'runSubagent', - 'testFailure', - 'todos', - 'usages', - ]; - } - - /** - * Clean up any existing BMAD files in target directory - */ - async cleanupBmadFiles(targetDir, fileExtension = '.md') { - if (!(await fs.pathExists(targetDir))) { - return; - } - - const entries = await fs.readdir(targetDir, { withFileTypes: true }); - - for (const entry of entries) { - if (entry.name.startsWith('bmad') && entry.name.endsWith(fileExtension)) { - const entryPath = path.join(targetDir, entry.name); - await fs.remove(entryPath); - } - } - } - - /** - * Write artifacts with specified naming style and template - */ - async writeArtifacts( - artifacts, - targetDir, - namingStyle, - templateContent, - templateFile, - fileExtension, - customTemplateFn, - artifactType, - skipExisting = false, - ) { - let written = 0; - let skipped = 0; - - for (const artifact of artifacts) { - // Determine target path based on naming style - let targetPath; - let content = artifact.content; - - switch (namingStyle) { - case NamingStyle.SUFFIX_BASED: { - const suffixName = toSuffixBasedName(artifact.relativePath, artifactType, fileExtension); - targetPath = path.join(targetDir, suffixName); - - break; - } - case NamingStyle.FLAT_COLON: { - const flatName = toColonPath(artifact.relativePath, fileExtension); - targetPath = path.join(targetDir, flatName); - - break; - } - case NamingStyle.FLAT_DASH: { - const flatName = toDashPath(artifact.relativePath, fileExtension); - targetPath = path.join(targetDir, flatName); - - break; - } - default: { - const flatName = toColonPath(artifact.relativePath, fileExtension); - targetPath = path.join(targetDir, flatName); - } - } - - // Skip if file already exists - if (skipExisting && (await fs.pathExists(targetPath))) { - skipped++; - continue; - } - - // Apply template transformations - if (customTemplateFn) { - content = customTemplateFn(artifact, content, templateFile); - } else if (templateFile !== 'none') { - content = this.applyFrontmatterTemplate(artifact, content, templateContent, templateFile); - } - - await fs.ensureDir(targetDir); - await fs.writeFile(targetPath, content, 'utf8'); - written++; - } - - if (skipped > 0) { - console.log(chalk.dim(` Skipped ${skipped} existing files`)); - } - - return written; - } - - /** - * Format name as title - */ - formatTitle(name) { - return name - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - } -} - -module.exports = { - UnifiedInstaller, - NamingStyle, -}; diff --git a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js index f1a64467..ebf8b7f5 100644 --- a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +++ b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js @@ -14,10 +14,44 @@ class WorkflowCommandGenerator { } /** - * REMOVED: Old generateWorkflowCommands method that created nested structure. - * This was hardcoded to .claude/commands/bmad and caused bugs. - * Use collectWorkflowArtifacts() + writeColonArtifacts/writeDashArtifacts() instead. + * Generate workflow commands from the manifest CSV + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory */ + async generateWorkflowCommands(projectDir, bmadDir) { + const workflows = await this.loadWorkflowManifest(bmadDir); + + if (!workflows) { + console.log(chalk.yellow('Workflow manifest not found. Skipping command generation.')); + return { generated: 0 }; + } + + // ALL workflows now generate commands - no standalone filtering + const allWorkflows = workflows; + + // Base commands directory + const baseCommandsDir = path.join(projectDir, '.claude', 'commands', 'bmad'); + + let generatedCount = 0; + + // Generate a command file for each workflow, organized by module + for (const workflow of allWorkflows) { + const moduleWorkflowsDir = path.join(baseCommandsDir, workflow.module, 'workflows'); + await fs.ensureDir(moduleWorkflowsDir); + + const commandContent = await this.generateCommandContent(workflow, bmadDir); + const commandPath = path.join(moduleWorkflowsDir, `${workflow.name}.md`); + + await fs.writeFile(commandPath, commandContent); + generatedCount++; + } + + // Also create a workflow launcher README in each module + const groupedWorkflows = this.groupWorkflowsByModule(allWorkflows); + await this.createModuleWorkflowLaunchers(baseCommandsDir, groupedWorkflows); + + return { generated: generatedCount }; + } async collectWorkflowArtifacts(bmadDir) { const workflows = await this.loadWorkflowManifest(bmadDir); @@ -35,9 +69,6 @@ class WorkflowCommandGenerator { const commandContent = await this.generateCommandContent(workflow, bmadDir); artifacts.push({ type: 'workflow-command', - name: workflow.name, - displayName: workflow.displayName || workflow.name, - description: workflow.description, module: workflow.module, relativePath: path.join(workflow.module, 'workflows', `${workflow.name}.md`), content: commandContent, diff --git a/tools/cli/installers/lib/ide/templates/agent-command-template.md b/tools/cli/installers/lib/ide/templates/agent-command-template.md index 23974f0d..89713631 100644 --- a/tools/cli/installers/lib/ide/templates/agent-command-template.md +++ b/tools/cli/installers/lib/ide/templates/agent-command-template.md @@ -6,7 +6,7 @@ description: '{{description}}' You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. -1. LOAD the FULL agent file from @_bmad/{{relativePath}} +1. LOAD the FULL agent file from @_bmad/{{module}}/agents/{{path}} 2. READ its entire contents - this contains the complete agent persona, menu, and instructions 3. Execute ALL activation steps exactly as written in the agent file 4. Follow the agent's persona and menu system precisely diff --git a/tools/cli/installers/lib/ide/templates/codex-agent-command-template.md b/tools/cli/installers/lib/ide/templates/codex-agent-command-template.md deleted file mode 100644 index 1c82539d..00000000 --- a/tools/cli/installers/lib/ide/templates/codex-agent-command-template.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: '{{name}}' -description: '{{description}}' ---- - -You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. - - -1. LOAD the FULL agent file from @_bmad/{{relativePath}} -2. READ its entire contents - this contains the complete agent persona, menu, and instructions -3. FOLLOW every step in the section precisely -4. DISPLAY the welcome/greeting as instructed -5. PRESENT the numbered menu -6. WAIT for user input before proceeding - diff --git a/tools/cli/installers/lib/ide/templates/codex-custom-agent-template.md b/tools/cli/installers/lib/ide/templates/codex-custom-agent-template.md deleted file mode 100644 index c1a547fa..00000000 --- a/tools/cli/installers/lib/ide/templates/codex-custom-agent-template.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: '{{name}}' -description: '{{description}}' ---- - -{{activationHeader}} - -Run @_bmad/{{relativePath}} to load the full agent. diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/common-toml.md b/tools/cli/installers/lib/ide/templates/frontmatter/common-toml.md deleted file mode 100644 index d26a92d3..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/common-toml.md +++ /dev/null @@ -1,4 +0,0 @@ -description = "{{description}}" -prompt = """ -{{content}} -""" diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/common-yaml.md b/tools/cli/installers/lib/ide/templates/frontmatter/common-yaml.md deleted file mode 100644 index a384374c..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/common-yaml.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: '{{name}}' -description: '{{description}}' ---- diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/copilot-agent.md b/tools/cli/installers/lib/ide/templates/frontmatter/copilot-agent.md deleted file mode 100644 index 389b1862..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/copilot-agent.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -description: "{{description}}" -tools: {{tools}} ---- - -# {{title}} - diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/copilot.md b/tools/cli/installers/lib/ide/templates/frontmatter/copilot.md deleted file mode 100644 index 76d696e8..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/copilot.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -description: "{{description}}" -tools: {{tools}} ---- diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/opencode-agent.md b/tools/cli/installers/lib/ide/templates/frontmatter/opencode-agent.md deleted file mode 100644 index 2b633613..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/opencode-agent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -name: '{{name}}' -description: 'BMAD {{name}} agent' -mode: 'primary' ---- diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/opencode.md b/tools/cli/installers/lib/ide/templates/frontmatter/opencode.md deleted file mode 100644 index e2ae0a7c..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/opencode.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: '{{name}}' -description: 'BMAD {{name}} command' ---- diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/roo.md b/tools/cli/installers/lib/ide/templates/frontmatter/roo.md deleted file mode 100644 index 758f9b8f..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/roo.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: '{{icon}} {{title}}' -description: 'Use for {{title}} tasks' ---- diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/trae.md b/tools/cli/installers/lib/ide/templates/frontmatter/trae.md deleted file mode 100644 index d979b4c3..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/trae.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -description: "{{name}}" -always: true ---- diff --git a/tools/cli/installers/lib/ide/templates/frontmatter/windsurf.md b/tools/cli/installers/lib/ide/templates/frontmatter/windsurf.md deleted file mode 100644 index e1d48c9e..00000000 --- a/tools/cli/installers/lib/ide/templates/frontmatter/windsurf.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -description: {{name}} -auto_execution_mode: {{autoExecMode}} ---- diff --git a/tools/cli/installers/lib/ide/templates/workflow-commander.md b/tools/cli/installers/lib/ide/templates/workflow-commander.md index 7355bc73..3645c1a2 100644 --- a/tools/cli/installers/lib/ide/templates/workflow-commander.md +++ b/tools/cli/installers/lib/ide/templates/workflow-commander.md @@ -1,5 +1,4 @@ --- -name: '{{name}}' description: '{{description}}' --- diff --git a/tools/cli/installers/lib/ide/trae.js b/tools/cli/installers/lib/ide/trae.js new file mode 100644 index 00000000..c9f8e893 --- /dev/null +++ b/tools/cli/installers/lib/ide/trae.js @@ -0,0 +1,313 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); + +/** + * Trae IDE setup handler + */ +class TraeSetup extends BaseIdeSetup { + constructor() { + super('trae', 'Trae'); + this.configDir = '.trae'; + this.rulesDir = 'rules'; + } + + /** + * Setup Trae IDE 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}...`)); + + // Create .trae/rules directory + const traeDir = path.join(projectDir, this.configDir); + const rulesDir = path.join(traeDir, this.rulesDir); + + await this.ensureDir(rulesDir); + + // Clean up any existing BMAD files before reinstalling + await this.cleanup(projectDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Get tasks, tools, and workflows (standalone only) + const tasks = await this.getTasks(bmadDir, true); + const tools = await this.getTools(bmadDir, true); + const workflows = await this.getWorkflows(bmadDir, true); + + // Process agents as rules with bmad- prefix + let agentCount = 0; + for (const artifact of agentArtifacts) { + const processedContent = await this.createAgentRule(artifact, bmadDir, projectDir); + + // Use bmad- prefix: bmad-agent-{module}-{name}.md + const targetPath = path.join(rulesDir, `bmad-agent-${artifact.module}-${artifact.name}.md`); + await this.writeFile(targetPath, processedContent); + agentCount++; + } + + // Process tasks as rules with bmad- prefix + let taskCount = 0; + for (const task of tasks) { + const content = await this.readFile(task.path); + const processedContent = this.createTaskRule(task, content); + + // Use bmad- prefix: bmad-task-{module}-{name}.md + const targetPath = path.join(rulesDir, `bmad-task-${task.module}-${task.name}.md`); + await this.writeFile(targetPath, processedContent); + taskCount++; + } + + // Process tools as rules with bmad- prefix + let toolCount = 0; + for (const tool of tools) { + const content = await this.readFile(tool.path); + const processedContent = this.createToolRule(tool, content); + + // Use bmad- prefix: bmad-tool-{module}-{name}.md + const targetPath = path.join(rulesDir, `bmad-tool-${tool.module}-${tool.name}.md`); + await this.writeFile(targetPath, processedContent); + toolCount++; + } + + // Process workflows as rules with bmad- prefix + let workflowCount = 0; + for (const workflow of workflows) { + const content = await this.readFile(workflow.path); + const processedContent = this.createWorkflowRule(workflow, content); + + // Use bmad- prefix: bmad-workflow-{module}-{name}.md + const targetPath = path.join(rulesDir, `bmad-workflow-${workflow.module}-${workflow.name}.md`); + await this.writeFile(targetPath, processedContent); + workflowCount++; + } + + const totalRules = agentCount + taskCount + toolCount + workflowCount; + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agent rules created`)); + console.log(chalk.dim(` - ${taskCount} task rules created`)); + console.log(chalk.dim(` - ${toolCount} tool rules created`)); + console.log(chalk.dim(` - ${workflowCount} workflow rules created`)); + console.log(chalk.dim(` - Total: ${totalRules} rules`)); + console.log(chalk.dim(` - Rules directory: ${path.relative(projectDir, rulesDir)}`)); + console.log(chalk.dim(` - Agents can be activated with @{agent-name}`)); + + return { + success: true, + rules: totalRules, + agents: agentCount, + tasks: taskCount, + tools: toolCount, + workflows: workflowCount, + }; + } + + /** + * Create rule content for an agent + */ + async createAgentRule(artifact, bmadDir, projectDir) { + // Strip frontmatter from launcher + const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; + const contentWithoutFrontmatter = artifact.content.replace(frontmatterRegex, '').trim(); + + // Extract metadata from launcher content + const titleMatch = artifact.content.match(/description:\s*"([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(artifact.name); + + // Calculate relative path for reference + const relativePath = path.relative(projectDir, artifact.sourcePath).replaceAll('\\', '/'); + + let ruleContent = `# ${title} Agent Rule + +This rule is triggered when the user types \`@${artifact.name}\` and activates the ${title} agent persona. + +## Agent Activation + +${contentWithoutFrontmatter} + +## File Reference + +The full agent definition is located at: \`${relativePath}\` +`; + + return ruleContent; + } + + /** + * Create rule content for a task + */ + createTaskRule(task, content) { + // Extract task name from content + const nameMatch = content.match(/name="([^"]+)"/); + const taskName = nameMatch ? nameMatch[1] : this.formatTitle(task.name); + + let ruleContent = `# ${taskName} Task Rule + +This rule defines the ${taskName} task workflow. + +## Task Definition + +When this task is triggered, execute the following workflow: + +${content} + +## Usage + +Reference this task with \`@task-${task.name}\` to execute the defined workflow. + +## Module + +Part of the BMAD ${task.module.toUpperCase()} module. +`; + + return ruleContent; + } + + /** + * Create rule content for a tool + */ + createToolRule(tool, content) { + // Extract tool name from content + const nameMatch = content.match(/name="([^"]+)"/); + const toolName = nameMatch ? nameMatch[1] : this.formatTitle(tool.name); + + let ruleContent = `# ${toolName} Tool Rule + +This rule defines the ${toolName} tool. + +## Tool Definition + +When this tool is triggered, execute the following: + +${content} + +## Usage + +Reference this tool with \`@tool-${tool.name}\` to execute it. + +## Module + +Part of the BMAD ${tool.module.toUpperCase()} module. +`; + + return ruleContent; + } + + /** + * Create rule content for a workflow + */ + createWorkflowRule(workflow, content) { + let ruleContent = `# ${workflow.name} Workflow Rule + +This rule defines the ${workflow.name} workflow. + +## Workflow Description + +${workflow.description || 'No description provided'} + +## Workflow Definition + +${content} + +## Usage + +Reference this workflow with \`@workflow-${workflow.name}\` to execute the guided workflow. + +## Module + +Part of the BMAD ${workflow.module.toUpperCase()} module. +`; + + return ruleContent; + } + + /** + * Format agent/task name as title + */ + formatTitle(name) { + return name + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } + + /** + * Cleanup Trae configuration - surgically remove only BMAD files + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const rulesPath = path.join(projectDir, this.configDir, this.rulesDir); + + if (await fs.pathExists(rulesPath)) { + // Remove any bmad* files (cleans up old bmad- and bmad: formats) + const files = await fs.readdir(rulesPath); + let removed = 0; + + for (const file of files) { + if (file.startsWith('bmad') && file.endsWith('.md')) { + await fs.remove(path.join(rulesPath, file)); + removed++; + } + } + + if (removed > 0) { + console.log(chalk.dim(` Cleaned up ${removed} existing BMAD rules`)); + } + } + } + + /** + * Install a custom agent launcher for Trae + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object} Installation result + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const traeDir = path.join(projectDir, this.configDir); + const rulesDir = path.join(traeDir, this.rulesDir); + + // Create .trae/rules directory if it doesn't exist + await fs.ensureDir(rulesDir); + + // Create custom agent launcher + const launcherContent = `# ${agentName} Custom Agent + +**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent! + +This is a launcher for the custom BMAD agent "${agentName}". + +## Usage +1. First run: \`${agentPath}\` to load the complete agent +2. Then use this rule to activate ${agentName} + +The agent will follow the persona and instructions from the main agent file. + +--- + +*Generated by BMAD Method*`; + + const fileName = `bmad-agent-custom-${agentName.toLowerCase()}.md`; + const launcherPath = path.join(rulesDir, fileName); + + // Write the launcher file + await fs.writeFile(launcherPath, launcherContent, 'utf8'); + + return { + ide: 'trae', + path: path.relative(projectDir, launcherPath), + command: agentName, + type: 'custom-agent-launcher', + }; + } +} + +module.exports = { TraeSetup }; diff --git a/tools/cli/installers/lib/ide/windsurf.js b/tools/cli/installers/lib/ide/windsurf.js new file mode 100644 index 00000000..92596db3 --- /dev/null +++ b/tools/cli/installers/lib/ide/windsurf.js @@ -0,0 +1,258 @@ +const path = require('node:path'); +const { BaseIdeSetup } = require('./_base-ide'); +const chalk = require('chalk'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); + +/** + * Windsurf IDE setup handler + */ +class WindsurfSetup extends BaseIdeSetup { + constructor() { + super('windsurf', 'Windsurf', true); // preferred IDE + this.configDir = '.windsurf'; + this.workflowsDir = 'workflows'; + } + + /** + * Setup Windsurf IDE 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}...`)); + + // Create .windsurf/workflows/bmad directory structure + const windsurfDir = path.join(projectDir, this.configDir); + const workflowsDir = path.join(windsurfDir, this.workflowsDir); + const bmadWorkflowsDir = path.join(workflowsDir, 'bmad'); + + await this.ensureDir(bmadWorkflowsDir); + + // Clean up any existing BMAD workflows before reinstalling + await this.cleanup(projectDir); + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + + // Convert artifacts to agent format for module organization + const agents = agentArtifacts.map((a) => ({ module: a.module, name: a.name })); + + // Get tasks, tools, and workflows (standalone only) + const tasks = await this.getTasks(bmadDir, true); + const tools = await this.getTools(bmadDir, true); + const workflows = await this.getWorkflows(bmadDir, true); + + // Create directories for each module under bmad/ + const modules = new Set(); + for (const item of [...agents, ...tasks, ...tools, ...workflows]) modules.add(item.module); + + for (const module of modules) { + await this.ensureDir(path.join(bmadWorkflowsDir, module)); + await this.ensureDir(path.join(bmadWorkflowsDir, module, 'agents')); + await this.ensureDir(path.join(bmadWorkflowsDir, module, 'tasks')); + await this.ensureDir(path.join(bmadWorkflowsDir, module, 'tools')); + await this.ensureDir(path.join(bmadWorkflowsDir, module, 'workflows')); + } + + // Process agent launchers as workflows with organized structure + let agentCount = 0; + for (const artifact of agentArtifacts) { + const processedContent = this.createWorkflowContent({ module: artifact.module, name: artifact.name }, artifact.content); + + // Organized path: bmad/module/agents/agent-name.md + const targetPath = path.join(bmadWorkflowsDir, artifact.module, 'agents', `${artifact.name}.md`); + await this.writeFile(targetPath, processedContent); + agentCount++; + } + + // Process tasks as workflows with organized structure + let taskCount = 0; + for (const task of tasks) { + const content = await this.readFile(task.path); + const processedContent = this.createTaskWorkflowContent(task, content); + + // Organized path: bmad/module/tasks/task-name.md + const targetPath = path.join(bmadWorkflowsDir, task.module, 'tasks', `${task.name}.md`); + await this.writeFile(targetPath, processedContent); + taskCount++; + } + + // Process tools as workflows with organized structure + let toolCount = 0; + for (const tool of tools) { + const content = await this.readFile(tool.path); + const processedContent = this.createToolWorkflowContent(tool, content); + + // Organized path: bmad/module/tools/tool-name.md + const targetPath = path.join(bmadWorkflowsDir, tool.module, 'tools', `${tool.name}.md`); + await this.writeFile(targetPath, processedContent); + toolCount++; + } + + // Process workflows with organized structure + let workflowCount = 0; + for (const workflow of workflows) { + const content = await this.readFile(workflow.path); + const processedContent = this.createWorkflowWorkflowContent(workflow, content); + + // Organized path: bmad/module/workflows/workflow-name.md + const targetPath = path.join(bmadWorkflowsDir, workflow.module, 'workflows', `${workflow.name}.md`); + await this.writeFile(targetPath, processedContent); + workflowCount++; + } + + console.log(chalk.green(`✓ ${this.name} configured:`)); + console.log(chalk.dim(` - ${agentCount} agents installed`)); + console.log(chalk.dim(` - ${taskCount} tasks installed`)); + console.log(chalk.dim(` - ${toolCount} tools installed`)); + console.log(chalk.dim(` - ${workflowCount} workflows installed`)); + console.log(chalk.dim(` - Organized in modules: ${[...modules].join(', ')}`)); + console.log(chalk.dim(` - Workflows directory: ${path.relative(projectDir, workflowsDir)}`)); + + // Provide additional configuration hints + if (options.showHints !== false) { + console.log(chalk.dim('\n Windsurf workflow settings:')); + console.log(chalk.dim(' - auto_execution_mode: 3 (recommended for agents)')); + console.log(chalk.dim(' - auto_execution_mode: 2 (recommended for tasks/tools)')); + console.log(chalk.dim(' - auto_execution_mode: 1 (recommended for workflows)')); + console.log(chalk.dim(' - Workflows can be triggered via the Windsurf menu')); + } + + return { + success: true, + agents: agentCount, + tasks: taskCount, + tools: toolCount, + workflows: workflowCount, + }; + } + + /** + * Create workflow content for an agent + */ + createWorkflowContent(agent, content) { + // Strip existing frontmatter from launcher + const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/; + const contentWithoutFrontmatter = content.replace(frontmatterRegex, ''); + + // Create simple Windsurf frontmatter matching original format + let workflowContent = `--- +description: ${agent.name} +auto_execution_mode: 3 +--- + +${contentWithoutFrontmatter}`; + + return workflowContent; + } + + /** + * Create workflow content for a task + */ + createTaskWorkflowContent(task, content) { + // Create simple Windsurf frontmatter matching original format + let workflowContent = `--- +description: task-${task.name} +auto_execution_mode: 2 +--- + +${content}`; + + return workflowContent; + } + + /** + * Create workflow content for a tool + */ + createToolWorkflowContent(tool, content) { + // Create simple Windsurf frontmatter matching original format + let workflowContent = `--- +description: tool-${tool.name} +auto_execution_mode: 2 +--- + +${content}`; + + return workflowContent; + } + + /** + * Create workflow content for a workflow + */ + createWorkflowWorkflowContent(workflow, content) { + // Create simple Windsurf frontmatter matching original format + let workflowContent = `--- +description: ${workflow.name} +auto_execution_mode: 1 +--- + +${content}`; + + return workflowContent; + } + + /** + * Cleanup Windsurf configuration - surgically remove only BMAD files + */ + async cleanup(projectDir) { + const fs = require('fs-extra'); + const bmadPath = path.join(projectDir, this.configDir, this.workflowsDir, 'bmad'); + + if (await fs.pathExists(bmadPath)) { + // Remove the entire bmad folder - this is our territory + await fs.remove(bmadPath); + console.log(chalk.dim(` Cleaned up existing BMAD workflows`)); + } + } + + /** + * Install a custom agent launcher for Windsurf + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created command + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const fs = require('fs-extra'); + const customAgentsDir = path.join(projectDir, this.configDir, this.workflowsDir, 'bmad', 'custom', 'agents'); + + if (!(await this.exists(path.join(projectDir, this.configDir)))) { + return null; // IDE not configured for this project + } + + await this.ensureDir(customAgentsDir); + + 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. + + +1. LOAD the FULL agent file from @${agentPath} +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + +`; + + // Windsurf uses workflow format with frontmatter + const workflowContent = `--- +description: ${metadata.title || agentName} +auto_execution_mode: 3 +--- + +${launcherContent}`; + + const launcherPath = path.join(customAgentsDir, `${agentName}.md`); + await fs.writeFile(launcherPath, workflowContent); + + return { + path: launcherPath, + command: `bmad/custom/agents/${agentName}`, + }; + } +} + +module.exports = { WindsurfSetup }; diff --git a/tools/cli/lib/platform-codes.js b/tools/cli/lib/platform-codes.js index 4fa85e3f..bdf0e48c 100644 --- a/tools/cli/lib/platform-codes.js +++ b/tools/cli/lib/platform-codes.js @@ -9,7 +9,7 @@ const { getProjectRoot } = require('./project-root'); */ class PlatformCodes { constructor() { - this.configPath = path.join(getProjectRoot(), 'tools/cli/installers/lib/ide/platform-codes.yaml'); + this.configPath = path.join(getProjectRoot(), 'tools', 'platform-codes.yaml'); this.loadConfig(); } diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index eee4ad75..614e5016 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -363,8 +363,8 @@ class UI { const { IdeManager } = require('../installers/lib/ide/manager'); const ideManager = new IdeManager(); - const preferredIdes = await ideManager.getPreferredIdes(); - const otherIdes = await ideManager.getOtherIdes(); + const preferredIdes = ideManager.getPreferredIdes(); + const otherIdes = ideManager.getOtherIdes(); // Build grouped options object for groupMultiselect const groupedOptions = {}; diff --git a/tools/docs/fix-refs.md b/tools/docs/fix-refs.md new file mode 100644 index 00000000..df9835b4 --- /dev/null +++ b/tools/docs/fix-refs.md @@ -0,0 +1,91 @@ +--- +title: Fix Documentation References +description: Corrects workflow, agent, and command references in BMad documentation +--- + +# Fix Documentation References + +## Scope + +Fix reference patterns ONLY. Do not modify links, formatting, structure, or other content. + +## Purpose + +Fix incorrect references to workflows, agents, and commands in BMad documentation files. + +## Step 1: Establish Target Audience + +Before fixing references, determine who the document is for: + +| Audience | Indicators | Style | +|----------|------------|-------| +| **Newbies** | tutorials/, getting-started, installation/, "What You'll Learn" | Keep "workflow", include platform hints | +| **Experienced** | reference/, explanation/ | Drop "workflow", no platform hints | +| **How-To** | how-to/ | **Ask** — depends on the task | + +**How-To guides require judgment**: Don't assume experienced. Ask: "Does this task require prior BMad knowledge?" Early-journey tasks (first PRD, first sprint) are newbie docs. Customization and advanced features are experienced. + +**If unclear**: Ask the user "Who is the target audience for this document — new users learning BMad, or experienced users who know the system?" + +This determines whether helper words like "workflow" and platform hints are helpful context or just noise. + +## Reference Patterns to Fix + +### Always Wrong + +| Pattern | Example | Problem | +|---------|---------|---------| +| `*workflow` | `*prd` | Obsolete menu shortcut notation | +| `/workflow` | `/workflow-init` | Platform-specific slash command | +| `bmad_bmm_*` | `bmad_bmm_workflow-init` | Internal slash command name, platform-specific | + +### Correct Format + +Use backticks with plain workflow name: +- **Wrong**: Run `/workflow-init` +- **Wrong**: Run `*prd` + +**When to say "workflow"**: +- **Newbie docs** (getting-started): "Run the `prd` workflow" — helps them learn what it is +- **Other docs**: "Run `prd`" — they already know, so "workflow" is noise + +**Platform hint**: Only in newbie docs, and only on the **first** workflow mention: +- First mention: Run the `help` workflow (`/bmad-help` on most platforms) +- Subsequent mentions: Run `prd` — no hint, no "workflow" needed after they've seen the pattern + +In experienced docs, the hint is always noise — just use the workflow name. + +### Workflow Name Changes + +| Old Name | New Name | Notes | +|----------|----------|-------| +| `workflow-init` | `bmad-help` | DEPRECATED - help system replaces initialization | +| `workflow-status` | `bmad-help` | DEPRECATED - help system replaces status checking | + +### The Help System + +The `bmad-help` workflow is the modern replacement for both `workflow-init` and `workflow-status`: +- **Universal**: Works regardless of workflow state or module +- **Contextual**: Infers completion from artifacts and conversation +- **Adaptive**: Guides users through workflows based on phase ordering +- **Anytime**: Can be run at any point, no pre-initialization needed + +Users can run `bmad-help` to get guidance on what to do next. It detects: +- What workflows have been completed (by checking for output artifacts) +- What module is active +- What the next recommended/required step is + +## Lessons Learned + +1. **Platform-agnostic**: Docs should never include platform-specific invocation patterns (slashes, prefixes) +2. **Backtick the name**: Use backticks around workflow names: `workflow-name` +3. **Simple names**: Just the workflow name, no `bmad_bmm_` prefix, no `/` prefix + +## Self-Check + +Before finishing, verify you ONLY changed reference patterns: + +- [ ] Did I change any hyperlinks? **If yes, revert.** +- [ ] Did I change any formatting (horizontal rules, whitespace, structure)? **If yes, revert.** +- [ ] Did I remove or add any sections? **If yes, revert.** +- [ ] Did I modify anything not matching the patterns in "Reference Patterns to Fix"? **If yes, revert.** diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/platform-codes.yaml similarity index 52% rename from tools/cli/installers/lib/ide/platform-codes.yaml rename to tools/platform-codes.yaml index 61f5b854..04c4a45f 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/platform-codes.yaml @@ -5,177 +5,127 @@ # the installation system to identify different platforms (IDEs, tools, etc.) # # Format: -# code: Platform identifier used internally (key) +# code: Platform identifier used internally # name: Display name shown to users # preferred: Whether this platform is shown as a recommended option on install -# category: Type of platform (ide, cli, tool, service, etc.) -# installer: Installation configuration (optional) -# frontmatter_template: Path to frontmatter template file (relative to templates/frontmatter/) -# If not specified, uses 'common-yaml.md' default +# category: Type of platform (ide, tool, service, etc.) platforms: - antigravity: - name: "Google Antigravity" - preferred: false - category: ide - description: "Google's AI development environment" - installer: - target_dir: .antigravity/commands - frontmatter_template: common-yaml.md - - auggie: - name: "Auggie" - preferred: false - category: cli - description: "AI development tool" - installer: - target_dir: .augment/commands - frontmatter_template: common-yaml.md - - cline: - name: "Cline" - preferred: false - category: ide - description: "AI coding assistant" - installer: - target_dir: .cline/commands - frontmatter_template: none # No frontmatter, content as-is - + # Recommended Platforms claude-code: name: "Claude Code" preferred: true category: cli description: "Anthropic's official CLI for Claude" - installer: - target_dir: .claude/commands - frontmatter_template: common-yaml.md - - crush: - name: "Crush" - preferred: false - category: ide - description: "AI development assistant" - installer: - target_dir: .crush/commands - frontmatter_template: common-yaml.md - - cursor: - name: "Cursor" - preferred: true - category: ide - description: "AI-first code editor" - installer: - target_dir: .cursor/commands - frontmatter_template: common-yaml.md - - gemini: - name: "Gemini CLI" - preferred: false - category: cli - description: "Google's CLI for Gemini" - installer: - target_dir: .gemini/commands - file_extension: .toml - frontmatter_template: common-toml.md - - github-copilot: - name: "GitHub Copilot" - preferred: true - category: ide - description: "GitHub's AI pair programmer" - installer: - targets: - - dir: .github/agents - frontmatter_template: copilot-agent.md - artifact_types: [agents] - - dir: .github/prompts - frontmatter_template: copilot.md - artifact_types: [workflows, tasks, tools] - has_vscode_settings: true - - iflow: - name: "iFlow" - preferred: false - category: ide - description: "AI workflow automation" - installer: - target_dir: .iflow/commands - frontmatter_template: common-yaml.md - - kilo: - name: "KiloCoder" - preferred: false - category: ide - description: "AI coding platform" - # Kilo has custom installer (.kilocodemodes YAML format) - not config-driven - - kiro-cli: - name: "Kiro CLI" - preferred: false - category: cli - description: "Kiro command-line interface" - # Kiro CLI has custom installer (YAML->JSON conversion) - not config-driven - - opencode: - name: "OpenCode" - preferred: false - category: ide - description: "OpenCode terminal coding assistant" - installer: - targets: - - dir: .opencode/agent - frontmatter_template: opencode-agent.md - artifact_types: [agents] - - dir: .opencode/command - frontmatter_template: opencode.md - artifact_types: [workflows, tasks, tools] - - qwen: - name: "QwenCoder" - preferred: false - category: ide - description: "Qwen AI coding assistant" - installer: - target_dir: .qwen/commands - file_extension: .toml - frontmatter_template: common-toml.md - - roo: - name: "Roo Code" - preferred: false - category: ide - description: "Enhanced Cline fork" - installer: - target_dir: .roo/commands - frontmatter_template: roo.md - skip_existing: true - - rovo-dev: - name: "Rovo Dev" - preferred: false - category: ide - description: "Atlassian's Rovo development environment" - installer: - target_dir: .rovo-dev/commands - frontmatter_template: common-yaml.md - - trae: - name: "Trae" - preferred: false - category: ide - description: "AI coding tool" - installer: - target_dir: .trae/rules - frontmatter_template: trae.md windsurf: name: "Windsurf" preferred: true category: ide description: "AI-powered IDE with cascade flows" - installer: - target_dir: .windsurf/workflows - frontmatter_template: windsurf.md + + cursor: + name: "Cursor" + preferred: true + category: ide + description: "AI-first code editor" + + # Other IDEs and Tools + cline: + name: "Cline" + preferred: false + category: ide + description: "AI coding assistant" + + opencode: + name: "OpenCode" + preferred: false + category: ide + description: "OpenCode terminal coding assistant" + + auggie: + name: "Auggie" + preferred: false + category: cli + description: "AI development tool" + + roo: + name: "Roo Cline" + preferred: false + category: ide + description: "Enhanced Cline fork" + + rovo: + name: "Rovo" + preferred: false + category: ide + description: "Atlassian's AI coding assistant" + + rovo-dev: + name: "Rovo Dev" + preferred: false + category: ide + description: "Atlassian's Rovo development environment" + + kiro-cli: + name: "Kiro CLI" + preferred: false + category: cli + description: "Kiro command-line interface" + + github-copilot: + name: "GitHub Copilot" + preferred: false + category: ide + description: "GitHub's AI pair programmer" + + codex: + name: "Codex" + preferred: false + category: cli + description: "OpenAI Codex integration" + + qwen: + name: "QwenCoder" + preferred: false + category: ide + description: "Qwen AI coding assistant" + + gemini: + name: "Gemini CLI" + preferred: false + category: cli + description: "Google's CLI for Gemini" + + iflow: + name: "iFlow" + preferred: false + category: ide + description: "AI workflow automation" + + kilo: + name: "KiloCoder" + preferred: false + category: ide + description: "AI coding platform" + + crush: + name: "Crush" + preferred: false + category: ide + description: "AI development assistant" + + antigravity: + name: "Google Antigravity" + preferred: false + category: ide + description: "Google's AI development environment" + + trae: + name: "Trae" + preferred: false + category: ide + description: "AI coding tool" # Platform categories categories: @@ -205,12 +155,3 @@ conventions: name_format: "Title Case" max_code_length: 20 allowed_characters: "a-z0-9-" - - # New universal file naming standard - file_naming: - agent: "bmad-{module}-{name}.agent.md" - workflow: "bmad-{module}-{name}.workflow.md" - task: "bmad-{module}-{name}.task.md" - tool: "bmad-{module}-{name}.tool.md" - example_agent: "bmad-cis-storymaster.agent.md" - example_workflow: "bmad-bmm-plan-project.workflow.md" diff --git a/tools/validate-doc-links.js b/tools/validate-doc-links.js index 55b23fd7..f6f514cc 100644 --- a/tools/validate-doc-links.js +++ b/tools/validate-doc-links.js @@ -28,7 +28,7 @@ const LINK_REGEX = /\[([^\]]*)\]\((\/[^)]+)\)/g; const STATIC_ASSET_EXTENSIONS = ['.zip', '.txt', '.pdf', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico']; // Custom Astro page routes (not part of the docs content collection) -const CUSTOM_PAGE_ROUTES = new Set(['/workflow-guide']); +const CUSTOM_PAGE_ROUTES = new Set([]); // Regex to extract headings for anchor validation const HEADING_PATTERN = /^#{1,6}\s+(.+)$/gm; diff --git a/website/src/components/WorkflowGuide.astro b/website/_basement/components/WorkflowGuide.astro similarity index 100% rename from website/src/components/WorkflowGuide.astro rename to website/_basement/components/WorkflowGuide.astro diff --git a/website/src/pages/workflow-guide.astro b/website/_basement/pages/workflow-guide.astro similarity index 100% rename from website/src/pages/workflow-guide.astro rename to website/_basement/pages/workflow-guide.astro diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 22c89e1e..a57bc648 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -3,6 +3,7 @@ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import sitemap from '@astrojs/sitemap'; import rehypeMarkdownLinks from './src/rehype-markdown-links.js'; +import rehypeBasePaths from './src/rehype-base-paths.js'; import { getSiteUrl } from './src/lib/site-url.js'; const siteUrl = getSiteUrl(); @@ -28,7 +29,10 @@ export default defineConfig({ }, markdown: { - rehypePlugins: [rehypeMarkdownLinks], + rehypePlugins: [ + [rehypeMarkdownLinks, { base: basePath }], + [rehypeBasePaths, { base: basePath }], + ], }, integrations: [ @@ -89,121 +93,55 @@ export default defineConfig({ { label: 'Tutorials', collapsed: false, - items: [ - { - label: 'Getting Started', - autogenerate: { directory: 'tutorials/getting-started' }, - }, - { - label: 'Advanced', - autogenerate: { directory: 'tutorials/advanced' }, - }, - ], + autogenerate: { directory: 'tutorials' }, }, { label: 'How-To Guides', collapsed: true, - items: [ - { slug: 'how-to/get-answers-about-bmad' }, - { - label: 'Installation', - autogenerate: { directory: 'how-to/installation' }, - }, - { - label: 'Workflows', - autogenerate: { directory: 'how-to/workflows' }, - }, - { - label: 'Customization', - autogenerate: { directory: 'how-to/customization' }, - }, - { - label: 'Brownfield Development', - autogenerate: { directory: 'how-to/brownfield' }, - }, - { - label: 'Troubleshooting', - autogenerate: { directory: 'how-to/troubleshooting' }, - }, - ], + autogenerate: { directory: 'how-to' }, }, { label: 'Explanation', collapsed: true, - items: [ - { - label: 'Core Concepts', - autogenerate: { directory: 'explanation/core-concepts' }, - }, - { - label: 'Architecture', - autogenerate: { directory: 'explanation/architecture' }, - }, - { - label: 'Philosophy', - autogenerate: { directory: 'explanation/philosophy' }, - }, - { - label: 'Features', - autogenerate: { directory: 'explanation/features' }, - }, - { - label: 'TEA (Test Architect)', - autogenerate: { directory: 'explanation/tea' }, - }, - { - label: 'Agents', - autogenerate: { directory: 'explanation/agents' }, - }, - { - label: 'BMM', - autogenerate: { directory: 'explanation/bmm' }, - }, - { - label: 'BMad Builder', - autogenerate: { directory: 'explanation/bmad-builder' }, - }, - { - label: 'Game Development', - autogenerate: { directory: 'explanation/game-dev' }, - }, - { - label: 'Creative Intelligence', - autogenerate: { directory: 'explanation/creative-intelligence' }, - }, - { - label: 'Core Module', - autogenerate: { directory: 'explanation/core' }, - }, - { - label: 'FAQ', - autogenerate: { directory: 'explanation/faq' }, - }, - ], + autogenerate: { directory: 'explanation' }, }, { label: 'Reference', collapsed: true, + autogenerate: { directory: 'reference' }, + }, + { + label: 'TEA - Testing in BMAD', + collapsed: true, items: [ { - label: 'Agents', - autogenerate: { directory: 'reference/agents' }, + label: 'Tutorials', + autogenerate: { directory: 'tea/tutorials' }, }, { - label: 'Workflows', - autogenerate: { directory: 'reference/workflows' }, + label: 'How-To Guides', + items: [ + { + label: 'Workflows', + autogenerate: { directory: 'tea/how-to/workflows' }, + }, + { + label: 'Customization', + autogenerate: { directory: 'tea/how-to/customization' }, + }, + { + label: 'Brownfield', + autogenerate: { directory: 'tea/how-to/brownfield' }, + }, + ], }, { - label: 'Configuration', - autogenerate: { directory: 'reference/configuration' }, + label: 'Explanation', + autogenerate: { directory: 'tea/explanation' }, }, { - label: 'TEA (Test Architect)', - autogenerate: { directory: 'reference/tea' }, - }, - { - label: 'Glossary', - autogenerate: { directory: 'reference/glossary' }, + label: 'Reference', + autogenerate: { directory: 'tea/reference' }, }, ], }, @@ -215,6 +153,9 @@ export default defineConfig({ // Pagination pagination: false, + // Use our docs/404.md instead of Starlight's built-in 404 + disable404Route: true, + // Custom components components: { Header: './src/components/Header.astro', diff --git a/website/public/img/workflow-map.png b/website/public/img/workflow-map.png new file mode 100644 index 00000000..61e0ab4a Binary files /dev/null and b/website/public/img/workflow-map.png differ diff --git a/website/src/pages/404.astro b/website/src/pages/404.astro new file mode 100644 index 00000000..46065d04 --- /dev/null +++ b/website/src/pages/404.astro @@ -0,0 +1,11 @@ +--- +import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +import { getEntry } from 'astro:content'; + +const entry = await getEntry('docs', '404'); +const { Content } = await entry.render(); +--- + + + + diff --git a/website/src/rehype-base-paths.js b/website/src/rehype-base-paths.js new file mode 100644 index 00000000..b5a4c88f --- /dev/null +++ b/website/src/rehype-base-paths.js @@ -0,0 +1,77 @@ +/** + * Rehype plugin to prepend base path to absolute URLs + * + * Transforms: + * /img/foo.png → /BMAD-METHOD/img/foo.png (when base is /BMAD-METHOD/) + * /downloads/file.zip → /BMAD-METHOD/downloads/file.zip + * /llms.txt → /BMAD-METHOD/llms.txt + * + * Only affects absolute paths (/) - relative paths and external URLs are unchanged. + * Does NOT process .md links (those are handled by rehype-markdown-links). + */ + +import { visit } from 'unist-util-visit'; + +/** + * Create a rehype plugin that prepends the base path to absolute URLs. + * + * @param {Object} options - Plugin options + * @param {string} options.base - The base path to prepend (e.g., '/BMAD-METHOD/') + * @returns {function} A HAST tree transformer + */ +export default function rehypeBasePaths(options = {}) { + const base = options.base || '/'; + + // Normalize base: ensure it ends with / and doesn't have double slashes + const normalizedBase = base === '/' ? '/' : base.endsWith('/') ? base : base + '/'; + + return (tree) => { + visit(tree, 'element', (node) => { + // Process img tags with src attribute + if (node.tagName === 'img' && node.properties?.src) { + const src = node.properties.src; + + if (typeof src === 'string' && src.startsWith('/') && !src.startsWith('//')) { + // Don't transform if already has the base path + if (normalizedBase !== '/' && !src.startsWith(normalizedBase)) { + node.properties.src = normalizedBase + src.slice(1); + } + } + } + + // Process anchor tags with href attribute + if (node.tagName === 'a' && node.properties?.href) { + const href = node.properties.href; + + if (typeof href !== 'string') { + return; + } + + // Only transform absolute paths starting with / (but not //) + if (!href.startsWith('/') || href.startsWith('//')) { + return; + } + + // Skip if already has the base path + if (normalizedBase !== '/' && href.startsWith(normalizedBase)) { + return; + } + + // Skip .md links - those are handled by rehype-markdown-links + // Extract path portion (before ? and #) + const firstDelimiter = Math.min( + href.indexOf('?') === -1 ? Infinity : href.indexOf('?'), + href.indexOf('#') === -1 ? Infinity : href.indexOf('#'), + ); + const pathPortion = firstDelimiter === Infinity ? href : href.substring(0, firstDelimiter); + + if (pathPortion.endsWith('.md')) { + return; // Let rehype-markdown-links handle this + } + + // Prepend base path + node.properties.href = normalizedBase + href.slice(1); + } + }); + }; +} diff --git a/website/src/rehype-markdown-links.js b/website/src/rehype-markdown-links.js index 97d8dec3..352b60de 100644 --- a/website/src/rehype-markdown-links.js +++ b/website/src/rehype-markdown-links.js @@ -6,10 +6,11 @@ * ./path/index.md → ./path/ (index.md becomes directory root) * ../path/file.md#anchor → ../path/file/#anchor * ./file.md?query=param → ./file/?query=param - * /docs/absolute/path/file.md → /absolute/path/file/ + * /docs/absolute/path/file.md → {base}/absolute/path/file/ * * For absolute paths starting with /docs/, the /docs prefix is stripped * since the Astro site serves content from the docs directory as the root. + * The base path is prepended to absolute paths for subdirectory deployments. * * Affects relative links (./, ../) and absolute paths (/) - external links are unchanged */ @@ -21,9 +22,15 @@ import { visit } from 'unist-util-visit'; * * The returned transformer walks the HTML tree and rewrites anchor `href` values that are relative paths (./, ../) or absolute paths (/) pointing to `.md` files. It preserves query strings and hash anchors, rewrites `.../index.md` to the directory root path (`.../`), and rewrites other `.md` file paths by removing the `.md` extension and ensuring a trailing slash. External links (http://, https://) and non-.md links are left unchanged. * + * @param {Object} options - Plugin options + * @param {string} options.base - The base path to prepend to absolute URLs (e.g., '/BMAD-METHOD/') * @returns {function} A HAST tree transformer that mutates `a` element `href` properties as described. */ -export default function rehypeMarkdownLinks() { +export default function rehypeMarkdownLinks(options = {}) { + const base = options.base || '/'; + // Normalize base: ensure it ends with / and doesn't have double slashes + const normalizedBase = base === '/' ? '' : base.endsWith('/') ? base.slice(0, -1) : base; + return (tree) => { visit(tree, 'element', (node) => { // Only process anchor tags with href @@ -82,6 +89,9 @@ export default function rehypeMarkdownLinks() { } } + // Track if this was an absolute path (for base path prepending) + const isAbsolute = urlPath.startsWith('/'); + // Strip /docs/ prefix from absolute paths (repo-relative → site-relative) if (urlPath.startsWith('/docs/')) { urlPath = urlPath.slice(5); // Remove '/docs' prefix, keeping the leading / @@ -95,6 +105,11 @@ export default function rehypeMarkdownLinks() { urlPath = urlPath.replace(/\.md$/, '/'); } + // Prepend base path to absolute URLs for subdirectory deployments + if (isAbsolute && normalizedBase) { + urlPath = normalizedBase + urlPath; + } + // Reconstruct the href node.properties.href = urlPath + query + anchor; });