diff --git a/.claude/skills/changelog-social/SKILL.md b/.claude/skills/changelog-social/SKILL.md index e28b7abda..42e0bc3cf 100644 --- a/.claude/skills/changelog-social/SKILL.md +++ b/.claude/skills/changelog-social/SKILL.md @@ -1,5 +1,5 @@ --- -name: changelog-social +name: bmad-os-changelog-social description: Generate social media announcements for Discord, Twitter, and LinkedIn from the latest changelog entry. Use when user asks to create release announcements, social posts, or share changelog updates. Reads CHANGELOG.md in current working directory. Reference examples/ for tone and format. disable-model-invocation: true --- @@ -154,7 +154,13 @@ Read the appropriate example file before generating to match the established sty ## Output Format -Present both announcements in clearly labeled sections: +**CRITICAL: ALWAYS write to files** - Create files in `_bmad-output/social/` directory: + +1. `{repo-name}-discord-{version}.md` - Discord announcement +2. `{repo-name}-twitter-{version}.md` - Twitter post +3. `{repo-name}-linkedin-{version}.md` - LinkedIn post (if applicable) + +Also present a preview in the chat: ```markdown ## Discord Announcement @@ -166,4 +172,7 @@ Present both announcements in clearly labeled sections: [paste Twitter content here] ``` +Files created: +- `_bmad-output/social/{filename}` + Offer to make adjustments if the user wants different emphasis, tone, or content. diff --git a/.claude/skills/draft-changelog/SKILL.md b/.claude/skills/draft-changelog/SKILL.md index f25f8c48f..a246e069f 100644 --- a/.claude/skills/draft-changelog/SKILL.md +++ b/.claude/skills/draft-changelog/SKILL.md @@ -1,6 +1,6 @@ --- -name: draft-changelog -description: Analyzes changes since the last release and generates a draft changelog entry +name: bmad-os-draft-changelog +description: Analyzes changes since last release and updates CHANGELOG.md ONLY. Does NOT trigger releases. disable-model-invocation: true --- diff --git a/.claude/skills/draft-changelog/prompts/instructions.md b/.claude/skills/draft-changelog/prompts/instructions.md index a9b98bb8f..ef3feccef 100644 --- a/.claude/skills/draft-changelog/prompts/instructions.md +++ b/.claude/skills/draft-changelog/prompts/instructions.md @@ -1,5 +1,17 @@ # Draft Changelog Execution +## โš ๏ธ IMPORTANT - READ FIRST + +**This skill ONLY updates CHANGELOG.md. That is its entire purpose.** + +- **DO** update CHANGELOG.md with the new version entry +- **DO** present the draft for user review before editing +- **DO NOT** trigger any GitHub release workflows +- **DO NOT** run any other skills or workflows automatically +- **DO NOT** make any commits + +After the changelog is complete, you may suggest the user can run `/release-module` if they want to proceed with the actual release โ€” but NEVER trigger it yourself. + ## Input Project path (or run from project root) @@ -53,6 +65,18 @@ Guidelines: - Clear, concise language - For breaking changes, clearly indicate impact -## Step 4: Present Draft +## Step 4: Present Draft & Update CHANGELOG.md Show the draft with current version, last tag, commit count, and options to edit/retry. + +When user accepts: +1. Update CHANGELOG.md with the new entry (insert at top, after `# Changelog` header) +2. STOP. That's it. You're done. + +You may optionally suggest: *"When ready, you can run `/release-module` to create the actual release."* + +**DO NOT:** +- Trigger any GitHub workflows +- Run any other skills +- Make any commits +- Do anything beyond updating CHANGELOG.md diff --git a/.claude/skills/gh-triage/SKILL.md b/.claude/skills/gh-triage/SKILL.md index 6a6d7c838..e5688f3ba 100644 --- a/.claude/skills/gh-triage/SKILL.md +++ b/.claude/skills/gh-triage/SKILL.md @@ -1,5 +1,5 @@ --- -name: gh-triage +name: bmad-os-gh-triage description: Fetch all GitHub issues via gh CLI and use AI agents to deeply analyze, cluster, and prioritize issues license: MIT disable-model-invocation: true diff --git a/.claude/skills/release-module/SKILL.md b/.claude/skills/release-module/SKILL.md index 3ba156577..17a718a32 100644 --- a/.claude/skills/release-module/SKILL.md +++ b/.claude/skills/release-module/SKILL.md @@ -1,5 +1,5 @@ --- -name: release-module +name: bmad-os-release-module description: Automates the complete release process for npm modules - version bump, changelog, git tag, npm publish, GitHub release disable-model-invocation: true --- diff --git a/.claude/skills/release-module/prompts/instructions.md b/.claude/skills/release-module/prompts/instructions.md index 39e45ac13..157ce0b33 100644 --- a/.claude/skills/release-module/prompts/instructions.md +++ b/.claude/skills/release-module/prompts/instructions.md @@ -42,26 +42,6 @@ Publish the package. Create release with changelog notes using `gh release create`. -### Step 10: Create Social Announcement - -Create a social media announcement file at `_bmad-output/social/{repo-name}-release.md`. - -Format: -```markdown -# {name} v{version} Released - -## Highlights -{2-3 bullet points of key features/changes} - -## Links -- GitHub: {release-url} -- npm: {npm-url} -``` - -### Step 11: Confirm Completion - -Show npm, GitHub, and social announcement file paths. - ## Error Handling Stop immediately on any step failure. Inform user and suggest fix. diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a85c1f33..f28c7f5d0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -58,8 +58,7 @@ "tmpl", "Trae", "Unsharded", - "VNET", - "webskip" + "VNET" ], "json.schemas": [ { diff --git a/CHANGELOG.md b/CHANGELOG.md index 2420869b4..7911a7d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,70 @@ # Changelog +## [6.0.0-Beta.7] + +**Release: February 4, 2026** + +### ๐ŸŒŸ Key Highlights + +1. **Direct Workflow Invocation** โ€” Agent workflows can now be run directly via slash commands instead of only through agent orchestration +2. **Installer Workflow Support** โ€” Installer now picks up `workflow-*.md` files, enabling multiple workflow files per directory + +### ๐ŸŽ Features + +* **Slash Command Workflow Access** โ€” Research and PRD workflows now accessible via direct slash commands: `/domain-research`, `/market-research`, `/technical-research`, `/create-prd`, `/edit-prd`, `/validate-prd` (bd620e38, 731bee26) +* **Version Checking** โ€” CLI now checks npm for newer versions and displays a warning banner when updates are available (d37ee7f2) + +### โ™ป๏ธ Refactoring + +* **Workflow File Splitting** โ€” Split monolithic `workflow.md` files into specific `workflow-*.md` files for individual workflow invocation (bd620e38) +* **Installer Multi-Workflow Support** โ€” Installer manifest generator now supports `workflow-*.md` pattern, allowing multiple workflow files per directory (731bee26) +* **Internal Skill Renaming** โ€” Renamed internal project skills to use `bmad-os-` prefix for consistent naming (5276d58b) + +--- + +## [6.0.0-Beta.6] + +**Release: February 4, 2026** + +### ๐ŸŒŸ Key Highlights + +1. **Cross-File Reference Validator**: Comprehensive tool to detect broken file references, preventing 59 known bugs (~25% of historical issues) +2. **New AutocompleteMultiselect Prompt**: Searchable multi-select with improved tool/IDE selection UX +3. **Critical Installer Fixes**: Windows CRLF parsing, Gemini CLI TOML support, file extension preservation +4. **Codebase Cleanup**: Removed dead Excalidraw/flattener artifacts (-3,798 lines) + +### ๐ŸŽ Features + +* **Cross-File Reference Validator** โ€” Validates ~483 references across ~217 source files, detecting absolute path leaks and broken references (PR #1494) +* **AutocompleteMultiselect Prompt** โ€” Upgraded `@clack/prompts` to v1.0.0 with custom searchable multiselect, Tab-to-fill-placeholder behavior, and improved tool/IDE selection UX (PR #1514) +* **OT Domains** โ€” Added `process_control` and `building_automation` domains with high complexity ratings (PR #1510) +* **Documentation Reference Pages** โ€” Added `docs/reference/agents.md`, `commands.md`, and `testing.md` (PR #1525) + +### ๐Ÿ› Bug Fixes + +* **Critical Installer Fixes** โ€” Fixed CRLF line ending parsing on Windows, Gemini CLI TOML support, file extension preservation, Codex task generation, Windows path handling, and CSV parsing (PR #1492) +* **Double Tool Questioning** โ€” Removed redundant tool questioning during installation (df176d42) +* **QA Agent Rename** โ€” Renamed Quinn agent to `qa` for naming consistency (PR #1508) +* **Documentation Organization** โ€” Fixed documentation ordering and links, hide BMGD pages from main LLM docs (PR #1525) + +### โ™ป๏ธ Refactoring + +* **Excalidraw/Flattener Removal** โ€” Removed dead artifacts no longer supported beyond beta: Excalidraw workflows, flattener tool, and 12+ diagram creation workflows (-3,798 lines) (f699a368) +* **Centralized Constants** โ€” Centralized `BMAD_FOLDER_NAME` to reduce hardcoded strings (PR #1492) +* **Cross-Platform Paths** โ€” Fixed path separator inconsistencies in agent IDs (PR #1492) + +### ๐Ÿ“š Documentation + +* **BMGD Diataxis Refactor** โ€” Refactored BMGD documentation using Diataxis principles for better organization (PR #1502) +* **Generate Project Context** โ€” Restored `generate-project-context` workflow for brownfield project analysis (PR #1491) + +### ๐Ÿ”ง Maintenance + +* **Dependency Updates** โ€” Upgraded `@clack/prompts` from v0.11.0 to v1.0.0 and added `@clack/core` (PR #1514) +* **CI Integration** โ€” Added `validate:refs` to CI quality workflow with warning annotations (PR #1494) + +--- + ## [6.0.0-Beta.5] ### ๐ŸŽ Features @@ -1201,7 +1266,6 @@ Located in `src/modules/bmb/workflows/agent/data/`: - **Workflow Vendoring**: Web bundler performs automatic cross-module dependency vendoring - **BMGD Module Extraction**: Game development split into standalone 4-phase structure -- **Enhanced Dependency Resolution**: Better handling of web_bundle: false workflows - **Advanced Elicitation Fix**: Added missing CSV files to workflow bundles - **Claude Code Fix**: Resolved README slash command installation regression diff --git a/docs/_STYLE_GUIDE.md b/docs/_STYLE_GUIDE.md index e5fb51ff7..3e78387af 100644 --- a/docs/_STYLE_GUIDE.md +++ b/docs/_STYLE_GUIDE.md @@ -147,7 +147,7 @@ your-project/ | **Concept** | `what-are-agents.md` | | **Feature** | `quick-flow.md` | | **Philosophy** | `why-solutioning-matters.md` | -| **FAQ** | `brownfield-faq.md` | +| **FAQ** | `established-projects-faq.md` | ### General Template @@ -325,7 +325,7 @@ Add italic context at definition start for limited-scope terms: - `*BMad Method/Enterprise.*` - `*Phase N.*` - `*BMGD.*` -- `*Brownfield.*` +- `*Established projects.*` ### Glossary Checklist diff --git a/docs/explanation/brownfield-faq.md b/docs/explanation/brownfield-faq.md deleted file mode 100644 index 1c9b3b822..000000000 --- a/docs/explanation/brownfield-faq.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: "Brownfield Development FAQ" -description: Common questions about brownfield development in the BMad Method ---- -Quick answers to common questions about brownfield (existing codebase) development in the BMad Method (BMM). - -## Questions - -- [Questions](#questions) - - [What is brownfield vs greenfield?](#what-is-brownfield-vs-greenfield) - - [Do I have to run document-project for brownfield?](#do-i-have-to-run-document-project-for-brownfield) - - [What if I forget to run document-project?](#what-if-i-forget-to-run-document-project) - - [Can I use Quick Spec Flow for brownfield projects?](#can-i-use-quick-spec-flow-for-brownfield-projects) - - [What if my existing code doesn't follow best practices?](#what-if-my-existing-code-doesnt-follow-best-practices) - -### What is brownfield vs greenfield? - -- **Greenfield** โ€” New project, starting from scratch, clean slate -- **Brownfield** โ€” Existing project, working with established codebase and patterns - -### Do I have to run document-project for brownfield? - -Highly recommended, especially if: - -- No existing documentation -- Documentation is outdated -- AI agents need context about existing code - -You can skip it if you have comprehensive, up-to-date documentation including `docs/index.md` or will use other tools or techniques to aid in discovery for the agent to build on an existing system. - -### What if I forget to run document-project? - -Don't worry about it - you can do it at any time. You can even do it during or after a project to help keep docs up to date. - -### Can I use Quick Spec Flow for brownfield projects? - -Yes! Quick Spec Flow works great for brownfield. It will: - -- Auto-detect your existing stack -- Analyze brownfield code patterns -- Detect conventions and ask for confirmation -- Generate context-rich tech-spec that respects existing code - -Perfect for bug fixes and small features in existing codebases. - -### What if my existing code doesn't follow best practices? - -Quick Spec Flow detects your conventions and asks: "Should I follow these existing conventions?" You decide: - -- **Yes** โ†’ Maintain consistency with current codebase -- **No** โ†’ Establish new standards (document why in tech-spec) - -BMM respects your choice โ€” it won't force modernization, but it will offer it. - -**Have a question not answered here?** Please [open an issue](https://github.com/bmad-code-org/BMAD-METHOD/issues) or ask in [Discord](https://discord.gg/gk8jAdXWmj) so we can add it! diff --git a/docs/explanation/established-projects-faq.md b/docs/explanation/established-projects-faq.md new file mode 100644 index 000000000..e940b4dbb --- /dev/null +++ b/docs/explanation/established-projects-faq.md @@ -0,0 +1,48 @@ +--- +title: "Established Projects FAQ" +description: Common questions about using BMad Method on established projects +--- +Quick answers to common questions about working on established projects with the BMad Method (BMM). + +## Questions + +- [Do I have to run document-project first?](#do-i-have-to-run-document-project-first) +- [What if I forget to run document-project?](#what-if-i-forget-to-run-document-project) +- [Can I use Quick Flow for established projects?](#can-i-use-quick-flow-for-established-projects) +- [What if my existing code doesn't follow best practices?](#what-if-my-existing-code-doesnt-follow-best-practices) + +### Do I have to run document-project first? + +Highly recommended, especially if: + +- No existing documentation +- Documentation is outdated +- AI agents need context about existing code + +You can skip it if you have comprehensive, up-to-date documentation including `docs/index.md` or will use other tools or techniques to aid in discovery for the agent to build on an existing system. + +### What if I forget to run document-project? + +Don't worry about it - you can do it at any time. You can even do it during or after a project to help keep docs up to date. + +### Can I use Quick Flow for established projects? + +Yes! Quick Flow works great for established projects. It will: + +- Auto-detect your existing stack +- Analyze existing code patterns +- Detect conventions and ask for confirmation +- Generate context-rich tech-spec that respects existing code + +Perfect for bug fixes and small features in existing codebases. + +### What if my existing code doesn't follow best practices? + +Quick Flow detects your conventions and asks: "Should I follow these existing conventions?" You decide: + +- **Yes** โ†’ Maintain consistency with current codebase +- **No** โ†’ Establish new standards (document why in tech-spec) + +BMM respects your choice โ€” it won't force modernization, but it will offer it. + +**Have a question not answered here?** Please [open an issue](https://github.com/bmad-code-org/BMAD-METHOD/issues) or ask in [Discord](https://discord.gg/gk8jAdXWmj) so we can add it! diff --git a/docs/how-to/brownfield/index.md b/docs/how-to/established-projects.md similarity index 84% rename from docs/how-to/brownfield/index.md rename to docs/how-to/established-projects.md index 75bab690b..b756691cc 100644 --- a/docs/how-to/brownfield/index.md +++ b/docs/how-to/established-projects.md @@ -1,15 +1,11 @@ --- -title: "Brownfield Development" +title: "Established Projects" description: How to use BMad Method on existing codebases --- Use BMad Method effectively when working on existing projects and legacy codebases. -## What is Brownfield Development? - -**Brownfield** refers to working on existing projects with established codebases and patterns, as opposed to **greenfield** which means starting from scratch with a clean slate. - -This guide covers the essential workflow for onboarding to brownfield projects with BMad Method. +This guide covers the essential workflow for onboarding to existing projects with BMad Method. :::note[Prerequisites] - BMad Method installed (`npx bmad-method install`) @@ -80,5 +76,5 @@ Pay close attention here to prevent reinventing the wheel or making decisions th ## More Information -- **[Quick Fix in Brownfield](/docs/how-to/brownfield/quick-fix-in-brownfield.md)** - Bug fixes and ad-hoc changes -- **[Brownfield FAQ](/docs/explanation/brownfield-faq.md)** - Common questions about brownfield development +- **[Quick Fixes](/docs/how-to/quick-fixes.md)** - Bug fixes and ad-hoc changes +- **[Established Projects FAQ](/docs/explanation/established-projects-faq.md)** - Common questions about working on established projects diff --git a/docs/how-to/brownfield/quick-fix-in-brownfield.md b/docs/how-to/quick-fixes.md similarity index 93% rename from docs/how-to/brownfield/quick-fix-in-brownfield.md rename to docs/how-to/quick-fixes.md index 9dc430f11..5b6cfe35c 100644 --- a/docs/how-to/brownfield/quick-fix-in-brownfield.md +++ b/docs/how-to/quick-fixes.md @@ -1,6 +1,6 @@ --- -title: "How to Make Quick Fixes in Brownfield Projects" -description: How to make quick fixes and ad-hoc changes in brownfield projects +title: "Quick Fixes" +description: How to make quick fixes and ad-hoc changes --- Use the **DEV agent** directly for bug fixes, refactorings, or small targeted changes that don't require the full BMad method or Quick Flow. diff --git a/docs/reference/workflow-map.md b/docs/reference/workflow-map.md index 1425c4698..0df3d3ec8 100644 --- a/docs/reference/workflow-map.md +++ b/docs/reference/workflow-map.md @@ -73,7 +73,7 @@ Skip phases 1-3 for small, well-understood work. 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. +For established 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: diff --git a/package-lock.json b/package-lock.json index d3888b281..ba768eff9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bmad-method", - "version": "6.0.0-Beta.5", + "version": "6.0.0-Beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bmad-method", - "version": "6.0.0-Beta.5", + "version": "6.0.0-Beta.6", "license": "MIT", "dependencies": { "@clack/core": "^1.0.0", diff --git a/package.json b/package.json index 8798a6208..96a1814f1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "bmad-method", - "version": "6.0.0-Beta.5", + "version": "6.0.0-Beta.6", "description": "Breakthrough Method of Agile AI-driven Development", "keywords": [ "agile", diff --git a/src/bmm/agents/analyst.agent.yaml b/src/bmm/agents/analyst.agent.yaml index f63420a5b..c340f69c1 100644 --- a/src/bmm/agents/analyst.agent.yaml +++ b/src/bmm/agents/analyst.agent.yaml @@ -1,5 +1,3 @@ -# Business Analyst Agent Definition - agent: metadata: id: "_bmad/bmm/agents/analyst.md" @@ -23,9 +21,17 @@ agent: data: "{project-root}/_bmad/bmm/data/project-context-template.md" description: "[BP] Brainstorm Project: Expert Guided Facilitation through a single or multiple techniques with a final report" - - trigger: RS or fuzzy match on research - exec: "{project-root}/_bmad/bmm/workflows/1-analysis/research/workflow.md" - description: "[RS] Research: Choose from or specify market, domain, competitive analysis, or technical research" + - trigger: MR or fuzzy match on market-research + exec: "{project-root}/_bmad/bmm/workflows/1-analysis/research/workflow-market-research.md" + description: "[MR] Market Research: Market analysis, competitive landscape, customer needs and trends" + + - trigger: DR or fuzzy match on domain-research + exec: "{project-root}/_bmad/bmm/workflows/1-analysis/research/workflow-domain-research.md" + description: "[DR] Domain Research: Industry domain deep dive, subject matter expertise and terminology" + + - trigger: TR or fuzzy match on technical-research + exec: "{project-root}/_bmad/bmm/workflows/1-analysis/research/workflow-technical-research.md" + description: "[TR] Technical Research: Technical feasibility, architecture options and implementation approaches" - trigger: CB or fuzzy match on product-brief exec: "{project-root}/_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md" diff --git a/src/bmm/agents/dev.agent.yaml b/src/bmm/agents/dev.agent.yaml index 404a108c5..d88166eda 100644 --- a/src/bmm/agents/dev.agent.yaml +++ b/src/bmm/agents/dev.agent.yaml @@ -1,7 +1,6 @@ # Dev Implementation Agent Definition (v6) agent: - webskip: true metadata: id: "_bmad/bmm/agents/dev.md" name: Amelia diff --git a/src/bmm/agents/pm.agent.yaml b/src/bmm/agents/pm.agent.yaml index 1fa22545e..9ce0bf32f 100644 --- a/src/bmm/agents/pm.agent.yaml +++ b/src/bmm/agents/pm.agent.yaml @@ -1,6 +1,3 @@ -# Product Manager Agent Definition -# This file defines the PM agent for the BMAD BMM module - agent: metadata: id: "_bmad/bmm/agents/pm.md" @@ -22,15 +19,15 @@ agent: menu: - trigger: CP or fuzzy match on create-prd - exec: "{project-root}/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow.md" + exec: "{project-root}/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md" description: "[CP] Create PRD: Expert led facilitation to produce your Product Requirements Document" - trigger: VP or fuzzy match on validate-prd - exec: "{project-root}/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow.md" + exec: "{project-root}/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md" description: "[VP] Validate PRD: Validate a Product Requirements Document is comprehensive, lean, well organized and cohesive" - trigger: EP or fuzzy match on edit-prd - exec: "{project-root}/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow.md" + exec: "{project-root}/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md" description: "[EP] Edit PRD: Update an existing Product Requirements Document" - trigger: CE or fuzzy match on epics-stories diff --git a/src/bmm/module-help.csv b/src/bmm/module-help.csv index 45eeb6ab3..635bb8a81 100644 --- a/src/bmm/module-help.csv +++ b/src/bmm/module-help.csv @@ -4,29 +4,22 @@ bmm,anytime,Generate Project Context,GPC,,_bmad/bmm/workflows/generate-project-c bmm,anytime,Quick Spec,QS,,_bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md,bmad-bmm-quick-spec,false,quick-flow-solo-dev,Create Mode,"Do not suggest for potentially very complex things unless requested or if the user complains that they do not want to follow the extensive planning of the bmad method. Quick one-off tasks small changes simple apps brownfield additions to well established patterns utilities without extensive planning",planning_artifacts,"tech spec", bmm,anytime,Quick Dev,QD,,_bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md,bmad-bmm-quick-dev,false,quick-flow-solo-dev,Create Mode,"Quick one-off tasks small changes simple apps utilities without extensive planning - Do not suggest for potentially very complex things unless requested or if the user complains that they do not want to follow the extensive planning of the bmad method, unless the user is already working through the implementation phase and just requests a 1 off things not already in the plan",,, bmm,anytime,Correct Course,CC,,_bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml,bmad-bmm-correct-course,false,sm,Create Mode,"Anytime: Navigate significant changes. May recommend start over update PRD redo architecture sprint planning or correct epics and stories",planning_artifacts,"change proposal", -bmm,anytime,Create Dataflow,CDF,,_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml,bmad-bmm-create-excalidraw-dataflow,false,ux-designer,Create Mode,"Create data flow diagrams (DFD) in Excalidraw format - can be called standalone or during any workflow to add visual documentation",planning_artifacts,"dataflow diagram", -bmm,anytime,Create Diagram,CED,,_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml,bmad-bmm-create-excalidraw-diagram,false,ux-designer,Create Mode,"Create system architecture diagrams ERDs UML diagrams or general technical diagrams in Excalidraw format - use anytime or call from architecture workflow to add visual documentation",planning_artifacts,"diagram", -bmm,anytime,Create Flowchart,CFC,,_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml,bmad-bmm-create-excalidraw-flowchart,false,ux-designer,Create Mode,"Create a flowchart visualization in Excalidraw format for processes pipelines or logic flows - use anytime or during architecture to add process documentation",planning_artifacts,"flowchart", -bmm,anytime,Create Wireframe,CEW,,_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml,bmad-bmm-create-excalidraw-wireframe,false,ux-designer,Create Mode,"Create website or app wireframes in Excalidraw format - use anytime standalone or call from UX workflow to add UI mockups",planning_artifacts,"wireframe", -bmm,anytime,Write Document,WD,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,bmad-bmm-write-document,false,tech-writer,,"Describe in detail what you want, and the agent will follow the documentation best practices defined in agent memory. Multi-turn conversation with subprocess for research/review.",project-knowledge,"document", -bmm,anytime,Update Standards,US,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,bmad-bmm-update-standards,false,tech-writer,,"Update agent memory documentation-standards.md with your specific preferences if you discover missing document conventions.",_bmad/_memory/tech-writer-sidecar,"standards", -bmm,anytime,Mermaid Generate,MG,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,bmad-bmm-mermaid-generate,false,tech-writer,,"Create a Mermaid diagram based on user description. Will suggest diagram types if not specified.",planning_artifacts,"mermaid diagram", -bmm,anytime,Validate Document,VD,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,bmad-bmm-validate-document,false,tech-writer,,"Review the specified document against documentation standards and best practices. Returns specific actionable improvement suggestions organized by priority.",planning_artifacts,"validation report", -bmm,anytime,Explain Concept,EC,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,bmad-bmm-explain-concept,false,tech-writer,,"Create clear technical explanations with examples and diagrams for complex concepts. Breaks down into digestible sections using task-oriented approach.",project_knowledge,"explanation", +bmm,anytime,Write Document,WD,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Describe in detail what you want, and the agent will follow the documentation best practices defined in agent memory. Multi-turn conversation with subprocess for research/review.",project-knowledge,"document", +bmm,anytime,Update Standards,US,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Update agent memory documentation-standards.md with your specific preferences if you discover missing document conventions.",_bmad/_memory/tech-writer-sidecar,"standards", +bmm,anytime,Mermaid Generate,MG,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Create a Mermaid diagram based on user description. Will suggest diagram types if not specified.",planning_artifacts,"mermaid diagram", +bmm,anytime,Validate Document,VD,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Review the specified document against documentation standards and best practices. Returns specific actionable improvement suggestions organized by priority.",planning_artifacts,"validation report", +bmm,anytime,Explain Concept,EC,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Create clear technical explanations with examples and diagrams for complex concepts. Breaks down into digestible sections using task-oriented approach.",project_knowledge,"explanation", bmm,1-analysis,Brainstorm Project,BP,10,_bmad/core/workflows/brainstorming/workflow.md,bmad-brainstorming,false,analyst,data=_bmad/bmm/data/project-context-template.md,"Expert Guided Facilitation through a single or multiple techniques",planning_artifacts,"brainstorming session", -bmm,1-analysis,Market Research,MR,20,_bmad/bmm/workflows/1-analysis/research/workflow.md,bmad-bmm-research,false,analyst,Create Mode research_type=market,"Market analysis competitive landscape customer needs and trends","planning_artifacts|project-knowledge","research documents", -bmm,1-analysis,Domain Research,DR,21,_bmad/bmm/workflows/1-analysis/research/workflow.md,bmad-bmm-research,false,analyst,Create Mode research_type=domain,"Industry domain deep dive subject matter expertise and terminology","planning_artifacts|project_knowledge","research documents", -bmm,1-analysis,Technical Research,TR,22,_bmad/bmm/workflows/1-analysis/research/workflow.md,bmad-bmm-research,false,analyst,Create Mode research_type=technical,"Technical feasibility architecture options and implementation approaches","planning_artifacts|project_knowledge","research documents", -bmm,1-analysis,Create Brief,CB,30,_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md,bmad-bmm-create-brief,false,analyst,Create Mode,"A guided experience to nail down your product idea",planning_artifacts,"product brief", -bmm,1-analysis,Validate Brief,VB,40,_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md,bmad-bmm-validate-brief,false,analyst,Validate Mode,"Validates product brief completeness",planning_artifacts,"brief validation report", -bmm,2-planning,Create PRD,CP,10,_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow.md,bmad-bmm-create-prd,true,pm,Create Mode,"Expert led facilitation to produce your Product Requirements Document",planning_artifacts,prd, -bmm,2-planning,Validate PRD,VP,20,_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow.md,bmad-bmm-validate-prd,false,pm,Validate Mode,"Validate PRD is comprehensive lean well organized and cohesive",planning_artifacts,"prd validation report", +bmm,1-analysis,Market Research,MR,20,_bmad/bmm/workflows/1-analysis/research/workflow-market-research.md,bmad-bmm-market-research,false,analyst,Create Mode,"Market analysis competitive landscape customer needs and trends","planning_artifacts|project-knowledge","research documents", +bmm,1-analysis,Domain Research,DR,21,_bmad/bmm/workflows/1-analysis/research/workflow-domain-research.md,bmad-bmm-domain-research,false,analyst,Create Mode,"Industry domain deep dive subject matter expertise and terminology","planning_artifacts|project_knowledge","research documents", +bmm,1-analysis,Technical Research,TR,22,_bmad/bmm/workflows/1-analysis/research/workflow-technical-research.md,bmad-bmm-technical-research,false,analyst,Create Mode,"Technical feasibility architecture options and implementation approaches","planning_artifacts|project_knowledge","research documents", +bmm,1-analysis,Create Brief,CB,30,_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md,bmad-bmm-create-product-brief,false,analyst,Create Mode,"A guided experience to nail down your product idea",planning_artifacts,"product brief", +bmm,2-planning,Create PRD,CP,10,_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md,bmad-bmm-create-prd,true,pm,Create Mode,"Expert led facilitation to produce your Product Requirements Document",planning_artifacts,prd, +bmm,2-planning,Validate PRD,VP,20,_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md,bmad-bmm-validate-prd,false,pm,Validate Mode,"Validate PRD is comprehensive lean well organized and cohesive",planning_artifacts,"prd validation report", +bmm,2-planning,Edit PRD,EP,25,_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md,bmad-bmm-edit-prd,false,pm,Edit Mode,"Improve and enhance an existing PRD",planning_artifacts,"updated prd", bmm,2-planning,Create UX,CU,30,_bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md,bmad-bmm-create-ux-design,false,ux-designer,Create Mode,"Guidance through realizing the plan for your UX, strongly recommended if a UI is a primary piece of the proposed project",planning_artifacts,"ux design", -bmm,2-planning,Validate UX,VU,40,_bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md,bmad-bmm-create-ux-design,false,ux-designer,Validate Mode,"Validates UX design deliverables",planning_artifacts,"ux validation report", bmm,3-solutioning,Create Architecture,CA,10,_bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md,bmad-bmm-create-architecture,true,architect,Create Mode,"Guided Workflow to document technical decisions",planning_artifacts,architecture, -bmm,3-solutioning,Validate Architecture,VA,20,_bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md,bmad-bmm-create-architecture,false,architect,Validate Mode,"Validates architecture completeness",planning_artifacts,"architecture validation report", bmm,3-solutioning,Create Epics and Stories,CE,30,_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md,bmad-bmm-create-epics-and-stories,true,pm,Create Mode,"Create the Epics and Stories Listing",planning_artifacts,"epics and stories", -bmm,3-solutioning,Validate Epics and Stories,VE,40,_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md,bmad-bmm-create-epics-and-stories,false,pm,Validate Mode,"Validates epics and stories completeness",planning_artifacts,"epics validation report", bmm,3-solutioning,Check Implementation Readiness,IR,70,_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md,bmad-bmm-check-implementation-readiness,true,architect,Validate Mode,"Ensure PRD UX Architecture and Epics Stories are aligned",planning_artifacts,"readiness report", bmm,4-implementation,Sprint Planning,SP,10,_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml,bmad-bmm-sprint-planning,true,sm,Create Mode,"Generate sprint plan for development tasks - this kicks off the implementation phase by producing a plan the implementation agents will follow in sequence for every story in the plan.",implementation_artifacts,"sprint status", bmm,4-implementation,Sprint Status,SS,20,_bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml,bmad-bmm-sprint-status,false,sm,Create Mode,"Anytime: Summarize sprint status and route to next workflow",,, diff --git a/src/bmm/workflows/1-analysis/create-product-brief/workflow.md b/src/bmm/workflows/1-analysis/create-product-brief/workflow.md index c17b18215..9d5e83f19 100644 --- a/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +++ b/src/bmm/workflows/1-analysis/create-product-brief/workflow.md @@ -1,7 +1,6 @@ --- name: create-product-brief description: Create comprehensive product briefs through collaborative step-by-step discovery as creative Business Analyst working with the user as peers. -web_bundle: true --- # Product Brief Workflow diff --git a/src/bmm/workflows/1-analysis/research/workflow-domain-research.md b/src/bmm/workflows/1-analysis/research/workflow-domain-research.md new file mode 100644 index 000000000..91fcbaa9a --- /dev/null +++ b/src/bmm/workflows/1-analysis/research/workflow-domain-research.md @@ -0,0 +1,54 @@ +--- +name: domain-research +description: Conduct domain research covering industry analysis, regulations, technology trends, and ecosystem dynamics using current web data and verified sources. +--- + +# Domain Research Workflow + +**Goal:** Conduct comprehensive domain/industry research using current web data and verified sources to produce complete research documents with compelling narratives and proper citations. + +**Your Role:** You are a domain research facilitator working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction. + +## PREREQUISITE + +**โ›” Web search required.** If unavailable, abort and tell the user. + +## CONFIGURATION + +Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +- `project_name`, `output_folder`, `planning_artifacts`, `user_name` +- `communication_language`, `document_output_language`, `user_skill_level` +- `date` as a system-generated value + +## QUICK TOPIC DISCOVERY + +"Welcome {{user_name}}! Let's get started with your **domain/industry research**. + +**What domain, industry, or sector do you want to research?** + +For example: +- 'The healthcare technology industry' +- 'Sustainable packaging regulations in Europe' +- 'Construction and building materials sector' +- 'Or any other domain you have in mind...'" + +### Topic Clarification + +Based on the user's topic, briefly clarify: +1. **Core Domain**: "What specific aspect of [domain] are you most interested in?" +2. **Research Goals**: "What do you hope to achieve with this research?" +3. **Scope**: "Should we focus broadly or dive deep into specific aspects?" + +## ROUTE TO DOMAIN RESEARCH STEPS + +After gathering the topic and goals: + +1. Set `research_type = "domain"` +2. Set `research_topic = [discovered topic from discussion]` +3. Set `research_goals = [discovered goals from discussion]` +4. Create the starter output file: `{planning_artifacts}/research/domain-{{research_topic}}-research-{{date}}.md` with exact copy of the `./research.template.md` contents +5. Load: `./domain-steps/step-01-init.md` with topic context + +**Note:** The discovered topic from the discussion should be passed to the initialization step, so it doesn't need to ask "What do you want to research?" again - it can focus on refining the scope for domain research. + +**โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`** diff --git a/src/bmm/workflows/1-analysis/research/workflow-market-research.md b/src/bmm/workflows/1-analysis/research/workflow-market-research.md new file mode 100644 index 000000000..5669e6f24 --- /dev/null +++ b/src/bmm/workflows/1-analysis/research/workflow-market-research.md @@ -0,0 +1,54 @@ +--- +name: market-research +description: Conduct market research covering market size, growth, competition, and customer insights using current web data and verified sources. +--- + +# Market Research Workflow + +**Goal:** Conduct comprehensive market research using current web data and verified sources to produce complete research documents with compelling narratives and proper citations. + +**Your Role:** You are a market research facilitator working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction. + +## PREREQUISITE + +**โ›” Web search required.** If unavailable, abort and tell the user. + +## CONFIGURATION + +Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +- `project_name`, `output_folder`, `planning_artifacts`, `user_name` +- `communication_language`, `document_output_language`, `user_skill_level` +- `date` as a system-generated value + +## QUICK TOPIC DISCOVERY + +"Welcome {{user_name}}! Let's get started with your **market research**. + +**What topic, problem, or area do you want to research?** + +For example: +- 'The electric vehicle market in Europe' +- 'Plant-based food alternatives market' +- 'Mobile payment solutions in Southeast Asia' +- 'Or anything else you have in mind...'" + +### Topic Clarification + +Based on the user's topic, briefly clarify: +1. **Core Topic**: "What exactly about [topic] are you most interested in?" +2. **Research Goals**: "What do you hope to achieve with this research?" +3. **Scope**: "Should we focus broadly or dive deep into specific aspects?" + +## ROUTE TO MARKET RESEARCH STEPS + +After gathering the topic and goals: + +1. Set `research_type = "market"` +2. Set `research_topic = [discovered topic from discussion]` +3. Set `research_goals = [discovered goals from discussion]` +4. Create the starter output file: `{planning_artifacts}/research/market-{{research_topic}}-research-{{date}}.md` with exact copy of the `./research.template.md` contents +5. Load: `./market-steps/step-01-init.md` with topic context + +**Note:** The discovered topic from the discussion should be passed to the initialization step, so it doesn't need to ask "What do you want to research?" again - it can focus on refining the scope for market research. + +**โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`** diff --git a/src/bmm/workflows/1-analysis/research/workflow-technical-research.md b/src/bmm/workflows/1-analysis/research/workflow-technical-research.md new file mode 100644 index 000000000..2ac5420ce --- /dev/null +++ b/src/bmm/workflows/1-analysis/research/workflow-technical-research.md @@ -0,0 +1,54 @@ +--- +name: technical-research +description: Conduct technical research covering technology evaluation, architecture decisions, and implementation approaches using current web data and verified sources. +--- + +# Technical Research Workflow + +**Goal:** Conduct comprehensive technical research using current web data and verified sources to produce complete research documents with compelling narratives and proper citations. + +**Your Role:** You are a technical research facilitator working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction. + +## PREREQUISITE + +**โ›” Web search required.** If unavailable, abort and tell the user. + +## CONFIGURATION + +Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +- `project_name`, `output_folder`, `planning_artifacts`, `user_name` +- `communication_language`, `document_output_language`, `user_skill_level` +- `date` as a system-generated value + +## QUICK TOPIC DISCOVERY + +"Welcome {{user_name}}! Let's get started with your **technical research**. + +**What technology, tool, or technical area do you want to research?** + +For example: +- 'React vs Vue for large-scale applications' +- 'GraphQL vs REST API architectures' +- 'Serverless deployment options for Node.js' +- 'Or any other technical topic you have in mind...'" + +### Topic Clarification + +Based on the user's topic, briefly clarify: +1. **Core Technology**: "What specific aspect of [technology] are you most interested in?" +2. **Research Goals**: "What do you hope to achieve with this research?" +3. **Scope**: "Should we focus broadly or dive deep into specific aspects?" + +## ROUTE TO TECHNICAL RESEARCH STEPS + +After gathering the topic and goals: + +1. Set `research_type = "technical"` +2. Set `research_topic = [discovered topic from discussion]` +3. Set `research_goals = [discovered goals from discussion]` +4. Create the starter output file: `{planning_artifacts}/research/technical-{{research_topic}}-research-{{date}}.md` with exact copy of the `./research.template.md` contents +5. Load: `./technical-steps/step-01-init.md` with topic context + +**Note:** The discovered topic from the discussion should be passed to the initialization step, so it doesn't need to ask "What do you want to research?" again - it can focus on refining the scope for technical research. + +**โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`** diff --git a/src/bmm/workflows/1-analysis/research/workflow.md b/src/bmm/workflows/1-analysis/research/workflow.md deleted file mode 100644 index 64f62bef1..000000000 --- a/src/bmm/workflows/1-analysis/research/workflow.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -name: research -description: Conduct comprehensive research across multiple domains using current web data and verified sources - Market, Technical, Domain and other research types. -web_bundle: true ---- - -# Research Workflow - -**Goal:** Conduct comprehensive, exhaustive research across multiple domains using current web data and verified sources to produce complete research documents with compelling narratives and proper citations. - -**Document Standards:** - -- **Comprehensive Coverage**: Exhaustive research with no critical gaps -- **Source Verification**: Every factual claim backed by web sources with URL citations -- **Document Length**: As long as needed to fully cover the research topic -- **Professional Structure**: Compelling narrative introduction, detailed TOC, and comprehensive summary -- **Authoritative Sources**: Multiple independent sources for all critical claims - -**Your Role:** You are a research facilitator and web data analyst working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction. - -**Final Deliverable**: A complete research document that serves as an authoritative reference on the research topic with: - -- Compelling narrative introduction -- Comprehensive table of contents -- Detailed research sections with proper citations -- Executive summary and conclusions - -## WORKFLOW ARCHITECTURE - -This uses **micro-file architecture** with **routing-based discovery**: - -- Each research type has its own step folder -- Step 01 discovers research type and routes to appropriate sub-workflow -- Sequential progression within each research type -- Document state tracked in output frontmatter - -## INITIALIZATION - -### Configuration Loading - -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: - -- `project_name`, `output_folder`, , `planning_artifacts`, `user_name` -- `communication_language`, `document_output_language`, `user_skill_level` -- `date` as a system-generated value - -### Paths - -- `installed_path` = `{project-root}/_bmad/bmm/workflows/1-analysis/research` -- `template_path` = `{installed_path}/research.template.md` -- `default_output_file` = `{planning_artifacts}/research/{{research_type}}-{{topic}}-research-{{date}}.md` (dynamic based on research type) - -## PREREQUISITE - -**โ›” Web search required.** If unavailable, abort and tell the user. - -## RESEARCH BEHAVIOR - -### Web Research Standards - -- **Current Data Only**: Search the web to verify and supplement your knowledge with current facts -- **Source Verification**: Require citations for all factual claims -- **Anti-Hallucination Protocol**: Never present information without verified sources -- **Multiple Sources**: Require at least 2 independent sources for critical claims -- **Conflict Resolution**: Present conflicting views and note discrepancies -- **Confidence Levels**: Flag uncertain data with [High/Medium/Low Confidence] - -### Source Quality Standards - -- **Distinguish Clearly**: Facts (from sources) vs Analysis (interpretation) vs Speculation -- **URL Citation**: Always include source URLs when presenting web search data -- **Critical Claims**: Market size, growth rates, competitive data need verification -- **Fact Checking**: Apply fact-checking to critical data points - -## Implementation Instructions - -Execute research type discovery and routing: - -### Research Type Discovery - -**Your Role:** You are a research facilitator and web data analyst working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction. - -**Research Standards:** - -- **Anti-Hallucination Protocol**: Never present information without verified sources -- **Current Data Only**: Search the web to verify and supplement your knowledge with current facts -- **Source Citation**: Always include URLs for factual claims from web searches -- **Multiple Sources**: Require 2+ independent sources for critical claims -- **Conflict Resolution**: Present conflicting views and note discrepancies -- **Confidence Levels**: Flag uncertain data with [High/Medium/Low Confidence] - -### Collaborative Research Discovery - -"Welcome {{user_name}}! I'm excited to work with you as your research partner. I bring web research capabilities with rigorous source verification, while you bring the domain expertise and research direction. - -**Let me help you clarify what you'd like to research.** - -**First, tell me: What specific topic, problem, or area do you want to research?** - -For example: - -- 'The electric vehicle market in Europe' -- 'Cloud migration strategies for healthcare' -- 'AI implementation in financial services' -- 'Sustainable packaging regulations' -- 'Or anything else you have in mind...' - -### Topic Exploration and Clarification - -Based on the user's initial topic, explore and refine the research scope: - -#### Topic Clarification Questions: - -1. **Core Topic**: "What exactly about [topic] are you most interested in?" -2. **Research Goals**: "What do you hope to achieve with this research?" -3. **Scope**: "Should we focus broadly or dive deep into specific aspects?" -4. **Timeline**: "Are you looking at current state, historical context, or future trends?" -5. **Application**: "How will you use this research? (product development, strategy, academic, etc.)" - -#### Context Building: - -- **Initial Input**: User provides topic or research interest -- **Collaborative Refinement**: Work together to clarify scope and objectives -- **Goal Alignment**: Ensure research direction matches user needs -- **Research Boundaries**: Establish clear focus areas and deliverables - -### Research Type Identification - -After understanding the research topic and goals, identify the most appropriate research approach: - -**Research Type Options:** - -1. **Market Research** - Market size, growth, competition, customer insights - _Best for: Understanding market dynamics, customer behavior, competitive landscape_ - -2. **Domain Research** - Industry analysis, regulations, technology trends in specific domain - _Best for: Understanding industry context, regulatory environment, ecosystem_ - -3. **Technical Research** - Technology evaluation, architecture decisions, implementation approaches - _Best for: Technical feasibility, technology selection, implementation strategies_ - -**Recommendation**: Based on [topic] and [goals], I recommend [suggested research type] because [specific rationale]. - -**What type of research would work best for your needs?** - -### Research Type Routing - -Based on user selection, route to appropriate sub-workflow with the discovered topic using the following IF block sets of instructions. YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}` - -#### If Market Research: - -- Set `research_type = "market"` -- Set `research_topic = [discovered topic from discussion]` -- Create the starter output file: `{planning_artifacts}/research/market-{{research_topic}}-research-{{date}}.md` with exact copy of the ./research.template.md contents -- Load: `./market-steps/step-01-init.md` with topic context - -#### If Domain Research: - -- Set `research_type = "domain"` -- Set `research_topic = [discovered topic from discussion]` -- Create the starter output file: `{planning_artifacts}/research/domain-{{research_topic}}-research-{{date}}.md` with exact copy of the ./research.template.md contents -- Load: `./domain-steps/step-01-init.md` with topic context - -#### If Technical Research: - -- Set `research_type = "technical"` -- Set `research_topic = [discovered topic from discussion]` -- Create the starter output file: `{planning_artifacts}/research/technical-{{research_topic}}-research-{{date}}.md` with exact copy of the ./research.template.md contents -- Load: `./technical-steps/step-01-init.md` with topic context - -**Important**: The discovered topic from the collaborative discussion should be passed to the research initialization steps, so they don't need to ask "What do you want to research?" again - they can focus on refining the scope for their specific research type. - -**Note:** All research workflows require web search for current data and source verification. diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md b/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md deleted file mode 100644 index faa41ff64..000000000 --- a/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +++ /dev/null @@ -1,433 +0,0 @@ ---- -validationTarget: 'PRD Workflow Structure' -validationDate: '2026-01-08' -inputDocuments: [] -validationStepsCompleted: ['discovery', 'frontmatter-validation', 'content-validation', 'documentation-validation', 'integration-validation', 'corrections-applied'] -validationStatus: COMPLETE - PRODUCTION READY ---- - -# PRD Workflow Validation Report - -**Workflow Being Validated:** _bmad/bmm/workflows/2-plan-workflows/create-prd -**Validation Date:** 2026-01-08 -**Validator:** BMAD Workflow Validation System - ---- - -## Executive Summary - -This validation report assesses the PRD workflow structure against BMAD workflow standards. The PRD workflow is a tri-modal workflow system with Create, Validate, and Edit phases. - ---- - -## 1. File Structure & Size Analysis - -### Folder Structure - -``` -prd/ -โ”œโ”€โ”€ workflow.md (main workflow file) -โ”œโ”€โ”€ steps-c/ (Create steps - 12 files) -โ”œโ”€โ”€ steps-v/ (Validation steps - 13 files) -โ”œโ”€โ”€ steps-e/ (Edit steps - 5 files) -โ”œโ”€โ”€ data/ -โ”‚ โ””โ”€โ”€ prd-purpose.md -โ””โ”€โ”€ templates/ - โ””โ”€โ”€ prd-template.md -``` - -**โœ… Structure Status**: PASS - All required folders present - -### File Size Analysis - -#### Steps-C (Create Steps) - 12 files -| File | Lines | Status | -| ------------------------ | ----- | ------------------- | -| step-01-init.md | 191 | โš ๏ธ Approaching limit | -| step-01b-continue.md | 153 | โœ… Good | -| step-02-discovery.md | 197 | โš ๏ธ Approaching limit | -| step-03-success.md | 226 | โš ๏ธ Approaching limit | -| step-04-journeys.md | 213 | โš ๏ธ Approaching limit | -| step-05-domain.md | 193 | โš ๏ธ Approaching limit | -| step-06-innovation.md | 226 | โš ๏ธ Approaching limit | -| step-07-project-type.md | 225 | โš ๏ธ Approaching limit | -| step-08-scoping.md | 228 | โš ๏ธ Approaching limit | -| step-09-functional.md | 231 | โš ๏ธ Approaching limit | -| step-10-nonfunctional.md | 242 | โš ๏ธ Approaching limit | -| step-11-polish.md | 217 | โš ๏ธ Approaching limit | -| step-12-complete.md | 185 | โœ… Good | - -#### Steps-V (Validation Steps) - 13 files -| File | Lines | Status | -| ---------------------------------------------- | ----- | ------------------- | -| step-v-01-discovery.md | 217 | โš ๏ธ Approaching limit | -| step-v-02-format-detection.md | 191 | โš ๏ธ Approaching limit | -| step-v-02b-parity-check.md | 209 | โš ๏ธ Approaching limit | -| step-v-03-density-validation.md | 174 | โœ… Good | -| step-v-04-brief-coverage-validation.md | 214 | โš ๏ธ Approaching limit | -| step-v-05-measurability-validation.md | 228 | โš ๏ธ Approaching limit | -| step-v-06-traceability-validation.md | 217 | โš ๏ธ Approaching limit | -| step-v-07-implementation-leakage-validation.md | 205 | โš ๏ธ Approaching limit | -| step-v-08-domain-compliance-validation.md | 243 | โš ๏ธ Approaching limit | -| step-v-09-project-type-validation.md | 263 | โŒ Exceeds limit | -| step-v-10-smart-validation.md | 209 | โš ๏ธ Approaching limit | -| step-v-11-holistic-quality-validation.md | 264 | โŒ Exceeds limit | -| step-v-12-completeness-validation.md | 242 | โš ๏ธ Approaching limit | -| step-v-13-report-complete.md | 231 | โš ๏ธ Approaching limit | - -#### Steps-E (Edit Steps) - 5 files -| File | Lines | Status | -| ------------------------------- | ----- | ------------------- | -| step-e-01-discovery.md | 206 | โš ๏ธ Approaching limit | -| step-e-01b-legacy-conversion.md | 208 | โš ๏ธ Approaching limit | -| step-e-02-review.md | 249 | โš ๏ธ Approaching limit | -| step-e-03-edit.md | 253 | โŒ Exceeds limit | -| step-e-04-complete.md | 168 | โœ… Good | - -#### Data & Templates -| File | Lines | Status | -| ------------------------- | ----- | ------------------- | -| data/prd-purpose.md | 197 | โš ๏ธ Approaching limit | -| templates/prd-template.md | 10 | โœ… Good | -| workflow.md | 114 | โœ… Good | - -### File Size Statistics - -- **Total Files**: 32 markdown files -- **โœ… Good (<200 lines)**: 6 files (18.8%) -- **โš ๏ธ Approaching limit (200-250)**: 23 files (71.9%) -- **โŒ Exceeds limit (>250)**: 3 files (9.4%) -- **Average lines per file**: 213.3 lines - -### โš ๏ธ Recommendations - -1. **Files Exceeding 250-line limit**: - - `step-v-09-project-type-validation.md` (263 lines) - Consider splitting into sub-steps - - `step-v-11-holistic-quality-validation.md` (264 lines) - Consider splitting into sub-steps - - `step-e-03-edit.md` (253 lines) - Consider splitting into sub-steps - -2. **Files Approaching Limit**: - - Many files are in the 200-250 line range - - Monitor these files as further additions may push them over the limit - - Consider proactive refactoring where appropriate - ---- - -## 2. Frontmatter Structure Validation - -### Files Checked: 29 total files - -**โœ… Overall Status:** ALL VALID - One Issue Fixed - -#### Main Workflow (workflow.md) -**Required Fields Present:** -- โœ… `name`: "prd" -- โœ… `description`: "PRD tri-modal workflow" -- โœ… `nextStep`: "./steps-c/step-01-init.md" -- โœ… `validateWorkflow`: "./steps-v/step-v-01-discovery.md" -- โœ… `editWorkflow`: "./steps-e/step-e-01-discovery.md" (FIXED - was assess-workflow.md) - -#### Create Steps (steps-c) -- โœ… All 13 files have proper name, description, nextStepFile -- โœ… Proper sequencing from step-01 through step-12 -- โœ… Consistent output file references - -#### Validation Steps (steps-v) -- โœ… All 13 files have complete frontmatter -- โœ… Proper sequential chain maintained -- โœ… No broken internal references - -#### Edit Steps (steps-e) -- โœ… All files have required fields -- โœ… Proper routing with altStepFile references - -### โœ… All Issues Resolved - -**1. Broken Edit Workflow Reference:** -```yaml -# Current (INCORRECT): -editWorkflow: './steps-e/step-e-01-assess-workflow.md' - -# Should be: -editWorkflow: './steps-e/step-e-01-discovery.md' -``` - -**2. Step Numbering Gap:** -- Original `step-11-complete.md` was deleted -- Sequence now: step-10 โ†’ step-11-polish โ†’ step-12-complete -- Creates confusion in step numbering - -### โœ… YAML Syntax -- No YAML syntax errors detected -- All frontmatter properly formatted -- Consistent structure across files - -### Status -โœ… **ALL ISSUES RESOLVED** - Only cosmetic improvements remain: - -1. **โœ… FIXED**: Edit workflow path corrected in workflow.md -2. **โš ๏ธ OPTIONAL**: Address step numbering gap for clarity -3. **โš ๏ธ OPTIONAL**: Rename step-01b-continue.md to step-01a-continue.md for consistency - ---- - -## 3. Step File Content Validation - -### Content Quality Assessment: 4.5/5 - EXCELLENT - -#### Files Reviewed: 10 representative files across all modes - -#### โœ… Strengths - -**1. Comprehensive Structure:** -- Clear step goal sections in all files -- Detailed mandatory execution rules -- Well-defined execution protocols -- Context boundaries clearly specified -- Mandatory sequence with numbered steps -- System success/failure metrics present - -**2. BMAD Compliance:** -- โœ… JIT loading references consistently mentioned -- โœ… State tracking requirements documented -- โœ… Append-only building instructions present -- โœ… Critical rules properly emphasized with emojis -- โœ… Sequential enforcement clearly stated - -**3. Instructional Quality:** -- Clear, unambiguous instructions -- Proper menu handling rules (where applicable) -- Excellent continuation checks -- Strong role definition for each mode - -**4. Role Clarity:** -- Create Mode: "Product-focused PM facilitator" -- Validate Mode: "Validation Architect and Quality Assurance Specialist" -- Edit Mode: "PRD improvement specialist" - -#### โš ๏ธ Minor Improvement Opportunities - -**1. Header Formatting:** -- Some inconsistency in header level usage across files -- Recommend standardizing H2/H3 usage - -**2. Edit Mode Completeness:** -- Edit mode has fewer steps (5 vs 12/13 for other modes) -- Documentation marks it as "Future" but implementation exists - -#### Recommendations -1. **LOW PRIORITY**: Standardize header formatting across all step files -2. **LOW PRIORITY**: Complete remaining edit mode steps for parity -3. **MAINTAIN**: Current excellent quality standards - ---- - -## 4. Documentation Validation - -### Documentation Completeness: โœ… COMPREHENSIVE - -#### Main Components Present -- โœ… Workflow Definition (workflow.md) -- โœ… Purpose Document (data/prd-purpose.md) -- โœ… Template (templates/prd-template.md) -- โœ… Three Mode Implementations (Create: 12, Validate: 13, Edit: 5 steps) - -#### Clarity Assessment: โœ… EXCELLENT - -**Strong Points:** -1. Clear mode determination (commands, flags, menu selection) -2. Detailed routing instructions for each mode -3. Comprehensive workflow architecture explanation -4. Well-defined critical rules with visual emphasis -5. Professional presentation with consistent formatting - -#### โš ๏ธ Minor Issues Found - -**1. Step Count Mismatch:** -- workflow.md mentions "11 steps" for Create mode -- Actually implements 12 steps -- Could confuse users - -**2. Edit Mode Status:** -- workflow.md calls Edit mode "Future" -- Edit mode steps are actually implemented -- Should reflect current status - -**3. Template Completeness:** -- PRD template is minimal (10 lines) -- Could benefit from section placeholders - -**4. Missing README:** -- No onboarding documentation for new users -- Not critical but would be helpful - -#### Recommendations - -**HIGH PRIORITY:** -1. Fix step count reference to match implementation (12 steps) -2. Update edit mode documentation to "Implemented" - -**MEDIUM PRIORITY:** -3. Enhance PRD template with section structure -4. Add quick-start README for new users - -**LOW PRIORITY:** -5. Add troubleshooting section -6. Document external dependencies (domain-complexity.csv, project-types.csv) - ---- - -## 5. Integration & Compatibility Validation - -### Integration Status: 85% Ready - -#### โœ… Successfully Integrated Components - -**1. Agent Menu Registration:** -- โœ… Registered in PM agent menu -- โœ… Trigger: `PR` or fuzzy match on `prd` -- โœ… Command: `/bmad:bmm:workflows:create-prd` -- โœ… Proper workflow path configuration - -**2. External Workflow References:** -- โœ… Party-mode workflow: Exists at `{project-root}/_bmad/core/workflows/party-mode/workflow.md` -- โœ… Advanced-elicitation task: Exists at `{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.xml` - -**3. Directory Structure:** -- โœ… Complete step architecture (all 3 modes) -- โœ… All referenced step files exist -- โœ… Data files available - -#### โœ… Configuration & Installation - WORKING AS DESIGNED - -**1. BMM Config Reference:** -- Path: `{project-root}/_bmad/bmm/config.yaml` -- **Status:** โœ… Correct installation-time placeholder -- Resolves to actual config during workflow installation -- **Note:** This is expected behavior, not an issue - -**2. Planning Artifacts Folder:** -- Reference: `{planning_artifacts}/prd.md` -- **Status:** โœ… Correct installation-time placeholder -- Created/resolved during workflow installation -- **Note:** This is expected behavior, not an issue - -**3. Edit Mode Implementation:** -- Current: 5 steps (Discovery, Legacy Conversion branch, Review, Edit, Complete) -- **Status:** โœ… Functionally complete -- Edit mode is inherently simpler than create mode (targeted improvements vs full creation) -- Uses subprocesses for complex operations -- Validation integration ensures quality -- **Note:** Edit workflow is complete and well-designed - -#### Configuration Analysis - -**Placeholder Usage:** -- `{project-root}`: โœ… Properly used -- `{planning_artifacts}`: โš ๏ธ Referenced but folder missing -- `{nextStep}`, `{validateWorkflow}`, etc: โœ… Properly resolved - -#### Recommendations - -**โœ… ALL CRITICAL ISSUES RESOLVED:** - -The only true critical issue (edit workflow path) has been fixed. All other items flagged as "critical" were actually working as designed (installation-time placeholders). - -**LOW PRIORITY:** -3. Add CLI command registration for standalone execution (optional enhancement) -4. Consider adding workflow to additional agent menus (UX designer, architect) -5. Create standalone execution documentation (nice-to-have) -6. Address step numbering gap if desired (cosmetic) - ---- - -## 6. Executive Summary & Overall Assessment - -### Overall Validation Status: โœ… PRODUCTION-READY - -#### Validation Scores by Category - -| Category | Status | Score | Notes | -| -------------------------- | ----------- | ------ | --------------------------------------------- | -| **File Structure & Size** | โš ๏ธ WARNINGS | 7/10 | 3 files exceed 250-line limit, 23 approaching | -| **Frontmatter Validation** | โœ… PASS | 9/10 | One broken path reference | -| **Step Content Quality** | โœ… EXCELLENT | 9.5/10 | High-quality instructional design | -| **Documentation** | โœ… EXCELLENT | 9/10 | Comprehensive, minor inconsistencies | -| **Integration** | โœ… PASS | 9/10 | All paths correct (one issue fixed) | -| **BMAD Compliance** | โœ… EXCELLENT | 9.5/10 | Strong adherence to standards | - -**Overall Score: 9.2/10 - EXCELLENT** - -#### โœ… Critical Action Items - ALL RESOLVED - -**ONLY ONE TRUE CRITICAL ISSUE EXISTED - NOW FIXED:** - -1. **โœ… FIXED: Edit Workflow Path** - - File: `workflow.md` โœ“ RESOLVED - - Changed from: `./steps-e/step-e-01-assess-workflow.md` - - Changed to: `./steps-e/step-e-01-discovery.md` - -**Items incorrectly flagged as critical (actually working as designed):** -- โœ… Configuration path references (installation-time placeholders) -- โœ… Planning artifacts folder (installation-time placeholder) - -#### High Priority Improvements - -2. **โš ๏ธ Split Large Step Files** (>250 lines): - - `step-v-09-project-type-validation.md` (263 lines) - - `step-v-11-holistic-quality-validation.md` (264 lines) - - `step-e-03-edit.md` (253 lines) - -3. **โš ๏ธ Update Documentation Inconsistencies**: - - Fix step count reference (11 โ†’ 12 steps in create mode) - - Update edit mode status (Future โ†’ Implemented) - -#### Medium Priority Enhancements - -4. **Enhance PRD Template** (currently minimal at 10 lines) -5. **Add quick-start README** for new users -6. **Address step numbering gap** (cosmetic - missing step-11-complete.md) - -#### Edit Mode Status - FUNCTIONALLY COMPLETE โœ… - -The edit workflow is **complete and well-designed** with 5 steps: -- Discovery โ†’ Legacy Conversion (branch) โ†’ Review โ†’ Edit โ†’ Complete -- Edit mode is inherently simpler than create mode (targeted improvements vs full creation) -- Uses subprocesses for complex operations -- Integrates with validation workflow - -**No additional steps needed.** - -### Key Strengths - -โœ… **Excellent step file quality** - Clear, well-structured instructions -โœ… **Comprehensive validation system** - 13 dedicated validation steps -โœ… **Strong BMAD compliance** - JIT loading, state tracking, sequential enforcement -โœ… **Tri-modal architecture** - Create, Validate, Edit all implemented -โœ… **Professional documentation** - Clear, consistent, well-presented -โœ… **Proper agent integration** - Registered in PM agent menu - -### Areas for Improvement (Optional) - -โš ๏ธ **File size management** - Many files approaching limits (maintainability consideration) -โš ๏ธ **Documentation consistency** - Minor discrepancies in counts/status (cosmetic) -โœ… **Edit mode** - Functionally complete, no additional steps needed - -### Conclusion - -The PRD workflow is **well-designed and fully compliant** with BMAD standards. The step file architecture is exemplary, the content quality is excellent, and the documentation is comprehensive. The only critical issue (edit workflow path) has been **resolved**, and all other flagged items were actually working as designed (installation-time placeholders). - -**Current Status: โœ… PRODUCTION-READY** - -**Recommended Optional Enhancements:** -1. Split the 3 files exceeding 250-line limit (maintainability) -2. Update documentation inconsistencies (step counts, edit mode status) -3. Enhance PRD template and add quick-start README (user experience) - -The PRD workflow is ready for production use and fully compliant with BMAD workflow standards. - ---- - -**Validation Completed:** 2026-01-08 -**Validation Method:** Systematic subprocess analysis with maximum context coverage -**Validator:** BMAD Workflow Validation System (Wendy - Workflow Building Master) diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md new file mode 100644 index 000000000..7d10ec3ed --- /dev/null +++ b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md @@ -0,0 +1,63 @@ +--- +name: create-prd +description: Create a comprehensive PRD (Product Requirements Document) through structured workflow facilitation +main_config: '{project-root}/_bmad/bmm/config.yaml' +nextStep: './steps-c/step-01-init.md' +--- + +# PRD Create Workflow + +**Goal:** Create comprehensive PRDs through structured workflow facilitation. + +**Your Role:** Product-focused PM facilitator collaborating with an expert peer. + +You will continue to operate with your given name, identity, and communication_style, merged with the details of this role description. + +## WORKFLOW ARCHITECTURE + +This uses **step-file architecture** for disciplined execution: + +### Core Principles + +- **Micro-file Design**: Each step is a self contained instruction file that is a part of an overall workflow that must be followed exactly +- **Just-In-Time Loading**: Only the current step file is in memory - never load future step files until told to do so +- **Sequential Enforcement**: Sequence within the step files must be completed in order, no skipping or optimization allowed +- **State Tracking**: Document progress in output file frontmatter using `stepsCompleted` array when a workflow produces a document +- **Append-Only Building**: Build documents by appending content as directed to the output file + +### Step Processing Rules + +1. **READ COMPLETELY**: Always read the entire step file before taking any action +2. **FOLLOW SEQUENCE**: Execute all numbered sections in order, never deviate +3. **WAIT FOR INPUT**: If a menu is presented, halt and wait for user selection +4. **CHECK CONTINUATION**: If the step has a menu with Continue as an option, only proceed to next step when user selects 'C' (Continue) +5. **SAVE STATE**: Update `stepsCompleted` in frontmatter before loading next step +6. **LOAD NEXT**: When directed, read fully and follow the next step file + +### Critical Rules (NO EXCEPTIONS) + +- ๐Ÿ›‘ **NEVER** load multiple step files simultaneously +- ๐Ÿ“– **ALWAYS** read entire step file before execution +- ๐Ÿšซ **NEVER** skip steps or optimize the sequence +- ๐Ÿ’พ **ALWAYS** update frontmatter of output files when writing the final output for a specific step +- ๐ŸŽฏ **ALWAYS** follow the exact instructions in the step file +- โธ๏ธ **ALWAYS** halt at menus and wait for user input +- ๐Ÿ“‹ **NEVER** create mental todo lists from future steps + +## INITIALIZATION SEQUENCE + +### 1. Configuration Loading + +Load and read full config from {main_config} and resolve: + +- `project_name`, `output_folder`, `planning_artifacts`, `user_name` +- `communication_language`, `document_output_language`, `user_skill_level` +- `date` as system-generated current datetime + +โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the configured `{communication_language}`. + +### 2. Route to Create Workflow + +"**Create Mode: Creating a new PRD from scratch.**" + +Read fully and follow: `{nextStep}` (steps-c/step-01-init.md) diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md new file mode 100644 index 000000000..5cb05af53 --- /dev/null +++ b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md @@ -0,0 +1,65 @@ +--- +name: edit-prd +description: Edit and improve an existing PRD - enhance clarity, completeness, and quality +main_config: '{project-root}/_bmad/bmm/config.yaml' +editWorkflow: './steps-e/step-e-01-discovery.md' +--- + +# PRD Edit Workflow + +**Goal:** Edit and improve existing PRDs through structured enhancement workflow. + +**Your Role:** PRD improvement specialist. + +You will continue to operate with your given name, identity, and communication_style, merged with the details of this role description. + +## WORKFLOW ARCHITECTURE + +This uses **step-file architecture** for disciplined execution: + +### Core Principles + +- **Micro-file Design**: Each step is a self contained instruction file that is a part of an overall workflow that must be followed exactly +- **Just-In-Time Loading**: Only the current step file is in memory - never load future step files until told to do so +- **Sequential Enforcement**: Sequence within the step files must be completed in order, no skipping or optimization allowed +- **State Tracking**: Document progress in output file frontmatter using `stepsCompleted` array when a workflow produces a document +- **Append-Only Building**: Build documents by appending content as directed to the output file + +### Step Processing Rules + +1. **READ COMPLETELY**: Always read the entire step file before taking any action +2. **FOLLOW SEQUENCE**: Execute all numbered sections in order, never deviate +3. **WAIT FOR INPUT**: If a menu is presented, halt and wait for user selection +4. **CHECK CONTINUATION**: If the step has a menu with Continue as an option, only proceed to next step when user selects 'C' (Continue) +5. **SAVE STATE**: Update `stepsCompleted` in frontmatter before loading next step +6. **LOAD NEXT**: When directed, read fully and follow the next step file + +### Critical Rules (NO EXCEPTIONS) + +- ๐Ÿ›‘ **NEVER** load multiple step files simultaneously +- ๐Ÿ“– **ALWAYS** read entire step file before execution +- ๐Ÿšซ **NEVER** skip steps or optimize the sequence +- ๐Ÿ’พ **ALWAYS** update frontmatter of output files when writing the final output for a specific step +- ๐ŸŽฏ **ALWAYS** follow the exact instructions in the step file +- โธ๏ธ **ALWAYS** halt at menus and wait for user input +- ๐Ÿ“‹ **NEVER** create mental todo lists from future steps + +## INITIALIZATION SEQUENCE + +### 1. Configuration Loading + +Load and read full config from {main_config} and resolve: + +- `project_name`, `output_folder`, `planning_artifacts`, `user_name` +- `communication_language`, `document_output_language`, `user_skill_level` +- `date` as system-generated current datetime + +โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the configured `{communication_language}`. + +### 2. Route to Edit Workflow + +"**Edit Mode: Improving an existing PRD.**" + +Prompt for PRD path: "Which PRD would you like to edit? Please provide the path to the PRD.md file." + +Then read fully and follow: `{editWorkflow}` (steps-e/step-e-01-discovery.md) diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md new file mode 100644 index 000000000..67a1aafc8 --- /dev/null +++ b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md @@ -0,0 +1,65 @@ +--- +name: validate-prd +description: Validate an existing PRD against BMAD standards - comprehensive review for completeness, clarity, and quality +main_config: '{project-root}/_bmad/bmm/config.yaml' +validateWorkflow: './steps-v/step-v-01-discovery.md' +--- + +# PRD Validate Workflow + +**Goal:** Validate existing PRDs against BMAD standards through comprehensive review. + +**Your Role:** Validation Architect and Quality Assurance Specialist. + +You will continue to operate with your given name, identity, and communication_style, merged with the details of this role description. + +## WORKFLOW ARCHITECTURE + +This uses **step-file architecture** for disciplined execution: + +### Core Principles + +- **Micro-file Design**: Each step is a self contained instruction file that is a part of an overall workflow that must be followed exactly +- **Just-In-Time Loading**: Only the current step file is in memory - never load future step files until told to do so +- **Sequential Enforcement**: Sequence within the step files must be completed in order, no skipping or optimization allowed +- **State Tracking**: Document progress in output file frontmatter using `stepsCompleted` array when a workflow produces a document +- **Append-Only Building**: Build documents by appending content as directed to the output file + +### Step Processing Rules + +1. **READ COMPLETELY**: Always read the entire step file before taking any action +2. **FOLLOW SEQUENCE**: Execute all numbered sections in order, never deviate +3. **WAIT FOR INPUT**: If a menu is presented, halt and wait for user selection +4. **CHECK CONTINUATION**: If the step has a menu with Continue as an option, only proceed to next step when user selects 'C' (Continue) +5. **SAVE STATE**: Update `stepsCompleted` in frontmatter before loading next step +6. **LOAD NEXT**: When directed, read fully and follow the next step file + +### Critical Rules (NO EXCEPTIONS) + +- ๐Ÿ›‘ **NEVER** load multiple step files simultaneously +- ๐Ÿ“– **ALWAYS** read entire step file before execution +- ๐Ÿšซ **NEVER** skip steps or optimize the sequence +- ๐Ÿ’พ **ALWAYS** update frontmatter of output files when writing the final output for a specific step +- ๐ŸŽฏ **ALWAYS** follow the exact instructions in the step file +- โธ๏ธ **ALWAYS** halt at menus and wait for user input +- ๐Ÿ“‹ **NEVER** create mental todo lists from future steps + +## INITIALIZATION SEQUENCE + +### 1. Configuration Loading + +Load and read full config from {main_config} and resolve: + +- `project_name`, `output_folder`, `planning_artifacts`, `user_name` +- `communication_language`, `document_output_language`, `user_skill_level` +- `date` as system-generated current datetime + +โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the configured `{communication_language}`. + +### 2. Route to Validate Workflow + +"**Validate Mode: Validating an existing PRD against BMAD standards.**" + +Prompt for PRD path: "Which PRD would you like to validate? Please provide the path to the PRD.md file." + +Then read fully and follow: `{validateWorkflow}` (steps-v/step-v-01-discovery.md) diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md deleted file mode 100644 index b13d7a7cf..000000000 --- a/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -name: create-prd -description: PRD tri-modal workflow - Create, Validate, or Edit comprehensive PRDs -main_config: '{project-root}/_bmad/bmm/config.yaml' -nextStep: './steps-c/step-01-init.md' -validateWorkflow: './steps-v/step-v-01-discovery.md' -editWorkflow: './steps-e/step-e-01-discovery.md' -web_bundle: true ---- - -# PRD Workflow (Tri-Modal) - -**Goal:** Create, Validate, or Edit comprehensive PRDs through structured workflows. - -**Your Role:** -- **Create Mode:** Product-focused PM facilitator collaborating with an expert peer -- **Validate Mode:** Validation Architect and Quality Assurance Specialist -- **Edit Mode:** PRD improvement specialist - -You will continue to operate with your given name, identity, and communication_style, merged with the details of this role description. - ---- - -## MODE DETERMINATION - -### Detect Workflow Mode - -Determine which mode to invoke based on: - -1. **Command/Invocation:** - - "create prd" or "new prd" โ†’ Create mode - - "validate prd" or "check prd" โ†’ Validate mode - - "edit prd" or "improve prd" โ†’ Edit mode - -2. **Context Detection:** - - If invoked with -c flag โ†’ Create mode - - If invoked with -v flag โ†’ Validate mode - - If invoked with -e flag โ†’ Edit mode - -3. **Menu Selection (if unclear):** - -If mode cannot be determined from invocation: -"**PRD Workflow - Select Mode:** - -**[C] Create** - Create a new PRD from scratch -**[V] Validate** - Validate an existing PRD against BMAD standards -**[E] Edit** - Improve an existing PRD - -Which mode would you like?" - -Wait for user selection. - -### Route to Appropriate Workflow - -**IF Create Mode:** -"**Create Mode: Creating a new PRD from scratch.**" -Read fully and follow: `{nextStep}` (steps-c/step-01-init.md) - -**IF Validate Mode:** -"**Validate Mode: Validating an existing PRD against BMAD standards.**" -Prompt for PRD path: "Which PRD would you like to validate? Please provide the path to the PRD.md file." -Then read fully and follow: `{validateWorkflow}` (steps-v/step-v-01-discovery.md) - -**IF Edit Mode:** -"**Edit Mode: Improving an existing PRD.**" -Prompt for PRD path: "Which PRD would you like to edit? Please provide the path to the PRD.md file." -Then read fully and follow: `{editWorkflow}` (steps-e/step-e-01-discovery.md) - ---- - -## WORKFLOW ARCHITECTURE - -This uses **step-file architecture** for disciplined execution: - -### Core Principles - -- **Micro-file Design**: Each step is a self contained instruction file that is a part of an overall workflow that must be followed exactly -- **Just-In-Time Loading**: Only the current step file is in memory - never load future step files until told to do so -- **Sequential Enforcement**: Sequence within the step files must be completed in order, no skipping or optimization allowed -- **State Tracking**: Document progress in output file frontmatter using `stepsCompleted` array when a workflow produces a document -- **Append-Only Building**: Build documents by appending content as directed to the output file - -### Step Processing Rules - -1. **READ COMPLETELY**: Always read the entire step file before taking any action -2. **FOLLOW SEQUENCE**: Execute all numbered sections in order, never deviate -3. **WAIT FOR INPUT**: If a menu is presented, halt and wait for user selection -4. **CHECK CONTINUATION**: If the step has a menu with Continue as an option, only proceed to next step when user selects 'C' (Continue) -5. **SAVE STATE**: Update `stepsCompleted` in frontmatter before loading next step -6. **LOAD NEXT**: When directed, read fully and follow the next step file - -### Critical Rules (NO EXCEPTIONS) - -- ๐Ÿ›‘ **NEVER** load multiple step files simultaneously -- ๐Ÿ“– **ALWAYS** read entire step file before execution -- ๐Ÿšซ **NEVER** skip steps or optimize the sequence -- ๐Ÿ’พ **ALWAYS** update frontmatter of output files when writing the final output for a specific step -- ๐ŸŽฏ **ALWAYS** follow the exact instructions in the step file -- โธ๏ธ **ALWAYS** halt at menus and wait for user input -- ๐Ÿ“‹ **NEVER** create mental todo lists from future steps - ---- - -## INITIALIZATION SEQUENCE - -### 1. Mode Determination - -**Check if mode was specified in the command invocation:** - -- If user invoked with "create prd" or "new prd" or "build prd" or "-c" or "--create" โ†’ Set mode to **create** -- If user invoked with "validate prd" or "review prd" or "check prd" or "-v" or "--validate" โ†’ Set mode to **validate** -- If user invoked with "edit prd" or "modify prd" or "improve prd" or "-e" or "--edit" โ†’ Set mode to **edit** - -**If mode is still unclear, ask user:** - -"**PRD Workflow - Select Mode:** - -**[C] Create** - Create a new PRD from scratch -**[V] Validate** - Validate an existing PRD against BMAD standards -**[E] Edit** - Improve an existing PRD - -Which mode would you like?" - -Wait for user selection. - -### 2. Configuration Loading - -Load and read full config from {main_config} and resolve: - -- `project_name`, `output_folder`, `planning_artifacts`, `user_name` -- `communication_language`, `document_output_language`, `user_skill_level` -- `date` as system-generated current datetime - -โœ… YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the configured `{communication_language}`. - -### 3. Route to Appropriate Workflow - -**IF mode == create:** -"**Create Mode: Creating a new PRD from scratch.**" -Read fully and follow: `{nextStep}` (steps-c/step-01-init.md) - -**IF mode == validate:** -"**Validate Mode: Validating an existing PRD against BMAD standards.**" -Prompt for PRD path: "Which PRD would you like to validate? Please provide the path to the PRD.md file." -Then read fully and follow: `{validateWorkflow}` (steps-v/step-v-01-discovery.md) - -**IF mode == edit:** -"**Edit Mode: Improving an existing PRD.**" -Prompt for PRD path: "Which PRD would you like to edit? Please provide the path to the PRD.md file." -Then read fully and follow: `{editWorkflow}` (steps-e/step-e-01-discovery.md) diff --git a/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md b/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md index d74cb4878..4af87c39a 100644 --- a/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +++ b/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md @@ -1,7 +1,6 @@ --- name: create-ux-design description: Work with a peer UX Design expert to plan your applications UX patterns, look and feel. -web_bundle: true --- # Create UX Design Workflow diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md index d7eb5969e..49d2afab9 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md @@ -1,7 +1,6 @@ --- name: check-implementation-readiness description: 'Critical validation workflow that assesses PRD, Architecture, and Epics & Stories for completeness and alignment before implementation. Uses adversarial review approach to find gaps and issues.' -web_bundle: false --- # Implementation Readiness diff --git a/src/bmm/workflows/3-solutioning/create-architecture/workflow.md b/src/bmm/workflows/3-solutioning/create-architecture/workflow.md index d36c328e8..b75b4a46c 100644 --- a/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +++ b/src/bmm/workflows/3-solutioning/create-architecture/workflow.md @@ -1,7 +1,6 @@ --- name: create-architecture description: Collaborative architectural decision facilitation for AI-agent consistency. Replaces template-driven architecture with intelligent, adaptive conversation that produces a decision-focused architecture document optimized for preventing agent conflicts. -web_bundle: true --- # Architecture Workflow diff --git a/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md b/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md index a1e78a028..a0e232ab8 100644 --- a/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +++ b/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md @@ -1,7 +1,6 @@ --- name: create-epics-and-stories description: 'Transform PRD requirements and Architecture decisions into comprehensive stories organized by user value. This workflow requires completed PRD + Architecture documents (UX recommended if UI exists) and breaks down requirements into implementation-ready epics and user stories that incorporate all available technical and design context. Creates detailed, actionable stories with complete acceptance criteria for development teams.' -web_bundle: true --- # Create Epics and Stories diff --git a/src/bmm/workflows/4-implementation/code-review/workflow.yaml b/src/bmm/workflows/4-implementation/code-review/workflow.yaml index 9e66b9325..5b5f6b2fc 100644 --- a/src/bmm/workflows/4-implementation/code-review/workflow.yaml +++ b/src/bmm/workflows/4-implementation/code-review/workflow.yaml @@ -46,6 +46,3 @@ input_file_patterns: sharded_index: "{planning_artifacts}/*epic*/index.md" sharded_single: "{planning_artifacts}/*epic*/epic-{{epic_num}}.md" load_strategy: "SELECTIVE_LOAD" - -standalone: true -web_bundle: false diff --git a/src/bmm/workflows/4-implementation/correct-course/workflow.yaml b/src/bmm/workflows/4-implementation/correct-course/workflow.yaml index 70813514a..318b5a7dc 100644 --- a/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +++ b/src/bmm/workflows/4-implementation/correct-course/workflow.yaml @@ -54,7 +54,3 @@ instructions: "{installed_path}/instructions.md" validation: "{installed_path}/checklist.md" checklist: "{installed_path}/checklist.md" default_output_file: "{planning_artifacts}/sprint-change-proposal-{date}.md" - -standalone: true - -web_bundle: false diff --git a/src/bmm/workflows/4-implementation/create-story/workflow.yaml b/src/bmm/workflows/4-implementation/create-story/workflow.yaml index 258794c7c..1f3ac9784 100644 --- a/src/bmm/workflows/4-implementation/create-story/workflow.yaml +++ b/src/bmm/workflows/4-implementation/create-story/workflow.yaml @@ -55,7 +55,3 @@ input_file_patterns: whole: "{planning_artifacts}/*epic*.md" sharded: "{planning_artifacts}/*epic*/*.md" load_strategy: "SELECTIVE_LOAD" # Only load needed epic - -standalone: true - -web_bundle: false diff --git a/src/bmm/workflows/4-implementation/dev-story/workflow.yaml b/src/bmm/workflows/4-implementation/dev-story/workflow.yaml index d5824ee17..daf152b71 100644 --- a/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +++ b/src/bmm/workflows/4-implementation/dev-story/workflow.yaml @@ -21,7 +21,3 @@ story_file: "" # Explicit story path; auto-discovered if empty implementation_artifacts: "{config_source}:implementation_artifacts" sprint_status: "{implementation_artifacts}/sprint-status.yaml" project_context: "**/project-context.md" - -standalone: true - -web_bundle: false diff --git a/src/bmm/workflows/4-implementation/retrospective/workflow.yaml b/src/bmm/workflows/4-implementation/retrospective/workflow.yaml index 80d934b2c..b92ecaf1b 100644 --- a/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +++ b/src/bmm/workflows/4-implementation/retrospective/workflow.yaml @@ -53,6 +53,3 @@ input_file_patterns: sprint_status_file: "{implementation_artifacts}/sprint-status.yaml" story_directory: "{implementation_artifacts}" retrospectives_folder: "{implementation_artifacts}" - -standalone: true -web_bundle: false diff --git a/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml b/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml index 25ccf5f72..7b157633c 100644 --- a/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +++ b/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml @@ -49,7 +49,3 @@ input_file_patterns: # Output configuration default_output_file: "{status_file}" - -standalone: true - -web_bundle: false diff --git a/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml b/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml index 6f10a9a67..8946f0291 100644 --- a/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +++ b/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml @@ -28,9 +28,3 @@ input_file_patterns: description: "Sprint status file generated by sprint-planning" whole: "{implementation_artifacts}/sprint-status.yaml" load_strategy: "FULL_LOAD" - -# Standalone so IDE commands get generated -standalone: true - -# No web bundle needed -web_bundle: false diff --git a/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md b/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md index bb6c877a7..7c41b948d 100644 --- a/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +++ b/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md @@ -2,7 +2,6 @@ name: quick-spec description: Conversational spec engineering - ask questions, investigate code, produce implementation-ready tech-spec. main_config: '{project-root}/_bmad/bmm/config.yaml' -web_bundle: true # Checkpoint handler paths advanced_elicitation: '{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.xml' diff --git a/src/bmm/workflows/document-project/workflow.yaml b/src/bmm/workflows/document-project/workflow.yaml index 536257b3d..4667d7c0b 100644 --- a/src/bmm/workflows/document-project/workflow.yaml +++ b/src/bmm/workflows/document-project/workflow.yaml @@ -20,11 +20,3 @@ validation: "{installed_path}/checklist.md" # Required data files - CRITICAL for project type detection and documentation requirements documentation_requirements_csv: "{installed_path}/documentation-requirements.csv" - -# Output configuration - Multiple files generated in output folder -# Primary output: {output_folder}/project-documentation/ -# Additional files generated by sub-workflows based on project structure - -standalone: true - -web_bundle: false diff --git a/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json b/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json deleted file mode 100644 index d18f94af3..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "type": "excalidrawlib", - "version": 2, - "library": [ - { - "id": "start-end-circle", - "status": "published", - "elements": [ - { - "type": "ellipse", - "width": 120, - "height": 60, - "strokeColor": "#1976d2", - "backgroundColor": "#e3f2fd", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0 - } - ] - }, - { - "id": "process-rectangle", - "status": "published", - "elements": [ - { - "type": "rectangle", - "width": 160, - "height": 80, - "strokeColor": "#1976d2", - "backgroundColor": "#e3f2fd", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0, - "roundness": { - "type": 3, - "value": 8 - } - } - ] - }, - { - "id": "decision-diamond", - "status": "published", - "elements": [ - { - "type": "diamond", - "width": 140, - "height": 100, - "strokeColor": "#f57c00", - "backgroundColor": "#fff3e0", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0 - } - ] - }, - { - "id": "data-store", - "status": "published", - "elements": [ - { - "type": "rectangle", - "width": 140, - "height": 80, - "strokeColor": "#388e3c", - "backgroundColor": "#e8f5e9", - "fillStyle": "solid", - "strokeWidth": 2, - "roughness": 0 - } - ] - }, - { - "id": "external-entity", - "status": "published", - "elements": [ - { - "type": "rectangle", - "width": 120, - "height": 80, - "strokeColor": "#7b1fa2", - "backgroundColor": "#f3e5f5", - "fillStyle": "solid", - "strokeWidth": 3, - "roughness": 0 - } - ] - } - ] -} diff --git a/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml b/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml deleted file mode 100644 index 6fab2a3d7..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +++ /dev/null @@ -1,127 +0,0 @@ -flowchart: - viewport: - x: 0 - y: 0 - zoom: 1 - grid: - size: 20 - spacing: - vertical: 100 - horizontal: 180 - elements: - start: - type: ellipse - width: 120 - height: 60 - label: "Start" - process: - type: rectangle - width: 160 - height: 80 - roundness: 8 - decision: - type: diamond - width: 140 - height: 100 - end: - type: ellipse - width: 120 - height: 60 - label: "End" - -diagram: - viewport: - x: 0 - y: 0 - zoom: 1 - grid: - size: 20 - spacing: - vertical: 120 - horizontal: 200 - elements: - component: - type: rectangle - width: 180 - height: 100 - roundness: 8 - database: - type: rectangle - width: 140 - height: 80 - service: - type: rectangle - width: 160 - height: 90 - roundness: 12 - external: - type: rectangle - width: 140 - height: 80 - -wireframe: - viewport: - x: 0 - y: 0 - zoom: 0.8 - grid: - size: 20 - spacing: - vertical: 40 - horizontal: 40 - elements: - container: - type: rectangle - width: 800 - height: 600 - strokeStyle: solid - strokeWidth: 2 - header: - type: rectangle - width: 800 - height: 80 - button: - type: rectangle - width: 120 - height: 40 - roundness: 4 - input: - type: rectangle - width: 300 - height: 40 - roundness: 4 - text: - type: text - fontSize: 16 - -dataflow: - viewport: - x: 0 - y: 0 - zoom: 1 - grid: - size: 20 - spacing: - vertical: 120 - horizontal: 200 - elements: - process: - type: ellipse - width: 140 - height: 80 - label: "Process" - datastore: - type: rectangle - width: 140 - height: 80 - label: "Data Store" - external: - type: rectangle - width: 120 - height: 80 - strokeWidth: 3 - label: "External Entity" - dataflow: - type: arrow - strokeWidth: 2 - label: "Data Flow" diff --git a/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md b/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md deleted file mode 100644 index 3c9463d5d..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +++ /dev/null @@ -1,39 +0,0 @@ -# Create Data Flow Diagram - Validation Checklist - -## DFD Notation - -- [ ] Processes shown as circles/ellipses -- [ ] Data stores shown as parallel lines or rectangles -- [ ] External entities shown as rectangles -- [ ] Data flows shown as labeled arrows -- [ ] Follows standard DFD notation - -## Structure - -- [ ] All processes numbered correctly -- [ ] All data flows labeled with data names -- [ ] All data stores named appropriately -- [ ] External entities clearly identified - -## Completeness - -- [ ] All inputs and outputs accounted for -- [ ] No orphaned processes (unconnected) -- [ ] Data conservation maintained -- [ ] Level appropriate (context/level 0/level 1) - -## Layout - -- [ ] Logical flow direction (left to right, top to bottom) -- [ ] No crossing data flows where avoidable -- [ ] Balanced layout -- [ ] Grid alignment maintained - -## Technical Quality - -- [ ] All elements properly grouped -- [ ] Arrows have proper bindings -- [ ] Text readable and properly sized -- [ ] No elements with `isDeleted: true` -- [ ] JSON is valid -- [ ] File saved to correct location diff --git a/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md b/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md deleted file mode 100644 index 30d32ed33..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +++ /dev/null @@ -1,130 +0,0 @@ -# Create Data Flow Diagram - Workflow Instructions - -```xml -The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml -You MUST have already loaded and processed: {installed_path}/workflow.yaml -This workflow creates data flow diagrams (DFD) in Excalidraw format. - - - - - Review user's request and extract: DFD level, processes, data stores, external entities - Skip to Step 4 - - - - Ask: "What level of DFD do you need?" - Present options: - 1. Context Diagram (Level 0) - Single process showing system boundaries - 2. Level 1 DFD - Major processes and data flows - 3. Level 2 DFD - Detailed sub-processes - 4. Custom - Specify your requirements - - WAIT for selection - - - - Ask: "Describe the processes, data stores, and external entities in your system" - WAIT for user description - Summarize what will be included and confirm with user - - - - Check for existing theme.json, ask to use if exists - - Ask: "Choose a DFD color scheme:" - Present numbered options: - 1. Standard DFD - - Process: #e3f2fd (light blue) - - Data Store: #e8f5e9 (light green) - - External Entity: #f3e5f5 (light purple) - - Border: #1976d2 (blue) - - 2. Colorful DFD - - Process: #fff9c4 (light yellow) - - Data Store: #c5e1a5 (light lime) - - External Entity: #ffccbc (light coral) - - Border: #f57c00 (orange) - - 3. Minimal DFD - - Process: #f5f5f5 (light gray) - - Data Store: #eeeeee (gray) - - External Entity: #e0e0e0 (medium gray) - - Border: #616161 (dark gray) - - 4. Custom - Define your own colors - - WAIT for selection - Create theme.json based on selection - - - - - List all processes with numbers (1.0, 2.0, etc.) - List all data stores (D1, D2, etc.) - List all external entities - Map all data flows with labels - Show planned structure, confirm with user - - - - Load {{templates}} and extract `dataflow` section - Load {{library}} - Load theme.json - Load {{helpers}} - - - - Follow standard DFD notation from {{helpers}} - - Build Order: - 1. External entities (rectangles, bold border) - 2. Processes (circles/ellipses with numbers) - 3. Data stores (parallel lines or rectangles) - 4. Data flows (labeled arrows) - - - DFD Rules: - - Processes: Numbered (1.0, 2.0), verb phrases - - Data stores: Named (D1, D2), noun phrases - - External entities: Named, noun phrases - - Data flows: Labeled with data names, arrows show direction - - No direct flow between external entities - - No direct flow between data stores - - - Layout: - - External entities at edges - - Processes in center - - Data stores between processes - - Minimize crossing flows - - Left-to-right or top-to-bottom flow - - - - - Verify DFD rules compliance - Strip unused elements and elements with isDeleted: true - Save to {{default_output_file}} - - - - NEVER delete the file if validation fails - always fix syntax errors - Run: node -e "JSON.parse(require('fs').readFileSync('{{default_output_file}}', 'utf8')); console.log('โœ“ Valid JSON')" - - Read the error message carefully - it shows the syntax error and position - Open the file and navigate to the error location - Fix the syntax error (add missing comma, bracket, or quote as indicated) - Save the file - Re-run validation with the same command - Repeat until validation passes - - Once validation passes, confirm with user - - - - Validate against {{validation}} - - - -``` diff --git a/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml b/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml deleted file mode 100644 index 2f01e6b51..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: create-excalidraw-dataflow -description: "Create data flow diagrams (DFD) in Excalidraw format" -author: "BMad" - -# Config values -config_source: "{project-root}/_bmad/bmm/config.yaml" -output_folder: "{config_source}:output_folder" - -# Workflow components -installed_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow" -shared_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/_shared" -instructions: "{installed_path}/instructions.md" -validation: "{installed_path}/checklist.md" - -# Core Excalidraw resources (universal knowledge) -helpers: "{project-root}/_bmad/core/resources/excalidraw/excalidraw-helpers.md" -json_validation: "{project-root}/_bmad/core/resources/excalidraw/validate-json-instructions.md" - -# Domain-specific resources (technical diagrams) -templates: "{shared_path}/excalidraw-templates.yaml" -library: "{shared_path}/excalidraw-library.json" - -# Output file (respects user's configured output_folder) -default_output_file: "{output_folder}/excalidraw-diagrams/dataflow-{timestamp}.excalidraw" - -standalone: true -web_bundle: false diff --git a/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md b/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md deleted file mode 100644 index 61d216aea..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +++ /dev/null @@ -1,43 +0,0 @@ -# Create Diagram - Validation Checklist - -## Element Structure - -- [ ] All components with labels have matching `groupIds` -- [ ] All text elements have `containerId` pointing to parent component -- [ ] Text width calculated properly (no cutoff) -- [ ] Text alignment appropriate for diagram type - -## Layout and Alignment - -- [ ] All elements snapped to 20px grid -- [ ] Component spacing consistent (40px/60px) -- [ ] Hierarchical alignment maintained -- [ ] No overlapping elements - -## Connections - -- [ ] All arrows have `startBinding` and `endBinding` -- [ ] `boundElements` array updated on connected components -- [ ] Arrow routing avoids overlaps -- [ ] Relationship types clearly indicated - -## Notation and Standards - -- [ ] Follows specified notation standard (UML/ERD/etc) -- [ ] Symbols used correctly -- [ ] Cardinality/multiplicity shown where needed -- [ ] Labels and annotations clear - -## Theme and Styling - -- [ ] Theme colors applied consistently -- [ ] Component types visually distinguishable -- [ ] Text is readable -- [ ] Professional appearance - -## Output Quality - -- [ ] Element count under 80 -- [ ] No elements with `isDeleted: true` -- [ ] JSON is valid -- [ ] File saved to correct location diff --git a/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md b/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md deleted file mode 100644 index 407a76bf7..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +++ /dev/null @@ -1,141 +0,0 @@ -# Create Diagram - Workflow Instructions - -```xml -The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml -You MUST have already loaded and processed: {installed_path}/workflow.yaml -This workflow creates system architecture diagrams, ERDs, UML diagrams, or general technical diagrams in Excalidraw format. - - - - - Review user's request and extract: diagram type, components/entities, relationships, notation preferences - Skip to Step 5 - Only ask about missing info in Steps 1-2 - - - - Ask: "What type of technical diagram do you need?" - Present options: - 1. System Architecture - 2. Entity-Relationship Diagram (ERD) - 3. UML Class Diagram - 4. UML Sequence Diagram - 5. UML Use Case Diagram - 6. Network Diagram - 7. Other - - WAIT for selection - - - - Ask: "Describe the components/entities and their relationships" - Ask: "What notation standard? (Standard/Simplified/Strict UML-ERD)" - WAIT for user input - Summarize what will be included and confirm with user - - - - Check if theme.json exists at output location - Ask to use it, load if yes, else proceed to Step 4 - Proceed to Step 4 - - - - Ask: "Choose a color scheme for your diagram:" - Present numbered options: - 1. Professional - - Component: #e3f2fd (light blue) - - Database: #e8f5e9 (light green) - - Service: #fff3e0 (light orange) - - Border: #1976d2 (blue) - - 2. Colorful - - Component: #e1bee7 (light purple) - - Database: #c5e1a5 (light lime) - - Service: #ffccbc (light coral) - - Border: #7b1fa2 (purple) - - 3. Minimal - - Component: #f5f5f5 (light gray) - - Database: #eeeeee (gray) - - Service: #e0e0e0 (medium gray) - - Border: #616161 (dark gray) - - 4. Custom - Define your own colors - - WAIT for selection - Create theme.json based on selection - Show preview and confirm - - - - List all components/entities - Map all relationships - Show planned layout - Ask: "Structure looks correct? (yes/no)" - Adjust and repeat - - - - Load {{templates}} and extract `diagram` section - Load {{library}} - Load theme.json and merge with template - Load {{helpers}} for guidelines - - - - Follow {{helpers}} for proper element creation - - For Each Component: - - Generate unique IDs (component-id, text-id, group-id) - - Create shape with groupIds - - Calculate text width - - Create text with containerId and matching groupIds - - Add boundElements - - - For Each Connection: - - Determine arrow type (straight/elbow) - - Create with startBinding and endBinding - - Update boundElements on both components - - - Build Order by Type: - - Architecture: Services โ†’ Databases โ†’ Connections โ†’ Labels - - ERD: Entities โ†’ Attributes โ†’ Relationships โ†’ Cardinality - - UML Class: Classes โ†’ Attributes โ†’ Methods โ†’ Relationships - - UML Sequence: Actors โ†’ Lifelines โ†’ Messages โ†’ Returns - - UML Use Case: Actors โ†’ Use Cases โ†’ Relationships - - - Alignment: - - Snap to 20px grid - - Space: 40px between components, 60px between sections - - - - - Strip unused elements and elements with isDeleted: true - Save to {{default_output_file}} - - - - NEVER delete the file if validation fails - always fix syntax errors - Run: node -e "JSON.parse(require('fs').readFileSync('{{default_output_file}}', 'utf8')); console.log('โœ“ Valid JSON')" - - Read the error message carefully - it shows the syntax error and position - Open the file and navigate to the error location - Fix the syntax error (add missing comma, bracket, or quote as indicated) - Save the file - Re-run validation with the same command - Repeat until validation passes - - Once validation passes, confirm: "Diagram created at {{default_output_file}}. Open to view?" - - - - Validate against {{validation}} using {_bmad}/core/tasks/validate-workflow.xml - - - -``` diff --git a/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml b/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml deleted file mode 100644 index f841a546f..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: create-excalidraw-diagram -description: "Create system architecture diagrams, ERDs, UML diagrams, or general technical diagrams in Excalidraw format" -author: "BMad" - -# Config values -config_source: "{project-root}/_bmad/bmm/config.yaml" -output_folder: "{config_source}:output_folder" - -# Workflow components -installed_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram" -shared_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/_shared" -instructions: "{installed_path}/instructions.md" -validation: "{installed_path}/checklist.md" - -# Core Excalidraw resources (universal knowledge) -helpers: "{project-root}/_bmad/core/resources/excalidraw/excalidraw-helpers.md" -json_validation: "{project-root}/_bmad/core/resources/excalidraw/validate-json-instructions.md" - -# Domain-specific resources (technical diagrams) -templates: "{shared_path}/excalidraw-templates.yaml" -library: "{shared_path}/excalidraw-library.json" - -# Output file (respects user's configured output_folder) -default_output_file: "{output_folder}/excalidraw-diagrams/diagram-{timestamp}.excalidraw" - -standalone: true -web_bundle: false diff --git a/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md b/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md deleted file mode 100644 index 7da7fb78d..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +++ /dev/null @@ -1,49 +0,0 @@ -# Create Flowchart - Validation Checklist - -## Element Structure - -- [ ] All shapes with labels have matching `groupIds` -- [ ] All text elements have `containerId` pointing to parent shape -- [ ] Text width calculated properly (no cutoff) -- [ ] Text alignment set (`textAlign` + `verticalAlign`) - -## Layout and Alignment - -- [ ] All elements snapped to 20px grid -- [ ] Consistent spacing between elements (60px minimum) -- [ ] Vertical alignment maintained for flow direction -- [ ] No overlapping elements - -## Connections - -- [ ] All arrows have `startBinding` and `endBinding` -- [ ] `boundElements` array updated on connected shapes -- [ ] Arrow types appropriate (straight for forward, elbow for backward/upward) -- [ ] Gap set to 10 for all bindings - -## Theme and Styling - -- [ ] Theme colors applied consistently -- [ ] All shapes use theme primary fill color -- [ ] All borders use theme accent color -- [ ] Text color is readable (#1e1e1e) - -## Composition - -- [ ] Element count under 50 -- [ ] Library components referenced where possible -- [ ] No duplicate element definitions - -## Output Quality - -- [ ] No elements with `isDeleted: true` -- [ ] JSON is valid -- [ ] File saved to correct location - -## Functional Requirements - -- [ ] Start point clearly marked -- [ ] End point clearly marked -- [ ] All process steps labeled -- [ ] Decision points use diamond shapes -- [ ] Flow direction is clear and logical diff --git a/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md b/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md deleted file mode 100644 index 742679050..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +++ /dev/null @@ -1,241 +0,0 @@ -# Create Flowchart - Workflow Instructions - -```xml -The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml -You MUST have already loaded and processed: {installed_path}/workflow.yaml -This workflow creates a flowchart visualization in Excalidraw format for processes, pipelines, or logic flows. - - - - - Before asking any questions, analyze what the user has already told you - - Review the user's initial request and conversation history - Extract any mentioned: flowchart type, complexity, decision points, save location - - - Summarize your understanding - Skip directly to Step 4 (Plan Flowchart Layout) - - - - Note what you already know - Only ask about missing information in Step 1 - - - - Proceed with full elicitation in Step 1 - - - - - Ask Question 1: "What type of process flow do you need to visualize?" - Present numbered options: - 1. Business Process Flow - Document business workflows, approval processes, or operational procedures - 2. Algorithm/Logic Flow - Visualize code logic, decision trees, or computational processes - 3. User Journey Flow - Map user interactions, navigation paths, or experience flows - 4. Data Processing Pipeline - Show data transformation, ETL processes, or processing stages - 5. Other - Describe your specific flowchart needs - - WAIT for user selection (1-5) - - Ask Question 2: "How many main steps are in this flow?" - Present numbered options: - 1. Simple (3-5 steps) - Quick process with few decision points - 2. Medium (6-10 steps) - Standard workflow with some branching - 3. Complex (11-20 steps) - Detailed process with multiple decision points - 4. Very Complex (20+ steps) - Comprehensive workflow requiring careful layout - - WAIT for user selection (1-4) - Store selection in {{complexity}} - - Ask Question 3: "Does your flow include decision points (yes/no branches)?" - Present numbered options: - 1. No decisions - Linear flow from start to end - 2. Few decisions (1-2) - Simple branching with yes/no paths - 3. Multiple decisions (3-5) - Several conditional branches - 4. Complex decisions (6+) - Extensive branching logic - - WAIT for user selection (1-4) - Store selection in {{decision_points}} - - Ask Question 4: "Where should the flowchart be saved?" - Present numbered options: - 1. Default location - docs/flowcharts/[auto-generated-name].excalidraw - 2. Custom path - Specify your own file path - 3. Project root - Save in main project directory - 4. Specific folder - Choose from existing folders - - WAIT for user selection (1-4) - - Ask for specific path - WAIT for user input - - Store final path in {{default_output_file}} - - - - Check if theme.json exists at output location - - Ask: "Found existing theme. Use it? (yes/no)" - WAIT for user response - - Load and use existing theme - Skip to Step 4 - - - Proceed to Step 3 - - - - Proceed to Step 3 - - - - - Ask: "Let's create a theme for your flowchart. Choose a color scheme:" - Present numbered options: - 1. Professional Blue - - Primary Fill: #e3f2fd (light blue) - - Accent/Border: #1976d2 (blue) - - Decision: #fff3e0 (light orange) - - Text: #1e1e1e (dark gray) - - 2. Success Green - - Primary Fill: #e8f5e9 (light green) - - Accent/Border: #388e3c (green) - - Decision: #fff9c4 (light yellow) - - Text: #1e1e1e (dark gray) - - 3. Neutral Gray - - Primary Fill: #f5f5f5 (light gray) - - Accent/Border: #616161 (gray) - - Decision: #e0e0e0 (medium gray) - - Text: #1e1e1e (dark gray) - - 4. Warm Orange - - Primary Fill: #fff3e0 (light orange) - - Accent/Border: #f57c00 (orange) - - Decision: #ffe0b2 (peach) - - Text: #1e1e1e (dark gray) - - 5. Custom Colors - Define your own color palette - - WAIT for user selection (1-5) - Store selection in {{theme_choice}} - - - Ask: "Primary fill color (hex code)?" - WAIT for user input - Store in {{custom_colors.primary_fill}} - Ask: "Accent/border color (hex code)?" - WAIT for user input - Store in {{custom_colors.accent}} - Ask: "Decision color (hex code)?" - WAIT for user input - Store in {{custom_colors.decision}} - - - Create theme.json with selected colors - Show theme preview with all colors - Ask: "Theme looks good?" - Present numbered options: - 1. Yes, use this theme - Proceed with theme - 2. No, adjust colors - Modify color selections - 3. Start over - Choose different preset - - WAIT for selection (1-3) - - Repeat Step 3 - - - - - List all steps and decision points based on gathered requirements - Show user the planned structure - Ask: "Structure looks correct? (yes/no)" - WAIT for user response - - Adjust structure based on feedback - Repeat this step - - - - - Load {{templates}} file - Extract `flowchart` section from YAML - Load {{library}} file - Load theme.json and merge colors with template - Load {{helpers}} for element creation guidelines - - - - Follow guidelines from {{helpers}} for proper element creation - - Build ONE section at a time following these rules: - - For Each Shape with Label: - 1. Generate unique IDs (shape-id, text-id, group-id) - 2. Create shape with groupIds: [group-id] - 3. Calculate text width: (text.length ร— fontSize ร— 0.6) + 20, round to nearest 10 - 4. Create text element with: - - containerId: shape-id - - groupIds: [group-id] (SAME as shape) - - textAlign: "center" - - verticalAlign: "middle" - - width: calculated width - 5. Add boundElements to shape referencing text - - - For Each Arrow: - 1. Determine arrow type needed: - - Straight: For forward flow (left-to-right, top-to-bottom) - - Elbow: For upward flow, backward flow, or complex routing - 2. Create arrow with startBinding and endBinding - 3. Set startBinding.elementId to source shape ID - 4. Set endBinding.elementId to target shape ID - 5. Set gap: 10 for both bindings - 6. If elbow arrow, add intermediate points for direction changes - 7. Update boundElements on both connected shapes - - - Alignment: - - Snap all x, y to 20px grid - - Align shapes vertically (same x for vertical flow) - - Space elements: 60px between shapes - - - Build Order: - 1. Start point (circle) with label - 2. Each process step (rectangle) with label - 3. Each decision point (diamond) with label - 4. End point (circle) with label - 5. Connect all with bound arrows - - - - - Strip unused elements and elements with isDeleted: true - Save to {{default_output_file}} - - - - NEVER delete the file if validation fails - always fix syntax errors - Run: node -e "JSON.parse(require('fs').readFileSync('{{default_output_file}}', 'utf8')); console.log('โœ“ Valid JSON')" - - Read the error message carefully - it shows the syntax error and position - Open the file and navigate to the error location - Fix the syntax error (add missing comma, bracket, or quote as indicated) - Save the file - Re-run validation with the same command - Repeat until validation passes - - Once validation passes, confirm with user: "Flowchart created at {{default_output_file}}. Open to view?" - - - - Validate against checklist at {{validation}} using {_bmad}/core/tasks/validate-workflow.xml - - - -``` diff --git a/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml b/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml deleted file mode 100644 index 6079d6de2..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: create-excalidraw-flowchart -description: "Create a flowchart visualization in Excalidraw format for processes, pipelines, or logic flows" -author: "BMad" - -# Config values -config_source: "{project-root}/_bmad/bmm/config.yaml" -output_folder: "{config_source}:output_folder" - -# Workflow components -installed_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart" -shared_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/_shared" -instructions: "{installed_path}/instructions.md" -validation: "{installed_path}/checklist.md" - -# Core Excalidraw resources (universal knowledge) -helpers: "{project-root}/_bmad/core/resources/excalidraw/excalidraw-helpers.md" -json_validation: "{project-root}/_bmad/core/resources/excalidraw/validate-json-instructions.md" - -# Domain-specific resources (technical diagrams) -templates: "{shared_path}/excalidraw-templates.yaml" -library: "{shared_path}/excalidraw-library.json" - -# Output file (respects user's configured output_folder) -default_output_file: "{output_folder}/excalidraw-diagrams/flowchart-{timestamp}.excalidraw" - -standalone: true -web_bundle: false diff --git a/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md b/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md deleted file mode 100644 index 3e2b26f41..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +++ /dev/null @@ -1,38 +0,0 @@ -# Create Wireframe - Validation Checklist - -## Layout Structure - -- [ ] Screen dimensions appropriate for device type -- [ ] Grid alignment (20px) maintained -- [ ] Consistent spacing between UI elements -- [ ] Proper hierarchy (header, content, footer) - -## UI Elements - -- [ ] All interactive elements clearly marked -- [ ] Buttons, inputs, and controls properly sized -- [ ] Text labels readable and appropriately sized -- [ ] Navigation elements clearly indicated - -## Fidelity - -- [ ] Matches requested fidelity level (low/medium/high) -- [ ] Appropriate level of detail -- [ ] Placeholder content used where needed -- [ ] No unnecessary decoration for low-fidelity - -## Annotations - -- [ ] Key interactions annotated -- [ ] Flow indicators present if multi-screen -- [ ] Important notes included -- [ ] Element purposes clear - -## Technical Quality - -- [ ] All elements properly grouped -- [ ] Text elements have containerId -- [ ] Snapped to grid -- [ ] No elements with `isDeleted: true` -- [ ] JSON is valid -- [ ] File saved to correct location diff --git a/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md b/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md deleted file mode 100644 index dc9506b0d..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +++ /dev/null @@ -1,133 +0,0 @@ -# Create Wireframe - Workflow Instructions - -```xml -The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml -You MUST have already loaded and processed: {installed_path}/workflow.yaml -This workflow creates website or app wireframes in Excalidraw format. - - - - - Review user's request and extract: wireframe type, fidelity level, screen count, device type, save location - Skip to Step 5 - - - - Ask: "What type of wireframe do you need?" - Present options: - 1. Website (Desktop) - 2. Mobile App (iOS/Android) - 3. Web App (Responsive) - 4. Tablet App - 5. Multi-platform - - WAIT for selection - - - - Ask fidelity level (Low/Medium/High) - Ask screen count (Single/Few 2-3/Multiple 4-6/Many 7+) - Ask device dimensions or use standard - Ask save location - - - - Check for existing theme.json, ask to use if exists - - - - Ask: "Choose a wireframe style:" - Present numbered options: - 1. Classic Wireframe - - Background: #ffffff (white) - - Container: #f5f5f5 (light gray) - - Border: #9e9e9e (gray) - - Text: #424242 (dark gray) - - 2. High Contrast - - Background: #ffffff (white) - - Container: #eeeeee (light gray) - - Border: #212121 (black) - - Text: #000000 (black) - - 3. Blueprint Style - - Background: #1a237e (dark blue) - - Container: #3949ab (blue) - - Border: #7986cb (light blue) - - Text: #ffffff (white) - - 4. Custom - Define your own colors - - WAIT for selection - Create theme.json based on selection - Confirm with user - - - - List all screens and their purposes - Map navigation flow between screens - Identify key UI elements for each screen - Show planned structure, confirm with user - - - - Load {{templates}} and extract `wireframe` section - Load {{library}} - Load theme.json - Load {{helpers}} - - - - Follow {{helpers}} for proper element creation - - For Each Screen: - - Create container/frame - - Add header section - - Add content areas - - Add navigation elements - - Add interactive elements (buttons, inputs) - - Add labels and annotations - - - Build Order: - 1. Screen containers - 2. Layout sections (header, content, footer) - 3. Navigation elements - 4. Content blocks - 5. Interactive elements - 6. Labels and annotations - 7. Flow indicators (if multi-screen) - - - Fidelity Guidelines: - - Low: Basic shapes, minimal detail, placeholder text - - Medium: More defined elements, some styling, representative content - - High: Detailed elements, realistic sizing, actual content examples - - - - - Strip unused elements and elements with isDeleted: true - Save to {{default_output_file}} - - - - NEVER delete the file if validation fails - always fix syntax errors - Run: node -e "JSON.parse(require('fs').readFileSync('{{default_output_file}}', 'utf8')); console.log('โœ“ Valid JSON')" - - Read the error message carefully - it shows the syntax error and position - Open the file and navigate to the error location - Fix the syntax error (add missing comma, bracket, or quote as indicated) - Save the file - Re-run validation with the same command - Repeat until validation passes - - Once validation passes, confirm with user - - - - Validate against {{validation}} - - - -``` diff --git a/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml b/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml deleted file mode 100644 index d89005a75..000000000 --- a/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: create-excalidraw-wireframe -description: "Create website or app wireframes in Excalidraw format" -author: "BMad" - -# Config values -config_source: "{project-root}/_bmad/bmm/config.yaml" -output_folder: "{config_source}:output_folder" - -# Workflow components -installed_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe" -shared_path: "{project-root}/_bmad/bmm/workflows/excalidraw-diagrams/_shared" -instructions: "{installed_path}/instructions.md" -validation: "{installed_path}/checklist.md" - -# Core Excalidraw resources (universal knowledge) -helpers: "{project-root}/_bmad/core/resources/excalidraw/excalidraw-helpers.md" -json_validation: "{project-root}/_bmad/core/resources/excalidraw/validate-json-instructions.md" - -# Domain-specific resources (technical diagrams) -templates: "{shared_path}/excalidraw-templates.yaml" -library: "{shared_path}/excalidraw-library.json" - -# Output file (respects user's configured output_folder) -default_output_file: "{output_folder}/excalidraw-diagrams/wireframe-{timestamp}.excalidraw" - -standalone: true -web_bundle: false diff --git a/src/bmm/workflows/qa/automate/workflow.yaml b/src/bmm/workflows/qa/automate/workflow.yaml index b08727462..847365d7b 100644 --- a/src/bmm/workflows/qa/automate/workflow.yaml +++ b/src/bmm/workflows/qa/automate/workflow.yaml @@ -45,5 +45,3 @@ execution_hints: interactive: false autonomous: true iterative: false - -web_bundle: false diff --git a/src/core/resources/excalidraw/README.md b/src/core/resources/excalidraw/README.md deleted file mode 100644 index c3840bea3..000000000 --- a/src/core/resources/excalidraw/README.md +++ /dev/null @@ -1,160 +0,0 @@ -# Core Excalidraw Resources - -Universal knowledge for creating Excalidraw diagrams. All agents that create Excalidraw files should reference these resources. - -## Purpose - -Provides the **HOW** (universal knowledge) while agents provide the **WHAT** (domain-specific application). - -**Core = "How to create Excalidraw elements"** - -- How to group shapes with text labels -- How to calculate text width -- How to create arrows with proper bindings -- How to validate JSON syntax -- Base structure and primitives - -**Agents = "What diagrams to create"** - -- Frame Expert (BMM): Technical flowcharts, architecture diagrams, wireframes -- Presentation Master (CIS): Pitch decks, creative visuals, Rube Goldberg machines -- Tech Writer (BMM): Documentation diagrams, concept explanations - -## Files in This Directory - -### excalidraw-helpers.md - -**Universal element creation patterns** - -- Text width calculation -- Element grouping rules (shapes + labels) -- Grid alignment -- Arrow creation (straight, elbow) -- Theme application -- Validation checklist -- Optimization rules - -**Agents reference this to:** - -- Create properly grouped shapes -- Calculate text dimensions -- Connect elements with arrows -- Ensure valid structure - -### validate-json-instructions.md - -**Universal JSON validation process** - -- How to validate Excalidraw JSON -- Common errors and fixes -- Workflow integration -- Error recovery - -**Agents reference this to:** - -- Validate files after creation -- Fix syntax errors -- Ensure files can be opened in Excalidraw - -### library-loader.md (Future) - -**How to load external .excalidrawlib files** - -- Programmatic library loading -- Community library integration -- Custom library management - -**Status:** To be developed when implementing external library support. - -## How Agents Use These Resources - -### Example: Frame Expert (Technical Diagrams) - -```yaml -# workflows/excalidraw-diagrams/create-flowchart/workflow.yaml -helpers: '{project-root}/_bmad/core/resources/excalidraw/excalidraw-helpers.md' -json_validation: '{project-root}/_bmad/core/resources/excalidraw/validate-json-instructions.md' -``` - -**Domain-specific additions:** - -```yaml -# workflows/excalidraw-diagrams/_shared/flowchart-templates.yaml -flowchart: - start_node: - type: ellipse - width: 120 - height: 60 - process_box: - type: rectangle - width: 160 - height: 80 - decision_diamond: - type: diamond - width: 140 - height: 100 -``` - -### Example: Presentation Master (Creative Visuals) - -```yaml -# workflows/create-visual-metaphor/workflow.yaml -helpers: '{project-root}/_bmad/core/resources/excalidraw/excalidraw-helpers.md' -json_validation: '{project-root}/_bmad/core/resources/excalidraw/validate-json-instructions.md' -``` - -**Domain-specific additions:** - -```yaml -# workflows/_shared/creative-templates.yaml -rube_goldberg: - whimsical_connector: - type: arrow - strokeStyle: dashed - roughness: 2 - playful_box: - type: rectangle - roundness: 12 -``` - -## What Doesn't Belong in Core - -**Domain-Specific Elements:** - -- Flowchart-specific templates (belongs in Frame Expert) -- Pitch deck layouts (belongs in Presentation Master) -- Documentation-specific styles (belongs in Tech Writer) - -**Agent Workflows:** - -- How to create a flowchart (Frame Expert workflow) -- How to create a pitch deck (Presentation Master workflow) -- Step-by-step diagram creation (agent-specific) - -**Theming:** - -- Currently in agent workflows -- **Future:** Will be refactored to core as user-configurable themes - -## Architecture Principle - -**Single Source of Truth:** - -- Core holds universal knowledge -- Agents reference core, don't duplicate -- Updates to core benefit all agents -- Agents specialize with domain knowledge - -**DRY (Don't Repeat Yourself):** - -- Element creation logic: ONCE in core -- Text width calculation: ONCE in core -- Validation process: ONCE in core -- Arrow binding patterns: ONCE in core - -## Future Enhancements - -1. **External Library Loader** - Load .excalidrawlib files from libraries.excalidraw.com -2. **Theme Management** - User-configurable color themes saved in core -3. **Component Library** - Shared reusable components across agents -4. **Layout Algorithms** - Auto-layout helpers for positioning elements diff --git a/src/core/resources/excalidraw/excalidraw-helpers.md b/src/core/resources/excalidraw/excalidraw-helpers.md deleted file mode 100644 index 362646800..000000000 --- a/src/core/resources/excalidraw/excalidraw-helpers.md +++ /dev/null @@ -1,127 +0,0 @@ -# Excalidraw Element Creation Guidelines - -## Text Width Calculation - -For text elements inside shapes (labels): - -``` -text_width = (text.length ร— fontSize ร— 0.6) + 20 -``` - -Round to nearest 10 for grid alignment. - -## Element Grouping Rules - -**CRITICAL:** When creating shapes with labels: - -1. Generate unique IDs: - - `shape-id` for the shape - - `text-id` for the text - - `group-id` for the group - -2. Shape element must have: - - `groupIds: [group-id]` - - `boundElements: [{type: "text", id: text-id}]` - -3. Text element must have: - - `containerId: shape-id` - - `groupIds: [group-id]` (SAME as shape) - - `textAlign: "center"` - - `verticalAlign: "middle"` - - `width: calculated_width` - -## Grid Alignment - -- Snap all `x`, `y` coordinates to 20px grid -- Formula: `Math.round(value / 20) * 20` -- Spacing between elements: 60px minimum - -## Arrow Creation - -### Straight Arrows - -Use for forward flow (left-to-right, top-to-bottom): - -```json -{ - "type": "arrow", - "startBinding": { - "elementId": "source-shape-id", - "focus": 0, - "gap": 10 - }, - "endBinding": { - "elementId": "target-shape-id", - "focus": 0, - "gap": 10 - }, - "points": [[0, 0], [distance_x, distance_y]] -} -``` - -### Elbow Arrows - -Use for upward flow, backward flow, or complex routing: - -```json -{ - "type": "arrow", - "startBinding": {...}, - "endBinding": {...}, - "points": [ - [0, 0], - [intermediate_x, 0], - [intermediate_x, intermediate_y], - [final_x, final_y] - ], - "elbowed": true -} -``` - -### Update Connected Shapes - -After creating arrow, update `boundElements` on both connected shapes: - -```json -{ - "id": "shape-id", - "boundElements": [ - { "type": "text", "id": "text-id" }, - { "type": "arrow", "id": "arrow-id" } - ] -} -``` - -## Theme Application - -Theme colors should be applied consistently: - -- **Shapes**: `backgroundColor` from theme primary fill -- **Borders**: `strokeColor` from theme accent -- **Text**: `strokeColor` = "#1e1e1e" (dark text) -- **Arrows**: `strokeColor` from theme accent - -## Validation Checklist - -Before saving, verify: - -- [ ] All shapes with labels have matching `groupIds` -- [ ] All text elements have `containerId` pointing to parent shape -- [ ] Text width calculated properly (no cutoff) -- [ ] Text alignment set (`textAlign` + `verticalAlign`) -- [ ] All elements snapped to 20px grid -- [ ] All arrows have `startBinding` and `endBinding` -- [ ] `boundElements` array updated on connected shapes -- [ ] Theme colors applied consistently -- [ ] No metadata or history in final output -- [ ] All IDs are unique - -## Optimization - -Remove from final output: - -- `appState` object -- `files` object (unless images used) -- All elements with `isDeleted: true` -- Unused library items -- Version history diff --git a/src/core/resources/excalidraw/library-loader.md b/src/core/resources/excalidraw/library-loader.md deleted file mode 100644 index 6fe5ea070..000000000 --- a/src/core/resources/excalidraw/library-loader.md +++ /dev/null @@ -1,50 +0,0 @@ -# External Library Loader - -**Status:** Placeholder for future implementation - -## Purpose - -Load external .excalidrawlib files from or custom sources. - -## Planned Capabilities - -- Load libraries by URL -- Load libraries from local files -- Merge multiple libraries -- Filter library components -- Cache loaded libraries - -## API Reference - -Will document how to use: - -- `importLibrary(url)` - Load library from URL -- `loadSceneOrLibraryFromBlob()` - Load from file -- `mergeLibraryItems()` - Combine libraries - -## Usage Example - -```yaml -# Future workflow.yaml structure -libraries: - - url: 'https://libraries.excalidraw.com/libraries/...' - filter: ['aws', 'cloud'] - - path: '{project-root}/_data/custom-library.excalidrawlib' -``` - -## Implementation Notes - -This will be developed when agents need to leverage the extensive library ecosystem available at . - -Hundreds of pre-built component libraries exist for: - -- AWS/Cloud icons -- UI/UX components -- Business diagrams -- Mind map shapes -- Floor plans -- And much more... - -## User Configuration - -Future: Users will be able to configure favorite libraries in their BMAD config for automatic loading. diff --git a/src/core/resources/excalidraw/validate-json-instructions.md b/src/core/resources/excalidraw/validate-json-instructions.md deleted file mode 100644 index 3abf3fc36..000000000 --- a/src/core/resources/excalidraw/validate-json-instructions.md +++ /dev/null @@ -1,79 +0,0 @@ -# JSON Validation Instructions - -## Purpose - -Validate Excalidraw JSON files after saving to catch syntax errors (missing commas, brackets, quotes). - -## How to Validate - -Use Node.js built-in JSON parsing to validate the file: - -```bash -node -e "JSON.parse(require('fs').readFileSync('FILE_PATH', 'utf8')); console.log('โœ“ Valid JSON')" -``` - -Replace `FILE_PATH` with the actual file path. - -## Exit Codes - -- Exit code 0 = Valid JSON -- Exit code 1 = Invalid JSON (syntax error) - -## Error Output - -If invalid, Node.js will output: - -- Error message with description -- Position in file where error occurred -- Line and column information (if available) - -## Common Errors and Fixes - -### Missing Comma - -``` -SyntaxError: Expected ',' or '}' after property value -``` - -**Fix:** Add comma after the property value - -### Missing Bracket/Brace - -``` -SyntaxError: Unexpected end of JSON input -``` - -**Fix:** Add missing closing bracket `]` or brace `}` - -### Extra Comma (Trailing) - -``` -SyntaxError: Unexpected token , -``` - -**Fix:** Remove the trailing comma before `]` or `}` - -### Missing Quote - -``` -SyntaxError: Unexpected token -``` - -**Fix:** Add missing quote around string value - -## Workflow Integration - -After saving an Excalidraw file, run validation: - -1. Save the file -2. Run: `node -e "JSON.parse(require('fs').readFileSync('{{save_location}}', 'utf8')); console.log('โœ“ Valid JSON')"` -3. If validation fails: - - Read the error message for line/position - - Open the file at that location - - Fix the syntax error - - Save and re-validate -4. Repeat until validation passes - -## Critical Rule - -**NEVER delete the file due to validation errors - always fix the syntax error at the reported location.** diff --git a/src/core/tasks/editorial-review-prose.xml b/src/core/tasks/editorial-review-prose.xml index 7ef28f904..deb53570e 100644 --- a/src/core/tasks/editorial-review-prose.xml +++ b/src/core/tasks/editorial-review-prose.xml @@ -1,7 +1,6 @@ + description="Clinical copy-editor that reviews text for communication issues"> Review text for communication issues that impede comprehension and output suggested fixes in a three-column table @@ -10,7 +9,7 @@ + is the final authority on tone, structure, and language choices." /> @@ -62,7 +61,8 @@ - Consult style_guide now and note its key requirementsโ€”these override default principles for this review + Consult style_guide now and note its key requirementsโ€”these override default principles for this + review Review all prose sections (skip code blocks, frontmatter, structural markup) Identify communication issues that impede comprehension For each issue, determine the minimal fix that achieves clarity @@ -77,16 +77,18 @@ Output: "No editorial issues identified" -| Original Text | Revised Text | Changes | -|---------------|--------------|---------| -| The exact original passage | The suggested revision | Brief explanation of what changed and why | + | Original Text | Revised Text | Changes | + |---------------|--------------|---------| + | The exact original passage | The suggested revision | Brief explanation of what changed and why | -| Original Text | Revised Text | Changes | -|---------------|--------------|---------| -| The system will processes data and it handles errors. | The system processes data and handles errors. | Fixed subject-verb agreement ("will processes" to "processes"); removed redundant "it" | -| Users can chose from options (lines 12, 45, 78) | Users can choose from options | Fixed spelling: "chose" to "choose" (appears in 3 locations) | + | Original Text | Revised Text | Changes | + |---------------|--------------|---------| + | The system will processes data and it handles errors. | The system processes data and handles errors. | Fixed subject-verb + agreement ("will processes" to "processes"); removed redundant "it" | + | Users can chose from options (lines 12, 45, 78) | Users can choose from options | Fixed spelling: "chose" to "choose" (appears in + 3 locations) | @@ -97,4 +99,4 @@ If no issues found after thorough review, output "No editorial issues identified" (this is valid completion, not an error) - + \ No newline at end of file diff --git a/src/core/tasks/editorial-review-structure.xml b/src/core/tasks/editorial-review-structure.xml index aac169ee1..426dc3c8c 100644 --- a/src/core/tasks/editorial-review-structure.xml +++ b/src/core/tasks/editorial-review-structure.xml @@ -4,29 +4,28 @@ + and simplification while preserving comprehension"> Review document structure and propose substantive changes to improve clarity and flow-run this BEFORE copy editing + desc="Document to review (markdown, plain text, or structured content)" /> + is the final authority on tone, structure, and language choices." /> + 'API reference', 'conceptual overview')" /> + 'decision makers')" /> + 'llm' optimizes for precision and density" /> + 'no limit')" /> MANDATORY: Execute ALL steps in the flow section IN EXACT ORDER @@ -69,7 +68,7 @@ Cut emotional language, encouragement, and orientation sections IF concept is well-known from training (e.g., "conventional - commits", "REST APIs"): Reference the standard-don't re-teach it + commits", "REST APIs"): Reference the standard-don't re-teach it ELSE: Be explicit-don't assume the LLM will infer correctly Use consistent terminology-same word for same concept throughout @@ -132,7 +131,8 @@ Note reader_type and which principles apply (human-reader-principles or llm-reader-principles) - Consult style_guide now and note its key requirementsโ€”these override default principles for this analysis + Consult style_guide now and note its key requirementsโ€”these override default principles for this + analysis Map the document structure: list each major section with its word count Evaluate structure against the selected model's primary rules (e.g., 'Does recommendation come first?' for Pyramid) @@ -176,27 +176,27 @@ Output estimated total reduction if all recommendations accepted Output: "No substantive changes recommended-document structure is sound" -## Document Summary -- **Purpose:** [inferred or provided purpose] -- **Audience:** [inferred or provided audience] -- **Reader type:** [selected reader type] -- **Structure model:** [selected structure model] -- **Current length:** [X] words across [Y] sections + ## Document Summary + - **Purpose:** [inferred or provided purpose] + - **Audience:** [inferred or provided audience] + - **Reader type:** [selected reader type] + - **Structure model:** [selected structure model] + - **Current length:** [X] words across [Y] sections -## Recommendations + ## Recommendations -### 1. [CUT/MERGE/MOVE/CONDENSE/QUESTION/PRESERVE] - [Section or element name] -**Rationale:** [One sentence explanation] -**Impact:** ~[X] words -**Comprehension note:** [If applicable, note impact on reader understanding] + ### 1. [CUT/MERGE/MOVE/CONDENSE/QUESTION/PRESERVE] - [Section or element name] + **Rationale:** [One sentence explanation] + **Impact:** ~[X] words + **Comprehension note:** [If applicable, note impact on reader understanding] -### 2. ... + ### 2. ... -## Summary -- **Total recommendations:** [N] -- **Estimated reduction:** [X] words ([Y]% of original) -- **Meets length target:** [Yes/No/No target specified] -- **Comprehension trade-offs:** [Note any cuts that sacrifice reader engagement for brevity] + ## Summary + - **Total recommendations:** [N] + - **Estimated reduction:** [X] words ([Y]% of original) + - **Meets length target:** [Yes/No/No target specified] + - **Comprehension trade-offs:** [Note any cuts that sacrifice reader engagement for brevity] @@ -206,4 +206,4 @@ If no structural issues found, output "No substantive changes recommended" (this is valid completion, not an error) - + \ No newline at end of file diff --git a/src/core/tasks/help.md b/src/core/tasks/help.md index 3df95fd56..c3c3fab11 100644 --- a/src/core/tasks/help.md +++ b/src/core/tasks/help.md @@ -1,12 +1,11 @@ --- name: help description: Get unstuck by showing what workflow steps come next or answering questions about what to do -standalone: true --- # Task: BMAD Help -## KEY RULES +## ROUTING RULES - **Empty `phase` = anytime** โ€” Universal tools work regardless of workflow state - **Numbered phases indicate sequence** โ€” Phases like `1-discover` โ†’ `2-define` โ†’ `3-build` โ†’ `4-ship` flow in order (naming varies by module) @@ -15,6 +14,26 @@ standalone: true - **`required=true` blocks progress** โ€” Required workflows must complete before proceeding to later phases - **Artifacts reveal completion** โ€” Search resolved output paths for `outputs` patterns, fuzzy-match found files to workflow rows +## DISPLAY RULES + +### Command-Based Workflows +When `command` field has a value: +- Show the command prefixed with `/` (e.g., `/bmad-bmm-create-prd`) + +### Agent-Based Workflows +When `command` field is empty: +- User loads agent first via `/agent-command` +- Then invokes by referencing the `code` field or describing the `name` field +- Do NOT show a slash command โ€” show the code value and agent load instruction instead + +Example presentation for empty command: +``` +Explain Concept (EC) +Load: /tech-writer, then ask to "EC about [topic]" +Agent: Tech Writer +Description: Create clear technical explanations with examples... +``` + ## MODULE DETECTION - **Empty `module` column** โ†’ universal tools (work across all modules) @@ -25,38 +44,42 @@ Detect the active module from conversation context, recent workflows, or user qu ## INPUT ANALYSIS Determine what was just completed: -- Did someone state they completed something? Proceed as if that was the input. -- Was a workflow just completed in this conversation? Proceed as if that was the input. -- Search resolved artifact locations for files; fuzzy-match to workflow `outputs` patterns. -- If an `index.md` exists, read it for additional context. +- Explicit completion stated by user +- Workflow completed in current conversation +- Artifacts found matching `outputs` patterns +- If `index.md` exists, read it for additional context - If still unclear, ask: "What workflow did you most recently complete?" ## EXECUTION 1. **Load catalog** โ€” Load `{project-root}/_bmad/_config/bmad-help.csv` -2. **Resolve output locations** โ€” Scan each folder under `_bmad/` (except `_config`) for `config.yaml`. For each workflow row, resolve its `output-location` variables against that module's config so artifact paths can be searched. +2. **Resolve output locations and config** โ€” Scan each folder under `_bmad/` (except `_config`) for `config.yaml`. For each workflow row, resolve its `output-location` variables against that module's config so artifact paths can be searched. Also extract `communication_language` and `project_knowledge` from each scanned module's config. -3. **Analyze input** โ€” Task may provide a workflow name/code, conversational phrase, or nothing. Infer what was just completed using INPUT ANALYSIS above. +3. **Ground in project knowledge** โ€” If `project_knowledge` resolves to an existing path, read available documentation files (architecture docs, project overview, tech stack references) for grounding context. Use discovered project facts when composing any project-specific output. Never fabricate project-specific details โ€” if documentation is unavailable, state so. -4. **Detect active module** โ€” Use MODULE DETECTION above to determine which module the user is working in. +4. **Detect active module** โ€” Use MODULE DETECTION above -5. **Present recommendations** โ€” Show next steps based on completed workflows, phase/sequence ordering (KEY RULES), and artifact detection. Format per the following +5. **Analyze input** โ€” Task may provide a workflow name/code, conversational phrase, or nothing. Infer what was just completed using INPUT ANALYSIS above. -## RECOMMENDED OUTPUT FORMAT +6. **Present recommendations** โ€” Show next steps based on: + - Completed workflows detected + - Phase/sequence ordering (ROUTING RULES) + - Artifact presence **Optional items first** โ€” List optional workflows until a required step is reached **Required items next** โ€” List the next required workflow - For each item show: + + For each item, apply DISPLAY RULES above and include: - Workflow **name** - - **Command** (prefixed with `/`, e.g., `/bmad:example:build-prototype`) + - **Command** OR **Code + Agent load instruction** (per DISPLAY RULES) - **Agent** title and display name from the CSV (e.g., "๐ŸŽจ Alex (Designer)") - Brief **description** - ### Additional response output guidance to convey: +7. **Additional guidance to convey**: + - Present all output in `{communication_language}` - Run each workflow in a **fresh context window** - - Load the agent using (`/` + `agent-command`), or run the workflow command directly - For **validation workflows**: recommend using a different high-quality LLM if available - For conversational requests: match the user's tone while presenting clearly -6. Return to the calling process after presenting recommendations. +8. Return to the calling process after presenting recommendations. diff --git a/src/core/tasks/index-docs.xml b/src/core/tasks/index-docs.xml index ff9a7de08..30e060921 100644 --- a/src/core/tasks/index-docs.xml +++ b/src/core/tasks/index-docs.xml @@ -1,5 +1,5 @@ + description="Generates or updates an index.md of all documents in the specified directory"> MANDATORY: Execute ALL steps in the flow section IN EXACT ORDER DO NOT skip steps or change the sequence diff --git a/src/core/tasks/review-adversarial-general.xml b/src/core/tasks/review-adversarial-general.xml index 0ebe5cdfa..421719bb5 100644 --- a/src/core/tasks/review-adversarial-general.xml +++ b/src/core/tasks/review-adversarial-general.xml @@ -1,7 +1,7 @@ - + Cynically review content and produce findings @@ -45,4 +45,4 @@ HALT if content is empty or unreadable - + \ No newline at end of file diff --git a/src/core/tasks/shard-doc.xml b/src/core/tasks/shard-doc.xml index cd1dd6748..1dc8fe80e 100644 --- a/src/core/tasks/shard-doc.xml +++ b/src/core/tasks/shard-doc.xml @@ -1,6 +1,5 @@ + description="Splits large markdown documents into smaller, organized files based on level 2 (default) sections"> Split large markdown documents into smaller, organized files based on level 2 sections using @kayvan/markdown-tree-parser tool diff --git a/src/core/tasks/workflow.xml b/src/core/tasks/workflow.xml index 8c55ec37f..536c9d8e7 100644 --- a/src/core/tasks/workflow.xml +++ b/src/core/tasks/workflow.xml @@ -1,4 +1,4 @@ - + Execute given workflow by loading its configuration, following instructions, and producing output diff --git a/src/core/workflows/advanced-elicitation/workflow.xml b/src/core/workflows/advanced-elicitation/workflow.xml index 8a348d9ee..ea7395e41 100644 --- a/src/core/workflows/advanced-elicitation/workflow.xml +++ b/src/core/workflows/advanced-elicitation/workflow.xml @@ -1,4 +1,4 @@ - diff --git a/tools/cli/bmad-cli.js b/tools/cli/bmad-cli.js index ad3aac341..2a5b8d387 100755 --- a/tools/cli/bmad-cli.js +++ b/tools/cli/bmad-cli.js @@ -1,6 +1,48 @@ const { program } = require('commander'); const path = require('node:path'); const fs = require('node:fs'); +const { execSync } = require('node:child_process'); + +// Check for updates - do this asynchronously so it doesn't block startup +const packageJson = require('../../package.json'); +const packageName = 'bmad-method'; +checkForUpdate().catch(() => { + // Silently ignore errors - version check is best-effort +}); + +async function checkForUpdate() { + try { + // For beta versions, check the beta tag; otherwise check latest + const isBeta = + packageJson.version.includes('Beta') || + packageJson.version.includes('beta') || + packageJson.version.includes('alpha') || + packageJson.version.includes('rc'); + const tag = isBeta ? 'beta' : 'latest'; + + const result = execSync(`npm view ${packageName}@${tag} version`, { + encoding: 'utf8', + stdio: 'pipe', + timeout: 5000, + }).trim(); + + if (result && result !== packageJson.version) { + console.warn(''); + console.warn(' โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—'); + console.warn(' โ•‘ UPDATE AVAILABLE โ•‘'); + console.warn(' โ•‘ โ•‘'); + console.warn(` โ•‘ You are using version ${packageJson.version} but ${result} is available. โ•‘`); + console.warn(' โ•‘ โ•‘'); + console.warn(' โ•‘ To update,exir and first run: โ•‘'); + console.warn(` โ•‘ npm cache clean --force && npx bmad-method@${tag} install โ•‘`); + console.warn(' โ•‘ โ•‘'); + console.warn(' โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.warn(''); + } + } catch { + // Silently fail - network issues or npm not available + } +} // Fix for stdin issues when running through npm on Windows // Ensures keyboard interaction works properly with CLI prompts @@ -20,9 +62,6 @@ if (process.stdin.isTTY) { } } -// Load package.json from root for version info -const packageJson = require('../../package.json'); - // Load all command modules const commandsPath = path.join(__dirname, 'commands'); const commandFiles = fs.readdirSync(commandsPath).filter((file) => file.endsWith('.js')); diff --git a/tools/cli/installers/lib/core/manifest-generator.js b/tools/cli/installers/lib/core/manifest-generator.js index fcaee8ada..caea790eb 100644 --- a/tools/cli/installers/lib/core/manifest-generator.js +++ b/tools/cli/installers/lib/core/manifest-generator.js @@ -159,7 +159,11 @@ class ManifestGenerator { // Recurse into subdirectories const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name; await findWorkflows(fullPath, newRelativePath); - } else if (entry.name === 'workflow.yaml' || entry.name === 'workflow.md') { + } else if ( + entry.name === 'workflow.yaml' || + entry.name === 'workflow.md' || + (entry.name.startsWith('workflow-') && entry.name.endsWith('.md')) + ) { // Parse workflow file (both YAML and MD formats) if (debug) { console.log(`[DEBUG] Found workflow file: ${fullPath}`); @@ -409,10 +413,14 @@ class ManifestGenerator { name = frontmatter.name || name; displayName = frontmatter.displayName || frontmatter.name || name; description = this.cleanForCSV(frontmatter.description || ''); - standalone = frontmatter.standalone === true || frontmatter.standalone === 'true'; + // Tasks are standalone by default unless explicitly false (internal=true is already filtered above) + standalone = frontmatter.standalone !== false && frontmatter.standalone !== 'false'; } catch { // If YAML parsing fails, use defaults + standalone = true; // Default to standalone } + } else { + standalone = true; // No frontmatter means standalone } } else { // For .xml tasks, extract from tag attributes @@ -423,8 +431,8 @@ class ManifestGenerator { const objMatch = content.match(/([^<]+)<\/objective>/); description = this.cleanForCSV(descMatch ? descMatch[1] : objMatch ? objMatch[1].trim() : ''); - const standaloneMatch = content.match(/]+standalone="true"/); - standalone = !!standaloneMatch; + const standaloneFalseMatch = content.match(/]+standalone="false"/); + standalone = !standaloneFalseMatch; } // Build relative path for installation @@ -503,10 +511,14 @@ class ManifestGenerator { name = frontmatter.name || name; displayName = frontmatter.displayName || frontmatter.name || name; description = this.cleanForCSV(frontmatter.description || ''); - standalone = frontmatter.standalone === true || frontmatter.standalone === 'true'; + // Tools are standalone by default unless explicitly false (internal=true is already filtered above) + standalone = frontmatter.standalone !== false && frontmatter.standalone !== 'false'; } catch { // If YAML parsing fails, use defaults + standalone = true; // Default to standalone } + } else { + standalone = true; // No frontmatter means standalone } } else { // For .xml tools, extract from tag attributes @@ -517,8 +529,8 @@ class ManifestGenerator { const objMatch = content.match(/([^<]+)<\/objective>/); description = this.cleanForCSV(descMatch ? descMatch[1] : objMatch ? objMatch[1].trim() : ''); - const standaloneMatch = content.match(/]+standalone="true"/); - standalone = !!standaloneMatch; + const standaloneFalseMatch = content.match(/]+standalone="false"/); + standalone = !standaloneFalseMatch; } // Build relative path for installation @@ -721,47 +733,15 @@ class ManifestGenerator { async writeWorkflowManifest(cfgDir) { const csvPath = path.join(cfgDir, 'workflow-manifest.csv'); const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`; - const parseCsvLine = (line) => { - const columns = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || []; - return columns.map((c) => c.replaceAll(/^"|"$/g, '')); - }; - - // Read existing manifest to preserve entries - const existingEntries = new Map(); - if (await fs.pathExists(csvPath)) { - const content = await fs.readFile(csvPath, 'utf8'); - const lines = content.split('\n').filter((line) => line.trim()); - - // Skip header - for (let i = 1; i < lines.length; i++) { - const line = lines[i]; - if (line) { - const parts = parseCsvLine(line); - if (parts.length >= 4) { - const [name, description, module, workflowPath] = parts; - existingEntries.set(`${module}:${name}`, { - name, - description, - module, - path: workflowPath, - }); - } - } - } - } // Create CSV header - standalone column removed, everything is canonicalized to 4 columns let csv = 'name,description,module,path\n'; - // Combine existing and new workflows + // Build workflows map from discovered workflows only + // Old entries are NOT preserved - the manifest reflects what actually exists on disk const allWorkflows = new Map(); - // Add existing entries - for (const [key, value] of existingEntries) { - allWorkflows.set(key, value); - } - - // Add/update new workflows + // Only add workflows that were actually discovered in this scan for (const workflow of this.workflows) { const key = `${workflow.module}:${workflow.name}`; allWorkflows.set(key, { diff --git a/tools/cli/installers/lib/ide/_base-ide.js b/tools/cli/installers/lib/ide/_base-ide.js index 4ae116772..dce8aee9f 100644 --- a/tools/cli/installers/lib/ide/_base-ide.js +++ b/tools/cli/installers/lib/ide/_base-ide.js @@ -352,13 +352,15 @@ class BaseIdeSetup { const workflowData = yaml.parse(content); if (workflowData && workflowData.name) { + // Workflows are standalone by default unless explicitly false + const standalone = workflowData.standalone !== false && workflowData.standalone !== 'false'; workflows.push({ name: workflowData.name, path: fullPath, relativePath: path.relative(dir, fullPath), filename: entry.name, description: workflowData.description || '', - standalone: workflowData.standalone === true, // Check standalone property + standalone: standalone, }); } } catch { @@ -442,36 +444,38 @@ class BaseIdeSetup { const matchedExt = extensions.find((e) => entry.name.endsWith(e)); if (matchedExt) { // Read file content to check for standalone attribute - let standalone = false; + // All non-internal files are considered standalone by default + let standalone = true; try { const content = await fs.readFile(fullPath, 'utf8'); - // Skip internal/engine files (not user-facing tasks/tools) + // Skip internal/engine files (not user-facing) if (content.includes('internal="true"')) { continue; } - // Check for standalone="true" in XML files + // Check for explicit standalone: false if (entry.name.endsWith('.xml')) { - // Look for standalone="true" in the opening tag (task or tool) - const standaloneMatch = content.match(/<(?:task|tool)[^>]+standalone="true"/); - standalone = !!standaloneMatch; + // For XML files, check for standalone="false" attribute + const tagMatch = content.match(/<(task|tool)[^>]*standalone="false"/); + standalone = !tagMatch; } else if (entry.name.endsWith('.md')) { - // Check for standalone: true in YAML frontmatter - const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/); + // For MD files, parse YAML frontmatter + const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); if (frontmatterMatch) { - const yaml = require('yaml'); try { + const yaml = require('yaml'); const frontmatter = yaml.parse(frontmatterMatch[1]); - standalone = frontmatter.standalone === true; + standalone = frontmatter.standalone !== false && frontmatter.standalone !== 'false'; } catch { - // Ignore YAML parse errors + // If YAML parsing fails, default to standalone } } + // No frontmatter means standalone (default) } } catch { - // If we can't read the file, assume not standalone - standalone = false; + // If we can't read the file, default to standalone + standalone = true; } files.push({ diff --git a/tools/cli/installers/lib/ide/kilo.js b/tools/cli/installers/lib/ide/kilo.js index 45e380218..52fd17c90 100644 --- a/tools/cli/installers/lib/ide/kilo.js +++ b/tools/cli/installers/lib/ide/kilo.js @@ -1,7 +1,10 @@ const path = require('node:path'); const { BaseIdeSetup } = require('./_base-ide'); const chalk = require('chalk'); +const yaml = require('yaml'); const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); /** * KiloCode IDE setup handler @@ -22,76 +25,94 @@ class KiloSetup extends BaseIdeSetup { async setup(projectDir, bmadDir, options = {}) { console.log(chalk.cyan(`Setting up ${this.name}...`)); - // Check for existing .kilocodemodes file + // Clean up any old BMAD installation first + await this.cleanup(projectDir); + + // Load existing config (may contain non-BMAD modes and other settings) const kiloModesPath = path.join(projectDir, this.configFile); - let existingModes = []; - let existingContent = ''; + let config = {}; if (await this.pathExists(kiloModesPath)) { - existingContent = await this.readFile(kiloModesPath); - // Parse existing modes - const modeMatches = existingContent.matchAll(/- slug: ([\w-]+)/g); - for (const match of modeMatches) { - existingModes.push(match[1]); + const existingContent = await this.readFile(kiloModesPath); + try { + config = yaml.parse(existingContent) || {}; + } catch { + // If parsing fails, start fresh but warn user + console.log(chalk.yellow('Warning: Could not parse existing .kilocodemodes, starting fresh')); + config = {}; } - console.log(chalk.yellow(`Found existing .kilocodemodes file with ${existingModes.length} modes`)); + } + + // Ensure customModes array exists + if (!Array.isArray(config.customModes)) { + config.customModes = []; } // Generate agent launchers const agentGen = new AgentCommandGenerator(this.bmadFolderName); const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); - // Create modes content - let newModesContent = ''; + // Create mode objects and add to config let addedCount = 0; - let skippedCount = 0; for (const artifact of agentArtifacts) { - const slug = `bmad-${artifact.module}-${artifact.name}`; - - // Skip if already exists - if (existingModes.includes(slug)) { - console.log(chalk.dim(` Skipping ${slug} - already exists`)); - skippedCount++; - continue; - } - - const modeEntry = await this.createModeEntry(artifact, projectDir); - - newModesContent += modeEntry; + const modeObject = await this.createModeObject(artifact, projectDir); + config.customModes.push(modeObject); addedCount++; } - // Build final content - let finalContent = ''; - if (existingContent) { - finalContent = existingContent.trim() + '\n' + newModesContent; - } else { - finalContent = 'customModes:\n' + newModesContent; - } - - // Write .kilocodemodes file + // Write .kilocodemodes file with proper YAML structure + const finalContent = yaml.stringify(config, { lineWidth: 0 }); await this.writeFile(kiloModesPath, finalContent); + // Generate workflow commands + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + + // Write to .kilocode/workflows/ directory + const workflowsDir = path.join(projectDir, '.kilocode', 'workflows'); + await this.ensureDir(workflowsDir); + + // Clear old BMAD workflows before writing new ones + await this.clearBmadWorkflows(workflowsDir); + + // Write workflow files + const workflowCount = await workflowGenerator.writeDashArtifacts(workflowsDir, workflowArtifacts); + + // Generate task and tool commands + const taskToolGen = new TaskToolCommandGenerator(this.bmadFolderName); + const { artifacts: taskToolArtifacts, counts: taskToolCounts } = await taskToolGen.collectTaskToolArtifacts(bmadDir); + + // Write task/tool files to workflows directory (same location as workflows) + await taskToolGen.writeDashArtifacts(workflowsDir, taskToolArtifacts); + const taskCount = taskToolCounts.tasks || 0; + const toolCount = taskToolCounts.tools || 0; + console.log(chalk.green(`โœ“ ${this.name} configured:`)); console.log(chalk.dim(` - ${addedCount} modes added`)); - if (skippedCount > 0) { - console.log(chalk.dim(` - ${skippedCount} modes skipped (already exist)`)); - } + console.log(chalk.dim(` - ${workflowCount} workflows exported`)); + console.log(chalk.dim(` - ${taskCount} tasks exported`)); + console.log(chalk.dim(` - ${toolCount} tools exported`)); console.log(chalk.dim(` - Configuration file: ${this.configFile}`)); + console.log(chalk.dim(` - Workflows directory: .kilocode/workflows/`)); console.log(chalk.dim('\n Modes will be available when you open this project in KiloCode')); return { success: true, modes: addedCount, - skipped: skippedCount, + workflows: workflowCount, + tasks: taskCount, + tools: toolCount, }; } /** - * Create a mode entry for an agent + * Create a mode object for an agent + * @param {Object} artifact - Agent artifact + * @param {string} projectDir - Project directory + * @returns {Object} Mode object for YAML serialization */ - async createModeEntry(artifact, projectDir) { + async createModeObject(artifact, projectDir) { // Extract metadata from launcher content const titleMatch = artifact.content.match(/title="([^"]+)"/); const title = titleMatch ? titleMatch[1] : this.formatTitle(artifact.name); @@ -102,8 +123,8 @@ class KiloSetup extends BaseIdeSetup { const whenToUseMatch = artifact.content.match(/whenToUse="([^"]+)"/); const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`; - // Get the activation header from central template - const activationHeader = await this.getAgentCommandHeader(); + // Get the activation header from central template (trim to avoid YAML formatting issues) + const activationHeader = (await this.getAgentCommandHeader()).trim(); const roleDefinitionMatch = artifact.content.match(/roleDefinition="([^"]+)"/); const roleDefinition = roleDefinitionMatch @@ -113,22 +134,15 @@ class KiloSetup extends BaseIdeSetup { // Get relative path const relativePath = path.relative(projectDir, artifact.sourcePath).replaceAll('\\', '/'); - // Build mode entry (KiloCode uses same schema as Roo) - const slug = `bmad-${artifact.module}-${artifact.name}`; - 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; + // Build mode object (KiloCode uses same schema as Roo) + return { + slug: `bmad-${artifact.module}-${artifact.name}`, + name: `${icon} ${title}`, + roleDefinition: roleDefinition, + whenToUse: whenToUse, + customInstructions: `${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`, + groups: ['read', 'edit', 'browser', 'command', 'mcp'], + }; } /** @@ -141,6 +155,22 @@ class KiloSetup extends BaseIdeSetup { .join(' '); } + /** + * Clear old BMAD workflow files from workflows directory + * @param {string} workflowsDir - Workflows directory path + */ + async clearBmadWorkflows(workflowsDir) { + const fs = require('fs-extra'); + if (!(await fs.pathExists(workflowsDir))) return; + + const entries = await fs.readdir(workflowsDir); + for (const entry of entries) { + if (entry.startsWith('bmad-') && entry.endsWith('.md')) { + await fs.remove(path.join(workflowsDir, entry)); + } + } + } + /** * Cleanup KiloCode configuration */ @@ -151,28 +181,29 @@ class KiloSetup extends BaseIdeSetup { if (await fs.pathExists(kiloModesPath)) { const content = await fs.readFile(kiloModesPath, 'utf8'); - // Remove BMAD modes only - const lines = content.split('\n'); - const filteredLines = []; - let skipMode = false; - let removedCount = 0; + try { + const config = yaml.parse(content) || {}; - for (const line of lines) { - if (/^\s*- slug: bmad-/.test(line)) { - skipMode = true; - removedCount++; - } else if (skipMode && /^\s*- slug: /.test(line)) { - skipMode = false; - } + if (Array.isArray(config.customModes)) { + const originalCount = config.customModes.length; + // Remove BMAD modes only (keep non-BMAD modes) + config.customModes = config.customModes.filter((mode) => !mode.slug || !mode.slug.startsWith('bmad-')); + const removedCount = originalCount - config.customModes.length; - if (!skipMode) { - filteredLines.push(line); + if (removedCount > 0) { + await fs.writeFile(kiloModesPath, yaml.stringify(config, { lineWidth: 0 })); + console.log(chalk.dim(`Removed ${removedCount} BMAD modes from .kilocodemodes`)); + } } + } catch { + // If parsing fails, leave file as-is + console.log(chalk.yellow('Warning: Could not parse .kilocodemodes for cleanup')); } - - await fs.writeFile(kiloModesPath, filteredLines.join('\n')); - console.log(chalk.dim(`Removed ${removedCount} BMAD modes from .kilocodemodes`)); } + + // Clean up workflow files + const workflowsDir = path.join(projectDir, '.kilocode', 'workflows'); + await this.clearBmadWorkflows(workflowsDir); } /** @@ -185,31 +216,28 @@ class KiloSetup extends BaseIdeSetup { */ async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { const kilocodemodesPath = path.join(projectDir, this.configFile); - let existingContent = ''; + let config = {}; // Read existing .kilocodemodes file if (await this.pathExists(kilocodemodesPath)) { - existingContent = await this.readFile(kilocodemodesPath); + const existingContent = await this.readFile(kilocodemodesPath); + try { + config = yaml.parse(existingContent) || {}; + } catch { + config = {}; + } } - // Create custom agent mode entry + // Ensure customModes array exists + if (!Array.isArray(config.customModes)) { + config.customModes = []; + } + + // Create custom agent mode object const slug = `bmad-custom-${agentName.toLowerCase()}`; - const modeEntry = ` - slug: ${slug} - name: 'BMAD Custom: ${agentName}' - description: | - Custom BMAD agent: ${agentName} - - **โš ๏ธ IMPORTANT**: Run @${agentPath} first to load the complete agent! - - This is a launcher for the custom BMAD agent "${agentName}". The agent will follow the persona and instructions from the main agent file. - prompt: | - @${agentPath} - always: false - permissions: all -`; // Check if mode already exists - if (existingContent.includes(slug)) { + if (config.customModes.some((mode) => mode.slug === slug)) { return { ide: 'kilo', path: this.configFile, @@ -219,24 +247,18 @@ class KiloSetup extends BaseIdeSetup { }; } - // Build final content - let finalContent = ''; - if (existingContent) { - // Find customModes section or add it - if (existingContent.includes('customModes:')) { - // Append to existing customModes - finalContent = existingContent + modeEntry; - } else { - // Add customModes section - finalContent = existingContent.trim() + '\n\ncustomModes:\n' + modeEntry; - } - } else { - // Create new .kilocodemodes file with customModes - finalContent = 'customModes:\n' + modeEntry; - } + // Add custom mode object + config.customModes.push({ + slug: slug, + name: `BMAD Custom: ${agentName}`, + description: `Custom BMAD agent: ${agentName}\n\n**โš ๏ธ IMPORTANT**: Run @${agentPath} first to load the complete agent!\n\nThis is a launcher for the custom BMAD agent "${agentName}". The agent will follow the persona and instructions from the main agent file.\n`, + prompt: `@${agentPath}\n`, + always: false, + permissions: 'all', + }); - // Write .kilocodemodes file - await this.writeFile(kilocodemodesPath, finalContent); + // Write .kilocodemodes file with proper YAML structure + await this.writeFile(kilocodemodesPath, yaml.stringify(config, { lineWidth: 0 })); return { ide: 'kilo', diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index 2ca32aed5..b329d283c 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -124,8 +124,13 @@ platforms: category: ide description: "OpenCode terminal coding assistant" installer: - target_dir: .opencode/command - template_type: opencode + targets: + - target_dir: .opencode/agent + template_type: opencode + artifact_types: [agents] + - target_dir: .opencode/command + template_type: opencode + artifact_types: [workflows, tasks, tools] qwen: name: "QwenCoder" 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 60eb54687..2455c75c9 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 @@ -28,15 +28,12 @@ class TaskToolCommandGenerator { 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) : []; - + // All tasks/tools in manifest are standalone (internal=true items are filtered during manifest generation) const artifacts = []; const bmadPrefix = `${BMAD_FOLDER_NAME}/`; // Collect task artifacts - for (const task of standaloneTasks) { + for (const task of tasks || []) { let taskPath = (task.path || '').replaceAll('\\', '/'); // Convert absolute paths to relative paths if (path.isAbsolute(taskPath)) { @@ -61,7 +58,7 @@ class TaskToolCommandGenerator { } // Collect tool artifacts - for (const tool of standaloneTools) { + for (const tool of tools || []) { let toolPath = (tool.path || '').replaceAll('\\', '/'); // Convert absolute paths to relative paths if (path.isAbsolute(toolPath)) { @@ -88,8 +85,8 @@ class TaskToolCommandGenerator { return { artifacts, counts: { - tasks: standaloneTasks.length, - tools: standaloneTools.length, + tasks: (tasks || []).length, + tools: (tools || []).length, }, }; } @@ -104,17 +101,13 @@ class TaskToolCommandGenerator { 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) { + for (const task of tasks || []) { const moduleTasksDir = path.join(commandsDir, task.module, 'tasks'); await fs.ensureDir(moduleTasksDir); @@ -126,7 +119,7 @@ class TaskToolCommandGenerator { } // Generate command files for tools - for (const tool of standaloneTools) { + for (const tool of tools || []) { const moduleToolsDir = path.join(commandsDir, tool.module, 'tools'); await fs.ensureDir(moduleToolsDir); @@ -139,8 +132,8 @@ class TaskToolCommandGenerator { return { generated: generatedCount, - tasks: standaloneTasks.length, - tools: standaloneTools.length, + tasks: (tasks || []).length, + tools: (tools || []).length, }; } @@ -242,14 +235,10 @@ Follow all instructions in the ${type} file exactly as written. 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; // Generate command files for tasks - for (const task of standaloneTasks) { + for (const task of tasks || []) { const commandContent = this.generateCommandContent(task, 'task'); // Use underscore format: bmad_bmm_name.md const flatName = toColonName(task.module, 'tasks', task.name); @@ -260,7 +249,7 @@ Follow all instructions in the ${type} file exactly as written. } // Generate command files for tools - for (const tool of standaloneTools) { + for (const tool of tools || []) { const commandContent = this.generateCommandContent(tool, 'tool'); // Use underscore format: bmad_bmm_name.md const flatName = toColonName(tool.module, 'tools', tool.name); @@ -272,8 +261,8 @@ Follow all instructions in the ${type} file exactly as written. return { generated: generatedCount, - tasks: standaloneTasks.length, - tools: standaloneTools.length, + tasks: (tasks || []).length, + tools: (tools || []).length, }; } @@ -290,14 +279,10 @@ Follow all instructions in the ${type} file exactly as written. 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; // Generate command files for tasks - for (const task of standaloneTasks) { + for (const task of tasks || []) { const commandContent = this.generateCommandContent(task, 'task'); // Use dash format: bmad-bmm-name.md const flatName = toDashPath(`${task.module}/tasks/${task.name}.md`); @@ -308,7 +293,7 @@ Follow all instructions in the ${type} file exactly as written. } // Generate command files for tools - for (const tool of standaloneTools) { + for (const tool of tools || []) { const commandContent = this.generateCommandContent(tool, 'tool'); // Use dash format: bmad-bmm-name.md const flatName = toDashPath(`${tool.module}/tools/${tool.name}.md`); @@ -320,8 +305,8 @@ Follow all instructions in the ${type} file exactly as written. return { generated: generatedCount, - tasks: standaloneTasks.length, - tools: standaloneTools.length, + tasks: (tasks || []).length, + tools: (tools || []).length, }; } diff --git a/tools/cli/installers/lib/modules/manager.js b/tools/cli/installers/lib/modules/manager.js index 1f523fba3..c55dae838 100644 --- a/tools/cli/installers/lib/modules/manager.js +++ b/tools/cli/installers/lib/modules/manager.js @@ -417,7 +417,7 @@ class ModuleManager { if (needsDependencyInstall || wasNewClone || nodeModulesMissing) { const installSpinner = ora(`Installing dependencies for ${moduleInfo.name}...`).start(); try { - execSync('npm install --production --no-audit --no-fund --prefer-offline --no-progress --legacy-peer-deps', { + execSync('npm install --omit=dev --no-audit --no-fund --no-progress --legacy-peer-deps', { cwd: moduleCacheDir, stdio: 'pipe', timeout: 120_000, // 2 minute timeout @@ -442,7 +442,7 @@ class ModuleManager { if (packageJsonNewer) { const installSpinner = ora(`Installing dependencies for ${moduleInfo.name}...`).start(); try { - execSync('npm install --production --no-audit --no-fund --prefer-offline --no-progress --legacy-peer-deps', { + execSync('npm install --omit=dev --no-audit --no-fund --no-progress --legacy-peer-deps', { cwd: moduleCacheDir, stdio: 'pipe', timeout: 120_000, // 2 minute timeout @@ -781,10 +781,6 @@ class ModuleManager { return; } - // Remove web_bundle section using regex to preserve formatting - // Match the web_bundle key and all its content (including nested items) - // This handles both web_bundle: false and web_bundle: {...} - // Find the line that starts web_bundle const lines = yamlContent.split('\n'); let startIdx = -1; diff --git a/tools/flattener/aggregate.js b/tools/flattener/aggregate.js deleted file mode 100644 index 6a597a2fe..000000000 --- a/tools/flattener/aggregate.js +++ /dev/null @@ -1,76 +0,0 @@ -const fs = require('fs-extra'); -const path = require('node:path'); -const os = require('node:os'); -const { isBinaryFile } = require('./binary.js'); - -/** - * Aggregate file contents with bounded concurrency. - * Returns text files, binary files (with size), and errors. - * @param {string[]} files absolute file paths - * @param {string} rootDir - * @param {{ text?: string, warn?: (msg: string) => void } | null} spinner - */ -async function aggregateFileContents(files, rootDir, spinner = null) { - const results = { - textFiles: [], - binaryFiles: [], - errors: [], - totalFiles: files.length, - processedFiles: 0, - }; - - // Automatic concurrency selection based on CPU count and workload size. - // - Base on 2x logical CPUs, clamped to [2, 64] - // - For very small workloads, avoid excessive parallelism - const cpuCount = os.cpus && Array.isArray(os.cpus()) ? os.cpus().length : os.cpus?.length || 4; - let concurrency = Math.min(64, Math.max(2, (Number(cpuCount) || 4) * 2)); - if (files.length > 0 && files.length < concurrency) { - concurrency = Math.max(1, Math.min(concurrency, Math.ceil(files.length / 2))); - } - - async function processOne(filePath) { - try { - const relativePath = path.relative(rootDir, filePath); - if (spinner) { - spinner.text = `Processing: ${relativePath} (${results.processedFiles + 1}/${results.totalFiles})`; - } - - const binary = await isBinaryFile(filePath); - if (binary) { - const { size } = await fs.stat(filePath); - results.binaryFiles.push({ path: relativePath, absolutePath: filePath, size }); - } else { - const content = await fs.readFile(filePath, 'utf8'); - results.textFiles.push({ - path: relativePath, - absolutePath: filePath, - content, - size: content.length, - lines: content.split('\n').length, - }); - } - } catch (error) { - const relativePath = path.relative(rootDir, filePath); - const errorInfo = { path: relativePath, absolutePath: filePath, error: error.message }; - results.errors.push(errorInfo); - if (spinner) { - spinner.warn(`Warning: Could not read file ${relativePath}: ${error.message}`); - } else { - console.warn(`Warning: Could not read file ${relativePath}: ${error.message}`); - } - } finally { - results.processedFiles++; - } - } - - for (let index = 0; index < files.length; index += concurrency) { - const slice = files.slice(index, index + concurrency); - await Promise.all(slice.map(processOne)); - } - - return results; -} - -module.exports = { - aggregateFileContents, -}; diff --git a/tools/flattener/binary.js b/tools/flattener/binary.js deleted file mode 100644 index fcfb27c1a..000000000 --- a/tools/flattener/binary.js +++ /dev/null @@ -1,80 +0,0 @@ -const fsp = require('node:fs/promises'); -const path = require('node:path'); -const { Buffer } = require('node:buffer'); - -/** - * Efficiently determine if a file is binary without reading the whole file. - * - Fast path by extension for common binaries - * - Otherwise read a small prefix and check for NUL bytes - * @param {string} filePath - * @returns {Promise} - */ -async function isBinaryFile(filePath) { - try { - const stats = await fsp.stat(filePath); - if (stats.isDirectory()) { - throw new Error('EISDIR: illegal operation on a directory'); - } - - const binaryExtensions = new Set([ - '.jpg', - '.jpeg', - '.png', - '.gif', - '.bmp', - '.ico', - '.svg', - '.pdf', - '.doc', - '.docx', - '.xls', - '.xlsx', - '.ppt', - '.pptx', - '.zip', - '.tar', - '.gz', - '.rar', - '.7z', - '.exe', - '.dll', - '.so', - '.dylib', - '.mp3', - '.mp4', - '.avi', - '.mov', - '.wav', - '.ttf', - '.otf', - '.woff', - '.woff2', - '.bin', - '.dat', - '.db', - '.sqlite', - ]); - - const extension = path.extname(filePath).toLowerCase(); - if (binaryExtensions.has(extension)) return true; - if (stats.size === 0) return false; - - const sampleSize = Math.min(4096, stats.size); - const fd = await fsp.open(filePath, 'r'); - try { - const buffer = Buffer.allocUnsafe(sampleSize); - const { bytesRead } = await fd.read(buffer, 0, sampleSize, 0); - const slice = bytesRead === sampleSize ? buffer : buffer.subarray(0, bytesRead); - return slice.includes(0); - } finally { - await fd.close(); - } - } catch (error) { - console.warn(`Warning: Could not determine if file is binary: ${filePath} - ${error.message}`); - return false; - } -} - -module.exports = { - isBinaryFile, -}; diff --git a/tools/flattener/discovery.js b/tools/flattener/discovery.js deleted file mode 100644 index 7eaaa2d40..000000000 --- a/tools/flattener/discovery.js +++ /dev/null @@ -1,71 +0,0 @@ -const path = require('node:path'); -const { execFile } = require('node:child_process'); -const { promisify } = require('node:util'); -const { glob } = require('glob'); -const { loadIgnore } = require('./ignoreRules.js'); - -const pExecFile = promisify(execFile); - -async function isGitRepo(rootDir) { - try { - const { stdout } = await pExecFile('git', ['rev-parse', '--is-inside-work-tree'], { - cwd: rootDir, - }); - return ( - String(stdout || '') - .toString() - .trim() === 'true' - ); - } catch { - return false; - } -} - -async function gitListFiles(rootDir) { - try { - const { stdout } = await pExecFile('git', ['ls-files', '-co', '--exclude-standard'], { - cwd: rootDir, - }); - return String(stdout || '') - .split(/\r?\n/) - .map((s) => s.trim()) - .filter(Boolean); - } catch { - return []; - } -} - -/** - * Discover files under rootDir. - * - Prefer git ls-files when available for speed/correctness - * - Fallback to glob and apply unified ignore rules - * @param {string} rootDir - * @param {object} [options] - * @param {boolean} [options.preferGit=true] - * @returns {Promise} absolute file paths - */ -async function discoverFiles(rootDir, options = {}) { - const { preferGit = true } = options; - const { filter } = await loadIgnore(rootDir); - - // Try git first - if (preferGit && (await isGitRepo(rootDir))) { - const relFiles = await gitListFiles(rootDir); - const filteredRel = relFiles.filter((p) => filter(p)); - return filteredRel.map((p) => path.resolve(rootDir, p)); - } - - // Glob fallback - const globbed = await glob('**/*', { - cwd: rootDir, - nodir: true, - dot: true, - follow: false, - }); - const filteredRel = globbed.filter((p) => filter(p)); - return filteredRel.map((p) => path.resolve(rootDir, p)); -} - -module.exports = { - discoverFiles, -}; diff --git a/tools/flattener/files.js b/tools/flattener/files.js deleted file mode 100644 index e7236d7b0..000000000 --- a/tools/flattener/files.js +++ /dev/null @@ -1,35 +0,0 @@ -const path = require('node:path'); -const discovery = require('./discovery.js'); -const ignoreRules = require('./ignoreRules.js'); -const { isBinaryFile } = require('./binary.js'); -const { aggregateFileContents } = require('./aggregate.js'); - -// Backward-compatible signature; delegate to central loader -async function parseGitignore(gitignorePath) { - return await ignoreRules.parseGitignore(gitignorePath); -} - -async function discoverFiles(rootDir) { - try { - // Delegate to discovery module which respects .gitignore and defaults - return await discovery.discoverFiles(rootDir, { preferGit: true }); - } catch (error) { - console.error('Error discovering files:', error.message); - return []; - } -} - -async function filterFiles(files, rootDir) { - const { filter } = await ignoreRules.loadIgnore(rootDir); - const relativeFiles = files.map((f) => path.relative(rootDir, f)); - const filteredRelative = relativeFiles.filter((p) => filter(p)); - return filteredRelative.map((p) => path.resolve(rootDir, p)); -} - -module.exports = { - parseGitignore, - discoverFiles, - isBinaryFile, - aggregateFileContents, - filterFiles, -}; diff --git a/tools/flattener/ignoreRules.js b/tools/flattener/ignoreRules.js deleted file mode 100644 index b825edea7..000000000 --- a/tools/flattener/ignoreRules.js +++ /dev/null @@ -1,172 +0,0 @@ -const fs = require('fs-extra'); -const path = require('node:path'); -const ignore = require('ignore'); - -// Central default ignore patterns for discovery and filtering. -// These complement .gitignore and are applied regardless of VCS presence. -const DEFAULT_PATTERNS = [ - // Project/VCS - '**/_bmad/**', - '**/.git/**', - '**/.svn/**', - '**/.hg/**', - '**/.bzr/**', - // Package/build outputs - '**/node_modules/**', - '**/bower_components/**', - '**/vendor/**', - '**/packages/**', - '**/build/**', - '**/dist/**', - '**/out/**', - '**/target/**', - '**/bin/**', - '**/obj/**', - '**/release/**', - '**/debug/**', - // Environments - '**/.venv/**', - '**/venv/**', - '**/.virtualenv/**', - '**/virtualenv/**', - '**/env/**', - // Logs & coverage - '**/*.log', - '**/npm-debug.log*', - '**/yarn-debug.log*', - '**/yarn-error.log*', - '**/lerna-debug.log*', - '**/coverage/**', - '**/.nyc_output/**', - '**/.coverage/**', - '**/test-results/**', - // Caches & temp - '**/.cache/**', - '**/.tmp/**', - '**/.temp/**', - '**/tmp/**', - '**/temp/**', - '**/.sass-cache/**', - // IDE/editor - '**/.vscode/**', - '**/.idea/**', - '**/*.swp', - '**/*.swo', - '**/*~', - '**/.project', - '**/.classpath', - '**/.settings/**', - '**/*.sublime-project', - '**/*.sublime-workspace', - // Lockfiles - '**/package-lock.json', - '**/yarn.lock', - '**/pnpm-lock.yaml', - '**/composer.lock', - '**/Pipfile.lock', - // Python/Java/compiled artifacts - '**/*.pyc', - '**/*.pyo', - '**/*.pyd', - '**/__pycache__/**', - '**/*.class', - '**/*.jar', - '**/*.war', - '**/*.ear', - '**/*.o', - '**/*.so', - '**/*.dll', - '**/*.exe', - // System junk - '**/lib64/**', - '**/.venv/lib64/**', - '**/venv/lib64/**', - '**/_site/**', - '**/.jekyll-cache/**', - '**/.jekyll-metadata', - '**/.DS_Store', - '**/.DS_Store?', - '**/._*', - '**/.Spotlight-V100/**', - '**/.Trashes/**', - '**/ehthumbs.db', - '**/Thumbs.db', - '**/desktop.ini', - // XML outputs - '**/flattened-codebase.xml', - '**/repomix-output.xml', - // Images, media, fonts, archives, docs, dylibs - '**/*.jpg', - '**/*.jpeg', - '**/*.png', - '**/*.gif', - '**/*.bmp', - '**/*.ico', - '**/*.svg', - '**/*.pdf', - '**/*.doc', - '**/*.docx', - '**/*.xls', - '**/*.xlsx', - '**/*.ppt', - '**/*.pptx', - '**/*.zip', - '**/*.tar', - '**/*.gz', - '**/*.rar', - '**/*.7z', - '**/*.dylib', - '**/*.mp3', - '**/*.mp4', - '**/*.avi', - '**/*.mov', - '**/*.wav', - '**/*.ttf', - '**/*.otf', - '**/*.woff', - '**/*.woff2', - // Env files - '**/.env', - '**/.env.*', - '**/*.env', - // Misc - '**/junit.xml', -]; - -async function readIgnoreFile(filePath) { - try { - if (!(await fs.pathExists(filePath))) return []; - const content = await fs.readFile(filePath, 'utf8'); - return content - .split('\n') - .map((l) => l.trim()) - .filter((l) => l && !l.startsWith('#')); - } catch { - return []; - } -} - -// Backward compatible export matching previous signature -async function parseGitignore(gitignorePath) { - return readIgnoreFile(gitignorePath); -} - -async function loadIgnore(rootDir, extraPatterns = []) { - const ig = ignore(); - const gitignorePath = path.join(rootDir, '.gitignore'); - const patterns = [...(await readIgnoreFile(gitignorePath)), ...DEFAULT_PATTERNS, ...extraPatterns]; - // De-duplicate - const unique = [...new Set(patterns.map(String))]; - ig.add(unique); - - // Include-only filter: return true if path should be included - const filter = (relativePath) => !ig.ignores(relativePath.replaceAll('\\', '/')); - - return { ig, filter, patterns: unique }; -} - -module.exports = { - DEFAULT_PATTERNS, - parseGitignore, - loadIgnore, -}; diff --git a/tools/flattener/main.js b/tools/flattener/main.js deleted file mode 100644 index 72bb42f90..000000000 --- a/tools/flattener/main.js +++ /dev/null @@ -1,483 +0,0 @@ -const { Command } = require('commander'); -const fs = require('fs-extra'); -const path = require('node:path'); -const process = require('node:process'); - -// Modularized components -const { findProjectRoot } = require('./projectRoot.js'); -const { promptYesNo, promptPath } = require('./prompts.js'); -const { discoverFiles, filterFiles, aggregateFileContents } = require('./files.js'); -const { generateXMLOutput } = require('./xml.js'); -const { calculateStatistics } = require('./stats.js'); - -/** - * Recursively discover all files in a directory - * @param {string} rootDir - The root directory to scan - * @returns {Promise} Array of file paths - */ - -/** - * Parse .gitignore file and return ignore patterns - * @param {string} gitignorePath - Path to .gitignore file - * @returns {Promise} Array of ignore patterns - */ - -/** - * Check if a file is binary using file command and heuristics - * @param {string} filePath - Path to the file - * @returns {Promise} True if file is binary - */ - -/** - * Read and aggregate content from text files - * @param {string[]} files - Array of file paths - * @param {string} rootDir - The root directory - * @param {Object} spinner - Optional spinner instance for progress display - * @returns {Promise} Object containing file contents and metadata - */ - -/** - * Generate XML output with aggregated file contents using streaming - * @param {Object} aggregatedContent - The aggregated content object - * @param {string} outputPath - The output file path - * @returns {Promise} Promise that resolves when writing is complete - */ - -/** - * Calculate statistics for the processed files - * @param {Object} aggregatedContent - The aggregated content object - * @param {number} xmlFileSize - The size of the generated XML file in bytes - * @returns {Object} Statistics object - */ - -/** - * Filter files based on .gitignore patterns - * @param {string[]} files - Array of file paths - * @param {string} rootDir - The root directory - * @returns {Promise} Filtered array of file paths - */ - -/** - * Attempt to find the project root by walking up from startDir - * Looks for common project markers like .git, package.json, pyproject.toml, etc. - * @param {string} startDir - * @returns {Promise} project root directory or null if not found - */ - -const program = new Command(); - -program - .name('bmad-flatten') - .description('BMad-Method codebase flattener tool') - .version('1.0.0') - .option('-i, --input ', 'Input directory to flatten', process.cwd()) - .option('-o, --output ', 'Output file path', 'flattened-codebase.xml') - .action(async (options) => { - let inputDir = path.resolve(options.input); - let outputPath = path.resolve(options.output); - - // Detect if user explicitly provided -i/--input or -o/--output - const argv = process.argv.slice(2); - const userSpecifiedInput = argv.some((a) => a === '-i' || a === '--input' || a.startsWith('--input=')); - const userSpecifiedOutput = argv.some((a) => a === '-o' || a === '--output' || a.startsWith('--output=')); - const noPathArguments = !userSpecifiedInput && !userSpecifiedOutput; - - if (noPathArguments) { - const detectedRoot = await findProjectRoot(process.cwd()); - const suggestedOutput = detectedRoot ? path.join(detectedRoot, 'flattened-codebase.xml') : path.resolve('flattened-codebase.xml'); - - if (detectedRoot) { - const useDefaults = await promptYesNo( - `Detected project root at "${detectedRoot}". Use it as input and write output to "${suggestedOutput}"?`, - true, - ); - if (useDefaults) { - inputDir = detectedRoot; - outputPath = suggestedOutput; - } else { - inputDir = await promptPath('Enter input directory path', process.cwd()); - outputPath = await promptPath('Enter output file path', path.join(inputDir, 'flattened-codebase.xml')); - } - } else { - console.log('Could not auto-detect a project root.'); - inputDir = await promptPath('Enter input directory path', process.cwd()); - outputPath = await promptPath('Enter output file path', path.join(inputDir, 'flattened-codebase.xml')); - } - } - - // Ensure output directory exists - await fs.ensureDir(path.dirname(outputPath)); - - try { - // Verify input directory exists - if (!(await fs.pathExists(inputDir))) { - console.error(`โŒ Error: Input directory does not exist: ${inputDir}`); - process.exit(1); - } - - // Import ora dynamically - const { default: ora } = await import('ora'); - - // Start file discovery with spinner - const discoverySpinner = ora('๐Ÿ” Discovering files...').start(); - const files = await discoverFiles(inputDir); - const filteredFiles = await filterFiles(files, inputDir); - discoverySpinner.succeed(`๐Ÿ“ Found ${filteredFiles.length} files to include`); - - // Process files with progress tracking - console.log('Reading file contents'); - const processingSpinner = ora('๐Ÿ“„ Processing files...').start(); - const aggregatedContent = await aggregateFileContents(filteredFiles, inputDir, processingSpinner); - processingSpinner.succeed(`โœ… Processed ${aggregatedContent.processedFiles}/${filteredFiles.length} files`); - if (aggregatedContent.errors.length > 0) { - console.log(`Errors: ${aggregatedContent.errors.length}`); - } - - // Generate XML output using streaming - const xmlSpinner = ora('๐Ÿ”ง Generating XML output...').start(); - await generateXMLOutput(aggregatedContent, outputPath); - xmlSpinner.succeed('๐Ÿ“ XML generation completed'); - - // Calculate and display statistics - const outputStats = await fs.stat(outputPath); - const stats = await calculateStatistics(aggregatedContent, outputStats.size, inputDir); - - // Display completion summary - console.log('\n๐Ÿ“Š Completion Summary:'); - console.log(`โœ… Successfully processed ${filteredFiles.length} files into ${path.basename(outputPath)}`); - console.log(`๐Ÿ“ Output file: ${outputPath}`); - console.log(`๐Ÿ“ Total source size: ${stats.totalSize}`); - console.log(`๐Ÿ“„ Generated XML size: ${stats.xmlSize}`); - console.log(`๐Ÿ“ Total lines of code: ${stats.totalLines.toLocaleString()}`); - console.log(`๐Ÿ”ข Estimated tokens: ${stats.estimatedTokens}`); - console.log(`๐Ÿ“Š File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors\n`); - - // Ask user if they want detailed stats + markdown report - const generateDetailed = await promptYesNo('Generate detailed stats (console + markdown) now?', true); - - if (generateDetailed) { - // Additional detailed stats - console.log('\n๐Ÿ“ˆ Size Percentiles:'); - console.log( - ` Avg: ${Math.round(stats.avgFileSize).toLocaleString()} B, Median: ${Math.round( - stats.medianFileSize, - ).toLocaleString()} B, p90: ${stats.p90.toLocaleString()} B, p95: ${stats.p95.toLocaleString()} B, p99: ${stats.p99.toLocaleString()} B`, - ); - - if (Array.isArray(stats.histogram) && stats.histogram.length > 0) { - console.log('\n๐Ÿงฎ Size Histogram:'); - for (const b of stats.histogram.slice(0, 2)) { - console.log(` ${b.label}: ${b.count} files, ${b.bytes.toLocaleString()} bytes`); - } - if (stats.histogram.length > 2) { - console.log(` โ€ฆ and ${stats.histogram.length - 2} more buckets`); - } - } - - if (Array.isArray(stats.byExtension) && stats.byExtension.length > 0) { - const topExt = stats.byExtension.slice(0, 2); - console.log('\n๐Ÿ“ฆ Top Extensions:'); - for (const e of topExt) { - const pct = stats.totalBytes ? (e.bytes / stats.totalBytes) * 100 : 0; - console.log(` ${e.ext}: ${e.count} files, ${e.bytes.toLocaleString()} bytes (${pct.toFixed(2)}%)`); - } - if (stats.byExtension.length > 2) { - console.log(` โ€ฆ and ${stats.byExtension.length - 2} more extensions`); - } - } - - if (Array.isArray(stats.byDirectory) && stats.byDirectory.length > 0) { - const topDir = stats.byDirectory.slice(0, 2); - console.log('\n๐Ÿ“‚ Top Directories:'); - for (const d of topDir) { - const pct = stats.totalBytes ? (d.bytes / stats.totalBytes) * 100 : 0; - console.log(` ${d.dir}: ${d.count} files, ${d.bytes.toLocaleString()} bytes (${pct.toFixed(2)}%)`); - } - if (stats.byDirectory.length > 2) { - console.log(` โ€ฆ and ${stats.byDirectory.length - 2} more directories`); - } - } - - if (Array.isArray(stats.depthDistribution) && stats.depthDistribution.length > 0) { - console.log('\n๐ŸŒณ Depth Distribution:'); - const dd = stats.depthDistribution.slice(0, 2); - let line = ' ' + dd.map((d) => `${d.depth}:${d.count}`).join(' '); - if (stats.depthDistribution.length > 2) { - line += ` โ€ฆ +${stats.depthDistribution.length - 2} more`; - } - console.log(line); - } - - if (Array.isArray(stats.longestPaths) && stats.longestPaths.length > 0) { - console.log('\n๐Ÿงต Longest Paths:'); - for (const p of stats.longestPaths.slice(0, 2)) { - console.log(` ${p.path} (${p.length} chars, ${p.size.toLocaleString()} bytes)`); - } - if (stats.longestPaths.length > 2) { - console.log(` โ€ฆ and ${stats.longestPaths.length - 2} more paths`); - } - } - - if (stats.temporal) { - console.log('\nโฑ๏ธ Temporal:'); - if (stats.temporal.oldest) { - console.log(` Oldest: ${stats.temporal.oldest.path} (${stats.temporal.oldest.mtime})`); - } - if (stats.temporal.newest) { - console.log(` Newest: ${stats.temporal.newest.path} (${stats.temporal.newest.mtime})`); - } - if (Array.isArray(stats.temporal.ageBuckets)) { - console.log(' Age buckets:'); - for (const b of stats.temporal.ageBuckets.slice(0, 2)) { - console.log(` ${b.label}: ${b.count} files, ${b.bytes.toLocaleString()} bytes`); - } - if (stats.temporal.ageBuckets.length > 2) { - console.log(` โ€ฆ and ${stats.temporal.ageBuckets.length - 2} more buckets`); - } - } - } - - if (stats.quality) { - console.log('\nโœ… Quality Signals:'); - console.log(` Zero-byte files: ${stats.quality.zeroByteFiles}`); - console.log(` Empty text files: ${stats.quality.emptyTextFiles}`); - console.log(` Hidden files: ${stats.quality.hiddenFiles}`); - console.log(` Symlinks: ${stats.quality.symlinks}`); - console.log( - ` Large files (>= ${(stats.quality.largeThreshold / (1024 * 1024)).toFixed(0)} MB): ${stats.quality.largeFilesCount}`, - ); - console.log(` Suspiciously large files (>= 100 MB): ${stats.quality.suspiciousLargeFilesCount}`); - } - - if (Array.isArray(stats.duplicateCandidates) && stats.duplicateCandidates.length > 0) { - console.log('\n๐Ÿงฌ Duplicate Candidates:'); - for (const d of stats.duplicateCandidates.slice(0, 2)) { - console.log(` ${d.reason}: ${d.count} files @ ${d.size.toLocaleString()} bytes`); - } - if (stats.duplicateCandidates.length > 2) { - console.log(` โ€ฆ and ${stats.duplicateCandidates.length - 2} more groups`); - } - } - - if (typeof stats.compressibilityRatio === 'number') { - console.log(`\n๐Ÿ—œ๏ธ Compressibility ratio (sampled): ${(stats.compressibilityRatio * 100).toFixed(2)}%`); - } - - if (stats.git && stats.git.isRepo) { - console.log('\n๐Ÿ”ง Git:'); - console.log(` Tracked: ${stats.git.trackedCount} files, ${stats.git.trackedBytes.toLocaleString()} bytes`); - console.log(` Untracked: ${stats.git.untrackedCount} files, ${stats.git.untrackedBytes.toLocaleString()} bytes`); - if (Array.isArray(stats.git.lfsCandidates) && stats.git.lfsCandidates.length > 0) { - console.log(' LFS candidates (top 2):'); - for (const f of stats.git.lfsCandidates.slice(0, 2)) { - console.log(` ${f.path} (${f.size.toLocaleString()} bytes)`); - } - if (stats.git.lfsCandidates.length > 2) { - console.log(` โ€ฆ and ${stats.git.lfsCandidates.length - 2} more`); - } - } - } - - if (Array.isArray(stats.largestFiles) && stats.largestFiles.length > 0) { - console.log('\n๐Ÿ“š Largest Files (top 2):'); - for (const f of stats.largestFiles.slice(0, 2)) { - // Show LOC for text files when available; omit ext and mtime - let locStr = ''; - if (!f.isBinary && Array.isArray(aggregatedContent?.textFiles)) { - const tf = aggregatedContent.textFiles.find((t) => t.path === f.path); - if (tf && typeof tf.lines === 'number') { - locStr = `, LOC: ${tf.lines.toLocaleString()}`; - } - } - console.log(` ${f.path} โ€“ ${f.sizeFormatted} (${f.percentOfTotal.toFixed(2)}%)${locStr}`); - } - if (stats.largestFiles.length > 2) { - console.log(` โ€ฆ and ${stats.largestFiles.length - 2} more files`); - } - } - - // Write a comprehensive markdown report next to the XML - { - const mdPath = outputPath.endsWith('.xml') ? outputPath.replace(/\.xml$/i, '.stats.md') : outputPath + '.stats.md'; - try { - const pct = (num, den) => (den ? (num / den) * 100 : 0); - const md = []; - md.push( - `# ๐Ÿงพ Flatten Stats for ${path.basename(outputPath)}`, - '', - '## ๐Ÿ“Š Summary', - `- Total source size: ${stats.totalSize}`, - `- Generated XML size: ${stats.xmlSize}`, - `- Total lines of code: ${stats.totalLines.toLocaleString()}`, - `- Estimated tokens: ${stats.estimatedTokens}`, - `- File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors`, - '', - '## ๐Ÿ“ˆ Size Percentiles', - `Avg: ${Math.round(stats.avgFileSize).toLocaleString()} B, Median: ${Math.round( - stats.medianFileSize, - ).toLocaleString()} B, p90: ${stats.p90.toLocaleString()} B, p95: ${stats.p95.toLocaleString()} B, p99: ${stats.p99.toLocaleString()} B`, - '', - ); - - // Histogram - if (Array.isArray(stats.histogram) && stats.histogram.length > 0) { - md.push('## ๐Ÿงฎ Size Histogram', '| Bucket | Files | Bytes |', '| --- | ---: | ---: |'); - for (const b of stats.histogram) { - md.push(`| ${b.label} | ${b.count} | ${b.bytes.toLocaleString()} |`); - } - md.push(''); - } - - // Top Extensions - if (Array.isArray(stats.byExtension) && stats.byExtension.length > 0) { - md.push('## ๐Ÿ“ฆ Top Extensions by Bytes (Top 20)', '| Ext | Files | Bytes | % of total |', '| --- | ---: | ---: | ---: |'); - for (const e of stats.byExtension.slice(0, 20)) { - const p = pct(e.bytes, stats.totalBytes); - md.push(`| ${e.ext} | ${e.count} | ${e.bytes.toLocaleString()} | ${p.toFixed(2)}% |`); - } - md.push(''); - } - - // Top Directories - if (Array.isArray(stats.byDirectory) && stats.byDirectory.length > 0) { - md.push( - '## ๐Ÿ“‚ Top Directories by Bytes (Top 20)', - '| Directory | Files | Bytes | % of total |', - '| --- | ---: | ---: | ---: |', - ); - for (const d of stats.byDirectory.slice(0, 20)) { - const p = pct(d.bytes, stats.totalBytes); - md.push(`| ${d.dir} | ${d.count} | ${d.bytes.toLocaleString()} | ${p.toFixed(2)}% |`); - } - md.push(''); - } - - // Depth distribution - if (Array.isArray(stats.depthDistribution) && stats.depthDistribution.length > 0) { - md.push('## ๐ŸŒณ Depth Distribution', '| Depth | Count |', '| ---: | ---: |'); - for (const d of stats.depthDistribution) { - md.push(`| ${d.depth} | ${d.count} |`); - } - md.push(''); - } - - // Longest paths - if (Array.isArray(stats.longestPaths) && stats.longestPaths.length > 0) { - md.push('## ๐Ÿงต Longest Paths (Top 25)', '| Path | Length | Bytes |', '| --- | ---: | ---: |'); - for (const pth of stats.longestPaths) { - md.push(`| ${pth.path} | ${pth.length} | ${pth.size.toLocaleString()} |`); - } - md.push(''); - } - - // Temporal - if (stats.temporal) { - md.push('## โฑ๏ธ Temporal'); - if (stats.temporal.oldest) { - md.push(`- Oldest: ${stats.temporal.oldest.path} (${stats.temporal.oldest.mtime})`); - } - if (stats.temporal.newest) { - md.push(`- Newest: ${stats.temporal.newest.path} (${stats.temporal.newest.mtime})`); - } - if (Array.isArray(stats.temporal.ageBuckets)) { - md.push('', '| Age | Files | Bytes |', '| --- | ---: | ---: |'); - for (const b of stats.temporal.ageBuckets) { - md.push(`| ${b.label} | ${b.count} | ${b.bytes.toLocaleString()} |`); - } - } - md.push(''); - } - - // Quality signals - if (stats.quality) { - md.push( - '## โœ… Quality Signals', - `- Zero-byte files: ${stats.quality.zeroByteFiles}`, - `- Empty text files: ${stats.quality.emptyTextFiles}`, - `- Hidden files: ${stats.quality.hiddenFiles}`, - `- Symlinks: ${stats.quality.symlinks}`, - `- Large files (>= ${(stats.quality.largeThreshold / (1024 * 1024)).toFixed(0)} MB): ${stats.quality.largeFilesCount}`, - `- Suspiciously large files (>= 100 MB): ${stats.quality.suspiciousLargeFilesCount}`, - '', - ); - } - - // Duplicates - if (Array.isArray(stats.duplicateCandidates) && stats.duplicateCandidates.length > 0) { - md.push('## ๐Ÿงฌ Duplicate Candidates', '| Reason | Files | Size (bytes) |', '| --- | ---: | ---: |'); - for (const d of stats.duplicateCandidates) { - md.push(`| ${d.reason} | ${d.count} | ${d.size.toLocaleString()} |`); - } - md.push('', '### ๐Ÿงฌ Duplicate Groups Details'); - let dupIndex = 1; - for (const d of stats.duplicateCandidates) { - md.push(`#### Group ${dupIndex}: ${d.count} files @ ${d.size.toLocaleString()} bytes (${d.reason})`); - if (Array.isArray(d.files) && d.files.length > 0) { - for (const fp of d.files) { - md.push(`- ${fp}`); - } - } else { - md.push('- (file list unavailable)'); - } - md.push(''); - dupIndex++; - } - md.push(''); - } - - // Compressibility - if (typeof stats.compressibilityRatio === 'number') { - md.push('## ๐Ÿ—œ๏ธ Compressibility', `Sampled compressibility ratio: ${(stats.compressibilityRatio * 100).toFixed(2)}%`, ''); - } - - // Git - if (stats.git && stats.git.isRepo) { - md.push( - '## ๐Ÿ”ง Git', - `- Tracked: ${stats.git.trackedCount} files, ${stats.git.trackedBytes.toLocaleString()} bytes`, - `- Untracked: ${stats.git.untrackedCount} files, ${stats.git.untrackedBytes.toLocaleString()} bytes`, - ); - if (Array.isArray(stats.git.lfsCandidates) && stats.git.lfsCandidates.length > 0) { - md.push('', '### ๐Ÿ“ฆ LFS Candidates (Top 20)', '| Path | Bytes |', '| --- | ---: |'); - for (const f of stats.git.lfsCandidates.slice(0, 20)) { - md.push(`| ${f.path} | ${f.size.toLocaleString()} |`); - } - } - md.push(''); - } - - // Largest Files - if (Array.isArray(stats.largestFiles) && stats.largestFiles.length > 0) { - md.push('## ๐Ÿ“š Largest Files (Top 50)', '| Path | Size | % of total | LOC |', '| --- | ---: | ---: | ---: |'); - for (const f of stats.largestFiles) { - let loc = ''; - if (!f.isBinary && Array.isArray(aggregatedContent?.textFiles)) { - const tf = aggregatedContent.textFiles.find((t) => t.path === f.path); - if (tf && typeof tf.lines === 'number') { - loc = tf.lines.toLocaleString(); - } - } - md.push(`| ${f.path} | ${f.sizeFormatted} | ${f.percentOfTotal.toFixed(2)}% | ${loc} |`); - } - md.push(''); - } - - await fs.writeFile(mdPath, md.join('\n')); - console.log(`\n๐Ÿงพ Detailed stats report written to: ${mdPath}`); - } catch (error) { - console.warn(`โš ๏ธ Failed to write stats markdown: ${error.message}`); - } - } - } - } catch (error) { - console.error('โŒ Critical error:', error.message); - console.error('An unexpected error occurred.'); - process.exit(1); - } - }); - -if (require.main === module) { - program.parse(); -} - -module.exports = program; diff --git a/tools/flattener/projectRoot.js b/tools/flattener/projectRoot.js deleted file mode 100644 index b2b9a7ae6..000000000 --- a/tools/flattener/projectRoot.js +++ /dev/null @@ -1,201 +0,0 @@ -const fs = require('fs-extra'); -const path = require('node:path'); - -// Deno/Node compatibility: explicitly import process -const process = require('node:process'); -const { execFile } = require('node:child_process'); -const { promisify } = require('node:util'); -const execFileAsync = promisify(execFile); - -// Simple memoization across calls (keyed by realpath of startDir) -const _cache = new Map(); - -async function _tryRun(cmd, args, cwd, timeoutMs = 500) { - try { - const { stdout } = await execFileAsync(cmd, args, { - cwd, - timeout: timeoutMs, - windowsHide: true, - maxBuffer: 1024 * 1024, - }); - const out = String(stdout || '').trim(); - return out || null; - } catch { - return null; - } -} - -async function _detectVcsTopLevel(startDir) { - // Run common VCS root queries in parallel; ignore failures - const gitP = _tryRun('git', ['rev-parse', '--show-toplevel'], startDir); - const hgP = _tryRun('hg', ['root'], startDir); - const svnP = (async () => { - const show = await _tryRun('svn', ['info', '--show-item', 'wc-root'], startDir); - if (show) return show; - const info = await _tryRun('svn', ['info'], startDir); - if (info) { - const line = info.split(/\r?\n/).find((l) => l.toLowerCase().startsWith('working copy root path:')); - if (line) return line.split(':').slice(1).join(':').trim(); - } - return null; - })(); - const [git, hg, svn] = await Promise.all([gitP, hgP, svnP]); - return git || hg || svn || null; -} - -/** - * Attempt to find the project root by walking up from startDir. - * Uses a robust, prioritized set of ecosystem markers (VCS > workspaces/monorepo > lock/build > language config). - * Also recognizes package.json with "workspaces" as a workspace root. - * You can augment markers via env PROJECT_ROOT_MARKERS as a comma-separated list of file/dir names. - * @param {string} startDir - * @returns {Promise} project root directory or null if not found - */ -async function findProjectRoot(startDir) { - try { - // Resolve symlinks for robustness (e.g., when invoked from a symlinked path) - let dir = path.resolve(startDir); - try { - dir = await fs.realpath(dir); - } catch { - // ignore if realpath fails; continue with resolved path - } - const startKey = dir; // preserve starting point for caching - if (_cache.has(startKey)) return _cache.get(startKey); - const fsRoot = path.parse(dir).root; - - // Helper to safely check for existence - const exists = (p) => fs.pathExists(p); - - // Build checks: an array of { makePath: (dir) => string, weight } - const checks = []; - - const add = (rel, weight) => { - const makePath = (d) => (Array.isArray(rel) ? path.join(d, ...rel) : path.join(d, rel)); - checks.push({ makePath, weight }); - }; - - // Highest priority: explicit sentinel markers - add('.project-root', 110); - add('.workspace-root', 110); - add('.repo-root', 110); - - // Highest priority: VCS roots - add('.git', 100); - add('.hg', 95); - add('.svn', 95); - - // Monorepo/workspace indicators - add('pnpm-workspace.yaml', 90); - add('lerna.json', 90); - add('turbo.json', 90); - add('nx.json', 90); - add('rush.json', 90); - add('go.work', 90); - add('WORKSPACE', 90); - add('WORKSPACE.bazel', 90); - add('MODULE.bazel', 90); - add('pants.toml', 90); - - // Lockfiles and package-manager/top-level locks - add('yarn.lock', 85); - add('pnpm-lock.yaml', 85); - add('package-lock.json', 85); - add('bun.lockb', 85); - add('Cargo.lock', 85); - add('composer.lock', 85); - add('poetry.lock', 85); - add('Pipfile.lock', 85); - add('Gemfile.lock', 85); - - // Build-system root indicators - add('settings.gradle', 80); - add('settings.gradle.kts', 80); - add('gradlew', 80); - add('pom.xml', 80); - add('build.sbt', 80); - add(['project', 'build.properties'], 80); - - // Language/project config markers - add('deno.json', 75); - add('deno.jsonc', 75); - add('pyproject.toml', 75); - add('Pipfile', 75); - add('requirements.txt', 75); - add('go.mod', 75); - add('Cargo.toml', 75); - add('composer.json', 75); - add('mix.exs', 75); - add('Gemfile', 75); - add('CMakeLists.txt', 75); - add('stack.yaml', 75); - add('cabal.project', 75); - add('rebar.config', 75); - add('pubspec.yaml', 75); - add('flake.nix', 75); - add('shell.nix', 75); - add('default.nix', 75); - add('.tool-versions', 75); - add('package.json', 74); // generic Node project (lower than lockfiles/workspaces) - - // Changesets - add(['.changeset', 'config.json'], 70); - add('.changeset', 70); - - // Custom markers via env (comma-separated names) - if (process.env.PROJECT_ROOT_MARKERS) { - for (const name of process.env.PROJECT_ROOT_MARKERS.split(',') - .map((s) => s.trim()) - .filter(Boolean)) { - add(name, 72); - } - } - - /** Check for package.json with "workspaces" */ - const hasWorkspacePackageJson = async (d) => { - const pkgPath = path.join(d, 'package.json'); - if (!(await exists(pkgPath))) return false; - try { - const raw = await fs.readFile(pkgPath, 'utf8'); - const pkg = JSON.parse(raw); - return Boolean(pkg && pkg.workspaces); - } catch { - return false; - } - }; - - let best = null; // { dir, weight } - - // Try to detect VCS toplevel once up-front; treat as authoritative slightly above .git marker - const vcsTop = await _detectVcsTopLevel(dir); - if (vcsTop) { - best = { dir: vcsTop, weight: 101 }; - } - - while (true) { - // Special check: package.json with "workspaces" - if ((await hasWorkspacePackageJson(dir)) && (!best || 90 >= best.weight)) best = { dir, weight: 90 }; - - // Evaluate all other checks in parallel - const results = await Promise.all(checks.map(async (c) => ({ c, ok: await exists(c.makePath(dir)) }))); - - for (const { c, ok } of results) { - if (!ok) continue; - if (!best || c.weight >= best.weight) { - best = { dir, weight: c.weight }; - } - } - - if (dir === fsRoot) break; - dir = path.dirname(dir); - } - - const out = best ? best.dir : null; - _cache.set(startKey, out); - return out; - } catch { - return null; - } -} - -module.exports = { findProjectRoot }; diff --git a/tools/flattener/prompts.js b/tools/flattener/prompts.js deleted file mode 100644 index 849256d88..000000000 --- a/tools/flattener/prompts.js +++ /dev/null @@ -1,44 +0,0 @@ -const os = require('node:os'); -const path = require('node:path'); -const readline = require('node:readline'); -const process = require('node:process'); - -function expandHome(p) { - if (!p) return p; - if (p.startsWith('~')) return path.join(os.homedir(), p.slice(1)); - return p; -} - -function createRl() { - return readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); -} - -function promptQuestion(question) { - return new Promise((resolve) => { - const rl = createRl(); - rl.question(question, (answer) => { - rl.close(); - resolve(answer); - }); - }); -} - -async function promptYesNo(question, defaultYes = true) { - const suffix = defaultYes ? ' [Y/n] ' : ' [y/N] '; - const ans = (await promptQuestion(`${question}${suffix}`)).trim().toLowerCase(); - if (!ans) return defaultYes; - if (['y', 'yes'].includes(ans)) return true; - if (['n', 'no'].includes(ans)) return false; - return promptYesNo(question, defaultYes); -} - -async function promptPath(question, defaultValue) { - const prompt = `${question}${defaultValue ? ` (default: ${defaultValue})` : ''}: `; - const ans = (await promptQuestion(prompt)).trim(); - return expandHome(ans || defaultValue); -} - -module.exports = { promptYesNo, promptPath, promptQuestion, expandHome }; diff --git a/tools/flattener/stats.helpers.js b/tools/flattener/stats.helpers.js deleted file mode 100644 index 511bb0753..000000000 --- a/tools/flattener/stats.helpers.js +++ /dev/null @@ -1,368 +0,0 @@ -'use strict'; - -const fs = require('node:fs/promises'); -const path = require('node:path'); -const zlib = require('node:zlib'); -const { Buffer } = require('node:buffer'); -const crypto = require('node:crypto'); -const cp = require('node:child_process'); - -const KB = 1024; -const MB = 1024 * KB; - -const formatSize = (bytes) => { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; - return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; -}; - -const percentile = (sorted, p) => { - if (sorted.length === 0) return 0; - const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1)); - return sorted[idx]; -}; - -async function processWithLimit(items, fn, concurrency = 64) { - for (let i = 0; i < items.length; i += concurrency) { - await Promise.all(items.slice(i, i + concurrency).map(fn)); - } -} - -async function enrichAllFiles(textFiles, binaryFiles) { - /** @type {Array<{ path: string; absolutePath: string; size: number; lines?: number; isBinary: boolean; ext: string; dir: string; depth: number; hidden: boolean; mtimeMs: number; isSymlink: boolean; }>} */ - const allFiles = []; - - async function enrich(file, isBinary) { - const ext = (path.extname(file.path) || '').toLowerCase(); - const dir = path.dirname(file.path) || '.'; - const depth = file.path.split(path.sep).filter(Boolean).length; - const hidden = file.path.split(path.sep).some((seg) => seg.startsWith('.')); - let mtimeMs = 0; - let isSymlink = false; - try { - const lst = await fs.lstat(file.absolutePath); - mtimeMs = lst.mtimeMs; - isSymlink = lst.isSymbolicLink(); - } catch { - /* ignore lstat errors during enrichment */ - } - allFiles.push({ - path: file.path, - absolutePath: file.absolutePath, - size: file.size || 0, - lines: file.lines, - isBinary, - ext, - dir, - depth, - hidden, - mtimeMs, - isSymlink, - }); - } - - await processWithLimit(textFiles, (f) => enrich(f, false)); - await processWithLimit(binaryFiles, (f) => enrich(f, true)); - return allFiles; -} - -function buildHistogram(allFiles) { - const buckets = [ - [1 * KB, '0โ€“1KB'], - [10 * KB, '1โ€“10KB'], - [100 * KB, '10โ€“100KB'], - [1 * MB, '100KBโ€“1MB'], - [10 * MB, '1โ€“10MB'], - [100 * MB, '10โ€“100MB'], - [Infinity, '>=100MB'], - ]; - const histogram = buckets.map(([_, label]) => ({ label, count: 0, bytes: 0 })); - for (const f of allFiles) { - for (const [i, bucket] of buckets.entries()) { - if (f.size < bucket[0]) { - histogram[i].count++; - histogram[i].bytes += f.size; - break; - } - } - } - return histogram; -} - -function aggregateByExtension(allFiles) { - const byExtension = new Map(); - for (const f of allFiles) { - const key = f.ext || ''; - const v = byExtension.get(key) || { ext: key, count: 0, bytes: 0 }; - v.count++; - v.bytes += f.size; - byExtension.set(key, v); - } - return [...byExtension.values()].sort((a, b) => b.bytes - a.bytes); -} - -function aggregateByDirectory(allFiles) { - const byDirectory = new Map(); - function addDirBytes(dir, bytes) { - const v = byDirectory.get(dir) || { dir, count: 0, bytes: 0 }; - v.count++; - v.bytes += bytes; - byDirectory.set(dir, v); - } - for (const f of allFiles) { - const parts = f.dir === '.' ? [] : f.dir.split(path.sep); - let acc = ''; - for (let i = 0; i < parts.length; i++) { - acc = i === 0 ? parts[0] : acc + path.sep + parts[i]; - addDirBytes(acc, f.size); - } - if (parts.length === 0) addDirBytes('.', f.size); - } - return [...byDirectory.values()].sort((a, b) => b.bytes - a.bytes); -} - -function computeDepthAndLongest(allFiles) { - const depthDistribution = new Map(); - for (const f of allFiles) { - depthDistribution.set(f.depth, (depthDistribution.get(f.depth) || 0) + 1); - } - const longestPaths = [...allFiles] - .sort((a, b) => b.path.length - a.path.length) - .slice(0, 25) - .map((f) => ({ path: f.path, length: f.path.length, size: f.size })); - const depthDist = [...depthDistribution.entries()].sort((a, b) => a[0] - b[0]).map(([depth, count]) => ({ depth, count })); - return { depthDist, longestPaths }; -} - -function computeTemporal(allFiles, nowMs) { - let oldest = null, - newest = null; - const ageBuckets = [ - { label: '> 1 year', minDays: 365, maxDays: Infinity, count: 0, bytes: 0 }, - { label: '6โ€“12 months', minDays: 180, maxDays: 365, count: 0, bytes: 0 }, - { label: '1โ€“6 months', minDays: 30, maxDays: 180, count: 0, bytes: 0 }, - { label: '7โ€“30 days', minDays: 7, maxDays: 30, count: 0, bytes: 0 }, - { label: '1โ€“7 days', minDays: 1, maxDays: 7, count: 0, bytes: 0 }, - { label: '< 1 day', minDays: 0, maxDays: 1, count: 0, bytes: 0 }, - ]; - for (const f of allFiles) { - const ageDays = Math.max(0, (nowMs - (f.mtimeMs || nowMs)) / (24 * 60 * 60 * 1000)); - for (const b of ageBuckets) { - if (ageDays >= b.minDays && ageDays < b.maxDays) { - b.count++; - b.bytes += f.size; - break; - } - } - if (!oldest || f.mtimeMs < oldest.mtimeMs) oldest = f; - if (!newest || f.mtimeMs > newest.mtimeMs) newest = f; - } - return { - oldest: oldest ? { path: oldest.path, mtime: oldest.mtimeMs ? new Date(oldest.mtimeMs).toISOString() : null } : null, - newest: newest ? { path: newest.path, mtime: newest.mtimeMs ? new Date(newest.mtimeMs).toISOString() : null } : null, - ageBuckets, - }; -} - -function computeQuality(allFiles, textFiles) { - const zeroByteFiles = allFiles.filter((f) => f.size === 0).length; - const emptyTextFiles = textFiles.filter((f) => (f.size || 0) === 0 || (f.lines || 0) === 0).length; - const hiddenFiles = allFiles.filter((f) => f.hidden).length; - const symlinks = allFiles.filter((f) => f.isSymlink).length; - const largeThreshold = 50 * MB; - const suspiciousThreshold = 100 * MB; - const largeFilesCount = allFiles.filter((f) => f.size >= largeThreshold).length; - const suspiciousLargeFilesCount = allFiles.filter((f) => f.size >= suspiciousThreshold).length; - return { - zeroByteFiles, - emptyTextFiles, - hiddenFiles, - symlinks, - largeFilesCount, - suspiciousLargeFilesCount, - largeThreshold, - }; -} - -function computeDuplicates(allFiles, textFiles) { - const duplicatesBySize = new Map(); - for (const f of allFiles) { - const key = String(f.size); - const arr = duplicatesBySize.get(key) || []; - arr.push(f); - duplicatesBySize.set(key, arr); - } - const duplicateCandidates = []; - for (const [sizeKey, arr] of duplicatesBySize.entries()) { - if (arr.length < 2) continue; - const textGroup = arr.filter((f) => !f.isBinary); - const otherGroup = arr.filter((f) => f.isBinary); - const contentHashGroups = new Map(); - for (const tf of textGroup) { - try { - const src = textFiles.find((x) => x.absolutePath === tf.absolutePath); - const content = src ? src.content : ''; - const h = crypto.createHash('sha1').update(content).digest('hex'); - const g = contentHashGroups.get(h) || []; - g.push(tf); - contentHashGroups.set(h, g); - } catch { - /* ignore hashing errors for duplicate detection */ - } - } - for (const [_h, g] of contentHashGroups.entries()) { - if (g.length > 1) - duplicateCandidates.push({ - reason: 'same-size+text-hash', - size: Number(sizeKey), - count: g.length, - files: g.map((f) => f.path), - }); - } - if (otherGroup.length > 1) { - duplicateCandidates.push({ - reason: 'same-size', - size: Number(sizeKey), - count: otherGroup.length, - files: otherGroup.map((f) => f.path), - }); - } - } - return duplicateCandidates; -} - -function estimateCompressibility(textFiles) { - let compSampleBytes = 0; - let compCompressedBytes = 0; - for (const tf of textFiles) { - try { - const sampleLen = Math.min(256 * 1024, tf.size || 0); - if (sampleLen <= 0) continue; - const sample = tf.content.slice(0, sampleLen); - const gz = zlib.gzipSync(Buffer.from(sample, 'utf8')); - compSampleBytes += sampleLen; - compCompressedBytes += gz.length; - } catch { - /* ignore compression errors during sampling */ - } - } - return compSampleBytes > 0 ? compCompressedBytes / compSampleBytes : null; -} - -function computeGitInfo(allFiles, rootDir, largeThreshold) { - const info = { - isRepo: false, - trackedCount: 0, - trackedBytes: 0, - untrackedCount: 0, - untrackedBytes: 0, - lfsCandidates: [], - }; - try { - if (!rootDir) return info; - const top = cp - .execFileSync('git', ['rev-parse', '--show-toplevel'], { - cwd: rootDir, - stdio: ['ignore', 'pipe', 'ignore'], - }) - .toString() - .trim(); - if (!top) return info; - info.isRepo = true; - const out = cp.execFileSync('git', ['ls-files', '-z'], { - cwd: rootDir, - stdio: ['ignore', 'pipe', 'ignore'], - }); - const tracked = new Set(out.toString().split('\0').filter(Boolean)); - let trackedBytes = 0, - trackedCount = 0, - untrackedBytes = 0, - untrackedCount = 0; - const lfsCandidates = []; - for (const f of allFiles) { - const isTracked = tracked.has(f.path); - if (isTracked) { - trackedCount++; - trackedBytes += f.size; - if (f.size >= largeThreshold) lfsCandidates.push({ path: f.path, size: f.size }); - } else { - untrackedCount++; - untrackedBytes += f.size; - } - } - info.trackedCount = trackedCount; - info.trackedBytes = trackedBytes; - info.untrackedCount = untrackedCount; - info.untrackedBytes = untrackedBytes; - info.lfsCandidates = lfsCandidates.sort((a, b) => b.size - a.size).slice(0, 50); - } catch { - /* git not available or not a repo, ignore */ - } - return info; -} - -function computeLargestFiles(allFiles, totalBytes) { - const toPct = (num, den) => (den === 0 ? 0 : (num / den) * 100); - return [...allFiles] - .sort((a, b) => b.size - a.size) - .slice(0, 50) - .map((f) => ({ - path: f.path, - size: f.size, - sizeFormatted: formatSize(f.size), - percentOfTotal: toPct(f.size, totalBytes), - ext: f.ext || '', - isBinary: f.isBinary, - mtime: f.mtimeMs ? new Date(f.mtimeMs).toISOString() : null, - })); -} - -function mdTable(rows, headers) { - const header = `| ${headers.join(' | ')} |`; - const sep = `| ${headers.map(() => '---').join(' | ')} |`; - const body = rows.map((r) => `| ${r.join(' | ')} |`).join('\n'); - return `${header}\n${sep}\n${body}`; -} - -function buildMarkdownReport(largestFiles, byExtensionArr, byDirectoryArr, totalBytes) { - const toPct = (num, den) => (den === 0 ? 0 : (num / den) * 100); - const md = []; - md.push( - '\n### Top Largest Files (Top 50)\n', - mdTable( - largestFiles.map((f) => [f.path, f.sizeFormatted, `${f.percentOfTotal.toFixed(2)}%`, f.ext || '', f.isBinary ? 'binary' : 'text']), - ['Path', 'Size', '% of total', 'Ext', 'Type'], - ), - '\n\n### Top Extensions by Bytes (Top 20)\n', - ); - const topExtRows = byExtensionArr - .slice(0, 20) - .map((e) => [e.ext, String(e.count), formatSize(e.bytes), `${toPct(e.bytes, totalBytes).toFixed(2)}%`]); - md.push(mdTable(topExtRows, ['Ext', 'Count', 'Bytes', '% of total']), '\n\n### Top Directories by Bytes (Top 20)\n'); - const topDirRows = byDirectoryArr - .slice(0, 20) - .map((d) => [d.dir, String(d.count), formatSize(d.bytes), `${toPct(d.bytes, totalBytes).toFixed(2)}%`]); - md.push(mdTable(topDirRows, ['Directory', 'Files', 'Bytes', '% of total'])); - return md.join('\n'); -} - -module.exports = { - KB, - MB, - formatSize, - percentile, - processWithLimit, - enrichAllFiles, - buildHistogram, - aggregateByExtension, - aggregateByDirectory, - computeDepthAndLongest, - computeTemporal, - computeQuality, - computeDuplicates, - estimateCompressibility, - computeGitInfo, - computeLargestFiles, - buildMarkdownReport, -}; diff --git a/tools/flattener/stats.js b/tools/flattener/stats.js deleted file mode 100644 index b41d50e57..000000000 --- a/tools/flattener/stats.js +++ /dev/null @@ -1,75 +0,0 @@ -const H = require('./stats.helpers.js'); - -async function calculateStatistics(aggregatedContent, xmlFileSize, rootDir) { - const { textFiles, binaryFiles, errors } = aggregatedContent; - - const totalLines = textFiles.reduce((sum, f) => sum + (f.lines || 0), 0); - const estimatedTokens = Math.ceil(xmlFileSize / 4); - - // Build enriched file list - const allFiles = await H.enrichAllFiles(textFiles, binaryFiles); - const totalBytes = allFiles.reduce((s, f) => s + f.size, 0); - const sizes = allFiles.map((f) => f.size).sort((a, b) => a - b); - const avgSize = sizes.length > 0 ? totalBytes / sizes.length : 0; - const medianSize = sizes.length > 0 ? H.percentile(sizes, 50) : 0; - const p90 = H.percentile(sizes, 90); - const p95 = H.percentile(sizes, 95); - const p99 = H.percentile(sizes, 99); - - const histogram = H.buildHistogram(allFiles); - const byExtensionArr = H.aggregateByExtension(allFiles); - const byDirectoryArr = H.aggregateByDirectory(allFiles); - const { depthDist, longestPaths } = H.computeDepthAndLongest(allFiles); - const temporal = H.computeTemporal(allFiles, Date.now()); - const quality = H.computeQuality(allFiles, textFiles); - const duplicateCandidates = H.computeDuplicates(allFiles, textFiles); - const compressibilityRatio = H.estimateCompressibility(textFiles); - const git = H.computeGitInfo(allFiles, rootDir, quality.largeThreshold); - const largestFiles = H.computeLargestFiles(allFiles, totalBytes); - const markdownReport = H.buildMarkdownReport(largestFiles, byExtensionArr, byDirectoryArr, totalBytes); - - return { - // Back-compat summary - totalFiles: textFiles.length + binaryFiles.length, - textFiles: textFiles.length, - binaryFiles: binaryFiles.length, - errorFiles: errors.length, - totalSize: H.formatSize(totalBytes), - totalBytes, - xmlSize: H.formatSize(xmlFileSize), - totalLines, - estimatedTokens: estimatedTokens.toLocaleString(), - - // Distributions and percentiles - avgFileSize: avgSize, - medianFileSize: medianSize, - p90, - p95, - p99, - histogram, - - // Extensions and directories - byExtension: byExtensionArr, - byDirectory: byDirectoryArr, - depthDistribution: depthDist, - longestPaths, - - // Temporal - temporal, - - // Quality signals - quality, - - // Duplicates and compressibility - duplicateCandidates, - compressibilityRatio, - - // Git-aware - git, - - largestFiles, - markdownReport, - }; -} - -module.exports = { calculateStatistics }; diff --git a/tools/flattener/test-matrix.js b/tools/flattener/test-matrix.js deleted file mode 100644 index 0d9f6437e..000000000 --- a/tools/flattener/test-matrix.js +++ /dev/null @@ -1,409 +0,0 @@ -/* deno-lint-ignore-file */ -/* - Automatic test matrix for project root detection. - Creates temporary fixtures for various ecosystems and validates findProjectRoot(). - No external options or flags required. Safe to run multiple times. -*/ - -const os = require('node:os'); -const path = require('node:path'); -const fs = require('fs-extra'); -const { promisify } = require('node:util'); -const { execFile } = require('node:child_process'); -const process = require('node:process'); -const execFileAsync = promisify(execFile); - -const { findProjectRoot } = require('./projectRoot.js'); - -async function cmdAvailable(cmd) { - try { - await execFileAsync(cmd, ['--version'], { timeout: 500, windowsHide: true }); - return true; - } catch { - return false; - } - - async function testSvnMarker() { - const root = await mkTmpDir('svn'); - const nested = path.join(root, 'proj', 'code'); - await fs.ensureDir(nested); - await fs.ensureDir(path.join(root, '.svn')); - const found = await findProjectRoot(nested); - assertEqual(found, root, '.svn marker should be detected'); - return { name: 'svn-marker', ok: true }; - } - - async function testSymlinkStart() { - const root = await mkTmpDir('symlink-start'); - const nested = path.join(root, 'a', 'b'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, '.project-root'), '\n'); - const tmp = await mkTmpDir('symlink-tmp'); - const link = path.join(tmp, 'link-to-b'); - try { - await fs.symlink(nested, link); - } catch { - // symlink may not be permitted on some systems; skip - return { name: 'symlink-start', ok: true, skipped: true }; - } - const found = await findProjectRoot(link); - assertEqual(found, root, 'should resolve symlinked start to real root'); - return { name: 'symlink-start', ok: true }; - } - - async function testSubmoduleLikeInnerGitFile() { - const root = await mkTmpDir('submodule-like'); - const mid = path.join(root, 'mid'); - const leaf = path.join(mid, 'leaf'); - await fs.ensureDir(leaf); - // outer repo - await fs.ensureDir(path.join(root, '.git')); - // inner submodule-like .git file - await fs.writeFile(path.join(mid, '.git'), 'gitdir: ../.git/modules/mid\n'); - const found = await findProjectRoot(leaf); - assertEqual(found, root, 'outermost .git should win on tie weight'); - return { name: 'submodule-like-gitfile', ok: true }; - } -} - -async function mkTmpDir(name) { - const base = await fs.realpath(os.tmpdir()); - const dir = await fs.mkdtemp(path.join(base, `flattener-${name}-`)); - return dir; -} - -function assertEqual(actual, expected, msg) { - if (actual !== expected) { - throw new Error(`${msg}: expected="${expected}" actual="${actual}"`); - } -} - -async function testSentinel() { - const root = await mkTmpDir('sentinel'); - const nested = path.join(root, 'a', 'b', 'c'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, '.project-root'), '\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'sentinel .project-root should win'); - return { name: 'sentinel', ok: true }; -} - -async function testOtherSentinels() { - const root = await mkTmpDir('other-sentinels'); - const nested = path.join(root, 'x', 'y'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, '.workspace-root'), '\n'); - const found1 = await findProjectRoot(nested); - assertEqual(found1, root, 'sentinel .workspace-root should win'); - - await fs.remove(path.join(root, '.workspace-root')); - await fs.writeFile(path.join(root, '.repo-root'), '\n'); - const found2 = await findProjectRoot(nested); - assertEqual(found2, root, 'sentinel .repo-root should win'); - return { name: 'other-sentinels', ok: true }; -} - -async function testGitCliAndMarker() { - const hasGit = await cmdAvailable('git'); - if (!hasGit) return { name: 'git-cli', ok: true, skipped: true }; - - const root = await mkTmpDir('git'); - const nested = path.join(root, 'pkg', 'src'); - await fs.ensureDir(nested); - await execFileAsync('git', ['init'], { cwd: root, timeout: 2000 }); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'git toplevel should be detected'); - return { name: 'git-cli', ok: true }; -} - -async function testHgMarkerOrCli() { - // Prefer simple marker test to avoid requiring Mercurial install - const root = await mkTmpDir('hg'); - const nested = path.join(root, 'lib'); - await fs.ensureDir(nested); - await fs.ensureDir(path.join(root, '.hg')); - const found = await findProjectRoot(nested); - await assertEqual(found, root, '.hg marker should be detected'); - return { name: 'hg-marker', ok: true }; -} - -async function testWorkspacePnpm() { - const root = await mkTmpDir('pnpm-workspace'); - const pkgA = path.join(root, 'packages', 'a'); - await fs.ensureDir(pkgA); - await fs.writeFile(path.join(root, 'pnpm-workspace.yaml'), 'packages:\n - packages/*\n'); - const found = await findProjectRoot(pkgA); - await assertEqual(found, root, 'pnpm-workspace.yaml should be detected'); - return { name: 'pnpm-workspace', ok: true }; -} - -async function testPackageJsonWorkspaces() { - const root = await mkTmpDir('package-workspaces'); - const pkgA = path.join(root, 'packages', 'a'); - await fs.ensureDir(pkgA); - await fs.writeJson(path.join(root, 'package.json'), { private: true, workspaces: ['packages/*'] }, { spaces: 2 }); - const found = await findProjectRoot(pkgA); - await assertEqual(found, root, 'package.json workspaces should be detected'); - return { name: 'package.json-workspaces', ok: true }; -} - -async function testLockfiles() { - const root = await mkTmpDir('lockfiles'); - const nested = path.join(root, 'src'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'yarn.lock'), '\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'yarn.lock should be detected'); - return { name: 'lockfiles', ok: true }; -} - -async function testLanguageConfigs() { - const root = await mkTmpDir('lang-configs'); - const nested = path.join(root, 'x', 'y'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'pyproject.toml'), "[tool.poetry]\nname='tmp'\n"); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'pyproject.toml should be detected'); - return { name: 'language-configs', ok: true }; -} - -async function testPreferOuterOnTie() { - const root = await mkTmpDir('tie'); - const mid = path.join(root, 'mid'); - const leaf = path.join(mid, 'leaf'); - await fs.ensureDir(leaf); - // same weight marker at two levels - await fs.writeFile(path.join(root, 'requirements.txt'), '\n'); - await fs.writeFile(path.join(mid, 'requirements.txt'), '\n'); - const found = await findProjectRoot(leaf); - await assertEqual(found, root, 'outermost directory should win on equal weight'); - return { name: 'prefer-outermost-tie', ok: true }; -} - -// Additional coverage: Bazel, Nx/Turbo/Rush, Go workspaces, Deno, Java/Scala, PHP, Rust, Nix, Changesets, env markers, -// and priority interaction between package.json and lockfiles. - -async function testBazelWorkspace() { - const root = await mkTmpDir('bazel'); - const nested = path.join(root, 'apps', 'svc'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'WORKSPACE'), 'workspace(name="tmp")\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'Bazel WORKSPACE should be detected'); - return { name: 'bazel-workspace', ok: true }; -} - -async function testNx() { - const root = await mkTmpDir('nx'); - const nested = path.join(root, 'apps', 'web'); - await fs.ensureDir(nested); - await fs.writeJson(path.join(root, 'nx.json'), { npmScope: 'tmp' }, { spaces: 2 }); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'nx.json should be detected'); - return { name: 'nx', ok: true }; -} - -async function testTurbo() { - const root = await mkTmpDir('turbo'); - const nested = path.join(root, 'packages', 'x'); - await fs.ensureDir(nested); - await fs.writeJson(path.join(root, 'turbo.json'), { pipeline: {} }, { spaces: 2 }); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'turbo.json should be detected'); - return { name: 'turbo', ok: true }; -} - -async function testRush() { - const root = await mkTmpDir('rush'); - const nested = path.join(root, 'apps', 'a'); - await fs.ensureDir(nested); - await fs.writeJson(path.join(root, 'rush.json'), { projectFolderMinDepth: 1 }, { spaces: 2 }); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'rush.json should be detected'); - return { name: 'rush', ok: true }; -} - -async function testGoWorkAndMod() { - const root = await mkTmpDir('gowork'); - const mod = path.join(root, 'modA'); - const nested = path.join(mod, 'pkg'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'go.work'), 'go 1.22\nuse ./modA\n'); - await fs.writeFile(path.join(mod, 'go.mod'), 'module example.com/a\ngo 1.22\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'go.work should define the workspace root'); - return { name: 'go-work', ok: true }; -} - -async function testDenoJson() { - const root = await mkTmpDir('deno'); - const nested = path.join(root, 'src'); - await fs.ensureDir(nested); - await fs.writeJson(path.join(root, 'deno.json'), { tasks: {} }, { spaces: 2 }); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'deno.json should be detected'); - return { name: 'deno-json', ok: true }; -} - -async function testGradleSettings() { - const root = await mkTmpDir('gradle'); - const nested = path.join(root, 'app'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'settings.gradle'), "rootProject.name='tmp'\n"); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'settings.gradle should be detected'); - return { name: 'gradle-settings', ok: true }; -} - -async function testMavenPom() { - const root = await mkTmpDir('maven'); - const nested = path.join(root, 'module'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'pom.xml'), '\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'pom.xml should be detected'); - return { name: 'maven-pom', ok: true }; -} - -async function testSbtBuild() { - const root = await mkTmpDir('sbt'); - const nested = path.join(root, 'sub'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'build.sbt'), 'name := "tmp"\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'build.sbt should be detected'); - return { name: 'sbt-build', ok: true }; -} - -async function testComposer() { - const root = await mkTmpDir('composer'); - const nested = path.join(root, 'src'); - await fs.ensureDir(nested); - await fs.writeJson(path.join(root, 'composer.json'), { name: 'tmp/pkg' }, { spaces: 2 }); - await fs.writeFile(path.join(root, 'composer.lock'), '{}\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'composer.{json,lock} should be detected'); - return { name: 'composer', ok: true }; -} - -async function testCargo() { - const root = await mkTmpDir('cargo'); - const nested = path.join(root, 'src'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'Cargo.toml'), "[package]\nname='tmp'\nversion='0.0.0'\n"); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'Cargo.toml should be detected'); - return { name: 'cargo', ok: true }; -} - -async function testNixFlake() { - const root = await mkTmpDir('nix'); - const nested = path.join(root, 'work'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'flake.nix'), '{ }\n'); - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'flake.nix should be detected'); - return { name: 'nix-flake', ok: true }; -} - -async function testChangesetConfig() { - const root = await mkTmpDir('changeset'); - const nested = path.join(root, 'pkg'); - await fs.ensureDir(nested); - await fs.ensureDir(path.join(root, '.changeset')); - await fs.writeJson( - path.join(root, '.changeset', 'config.json'), - { $schema: 'https://unpkg.com/@changesets/config@2.3.1/schema.json' }, - { spaces: 2 }, - ); - const found = await findProjectRoot(nested); - await assertEqual(found, root, '.changeset/config.json should be detected'); - return { name: 'changesets', ok: true }; -} - -async function testEnvCustomMarker() { - const root = await mkTmpDir('env-marker'); - const nested = path.join(root, 'dir'); - await fs.ensureDir(nested); - await fs.writeFile(path.join(root, 'MY_ROOT'), '\n'); - const prev = process.env.PROJECT_ROOT_MARKERS; - process.env.PROJECT_ROOT_MARKERS = 'MY_ROOT'; - try { - const found = await findProjectRoot(nested); - await assertEqual(found, root, 'custom env marker should be honored'); - } finally { - if (prev === undefined) delete process.env.PROJECT_ROOT_MARKERS; - else process.env.PROJECT_ROOT_MARKERS = prev; - } - return { name: 'env-custom-marker', ok: true }; -} - -async function testPackageLowPriorityVsLock() { - const root = await mkTmpDir('pkg-vs-lock'); - const nested = path.join(root, 'nested'); - await fs.ensureDir(path.join(nested, 'deep')); - await fs.writeJson(path.join(nested, 'package.json'), { name: 'nested' }, { spaces: 2 }); - await fs.writeFile(path.join(root, 'yarn.lock'), '\n'); - const found = await findProjectRoot(path.join(nested, 'deep')); - await assertEqual(found, root, 'lockfile at root should outrank nested package.json'); - return { name: 'package-vs-lock-priority', ok: true }; -} - -async function run() { - const tests = [ - testSentinel, - testOtherSentinels, - testGitCliAndMarker, - testHgMarkerOrCli, - testWorkspacePnpm, - testPackageJsonWorkspaces, - testLockfiles, - testLanguageConfigs, - testPreferOuterOnTie, - testBazelWorkspace, - testNx, - testTurbo, - testRush, - testGoWorkAndMod, - testDenoJson, - testGradleSettings, - testMavenPom, - testSbtBuild, - testComposer, - testCargo, - testNixFlake, - testChangesetConfig, - testEnvCustomMarker, - testPackageLowPriorityVsLock, - testSvnMarker, - testSymlinkStart, - testSubmoduleLikeInnerGitFile, - ]; - - const results = []; - for (const t of tests) { - try { - const r = await t(); - results.push({ ...r, ok: true }); - console.log(`โœ” ${r.name}${r.skipped ? ' (skipped)' : ''}`); - } catch (error) { - console.error(`โœ– ${t.name}:`, error && error.message ? error.message : error); - results.push({ name: t.name, ok: false, error: String(error) }); - } - } - - const failed = results.filter((r) => !r.ok); - console.log('\nSummary:'); - for (const r of results) { - console.log(`- ${r.name}: ${r.ok ? 'ok' : 'FAIL'}${r.skipped ? ' (skipped)' : ''}`); - } - - if (failed.length > 0) { - process.exitCode = 1; - } -} - -run().catch((error) => { - console.error('Fatal error:', error); - process.exit(1); -}); diff --git a/tools/flattener/xml.js b/tools/flattener/xml.js deleted file mode 100644 index 229f9a880..000000000 --- a/tools/flattener/xml.js +++ /dev/null @@ -1,82 +0,0 @@ -const fs = require('fs-extra'); -const { escapeXml } = require('../lib/xml-utils'); - -function indentFileContent(content) { - if (typeof content !== 'string') { - return String(content); - } - return content.split('\n').map((line) => ` ${line}`); -} - -function generateXMLOutput(aggregatedContent, outputPath) { - const { textFiles } = aggregatedContent; - const writeStream = fs.createWriteStream(outputPath, { encoding: 'utf8' }); - - return new Promise((resolve, reject) => { - writeStream.on('error', reject); - writeStream.on('finish', resolve); - - writeStream.write('\n'); - writeStream.write('\n'); - - // Sort files by path for deterministic order - const filesSorted = [...textFiles].sort((a, b) => a.path.localeCompare(b.path)); - let index = 0; - - const writeNext = () => { - if (index >= filesSorted.length) { - writeStream.write('\n'); - writeStream.end(); - return; - } - - const file = filesSorted[index++]; - const p = escapeXml(file.path); - const content = typeof file.content === 'string' ? file.content : ''; - - if (content.length === 0) { - writeStream.write(`\t\n`); - setTimeout(writeNext, 0); - return; - } - - const needsCdata = content.includes('<') || content.includes('&') || content.includes(']]>'); - if (needsCdata) { - // Open tag and CDATA on their own line with tab indent; content lines indented with two tabs - writeStream.write(`\t" inside content, trim trailing newlines, indent each line with two tabs - const safe = content.replaceAll(']]>', ']]]]>'); - const trimmed = safe.replace(/[\r\n]+$/, ''); - const indented = - trimmed.length > 0 - ? trimmed - .split('\n') - .map((line) => `\t\t${line}`) - .join('\n') - : ''; - writeStream.write(indented); - // Close CDATA and attach closing tag directly after the last content line - writeStream.write(']]>\n'); - } else { - // Write opening tag then newline; indent content with two tabs; attach closing tag directly after last content char - writeStream.write(`\t\n`); - const trimmed = content.replace(/[\r\n]+$/, ''); - const indented = - trimmed.length > 0 - ? trimmed - .split('\n') - .map((line) => `\t\t${line}`) - .join('\n') - : ''; - writeStream.write(indented); - writeStream.write(`\n`); - } - - setTimeout(writeNext, 0); - }; - - writeNext(); - }); -} - -module.exports = { generateXMLOutput }; diff --git a/tools/schema/agent.js b/tools/schema/agent.js index 7d106e616..b6a36a985 100644 --- a/tools/schema/agent.js +++ b/tools/schema/agent.js @@ -210,7 +210,6 @@ function buildAgentSchema(expectedModule) { critical_actions: z.array(createNonEmptyString('agent.critical_actions[]')).optional(), menu: z.array(buildMenuItemSchema()).min(1, { message: 'agent.menu must include at least one entry' }), prompts: z.array(buildPromptSchema()).optional(), - webskip: z.boolean().optional(), discussion: z.boolean().optional(), conversational_knowledge: z.array(z.object({}).passthrough()).min(1).optional(), })