chore: add eslint config and apply code style fixes across codebase
This commit is contained in:
parent
3efcfd54d4
commit
d44aa4fed5
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ""
|
||||
labels: ""
|
||||
assignees: ""
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ""
|
||||
labels: ""
|
||||
assignees: ""
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Did you discuss the idea first in Discord Server (#general-dev)**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
name: Discord Notification
|
||||
|
||||
on: [pull_request, release, create, delete, issue_comment, pull_request_review, pull_request_review_comment]
|
||||
on:
|
||||
[
|
||||
pull_request,
|
||||
release,
|
||||
create,
|
||||
delete,
|
||||
issue_comment,
|
||||
pull_request_review,
|
||||
pull_request_review_comment,
|
||||
]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
name: format-check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ["**"]
|
||||
|
||||
jobs:
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Prettier format check
|
||||
run: npm run format:check
|
||||
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: ESLint
|
||||
run: npm run lint
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name: Release
|
||||
'on':
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
|
@ -22,7 +22,7 @@ permissions:
|
|||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
if: '!contains(github.event.head_commit.message, ''[skip ci]'')'
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: "20"
|
||||
cache: npm
|
||||
registry-url: https://registry.npmjs.org
|
||||
- name: Install dependencies
|
||||
|
|
|
|||
|
|
@ -9,7 +9,12 @@
|
|||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": ["package.json", "package-lock.json", "tools/installer/package.json", "CHANGELOG.md"],
|
||||
"assets": [
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"tools/installer/package.json",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -40,5 +40,30 @@
|
|||
"tileset",
|
||||
"Trae",
|
||||
"VNET"
|
||||
]
|
||||
],
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["package.json"],
|
||||
"url": "https://json.schemastore.org/package.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [".vscode/settings.json"],
|
||||
"url": "vscode://schemas/settings/folder"
|
||||
}
|
||||
],
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[yaml]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"prettier.prettierPath": "node_modules/prettier",
|
||||
"prettier.requireConfig": true,
|
||||
"yaml.format.enable": false,
|
||||
"eslint.useFlatConfig": true,
|
||||
"eslint.validate": ["javascript", "yaml"],
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"editor.rulers": [100]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ bundle:
|
|||
description: Includes every core system agent.
|
||||
agents:
|
||||
- bmad-orchestrator
|
||||
- '*'
|
||||
- "*"
|
||||
workflows:
|
||||
- brownfield-fullstack.yaml
|
||||
- brownfield-service.yaml
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# architect
|
||||
|
||||
|
||||
ACTIVATION-NOTICE: This file contains your full agent operating guidelines. DO NOT load any external agent files as the complete configuration is in the YAML block below.
|
||||
|
||||
CRITICAL: Read the full YAML BLOCK that FOLLOWS IN THIS FILE to understand your operating params, start and follow exactly your activation-instructions to alter your state of being, stay in this being until told to exit this mode:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# BMad Master
|
||||
|
||||
|
||||
ACTIVATION-NOTICE: This file contains your full agent operating guidelines. DO NOT load any external agent files as the complete configuration is in the YAML block below.
|
||||
|
||||
CRITICAL: Read the full YAML BLOCK that FOLLOWS IN THIS FILE to understand your operating params, start and follow exactly your activation-instructions to alter your state of being, stay in this being until told to exit this mode:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# BMad Web Orchestrator
|
||||
|
||||
|
||||
ACTIVATION-NOTICE: This file contains your full agent operating guidelines. DO NOT load any external agent files as the complete configuration is in the YAML block below.
|
||||
|
||||
CRITICAL: Read the full YAML BLOCK that FOLLOWS IN THIS FILE to understand your operating params, start and follow exactly your activation-instructions to alter your state of being, stay in this being until told to exit this mode:
|
||||
|
|
@ -132,7 +131,7 @@ workflow-guidance:
|
|||
- Understand each workflow's purpose, options, and decision points
|
||||
- Ask clarifying questions based on the workflow's structure
|
||||
- Guide users through workflow selection when multiple options exist
|
||||
- When appropriate, suggest: "Would you like me to create a detailed workflow plan before starting?"
|
||||
- When appropriate, suggest: 'Would you like me to create a detailed workflow plan before starting?'
|
||||
- For workflows with divergent paths, help users choose the right path
|
||||
- Adapt questions to the specific domain (e.g., game dev vs infrastructure vs web dev)
|
||||
- Only recommend workflows that actually exist in the current bundle
|
||||
|
|
|
|||
|
|
@ -35,10 +35,9 @@ agent:
|
|||
id: dev
|
||||
title: Full Stack Developer
|
||||
icon: 💻
|
||||
whenToUse: "Use for code implementation, debugging, refactoring, and development best practices"
|
||||
whenToUse: 'Use for code implementation, debugging, refactoring, and development best practices'
|
||||
customization:
|
||||
|
||||
|
||||
persona:
|
||||
role: Expert Senior Software Engineer & Implementation Specialist
|
||||
style: Extremely concise, pragmatic, detail-oriented, solution-focused
|
||||
|
|
@ -58,13 +57,13 @@ commands:
|
|||
- explain: teach me what and why you did whatever you just did in detail so I can learn. Explain to me as if you were training a junior engineer.
|
||||
- exit: Say goodbye as the Developer, and then abandon inhabiting this persona
|
||||
- develop-story:
|
||||
- order-of-execution: "Read (first or next) task→Implement Task and its subtasks→Write tests→Execute validations→Only if ALL pass, then update the task checkbox with [x]→Update story section File List to ensure it lists and new or modified or deleted source file→repeat order-of-execution until complete"
|
||||
- order-of-execution: 'Read (first or next) task→Implement Task and its subtasks→Write tests→Execute validations→Only if ALL pass, then update the task checkbox with [x]→Update story section File List to ensure it lists and new or modified or deleted source file→repeat order-of-execution until complete'
|
||||
- story-file-updates-ONLY:
|
||||
- CRITICAL: ONLY UPDATE THE STORY FILE WITH UPDATES TO SECTIONS INDICATED BELOW. DO NOT MODIFY ANY OTHER SECTIONS.
|
||||
- CRITICAL: You are ONLY authorized to edit these specific sections of story files - Tasks / Subtasks Checkboxes, Dev Agent Record section and all its subsections, Agent Model Used, Debug Log References, Completion Notes List, File List, Change Log, Status
|
||||
- CRITICAL: DO NOT modify Status, Story, Acceptance Criteria, Dev Notes, Testing sections, or any other sections not listed above
|
||||
- blocking: "HALT for: Unapproved deps needed, confirm with user | Ambiguous after story check | 3 failures attempting to implement or fix something repeatedly | Missing config | Failing regression"
|
||||
- ready-for-review: "Code matches requirements + All validations pass + Follows standards + File List complete"
|
||||
- blocking: 'HALT for: Unapproved deps needed, confirm with user | Ambiguous after story check | 3 failures attempting to implement or fix something repeatedly | Missing config | Failing regression'
|
||||
- ready-for-review: 'Code matches requirements + All validations pass + Follows standards + File List complete'
|
||||
- completion: "All Tasks and Subtasks marked [x] and have tests→Validations and full regression passes (DON'T BE LAZY, EXECUTE ALL TESTS and CONFIRM)→Ensure File List is Complete→run the task execute-checklist for the checklist story-dod-checklist→set story status: 'Ready for Review'→HALT"
|
||||
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -403,33 +403,28 @@ Ask the user if they want to work through the checklist:
|
|||
Now that you've completed the checklist, generate a comprehensive validation report that includes:
|
||||
|
||||
1. Executive Summary
|
||||
|
||||
- Overall architecture readiness (High/Medium/Low)
|
||||
- Critical risks identified
|
||||
- Key strengths of the architecture
|
||||
- Project type (Full-stack/Frontend/Backend) and sections evaluated
|
||||
|
||||
2. Section Analysis
|
||||
|
||||
- Pass rate for each major section (percentage of items passed)
|
||||
- Most concerning failures or gaps
|
||||
- Sections requiring immediate attention
|
||||
- Note any sections skipped due to project type
|
||||
|
||||
3. Risk Assessment
|
||||
|
||||
- Top 5 risks by severity
|
||||
- Mitigation recommendations for each
|
||||
- Timeline impact of addressing issues
|
||||
|
||||
4. Recommendations
|
||||
|
||||
- Must-fix items before development
|
||||
- Should-fix items for better quality
|
||||
- Nice-to-have improvements
|
||||
|
||||
5. AI Implementation Readiness
|
||||
|
||||
- Specific concerns for AI agent implementation
|
||||
- Areas needing additional clarification
|
||||
- Complexity hotspots to address
|
||||
|
|
|
|||
|
|
@ -304,7 +304,6 @@ Ask the user if they want to work through the checklist:
|
|||
Create a comprehensive validation report that includes:
|
||||
|
||||
1. Executive Summary
|
||||
|
||||
- Overall PRD completeness (percentage)
|
||||
- MVP scope appropriateness (Too Large/Just Right/Too Small)
|
||||
- Readiness for architecture phase (Ready/Nearly Ready/Not Ready)
|
||||
|
|
@ -312,26 +311,22 @@ Create a comprehensive validation report that includes:
|
|||
|
||||
2. Category Analysis Table
|
||||
Fill in the actual table with:
|
||||
|
||||
- Status: PASS (90%+ complete), PARTIAL (60-89%), FAIL (<60%)
|
||||
- Critical Issues: Specific problems that block progress
|
||||
|
||||
3. Top Issues by Priority
|
||||
|
||||
- BLOCKERS: Must fix before architect can proceed
|
||||
- HIGH: Should fix for quality
|
||||
- MEDIUM: Would improve clarity
|
||||
- LOW: Nice to have
|
||||
|
||||
4. MVP Scope Assessment
|
||||
|
||||
- Features that might be cut for true MVP
|
||||
- Missing features that are essential
|
||||
- Complexity concerns
|
||||
- Timeline realism
|
||||
|
||||
5. Technical Readiness
|
||||
|
||||
- Clarity of technical constraints
|
||||
- Identified technical risks
|
||||
- Areas needing architect investigation
|
||||
|
|
|
|||
|
|
@ -8,12 +8,10 @@ PROJECT TYPE DETECTION:
|
|||
First, determine the project type by checking:
|
||||
|
||||
1. Is this a GREENFIELD project (new from scratch)?
|
||||
|
||||
- Look for: New project initialization, no existing codebase references
|
||||
- Check for: prd.md, architecture.md, new project setup stories
|
||||
|
||||
2. Is this a BROWNFIELD project (enhancing existing system)?
|
||||
|
||||
- Look for: References to existing codebase, enhancement/modification language
|
||||
- Check for: brownfield-prd.md, brownfield-architecture.md, existing system analysis
|
||||
|
||||
|
|
@ -347,7 +345,6 @@ Ask the user if they want to work through the checklist:
|
|||
Generate a comprehensive validation report that adapts to project type:
|
||||
|
||||
1. Executive Summary
|
||||
|
||||
- Project type: [Greenfield/Brownfield] with [UI/No UI]
|
||||
- Overall readiness (percentage)
|
||||
- Go/No-Go recommendation
|
||||
|
|
@ -357,42 +354,36 @@ Generate a comprehensive validation report that adapts to project type:
|
|||
2. Project-Specific Analysis
|
||||
|
||||
FOR GREENFIELD:
|
||||
|
||||
- Setup completeness
|
||||
- Dependency sequencing
|
||||
- MVP scope appropriateness
|
||||
- Development timeline feasibility
|
||||
|
||||
FOR BROWNFIELD:
|
||||
|
||||
- Integration risk level (High/Medium/Low)
|
||||
- Existing system impact assessment
|
||||
- Rollback readiness
|
||||
- User disruption potential
|
||||
|
||||
3. Risk Assessment
|
||||
|
||||
- Top 5 risks by severity
|
||||
- Mitigation recommendations
|
||||
- Timeline impact of addressing issues
|
||||
- [BROWNFIELD] Specific integration risks
|
||||
|
||||
4. MVP Completeness
|
||||
|
||||
- Core features coverage
|
||||
- Missing essential functionality
|
||||
- Scope creep identified
|
||||
- True MVP vs over-engineering
|
||||
|
||||
5. Implementation Readiness
|
||||
|
||||
- Developer clarity score (1-10)
|
||||
- Ambiguous requirements count
|
||||
- Missing technical details
|
||||
- [BROWNFIELD] Integration point clarity
|
||||
|
||||
6. Recommendations
|
||||
|
||||
- Must-fix before development
|
||||
- Should-fix for quality
|
||||
- Consider for improvement
|
||||
|
|
|
|||
|
|
@ -25,14 +25,12 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
1. **Requirements Met:**
|
||||
|
||||
[[LLM: Be specific - list each requirement and whether it's complete]]
|
||||
|
||||
- [ ] All functional requirements specified in the story are implemented.
|
||||
- [ ] All acceptance criteria defined in the story are met.
|
||||
|
||||
2. **Coding Standards & Project Structure:**
|
||||
|
||||
[[LLM: Code quality matters for maintainability. Check each item carefully]]
|
||||
|
||||
- [ ] All new/modified code strictly adheres to `Operational Guidelines`.
|
||||
- [ ] All new/modified code aligns with `Project Structure` (file locations, naming, etc.).
|
||||
- [ ] Adherence to `Tech Stack` for technologies/versions used (if story introduces or modifies tech usage).
|
||||
|
|
@ -44,7 +42,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
3. **Testing:**
|
||||
|
||||
[[LLM: Testing proves your code works. Be honest about test coverage]]
|
||||
|
||||
- [ ] All required unit tests as per the story and `Operational Guidelines` Testing Strategy are implemented.
|
||||
- [ ] All required integration tests (if applicable) as per the story and `Operational Guidelines` Testing Strategy are implemented.
|
||||
- [ ] All tests (unit, integration, E2E if applicable) pass successfully.
|
||||
|
|
@ -53,14 +50,12 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
4. **Functionality & Verification:**
|
||||
|
||||
[[LLM: Did you actually run and test your code? Be specific about what you tested]]
|
||||
|
||||
- [ ] Functionality has been manually verified by the developer (e.g., running the app locally, checking UI, testing API endpoints).
|
||||
- [ ] Edge cases and potential error conditions considered and handled gracefully.
|
||||
|
||||
5. **Story Administration:**
|
||||
|
||||
[[LLM: Documentation helps the next developer. What should they know?]]
|
||||
|
||||
- [ ] All tasks within the story file are marked as complete.
|
||||
- [ ] Any clarifications or decisions made during development are documented in the story file or linked appropriately.
|
||||
- [ ] The story wrap up section has been completed with notes of changes or information relevant to the next story or overall project, the agent model that was primarily used during development, and the changelog of any changes is properly updated.
|
||||
|
|
@ -68,7 +63,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
6. **Dependencies, Build & Configuration:**
|
||||
|
||||
[[LLM: Build issues block everyone. Ensure everything compiles and runs cleanly]]
|
||||
|
||||
- [ ] Project builds successfully without errors.
|
||||
- [ ] Project linting passes
|
||||
- [ ] Any new dependencies added were either pre-approved in the story requirements OR explicitly approved by the user during development (approval documented in story file).
|
||||
|
|
@ -79,7 +73,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
7. **Documentation (If Applicable):**
|
||||
|
||||
[[LLM: Good documentation prevents future confusion. What needs explaining?]]
|
||||
|
||||
- [ ] Relevant inline code documentation (e.g., JSDoc, TSDoc, Python docstrings) for new public APIs or complex logic is complete.
|
||||
- [ ] User-facing documentation updated, if changes impact users.
|
||||
- [ ] Technical documentation (e.g., READMEs, system diagrams) updated if significant architectural changes were made.
|
||||
|
|
|
|||
|
|
@ -117,19 +117,16 @@ Note: We don't need every file listed - just the important ones.]]
|
|||
Generate a concise validation report:
|
||||
|
||||
1. Quick Summary
|
||||
|
||||
- Story readiness: READY / NEEDS REVISION / BLOCKED
|
||||
- Clarity score (1-10)
|
||||
- Major gaps identified
|
||||
|
||||
2. Fill in the validation table with:
|
||||
|
||||
- PASS: Requirements clearly met
|
||||
- PARTIAL: Some gaps but workable
|
||||
- FAIL: Critical information missing
|
||||
|
||||
3. Specific Issues (if any)
|
||||
|
||||
- List concrete problems to fix
|
||||
- Suggest specific improvements
|
||||
- Identify any blocking dependencies
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
@ -651,8 +651,11 @@ Templates with Level 2 headings (`##`) can be automatically sharded:
|
|||
|
||||
```markdown
|
||||
## Goals and Background Context
|
||||
|
||||
## Requirements
|
||||
|
||||
## User Interface Design Goals
|
||||
|
||||
## Success Metrics
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@
|
|||
## Core Reflective Methods
|
||||
|
||||
**Expand or Contract for Audience**
|
||||
|
||||
- Ask whether to 'expand' (add detail, elaborate) or 'contract' (simplify, clarify)
|
||||
- Identify specific target audience if relevant
|
||||
- Tailor content complexity and depth accordingly
|
||||
|
||||
**Explain Reasoning (CoT Step-by-Step)**
|
||||
|
||||
- Walk through the step-by-step thinking process
|
||||
- Reveal underlying assumptions and decision points
|
||||
- Show how conclusions were reached from current role's perspective
|
||||
|
||||
**Critique and Refine**
|
||||
|
||||
- Review output for flaws, inconsistencies, or improvement areas
|
||||
- Identify specific weaknesses from role's expertise
|
||||
- Suggest refined version reflecting domain knowledge
|
||||
|
|
@ -20,12 +23,14 @@
|
|||
## Structural Analysis Methods
|
||||
|
||||
**Analyze Logical Flow and Dependencies**
|
||||
|
||||
- Examine content structure for logical progression
|
||||
- Check internal consistency and coherence
|
||||
- Identify and validate dependencies between elements
|
||||
- Confirm effective ordering and sequencing
|
||||
|
||||
**Assess Alignment with Overall Goals**
|
||||
|
||||
- Evaluate content contribution to stated objectives
|
||||
- Identify any misalignments or gaps
|
||||
- Interpret alignment from specific role's perspective
|
||||
|
|
@ -34,12 +39,14 @@
|
|||
## Risk and Challenge Methods
|
||||
|
||||
**Identify Potential Risks and Unforeseen Issues**
|
||||
|
||||
- Brainstorm potential risks from role's expertise
|
||||
- Identify overlooked edge cases or scenarios
|
||||
- Anticipate unintended consequences
|
||||
- Highlight implementation challenges
|
||||
|
||||
**Challenge from Critical Perspective**
|
||||
|
||||
- Adopt critical stance on current content
|
||||
- Play devil's advocate from specified viewpoint
|
||||
- Argue against proposal highlighting weaknesses
|
||||
|
|
@ -48,12 +55,14 @@
|
|||
## Creative Exploration Methods
|
||||
|
||||
**Tree of Thoughts Deep Dive**
|
||||
|
||||
- Break problem into discrete "thoughts" or intermediate steps
|
||||
- Explore multiple reasoning paths simultaneously
|
||||
- Use self-evaluation to classify each path as "sure", "likely", or "impossible"
|
||||
- Apply search algorithms (BFS/DFS) to find optimal solution paths
|
||||
|
||||
**Hindsight is 20/20: The 'If Only...' Reflection**
|
||||
|
||||
- Imagine retrospective scenario based on current content
|
||||
- Identify the one "if only we had known/done X..." insight
|
||||
- Describe imagined consequences humorously or dramatically
|
||||
|
|
@ -62,6 +71,7 @@
|
|||
## Multi-Persona Collaboration Methods
|
||||
|
||||
**Agile Team Perspective Shift**
|
||||
|
||||
- Rotate through different Scrum team member viewpoints
|
||||
- Product Owner: Focus on user value and business impact
|
||||
- Scrum Master: Examine process flow and team dynamics
|
||||
|
|
@ -69,12 +79,14 @@
|
|||
- QA: Identify testing scenarios and quality concerns
|
||||
|
||||
**Stakeholder Round Table**
|
||||
|
||||
- Convene virtual meeting with multiple personas
|
||||
- Each persona contributes unique perspective on content
|
||||
- Identify conflicts and synergies between viewpoints
|
||||
- Synthesize insights into actionable recommendations
|
||||
|
||||
**Meta-Prompting Analysis**
|
||||
|
||||
- Step back to analyze the structure and logic of current approach
|
||||
- Question the format and methodology being used
|
||||
- Suggest alternative frameworks or mental models
|
||||
|
|
@ -83,24 +95,28 @@
|
|||
## Advanced 2025 Techniques
|
||||
|
||||
**Self-Consistency Validation**
|
||||
|
||||
- Generate multiple reasoning paths for same problem
|
||||
- Compare consistency across different approaches
|
||||
- Identify most reliable and robust solution
|
||||
- Highlight areas where approaches diverge and why
|
||||
|
||||
**ReWOO (Reasoning Without Observation)**
|
||||
|
||||
- Separate parametric reasoning from tool-based actions
|
||||
- Create reasoning plan without external dependencies
|
||||
- Identify what can be solved through pure reasoning
|
||||
- Optimize for efficiency and reduced token usage
|
||||
|
||||
**Persona-Pattern Hybrid**
|
||||
|
||||
- Combine specific role expertise with elicitation pattern
|
||||
- Architect + Risk Analysis: Deep technical risk assessment
|
||||
- UX Expert + User Journey: End-to-end experience critique
|
||||
- PM + Stakeholder Analysis: Multi-perspective impact review
|
||||
|
||||
**Emergent Collaboration Discovery**
|
||||
|
||||
- Allow multiple perspectives to naturally emerge
|
||||
- Identify unexpected insights from persona interactions
|
||||
- Explore novel combinations of viewpoints
|
||||
|
|
@ -109,18 +125,21 @@
|
|||
## Game-Based Elicitation Methods
|
||||
|
||||
**Red Team vs Blue Team**
|
||||
|
||||
- Red Team: Attack the proposal, find vulnerabilities
|
||||
- Blue Team: Defend and strengthen the approach
|
||||
- Competitive analysis reveals blind spots
|
||||
- Results in more robust, battle-tested solutions
|
||||
|
||||
**Innovation Tournament**
|
||||
|
||||
- Pit multiple alternative approaches against each other
|
||||
- Score each approach across different criteria
|
||||
- Crowd-source evaluation from different personas
|
||||
- Identify winning combination of features
|
||||
|
||||
**Escape Room Challenge**
|
||||
|
||||
- Present content as constraints to work within
|
||||
- Find creative solutions within tight limitations
|
||||
- Identify minimum viable approach
|
||||
|
|
@ -129,6 +148,7 @@
|
|||
## Process Control
|
||||
|
||||
**Proceed / No Further Actions**
|
||||
|
||||
- Acknowledge choice to finalize current work
|
||||
- Accept output as-is or move to next step
|
||||
- Prepare to continue without additional elicitation
|
||||
|
|
|
|||
|
|
@ -139,16 +139,19 @@ Critical: This is where you'll need to be interactive with the user if informati
|
|||
|
||||
Create Dev Technical Guidance section with available information:
|
||||
|
||||
```markdown
|
||||
````markdown
|
||||
## Dev Technical Guidance
|
||||
|
||||
### Existing System Context
|
||||
|
||||
[Extract from available documentation]
|
||||
|
||||
### Integration Approach
|
||||
|
||||
[Based on patterns found or ask user]
|
||||
|
||||
### Technical Constraints
|
||||
|
||||
[From documentation or user input]
|
||||
|
||||
### Missing Information
|
||||
|
|
@ -191,6 +194,7 @@ Example task structure for brownfield:
|
|||
- [ ] Integration test for {{integration point}}
|
||||
- [ ] Update existing tests if needed
|
||||
```
|
||||
````
|
||||
|
||||
### 5. Risk Assessment and Mitigation
|
||||
|
||||
|
|
@ -202,14 +206,17 @@ Add section for brownfield-specific risks:
|
|||
## Risk Assessment
|
||||
|
||||
### Implementation Risks
|
||||
|
||||
- **Primary Risk**: {{main risk to existing system}}
|
||||
- **Mitigation**: {{how to address}}
|
||||
- **Verification**: {{how to confirm safety}}
|
||||
|
||||
### Rollback Plan
|
||||
|
||||
- {{Simple steps to undo changes if needed}}
|
||||
|
||||
### Safety Checks
|
||||
|
||||
- [ ] Existing {{feature}} tested before changes
|
||||
- [ ] Changes can be feature-flagged or isolated
|
||||
- [ ] Rollback procedure documented
|
||||
|
|
@ -252,6 +259,7 @@ Include header noting documentation context:
|
|||
<!-- Context: Brownfield enhancement to {{existing system}} -->
|
||||
|
||||
## Status: Draft
|
||||
|
||||
[Rest of story content...]
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -21,63 +21,54 @@ CRITICAL: First, help the user select the most appropriate research focus based
|
|||
Present these numbered options to the user:
|
||||
|
||||
1. **Product Validation Research**
|
||||
|
||||
- Validate product hypotheses and market fit
|
||||
- Test assumptions about user needs and solutions
|
||||
- Assess technical and business feasibility
|
||||
- Identify risks and mitigation strategies
|
||||
|
||||
2. **Market Opportunity Research**
|
||||
|
||||
- Analyze market size and growth potential
|
||||
- Identify market segments and dynamics
|
||||
- Assess market entry strategies
|
||||
- Evaluate timing and market readiness
|
||||
|
||||
3. **User & Customer Research**
|
||||
|
||||
- Deep dive into user personas and behaviors
|
||||
- Understand jobs-to-be-done and pain points
|
||||
- Map customer journeys and touchpoints
|
||||
- Analyze willingness to pay and value perception
|
||||
|
||||
4. **Competitive Intelligence Research**
|
||||
|
||||
- Detailed competitor analysis and positioning
|
||||
- Feature and capability comparisons
|
||||
- Business model and strategy analysis
|
||||
- Identify competitive advantages and gaps
|
||||
|
||||
5. **Technology & Innovation Research**
|
||||
|
||||
- Assess technology trends and possibilities
|
||||
- Evaluate technical approaches and architectures
|
||||
- Identify emerging technologies and disruptions
|
||||
- Analyze build vs. buy vs. partner options
|
||||
|
||||
6. **Industry & Ecosystem Research**
|
||||
|
||||
- Map industry value chains and dynamics
|
||||
- Identify key players and relationships
|
||||
- Analyze regulatory and compliance factors
|
||||
- Understand partnership opportunities
|
||||
|
||||
7. **Strategic Options Research**
|
||||
|
||||
- Evaluate different strategic directions
|
||||
- Assess business model alternatives
|
||||
- Analyze go-to-market strategies
|
||||
- Consider expansion and scaling paths
|
||||
|
||||
8. **Risk & Feasibility Research**
|
||||
|
||||
- Identify and assess various risk factors
|
||||
- Evaluate implementation challenges
|
||||
- Analyze resource requirements
|
||||
- Consider regulatory and legal implications
|
||||
|
||||
9. **Custom Research Focus**
|
||||
|
||||
- User-defined research objectives
|
||||
- Specialized domain investigation
|
||||
- Cross-functional research needs
|
||||
|
|
@ -246,13 +237,11 @@ CRITICAL: collaborate with the user to develop specific, actionable research que
|
|||
### 5. Review and Refinement
|
||||
|
||||
1. **Present Complete Prompt**
|
||||
|
||||
- Show the full research prompt
|
||||
- Explain key elements and rationale
|
||||
- Highlight any assumptions made
|
||||
|
||||
2. **Gather Feedback**
|
||||
|
||||
- Are the objectives clear and correct?
|
||||
- Do the questions address all concerns?
|
||||
- Is the scope appropriate?
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ This document captures the CURRENT STATE of the [Project Name] codebase, includi
|
|||
### Change Log
|
||||
|
||||
| Date | Version | Description | Author |
|
||||
|------|---------|-------------|--------|
|
||||
| ------ | ------- | --------------------------- | --------- |
|
||||
| [Date] | 1.0 | Initial brownfield analysis | [Analyst] |
|
||||
|
||||
## Quick Reference - Key Files and Entry Points
|
||||
|
|
@ -137,7 +137,7 @@ This document captures the CURRENT STATE of the [Project Name] codebase, includi
|
|||
### Actual Tech Stack (from package.json/requirements.txt)
|
||||
|
||||
| Category | Technology | Version | Notes |
|
||||
|----------|------------|---------|--------|
|
||||
| --------- | ---------- | ------- | -------------------------- |
|
||||
| Runtime | Node.js | 16.x | [Any constraints] |
|
||||
| Framework | Express | 4.18.2 | [Custom middleware?] |
|
||||
| Database | PostgreSQL | 13 | [Connection pooling setup] |
|
||||
|
|
@ -179,6 +179,7 @@ project-root/
|
|||
### Data Models
|
||||
|
||||
Instead of duplicating, reference actual model files:
|
||||
|
||||
- **User Model**: See `src/models/User.js`
|
||||
- **Order Model**: See `src/models/Order.js`
|
||||
- **Related Types**: TypeScript definitions in `src/types/`
|
||||
|
|
@ -209,7 +210,7 @@ Instead of duplicating, reference actual model files:
|
|||
### External Services
|
||||
|
||||
| Service | Purpose | Integration Type | Key Files |
|
||||
|---------|---------|------------------|-----------|
|
||||
| -------- | -------- | ---------------- | ------------------------------ |
|
||||
| Stripe | Payments | REST API | `src/integrations/stripe/` |
|
||||
| SendGrid | Emails | SDK | `src/services/emailService.js` |
|
||||
|
||||
|
|
@ -256,6 +257,7 @@ npm run test:integration # Runs integration tests (requires local DB)
|
|||
### Files That Will Need Modification
|
||||
|
||||
Based on the enhancement requirements, these files will be affected:
|
||||
|
||||
- `src/services/userService.js` - Add new user fields
|
||||
- `src/models/User.js` - Update schema
|
||||
- `src/routes/userRoutes.js` - New endpoints
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
docOutputLocation: docs/brainstorming-session-results.md
|
||||
template: "{root}/templates/brainstorming-output-tmpl.yaml"
|
||||
template: '{root}/templates/brainstorming-output-tmpl.yaml'
|
||||
---
|
||||
|
||||
# Facilitate Brainstorming Session Task
|
||||
|
|
|
|||
|
|
@ -11,14 +11,12 @@ You are now operating as a Documentation Indexer. Your goal is to ensure all doc
|
|||
### Required Steps
|
||||
|
||||
1. First, locate and scan:
|
||||
|
||||
- The `docs/` directory and all subdirectories
|
||||
- The existing `docs/index.md` file (create if absent)
|
||||
- All markdown (`.md`) and text (`.txt`) files in the documentation structure
|
||||
- Note the folder structure for hierarchical organization
|
||||
|
||||
2. For the existing `docs/index.md`:
|
||||
|
||||
- Parse current entries
|
||||
- Note existing file references and descriptions
|
||||
- Identify any broken links or missing files
|
||||
|
|
@ -26,7 +24,6 @@ You are now operating as a Documentation Indexer. Your goal is to ensure all doc
|
|||
- Preserve existing folder sections
|
||||
|
||||
3. For each documentation file found:
|
||||
|
||||
- Extract the title (from first heading or filename)
|
||||
- Generate a brief description by analyzing the content
|
||||
- Create a relative markdown link to the file
|
||||
|
|
@ -35,7 +32,6 @@ You are now operating as a Documentation Indexer. Your goal is to ensure all doc
|
|||
- If missing or outdated, prepare an update
|
||||
|
||||
4. For any missing or non-existent files found in index:
|
||||
|
||||
- Present a list of all entries that reference non-existent files
|
||||
- For each entry:
|
||||
- Show the full entry details (title, path, description)
|
||||
|
|
@ -88,7 +84,6 @@ Documents within the `another-folder/` directory:
|
|||
### [Nested Document](./another-folder/document.md)
|
||||
|
||||
Description of nested document.
|
||||
|
||||
```
|
||||
|
||||
### Index Entry Format
|
||||
|
|
@ -157,7 +152,6 @@ For each file referenced in the index but not found in the filesystem:
|
|||
### Special Cases
|
||||
|
||||
1. **Sharded Documents**: If a folder contains an `index.md` file, treat it as a sharded document:
|
||||
|
||||
- Use the folder's `index.md` title as the section title
|
||||
- List the folder's documents as subsections
|
||||
- Note in the description that this is a multi-part document
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Provide a user-friendly interface to the BMad knowledge base without overwhelmin
|
|||
|
||||
## Instructions
|
||||
|
||||
When entering KB mode (*kb-mode), follow these steps:
|
||||
When entering KB mode (\*kb-mode), follow these steps:
|
||||
|
||||
### 1. Welcome and Guide
|
||||
|
||||
|
|
@ -48,12 +48,12 @@ Or ask me about anything else related to BMad-Method!
|
|||
When user is done or wants to exit KB mode:
|
||||
|
||||
- Summarize key points discussed if helpful
|
||||
- Remind them they can return to KB mode anytime with *kb-mode
|
||||
- Remind them they can return to KB mode anytime with \*kb-mode
|
||||
- Suggest next steps based on what was discussed
|
||||
|
||||
## Example Interaction
|
||||
|
||||
**User**: *kb-mode
|
||||
**User**: \*kb-mode
|
||||
|
||||
**Assistant**: I've entered KB mode and have access to the full BMad knowledge base. I can help you with detailed information about any aspect of BMad-Method.
|
||||
|
||||
|
|
|
|||
|
|
@ -81,25 +81,31 @@ After review and any refactoring, append your results to the story file in the Q
|
|||
## QA Results
|
||||
|
||||
### Review Date: [Date]
|
||||
|
||||
### Reviewed By: Quinn (Senior Developer QA)
|
||||
|
||||
### Code Quality Assessment
|
||||
|
||||
[Overall assessment of implementation quality]
|
||||
|
||||
### Refactoring Performed
|
||||
|
||||
[List any refactoring you performed with explanations]
|
||||
|
||||
- **File**: [filename]
|
||||
- **Change**: [what was changed]
|
||||
- **Why**: [reason for change]
|
||||
- **How**: [how it improves the code]
|
||||
|
||||
### Compliance Check
|
||||
|
||||
- Coding Standards: [✓/✗] [notes if any]
|
||||
- Project Structure: [✓/✗] [notes if any]
|
||||
- Testing Strategy: [✓/✗] [notes if any]
|
||||
- All ACs Met: [✓/✗] [notes if any]
|
||||
|
||||
### Improvements Checklist
|
||||
|
||||
[Check off items you handled yourself, leave unchecked for dev to address]
|
||||
|
||||
- [x] Refactored user service for better error handling (services/user.service.ts)
|
||||
|
|
@ -109,12 +115,15 @@ After review and any refactoring, append your results to the story file in the Q
|
|||
- [ ] Update API documentation for new error codes
|
||||
|
||||
### Security Review
|
||||
|
||||
[Any security concerns found and whether addressed]
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
[Any performance issues found and whether addressed]
|
||||
|
||||
### Final Status
|
||||
|
||||
[✓ Approved - Ready for Done] / [✗ Changes Required - See unchecked items above]
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -91,13 +91,11 @@ CRITICAL: Use proper parsing that understands markdown context. A ## inside a co
|
|||
For each extracted section:
|
||||
|
||||
1. **Generate filename**: Convert the section heading to lowercase-dash-case
|
||||
|
||||
- Remove special characters
|
||||
- Replace spaces with dashes
|
||||
- Example: "## Tech Stack" → `tech-stack.md`
|
||||
|
||||
2. **Adjust heading levels**:
|
||||
|
||||
- The level 2 heading becomes level 1 (# instead of ##) in the sharded new document
|
||||
- All subsection levels decrease by 1:
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,14 @@ sections:
|
|||
title: Feature Comparison Matrix
|
||||
instruction: Create a detailed comparison table of key features across competitors
|
||||
type: table
|
||||
columns: ["Feature Category", "{{your_company}}", "{{competitor_1}}", "{{competitor_2}}", "{{competitor_3}}"]
|
||||
columns:
|
||||
[
|
||||
"Feature Category",
|
||||
"{{your_company}}",
|
||||
"{{competitor_1}}",
|
||||
"{{competitor_2}}",
|
||||
"{{competitor_3}}",
|
||||
]
|
||||
rows:
|
||||
- category: "Core Functionality"
|
||||
items:
|
||||
|
|
@ -153,7 +160,13 @@ sections:
|
|||
- ["Onboarding Time", "{{time}}", "{{time}}", "{{time}}", "{{time}}"]
|
||||
- category: "Integration & Ecosystem"
|
||||
items:
|
||||
- ["API Availability", "{{availability}}", "{{availability}}", "{{availability}}", "{{availability}}"]
|
||||
- [
|
||||
"API Availability",
|
||||
"{{availability}}",
|
||||
"{{availability}}",
|
||||
"{{availability}}",
|
||||
"{{availability}}",
|
||||
]
|
||||
- ["Third-party Integrations", "{{number}}", "{{number}}", "{{number}}", "{{number}}"]
|
||||
- category: "Pricing & Plans"
|
||||
items:
|
||||
|
|
|
|||
|
|
@ -75,12 +75,24 @@ sections:
|
|||
rows:
|
||||
- ["Framework", "{{framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["UI Library", "{{ui_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["State Management", "{{state_management}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- [
|
||||
"State Management",
|
||||
"{{state_management}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
]
|
||||
- ["Routing", "{{routing_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Build Tool", "{{build_tool}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Styling", "{{styling_solution}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Testing", "{{test_framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Component Library", "{{component_lib}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- [
|
||||
"Component Library",
|
||||
"{{component_lib}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
]
|
||||
- ["Form Handling", "{{form_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Animation", "{{animation_lib}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Dev Tools", "{{dev_tools}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
|
|
|
|||
|
|
@ -156,11 +156,29 @@ sections:
|
|||
columns: [Category, Technology, Version, Purpose, Rationale]
|
||||
rows:
|
||||
- ["Frontend Language", "{{fe_language}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Frontend Framework", "{{fe_framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["UI Component Library", "{{ui_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- [
|
||||
"Frontend Framework",
|
||||
"{{fe_framework}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
]
|
||||
- [
|
||||
"UI Component Library",
|
||||
"{{ui_library}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
]
|
||||
- ["State Management", "{{state_mgmt}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Backend Language", "{{be_language}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Backend Framework", "{{be_framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- [
|
||||
"Backend Framework",
|
||||
"{{be_framework}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
]
|
||||
- ["API Style", "{{api_style}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Database", "{{database}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Cache", "{{cache}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ If the user asks or does not specify a specific checklist, list the checklists a
|
|||
## Instructions
|
||||
|
||||
1. **Initial Assessment**
|
||||
|
||||
- If user or the task being run provides a checklist name:
|
||||
- Try fuzzy matching (e.g. "architecture checklist" -> "architect-checklist")
|
||||
- If multiple matches found, ask user to clarify
|
||||
|
|
@ -22,14 +21,12 @@ If the user asks or does not specify a specific checklist, list the checklists a
|
|||
- All at once (YOLO mode - recommended for checklists, there will be a summary of sections at the end to discuss)
|
||||
|
||||
2. **Document and Artifact Gathering**
|
||||
|
||||
- Each checklist will specify its required documents/artifacts at the beginning
|
||||
- Follow the checklist's specific instructions for what to gather, generally a file can be resolved in the docs folder, if not or unsure, halt and ask or confirm with the user.
|
||||
|
||||
3. **Checklist Processing**
|
||||
|
||||
If in interactive mode:
|
||||
|
||||
- Work through each section of the checklist one at a time
|
||||
- For each section:
|
||||
- Review all items in the section following instructions for that section embedded in the checklist
|
||||
|
|
@ -38,7 +35,6 @@ If the user asks or does not specify a specific checklist, list the checklists a
|
|||
- Get user confirmation before proceeding to next section or if any thing major do we need to halt and take corrective action
|
||||
|
||||
If in YOLO mode:
|
||||
|
||||
- Process all sections at once
|
||||
- Create a comprehensive report of all findings
|
||||
- Present the complete analysis to the user
|
||||
|
|
@ -46,7 +42,6 @@ If the user asks or does not specify a specific checklist, list the checklists a
|
|||
4. **Validation Approach**
|
||||
|
||||
For each checklist item:
|
||||
|
||||
- Read and understand the requirement
|
||||
- Look for evidence in the documentation that satisfies the requirement
|
||||
- Consider both explicit mentions and implicit coverage
|
||||
|
|
@ -60,7 +55,6 @@ If the user asks or does not specify a specific checklist, list the checklists a
|
|||
5. **Section Analysis**
|
||||
|
||||
For each section:
|
||||
|
||||
- think step by step to calculate pass rate
|
||||
- Identify common themes in failed items
|
||||
- Provide specific recommendations for improvement
|
||||
|
|
@ -70,7 +64,6 @@ If the user asks or does not specify a specific checklist, list the checklists a
|
|||
6. **Final Report**
|
||||
|
||||
Prepare a summary that includes:
|
||||
|
||||
- Overall checklist completion status
|
||||
- Pass rates by section
|
||||
- List of failed items with context
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ template:
|
|||
output:
|
||||
format: markdown
|
||||
filename: default-path/to/{{filename}}.md
|
||||
title: "{{variable}} Document Title"
|
||||
title: '{{variable}} Document Title'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
|
|
@ -108,8 +108,8 @@ sections:
|
|||
Use `{{variable_name}}` in titles, templates, and content:
|
||||
|
||||
```yaml
|
||||
title: "Epic {{epic_number}} {{epic_title}}"
|
||||
template: "As a {{user_type}}, I want {{action}}, so that {{benefit}}."
|
||||
title: 'Epic {{epic_number}} {{epic_title}}'
|
||||
template: 'As a {{user_type}}, I want {{action}}, so that {{benefit}}.'
|
||||
```
|
||||
|
||||
### Conditional Sections
|
||||
|
|
@ -212,7 +212,7 @@ choices:
|
|||
- id: criteria
|
||||
title: Acceptance Criteria
|
||||
type: numbered-list
|
||||
item_template: "{{criterion_number}}: {{criteria}}"
|
||||
item_template: '{{criterion_number}}: {{criteria}}'
|
||||
repeatable: true
|
||||
```
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ choices:
|
|||
|
||||
````yaml
|
||||
examples:
|
||||
- "FR6: The system must authenticate users within 2 seconds"
|
||||
- 'FR6: The system must authenticate users within 2 seconds'
|
||||
- |
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
|
|
|||
|
|
@ -2337,7 +2337,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -8068,7 +8068,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -775,7 +775,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -3733,7 +3733,7 @@ Use the `shard-doc` task or `@kayvan/markdown-tree-parser` tool for automatic ga
|
|||
|
||||
- **Claude Code**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Cursor**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Trae**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Roo Code**: Select mode from mode selector with bmad2du prefix
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select the appropriate game agent.
|
||||
|
|
|
|||
|
|
@ -3430,7 +3430,7 @@ Use the `shard-doc` task or `@kayvan/markdown-tree-parser` tool for automatic ga
|
|||
|
||||
- **Claude Code**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Cursor**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Trae**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Roo Code**: Select mode from mode selector with bmad2du prefix
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select the appropriate game agent.
|
||||
|
|
|
|||
|
|
@ -2874,7 +2874,7 @@ Use the `shard-doc` task or `@kayvan/markdown-tree-parser` tool for automatic ga
|
|||
|
||||
- **Claude Code**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Cursor**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Trae**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Roo Code**: Select mode from mode selector with bmad2du prefix
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select the appropriate game agent.
|
||||
|
|
@ -14560,7 +14560,7 @@ Use the `shard-doc` task or `@kayvan/markdown-tree-parser` tool for automatic ga
|
|||
|
||||
- **Claude Code**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Cursor**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Trae**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Roo Code**: Select mode from mode selector with bmad2du prefix
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select the appropriate game agent.
|
||||
|
|
|
|||
|
|
@ -1242,7 +1242,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -1098,7 +1098,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -995,7 +995,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -1044,7 +1044,7 @@ You are the "Vibe CEO" - thinking like a CEO with unlimited resources and a sing
|
|||
|
||||
- **Claude Code**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Cursor**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Windsurf**: `/agent-name` (e.g., `/bmad-master`)
|
||||
- **Trae**: `@agent-name` (e.g., `@bmad-master`)
|
||||
- **Roo Code**: Select mode from mode selector (e.g., `bmad-master`)
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select **Agent** from the chat mode selector.
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ If you have just completed an MVP with BMad, and you want to continue with post-
|
|||
## The Complete Brownfield Workflow
|
||||
|
||||
1. **Follow the [<ins>User Guide - Installation</ins>](user-guide.md#installation) steps to setup your agent in the web.**
|
||||
2. **Generate a 'flattened' single file of your entire codebase** run: ```npx bmad-method flatten```
|
||||
2. **Generate a 'flattened' single file of your entire codebase** run: `npx bmad-method flatten`
|
||||
|
||||
### Choose Your Approach
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
import js from '@eslint/js';
|
||||
import nodePlugin from 'eslint-plugin-n';
|
||||
import yml from 'eslint-plugin-yml';
|
||||
import unicorn from 'eslint-plugin-unicorn';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier/flat';
|
||||
|
||||
export default [
|
||||
// Global ignores for files/folders that should not be linted
|
||||
{
|
||||
ignores: ['dist/**', 'coverage/**', '**/*.min.js'],
|
||||
},
|
||||
|
||||
// Base JavaScript recommended rules
|
||||
js.configs.recommended,
|
||||
|
||||
// Node.js rules
|
||||
...nodePlugin.configs['flat/mixed-esm-and-cjs'],
|
||||
|
||||
// Unicorn rules (modern best practices)
|
||||
unicorn.configs.recommended,
|
||||
|
||||
// YAML linting
|
||||
...yml.configs['flat/recommended'],
|
||||
|
||||
// Place Prettier last to disable conflicting stylistic rules
|
||||
eslintConfigPrettier,
|
||||
|
||||
// Project-specific tweaks
|
||||
{
|
||||
rules: {
|
||||
// Allow console for CLI tools in this repo
|
||||
'no-console': 'off',
|
||||
// Do not enforce a specific YAML file extension (.yml vs .yaml)
|
||||
'yml/file-extension': 'off',
|
||||
// Relax some Unicorn rules that are too opinionated for this codebase
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'unicorn/no-null': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// CLI/CommonJS scripts under tools/**
|
||||
{
|
||||
files: ['tools/**/*.js'],
|
||||
rules: {
|
||||
// Allow CommonJS patterns for Node CLI scripts
|
||||
'unicorn/prefer-module': 'off',
|
||||
'unicorn/import-style': 'off',
|
||||
'unicorn/no-process-exit': 'off',
|
||||
'n/no-process-exit': 'off',
|
||||
'unicorn/no-await-expression-member': 'off',
|
||||
'unicorn/prefer-top-level-await': 'off',
|
||||
// Avoid failing CI on incidental unused vars in internal scripts
|
||||
'no-unused-vars': 'off',
|
||||
// Reduce style-only churn in internal tools
|
||||
'unicorn/prefer-ternary': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'unicorn/no-array-reduce': 'off',
|
||||
'unicorn/no-array-callback-reference': 'off',
|
||||
'unicorn/consistent-function-scoping': 'off',
|
||||
'n/no-extraneous-require': 'off',
|
||||
'n/no-extraneous-import': 'off',
|
||||
'n/no-unpublished-require': 'off',
|
||||
'n/no-unpublished-import': 'off',
|
||||
// Some scripts intentionally use globals provided at runtime
|
||||
'no-undef': 'off',
|
||||
// Additional relaxed rules for legacy/internal scripts
|
||||
'no-useless-catch': 'off',
|
||||
'unicorn/prefer-number-properties': 'off',
|
||||
'no-unreachable': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// ESLint config file should not be checked for publish-related Node rules
|
||||
{
|
||||
files: ['eslint.config.mjs'],
|
||||
rules: {
|
||||
'n/no-unpublished-import': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// YAML workflow templates allow empty mapping values intentionally
|
||||
{
|
||||
files: ['bmad-core/workflows/**/*.{yml,yaml}'],
|
||||
rules: {
|
||||
'yml/no-empty-mapping-value': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// GitHub workflow files in this repo may use empty mapping values
|
||||
{
|
||||
files: ['.github/workflows/**/*.{yml,yaml}'],
|
||||
rules: {
|
||||
'yml/no-empty-mapping-value': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// Other GitHub YAML files may intentionally use empty values and reserved filenames
|
||||
{
|
||||
files: ['.github/**/*.{yml,yaml}'],
|
||||
rules: {
|
||||
'yml/no-empty-mapping-value': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
steps:
|
||||
# Build the container image
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: ['build', '-t', 'gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA', '.']
|
||||
- name: "gcr.io/cloud-builders/docker"
|
||||
args: ["build", "-t", "gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA", "."]
|
||||
|
||||
# Push the container image to Container Registry
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: ['push', 'gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA']
|
||||
- name: "gcr.io/cloud-builders/docker"
|
||||
args: ["push", "gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA"]
|
||||
|
||||
# Deploy container image to Cloud Run
|
||||
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
|
||||
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
|
||||
entrypoint: gcloud
|
||||
args:
|
||||
- 'run'
|
||||
- 'deploy'
|
||||
- '{{COMPANY_NAME}}-ai-agents'
|
||||
- '--image'
|
||||
- 'gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA'
|
||||
- '--region'
|
||||
- '{{LOCATION}}'
|
||||
- '--platform'
|
||||
- 'managed'
|
||||
- '--allow-unauthenticated'
|
||||
- "run"
|
||||
- "deploy"
|
||||
- "{{COMPANY_NAME}}-ai-agents"
|
||||
- "--image"
|
||||
- "gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA"
|
||||
- "--region"
|
||||
- "{{LOCATION}}"
|
||||
- "--platform"
|
||||
- "managed"
|
||||
- "--allow-unauthenticated"
|
||||
|
||||
images:
|
||||
- 'gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA'
|
||||
- "gcr.io/{{PROJECT_ID}}/{{COMPANY_NAME}}-ai-agents:$COMMIT_SHA"
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@ This expansion pack provides a complete, deployable starter kit for building and
|
|||
|
||||
## Features
|
||||
|
||||
* **Automated GCP Setup**: `gcloud` scripts to configure your project, service accounts, and required APIs in minutes.
|
||||
* **Production-Ready Deployment**: Includes a `Dockerfile` and `cloudbuild.yaml` for easy, repeatable deployments to Google Cloud Run.
|
||||
* **Rich Template Library**: A comprehensive set of BMad-compatible templates for Teams, Agents, Tasks, Workflows, Documents, and Checklists.
|
||||
* **Pre-configured Agent Roles**: Includes powerful master templates for key agent archetypes like Orchestrators and Specialists.
|
||||
* **Highly Customizable**: Easily adapt the entire system with company-specific variables and industry-specific configurations.
|
||||
* **Powered by Google ADK**: Built on the official Google Agent Development Kit for robust and native integration with Vertex AI services.
|
||||
- **Automated GCP Setup**: `gcloud` scripts to configure your project, service accounts, and required APIs in minutes.
|
||||
- **Production-Ready Deployment**: Includes a `Dockerfile` and `cloudbuild.yaml` for easy, repeatable deployments to Google Cloud Run.
|
||||
- **Rich Template Library**: A comprehensive set of BMad-compatible templates for Teams, Agents, Tasks, Workflows, Documents, and Checklists.
|
||||
- **Pre-configured Agent Roles**: Includes powerful master templates for key agent archetypes like Orchestrators and Specialists.
|
||||
- **Highly Customizable**: Easily adapt the entire system with company-specific variables and industry-specific configurations.
|
||||
- **Powered by Google ADK**: Built on the official Google Agent Development Kit for robust and native integration with Vertex AI services.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following installed and configured:
|
||||
|
||||
* A Google Cloud Platform (GCP) Account with an active billing account.
|
||||
* The [Google Cloud SDK (`gcloud` CLI)](https://www.google.com/search?q=%5Bhttps://cloud.google.com/sdk/docs/install%5D\(https://cloud.google.com/sdk/docs/install\)) installed and authenticated.
|
||||
* [Docker](https://www.docker.com/products/docker-desktop/) installed on your local machine.
|
||||
* Python 3.11+
|
||||
- A Google Cloud Platform (GCP) Account with an active billing account.
|
||||
- The [Google Cloud SDK (`gcloud` CLI)](<https://www.google.com/search?q=%5Bhttps://cloud.google.com/sdk/docs/install%5D(https://cloud.google.com/sdk/docs/install)>) installed and authenticated.
|
||||
- [Docker](https://www.docker.com/products/docker-desktop/) installed on your local machine.
|
||||
- Python 3.11+
|
||||
|
||||
## Quick Start Guide
|
||||
|
||||
|
|
@ -32,9 +32,9 @@ Follow these steps to get your own AI agent system running on Google Cloud.
|
|||
|
||||
The setup scripts use placeholder variables. Before running them, open the files in the `/scripts` directory and replace the following placeholders with your own values:
|
||||
|
||||
* `{{PROJECT_ID}}`: Your unique Google Cloud project ID.
|
||||
* `{{COMPANY_NAME}}`: Your company or project name (used for naming resources).
|
||||
* `{{LOCATION}}`: The GCP region you want to deploy to (e.g., `us-central1`).
|
||||
- `{{PROJECT_ID}}`: Your unique Google Cloud project ID.
|
||||
- `{{COMPANY_NAME}}`: Your company or project name (used for naming resources).
|
||||
- `{{LOCATION}}`: The GCP region you want to deploy to (e.g., `us-central1`).
|
||||
|
||||
### 2\. Run the GCP Setup Scripts
|
||||
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ commands:
|
|||
task-execution:
|
||||
flow: Read story → Implement game feature → Write tests → Pass tests → Update [x] → Next task
|
||||
updates-ONLY:
|
||||
- "Checkboxes: [ ] not started | [-] in progress | [x] complete"
|
||||
- "Debug Log: | Task | File | Change | Reverted? |"
|
||||
- "Completion Notes: Deviations only, <50 words"
|
||||
- "Change Log: Requirement changes only"
|
||||
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
||||
- 'Debug Log: | Task | File | Change | Reverted? |'
|
||||
- 'Completion Notes: Deviations only, <50 words'
|
||||
- 'Change Log: Requirement changes only'
|
||||
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing game config
|
||||
done: Game feature works + Tests pass + 60 FPS + No lint errors + Follows Phaser 3 best practices
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ activation-instructions:
|
|||
- When listing tasks/templates or presenting options during conversations, always show as numbered options list, allowing the user to type a number to select or execute
|
||||
- STAY IN CHARACTER!
|
||||
- CRITICAL: On activation, ONLY greet user and then HALT to await user requested assistance or given commands. ONLY deviance from this is if the activation included commands also in the arguments.
|
||||
- "CRITICAL RULE: You are ONLY allowed to create/modify story files - NEVER implement! If asked to implement, tell user they MUST switch to Game Developer Agent"
|
||||
- 'CRITICAL RULE: You are ONLY allowed to create/modify story files - NEVER implement! If asked to implement, tell user they MUST switch to Game Developer Agent'
|
||||
agent:
|
||||
name: Jordan
|
||||
id: game-sm
|
||||
|
|
|
|||
|
|
@ -39,13 +39,11 @@ You are developing games as a "Player Experience CEO" - thinking like a game dir
|
|||
### Phase 1: Game Concept and Design
|
||||
|
||||
1. **Game Designer**: Start with brainstorming and concept development
|
||||
|
||||
- Use \*brainstorm to explore game concepts and mechanics
|
||||
- Create Game Brief using game-brief-tmpl
|
||||
- Develop core game pillars and player experience goals
|
||||
|
||||
2. **Game Designer**: Create comprehensive Game Design Document
|
||||
|
||||
- Use game-design-doc-tmpl to create detailed GDD
|
||||
- Define all game mechanics, progression, and balance
|
||||
- Specify technical requirements and platform targets
|
||||
|
|
@ -65,13 +63,11 @@ You are developing games as a "Player Experience CEO" - thinking like a game dir
|
|||
### Phase 3: Story-Driven Development
|
||||
|
||||
5. **Game Scrum Master**: Break down design into development stories
|
||||
|
||||
- Use create-game-story task to create detailed implementation stories
|
||||
- Each story should be immediately actionable by game developers
|
||||
- Apply game-story-dod-checklist to ensure story quality
|
||||
|
||||
6. **Game Developer**: Implement game features story by story
|
||||
|
||||
- Follow TypeScript strict mode and Phaser 3 best practices
|
||||
- Maintain 60 FPS performance target throughout development
|
||||
- Use test-driven development for game logic components
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ interface GameState {
|
|||
interface GameSettings {
|
||||
musicVolume: number;
|
||||
sfxVolume: number;
|
||||
difficulty: "easy" | "normal" | "hard";
|
||||
difficulty: 'easy' | 'normal' | 'hard';
|
||||
controls: ControlScheme;
|
||||
}
|
||||
```
|
||||
|
|
@ -114,12 +114,12 @@ class GameScene extends Phaser.Scene {
|
|||
private inputManager!: InputManager;
|
||||
|
||||
constructor() {
|
||||
super({ key: "GameScene" });
|
||||
super({ key: 'GameScene' });
|
||||
}
|
||||
|
||||
preload(): void {
|
||||
// Load only scene-specific assets
|
||||
this.load.image("player", "assets/player.png");
|
||||
this.load.image('player', 'assets/player.png');
|
||||
}
|
||||
|
||||
create(data: SceneData): void {
|
||||
|
|
@ -144,7 +144,7 @@ class GameScene extends Phaser.Scene {
|
|||
this.inputManager.destroy();
|
||||
|
||||
// Remove event listeners
|
||||
this.events.off("*");
|
||||
this.events.off('*');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -153,13 +153,13 @@ class GameScene extends Phaser.Scene {
|
|||
|
||||
```typescript
|
||||
// Proper scene transitions with data
|
||||
this.scene.start("NextScene", {
|
||||
this.scene.start('NextScene', {
|
||||
playerScore: this.playerScore,
|
||||
currentLevel: this.currentLevel + 1,
|
||||
});
|
||||
|
||||
// Scene overlays for UI
|
||||
this.scene.launch("PauseMenuScene");
|
||||
this.scene.launch('PauseMenuScene');
|
||||
this.scene.pause();
|
||||
```
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ class Player extends GameEntity {
|
|||
private health!: HealthComponent;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y, "player");
|
||||
super(scene, x, y, 'player');
|
||||
|
||||
this.movement = this.addComponent(new MovementComponent(this));
|
||||
this.health = this.addComponent(new HealthComponent(this, 100));
|
||||
|
|
@ -223,7 +223,7 @@ class GameManager {
|
|||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
if (GameManager.instance) {
|
||||
throw new Error("GameManager already exists!");
|
||||
throw new Error('GameManager already exists!');
|
||||
}
|
||||
|
||||
this.scene = scene;
|
||||
|
|
@ -233,7 +233,7 @@ class GameManager {
|
|||
|
||||
static getInstance(): GameManager {
|
||||
if (!GameManager.instance) {
|
||||
throw new Error("GameManager not initialized!");
|
||||
throw new Error('GameManager not initialized!');
|
||||
}
|
||||
return GameManager.instance;
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ class BulletPool {
|
|||
}
|
||||
|
||||
// Pool exhausted - create new bullet
|
||||
console.warn("Bullet pool exhausted, creating new bullet");
|
||||
console.warn('Bullet pool exhausted, creating new bullet');
|
||||
return new Bullet(this.scene, 0, 0);
|
||||
}
|
||||
|
||||
|
|
@ -380,12 +380,12 @@ class InputManager {
|
|||
}
|
||||
|
||||
private setupKeyboard(): void {
|
||||
this.keys = this.scene.input.keyboard.addKeys("W,A,S,D,SPACE,ESC,UP,DOWN,LEFT,RIGHT");
|
||||
this.keys = this.scene.input.keyboard.addKeys('W,A,S,D,SPACE,ESC,UP,DOWN,LEFT,RIGHT');
|
||||
}
|
||||
|
||||
private setupTouch(): void {
|
||||
this.scene.input.on("pointerdown", this.handlePointerDown, this);
|
||||
this.scene.input.on("pointerup", this.handlePointerUp, this);
|
||||
this.scene.input.on('pointerdown', this.handlePointerDown, this);
|
||||
this.scene.input.on('pointerup', this.handlePointerUp, this);
|
||||
}
|
||||
|
||||
update(): void {
|
||||
|
|
@ -412,9 +412,9 @@ class InputManager {
|
|||
class AssetManager {
|
||||
loadAssets(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.scene.load.on("filecomplete", this.handleFileComplete, this);
|
||||
this.scene.load.on("loaderror", this.handleLoadError, this);
|
||||
this.scene.load.on("complete", () => resolve());
|
||||
this.scene.load.on('filecomplete', this.handleFileComplete, this);
|
||||
this.scene.load.on('loaderror', this.handleLoadError, this);
|
||||
this.scene.load.on('complete', () => resolve());
|
||||
|
||||
this.scene.load.start();
|
||||
});
|
||||
|
|
@ -430,8 +430,8 @@ class AssetManager {
|
|||
private loadFallbackAsset(key: string): void {
|
||||
// Load placeholder or default assets
|
||||
switch (key) {
|
||||
case "player":
|
||||
this.scene.load.image("player", "assets/defaults/default-player.png");
|
||||
case 'player':
|
||||
this.scene.load.image('player', 'assets/defaults/default-player.png');
|
||||
break;
|
||||
default:
|
||||
console.warn(`No fallback for asset: ${key}`);
|
||||
|
|
@ -458,11 +458,11 @@ class GameSystem {
|
|||
|
||||
private attemptRecovery(context: string): void {
|
||||
switch (context) {
|
||||
case "update":
|
||||
case 'update':
|
||||
// Reset system state
|
||||
this.reset();
|
||||
break;
|
||||
case "render":
|
||||
case 'render':
|
||||
// Disable visual effects
|
||||
this.disableEffects();
|
||||
break;
|
||||
|
|
@ -482,7 +482,7 @@ class GameSystem {
|
|||
|
||||
```typescript
|
||||
// Example test for game mechanics
|
||||
describe("HealthComponent", () => {
|
||||
describe('HealthComponent', () => {
|
||||
let healthComponent: HealthComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -490,18 +490,18 @@ describe("HealthComponent", () => {
|
|||
healthComponent = new HealthComponent(mockEntity, 100);
|
||||
});
|
||||
|
||||
test("should initialize with correct health", () => {
|
||||
test('should initialize with correct health', () => {
|
||||
expect(healthComponent.currentHealth).toBe(100);
|
||||
expect(healthComponent.maxHealth).toBe(100);
|
||||
});
|
||||
|
||||
test("should handle damage correctly", () => {
|
||||
test('should handle damage correctly', () => {
|
||||
healthComponent.takeDamage(25);
|
||||
expect(healthComponent.currentHealth).toBe(75);
|
||||
expect(healthComponent.isAlive()).toBe(true);
|
||||
});
|
||||
|
||||
test("should handle death correctly", () => {
|
||||
test('should handle death correctly', () => {
|
||||
healthComponent.takeDamage(150);
|
||||
expect(healthComponent.currentHealth).toBe(0);
|
||||
expect(healthComponent.isAlive()).toBe(false);
|
||||
|
|
@ -514,7 +514,7 @@ describe("HealthComponent", () => {
|
|||
**Scene Testing:**
|
||||
|
||||
```typescript
|
||||
describe("GameScene Integration", () => {
|
||||
describe('GameScene Integration', () => {
|
||||
let scene: GameScene;
|
||||
let mockGame: Phaser.Game;
|
||||
|
||||
|
|
@ -524,7 +524,7 @@ describe("GameScene Integration", () => {
|
|||
scene = new GameScene();
|
||||
});
|
||||
|
||||
test("should initialize all systems", () => {
|
||||
test('should initialize all systems', () => {
|
||||
scene.create({});
|
||||
|
||||
expect(scene.gameManager).toBeDefined();
|
||||
|
|
@ -585,25 +585,21 @@ src/
|
|||
### Story Implementation Process
|
||||
|
||||
1. **Read Story Requirements:**
|
||||
|
||||
- Understand acceptance criteria
|
||||
- Identify technical requirements
|
||||
- Review performance constraints
|
||||
|
||||
2. **Plan Implementation:**
|
||||
|
||||
- Identify files to create/modify
|
||||
- Consider component architecture
|
||||
- Plan testing approach
|
||||
|
||||
3. **Implement Feature:**
|
||||
|
||||
- Follow TypeScript strict mode
|
||||
- Use established patterns
|
||||
- Maintain 60 FPS performance
|
||||
|
||||
4. **Test Implementation:**
|
||||
|
||||
- Write unit tests for game logic
|
||||
- Test cross-platform functionality
|
||||
- Validate performance targets
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
2. If the section contains game flow diagrams, level layouts, or system diagrams, explain each diagram briefly with game development context before offering elicitation options (e.g., "The gameplay loop diagram shows how player actions lead to rewards and progression. Notice how each step maintains player engagement and creates opportunities for skill development.")
|
||||
|
||||
3. If the section contains multiple game elements (like multiple mechanics, multiple levels, multiple systems, etc.), inform the user they can apply elicitation actions to:
|
||||
|
||||
- The entire section as a whole
|
||||
- Individual game elements within the section (specify which element when selecting an action)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
[[LLM: Begin by understanding the game design context and goals. Ask clarifying questions if needed to determine the best approach for game-specific ideation.]]
|
||||
|
||||
1. **Establish Game Context**
|
||||
|
||||
- Understand the game genre or opportunity area
|
||||
- Identify target audience and platform constraints
|
||||
- Determine session goals (concept exploration vs. mechanic refinement)
|
||||
|
|
@ -27,7 +26,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **"What If" Game Scenarios**
|
||||
[[LLM: Generate provocative what-if questions that challenge game design assumptions and expand thinking beyond current genre limitations.]]
|
||||
|
||||
- What if players could rewind time in any genre?
|
||||
- What if the game world reacted to the player's real-world location?
|
||||
- What if failure was more rewarding than success?
|
||||
|
|
@ -36,7 +34,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Cross-Genre Fusion**
|
||||
[[LLM: Help user combine unexpected game genres and mechanics to create unique experiences.]]
|
||||
|
||||
- "How might [genre A] mechanics work in [genre B]?"
|
||||
- Puzzle mechanics in action games
|
||||
- Dating sim elements in strategy games
|
||||
|
|
@ -45,7 +42,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Player Motivation Reversal**
|
||||
[[LLM: Flip traditional player motivations to reveal new gameplay possibilities.]]
|
||||
|
||||
- What if losing was the goal?
|
||||
- What if cooperation was forced in competitive games?
|
||||
- What if players had to help their enemies?
|
||||
|
|
@ -62,7 +58,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **SCAMPER for Game Mechanics**
|
||||
[[LLM: Guide through each SCAMPER prompt specifically for game design.]]
|
||||
|
||||
- **S** = Substitute: What mechanics can be substituted? (walking → flying → swimming)
|
||||
- **C** = Combine: What systems can be merged? (inventory + character growth)
|
||||
- **A** = Adapt: What mechanics from other media? (books, movies, sports)
|
||||
|
|
@ -73,7 +68,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Player Agency Spectrum**
|
||||
[[LLM: Explore different levels of player control and agency across game systems.]]
|
||||
|
||||
- Full Control: Direct character movement, combat, building
|
||||
- Indirect Control: Setting rules, giving commands, environmental changes
|
||||
- Influence Only: Suggestions, preferences, emotional reactions
|
||||
|
|
@ -81,7 +75,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Temporal Game Design**
|
||||
[[LLM: Explore how time affects gameplay and player experience.]]
|
||||
|
||||
- Real-time vs. turn-based mechanics
|
||||
- Time travel and manipulation
|
||||
- Persistent vs. session-based progress
|
||||
|
|
@ -92,7 +85,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **Emotion-First Design**
|
||||
[[LLM: Start with target emotions and work backward to mechanics that create them.]]
|
||||
|
||||
- Target Emotion: Wonder → Mechanics: Discovery, mystery, scale
|
||||
- Target Emotion: Triumph → Mechanics: Challenge, skill growth, recognition
|
||||
- Target Emotion: Connection → Mechanics: Cooperation, shared goals, communication
|
||||
|
|
@ -100,7 +92,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Player Archetype Brainstorming**
|
||||
[[LLM: Design for different player types and motivations.]]
|
||||
|
||||
- Achievers: Progression, completion, mastery
|
||||
- Explorers: Discovery, secrets, world-building
|
||||
- Socializers: Interaction, cooperation, community
|
||||
|
|
@ -109,7 +100,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Accessibility-First Innovation**
|
||||
[[LLM: Generate ideas that make games more accessible while creating new gameplay.]]
|
||||
|
||||
- Visual impairment considerations leading to audio-focused mechanics
|
||||
- Motor accessibility inspiring one-handed or simplified controls
|
||||
- Cognitive accessibility driving clear feedback and pacing
|
||||
|
|
@ -119,7 +109,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **Environmental Storytelling**
|
||||
[[LLM: Brainstorm ways the game world itself tells stories without explicit narrative.]]
|
||||
|
||||
- How does the environment show history?
|
||||
- What do interactive objects reveal about characters?
|
||||
- How can level design communicate mood?
|
||||
|
|
@ -127,7 +116,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Player-Generated Narrative**
|
||||
[[LLM: Explore ways players create their own stories through gameplay.]]
|
||||
|
||||
- Emergent storytelling through player choices
|
||||
- Procedural narrative generation
|
||||
- Player-to-player story sharing
|
||||
|
|
@ -135,7 +123,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Genre Expectation Subversion**
|
||||
[[LLM: Identify and deliberately subvert player expectations within genres.]]
|
||||
|
||||
- Fantasy RPG where magic is mundane
|
||||
- Horror game where monsters are friendly
|
||||
- Racing game where going slow is optimal
|
||||
|
|
@ -145,7 +132,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **Platform-Specific Design**
|
||||
[[LLM: Generate ideas that leverage unique platform capabilities.]]
|
||||
|
||||
- Mobile: GPS, accelerometer, camera, always-connected
|
||||
- Web: URLs, tabs, social sharing, real-time collaboration
|
||||
- Console: Controllers, TV viewing, couch co-op
|
||||
|
|
@ -153,7 +139,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Constraint-Based Creativity**
|
||||
[[LLM: Use technical or design constraints as creative catalysts.]]
|
||||
|
||||
- One-button games
|
||||
- Games without graphics
|
||||
- Games that play in notification bars
|
||||
|
|
@ -199,19 +184,16 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
[[LLM: Guide the brainstorming session with appropriate pacing for game design exploration.]]
|
||||
|
||||
1. **Inspiration Phase** (10-15 min)
|
||||
|
||||
- Reference existing games and mechanics
|
||||
- Explore player experiences and emotions
|
||||
- Gather visual and thematic inspiration
|
||||
|
||||
2. **Divergent Exploration** (25-35 min)
|
||||
|
||||
- Generate many game concepts or mechanics
|
||||
- Use expansion and fusion techniques
|
||||
- Encourage wild and impossible ideas
|
||||
|
||||
3. **Player-Centered Filtering** (15-20 min)
|
||||
|
||||
- Consider target audience reactions
|
||||
- Evaluate emotional impact and engagement
|
||||
- Group ideas by player experience goals
|
||||
|
|
|
|||
|
|
@ -17,21 +17,21 @@ workflow:
|
|||
- brainstorming_session
|
||||
- game_research_prompt
|
||||
- player_research
|
||||
notes: 'Start with brainstorming game concepts, then create comprehensive game brief. SAVE OUTPUT: Copy final game-brief.md to your project''s docs/design/ folder.'
|
||||
notes: "Start with brainstorming game concepts, then create comprehensive game brief. SAVE OUTPUT: Copy final game-brief.md to your project's docs/design/ folder."
|
||||
- agent: game-designer
|
||||
creates: game-design-doc.md
|
||||
requires: game-brief.md
|
||||
optional_steps:
|
||||
- competitive_analysis
|
||||
- technical_research
|
||||
notes: 'Create detailed Game Design Document using game-design-doc-tmpl. Defines all gameplay mechanics, progression, and technical requirements. SAVE OUTPUT: Copy final game-design-doc.md to your project''s docs/design/ folder.'
|
||||
notes: "Create detailed Game Design Document using game-design-doc-tmpl. Defines all gameplay mechanics, progression, and technical requirements. SAVE OUTPUT: Copy final game-design-doc.md to your project's docs/design/ folder."
|
||||
- agent: game-designer
|
||||
creates: level-design-doc.md
|
||||
requires: game-design-doc.md
|
||||
optional_steps:
|
||||
- level_prototyping
|
||||
- difficulty_analysis
|
||||
notes: 'Create level design framework using level-design-doc-tmpl. Establishes content creation guidelines and performance requirements. SAVE OUTPUT: Copy final level-design-doc.md to your project''s docs/design/ folder.'
|
||||
notes: "Create level design framework using level-design-doc-tmpl. Establishes content creation guidelines and performance requirements. SAVE OUTPUT: Copy final level-design-doc.md to your project's docs/design/ folder."
|
||||
- agent: solution-architect
|
||||
creates: game-architecture.md
|
||||
requires:
|
||||
|
|
@ -41,7 +41,7 @@ workflow:
|
|||
- technical_research_prompt
|
||||
- performance_analysis
|
||||
- platform_research
|
||||
notes: 'Create comprehensive technical architecture using game-architecture-tmpl. Defines Phaser 3 systems, performance optimization, and code structure. SAVE OUTPUT: Copy final game-architecture.md to your project''s docs/architecture/ folder.'
|
||||
notes: "Create comprehensive technical architecture using game-architecture-tmpl. Defines Phaser 3 systems, performance optimization, and code structure. SAVE OUTPUT: Copy final game-architecture.md to your project's docs/architecture/ folder."
|
||||
- agent: game-designer
|
||||
validates: design_consistency
|
||||
requires: all_design_documents
|
||||
|
|
@ -66,7 +66,7 @@ workflow:
|
|||
optional_steps:
|
||||
- quick_brainstorming
|
||||
- concept_validation
|
||||
notes: 'Create focused game brief for prototype. Emphasize core mechanics and immediate playability. SAVE OUTPUT: Copy final game-brief.md to your project''s docs/ folder.'
|
||||
notes: "Create focused game brief for prototype. Emphasize core mechanics and immediate playability. SAVE OUTPUT: Copy final game-brief.md to your project's docs/ folder."
|
||||
- agent: game-designer
|
||||
creates: prototype-design.md
|
||||
uses: create-doc prototype-design OR create-game-story
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ workflow:
|
|||
notes: Implement stories in priority order. Test frequently and adjust design based on what feels fun. Document discoveries.
|
||||
workflow_end:
|
||||
action: prototype_evaluation
|
||||
notes: 'Prototype complete. Evaluate core mechanics, gather feedback, and decide next steps: iterate, expand, or archive.'
|
||||
notes: "Prototype complete. Evaluate core mechanics, gather feedback, and decide next steps: iterate, expand, or archive."
|
||||
game_jam_sequence:
|
||||
- step: jam_concept
|
||||
agent: game-designer
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ commands:
|
|||
- explain: teach me what and why you did whatever you just did in detail so I can learn. Explain to me as if you were training a junior Unity developer.
|
||||
- exit: Say goodbye as the Game Developer, and then abandon inhabiting this persona
|
||||
develop-story:
|
||||
order-of-execution: "Read (first or next) task→Implement Task and its subtasks→Write tests→Execute validations→Only if ALL pass, then update the task checkbox with [x]→Update story section File List to ensure it lists and new or modified or deleted source file→repeat order-of-execution until complete"
|
||||
order-of-execution: 'Read (first or next) task→Implement Task and its subtasks→Write tests→Execute validations→Only if ALL pass, then update the task checkbox with [x]→Update story section File List to ensure it lists and new or modified or deleted source file→repeat order-of-execution until complete'
|
||||
story-file-updates-ONLY:
|
||||
- CRITICAL: ONLY UPDATE THE STORY FILE WITH UPDATES TO SECTIONS INDICATED BELOW. DO NOT MODIFY ANY OTHER SECTIONS.
|
||||
- CRITICAL: You are ONLY authorized to edit these specific sections of story files - Tasks / Subtasks Checkboxes, Dev Agent Record section and all its subsections, Agent Model Used, Debug Log References, Completion Notes List, File List, Change Log, Status
|
||||
- CRITICAL: DO NOT modify Status, Story, Acceptance Criteria, Dev Notes, Testing sections, or any other sections not listed above
|
||||
blocking: "HALT for: Unapproved deps needed, confirm with user | Ambiguous after story check | 3 failures attempting to implement or fix something repeatedly | Missing config | Failing regression"
|
||||
ready-for-review: "Code matches requirements + All validations pass + Follows Unity & C# standards + File List complete + Stable FPS"
|
||||
blocking: 'HALT for: Unapproved deps needed, confirm with user | Ambiguous after story check | 3 failures attempting to implement or fix something repeatedly | Missing config | Failing regression'
|
||||
ready-for-review: 'Code matches requirements + All validations pass + Follows Unity & C# standards + File List complete + Stable FPS'
|
||||
completion: "All Tasks and Subtasks marked [x] and have tests→Validations and full regression passes (DON'T BE LAZY, EXECUTE ALL TESTS and CONFIRM)→Ensure File List is Complete→run the task execute-checklist for the checklist game-story-dod-checklist→set story status: 'Ready for Review'→HALT"
|
||||
dependencies:
|
||||
tasks:
|
||||
|
|
|
|||
|
|
@ -355,34 +355,29 @@ Ask the user if they want to work through the checklist:
|
|||
Generate a comprehensive validation report that includes:
|
||||
|
||||
1. Executive Summary
|
||||
|
||||
- Overall game architecture readiness (High/Medium/Low)
|
||||
- Critical risks for game development
|
||||
- Key strengths of the game architecture
|
||||
- Unity-specific assessment
|
||||
|
||||
2. Game Systems Analysis
|
||||
|
||||
- Pass rate for each major system section
|
||||
- Most concerning gaps in game architecture
|
||||
- Systems requiring immediate attention
|
||||
- Unity integration completeness
|
||||
|
||||
3. Performance Risk Assessment
|
||||
|
||||
- Top 5 performance risks for the game
|
||||
- Mobile platform specific concerns
|
||||
- Frame rate stability risks
|
||||
- Memory usage concerns
|
||||
|
||||
4. Implementation Recommendations
|
||||
|
||||
- Must-fix items before development
|
||||
- Unity-specific improvements needed
|
||||
- Game development workflow enhancements
|
||||
|
||||
5. AI Agent Implementation Readiness
|
||||
|
||||
- Game-specific concerns for AI implementation
|
||||
- Unity component complexity assessment
|
||||
- Areas needing additional clarification
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
1. **Requirements Met:**
|
||||
|
||||
[[LLM: Be specific - list each requirement and whether it's complete. Include game-specific requirements from GDD]]
|
||||
|
||||
- [ ] All functional requirements specified in the story are implemented.
|
||||
- [ ] All acceptance criteria defined in the story are met.
|
||||
- [ ] Game Design Document (GDD) requirements referenced in the story are implemented.
|
||||
|
|
@ -34,7 +33,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
2. **Coding Standards & Project Structure:**
|
||||
|
||||
[[LLM: Code quality matters for maintainability. Check Unity-specific patterns and C# standards]]
|
||||
|
||||
- [ ] All new/modified code strictly adheres to `Operational Guidelines`.
|
||||
- [ ] All new/modified code aligns with `Project Structure` (Scripts/, Prefabs/, Scenes/, etc.).
|
||||
- [ ] Adherence to `Tech Stack` for Unity version and packages used.
|
||||
|
|
@ -48,7 +46,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
3. **Testing:**
|
||||
|
||||
[[LLM: Testing proves your code works. Include Unity-specific testing with NUnit and manual testing]]
|
||||
|
||||
- [ ] All required unit tests (NUnit) as per the story and testing strategy are implemented.
|
||||
- [ ] All required integration tests (if applicable) are implemented.
|
||||
- [ ] Manual testing performed in Unity Editor for all game functionality.
|
||||
|
|
@ -60,7 +57,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
4. **Functionality & Verification:**
|
||||
|
||||
[[LLM: Did you actually run and test your code in Unity? Be specific about game mechanics tested]]
|
||||
|
||||
- [ ] Functionality has been manually verified in Unity Editor and play mode.
|
||||
- [ ] Game mechanics work as specified in the GDD.
|
||||
- [ ] Player controls and input handling work correctly.
|
||||
|
|
@ -73,7 +69,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
5. **Story Administration:**
|
||||
|
||||
[[LLM: Documentation helps the next developer. Include Unity-specific implementation notes]]
|
||||
|
||||
- [ ] All tasks within the story file are marked as complete.
|
||||
- [ ] Any clarifications or decisions made during development are documented.
|
||||
- [ ] Unity-specific implementation details documented (scene changes, prefab modifications).
|
||||
|
|
@ -83,7 +78,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
6. **Dependencies, Build & Configuration:**
|
||||
|
||||
[[LLM: Build issues block everyone. Ensure Unity project builds for all target platforms]]
|
||||
|
||||
- [ ] Unity project builds successfully without errors.
|
||||
- [ ] Project builds for all target platforms (desktop/mobile as specified).
|
||||
- [ ] Any new Unity packages or Asset Store items were pre-approved OR approved by user.
|
||||
|
|
@ -95,7 +89,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
7. **Game-Specific Quality:**
|
||||
|
||||
[[LLM: Game quality matters. Check performance, game feel, and player experience]]
|
||||
|
||||
- [ ] Frame rate meets target (30/60 FPS) on all platforms.
|
||||
- [ ] Memory usage within acceptable limits.
|
||||
- [ ] Game feel and responsiveness meet design requirements.
|
||||
|
|
@ -107,7 +100,6 @@ The goal is quality delivery, not just checking boxes.]]
|
|||
8. **Documentation (If Applicable):**
|
||||
|
||||
[[LLM: Good documentation prevents future confusion. Include Unity-specific docs]]
|
||||
|
||||
- [ ] Code documentation (XML comments) for public APIs complete.
|
||||
- [ ] Unity component documentation in Inspector updated.
|
||||
- [ ] User-facing documentation updated, if changes impact players.
|
||||
|
|
|
|||
|
|
@ -270,7 +270,6 @@ that can handle [specific game requirements] with stable performance."
|
|||
**Prerequisites**: Game planning documents must exist in `docs/` folder of Unity project
|
||||
|
||||
1. **Document Sharding** (CRITICAL STEP for Game Development):
|
||||
|
||||
- Documents created by Game Designer/Architect (in Web or IDE) MUST be sharded for development
|
||||
- Use core BMad agents or tools to shard:
|
||||
a) **Manual**: Use core BMad `shard-doc` task if available
|
||||
|
|
@ -293,20 +292,17 @@ Resulting Unity Project Folder Structure:
|
|||
3. **Game Development Cycle** (Sequential, one game story at a time):
|
||||
|
||||
**CRITICAL CONTEXT MANAGEMENT for Unity Development**:
|
||||
|
||||
- **Context windows matter!** Always use fresh, clean context windows
|
||||
- **Model selection matters!** Use most powerful thinking model for Game SM story creation
|
||||
- **ALWAYS start new chat between Game SM, Game Dev, and QA work**
|
||||
|
||||
**Step 1 - Game Story Creation**:
|
||||
|
||||
- **NEW CLEAN CHAT** → Select powerful model → `/bmad2du/game-sm` → `*draft`
|
||||
- Game SM executes create-game-story task using `game-story-tmpl`
|
||||
- Review generated story in `docs/game-stories/`
|
||||
- Update status from "Draft" to "Approved"
|
||||
|
||||
**Step 2 - Unity Game Story Implementation**:
|
||||
|
||||
- **NEW CLEAN CHAT** → `/bmad2du/game-developer`
|
||||
- Agent asks which game story to implement
|
||||
- Include story file content to save game dev agent lookup time
|
||||
|
|
@ -315,7 +311,6 @@ Resulting Unity Project Folder Structure:
|
|||
- Game Dev marks story as "Review" when complete with all Unity tests passing
|
||||
|
||||
**Step 3 - Game QA Review**:
|
||||
|
||||
- **NEW CLEAN CHAT** → Use core `@qa` agent → execute review-story task
|
||||
- QA performs senior Unity developer code review
|
||||
- QA can refactor and improve Unity code directly
|
||||
|
|
@ -355,14 +350,12 @@ Since this expansion pack doesn't include specific brownfield templates, you'll
|
|||
|
||||
1. **Upload Unity project to Web UI** (GitHub URL, files, or zip)
|
||||
2. **Create adapted Game Design Document**: `/bmad2du/game-designer` - Modify `game-design-doc-tmpl` to include:
|
||||
|
||||
- Analysis of existing game systems
|
||||
- Integration points for new features
|
||||
- Compatibility requirements
|
||||
- Risk assessment for changes
|
||||
|
||||
3. **Game Architecture Planning**:
|
||||
|
||||
- Use `/bmad2du/game-architect` with `game-architecture-tmpl`
|
||||
- Focus on how new features integrate with existing Unity systems
|
||||
- Plan for gradual rollout and testing
|
||||
|
|
@ -463,7 +456,7 @@ Use the `shard-doc` task or `@kayvan/markdown-tree-parser` tool for automatic ga
|
|||
|
||||
- **Claude Code**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Cursor**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Windsurf**: `/bmad2du/game-designer`, `/bmad2du/game-developer`, `/bmad2du/game-sm`, `/bmad2du/game-architect`
|
||||
- **Trae**: `@bmad2du/game-designer`, `@bmad2du/game-developer`, `@bmad2du/game-sm`, `@bmad2du/game-architect`
|
||||
- **Roo Code**: Select mode from mode selector with bmad2du prefix
|
||||
- **GitHub Copilot**: Open the Chat view (`⌃⌘I` on Mac, `Ctrl+Alt+I` on Windows/Linux) and select the appropriate game agent.
|
||||
|
|
|
|||
|
|
@ -531,25 +531,21 @@ Assets/
|
|||
### Story Implementation Process
|
||||
|
||||
1. **Read Story Requirements:**
|
||||
|
||||
- Understand acceptance criteria
|
||||
- Identify technical requirements
|
||||
- Review performance constraints
|
||||
|
||||
2. **Plan Implementation:**
|
||||
|
||||
- Identify files to create/modify
|
||||
- Consider Unity's component-based architecture
|
||||
- Plan testing approach
|
||||
|
||||
3. **Implement Feature:**
|
||||
|
||||
- Write clean C# code following all guidelines
|
||||
- Use established patterns
|
||||
- Maintain stable FPS performance
|
||||
|
||||
4. **Test Implementation:**
|
||||
|
||||
- Write edit mode tests for game logic
|
||||
- Write play mode tests for integration testing
|
||||
- Test cross-platform functionality
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
2. If the section contains game flow diagrams, level layouts, or system diagrams, explain each diagram briefly with game development context before offering elicitation options (e.g., "The gameplay loop diagram shows how player actions lead to rewards and progression. Notice how each step maintains player engagement and creates opportunities for skill development.")
|
||||
|
||||
3. If the section contains multiple game elements (like multiple mechanics, multiple levels, multiple systems, etc.), inform the user they can apply elicitation actions to:
|
||||
|
||||
- The entire section as a whole
|
||||
- Individual game elements within the section (specify which element when selecting an action)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
### 1. Initial Setup & Mode Selection
|
||||
|
||||
- **Acknowledge Task & Inputs:**
|
||||
|
||||
- Confirm with the user that the "Game Development Correct Course Task" is being initiated.
|
||||
- Verify the change trigger (e.g., performance issue, platform constraint, gameplay feedback, technical blocker).
|
||||
- Confirm access to relevant game artifacts:
|
||||
|
|
@ -35,7 +34,6 @@
|
|||
### 2. Execute Game Development Checklist Analysis
|
||||
|
||||
- Systematically work through the game-change-checklist sections:
|
||||
|
||||
1. **Change Context & Game Impact**
|
||||
2. **Feature/System Impact Analysis**
|
||||
3. **Technical Artifact Conflict Resolution**
|
||||
|
|
@ -60,7 +58,6 @@
|
|||
Based on the analysis and agreed path forward:
|
||||
|
||||
- **Identify affected game artifacts requiring updates:**
|
||||
|
||||
- GDD sections (mechanics, systems, progression)
|
||||
- Technical specifications (architecture, performance targets)
|
||||
- Unity-specific configurations (build settings, quality settings)
|
||||
|
|
@ -69,7 +66,6 @@ Based on the analysis and agreed path forward:
|
|||
- Platform-specific adaptations
|
||||
|
||||
- **Draft explicit changes for each artifact:**
|
||||
|
||||
- **Game Stories:** Revise story text, Unity-specific acceptance criteria, technical constraints
|
||||
- **Technical Specs:** Update architecture diagrams, component hierarchies, performance budgets
|
||||
- **Unity Configurations:** Propose settings changes, optimization strategies, platform variants
|
||||
|
|
@ -89,14 +85,12 @@ Based on the analysis and agreed path forward:
|
|||
- Create a comprehensive proposal document containing:
|
||||
|
||||
**A. Change Summary:**
|
||||
|
||||
- Original issue (performance, gameplay, technical constraint)
|
||||
- Game systems affected
|
||||
- Platform/performance implications
|
||||
- Chosen solution approach
|
||||
|
||||
**B. Technical Impact Analysis:**
|
||||
|
||||
- Unity architecture changes needed
|
||||
- Performance implications (with metrics)
|
||||
- Platform compatibility effects
|
||||
|
|
@ -104,14 +98,12 @@ Based on the analysis and agreed path forward:
|
|||
- Third-party dependency impacts
|
||||
|
||||
**C. Specific Proposed Edits:**
|
||||
|
||||
- For each game story: "Change Story GS-X.Y from: [old] To: [new]"
|
||||
- For technical specs: "Update Unity Architecture Section X: [changes]"
|
||||
- For GDD: "Modify [Feature] in Section Y: [updates]"
|
||||
- For configurations: "Change [Setting] from [old_value] to [new_value]"
|
||||
|
||||
**D. Implementation Considerations:**
|
||||
|
||||
- Required Unity version updates
|
||||
- Asset reimport needs
|
||||
- Shader recompilation requirements
|
||||
|
|
@ -123,7 +115,6 @@ Based on the analysis and agreed path forward:
|
|||
- Provide the finalized document to the user
|
||||
|
||||
- **Based on change scope:**
|
||||
|
||||
- **Minor adjustments (can be handled in current sprint):**
|
||||
- Confirm task completion
|
||||
- Suggest handoff to game-dev agent for implementation
|
||||
|
|
@ -137,7 +128,6 @@ Based on the analysis and agreed path forward:
|
|||
## Output Deliverables
|
||||
|
||||
- **Primary:** "Game Development Change Proposal" document containing:
|
||||
|
||||
- Game-specific change analysis
|
||||
- Technical impact assessment with Unity context
|
||||
- Platform and performance considerations
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
[[LLM: Begin by understanding the game design context and goals. Ask clarifying questions if needed to determine the best approach for game-specific ideation.]]
|
||||
|
||||
1. **Establish Game Context**
|
||||
|
||||
- Understand the game genre or opportunity area
|
||||
- Identify target audience and platform constraints
|
||||
- Determine session goals (concept exploration vs. mechanic refinement)
|
||||
|
|
@ -27,7 +26,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **"What If" Game Scenarios**
|
||||
[[LLM: Generate provocative what-if questions that challenge game design assumptions and expand thinking beyond current genre limitations.]]
|
||||
|
||||
- What if players could rewind time in any genre?
|
||||
- What if the game world reacted to the player's real-world location?
|
||||
- What if failure was more rewarding than success?
|
||||
|
|
@ -36,7 +34,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Cross-Genre Fusion**
|
||||
[[LLM: Help user combine unexpected game genres and mechanics to create unique experiences.]]
|
||||
|
||||
- "How might [genre A] mechanics work in [genre B]?"
|
||||
- Puzzle mechanics in action games
|
||||
- Dating sim elements in strategy games
|
||||
|
|
@ -45,7 +42,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Player Motivation Reversal**
|
||||
[[LLM: Flip traditional player motivations to reveal new gameplay possibilities.]]
|
||||
|
||||
- What if losing was the goal?
|
||||
- What if cooperation was forced in competitive games?
|
||||
- What if players had to help their enemies?
|
||||
|
|
@ -62,7 +58,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **SCAMPER for Game Mechanics**
|
||||
[[LLM: Guide through each SCAMPER prompt specifically for game design.]]
|
||||
|
||||
- **S** = Substitute: What mechanics can be substituted? (walking → flying → swimming)
|
||||
- **C** = Combine: What systems can be merged? (inventory + character growth)
|
||||
- **A** = Adapt: What mechanics from other media? (books, movies, sports)
|
||||
|
|
@ -73,7 +68,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Player Agency Spectrum**
|
||||
[[LLM: Explore different levels of player control and agency across game systems.]]
|
||||
|
||||
- Full Control: Direct character movement, combat, building
|
||||
- Indirect Control: Setting rules, giving commands, environmental changes
|
||||
- Influence Only: Suggestions, preferences, emotional reactions
|
||||
|
|
@ -81,7 +75,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Temporal Game Design**
|
||||
[[LLM: Explore how time affects gameplay and player experience.]]
|
||||
|
||||
- Real-time vs. turn-based mechanics
|
||||
- Time travel and manipulation
|
||||
- Persistent vs. session-based progress
|
||||
|
|
@ -92,7 +85,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **Emotion-First Design**
|
||||
[[LLM: Start with target emotions and work backward to mechanics that create them.]]
|
||||
|
||||
- Target Emotion: Wonder → Mechanics: Discovery, mystery, scale
|
||||
- Target Emotion: Triumph → Mechanics: Challenge, skill growth, recognition
|
||||
- Target Emotion: Connection → Mechanics: Cooperation, shared goals, communication
|
||||
|
|
@ -100,7 +92,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Player Archetype Brainstorming**
|
||||
[[LLM: Design for different player types and motivations.]]
|
||||
|
||||
- Achievers: Progression, completion, mastery
|
||||
- Explorers: Discovery, secrets, world-building
|
||||
- Socializers: Interaction, cooperation, community
|
||||
|
|
@ -109,7 +100,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Accessibility-First Innovation**
|
||||
[[LLM: Generate ideas that make games more accessible while creating new gameplay.]]
|
||||
|
||||
- Visual impairment considerations leading to audio-focused mechanics
|
||||
- Motor accessibility inspiring one-handed or simplified controls
|
||||
- Cognitive accessibility driving clear feedback and pacing
|
||||
|
|
@ -119,7 +109,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **Environmental Storytelling**
|
||||
[[LLM: Brainstorm ways the game world itself tells stories without explicit narrative.]]
|
||||
|
||||
- How does the environment show history?
|
||||
- What do interactive objects reveal about characters?
|
||||
- How can level design communicate mood?
|
||||
|
|
@ -127,7 +116,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Player-Generated Narrative**
|
||||
[[LLM: Explore ways players create their own stories through gameplay.]]
|
||||
|
||||
- Emergent storytelling through player choices
|
||||
- Procedural narrative generation
|
||||
- Player-to-player story sharing
|
||||
|
|
@ -135,7 +123,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
3. **Genre Expectation Subversion**
|
||||
[[LLM: Identify and deliberately subvert player expectations within genres.]]
|
||||
|
||||
- Fantasy RPG where magic is mundane
|
||||
- Horror game where monsters are friendly
|
||||
- Racing game where going slow is optimal
|
||||
|
|
@ -145,7 +132,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
1. **Platform-Specific Design**
|
||||
[[LLM: Generate ideas that leverage unique platform capabilities.]]
|
||||
|
||||
- Mobile: GPS, accelerometer, camera, always-connected
|
||||
- Web: URLs, tabs, social sharing, real-time collaboration
|
||||
- Console: Controllers, TV viewing, couch co-op
|
||||
|
|
@ -153,7 +139,6 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
|
||||
2. **Constraint-Based Creativity**
|
||||
[[LLM: Use technical or design constraints as creative catalysts.]]
|
||||
|
||||
- One-button games
|
||||
- Games without graphics
|
||||
- Games that play in notification bars
|
||||
|
|
@ -199,19 +184,16 @@ This task provides a comprehensive toolkit of creative brainstorming techniques
|
|||
[[LLM: Guide the brainstorming session with appropriate pacing for game design exploration.]]
|
||||
|
||||
1. **Inspiration Phase** (10-15 min)
|
||||
|
||||
- Reference existing games and mechanics
|
||||
- Explore player experiences and emotions
|
||||
- Gather visual and thematic inspiration
|
||||
|
||||
2. **Divergent Exploration** (25-35 min)
|
||||
|
||||
- Generate many game concepts or mechanics
|
||||
- Use expansion and fusion techniques
|
||||
- Encourage wild and impossible ideas
|
||||
|
||||
3. **Player-Centered Filtering** (15-20 min)
|
||||
|
||||
- Consider target audience reactions
|
||||
- Evaluate emotional impact and engagement
|
||||
- Group ideas by player experience goals
|
||||
|
|
|
|||
|
|
@ -17,21 +17,21 @@ workflow:
|
|||
- brainstorming_session
|
||||
- game_research_prompt
|
||||
- player_research
|
||||
notes: 'Start with brainstorming game concepts, then create comprehensive game brief. SAVE OUTPUT: Copy final game-brief.md to your project''s docs/design/ folder.'
|
||||
notes: "Start with brainstorming game concepts, then create comprehensive game brief. SAVE OUTPUT: Copy final game-brief.md to your project's docs/design/ folder."
|
||||
- agent: game-designer
|
||||
creates: game-design-doc.md
|
||||
requires: game-brief.md
|
||||
optional_steps:
|
||||
- competitive_analysis
|
||||
- technical_research
|
||||
notes: 'Create detailed Game Design Document using game-design-doc-tmpl. Defines all gameplay mechanics, progression, and technical requirements. SAVE OUTPUT: Copy final game-design-doc.md to your project''s docs/design/ folder.'
|
||||
notes: "Create detailed Game Design Document using game-design-doc-tmpl. Defines all gameplay mechanics, progression, and technical requirements. SAVE OUTPUT: Copy final game-design-doc.md to your project's docs/design/ folder."
|
||||
- agent: game-designer
|
||||
creates: level-design-doc.md
|
||||
requires: game-design-doc.md
|
||||
optional_steps:
|
||||
- level_prototyping
|
||||
- difficulty_analysis
|
||||
notes: 'Create level design framework using level-design-doc-tmpl. Establishes content creation guidelines and performance requirements. SAVE OUTPUT: Copy final level-design-doc.md to your project''s docs/design/ folder.'
|
||||
notes: "Create level design framework using level-design-doc-tmpl. Establishes content creation guidelines and performance requirements. SAVE OUTPUT: Copy final level-design-doc.md to your project's docs/design/ folder."
|
||||
- agent: solution-architect
|
||||
creates: game-architecture.md
|
||||
requires:
|
||||
|
|
@ -41,7 +41,7 @@ workflow:
|
|||
- technical_research_prompt
|
||||
- performance_analysis
|
||||
- platform_research
|
||||
notes: 'Create comprehensive technical architecture using game-architecture-tmpl. Defines Unity systems, performance optimization, and code structure. SAVE OUTPUT: Copy final game-architecture.md to your project''s docs/architecture/ folder.'
|
||||
notes: "Create comprehensive technical architecture using game-architecture-tmpl. Defines Unity systems, performance optimization, and code structure. SAVE OUTPUT: Copy final game-architecture.md to your project's docs/architecture/ folder."
|
||||
- agent: game-designer
|
||||
validates: design_consistency
|
||||
requires: all_design_documents
|
||||
|
|
@ -66,7 +66,7 @@ workflow:
|
|||
optional_steps:
|
||||
- quick_brainstorming
|
||||
- concept_validation
|
||||
notes: 'Create focused game brief for prototype. Emphasize core mechanics and immediate playability. SAVE OUTPUT: Copy final game-brief.md to your project''s docs/ folder.'
|
||||
notes: "Create focused game brief for prototype. Emphasize core mechanics and immediate playability. SAVE OUTPUT: Copy final game-brief.md to your project's docs/ folder."
|
||||
- agent: game-designer
|
||||
creates: prototype-design.md
|
||||
uses: create-doc prototype-design OR create-game-story
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ workflow:
|
|||
notes: Implement stories in priority order. Test frequently in the Unity Editor and adjust design based on what feels fun. Document discoveries.
|
||||
workflow_end:
|
||||
action: prototype_evaluation
|
||||
notes: 'Prototype complete. Evaluate core mechanics, gather feedback, and decide next steps: iterate, expand, or archive.'
|
||||
notes: "Prototype complete. Evaluate core mechanics, gather feedback, and decide next steps: iterate, expand, or archive."
|
||||
game_jam_sequence:
|
||||
- step: jam_concept
|
||||
agent: game-designer
|
||||
|
|
|
|||
|
|
@ -247,17 +247,14 @@ A comprehensive 16-section checklist covering:
|
|||
### Common Issues
|
||||
|
||||
1. **Infrastructure Drift**
|
||||
|
||||
- Solution: Implement drift detection in IaC pipelines
|
||||
- Prevention: Restrict manual changes, enforce GitOps
|
||||
|
||||
2. **Cost Overruns**
|
||||
|
||||
- Solution: Implement cost monitoring and alerts
|
||||
- Prevention: Resource tagging, budget limits
|
||||
|
||||
3. **Performance Problems**
|
||||
|
||||
- Solution: Review monitoring data, scale resources
|
||||
- Prevention: Load testing, capacity planning
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ To conduct a thorough review of existing infrastructure to identify improvement
|
|||
### 3. Conduct Systematic Review
|
||||
|
||||
- **If "Incremental Mode" was selected:**
|
||||
|
||||
- For each section of the infrastructure checklist:
|
||||
- **a. Present Section Focus:** Explain what aspects of infrastructure this section reviews
|
||||
- **b. Work Through Items:** Examine each checklist item against current infrastructure
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ To comprehensively validate platform infrastructure changes against security, re
|
|||
### 4. Execute Comprehensive Platform Validation Process
|
||||
|
||||
- **If "Incremental Mode" was selected:**
|
||||
|
||||
- For each section of the infrastructure checklist (Sections 1-16):
|
||||
- **a. Present Section Purpose:** Explain what this section validates and why it's important for platform operations
|
||||
- **b. Work Through Items:** Present each checklist item, guide the user through validation, and document compliance or gaps
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
98
package.json
98
package.json
|
|
@ -1,7 +1,23 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "bmad-method",
|
||||
"version": "4.36.2",
|
||||
"description": "Breakthrough Method of Agile AI-driven Development",
|
||||
"keywords": [
|
||||
"agile",
|
||||
"ai",
|
||||
"orchestrator",
|
||||
"development",
|
||||
"methodology",
|
||||
"agents",
|
||||
"bmad"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/bmadcode/BMAD-METHOD.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Brian (BMad) Madison",
|
||||
"main": "tools/cli.js",
|
||||
"bin": {
|
||||
"bmad": "tools/bmad-npx-wrapper.js",
|
||||
|
|
@ -11,27 +27,43 @@
|
|||
"build": "node tools/cli.js build",
|
||||
"build:agents": "node tools/cli.js build --agents-only",
|
||||
"build:teams": "node tools/cli.js build --teams-only",
|
||||
"list:agents": "node tools/cli.js list:agents",
|
||||
"validate": "node tools/cli.js validate",
|
||||
"flatten": "node tools/flattener/main.js",
|
||||
"format": "prettier --write \"**/*.{js,cjs,mjs,json,md,yml,yaml}\"",
|
||||
"format:check": "prettier --check \"**/*.{js,cjs,mjs,json,md,yml,yaml}\"",
|
||||
"install:bmad": "node tools/installer/bin/bmad.js install",
|
||||
"format": "prettier --write \"**/*.md\"",
|
||||
"version:patch": "node tools/version-bump.js patch",
|
||||
"version:minor": "node tools/version-bump.js minor",
|
||||
"version:major": "node tools/version-bump.js major",
|
||||
"version:expansion": "node tools/bump-expansion-version.js",
|
||||
"version:expansion:set": "node tools/update-expansion-version.js",
|
||||
"version:all": "node tools/bump-all-versions.js",
|
||||
"version:all:minor": "node tools/bump-all-versions.js minor",
|
||||
"version:all:major": "node tools/bump-all-versions.js major",
|
||||
"version:all:patch": "node tools/bump-all-versions.js patch",
|
||||
"version:expansion:all": "node tools/bump-all-versions.js",
|
||||
"version:expansion:all:minor": "node tools/bump-all-versions.js minor",
|
||||
"version:expansion:all:major": "node tools/bump-all-versions.js major",
|
||||
"version:expansion:all:patch": "node tools/bump-all-versions.js patch",
|
||||
"lint": "eslint . --ext .js,.cjs,.mjs,.yml,.yaml --max-warnings=0",
|
||||
"lint:fix": "eslint . --ext .js,.cjs,.mjs,.yml,.yaml --fix",
|
||||
"list:agents": "node tools/cli.js list:agents",
|
||||
"prepare": "husky",
|
||||
"release": "semantic-release",
|
||||
"release:test": "semantic-release --dry-run --no-ci || echo 'Config test complete - authentication errors are expected locally'",
|
||||
"prepare": "husky"
|
||||
"validate": "node tools/cli.js validate",
|
||||
"version:all": "node tools/bump-all-versions.js",
|
||||
"version:all:major": "node tools/bump-all-versions.js major",
|
||||
"version:all:minor": "node tools/bump-all-versions.js minor",
|
||||
"version:all:patch": "node tools/bump-all-versions.js patch",
|
||||
"version:expansion": "node tools/bump-expansion-version.js",
|
||||
"version:expansion:all": "node tools/bump-all-versions.js",
|
||||
"version:expansion:all:major": "node tools/bump-all-versions.js major",
|
||||
"version:expansion:all:minor": "node tools/bump-all-versions.js minor",
|
||||
"version:expansion:all:patch": "node tools/bump-all-versions.js patch",
|
||||
"version:expansion:set": "node tools/update-expansion-version.js",
|
||||
"version:major": "node tools/version-bump.js major",
|
||||
"version:minor": "node tools/version-bump.js minor",
|
||||
"version:patch": "node tools/version-bump.js patch"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,cjs,mjs}": [
|
||||
"eslint --fix --max-warnings=0",
|
||||
"prettier --write"
|
||||
],
|
||||
"**/*.{yml,yaml}": [
|
||||
"eslint --fix",
|
||||
"prettier --write"
|
||||
],
|
||||
"**/*.{json,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@kayvan/markdown-tree-parser": "^1.5.0",
|
||||
|
|
@ -45,37 +77,25 @@
|
|||
"js-yaml": "^4.1.0",
|
||||
"ora": "^5.4.1"
|
||||
},
|
||||
"keywords": [
|
||||
"agile",
|
||||
"ai",
|
||||
"orchestrator",
|
||||
"development",
|
||||
"methodology",
|
||||
"agents",
|
||||
"bmad"
|
||||
],
|
||||
"author": "Brian (BMad) Madison",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/bmadcode/BMAD-METHOD.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-n": "^17.21.3",
|
||||
"eslint-plugin-unicorn": "^60.0.0",
|
||||
"eslint-plugin-yml": "^1.18.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^30.0.4",
|
||||
"lint-staged": "^16.1.1",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-packagejson": "^2.5.19",
|
||||
"semantic-release": "^22.0.0",
|
||||
"yaml-eslint-parser": "^1.2.3",
|
||||
"yaml-lint": "^1.7.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.md": [
|
||||
"prettier --write"
|
||||
]
|
||||
"engines": {
|
||||
"node": ">=20.10.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
export default {
|
||||
$schema: 'https://json.schemastore.org/prettierrc',
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
bracketSpacing: true,
|
||||
arrowParens: 'always',
|
||||
endOfLine: 'lf',
|
||||
proseWrap: 'preserve',
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.md'],
|
||||
options: { proseWrap: 'preserve' },
|
||||
},
|
||||
{
|
||||
files: ['*.yml', '*.yaml'],
|
||||
options: { singleQuote: false },
|
||||
},
|
||||
{
|
||||
files: ['*.json', '*.jsonc'],
|
||||
options: { singleQuote: false },
|
||||
},
|
||||
{
|
||||
files: ['*.cjs'],
|
||||
options: { parser: 'babel' },
|
||||
},
|
||||
],
|
||||
plugins: ['prettier-plugin-packagejson'],
|
||||
};
|
||||
|
|
@ -5,16 +5,16 @@
|
|||
* This file ensures proper execution when run via npx from GitHub
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
|
||||
// Check if we're running in an npx temporary directory
|
||||
const isNpxExecution = __dirname.includes('_npx') || __dirname.includes('.npm');
|
||||
|
||||
// If running via npx, we need to handle things differently
|
||||
if (isNpxExecution) {
|
||||
const args = process.argv.slice(2);
|
||||
const arguments_ = process.argv.slice(2);
|
||||
|
||||
// Use the installer for all commands
|
||||
const bmadScriptPath = path.join(__dirname, 'installer', 'bin', 'bmad.js');
|
||||
|
|
@ -26,9 +26,9 @@ if (isNpxExecution) {
|
|||
}
|
||||
|
||||
try {
|
||||
execSync(`node "${bmadScriptPath}" ${args.join(' ')}`, {
|
||||
execSync(`node "${bmadScriptPath}" ${arguments_.join(' ')}`, {
|
||||
stdio: 'inherit',
|
||||
cwd: path.dirname(__dirname)
|
||||
cwd: path.dirname(__dirname),
|
||||
});
|
||||
} catch (error) {
|
||||
process.exit(error.status || 1);
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
const fs = require("node:fs").promises;
|
||||
const path = require("node:path");
|
||||
const DependencyResolver = require("../lib/dependency-resolver");
|
||||
const yamlUtils = require("../lib/yaml-utils");
|
||||
const fs = require('node:fs').promises;
|
||||
const path = require('node:path');
|
||||
const DependencyResolver = require('../lib/dependency-resolver');
|
||||
const yamlUtilities = require('../lib/yaml-utils');
|
||||
|
||||
class WebBuilder {
|
||||
constructor(options = {}) {
|
||||
this.rootDir = options.rootDir || process.cwd();
|
||||
this.outputDirs = options.outputDirs || [path.join(this.rootDir, "dist")];
|
||||
this.outputDirs = options.outputDirs || [path.join(this.rootDir, 'dist')];
|
||||
this.resolver = new DependencyResolver(this.rootDir);
|
||||
this.templatePath = path.join(
|
||||
this.rootDir,
|
||||
"tools",
|
||||
"md-assets",
|
||||
"web-agent-startup-instructions.md"
|
||||
'tools',
|
||||
'md-assets',
|
||||
'web-agent-startup-instructions.md',
|
||||
);
|
||||
}
|
||||
|
||||
parseYaml(content) {
|
||||
const yaml = require("js-yaml");
|
||||
const yaml = require('js-yaml');
|
||||
return yaml.load(content);
|
||||
}
|
||||
|
||||
|
|
@ -42,11 +42,21 @@ class WebBuilder {
|
|||
generateWebInstructions(bundleType, packName = null) {
|
||||
// Generate dynamic web instructions based on bundle type
|
||||
const rootExample = packName ? `.${packName}` : '.bmad-core';
|
||||
const examplePath = packName ? `.${packName}/folder/filename.md` : '.bmad-core/folder/filename.md';
|
||||
const personasExample = packName ? `.${packName}/personas/analyst.md` : '.bmad-core/personas/analyst.md';
|
||||
const tasksExample = packName ? `.${packName}/tasks/create-story.md` : '.bmad-core/tasks/create-story.md';
|
||||
const utilsExample = packName ? `.${packName}/utils/template-format.md` : '.bmad-core/utils/template-format.md';
|
||||
const tasksRef = packName ? `.${packName}/tasks/create-story.md` : '.bmad-core/tasks/create-story.md';
|
||||
const examplePath = packName
|
||||
? `.${packName}/folder/filename.md`
|
||||
: '.bmad-core/folder/filename.md';
|
||||
const personasExample = packName
|
||||
? `.${packName}/personas/analyst.md`
|
||||
: '.bmad-core/personas/analyst.md';
|
||||
const tasksExample = packName
|
||||
? `.${packName}/tasks/create-story.md`
|
||||
: '.bmad-core/tasks/create-story.md';
|
||||
const utilitiesExample = packName
|
||||
? `.${packName}/utils/template-format.md`
|
||||
: '.bmad-core/utils/template-format.md';
|
||||
const tasksReference = packName
|
||||
? `.${packName}/tasks/create-story.md`
|
||||
: '.bmad-core/tasks/create-story.md';
|
||||
|
||||
return `# Web Agent Bundle Instructions
|
||||
|
||||
|
|
@ -79,8 +89,8 @@ dependencies:
|
|||
|
||||
These references map directly to bundle sections:
|
||||
|
||||
- \`utils: template-format\` → Look for \`==================== START: ${utilsExample} ====================\`
|
||||
- \`tasks: create-story\` → Look for \`==================== START: ${tasksRef} ====================\`
|
||||
- \`utils: template-format\` → Look for \`==================== START: ${utilitiesExample} ====================\`
|
||||
- \`tasks: create-story\` → Look for \`==================== START: ${tasksReference} ====================\`
|
||||
|
||||
3. **Execution Context**: You are operating in a web environment. All your capabilities and knowledge are contained within this bundle. Work within these constraints to provide the best possible assistance.
|
||||
|
||||
|
|
@ -112,10 +122,10 @@ These references map directly to bundle sections:
|
|||
|
||||
// Write to all output directories
|
||||
for (const outputDir of this.outputDirs) {
|
||||
const outputPath = path.join(outputDir, "agents");
|
||||
const outputPath = path.join(outputDir, 'agents');
|
||||
await fs.mkdir(outputPath, { recursive: true });
|
||||
const outputFile = path.join(outputPath, `${agentId}.txt`);
|
||||
await fs.writeFile(outputFile, bundle, "utf8");
|
||||
await fs.writeFile(outputFile, bundle, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,10 +141,10 @@ These references map directly to bundle sections:
|
|||
|
||||
// Write to all output directories
|
||||
for (const outputDir of this.outputDirs) {
|
||||
const outputPath = path.join(outputDir, "teams");
|
||||
const outputPath = path.join(outputDir, 'teams');
|
||||
await fs.mkdir(outputPath, { recursive: true });
|
||||
const outputFile = path.join(outputPath, `${teamId}.txt`);
|
||||
await fs.writeFile(outputFile, bundle, "utf8");
|
||||
await fs.writeFile(outputFile, bundle, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +167,7 @@ These references map directly to bundle sections:
|
|||
sections.push(this.formatSection(resourcePath, resource.content, 'bmad-core'));
|
||||
}
|
||||
|
||||
return sections.join("\n");
|
||||
return sections.join('\n');
|
||||
}
|
||||
|
||||
async buildTeamBundle(teamId) {
|
||||
|
|
@ -182,12 +192,12 @@ These references map directly to bundle sections:
|
|||
sections.push(this.formatSection(resourcePath, resource.content, 'bmad-core'));
|
||||
}
|
||||
|
||||
return sections.join("\n");
|
||||
return sections.join('\n');
|
||||
}
|
||||
|
||||
processAgentContent(content) {
|
||||
// First, replace content before YAML with the template
|
||||
const yamlContent = yamlUtils.extractYamlFromAgent(content);
|
||||
const yamlContent = yamlUtilities.extractYamlFromAgent(content);
|
||||
if (!yamlContent) return content;
|
||||
|
||||
const yamlMatch = content.match(/```ya?ml\n([\s\S]*?)\n```/);
|
||||
|
|
@ -198,24 +208,24 @@ These references map directly to bundle sections:
|
|||
|
||||
// Parse YAML and remove root and IDE-FILE-RESOLUTION properties
|
||||
try {
|
||||
const yaml = require("js-yaml");
|
||||
const yaml = require('js-yaml');
|
||||
const parsed = yaml.load(yamlContent);
|
||||
|
||||
// Remove the properties if they exist at root level
|
||||
delete parsed.root;
|
||||
delete parsed["IDE-FILE-RESOLUTION"];
|
||||
delete parsed["REQUEST-RESOLUTION"];
|
||||
delete parsed['IDE-FILE-RESOLUTION'];
|
||||
delete parsed['REQUEST-RESOLUTION'];
|
||||
|
||||
// Also remove from activation-instructions if they exist
|
||||
if (parsed["activation-instructions"] && Array.isArray(parsed["activation-instructions"])) {
|
||||
parsed["activation-instructions"] = parsed["activation-instructions"].filter(
|
||||
if (parsed['activation-instructions'] && Array.isArray(parsed['activation-instructions'])) {
|
||||
parsed['activation-instructions'] = parsed['activation-instructions'].filter(
|
||||
(instruction) => {
|
||||
return (
|
||||
typeof instruction === 'string' &&
|
||||
!instruction.startsWith("IDE-FILE-RESOLUTION:") &&
|
||||
!instruction.startsWith("REQUEST-RESOLUTION:")
|
||||
!instruction.startsWith('IDE-FILE-RESOLUTION:') &&
|
||||
!instruction.startsWith('REQUEST-RESOLUTION:')
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -223,25 +233,25 @@ These references map directly to bundle sections:
|
|||
const cleanedYaml = yaml.dump(parsed, { lineWidth: -1 });
|
||||
|
||||
// Get the agent name from the YAML for the header
|
||||
const agentName = parsed.agent?.id || "agent";
|
||||
const agentName = parsed.agent?.id || 'agent';
|
||||
|
||||
// Build the new content with just the agent header and YAML
|
||||
const newHeader = `# ${agentName}\n\nCRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n`;
|
||||
const afterYaml = content.substring(yamlEndIndex);
|
||||
const afterYaml = content.slice(Math.max(0, yamlEndIndex));
|
||||
|
||||
return newHeader + "```yaml\n" + cleanedYaml.trim() + "\n```" + afterYaml;
|
||||
return newHeader + '```yaml\n' + cleanedYaml.trim() + '\n```' + afterYaml;
|
||||
} catch (error) {
|
||||
console.warn("Failed to process agent YAML:", error.message);
|
||||
console.warn('Failed to process agent YAML:', error.message);
|
||||
// If parsing fails, return original content
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
formatSection(path, content, bundleRoot = 'bmad-core') {
|
||||
const separator = "====================";
|
||||
const separator = '====================';
|
||||
|
||||
// Process agent content if this is an agent file
|
||||
if (path.includes("/agents/")) {
|
||||
if (path.includes('/agents/')) {
|
||||
content = this.processAgentContent(content);
|
||||
}
|
||||
|
||||
|
|
@ -252,17 +262,17 @@ These references map directly to bundle sections:
|
|||
`${separator} START: ${path} ${separator}`,
|
||||
content.trim(),
|
||||
`${separator} END: ${path} ${separator}`,
|
||||
"",
|
||||
].join("\n");
|
||||
'',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
replaceRootReferences(content, bundleRoot) {
|
||||
// Replace {root} with the appropriate bundle root path
|
||||
return content.replace(/\{root\}/g, `.${bundleRoot}`);
|
||||
return content.replaceAll('{root}', `.${bundleRoot}`);
|
||||
}
|
||||
|
||||
async validate() {
|
||||
console.log("Validating agent configurations...");
|
||||
console.log('Validating agent configurations...');
|
||||
const agents = await this.resolver.listAgents();
|
||||
for (const agentId of agents) {
|
||||
try {
|
||||
|
|
@ -274,7 +284,7 @@ These references map directly to bundle sections:
|
|||
}
|
||||
}
|
||||
|
||||
console.log("\nValidating team configurations...");
|
||||
console.log('\nValidating team configurations...');
|
||||
const teams = await this.resolver.listTeams();
|
||||
for (const teamId of teams) {
|
||||
try {
|
||||
|
|
@ -299,54 +309,54 @@ These references map directly to bundle sections:
|
|||
}
|
||||
|
||||
async buildExpansionPack(packName, options = {}) {
|
||||
const packDir = path.join(this.rootDir, "expansion-packs", packName);
|
||||
const outputDirs = [path.join(this.rootDir, "dist", "expansion-packs", packName)];
|
||||
const packDir = path.join(this.rootDir, 'expansion-packs', packName);
|
||||
const outputDirectories = [path.join(this.rootDir, 'dist', 'expansion-packs', packName)];
|
||||
|
||||
// Clean output directories if requested
|
||||
if (options.clean !== false) {
|
||||
for (const outputDir of outputDirs) {
|
||||
for (const outputDir of outputDirectories) {
|
||||
try {
|
||||
await fs.rm(outputDir, { recursive: true, force: true });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Directory might not exist, that's fine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build individual agents first
|
||||
const agentsDir = path.join(packDir, "agents");
|
||||
const agentsDir = path.join(packDir, 'agents');
|
||||
try {
|
||||
const agentFiles = await fs.readdir(agentsDir);
|
||||
const agentMarkdownFiles = agentFiles.filter((f) => f.endsWith(".md"));
|
||||
const agentMarkdownFiles = agentFiles.filter((f) => f.endsWith('.md'));
|
||||
|
||||
if (agentMarkdownFiles.length > 0) {
|
||||
console.log(` Building individual agents for ${packName}:`);
|
||||
|
||||
for (const agentFile of agentMarkdownFiles) {
|
||||
const agentName = agentFile.replace(".md", "");
|
||||
const agentName = agentFile.replace('.md', '');
|
||||
console.log(` - ${agentName}`);
|
||||
|
||||
// Build individual agent bundle
|
||||
const bundle = await this.buildExpansionAgentBundle(packName, packDir, agentName);
|
||||
|
||||
// Write to all output directories
|
||||
for (const outputDir of outputDirs) {
|
||||
const agentsOutputDir = path.join(outputDir, "agents");
|
||||
for (const outputDir of outputDirectories) {
|
||||
const agentsOutputDir = path.join(outputDir, 'agents');
|
||||
await fs.mkdir(agentsOutputDir, { recursive: true });
|
||||
const outputFile = path.join(agentsOutputDir, `${agentName}.txt`);
|
||||
await fs.writeFile(outputFile, bundle, "utf8");
|
||||
await fs.writeFile(outputFile, bundle, 'utf8');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
console.debug(` No agents directory found for ${packName}`);
|
||||
}
|
||||
|
||||
// Build team bundle
|
||||
const agentTeamsDir = path.join(packDir, "agent-teams");
|
||||
const agentTeamsDir = path.join(packDir, 'agent-teams');
|
||||
try {
|
||||
const teamFiles = await fs.readdir(agentTeamsDir);
|
||||
const teamFile = teamFiles.find((f) => f.endsWith(".yaml"));
|
||||
const teamFile = teamFiles.find((f) => f.endsWith('.yaml'));
|
||||
|
||||
if (teamFile) {
|
||||
console.log(` Building team bundle for ${packName}`);
|
||||
|
|
@ -356,17 +366,17 @@ These references map directly to bundle sections:
|
|||
const bundle = await this.buildExpansionTeamBundle(packName, packDir, teamConfigPath);
|
||||
|
||||
// Write to all output directories
|
||||
for (const outputDir of outputDirs) {
|
||||
const teamsOutputDir = path.join(outputDir, "teams");
|
||||
for (const outputDir of outputDirectories) {
|
||||
const teamsOutputDir = path.join(outputDir, 'teams');
|
||||
await fs.mkdir(teamsOutputDir, { recursive: true });
|
||||
const outputFile = path.join(teamsOutputDir, teamFile.replace(".yaml", ".txt"));
|
||||
await fs.writeFile(outputFile, bundle, "utf8");
|
||||
const outputFile = path.join(teamsOutputDir, teamFile.replace('.yaml', '.txt'));
|
||||
await fs.writeFile(outputFile, bundle, 'utf8');
|
||||
console.log(` ✓ Created bundle: ${path.relative(this.rootDir, outputFile)}`);
|
||||
}
|
||||
} else {
|
||||
console.warn(` ⚠ No team configuration found in ${packName}/agent-teams/`);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
console.warn(` ⚠ No agent-teams directory found for ${packName}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -376,16 +386,16 @@ These references map directly to bundle sections:
|
|||
const sections = [template];
|
||||
|
||||
// Add agent configuration
|
||||
const agentPath = path.join(packDir, "agents", `${agentName}.md`);
|
||||
const agentContent = await fs.readFile(agentPath, "utf8");
|
||||
const agentPath = path.join(packDir, 'agents', `${agentName}.md`);
|
||||
const agentContent = await fs.readFile(agentPath, 'utf8');
|
||||
const agentWebPath = this.convertToWebPath(agentPath, packName);
|
||||
sections.push(this.formatSection(agentWebPath, agentContent, packName));
|
||||
|
||||
// Resolve and add agent dependencies
|
||||
const yamlContent = yamlUtils.extractYamlFromAgent(agentContent);
|
||||
const yamlContent = yamlUtilities.extractYamlFromAgent(agentContent);
|
||||
if (yamlContent) {
|
||||
try {
|
||||
const yaml = require("js-yaml");
|
||||
const yaml = require('js-yaml');
|
||||
const agentConfig = yaml.load(yamlContent);
|
||||
|
||||
if (agentConfig.dependencies) {
|
||||
|
|
@ -398,59 +408,43 @@ These references map directly to bundle sections:
|
|||
// Try expansion pack first
|
||||
const resourcePath = path.join(packDir, resourceType, resourceName);
|
||||
try {
|
||||
const resourceContent = await fs.readFile(resourcePath, "utf8");
|
||||
const resourceContent = await fs.readFile(resourcePath, 'utf8');
|
||||
const resourceWebPath = this.convertToWebPath(resourcePath, packName);
|
||||
sections.push(
|
||||
this.formatSection(resourceWebPath, resourceContent, packName)
|
||||
);
|
||||
sections.push(this.formatSection(resourceWebPath, resourceContent, packName));
|
||||
found = true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Not in expansion pack, continue
|
||||
}
|
||||
|
||||
// If not found in expansion pack, try core
|
||||
if (!found) {
|
||||
const corePath = path.join(
|
||||
this.rootDir,
|
||||
"bmad-core",
|
||||
resourceType,
|
||||
resourceName
|
||||
);
|
||||
const corePath = path.join(this.rootDir, 'bmad-core', resourceType, resourceName);
|
||||
try {
|
||||
const coreContent = await fs.readFile(corePath, "utf8");
|
||||
const coreContent = await fs.readFile(corePath, 'utf8');
|
||||
const coreWebPath = this.convertToWebPath(corePath, packName);
|
||||
sections.push(
|
||||
this.formatSection(coreWebPath, coreContent, packName)
|
||||
);
|
||||
sections.push(this.formatSection(coreWebPath, coreContent, packName));
|
||||
found = true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Not in core either, continue
|
||||
}
|
||||
}
|
||||
|
||||
// If not found in core, try common folder
|
||||
if (!found) {
|
||||
const commonPath = path.join(
|
||||
this.rootDir,
|
||||
"common",
|
||||
resourceType,
|
||||
resourceName
|
||||
);
|
||||
const commonPath = path.join(this.rootDir, 'common', resourceType, resourceName);
|
||||
try {
|
||||
const commonContent = await fs.readFile(commonPath, "utf8");
|
||||
const commonContent = await fs.readFile(commonPath, 'utf8');
|
||||
const commonWebPath = this.convertToWebPath(commonPath, packName);
|
||||
sections.push(
|
||||
this.formatSection(commonWebPath, commonContent, packName)
|
||||
);
|
||||
sections.push(this.formatSection(commonWebPath, commonContent, packName));
|
||||
found = true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Not in common either, continue
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
console.warn(
|
||||
` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`
|
||||
` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -462,7 +456,7 @@ These references map directly to bundle sections:
|
|||
}
|
||||
}
|
||||
|
||||
return sections.join("\n");
|
||||
return sections.join('\n');
|
||||
}
|
||||
|
||||
async buildExpansionTeamBundle(packName, packDir, teamConfigPath) {
|
||||
|
|
@ -471,38 +465,38 @@ These references map directly to bundle sections:
|
|||
const sections = [template];
|
||||
|
||||
// Add team configuration and parse to get agent list
|
||||
const teamContent = await fs.readFile(teamConfigPath, "utf8");
|
||||
const teamFileName = path.basename(teamConfigPath, ".yaml");
|
||||
const teamContent = await fs.readFile(teamConfigPath, 'utf8');
|
||||
const teamFileName = path.basename(teamConfigPath, '.yaml');
|
||||
const teamConfig = this.parseYaml(teamContent);
|
||||
const teamWebPath = this.convertToWebPath(teamConfigPath, packName);
|
||||
sections.push(this.formatSection(teamWebPath, teamContent, packName));
|
||||
|
||||
// Get list of expansion pack agents
|
||||
const expansionAgents = new Set();
|
||||
const agentsDir = path.join(packDir, "agents");
|
||||
const agentsDir = path.join(packDir, 'agents');
|
||||
try {
|
||||
const agentFiles = await fs.readdir(agentsDir);
|
||||
for (const agentFile of agentFiles.filter((f) => f.endsWith(".md"))) {
|
||||
const agentName = agentFile.replace(".md", "");
|
||||
for (const agentFile of agentFiles.filter((f) => f.endsWith('.md'))) {
|
||||
const agentName = agentFile.replace('.md', '');
|
||||
expansionAgents.add(agentName);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
console.warn(` ⚠ No agents directory found in ${packName}`);
|
||||
}
|
||||
|
||||
// Build a map of all available expansion pack resources for override checking
|
||||
const expansionResources = new Map();
|
||||
const resourceDirs = ["templates", "tasks", "checklists", "workflows", "data"];
|
||||
for (const resourceDir of resourceDirs) {
|
||||
const resourceDirectories = ['templates', 'tasks', 'checklists', 'workflows', 'data'];
|
||||
for (const resourceDir of resourceDirectories) {
|
||||
const resourcePath = path.join(packDir, resourceDir);
|
||||
try {
|
||||
const resourceFiles = await fs.readdir(resourcePath);
|
||||
for (const resourceFile of resourceFiles.filter(
|
||||
(f) => f.endsWith(".md") || f.endsWith(".yaml")
|
||||
(f) => f.endsWith('.md') || f.endsWith('.yaml'),
|
||||
)) {
|
||||
expansionResources.set(`${resourceDir}#${resourceFile}`, true);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Directory might not exist, that's fine
|
||||
}
|
||||
}
|
||||
|
|
@ -511,9 +505,9 @@ These references map directly to bundle sections:
|
|||
const agentsToProcess = teamConfig.agents || [];
|
||||
|
||||
// Ensure bmad-orchestrator is always included for teams
|
||||
if (!agentsToProcess.includes("bmad-orchestrator")) {
|
||||
if (!agentsToProcess.includes('bmad-orchestrator')) {
|
||||
console.warn(` ⚠ Team ${teamFileName} missing bmad-orchestrator, adding automatically`);
|
||||
agentsToProcess.unshift("bmad-orchestrator");
|
||||
agentsToProcess.unshift('bmad-orchestrator');
|
||||
}
|
||||
|
||||
// Track all dependencies from all agents (deduplicated)
|
||||
|
|
@ -523,7 +517,7 @@ These references map directly to bundle sections:
|
|||
if (expansionAgents.has(agentId)) {
|
||||
// Use expansion pack version (override)
|
||||
const agentPath = path.join(agentsDir, `${agentId}.md`);
|
||||
const agentContent = await fs.readFile(agentPath, "utf8");
|
||||
const agentContent = await fs.readFile(agentPath, 'utf8');
|
||||
const expansionAgentWebPath = this.convertToWebPath(agentPath, packName);
|
||||
sections.push(this.formatSection(expansionAgentWebPath, agentContent, packName));
|
||||
|
||||
|
|
@ -551,13 +545,13 @@ These references map directly to bundle sections:
|
|||
} else {
|
||||
// Use core BMad version
|
||||
try {
|
||||
const coreAgentPath = path.join(this.rootDir, "bmad-core", "agents", `${agentId}.md`);
|
||||
const coreAgentContent = await fs.readFile(coreAgentPath, "utf8");
|
||||
const coreAgentPath = path.join(this.rootDir, 'bmad-core', 'agents', `${agentId}.md`);
|
||||
const coreAgentContent = await fs.readFile(coreAgentPath, 'utf8');
|
||||
const coreAgentWebPath = this.convertToWebPath(coreAgentPath, packName);
|
||||
sections.push(this.formatSection(coreAgentWebPath, coreAgentContent, packName));
|
||||
|
||||
// Parse and collect dependencies from core agent
|
||||
const yamlContent = yamlUtils.extractYamlFromAgent(coreAgentContent, true);
|
||||
const yamlContent = yamlUtilities.extractYamlFromAgent(coreAgentContent, true);
|
||||
if (yamlContent) {
|
||||
try {
|
||||
const agentConfig = this.parseYaml(yamlContent);
|
||||
|
|
@ -577,7 +571,7 @@ These references map directly to bundle sections:
|
|||
console.debug(`Failed to parse agent YAML for ${agentId}:`, error.message);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
console.warn(` ⚠ Agent ${agentId} not found in core or expansion pack`);
|
||||
}
|
||||
}
|
||||
|
|
@ -593,38 +587,38 @@ These references map directly to bundle sections:
|
|||
// We know it exists in expansion pack, find and load it
|
||||
const expansionPath = path.join(packDir, dep.type, dep.name);
|
||||
try {
|
||||
const content = await fs.readFile(expansionPath, "utf8");
|
||||
const content = await fs.readFile(expansionPath, 'utf8');
|
||||
const expansionWebPath = this.convertToWebPath(expansionPath, packName);
|
||||
sections.push(this.formatSection(expansionWebPath, content, packName));
|
||||
console.log(` ✓ Using expansion override for ${key}`);
|
||||
found = true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Try next extension
|
||||
}
|
||||
}
|
||||
|
||||
// If not found in expansion pack (or doesn't exist there), try core
|
||||
if (!found) {
|
||||
const corePath = path.join(this.rootDir, "bmad-core", dep.type, dep.name);
|
||||
const corePath = path.join(this.rootDir, 'bmad-core', dep.type, dep.name);
|
||||
try {
|
||||
const content = await fs.readFile(corePath, "utf8");
|
||||
const content = await fs.readFile(corePath, 'utf8');
|
||||
const coreWebPath = this.convertToWebPath(corePath, packName);
|
||||
sections.push(this.formatSection(coreWebPath, content, packName));
|
||||
found = true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Not in core either, continue
|
||||
}
|
||||
}
|
||||
|
||||
// If not found in core, try common folder
|
||||
if (!found) {
|
||||
const commonPath = path.join(this.rootDir, "common", dep.type, dep.name);
|
||||
const commonPath = path.join(this.rootDir, 'common', dep.type, dep.name);
|
||||
try {
|
||||
const content = await fs.readFile(commonPath, "utf8");
|
||||
const content = await fs.readFile(commonPath, 'utf8');
|
||||
const commonWebPath = this.convertToWebPath(commonPath, packName);
|
||||
sections.push(this.formatSection(commonWebPath, content, packName));
|
||||
found = true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Not in common either, continue
|
||||
}
|
||||
}
|
||||
|
|
@ -635,16 +629,16 @@ These references map directly to bundle sections:
|
|||
}
|
||||
|
||||
// Add remaining expansion pack resources not already included as dependencies
|
||||
for (const resourceDir of resourceDirs) {
|
||||
for (const resourceDir of resourceDirectories) {
|
||||
const resourcePath = path.join(packDir, resourceDir);
|
||||
try {
|
||||
const resourceFiles = await fs.readdir(resourcePath);
|
||||
for (const resourceFile of resourceFiles.filter(
|
||||
(f) => f.endsWith(".md") || f.endsWith(".yaml")
|
||||
(f) => f.endsWith('.md') || f.endsWith('.yaml'),
|
||||
)) {
|
||||
const filePath = path.join(resourcePath, resourceFile);
|
||||
const fileContent = await fs.readFile(filePath, "utf8");
|
||||
const fileName = resourceFile.replace(/\.(md|yaml)$/, "");
|
||||
const fileContent = await fs.readFile(filePath, 'utf8');
|
||||
const fileName = resourceFile.replace(/\.(md|yaml)$/, '');
|
||||
|
||||
// Only add if not already included as a dependency
|
||||
const resourceKey = `${resourceDir}#${fileName}`;
|
||||
|
|
@ -654,21 +648,21 @@ These references map directly to bundle sections:
|
|||
sections.push(this.formatSection(resourceWebPath, fileContent, packName));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Directory might not exist, that's fine
|
||||
}
|
||||
}
|
||||
|
||||
return sections.join("\n");
|
||||
return sections.join('\n');
|
||||
}
|
||||
|
||||
async listExpansionPacks() {
|
||||
const expansionPacksDir = path.join(this.rootDir, "expansion-packs");
|
||||
const expansionPacksDir = path.join(this.rootDir, 'expansion-packs');
|
||||
try {
|
||||
const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true });
|
||||
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
||||
} catch (error) {
|
||||
console.warn("No expansion-packs directory found");
|
||||
} catch {
|
||||
console.warn('No expansion-packs directory found');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const bumpType = args[0] || 'minor'; // default to minor
|
||||
const arguments_ = process.argv.slice(2);
|
||||
const bumpType = arguments_[0] || 'minor'; // default to minor
|
||||
|
||||
if (!['major', 'minor', 'patch'].includes(bumpType)) {
|
||||
console.log('Usage: node bump-all-versions.js [major|minor|patch]');
|
||||
|
|
@ -17,16 +15,20 @@ function bumpVersion(currentVersion, type) {
|
|||
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||
|
||||
switch (type) {
|
||||
case 'major':
|
||||
case 'major': {
|
||||
return `${major + 1}.0.0`;
|
||||
case 'minor':
|
||||
}
|
||||
case 'minor': {
|
||||
return `${major}.${minor + 1}.0`;
|
||||
case 'patch':
|
||||
}
|
||||
case 'patch': {
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
return currentVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function bumpAllVersions() {
|
||||
const updatedItems = [];
|
||||
|
|
@ -43,7 +45,12 @@ async function bumpAllVersions() {
|
|||
|
||||
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
updatedItems.push({ type: 'core', name: 'BMad Core', oldVersion: oldCoreVersion, newVersion: newCoreVersion });
|
||||
updatedItems.push({
|
||||
type: 'core',
|
||||
name: 'BMad Core',
|
||||
oldVersion: oldCoreVersion,
|
||||
newVersion: newCoreVersion,
|
||||
});
|
||||
console.log(`✓ BMad Core (package.json): ${oldCoreVersion} → ${newCoreVersion}`);
|
||||
} catch (error) {
|
||||
console.error(`✗ Failed to update BMad Core: ${error.message}`);
|
||||
|
|
@ -74,7 +81,6 @@ async function bumpAllVersions() {
|
|||
|
||||
updatedItems.push({ type: 'expansion', name: packId, oldVersion, newVersion });
|
||||
console.log(`✓ ${packId}: ${oldVersion} → ${newVersion}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`✗ Failed to update ${packId}: ${error.message}`);
|
||||
}
|
||||
|
|
@ -83,20 +89,23 @@ async function bumpAllVersions() {
|
|||
}
|
||||
|
||||
if (updatedItems.length > 0) {
|
||||
const coreCount = updatedItems.filter(i => i.type === 'core').length;
|
||||
const expansionCount = updatedItems.filter(i => i.type === 'expansion').length;
|
||||
const coreCount = updatedItems.filter((index) => index.type === 'core').length;
|
||||
const expansionCount = updatedItems.filter((index) => index.type === 'expansion').length;
|
||||
|
||||
console.log(`\n✓ Successfully bumped ${updatedItems.length} item(s) with ${bumpType} version bump`);
|
||||
console.log(
|
||||
`\n✓ Successfully bumped ${updatedItems.length} item(s) with ${bumpType} version bump`,
|
||||
);
|
||||
if (coreCount > 0) console.log(` - ${coreCount} core`);
|
||||
if (expansionCount > 0) console.log(` - ${expansionCount} expansion pack(s)`);
|
||||
|
||||
console.log('\nNext steps:');
|
||||
console.log('1. Test the changes');
|
||||
console.log('2. Commit: git add -A && git commit -m "chore: bump all versions (' + bumpType + ')"');
|
||||
console.log(
|
||||
'2. Commit: git add -A && git commit -m "chore: bump all versions (' + bumpType + ')"',
|
||||
);
|
||||
} else {
|
||||
console.log('No items found to update');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error reading expansion packs directory:', error.message);
|
||||
process.exit(1);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Load required modules
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
// Parse CLI arguments
|
||||
const args = process.argv.slice(2);
|
||||
const packId = args[0];
|
||||
const bumpType = args[1] || 'minor';
|
||||
const arguments_ = process.argv.slice(2);
|
||||
const packId = arguments_[0];
|
||||
const bumpType = arguments_[1] || 'minor';
|
||||
|
||||
// Validate arguments
|
||||
if (!packId || args.length > 2) {
|
||||
if (!packId || arguments_.length > 2) {
|
||||
console.log('Usage: node bump-expansion-version.js <expansion-pack-id> [major|minor|patch]');
|
||||
console.log('Default: minor');
|
||||
console.log('Example: node bump-expansion-version.js bmad-creator-tools patch');
|
||||
|
|
@ -28,10 +26,18 @@ function bumpVersion(currentVersion, type) {
|
|||
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||
|
||||
switch (type) {
|
||||
case 'major': return `${major + 1}.0.0`;
|
||||
case 'minor': return `${major}.${minor + 1}.0`;
|
||||
case 'patch': return `${major}.${minor}.${patch + 1}`;
|
||||
default: return currentVersion;
|
||||
case 'major': {
|
||||
return `${major + 1}.0.0`;
|
||||
}
|
||||
case 'minor': {
|
||||
return `${major}.${minor + 1}.0`;
|
||||
}
|
||||
case 'patch': {
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
default: {
|
||||
return currentVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,11 +53,11 @@ async function updateVersion() {
|
|||
const packsDir = path.join(__dirname, '..', 'expansion-packs');
|
||||
const entries = fs.readdirSync(packsDir, { withFileTypes: true });
|
||||
|
||||
entries.forEach(entry => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
||||
console.log(` - ${entry.name}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
@ -72,8 +78,9 @@ async function updateVersion() {
|
|||
console.log(`\n✓ Successfully bumped ${packId} with ${bumpType} version bump`);
|
||||
console.log('\nNext steps:');
|
||||
console.log(`1. Test the changes`);
|
||||
console.log(`2. Commit: git add -A && git commit -m "chore: bump ${packId} version (${bumpType})"`);
|
||||
|
||||
console.log(
|
||||
`2. Commit: git add -A && git commit -m "chore: bump ${packId} version (${bumpType})"`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error updating version:', error.message);
|
||||
process.exit(1);
|
||||
|
|
|
|||
14
tools/cli.js
14
tools/cli.js
|
|
@ -1,10 +1,8 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { Command } = require('commander');
|
||||
const WebBuilder = require('./builders/web-builder');
|
||||
const V3ToV4Upgrader = require('./upgraders/v3-to-v4-upgrader');
|
||||
const IdeSetup = require('./installer/lib/ide-setup');
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
|
||||
const program = new Command();
|
||||
|
||||
|
|
@ -23,7 +21,7 @@ program
|
|||
.option('--no-clean', 'Skip cleaning output directories')
|
||||
.action(async (options) => {
|
||||
const builder = new WebBuilder({
|
||||
rootDir: process.cwd()
|
||||
rootDir: process.cwd(),
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
@ -66,7 +64,7 @@ program
|
|||
.option('--no-clean', 'Skip cleaning output directories')
|
||||
.action(async (options) => {
|
||||
const builder = new WebBuilder({
|
||||
rootDir: process.cwd()
|
||||
rootDir: process.cwd(),
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
@ -92,7 +90,7 @@ program
|
|||
const builder = new WebBuilder({ rootDir: process.cwd() });
|
||||
const agents = await builder.resolver.listAgents();
|
||||
console.log('Available agents:');
|
||||
agents.forEach(agent => console.log(` - ${agent}`));
|
||||
for (const agent of agents) console.log(` - ${agent}`);
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
|
@ -103,7 +101,7 @@ program
|
|||
const builder = new WebBuilder({ rootDir: process.cwd() });
|
||||
const expansions = await builder.listExpansionPacks();
|
||||
console.log('Available expansion packs:');
|
||||
expansions.forEach(expansion => console.log(` - ${expansion}`));
|
||||
for (const expansion of expansions) console.log(` - ${expansion}`);
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
|
@ -147,7 +145,7 @@ program
|
|||
await upgrader.upgrade({
|
||||
projectPath: options.project,
|
||||
dryRun: options.dryRun,
|
||||
backup: options.backup
|
||||
backup: options.backup,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const fs = require("fs-extra");
|
||||
const path = require("node:path");
|
||||
const os = require("node:os");
|
||||
const { isBinaryFile } = require("./binary.js");
|
||||
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.
|
||||
|
|
@ -22,7 +22,7 @@ async function aggregateFileContents(files, rootDir, spinner = null) {
|
|||
// 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));
|
||||
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)));
|
||||
|
|
@ -37,16 +37,16 @@ async function aggregateFileContents(files, rootDir, spinner = null) {
|
|||
|
||||
const binary = await isBinaryFile(filePath);
|
||||
if (binary) {
|
||||
const size = (await fs.stat(filePath)).size;
|
||||
const { size } = await fs.stat(filePath);
|
||||
results.binaryFiles.push({ path: relativePath, absolutePath: filePath, size });
|
||||
} else {
|
||||
const content = await fs.readFile(filePath, "utf8");
|
||||
const content = await fs.readFile(filePath, 'utf8');
|
||||
results.textFiles.push({
|
||||
path: relativePath,
|
||||
absolutePath: filePath,
|
||||
content,
|
||||
size: content.length,
|
||||
lines: content.split("\n").length,
|
||||
lines: content.split('\n').length,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -63,8 +63,8 @@ async function aggregateFileContents(files, rootDir, spinner = null) {
|
|||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < files.length; i += concurrency) {
|
||||
const slice = files.slice(i, i + concurrency);
|
||||
for (let index = 0; index < files.length; index += concurrency) {
|
||||
const slice = files.slice(index, index + concurrency);
|
||||
await Promise.all(slice.map(processOne));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
const fsp = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
const { Buffer } = require("node:buffer");
|
||||
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.
|
||||
|
|
@ -13,25 +13,54 @@ async function isBinaryFile(filePath) {
|
|||
try {
|
||||
const stats = await fsp.stat(filePath);
|
||||
if (stats.isDirectory()) {
|
||||
throw new Error("EISDIR: illegal operation on a directory");
|
||||
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",
|
||||
'.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 ext = path.extname(filePath).toLowerCase();
|
||||
if (binaryExtensions.has(ext)) return true;
|
||||
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");
|
||||
const fd = await fsp.open(filePath, 'r');
|
||||
try {
|
||||
const buffer = Buffer.allocUnsafe(sampleSize);
|
||||
const { bytesRead } = await fd.read(buffer, 0, sampleSize, 0);
|
||||
|
|
@ -41,9 +70,7 @@ async function isBinaryFile(filePath) {
|
|||
await fd.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Warning: Could not determine if file is binary: ${filePath} - ${error.message}`,
|
||||
);
|
||||
console.warn(`Warning: Could not determine if file is binary: ${filePath} - ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
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 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";
|
||||
const { stdout } = await pExecFile('git', ['rev-parse', '--is-inside-work-tree'], {
|
||||
cwd: rootDir,
|
||||
});
|
||||
return (
|
||||
String(stdout || '')
|
||||
.toString()
|
||||
.trim() === 'true'
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -20,12 +23,10 @@ async function isGitRepo(rootDir) {
|
|||
|
||||
async function gitListFiles(rootDir) {
|
||||
try {
|
||||
const { stdout } = await pExecFile("git", [
|
||||
"ls-files",
|
||||
"-co",
|
||||
"--exclude-standard",
|
||||
], { cwd: rootDir });
|
||||
return String(stdout || "")
|
||||
const { stdout } = await pExecFile('git', ['ls-files', '-co', '--exclude-standard'], {
|
||||
cwd: rootDir,
|
||||
});
|
||||
return String(stdout || '')
|
||||
.split(/\r?\n/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
|
|
@ -48,14 +49,14 @@ async function discoverFiles(rootDir, options = {}) {
|
|||
const { filter } = await loadIgnore(rootDir);
|
||||
|
||||
// Try git first
|
||||
if (preferGit && await isGitRepo(rootDir)) {
|
||||
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("**/*", {
|
||||
const globbed = await glob('**/*', {
|
||||
cwd: rootDir,
|
||||
nodir: true,
|
||||
dot: true,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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");
|
||||
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) {
|
||||
|
|
@ -14,7 +14,7 @@ async function discoverFiles(rootDir) {
|
|||
// 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);
|
||||
console.error('Error discovering files:', error.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,147 +1,147 @@
|
|||
const fs = require("fs-extra");
|
||||
const path = require("node:path");
|
||||
const ignore = require("ignore");
|
||||
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-core/**",
|
||||
"**/.git/**",
|
||||
"**/.svn/**",
|
||||
"**/.hg/**",
|
||||
"**/.bzr/**",
|
||||
'**/.bmad-core/**',
|
||||
'**/.git/**',
|
||||
'**/.svn/**',
|
||||
'**/.hg/**',
|
||||
'**/.bzr/**',
|
||||
// Package/build outputs
|
||||
"**/node_modules/**",
|
||||
"**/bower_components/**",
|
||||
"**/vendor/**",
|
||||
"**/packages/**",
|
||||
"**/build/**",
|
||||
"**/dist/**",
|
||||
"**/out/**",
|
||||
"**/target/**",
|
||||
"**/bin/**",
|
||||
"**/obj/**",
|
||||
"**/release/**",
|
||||
"**/debug/**",
|
||||
'**/node_modules/**',
|
||||
'**/bower_components/**',
|
||||
'**/vendor/**',
|
||||
'**/packages/**',
|
||||
'**/build/**',
|
||||
'**/dist/**',
|
||||
'**/out/**',
|
||||
'**/target/**',
|
||||
'**/bin/**',
|
||||
'**/obj/**',
|
||||
'**/release/**',
|
||||
'**/debug/**',
|
||||
// Environments
|
||||
"**/.venv/**",
|
||||
"**/venv/**",
|
||||
"**/.virtualenv/**",
|
||||
"**/virtualenv/**",
|
||||
"**/env/**",
|
||||
'**/.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/**",
|
||||
'**/*.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/**",
|
||||
'**/.cache/**',
|
||||
'**/.tmp/**',
|
||||
'**/.temp/**',
|
||||
'**/tmp/**',
|
||||
'**/temp/**',
|
||||
'**/.sass-cache/**',
|
||||
// IDE/editor
|
||||
"**/.vscode/**",
|
||||
"**/.idea/**",
|
||||
"**/*.swp",
|
||||
"**/*.swo",
|
||||
"**/*~",
|
||||
"**/.project",
|
||||
"**/.classpath",
|
||||
"**/.settings/**",
|
||||
"**/*.sublime-project",
|
||||
"**/*.sublime-workspace",
|
||||
'**/.vscode/**',
|
||||
'**/.idea/**',
|
||||
'**/*.swp',
|
||||
'**/*.swo',
|
||||
'**/*~',
|
||||
'**/.project',
|
||||
'**/.classpath',
|
||||
'**/.settings/**',
|
||||
'**/*.sublime-project',
|
||||
'**/*.sublime-workspace',
|
||||
// Lockfiles
|
||||
"**/package-lock.json",
|
||||
"**/yarn.lock",
|
||||
"**/pnpm-lock.yaml",
|
||||
"**/composer.lock",
|
||||
"**/Pipfile.lock",
|
||||
'**/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",
|
||||
'**/*.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",
|
||||
'**/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",
|
||||
'**/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",
|
||||
'**/*.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",
|
||||
'**/.env',
|
||||
'**/.env.*',
|
||||
'**/*.env',
|
||||
// Misc
|
||||
"**/junit.xml",
|
||||
'**/junit.xml',
|
||||
];
|
||||
|
||||
async function readIgnoreFile(filePath) {
|
||||
try {
|
||||
if (!await fs.pathExists(filePath)) return [];
|
||||
const content = await fs.readFile(filePath, "utf8");
|
||||
if (!(await fs.pathExists(filePath))) return [];
|
||||
const content = await fs.readFile(filePath, 'utf8');
|
||||
return content
|
||||
.split("\n")
|
||||
.split('\n')
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l && !l.startsWith("#"));
|
||||
} catch (err) {
|
||||
.filter((l) => l && !l.startsWith('#'));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -153,18 +153,18 @@ async function parseGitignore(gitignorePath) {
|
|||
|
||||
async function loadIgnore(rootDir, extraPatterns = []) {
|
||||
const ig = ignore();
|
||||
const gitignorePath = path.join(rootDir, ".gitignore");
|
||||
const gitignorePath = path.join(rootDir, '.gitignore');
|
||||
const patterns = [
|
||||
...await readIgnoreFile(gitignorePath),
|
||||
...(await readIgnoreFile(gitignorePath)),
|
||||
...DEFAULT_PATTERNS,
|
||||
...extraPatterns,
|
||||
];
|
||||
// De-duplicate
|
||||
const unique = Array.from(new Set(patterns.map((p) => String(p))));
|
||||
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.replace(/\\/g, "/"));
|
||||
const filter = (relativePath) => !ig.ignores(relativePath.replaceAll('\\', '/'));
|
||||
|
||||
return { ig, filter, patterns: unique };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,14 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { Command } = require("commander");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("node:path");
|
||||
const process = require("node:process");
|
||||
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");
|
||||
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
|
||||
|
|
@ -73,30 +67,30 @@ const { calculateStatistics } = require("./stats.js");
|
|||
const program = new Command();
|
||||
|
||||
program
|
||||
.name("bmad-flatten")
|
||||
.description("BMad-Method codebase flattener tool")
|
||||
.version("1.0.0")
|
||||
.option("-i, --input <path>", "Input directory to flatten", process.cwd())
|
||||
.option("-o, --output <path>", "Output file path", "flattened-codebase.xml")
|
||||
.name('bmad-flatten')
|
||||
.description('BMad-Method codebase flattener tool')
|
||||
.version('1.0.0')
|
||||
.option('-i, --input <path>', 'Input directory to flatten', process.cwd())
|
||||
.option('-o, --output <path>', '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 userSpecifiedInput = argv.some(
|
||||
(a) => a === '-i' || a === '--input' || a.startsWith('--input='),
|
||||
);
|
||||
const userSpecifiedOutput = argv.some((a) =>
|
||||
a === "-o" || a === "--output" || a.startsWith("--output=")
|
||||
const userSpecifiedOutput = argv.some(
|
||||
(a) => a === '-o' || a === '--output' || a.startsWith('--output='),
|
||||
);
|
||||
const noPathArgs = !userSpecifiedInput && !userSpecifiedOutput;
|
||||
const noPathArguments = !userSpecifiedInput && !userSpecifiedOutput;
|
||||
|
||||
if (noPathArgs) {
|
||||
if (noPathArguments) {
|
||||
const detectedRoot = await findProjectRoot(process.cwd());
|
||||
const suggestedOutput = detectedRoot
|
||||
? path.join(detectedRoot, "flattened-codebase.xml")
|
||||
: path.resolve("flattened-codebase.xml");
|
||||
? path.join(detectedRoot, 'flattened-codebase.xml')
|
||||
: path.resolve('flattened-codebase.xml');
|
||||
|
||||
if (detectedRoot) {
|
||||
const useDefaults = await promptYesNo(
|
||||
|
|
@ -107,29 +101,23 @@ program
|
|||
inputDir = detectedRoot;
|
||||
outputPath = suggestedOutput;
|
||||
} else {
|
||||
inputDir = await promptPath(
|
||||
"Enter input directory path",
|
||||
process.cwd(),
|
||||
);
|
||||
inputDir = await promptPath('Enter input directory path', process.cwd());
|
||||
outputPath = await promptPath(
|
||||
"Enter output file path",
|
||||
path.join(inputDir, "flattened-codebase.xml"),
|
||||
'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(),
|
||||
);
|
||||
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"),
|
||||
'Enter output file path',
|
||||
path.join(inputDir, 'flattened-codebase.xml'),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
"Could not auto-detect a project root and no arguments were provided. Please specify -i/--input and -o/--output.",
|
||||
'Could not auto-detect a project root and no arguments were provided. Please specify -i/--input and -o/--output.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
@ -142,25 +130,23 @@ program
|
|||
|
||||
try {
|
||||
// Verify input directory exists
|
||||
if (!await fs.pathExists(inputDir)) {
|
||||
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");
|
||||
const { default: ora } = await import('ora');
|
||||
|
||||
// Start file discovery with spinner
|
||||
const discoverySpinner = ora("🔍 Discovering files...").start();
|
||||
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`,
|
||||
);
|
||||
discoverySpinner.succeed(`📁 Found ${filteredFiles.length} files to include`);
|
||||
|
||||
// Process files with progress tracking
|
||||
console.log("Reading file contents");
|
||||
const processingSpinner = ora("📄 Processing files...").start();
|
||||
console.log('Reading file contents');
|
||||
const processingSpinner = ora('📄 Processing files...').start();
|
||||
const aggregatedContent = await aggregateFileContents(
|
||||
filteredFiles,
|
||||
inputDir,
|
||||
|
|
@ -178,34 +164,30 @@ program
|
|||
}
|
||||
|
||||
// Generate XML output using streaming
|
||||
const xmlSpinner = ora("🔧 Generating XML output...").start();
|
||||
const xmlSpinner = ora('🔧 Generating XML output...').start();
|
||||
await generateXMLOutput(aggregatedContent, outputPath);
|
||||
xmlSpinner.succeed("📝 XML generation completed");
|
||||
xmlSpinner.succeed('📝 XML generation completed');
|
||||
|
||||
// Calculate and display statistics
|
||||
const outputStats = await fs.stat(outputPath);
|
||||
const stats = calculateStatistics(aggregatedContent, outputStats.size);
|
||||
|
||||
// Display completion summary
|
||||
console.log("\n📊 Completion Summary:");
|
||||
console.log('\n📊 Completion Summary:');
|
||||
console.log(
|
||||
`✅ Successfully processed ${filteredFiles.length} files into ${
|
||||
path.basename(outputPath)
|
||||
}`,
|
||||
`✅ 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(`📝 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`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("❌ Critical error:", error.message);
|
||||
console.error("An unexpected error occurred.");
|
||||
console.error('❌ Critical error:', error.message);
|
||||
console.error('An unexpected error occurred.');
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const fs = require("fs-extra");
|
||||
const path = require("node:path");
|
||||
const fs = require('fs-extra');
|
||||
const path = require('node:path');
|
||||
|
||||
/**
|
||||
* Attempt to find the project root by walking up from startDir
|
||||
|
|
@ -12,24 +12,22 @@ async function findProjectRoot(startDir) {
|
|||
let dir = path.resolve(startDir);
|
||||
const root = path.parse(dir).root;
|
||||
const markers = [
|
||||
".git",
|
||||
"package.json",
|
||||
"pnpm-workspace.yaml",
|
||||
"yarn.lock",
|
||||
"pnpm-lock.yaml",
|
||||
"pyproject.toml",
|
||||
"requirements.txt",
|
||||
"go.mod",
|
||||
"Cargo.toml",
|
||||
"composer.json",
|
||||
".hg",
|
||||
".svn",
|
||||
'.git',
|
||||
'package.json',
|
||||
'pnpm-workspace.yaml',
|
||||
'yarn.lock',
|
||||
'pnpm-lock.yaml',
|
||||
'pyproject.toml',
|
||||
'requirements.txt',
|
||||
'go.mod',
|
||||
'Cargo.toml',
|
||||
'composer.json',
|
||||
'.hg',
|
||||
'.svn',
|
||||
];
|
||||
|
||||
while (true) {
|
||||
const exists = await Promise.all(
|
||||
markers.map((m) => fs.pathExists(path.join(dir, m))),
|
||||
);
|
||||
const exists = await Promise.all(markers.map((m) => fs.pathExists(path.join(dir, m))));
|
||||
if (exists.some(Boolean)) {
|
||||
return dir;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
const os = require("node:os");
|
||||
const path = require("node:path");
|
||||
const readline = require("node:readline");
|
||||
const process = require("node:process");
|
||||
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));
|
||||
if (p.startsWith('~')) return path.join(os.homedir(), p.slice(1));
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
@ -27,16 +27,16 @@ function promptQuestion(question) {
|
|||
}
|
||||
|
||||
async function promptYesNo(question, defaultYes = true) {
|
||||
const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
|
||||
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;
|
||||
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 prompt = `${question}${defaultValue ? ` (default: ${defaultValue})` : ''}: `;
|
||||
const ans = (await promptQuestion(prompt)).trim();
|
||||
return expandHome(ans || defaultValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,44 @@
|
|||
const fs = require("fs-extra");
|
||||
const fs = require('fs-extra');
|
||||
|
||||
function escapeXml(str) {
|
||||
if (typeof str !== "string") {
|
||||
return String(str);
|
||||
function escapeXml(string_) {
|
||||
if (typeof string_ !== 'string') {
|
||||
return String(string_);
|
||||
}
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/'/g, "'");
|
||||
return string_.replaceAll('&', '&').replaceAll('<', '<').replaceAll("'", ''');
|
||||
}
|
||||
|
||||
function indentFileContent(content) {
|
||||
if (typeof content !== "string") {
|
||||
if (typeof content !== 'string') {
|
||||
return String(content);
|
||||
}
|
||||
return content.split("\n").map((line) => ` ${line}`);
|
||||
return content.split('\n').map((line) => ` ${line}`);
|
||||
}
|
||||
|
||||
function generateXMLOutput(aggregatedContent, outputPath) {
|
||||
const { textFiles } = aggregatedContent;
|
||||
const writeStream = fs.createWriteStream(outputPath, { encoding: "utf8" });
|
||||
const writeStream = fs.createWriteStream(outputPath, { encoding: 'utf8' });
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
writeStream.on("error", reject);
|
||||
writeStream.on("finish", resolve);
|
||||
writeStream.on('error', reject);
|
||||
writeStream.on('finish', resolve);
|
||||
|
||||
writeStream.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
||||
writeStream.write("<files>\n");
|
||||
writeStream.write('<files>\n');
|
||||
|
||||
// Sort files by path for deterministic order
|
||||
const filesSorted = [...textFiles].sort((a, b) =>
|
||||
a.path.localeCompare(b.path)
|
||||
);
|
||||
const filesSorted = [...textFiles].sort((a, b) => a.path.localeCompare(b.path));
|
||||
let index = 0;
|
||||
|
||||
const writeNext = () => {
|
||||
if (index >= filesSorted.length) {
|
||||
writeStream.write("</files>\n");
|
||||
writeStream.write('</files>\n');
|
||||
writeStream.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const file = filesSorted[index++];
|
||||
const p = escapeXml(file.path);
|
||||
const content = typeof file.content === "string" ? file.content : "";
|
||||
const content = typeof file.content === 'string' ? file.content : '';
|
||||
|
||||
if (content.length === 0) {
|
||||
writeStream.write(`\t<file path='${p}'/>\n`);
|
||||
|
|
@ -51,27 +46,34 @@ function generateXMLOutput(aggregatedContent, outputPath) {
|
|||
return;
|
||||
}
|
||||
|
||||
const needsCdata = content.includes("<") || content.includes("&") ||
|
||||
content.includes("]]>");
|
||||
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<file path='${p}'><![CDATA[\n`);
|
||||
// Safely split any occurrences of "]]>" inside content, trim trailing newlines, indent each line with two tabs
|
||||
const safe = content.replace(/]]>/g, "]]]]><![CDATA[>");
|
||||
const trimmed = safe.replace(/[\r\n]+$/, "");
|
||||
const indented = trimmed.length > 0
|
||||
? trimmed.split("\n").map((line) => `\t\t${line}`).join("\n")
|
||||
: "";
|
||||
const safe = content.replaceAll(']]>', ']]]]><![CDATA[>');
|
||||
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("]]></file>\n");
|
||||
writeStream.write(']]></file>\n');
|
||||
} else {
|
||||
// Write opening tag then newline; indent content with two tabs; attach closing tag directly after last content char
|
||||
writeStream.write(`\t<file path='${p}'>\n`);
|
||||
const trimmed = content.replace(/[\r\n]+$/, "");
|
||||
const indented = trimmed.length > 0
|
||||
? trimmed.split("\n").map((line) => `\t\t${line}`).join("\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(`</file>\n`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { program } = require('commander');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs').promises;
|
||||
const yaml = require('js-yaml');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
|
|
@ -14,18 +14,20 @@ try {
|
|||
// Try installer context first (when run from tools/installer/)
|
||||
version = require('../package.json').version;
|
||||
installer = require('../lib/installer');
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
// Fall back to root context (when run via npx from GitHub)
|
||||
console.log(`Installer context not found (${e.message}), trying root context...`);
|
||||
console.log(`Installer context not found (${error.message}), trying root context...`);
|
||||
try {
|
||||
version = require('../../../package.json').version;
|
||||
installer = require('../../../tools/installer/lib/installer');
|
||||
} catch (e2) {
|
||||
console.error('Error: Could not load required modules. Please ensure you are running from the correct directory.');
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Error: Could not load required modules. Please ensure you are running from the correct directory.',
|
||||
);
|
||||
console.error('Debug info:', {
|
||||
__dirname,
|
||||
cwd: process.cwd(),
|
||||
error: e2.message
|
||||
error: error.message,
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
@ -41,8 +43,14 @@ program
|
|||
.option('-f, --full', 'Install complete BMad Method')
|
||||
.option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)')
|
||||
.option('-d, --directory <path>', 'Installation directory')
|
||||
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, other)')
|
||||
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
|
||||
.option(
|
||||
'-i, --ide <ide...>',
|
||||
'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, other)',
|
||||
)
|
||||
.option(
|
||||
'-e, --expansion-packs <packs...>',
|
||||
'Install specific expansion packs (can specify multiple)',
|
||||
)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
if (!options.full && !options.expansionOnly) {
|
||||
|
|
@ -60,8 +68,8 @@ program
|
|||
const config = {
|
||||
installType,
|
||||
directory: options.directory || '.',
|
||||
ides: (options.ide || []).filter(ide => ide !== 'other'),
|
||||
expansionPacks: options.expansionPacks || []
|
||||
ides: (options.ide || []).filter((ide) => ide !== 'other'),
|
||||
expansionPacks: options.expansionPacks || [],
|
||||
};
|
||||
await installer.install(config);
|
||||
process.exit(0);
|
||||
|
|
@ -125,16 +133,17 @@ program
|
|||
});
|
||||
|
||||
async function promptInstallation() {
|
||||
|
||||
// Display ASCII logo
|
||||
console.log(chalk.bold.cyan(`
|
||||
console.log(
|
||||
chalk.bold.cyan(`
|
||||
██████╗ ███╗ ███╗ █████╗ ██████╗ ███╗ ███╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗
|
||||
██╔══██╗████╗ ████║██╔══██╗██╔══██╗ ████╗ ████║██╔════╝╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗
|
||||
██████╔╝██╔████╔██║███████║██║ ██║█████╗██╔████╔██║█████╗ ██║ ███████║██║ ██║██║ ██║
|
||||
██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║╚════╝██║╚██╔╝██║██╔══╝ ██║ ██╔══██║██║ ██║██║ ██║
|
||||
██████╔╝██║ ╚═╝ ██║██║ ██║██████╔╝ ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚██████╔╝██████╔╝
|
||||
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝
|
||||
`));
|
||||
`),
|
||||
);
|
||||
|
||||
console.log(chalk.bold.magenta('🚀 Universal AI Agent Framework for Any Domain'));
|
||||
console.log(chalk.bold.blue(`✨ Installer v${version}\n`));
|
||||
|
|
@ -152,8 +161,8 @@ async function promptInstallation() {
|
|||
return 'Please enter a valid project path';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
answers.directory = directory;
|
||||
|
||||
|
|
@ -180,7 +189,8 @@ async function promptInstallation() {
|
|||
if (state.type === 'v4_existing') {
|
||||
const currentVersion = state.manifest?.version || 'unknown';
|
||||
const newVersion = version; // Always use package.json version
|
||||
const versionInfo = currentVersion === newVersion
|
||||
const versionInfo =
|
||||
currentVersion === newVersion
|
||||
? `(v${currentVersion} - reinstall)`
|
||||
: `(v${currentVersion} → v${newVersion})`;
|
||||
bmadOptionText = `Update ${coreShortTitle} ${versionInfo} .bmad-core`;
|
||||
|
|
@ -191,7 +201,7 @@ async function promptInstallation() {
|
|||
choices.push({
|
||||
name: bmadOptionText,
|
||||
value: 'bmad-core',
|
||||
checked: true
|
||||
checked: true,
|
||||
});
|
||||
|
||||
// Add expansion pack options
|
||||
|
|
@ -202,7 +212,8 @@ async function promptInstallation() {
|
|||
if (existing) {
|
||||
const currentVersion = existing.manifest?.version || 'unknown';
|
||||
const newVersion = pack.version;
|
||||
const versionInfo = currentVersion === newVersion
|
||||
const versionInfo =
|
||||
currentVersion === newVersion
|
||||
? `(v${currentVersion} - reinstall)`
|
||||
: `(v${currentVersion} → v${newVersion})`;
|
||||
packOptionText = `Update ${pack.shortTitle} ${versionInfo} .${pack.id}`;
|
||||
|
|
@ -213,7 +224,7 @@ async function promptInstallation() {
|
|||
choices.push({
|
||||
name: packOptionText,
|
||||
value: pack.id,
|
||||
checked: false
|
||||
checked: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -229,13 +240,13 @@ async function promptInstallation() {
|
|||
return 'Please select at least one item to install';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Process selections
|
||||
answers.installType = selectedItems.includes('bmad-core') ? 'full' : 'expansion-only';
|
||||
answers.expansionPacks = selectedItems.filter(item => item !== 'bmad-core');
|
||||
answers.expansionPacks = selectedItems.filter((item) => item !== 'bmad-core');
|
||||
|
||||
// Ask sharding questions if installing BMad core
|
||||
if (selectedItems.includes('bmad-core')) {
|
||||
|
|
@ -248,8 +259,8 @@ async function promptInstallation() {
|
|||
type: 'confirm',
|
||||
name: 'prdSharded',
|
||||
message: 'Will the PRD (Product Requirements Document) be sharded into multiple files?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.prdSharded = prdSharded;
|
||||
|
||||
|
|
@ -259,18 +270,30 @@ async function promptInstallation() {
|
|||
type: 'confirm',
|
||||
name: 'architectureSharded',
|
||||
message: 'Will the architecture documentation be sharded into multiple files?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.architectureSharded = architectureSharded;
|
||||
|
||||
// Show warning if architecture sharding is disabled
|
||||
if (!architectureSharded) {
|
||||
console.log(chalk.yellow.bold('\n⚠️ IMPORTANT: Architecture Sharding Disabled'));
|
||||
console.log(chalk.yellow('With architecture sharding disabled, you should still create the files listed'));
|
||||
console.log(chalk.yellow('in devLoadAlwaysFiles (like coding-standards.md, tech-stack.md, source-tree.md)'));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'With architecture sharding disabled, you should still create the files listed',
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'in devLoadAlwaysFiles (like coding-standards.md, tech-stack.md, source-tree.md)',
|
||||
),
|
||||
);
|
||||
console.log(chalk.yellow('as these are used by the dev agent at runtime.'));
|
||||
console.log(chalk.yellow('\nAlternatively, you can remove these files from the devLoadAlwaysFiles list'));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nAlternatively, you can remove these files from the devLoadAlwaysFiles list',
|
||||
),
|
||||
);
|
||||
console.log(chalk.yellow('in your core-config.yaml after installation.'));
|
||||
|
||||
const { acknowledge } = await inquirer.prompt([
|
||||
|
|
@ -278,8 +301,8 @@ async function promptInstallation() {
|
|||
type: 'confirm',
|
||||
name: 'acknowledge',
|
||||
message: 'Do you acknowledge this requirement and want to proceed?',
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!acknowledge) {
|
||||
|
|
@ -295,7 +318,11 @@ async function promptInstallation() {
|
|||
|
||||
while (!ideSelectionComplete) {
|
||||
console.log(chalk.cyan('\n🛠️ IDE Configuration'));
|
||||
console.log(chalk.bold.yellow.bgRed(' ⚠️ IMPORTANT: This is a MULTISELECT! Use SPACEBAR to toggle each IDE! '));
|
||||
console.log(
|
||||
chalk.bold.yellow.bgRed(
|
||||
' ⚠️ IMPORTANT: This is a MULTISELECT! Use SPACEBAR to toggle each IDE! ',
|
||||
),
|
||||
);
|
||||
console.log(chalk.bold.magenta('🔸 Use arrow keys to navigate'));
|
||||
console.log(chalk.bold.magenta('🔸 Use SPACEBAR to select/deselect IDEs'));
|
||||
console.log(chalk.bold.magenta('🔸 Press ENTER when finished selecting\n'));
|
||||
|
|
@ -304,7 +331,8 @@ async function promptInstallation() {
|
|||
{
|
||||
type: 'checkbox',
|
||||
name: 'ides',
|
||||
message: 'Which IDE(s) do you want to configure? (Select with SPACEBAR, confirm with ENTER):',
|
||||
message:
|
||||
'Which IDE(s) do you want to configure? (Select with SPACEBAR, confirm with ENTER):',
|
||||
choices: [
|
||||
{ name: 'Cursor', value: 'cursor' },
|
||||
{ name: 'Claude Code', value: 'claude-code' },
|
||||
|
|
@ -315,9 +343,9 @@ async function promptInstallation() {
|
|||
{ name: 'Cline', value: 'cline' },
|
||||
{ name: 'Gemini CLI', value: 'gemini' },
|
||||
{ name: 'Qwen Code', value: 'qwen-code' },
|
||||
{ name: 'Github Copilot', value: 'github-copilot' }
|
||||
]
|
||||
}
|
||||
{ name: 'Github Copilot', value: 'github-copilot' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
ides = ideResponse.ides;
|
||||
|
|
@ -328,13 +356,19 @@ async function promptInstallation() {
|
|||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmNoIde',
|
||||
message: chalk.red('⚠️ You have NOT selected any IDEs. This means NO IDE integration will be set up. Is this correct?'),
|
||||
default: false
|
||||
}
|
||||
message: chalk.red(
|
||||
'⚠️ You have NOT selected any IDEs. This means NO IDE integration will be set up. Is this correct?',
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmNoIde) {
|
||||
console.log(chalk.bold.red('\n🔄 Returning to IDE selection. Remember to use SPACEBAR to select IDEs!\n'));
|
||||
console.log(
|
||||
chalk.bold.red(
|
||||
'\n🔄 Returning to IDE selection. Remember to use SPACEBAR to select IDEs!\n',
|
||||
),
|
||||
);
|
||||
continue; // Go back to IDE selection only
|
||||
}
|
||||
}
|
||||
|
|
@ -348,7 +382,9 @@ async function promptInstallation() {
|
|||
// Configure GitHub Copilot immediately if selected
|
||||
if (ides.includes('github-copilot')) {
|
||||
console.log(chalk.cyan('\n🔧 GitHub Copilot Configuration'));
|
||||
console.log(chalk.dim('BMad works best with specific VS Code settings for optimal agent experience.\n'));
|
||||
console.log(
|
||||
chalk.dim('BMad works best with specific VS Code settings for optimal agent experience.\n'),
|
||||
);
|
||||
|
||||
const { configChoice } = await inquirer.prompt([
|
||||
{
|
||||
|
|
@ -358,19 +394,19 @@ async function promptInstallation() {
|
|||
choices: [
|
||||
{
|
||||
name: 'Use recommended defaults (fastest setup)',
|
||||
value: 'defaults'
|
||||
value: 'defaults',
|
||||
},
|
||||
{
|
||||
name: 'Configure each setting manually (customize to your preferences)',
|
||||
value: 'manual'
|
||||
value: 'manual',
|
||||
},
|
||||
{
|
||||
name: 'Skip settings configuration (I\'ll configure manually later)',
|
||||
value: 'skip'
|
||||
}
|
||||
name: "Skip settings configuration (I'll configure manually later)",
|
||||
value: 'skip',
|
||||
},
|
||||
],
|
||||
default: 'defaults'
|
||||
}
|
||||
default: 'defaults',
|
||||
},
|
||||
]);
|
||||
|
||||
answers.githubCopilotConfig = { configChoice };
|
||||
|
|
@ -381,14 +417,17 @@ async function promptInstallation() {
|
|||
{
|
||||
type: 'confirm',
|
||||
name: 'includeWebBundles',
|
||||
message: 'Would you like to include pre-built web bundles? (standalone files for ChatGPT, Claude, Gemini)',
|
||||
default: false
|
||||
}
|
||||
message:
|
||||
'Would you like to include pre-built web bundles? (standalone files for ChatGPT, Claude, Gemini)',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (includeWebBundles) {
|
||||
console.log(chalk.cyan('\n📦 Web bundles are standalone files perfect for web AI platforms.'));
|
||||
console.log(chalk.dim(' You can choose different teams/agents than your IDE installation.\n'));
|
||||
console.log(
|
||||
chalk.dim(' You can choose different teams/agents than your IDE installation.\n'),
|
||||
);
|
||||
|
||||
const { webBundleType } = await inquirer.prompt([
|
||||
{
|
||||
|
|
@ -398,22 +437,22 @@ async function promptInstallation() {
|
|||
choices: [
|
||||
{
|
||||
name: 'All available bundles (agents, teams, expansion packs)',
|
||||
value: 'all'
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
name: 'Specific teams only',
|
||||
value: 'teams'
|
||||
value: 'teams',
|
||||
},
|
||||
{
|
||||
name: 'Individual agents only',
|
||||
value: 'agents'
|
||||
value: 'agents',
|
||||
},
|
||||
{
|
||||
name: 'Custom selection',
|
||||
value: 'custom'
|
||||
}
|
||||
]
|
||||
}
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
answers.webBundleType = webBundleType;
|
||||
|
|
@ -426,18 +465,18 @@ async function promptInstallation() {
|
|||
type: 'checkbox',
|
||||
name: 'selectedTeams',
|
||||
message: 'Select team bundles to include:',
|
||||
choices: teams.map(t => ({
|
||||
choices: teams.map((t) => ({
|
||||
name: `${t.icon || '📋'} ${t.name}: ${t.description}`,
|
||||
value: t.id,
|
||||
checked: webBundleType === 'teams' // Check all if teams-only mode
|
||||
checked: webBundleType === 'teams', // Check all if teams-only mode
|
||||
})),
|
||||
validate: (answer) => {
|
||||
if (answer.length < 1) {
|
||||
if (answer.length === 0) {
|
||||
return 'You must select at least one team.';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
answers.selectedWebBundleTeams = selectedTeams;
|
||||
}
|
||||
|
|
@ -449,8 +488,8 @@ async function promptInstallation() {
|
|||
type: 'confirm',
|
||||
name: 'includeIndividualAgents',
|
||||
message: 'Also include individual agent bundles?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.includeIndividualAgents = includeIndividualAgents;
|
||||
}
|
||||
|
|
@ -466,8 +505,8 @@ async function promptInstallation() {
|
|||
return 'Please enter a valid directory path';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
answers.webBundlesDirectory = webBundlesDirectory;
|
||||
}
|
||||
|
|
@ -480,6 +519,6 @@ async function promptInstallation() {
|
|||
program.parse(process.argv);
|
||||
|
||||
// Show help if no command provided
|
||||
if (!process.argv.slice(2).length) {
|
||||
if (process.argv.slice(2).length === 0) {
|
||||
program.outputHelp();
|
||||
}
|
||||
|
|
@ -30,12 +30,12 @@ ide-configurations:
|
|||
# 2. Claude will switch to that agent's persona
|
||||
windsurf:
|
||||
name: Windsurf
|
||||
rule-dir: .windsurf/rules/
|
||||
rule-dir: .windsurf/workflows/
|
||||
format: multi-file
|
||||
command-suffix: .md
|
||||
instructions: |
|
||||
# To use BMad agents in Windsurf:
|
||||
# 1. Type @agent-name (e.g., "@dev", "@pm")
|
||||
# 1. Type /agent-name (e.g., "/dev", "/pm")
|
||||
# 2. Windsurf will adopt that agent's persona
|
||||
trae:
|
||||
name: Trae
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
const yaml = require('js-yaml');
|
||||
const { extractYamlFromAgent } = require('../../lib/yaml-utils');
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ class ConfigLoader {
|
|||
id: agentId,
|
||||
name: agentConfig.title || agentConfig.name || agentId,
|
||||
file: `bmad-core/agents/${entry.name}`,
|
||||
description: agentConfig.whenToUse || 'No description available'
|
||||
description: agentConfig.whenToUse || 'No description available',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -90,21 +90,25 @@ class ConfigLoader {
|
|||
expansionPacks.push({
|
||||
id: entry.name,
|
||||
name: config.name || entry.name,
|
||||
description: config['short-title'] || config.description || 'No description available',
|
||||
fullDescription: config.description || config['short-title'] || 'No description available',
|
||||
description:
|
||||
config['short-title'] || config.description || 'No description available',
|
||||
fullDescription:
|
||||
config.description || config['short-title'] || 'No description available',
|
||||
version: config.version || '1.0.0',
|
||||
author: config.author || 'BMad Team',
|
||||
packPath: packPath,
|
||||
dependencies: config.dependencies?.agents || []
|
||||
dependencies: config.dependencies?.agents || [],
|
||||
});
|
||||
} catch (error) {
|
||||
// Fallback if config.yaml doesn't exist or can't be read
|
||||
console.warn(`Failed to read config for expansion pack ${entry.name}: ${error.message}`);
|
||||
console.warn(
|
||||
`Failed to read config for expansion pack ${entry.name}: ${error.message}`,
|
||||
);
|
||||
|
||||
// Try to derive info from directory name as fallback
|
||||
const name = entry.name
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
|
||||
expansionPacks.push({
|
||||
|
|
@ -115,7 +119,7 @@ class ConfigLoader {
|
|||
version: '1.0.0',
|
||||
author: 'BMad Team',
|
||||
packPath: packPath,
|
||||
dependencies: []
|
||||
dependencies: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -193,7 +197,7 @@ class ConfigLoader {
|
|||
id: path.basename(entry.name, '.yaml'),
|
||||
name: teamConfig.bundle.name || entry.name,
|
||||
description: teamConfig.bundle.description || 'Team configuration',
|
||||
icon: teamConfig.bundle.icon || '📋'
|
||||
icon: teamConfig.bundle.icon || '📋',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const crypto = require("crypto");
|
||||
const yaml = require("js-yaml");
|
||||
const chalk = require("chalk");
|
||||
const { createReadStream, createWriteStream, promises: fsPromises } = require('fs');
|
||||
const { pipeline } = require('stream/promises');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('node:path');
|
||||
const crypto = require('node:crypto');
|
||||
const yaml = require('js-yaml');
|
||||
const chalk = require('chalk');
|
||||
const { createReadStream, createWriteStream, promises: fsPromises } = require('node:fs');
|
||||
const { pipeline } = require('node:stream/promises');
|
||||
const resourceLocator = require('./resource-locator');
|
||||
|
||||
class FileManager {
|
||||
constructor() {
|
||||
this.manifestDir = ".bmad-core";
|
||||
this.manifestFile = "install-manifest.yaml";
|
||||
}
|
||||
constructor() {}
|
||||
|
||||
async copyFile(source, destination) {
|
||||
try {
|
||||
|
|
@ -19,14 +16,9 @@ class FileManager {
|
|||
|
||||
// Use streaming for large files (> 10MB)
|
||||
const stats = await fs.stat(source);
|
||||
if (stats.size > 10 * 1024 * 1024) {
|
||||
await pipeline(
|
||||
createReadStream(source),
|
||||
createWriteStream(destination)
|
||||
);
|
||||
} else {
|
||||
await fs.copy(source, destination);
|
||||
}
|
||||
await (stats.size > 10 * 1024 * 1024
|
||||
? pipeline(createReadStream(source), createWriteStream(destination))
|
||||
: fs.copy(source, destination));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Failed to copy ${source}:`), error.message);
|
||||
|
|
@ -41,28 +33,20 @@ class FileManager {
|
|||
// Use streaming copy for large directories
|
||||
const files = await resourceLocator.findFiles('**/*', {
|
||||
cwd: source,
|
||||
nodir: true
|
||||
nodir: true,
|
||||
});
|
||||
|
||||
// Process files in batches to avoid memory issues
|
||||
const batchSize = 50;
|
||||
for (let i = 0; i < files.length; i += batchSize) {
|
||||
const batch = files.slice(i, i + batchSize);
|
||||
for (let index = 0; index < files.length; index += batchSize) {
|
||||
const batch = files.slice(index, index + batchSize);
|
||||
await Promise.all(
|
||||
batch.map(file =>
|
||||
this.copyFile(
|
||||
path.join(source, file),
|
||||
path.join(destination, file)
|
||||
)
|
||||
)
|
||||
batch.map((file) => this.copyFile(path.join(source, file), path.join(destination, file))),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
chalk.red(`Failed to copy directory ${source}:`),
|
||||
error.message
|
||||
);
|
||||
console.error(chalk.red(`Failed to copy directory ${source}:`), error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -73,17 +57,16 @@ class FileManager {
|
|||
|
||||
for (const file of files) {
|
||||
const sourcePath = path.join(sourceDir, file);
|
||||
const destPath = path.join(destDir, file);
|
||||
const destinationPath = path.join(destDir, file);
|
||||
|
||||
// Use root replacement if rootValue is provided and file needs it
|
||||
const needsRootReplacement = rootValue && (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'));
|
||||
const needsRootReplacement =
|
||||
rootValue && (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'));
|
||||
|
||||
let success = false;
|
||||
if (needsRootReplacement) {
|
||||
success = await this.copyFileWithRootReplacement(sourcePath, destPath, rootValue);
|
||||
} else {
|
||||
success = await this.copyFile(sourcePath, destPath);
|
||||
}
|
||||
success = await (needsRootReplacement
|
||||
? this.copyFileWithRootReplacement(sourcePath, destinationPath, rootValue)
|
||||
: this.copyFile(sourcePath, destinationPath));
|
||||
|
||||
if (success) {
|
||||
copied.push(file);
|
||||
|
|
@ -97,32 +80,28 @@ class FileManager {
|
|||
try {
|
||||
// Use streaming for hash calculation to reduce memory usage
|
||||
const stream = createReadStream(filePath);
|
||||
const hash = crypto.createHash("sha256");
|
||||
const hash = crypto.createHash('sha256');
|
||||
|
||||
for await (const chunk of stream) {
|
||||
hash.update(chunk);
|
||||
}
|
||||
|
||||
return hash.digest("hex").slice(0, 16);
|
||||
} catch (error) {
|
||||
return hash.digest('hex').slice(0, 16);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async createManifest(installDir, config, files) {
|
||||
const manifestPath = path.join(
|
||||
installDir,
|
||||
this.manifestDir,
|
||||
this.manifestFile
|
||||
);
|
||||
const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile);
|
||||
|
||||
// Read version from package.json
|
||||
let coreVersion = "unknown";
|
||||
let coreVersion = 'unknown';
|
||||
try {
|
||||
const packagePath = path.join(__dirname, '..', '..', '..', 'package.json');
|
||||
const packageJson = require(packagePath);
|
||||
coreVersion = packageJson.version;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
console.warn("Could not read version from package.json, using 'unknown'");
|
||||
}
|
||||
|
||||
|
|
@ -156,31 +135,23 @@ class FileManager {
|
|||
}
|
||||
|
||||
async readManifest(installDir) {
|
||||
const manifestPath = path.join(
|
||||
installDir,
|
||||
this.manifestDir,
|
||||
this.manifestFile
|
||||
);
|
||||
const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile);
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(manifestPath, "utf8");
|
||||
const content = await fs.readFile(manifestPath, 'utf8');
|
||||
return yaml.load(content);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async readExpansionPackManifest(installDir, packId) {
|
||||
const manifestPath = path.join(
|
||||
installDir,
|
||||
`.${packId}`,
|
||||
this.manifestFile
|
||||
);
|
||||
const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(manifestPath, "utf8");
|
||||
const content = await fs.readFile(manifestPath, 'utf8');
|
||||
return yaml.load(content);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +174,7 @@ class FileManager {
|
|||
async checkFileIntegrity(installDir, manifest) {
|
||||
const result = {
|
||||
missing: [],
|
||||
modified: []
|
||||
modified: [],
|
||||
};
|
||||
|
||||
for (const file of manifest.files) {
|
||||
|
|
@ -214,13 +185,13 @@ class FileManager {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!(await this.pathExists(filePath))) {
|
||||
result.missing.push(file.path);
|
||||
} else {
|
||||
if (await this.pathExists(filePath)) {
|
||||
const currentHash = await this.calculateFileHash(filePath);
|
||||
if (currentHash && currentHash !== file.hash) {
|
||||
result.modified.push(file.path);
|
||||
}
|
||||
} else {
|
||||
result.missing.push(file.path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +199,7 @@ class FileManager {
|
|||
}
|
||||
|
||||
async backupFile(filePath) {
|
||||
const backupPath = filePath + ".bak";
|
||||
const backupPath = filePath + '.bak';
|
||||
let counter = 1;
|
||||
let finalBackupPath = backupPath;
|
||||
|
||||
|
|
@ -256,7 +227,7 @@ class FileManager {
|
|||
}
|
||||
|
||||
async readFile(filePath) {
|
||||
return fs.readFile(filePath, "utf8");
|
||||
return fs.readFile(filePath, 'utf8');
|
||||
}
|
||||
|
||||
async writeFile(filePath, content) {
|
||||
|
|
@ -269,14 +240,10 @@ class FileManager {
|
|||
}
|
||||
|
||||
async createExpansionPackManifest(installDir, packId, config, files) {
|
||||
const manifestPath = path.join(
|
||||
installDir,
|
||||
`.${packId}`,
|
||||
this.manifestFile
|
||||
);
|
||||
const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
|
||||
|
||||
const manifest = {
|
||||
version: config.expansionPackVersion || require("../../../package.json").version,
|
||||
version: config.expansionPackVersion || require('../../../package.json').version,
|
||||
installed_at: new Date().toISOString(),
|
||||
install_type: config.installType,
|
||||
expansion_pack_id: config.expansionPackId,
|
||||
|
|
@ -336,26 +303,27 @@ class FileManager {
|
|||
// Check file size to determine if we should stream
|
||||
const stats = await fs.stat(source);
|
||||
|
||||
if (stats.size > 5 * 1024 * 1024) { // 5MB threshold
|
||||
if (stats.size > 5 * 1024 * 1024) {
|
||||
// 5MB threshold
|
||||
// Use streaming for large files
|
||||
const { Transform } = require('stream');
|
||||
const { Transform } = require('node:stream');
|
||||
const replaceStream = new Transform({
|
||||
transform(chunk, encoding, callback) {
|
||||
const modified = chunk.toString().replace(/\{root\}/g, rootValue);
|
||||
const modified = chunk.toString().replaceAll('{root}', rootValue);
|
||||
callback(null, modified);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await this.ensureDirectory(path.dirname(destination));
|
||||
await pipeline(
|
||||
createReadStream(source, { encoding: 'utf8' }),
|
||||
replaceStream,
|
||||
createWriteStream(destination, { encoding: 'utf8' })
|
||||
createWriteStream(destination, { encoding: 'utf8' }),
|
||||
);
|
||||
} else {
|
||||
// Regular approach for smaller files
|
||||
const content = await fsPromises.readFile(source, 'utf8');
|
||||
const updatedContent = content.replace(/\{root\}/g, rootValue);
|
||||
const updatedContent = content.replaceAll('{root}', rootValue);
|
||||
await this.ensureDirectory(path.dirname(destination));
|
||||
await fsPromises.writeFile(destination, updatedContent, 'utf8');
|
||||
}
|
||||
|
|
@ -367,32 +335,37 @@ class FileManager {
|
|||
}
|
||||
}
|
||||
|
||||
async copyDirectoryWithRootReplacement(source, destination, rootValue, fileExtensions = ['.md', '.yaml', '.yml']) {
|
||||
async copyDirectoryWithRootReplacement(
|
||||
source,
|
||||
destination,
|
||||
rootValue,
|
||||
fileExtensions = ['.md', '.yaml', '.yml'],
|
||||
) {
|
||||
try {
|
||||
await this.ensureDirectory(destination);
|
||||
|
||||
// Get all files in source directory
|
||||
const files = await resourceLocator.findFiles('**/*', {
|
||||
cwd: source,
|
||||
nodir: true
|
||||
nodir: true,
|
||||
});
|
||||
|
||||
let replacedCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const sourcePath = path.join(source, file);
|
||||
const destPath = path.join(destination, file);
|
||||
const destinationPath = path.join(destination, file);
|
||||
|
||||
// Check if this file type should have {root} replacement
|
||||
const shouldReplace = fileExtensions.some(ext => file.endsWith(ext));
|
||||
const shouldReplace = fileExtensions.some((extension) => file.endsWith(extension));
|
||||
|
||||
if (shouldReplace) {
|
||||
if (await this.copyFileWithRootReplacement(sourcePath, destPath, rootValue)) {
|
||||
if (await this.copyFileWithRootReplacement(sourcePath, destinationPath, rootValue)) {
|
||||
replacedCount++;
|
||||
}
|
||||
} else {
|
||||
// Regular copy for files that don't need replacement
|
||||
await this.copyFile(sourcePath, destPath);
|
||||
await this.copyFile(sourcePath, destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,10 +375,15 @@ class FileManager {
|
|||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Failed to copy directory ${source} with root replacement:`), error.message);
|
||||
console.error(
|
||||
chalk.red(`Failed to copy directory ${source} with root replacement:`),
|
||||
error.message,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
manifestDir = '.bmad-core';
|
||||
manifestFile = 'install-manifest.yaml';
|
||||
}
|
||||
|
||||
module.exports = new FileManager();
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
* Reduces duplication and provides shared methods
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const yaml = require("js-yaml");
|
||||
const chalk = require("chalk");
|
||||
const fileManager = require("./file-manager");
|
||||
const resourceLocator = require("./resource-locator");
|
||||
const { extractYamlFromAgent } = require("../../lib/yaml-utils");
|
||||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const yaml = require('js-yaml');
|
||||
const chalk = require('chalk');
|
||||
const fileManager = require('./file-manager');
|
||||
const resourceLocator = require('./resource-locator');
|
||||
const { extractYamlFromAgent } = require('../../lib/yaml-utils');
|
||||
|
||||
class BaseIdeSetup {
|
||||
constructor() {
|
||||
|
|
@ -30,16 +30,16 @@ class BaseIdeSetup {
|
|||
|
||||
// Get core agents
|
||||
const coreAgents = await this.getCoreAgentIds(installDir);
|
||||
coreAgents.forEach(id => allAgents.add(id));
|
||||
for (const id of coreAgents) allAgents.add(id);
|
||||
|
||||
// Get expansion pack agents
|
||||
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
||||
for (const pack of expansionPacks) {
|
||||
const packAgents = await this.getExpansionPackAgents(pack.path);
|
||||
packAgents.forEach(id => allAgents.add(id));
|
||||
for (const id of packAgents) allAgents.add(id);
|
||||
}
|
||||
|
||||
const result = Array.from(allAgents);
|
||||
const result = [...allAgents];
|
||||
this._agentCache.set(cacheKey, result);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -50,14 +50,14 @@ class BaseIdeSetup {
|
|||
async getCoreAgentIds(installDir) {
|
||||
const coreAgents = [];
|
||||
const corePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents"),
|
||||
path.join(installDir, "bmad-core", "agents")
|
||||
path.join(installDir, '.bmad-core', 'agents'),
|
||||
path.join(installDir, 'bmad-core', 'agents'),
|
||||
];
|
||||
|
||||
for (const agentsDir of corePaths) {
|
||||
if (await fileManager.pathExists(agentsDir)) {
|
||||
const files = await resourceLocator.findFiles("*.md", { cwd: agentsDir });
|
||||
coreAgents.push(...files.map(file => path.basename(file, ".md")));
|
||||
const files = await resourceLocator.findFiles('*.md', { cwd: agentsDir });
|
||||
coreAgents.push(...files.map((file) => path.basename(file, '.md')));
|
||||
break; // Use first found
|
||||
}
|
||||
}
|
||||
|
|
@ -80,9 +80,9 @@ class BaseIdeSetup {
|
|||
if (!agentPath) {
|
||||
// Check installation-specific paths
|
||||
const possiblePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "common", "agents", `${agentId}.md`)
|
||||
path.join(installDir, '.bmad-core', 'agents', `${agentId}.md`),
|
||||
path.join(installDir, 'bmad-core', 'agents', `${agentId}.md`),
|
||||
path.join(installDir, 'common', 'agents', `${agentId}.md`),
|
||||
];
|
||||
|
||||
for (const testPath of possiblePaths) {
|
||||
|
|
@ -113,7 +113,7 @@ class BaseIdeSetup {
|
|||
const metadata = yaml.load(yamlContent);
|
||||
return metadata.agent_name || agentId;
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Fallback to agent ID
|
||||
}
|
||||
return agentId;
|
||||
|
|
@ -131,29 +131,29 @@ class BaseIdeSetup {
|
|||
const expansionPacks = [];
|
||||
|
||||
// Check for dot-prefixed expansion packs
|
||||
const dotExpansions = await resourceLocator.findFiles(".bmad-*", { cwd: installDir });
|
||||
const dotExpansions = await resourceLocator.findFiles('.bmad-*', { cwd: installDir });
|
||||
|
||||
for (const dotExpansion of dotExpansions) {
|
||||
if (dotExpansion !== ".bmad-core") {
|
||||
if (dotExpansion !== '.bmad-core') {
|
||||
const packPath = path.join(installDir, dotExpansion);
|
||||
const packName = dotExpansion.substring(1); // remove the dot
|
||||
const packName = dotExpansion.slice(1); // remove the dot
|
||||
expansionPacks.push({
|
||||
name: packName,
|
||||
path: packPath
|
||||
path: packPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check other dot folders that have config.yaml
|
||||
const allDotFolders = await resourceLocator.findFiles(".*", { cwd: installDir });
|
||||
const allDotFolders = await resourceLocator.findFiles('.*', { cwd: installDir });
|
||||
for (const folder of allDotFolders) {
|
||||
if (!folder.startsWith(".bmad-") && folder !== ".bmad-core") {
|
||||
if (!folder.startsWith('.bmad-') && folder !== '.bmad-core') {
|
||||
const packPath = path.join(installDir, folder);
|
||||
const configPath = path.join(packPath, "config.yaml");
|
||||
const configPath = path.join(packPath, 'config.yaml');
|
||||
if (await fileManager.pathExists(configPath)) {
|
||||
expansionPacks.push({
|
||||
name: folder.substring(1), // remove the dot
|
||||
path: packPath
|
||||
name: folder.slice(1), // remove the dot
|
||||
path: packPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -167,13 +167,13 @@ class BaseIdeSetup {
|
|||
* Get expansion pack agents
|
||||
*/
|
||||
async getExpansionPackAgents(packPath) {
|
||||
const agentsDir = path.join(packPath, "agents");
|
||||
const agentsDir = path.join(packPath, 'agents');
|
||||
if (!(await fileManager.pathExists(agentsDir))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const agentFiles = await resourceLocator.findFiles("*.md", { cwd: agentsDir });
|
||||
return agentFiles.map(file => path.basename(file, ".md"));
|
||||
const agentFiles = await resourceLocator.findFiles('*.md', { cwd: agentsDir });
|
||||
return agentFiles.map((file) => path.basename(file, '.md'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,26 +184,27 @@ class BaseIdeSetup {
|
|||
const agentTitle = await this.getAgentTitle(agentId, installDir);
|
||||
const yamlContent = extractYamlFromAgent(agentContent);
|
||||
|
||||
let content = "";
|
||||
let content = '';
|
||||
|
||||
if (format === 'mdc') {
|
||||
// MDC format for Cursor
|
||||
content = "---\n";
|
||||
content += "description: \n";
|
||||
content += "globs: []\n";
|
||||
content += "alwaysApply: false\n";
|
||||
content += "---\n\n";
|
||||
content = '---\n';
|
||||
content += 'description: \n';
|
||||
content += 'globs: []\n';
|
||||
content += 'alwaysApply: false\n';
|
||||
content += '---\n\n';
|
||||
content += `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
||||
content += `This rule is triggered when the user types \`@${agentId}\` and activates the ${agentTitle} agent persona.\n\n`;
|
||||
content += "## Agent Activation\n\n";
|
||||
content += "CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
|
||||
content += "```yaml\n";
|
||||
content += yamlContent || agentContent.replace(/^#.*$/m, "").trim();
|
||||
content += "\n```\n\n";
|
||||
content += "## File Reference\n\n";
|
||||
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
|
||||
content += '## Agent Activation\n\n';
|
||||
content +=
|
||||
'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n';
|
||||
content += '```yaml\n';
|
||||
content += yamlContent || agentContent.replace(/^#.*$/m, '').trim();
|
||||
content += '\n```\n\n';
|
||||
content += '## File Reference\n\n';
|
||||
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
||||
content += `The complete agent definition is available in [${relativePath}](mdc:${relativePath}).\n\n`;
|
||||
content += "## Usage\n\n";
|
||||
content += '## Usage\n\n';
|
||||
content += `When the user types \`@${agentId}\`, activate this ${agentTitle} persona and follow all instructions defined in the YAML configuration above.\n`;
|
||||
} else if (format === 'claude') {
|
||||
// Claude Code format
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -3,7 +3,7 @@
|
|||
* Helps identify memory leaks and optimize resource usage
|
||||
*/
|
||||
|
||||
const v8 = require('v8');
|
||||
const v8 = require('node:v8');
|
||||
|
||||
class MemoryProfiler {
|
||||
constructor() {
|
||||
|
|
@ -28,18 +28,18 @@ class MemoryProfiler {
|
|||
heapTotal: this.formatBytes(memUsage.heapTotal),
|
||||
heapUsed: this.formatBytes(memUsage.heapUsed),
|
||||
external: this.formatBytes(memUsage.external),
|
||||
arrayBuffers: this.formatBytes(memUsage.arrayBuffers || 0)
|
||||
arrayBuffers: this.formatBytes(memUsage.arrayBuffers || 0),
|
||||
},
|
||||
heap: {
|
||||
totalHeapSize: this.formatBytes(heapStats.total_heap_size),
|
||||
usedHeapSize: this.formatBytes(heapStats.used_heap_size),
|
||||
heapSizeLimit: this.formatBytes(heapStats.heap_size_limit),
|
||||
mallocedMemory: this.formatBytes(heapStats.malloced_memory),
|
||||
externalMemory: this.formatBytes(heapStats.external_memory)
|
||||
externalMemory: this.formatBytes(heapStats.external_memory),
|
||||
},
|
||||
raw: {
|
||||
heapUsed: memUsage.heapUsed
|
||||
}
|
||||
heapUsed: memUsage.heapUsed,
|
||||
},
|
||||
};
|
||||
|
||||
// Track peak memory
|
||||
|
|
@ -55,8 +55,8 @@ class MemoryProfiler {
|
|||
* Force garbage collection (requires --expose-gc flag)
|
||||
*/
|
||||
forceGC() {
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
if (globalThis.gc) {
|
||||
globalThis.gc();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -72,11 +72,11 @@ class MemoryProfiler {
|
|||
currentUsage: {
|
||||
rss: this.formatBytes(currentMemory.rss),
|
||||
heapTotal: this.formatBytes(currentMemory.heapTotal),
|
||||
heapUsed: this.formatBytes(currentMemory.heapUsed)
|
||||
heapUsed: this.formatBytes(currentMemory.heapUsed),
|
||||
},
|
||||
peakMemory: this.formatBytes(this.peakMemory),
|
||||
totalCheckpoints: this.checkpoints.length,
|
||||
runTime: `${((Date.now() - this.startTime) / 1000).toFixed(2)}s`
|
||||
runTime: `${((Date.now() - this.startTime) / 1000).toFixed(2)}s`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ class MemoryProfiler {
|
|||
summary,
|
||||
memoryGrowth,
|
||||
checkpoints: this.checkpoints,
|
||||
recommendations: this.getRecommendations(memoryGrowth)
|
||||
recommendations: this.getRecommendations(memoryGrowth),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -102,18 +102,18 @@ class MemoryProfiler {
|
|||
if (this.checkpoints.length < 2) return [];
|
||||
|
||||
const growth = [];
|
||||
for (let i = 1; i < this.checkpoints.length; i++) {
|
||||
const prev = this.checkpoints[i - 1];
|
||||
const curr = this.checkpoints[i];
|
||||
for (let index = 1; index < this.checkpoints.length; index++) {
|
||||
const previous = this.checkpoints[index - 1];
|
||||
const current = this.checkpoints[index];
|
||||
|
||||
const heapDiff = curr.raw.heapUsed - prev.raw.heapUsed;
|
||||
const heapDiff = current.raw.heapUsed - previous.raw.heapUsed;
|
||||
|
||||
growth.push({
|
||||
from: prev.label,
|
||||
to: curr.label,
|
||||
from: previous.label,
|
||||
to: current.label,
|
||||
heapGrowth: this.formatBytes(Math.abs(heapDiff)),
|
||||
isIncrease: heapDiff > 0,
|
||||
timeDiff: `${((curr.timestamp - prev.timestamp) / 1000).toFixed(2)}s`
|
||||
timeDiff: `${((current.timestamp - previous.timestamp) / 1000).toFixed(2)}s`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ class MemoryProfiler {
|
|||
const recommendations = [];
|
||||
|
||||
// Check for large memory growth
|
||||
const largeGrowths = memoryGrowth.filter(g => {
|
||||
const largeGrowths = memoryGrowth.filter((g) => {
|
||||
const bytes = this.parseBytes(g.heapGrowth);
|
||||
return bytes > 50 * 1024 * 1024; // 50MB
|
||||
});
|
||||
|
|
@ -136,16 +136,17 @@ class MemoryProfiler {
|
|||
recommendations.push({
|
||||
type: 'warning',
|
||||
message: `Large memory growth detected in ${largeGrowths.length} operations`,
|
||||
details: largeGrowths.map(g => `${g.from} → ${g.to}: ${g.heapGrowth}`)
|
||||
details: largeGrowths.map((g) => `${g.from} → ${g.to}: ${g.heapGrowth}`),
|
||||
});
|
||||
}
|
||||
|
||||
// Check peak memory
|
||||
if (this.peakMemory > 500 * 1024 * 1024) { // 500MB
|
||||
if (this.peakMemory > 500 * 1024 * 1024) {
|
||||
// 500MB
|
||||
recommendations.push({
|
||||
type: 'warning',
|
||||
message: `High peak memory usage: ${this.formatBytes(this.peakMemory)}`,
|
||||
suggestion: 'Consider processing files in smaller batches'
|
||||
suggestion: 'Consider processing files in smaller batches',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +156,7 @@ class MemoryProfiler {
|
|||
recommendations.push({
|
||||
type: 'error',
|
||||
message: 'Potential memory leak detected',
|
||||
details: 'Memory usage continuously increases without significant decreases'
|
||||
details: 'Memory usage continuously increases without significant decreases',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -169,8 +170,8 @@ class MemoryProfiler {
|
|||
if (this.checkpoints.length < 5) return false;
|
||||
|
||||
let increasingCount = 0;
|
||||
for (let i = 1; i < this.checkpoints.length; i++) {
|
||||
if (this.checkpoints[i].raw.heapUsed > this.checkpoints[i - 1].raw.heapUsed) {
|
||||
for (let index = 1; index < this.checkpoints.length; index++) {
|
||||
if (this.checkpoints[index].raw.heapUsed > this.checkpoints[index - 1].raw.heapUsed) {
|
||||
increasingCount++;
|
||||
}
|
||||
}
|
||||
|
|
@ -187,26 +188,26 @@ class MemoryProfiler {
|
|||
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
const index = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
return Number.parseFloat((bytes / Math.pow(k, index)).toFixed(2)) + ' ' + sizes[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse human-readable bytes back to number
|
||||
*/
|
||||
parseBytes(str) {
|
||||
const match = str.match(/^([\d.]+)\s*([KMGT]?B?)$/i);
|
||||
parseBytes(string_) {
|
||||
const match = string_.match(/^([\d.]+)\s*([KMGT]?B?)$/i);
|
||||
if (!match) return 0;
|
||||
|
||||
const value = parseFloat(match[1]);
|
||||
const value = Number.parseFloat(match[1]);
|
||||
const unit = match[2].toUpperCase();
|
||||
|
||||
const multipliers = {
|
||||
'B': 1,
|
||||
'KB': 1024,
|
||||
'MB': 1024 * 1024,
|
||||
'GB': 1024 * 1024 * 1024
|
||||
B: 1,
|
||||
KB: 1024,
|
||||
MB: 1024 * 1024,
|
||||
GB: 1024 * 1024 * 1024,
|
||||
};
|
||||
|
||||
return value * (multipliers[unit] || 1);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ class ModuleManager {
|
|||
const modules = await Promise.all([
|
||||
this.getModule('chalk'),
|
||||
this.getModule('ora'),
|
||||
this.getModule('inquirer')
|
||||
this.getModule('inquirer'),
|
||||
]);
|
||||
|
||||
return {
|
||||
chalk: modules[0],
|
||||
ora: modules[1],
|
||||
inquirer: modules[2]
|
||||
inquirer: modules[2],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -64,20 +64,26 @@ class ModuleManager {
|
|||
*/
|
||||
async _loadModule(moduleName) {
|
||||
switch (moduleName) {
|
||||
case 'chalk':
|
||||
case 'chalk': {
|
||||
return (await import('chalk')).default;
|
||||
case 'ora':
|
||||
}
|
||||
case 'ora': {
|
||||
return (await import('ora')).default;
|
||||
case 'inquirer':
|
||||
}
|
||||
case 'inquirer': {
|
||||
return (await import('inquirer')).default;
|
||||
case 'glob':
|
||||
}
|
||||
case 'glob': {
|
||||
return (await import('glob')).glob;
|
||||
case 'globSync':
|
||||
}
|
||||
case 'globSync': {
|
||||
return (await import('glob')).globSync;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown module: ${moduleName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the module cache to free memory
|
||||
|
|
@ -93,13 +99,11 @@ class ModuleManager {
|
|||
* @returns {Promise<Object>} Object with module names as keys
|
||||
*/
|
||||
async getModules(moduleNames) {
|
||||
const modules = await Promise.all(
|
||||
moduleNames.map(name => this.getModule(name))
|
||||
);
|
||||
const modules = await Promise.all(moduleNames.map((name) => this.getModule(name)));
|
||||
|
||||
return moduleNames.reduce((acc, name, index) => {
|
||||
acc[name] = modules[index];
|
||||
return acc;
|
||||
return moduleNames.reduce((accumulator, name, index) => {
|
||||
accumulator[name] = modules[index];
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,14 +107,11 @@ class ResourceLocator {
|
|||
|
||||
// Get agents from bmad-core
|
||||
const coreAgents = await this.findFiles('agents/*.md', {
|
||||
cwd: this.getBmadCorePath()
|
||||
cwd: this.getBmadCorePath(),
|
||||
});
|
||||
|
||||
for (const agentFile of coreAgents) {
|
||||
const content = await fs.readFile(
|
||||
path.join(this.getBmadCorePath(), agentFile),
|
||||
'utf8'
|
||||
);
|
||||
const content = await fs.readFile(path.join(this.getBmadCorePath(), agentFile), 'utf8');
|
||||
const yamlContent = extractYamlFromAgent(content);
|
||||
if (yamlContent) {
|
||||
try {
|
||||
|
|
@ -123,9 +120,9 @@ class ResourceLocator {
|
|||
id: path.basename(agentFile, '.md'),
|
||||
name: metadata.agent_name || path.basename(agentFile, '.md'),
|
||||
description: metadata.description || 'No description available',
|
||||
source: 'core'
|
||||
source: 'core',
|
||||
});
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Skip invalid agents
|
||||
}
|
||||
}
|
||||
|
|
@ -167,11 +164,12 @@ class ResourceLocator {
|
|||
name: config.name || entry.name,
|
||||
version: config.version || '1.0.0',
|
||||
description: config.description || 'No description available',
|
||||
shortTitle: config['short-title'] || config.description || 'No description available',
|
||||
shortTitle:
|
||||
config['short-title'] || config.description || 'No description available',
|
||||
author: config.author || 'Unknown',
|
||||
path: path.join(expansionPacksPath, entry.name)
|
||||
path: path.join(expansionPacksPath, entry.name),
|
||||
});
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Skip invalid packs
|
||||
}
|
||||
}
|
||||
|
|
@ -207,7 +205,7 @@ class ResourceLocator {
|
|||
const config = yaml.load(content);
|
||||
this._pathCache.set(cacheKey, config);
|
||||
return config;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -261,7 +259,7 @@ class ResourceLocator {
|
|||
const result = { all: allDeps, byType };
|
||||
this._pathCache.set(cacheKey, result);
|
||||
return result;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return { all: [], byType: {} };
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +293,7 @@ class ResourceLocator {
|
|||
const config = yaml.load(content);
|
||||
this._pathCache.set(cacheKey, config);
|
||||
return config;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,6 @@
|
|||
"name": "bmad-method",
|
||||
"version": "4.36.2",
|
||||
"description": "BMad Method installer - AI-powered Agile development framework",
|
||||
"main": "lib/installer.js",
|
||||
"bin": {
|
||||
"bmad": "./bin/bmad.js",
|
||||
"bmad-method": "./bin/bmad.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"bmad",
|
||||
"agile",
|
||||
|
|
@ -19,8 +11,24 @@
|
|||
"installer",
|
||||
"agents"
|
||||
],
|
||||
"author": "BMad Team",
|
||||
"homepage": "https://github.com/bmad-team/bmad-method#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/bmad-team/bmad-method/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bmad-team/bmad-method.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "BMad Team",
|
||||
"main": "lib/installer.js",
|
||||
"bin": {
|
||||
"bmad": "./bin/bmad.js",
|
||||
"bmad-method": "./bin/bmad.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"commander": "^14.0.0",
|
||||
|
|
@ -31,13 +39,5 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bmad-team/bmad-method.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/bmad-team/bmad-method/issues"
|
||||
},
|
||||
"homepage": "https://github.com/bmad-team/bmad-method#readme"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const fs = require('node:fs').promises;
|
||||
const path = require('node:path');
|
||||
const yaml = require('js-yaml');
|
||||
const { extractYamlFromAgent } = require('./yaml-utils');
|
||||
|
||||
|
|
@ -28,9 +28,9 @@ class DependencyResolver {
|
|||
id: agentId,
|
||||
path: agentPath,
|
||||
content: agentContent,
|
||||
config: agentConfig
|
||||
config: agentConfig,
|
||||
},
|
||||
resources: []
|
||||
resources: [],
|
||||
};
|
||||
|
||||
// Personas are now embedded in agent configs, no need to resolve separately
|
||||
|
|
@ -58,18 +58,18 @@ class DependencyResolver {
|
|||
id: teamId,
|
||||
path: teamPath,
|
||||
content: teamContent,
|
||||
config: teamConfig
|
||||
config: teamConfig,
|
||||
},
|
||||
agents: [],
|
||||
resources: new Map() // Use Map to deduplicate resources
|
||||
resources: new Map(), // Use Map to deduplicate resources
|
||||
};
|
||||
|
||||
// Always add bmad-orchestrator agent first if it's a team
|
||||
const bmadAgent = await this.resolveAgentDependencies('bmad-orchestrator');
|
||||
dependencies.agents.push(bmadAgent.agent);
|
||||
bmadAgent.resources.forEach(res => {
|
||||
for (const res of bmadAgent.resources) {
|
||||
dependencies.resources.set(res.path, res);
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve all agents in the team
|
||||
let agentsToResolve = teamConfig.agents || [];
|
||||
|
|
@ -78,7 +78,7 @@ class DependencyResolver {
|
|||
if (agentsToResolve.includes('*')) {
|
||||
const allAgents = await this.listAgents();
|
||||
// Remove wildcard and add all agents except those already in the list and bmad-master
|
||||
agentsToResolve = agentsToResolve.filter(a => a !== '*');
|
||||
agentsToResolve = agentsToResolve.filter((a) => a !== '*');
|
||||
for (const agent of allAgents) {
|
||||
if (!agentsToResolve.includes(agent) && agent !== 'bmad-master') {
|
||||
agentsToResolve.push(agent);
|
||||
|
|
@ -92,9 +92,9 @@ class DependencyResolver {
|
|||
dependencies.agents.push(agentDeps.agent);
|
||||
|
||||
// Add resources with deduplication
|
||||
agentDeps.resources.forEach(res => {
|
||||
for (const res of agentDeps.resources) {
|
||||
dependencies.resources.set(res.path, res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve workflows
|
||||
|
|
@ -104,7 +104,7 @@ class DependencyResolver {
|
|||
}
|
||||
|
||||
// Convert Map back to array
|
||||
dependencies.resources = Array.from(dependencies.resources.values());
|
||||
dependencies.resources = [...dependencies.resources.values()];
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
|
@ -123,12 +123,12 @@ class DependencyResolver {
|
|||
try {
|
||||
filePath = path.join(this.bmadCore, type, id);
|
||||
content = await fs.readFile(filePath, 'utf8');
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// If not found in bmad-core, try common folder
|
||||
try {
|
||||
filePath = path.join(this.common, type, id);
|
||||
content = await fs.readFile(filePath, 'utf8');
|
||||
} catch (e2) {
|
||||
} catch {
|
||||
// File not found in either location
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@ class DependencyResolver {
|
|||
type,
|
||||
id,
|
||||
path: filePath,
|
||||
content
|
||||
content,
|
||||
};
|
||||
|
||||
this.cache.set(cacheKey, resource);
|
||||
|
|
@ -156,10 +156,8 @@ class DependencyResolver {
|
|||
async listAgents() {
|
||||
try {
|
||||
const files = await fs.readdir(path.join(this.bmadCore, 'agents'));
|
||||
return files
|
||||
.filter(f => f.endsWith('.md'))
|
||||
.map(f => f.replace('.md', ''));
|
||||
} catch (error) {
|
||||
return files.filter((f) => f.endsWith('.md')).map((f) => f.replace('.md', ''));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -167,10 +165,8 @@ class DependencyResolver {
|
|||
async listTeams() {
|
||||
try {
|
||||
const files = await fs.readdir(path.join(this.bmadCore, 'agent-teams'));
|
||||
return files
|
||||
.filter(f => f.endsWith('.yaml'))
|
||||
.map(f => f.replace('.yaml', ''));
|
||||
} catch (error) {
|
||||
return files.filter((f) => f.endsWith('.yaml')).map((f) => f.replace('.yaml', ''));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
function extractYamlFromAgent(agentContent, cleanCommands = false) {
|
||||
// Remove carriage returns and match YAML block
|
||||
const yamlMatch = agentContent.replace(/\r/g, "").match(/```ya?ml\n([\s\S]*?)\n```/);
|
||||
const yamlMatch = agentContent.replaceAll('\r', '').match(/```ya?ml\n([\s\S]*?)\n```/);
|
||||
if (!yamlMatch) return null;
|
||||
|
||||
let yamlContent = yamlMatch[1].trim();
|
||||
|
|
@ -18,12 +18,12 @@ function extractYamlFromAgent(agentContent, cleanCommands = false) {
|
|||
// Clean up command descriptions if requested
|
||||
// Converts "- command - description" to just "- command"
|
||||
if (cleanCommands) {
|
||||
yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
|
||||
yamlContent = yamlContent.replaceAll(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
|
||||
}
|
||||
|
||||
return yamlContent;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractYamlFromAgent
|
||||
extractYamlFromAgent,
|
||||
};
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
* Semantic-release plugin to sync installer package.json version
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
// This function runs during the "prepare" step of semantic-release
|
||||
function prepare(_, { nextRelease, logger }) {
|
||||
|
|
@ -14,13 +14,13 @@ function prepare(_, { nextRelease, logger }) {
|
|||
if (!fs.existsSync(file)) return logger.log('Installer package.json not found, skipping');
|
||||
|
||||
// Read and parse the package.json file
|
||||
const pkg = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
const package_ = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
|
||||
// Update the version field with the next release version
|
||||
pkg.version = nextRelease.version;
|
||||
package_.version = nextRelease.version;
|
||||
|
||||
// Write the updated JSON back to the file
|
||||
fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n');
|
||||
fs.writeFileSync(file, JSON.stringify(package_, null, 2) + '\n');
|
||||
|
||||
// Log success message
|
||||
logger.log(`Synced installer package.json to version ${nextRelease.version}`);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// ASCII banner art definitions extracted from banners.js to separate art from logic
|
||||
|
||||
const BMAD_TITLE = "BMAD-METHOD";
|
||||
const FLATTENER_TITLE = "FLATTENER";
|
||||
const INSTALLER_TITLE = "INSTALLER";
|
||||
const BMAD_TITLE = 'BMAD-METHOD';
|
||||
const FLATTENER_TITLE = 'FLATTENER';
|
||||
const INSTALLER_TITLE = 'INSTALLER';
|
||||
|
||||
// Large ASCII blocks (block-style fonts)
|
||||
const BMAD_LARGE = `
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Sync installer package.json version with main package.json
|
||||
* Used by semantic-release to keep versions in sync
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function syncInstallerVersion() {
|
||||
// Read main package.json
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue