Compare commits
6 Commits
242d37215c
...
22e54868b0
| Author | SHA1 | Date |
|---|---|---|
|
|
22e54868b0 | |
|
|
41f9cc1913 | |
|
|
686af5b0ee | |
|
|
eb853aa3f6 | |
|
|
65658a499b | |
|
|
d553a09f73 |
|
|
@ -71,3 +71,4 @@ z*/
|
||||||
.github/chatmodes
|
.github/chatmodes
|
||||||
.agent
|
.agent
|
||||||
.agentvibes/
|
.agentvibes/
|
||||||
|
.kiro/
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
# Test fixtures with intentionally broken/malformed files
|
# Test fixtures with intentionally broken/malformed files
|
||||||
test/fixtures/**
|
test/fixtures/**
|
||||||
|
|
||||||
|
# Contributor Covenant (external standard)
|
||||||
|
CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
# BMAD runtime folders (user-specific, not in repo)
|
# BMAD runtime folders (user-specific, not in repo)
|
||||||
.bmad/
|
.bmad/
|
||||||
.bmad*/
|
.bmad*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
the official BMAD Discord server (https://discord.com/invite/gk8jAdXWmj) - DM a moderator or flag a post.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
|
|
@ -108,7 +108,8 @@ Stories move through these states in the sprint status file:
|
||||||
|
|
||||||
**As Needed:**
|
**As Needed:**
|
||||||
|
|
||||||
- Run `workflow-status` anytime to check progress
|
- Run `sprint-status` anytime in Phase 4 to inspect sprint-status.yaml and get the next implementation command
|
||||||
|
- Run `workflow-status` for cross-phase routing and project-level paths
|
||||||
- Run `correct-course` if significant changes needed
|
- Run `correct-course` if significant changes needed
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -155,7 +156,7 @@ PRD (PM) → Architecture (Architect)
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**Q: Which workflow should I run next?**
|
**Q: Which workflow should I run next?**
|
||||||
A: Run `workflow-status` - it reads the sprint status file and tells you exactly what to do.
|
A: Run `workflow-status` - it reads the sprint status file and tells you exactly what to do. During implementation (Phase 4) run `sprint-status` (fast check against sprint-status.yaml).
|
||||||
|
|
||||||
**Q: Story needs significant changes mid-implementation?**
|
**Q: Story needs significant changes mid-implementation?**
|
||||||
A: Run `correct-course` to analyze impact and route appropriately.
|
A: Run `correct-course` to analyze impact and route appropriately.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
# Sprint Status - Multi-Mode Service
|
||||||
|
|
||||||
|
<critical>The workflow execution engine is governed by: {project-root}/{bmad_folder}/core/tasks/workflow.xml</critical>
|
||||||
|
<critical>You MUST have already loaded and processed: {project-root}/{bmad_folder}/bmm/workflows/4-implementation/sprint-status/workflow.yaml</critical>
|
||||||
|
<critical>Modes: interactive (default), validate, data</critical>
|
||||||
|
<critical>⚠️ ABSOLUTELY NO TIME ESTIMATES. Do NOT mention hours, days, weeks, or timelines.</critical>
|
||||||
|
|
||||||
|
<workflow>
|
||||||
|
|
||||||
|
<step n="0" goal="Determine execution mode">
|
||||||
|
<action>Set mode = {{mode}} if provided by caller; otherwise mode = "interactive"</action>
|
||||||
|
|
||||||
|
<check if="mode == data">
|
||||||
|
<action>Jump to Step 20</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="mode == validate">
|
||||||
|
<action>Jump to Step 30</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="mode == interactive">
|
||||||
|
<action>Continue to Step 1</action>
|
||||||
|
</check>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<step n="1" goal="Locate sprint status file">
|
||||||
|
<action>Try {sprint_status_file}</action>
|
||||||
|
<check if="file not found">
|
||||||
|
<output>❌ sprint-status.yaml not found.
|
||||||
|
Run `/bmad:bmm:workflows:sprint-planning` to generate it, then rerun sprint-status.</output>
|
||||||
|
<action>Exit workflow</action>
|
||||||
|
</check>
|
||||||
|
<action>Continue to Step 2</action>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<step n="2" goal="Read and parse sprint-status.yaml">
|
||||||
|
<action>Read the FULL file: {sprint_status_file}</action>
|
||||||
|
<action>Parse fields: generated, project, project_key, tracking_system, story_location</action>
|
||||||
|
<action>Parse development_status map. Classify keys:</action>
|
||||||
|
- Epics: keys starting with "epic-" (and not ending with "-retrospective")
|
||||||
|
- Retrospectives: keys ending with "-retrospective"
|
||||||
|
- Stories: everything else (e.g., 1-2-login-form)
|
||||||
|
<action>Count story statuses: backlog, drafted, ready-for-dev, in-progress, review, done</action>
|
||||||
|
<action>Count epic statuses: backlog, contexted</action>
|
||||||
|
<action>Detect risks:</action>
|
||||||
|
- Stories in review but no reviewer assigned context → suggest `/bmad:bmm:workflows:code-review`
|
||||||
|
- Stories in in-progress with no ready-for-dev items behind them → keep focus on the active story
|
||||||
|
- All epics backlog/contexted but no stories drafted → prompt to run `/bmad:bmm:workflows:create-story`
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<step n="3" goal="Select next action recommendation">
|
||||||
|
<action>Pick the next recommended workflow using priority:</action>
|
||||||
|
1. If any story status == in-progress → recommend `dev-story` for the first in-progress story
|
||||||
|
2. Else if any story status == review → recommend `code-review` for the first review story
|
||||||
|
3. Else if any story status == ready-for-dev → recommend `dev-story`
|
||||||
|
4. Else if any story status == drafted → recommend `story-ready`
|
||||||
|
5. Else if any story status == backlog → recommend `create-story`
|
||||||
|
6. Else if any epic status == backlog → recommend `epic-tech-context`
|
||||||
|
7. Else if retrospectives are optional → recommend `retrospective`
|
||||||
|
8. Else → All implementation items done; suggest `workflow-status` to plan next phase
|
||||||
|
<action>Store selected recommendation as: next_story_id, next_workflow_id, next_agent (SM/DEV as appropriate)</action>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<step n="4" goal="Display summary">
|
||||||
|
<output>
|
||||||
|
## 📊 Sprint Status
|
||||||
|
|
||||||
|
- Project: {{project}} ({{project_key}})
|
||||||
|
- Tracking: {{tracking_system}}
|
||||||
|
- Status file: {sprint_status_file}
|
||||||
|
|
||||||
|
**Stories:** backlog {{count_backlog}}, drafted {{count_drafted}}, ready-for-dev {{count_ready}}, in-progress {{count_in_progress}}, review {{count_review}}, done {{count_done}}
|
||||||
|
|
||||||
|
**Epics:** backlog {{epic_backlog}}, contexted {{epic_contexted}}
|
||||||
|
|
||||||
|
**Next Recommendation:** /bmad:bmm:workflows:{{next_workflow_id}} ({{next_story_id}})
|
||||||
|
|
||||||
|
{{#if risks}}
|
||||||
|
**Risks:**
|
||||||
|
{{#each risks}}
|
||||||
|
|
||||||
|
- {{this}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if by_epic}}
|
||||||
|
**Per Epic:**
|
||||||
|
{{#each by_epic}}
|
||||||
|
|
||||||
|
- {{epic_id}}: context={{context_status}}, stories → backlog {{backlog}}, drafted {{drafted}}, ready {{ready_for_dev}}, in-progress {{in_progress}}, review {{review}}, done {{done}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</output>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<step n="5" goal="Offer actions">
|
||||||
|
<ask>Pick an option:
|
||||||
|
1) Run recommended workflow now
|
||||||
|
2) Show all stories grouped by status
|
||||||
|
3) Show raw sprint-status.yaml
|
||||||
|
4) Exit
|
||||||
|
Choice:</ask>
|
||||||
|
|
||||||
|
<check if="choice == 1">
|
||||||
|
<output>Run `/bmad:bmm:workflows:{{next_workflow_id}}`.
|
||||||
|
If the command targets a story, set `story_key={{next_story_id}}` when prompted.</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="choice == 2">
|
||||||
|
<output>
|
||||||
|
### Stories by Status
|
||||||
|
- In Progress: {{stories_in_progress}}
|
||||||
|
- Review: {{stories_in_review}}
|
||||||
|
- Ready for Dev: {{stories_ready_for_dev}}
|
||||||
|
- Drafted: {{stories_drafted}}
|
||||||
|
- Backlog: {{stories_backlog}}
|
||||||
|
- Done: {{stories_done}}
|
||||||
|
</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="choice == 3">
|
||||||
|
<action>Display the full contents of {sprint_status_file}</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="choice == 4">
|
||||||
|
<action>Exit workflow</action>
|
||||||
|
</check>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<!-- ========================= -->
|
||||||
|
<!-- Data mode for other flows -->
|
||||||
|
<!-- ========================= -->
|
||||||
|
|
||||||
|
<step n="20" goal="Data mode output">
|
||||||
|
<action>Load and parse {sprint_status_file} same as Step 2</action>
|
||||||
|
<action>Compute recommendation same as Step 3</action>
|
||||||
|
<template-output>next_workflow_id = {{next_workflow_id}}</template-output>
|
||||||
|
<template-output>next_story_id = {{next_story_id}}</template-output>
|
||||||
|
<template-output>count_backlog = {{count_backlog}}</template-output>
|
||||||
|
<template-output>count_drafted = {{count_drafted}}</template-output>
|
||||||
|
<template-output>count_ready = {{count_ready}}</template-output>
|
||||||
|
<template-output>count_in_progress = {{count_in_progress}}</template-output>
|
||||||
|
<template-output>count_review = {{count_review}}</template-output>
|
||||||
|
<template-output>count_done = {{count_done}}</template-output>
|
||||||
|
<template-output>epic_backlog = {{epic_backlog}}</template-output>
|
||||||
|
<template-output>epic_contexted = {{epic_contexted}}</template-output>
|
||||||
|
<template-output>warnings = {{risks}}</template-output>
|
||||||
|
<action>Return to caller</action>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
<!-- ========================= -->
|
||||||
|
<!-- Validate mode -->
|
||||||
|
<!-- ========================= -->
|
||||||
|
|
||||||
|
<step n="30" goal="Validate sprint-status file">
|
||||||
|
<action>Check that {sprint_status_file} exists</action>
|
||||||
|
<check if="missing">
|
||||||
|
<template-output>is_valid = false</template-output>
|
||||||
|
<template-output>error = "sprint-status.yaml missing"</template-output>
|
||||||
|
<template-output>suggestion = "Run sprint-planning to create it"</template-output>
|
||||||
|
<action>Return</action>
|
||||||
|
</check>
|
||||||
|
<action>Read file and verify it has a development_status section with at least one entry</action>
|
||||||
|
<check if="validation fails">
|
||||||
|
<template-output>is_valid = false</template-output>
|
||||||
|
<template-output>error = "development_status missing or empty"</template-output>
|
||||||
|
<template-output>suggestion = "Re-run sprint-planning or repair the file manually"</template-output>
|
||||||
|
<action>Return</action>
|
||||||
|
</check>
|
||||||
|
<template-output>is_valid = true</template-output>
|
||||||
|
<template-output>message = "sprint-status.yaml present and parsable"</template-output>
|
||||||
|
</step>
|
||||||
|
|
||||||
|
</workflow>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Sprint Status - Implementation Tracker
|
||||||
|
name: sprint-status
|
||||||
|
description: "Summarize sprint-status.yaml, surface risks, and route to the right implementation workflow."
|
||||||
|
author: "BMad"
|
||||||
|
|
||||||
|
# Critical variables from config
|
||||||
|
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||||
|
output_folder: "{config_source}:output_folder"
|
||||||
|
user_name: "{config_source}:user_name"
|
||||||
|
communication_language: "{config_source}:communication_language"
|
||||||
|
document_output_language: "{config_source}:document_output_language"
|
||||||
|
date: system-generated
|
||||||
|
sprint_artifacts: "{config_source}:sprint_artifacts"
|
||||||
|
|
||||||
|
# Workflow components
|
||||||
|
installed_path: "{project-root}/{bmad_folder}/bmm/workflows/4-implementation/sprint-status"
|
||||||
|
instructions: "{installed_path}/instructions.md"
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
variables:
|
||||||
|
sprint_status_file: "{sprint_artifacts}/sprint-status.yaml || {output_folder}/sprint-status.yaml"
|
||||||
|
tracking_system: "file-system"
|
||||||
|
|
||||||
|
# Smart input file references
|
||||||
|
input_file_patterns:
|
||||||
|
sprint_status:
|
||||||
|
description: "Sprint status file generated by sprint-planning"
|
||||||
|
whole: "{sprint_artifacts}/sprint-status.yaml || {output_folder}/sprint-status.yaml"
|
||||||
|
load_strategy: "FULL_LOAD"
|
||||||
|
|
||||||
|
# Standalone so IDE commands get generated
|
||||||
|
standalone: true
|
||||||
|
|
||||||
|
# No web bundle needed
|
||||||
|
web_bundle: false
|
||||||
|
|
@ -32,6 +32,29 @@
|
||||||
</check>
|
</check>
|
||||||
|
|
||||||
<check if="Mode B">
|
<check if="Mode B">
|
||||||
|
|
||||||
|
<!-- Escalation Threshold: Lightweight check - should we invoke scale-adaptive? -->
|
||||||
|
|
||||||
|
<action>Evaluate escalation threshold against user input (minimal tokens, no file loading):
|
||||||
|
|
||||||
|
**Triggers escalation** (if 2+ signals present):
|
||||||
|
|
||||||
|
- Multiple components mentioned (e.g., dashboard + api + database)
|
||||||
|
- System-level language (e.g., platform, integration, architecture)
|
||||||
|
- Uncertainty about approach (e.g., "how should I", "best way to")
|
||||||
|
- Multi-layer scope (e.g., UI + backend + data together)
|
||||||
|
- Extended timeframe (e.g., "this week", "over the next few days")
|
||||||
|
|
||||||
|
**Reduces signal:**
|
||||||
|
|
||||||
|
- Simplicity markers (e.g., "just", "quickly", "fix", "bug", "typo", "simple", "basic", "minor")
|
||||||
|
- Single file/component focus
|
||||||
|
- Confident, specific request
|
||||||
|
|
||||||
|
Use holistic judgment, not mechanical keyword matching.</action>
|
||||||
|
|
||||||
|
<!-- No Escalation: Simple request, offer existing choice -->
|
||||||
|
<check if="escalation threshold NOT triggered">
|
||||||
<ask>**[t] Plan first** - Create tech-spec then implement
|
<ask>**[t] Plan first** - Create tech-spec then implement
|
||||||
**[e] Execute directly** - Start now</ask>
|
**[e] Execute directly** - Start now</ask>
|
||||||
|
|
||||||
|
|
@ -44,6 +67,80 @@
|
||||||
<ask>Any additional guidance before I begin? (patterns, files, constraints) Or "go" to start.</ask>
|
<ask>Any additional guidance before I begin? (patterns, files, constraints) Or "go" to start.</ask>
|
||||||
<goto>step_2</goto>
|
<goto>step_2</goto>
|
||||||
</check>
|
</check>
|
||||||
|
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Escalation Triggered: Load scale-adaptive and evaluate level -->
|
||||||
|
<check if="escalation threshold triggered">
|
||||||
|
<action>Load {project_levels} and evaluate user input against detection_hints.keywords</action>
|
||||||
|
<action>Determine level (0-4) using scale-adaptive definitions</action>
|
||||||
|
|
||||||
|
<!-- Level 0: Scale-adaptive confirms simple, fall back to standard choice -->
|
||||||
|
<check if="level 0">
|
||||||
|
<ask>**[t] Plan first** - Create tech-spec then implement
|
||||||
|
|
||||||
|
**[e] Execute directly** - Start now</ask>
|
||||||
|
|
||||||
|
<check if="t">
|
||||||
|
<action>Load and execute {create_tech_spec_workflow}</action>
|
||||||
|
<action>Continue to implementation after spec complete</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="e">
|
||||||
|
<ask>Any additional guidance before I begin? (patterns, files, constraints) Or "go" to start.</ask>
|
||||||
|
<goto>step_2</goto>
|
||||||
|
</check>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="level 1 or 2 or couldn't determine level">
|
||||||
|
<ask>This looks like a focused feature with multiple components.
|
||||||
|
|
||||||
|
**[t] Create tech-spec first** (recommended)
|
||||||
|
**[w] Seems bigger than quick-dev** — see what BMad Method recommends (workflow-init)
|
||||||
|
**[e] Execute directly**</ask>
|
||||||
|
|
||||||
|
<check if="t">
|
||||||
|
<action>Load and execute {create_tech_spec_workflow}</action>
|
||||||
|
<action>Continue to implementation after spec complete</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="w">
|
||||||
|
<action>Load and execute {workflow_init}</action>
|
||||||
|
<action>EXIT quick-dev - user has been routed to BMad Method</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="e">
|
||||||
|
<ask>Any additional guidance before I begin? (patterns, files, constraints) Or "go" to start.</ask>
|
||||||
|
<goto>step_2</goto>
|
||||||
|
</check>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Level 3+: BMad Method territory, recommend workflow-init -->
|
||||||
|
<check if="level 3 or higher">
|
||||||
|
<ask>This sounds like platform/system work.
|
||||||
|
|
||||||
|
**[w] Start BMad Method** (recommended) (workflow-init)
|
||||||
|
**[t] Create tech-spec** (lighter planning)
|
||||||
|
**[e] Execute directly** - feeling lucky</ask>
|
||||||
|
|
||||||
|
<check if="w">
|
||||||
|
<action>Load and execute {workflow_init}</action>
|
||||||
|
<action>EXIT quick-dev - user has been routed to BMad Method</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="t">
|
||||||
|
<action>Load and execute {create_tech_spec_workflow}</action>
|
||||||
|
<action>Continue to implementation after spec complete</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="e">
|
||||||
|
<ask>Any additional guidance before I begin? (patterns, files, constraints) Or "go" to start.</ask>
|
||||||
|
<goto>step_2</goto>
|
||||||
|
</check>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
</check>
|
||||||
|
|
||||||
</check>
|
</check>
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
|
||||||
|
|
@ -25,5 +25,9 @@ create_tech_spec_workflow: "{project-root}/{bmad_folder}/bmm/workflows/bmad-quic
|
||||||
party_mode_exec: "{project-root}/{bmad_folder}/core/workflows/party-mode/workflow.md"
|
party_mode_exec: "{project-root}/{bmad_folder}/core/workflows/party-mode/workflow.md"
|
||||||
advanced_elicitation: "{project-root}/{bmad_folder}/core/tasks/advanced-elicitation.xml"
|
advanced_elicitation: "{project-root}/{bmad_folder}/core/tasks/advanced-elicitation.xml"
|
||||||
|
|
||||||
|
# Routing resources (lazy-loaded)
|
||||||
|
project_levels: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/project-levels.yaml"
|
||||||
|
workflow_init: "{project-root}/{bmad_folder}/bmm/workflows/workflow-status/init/workflow.yaml"
|
||||||
|
|
||||||
standalone: true
|
standalone: true
|
||||||
web_bundle: false
|
web_bundle: false
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kiro CLI setup handler for BMad Method
|
||||||
|
*/
|
||||||
|
class KiroCliSetup extends BaseIdeSetup {
|
||||||
|
constructor() {
|
||||||
|
super('kiro-cli', 'Kiro CLI', false);
|
||||||
|
this.configDir = '.kiro';
|
||||||
|
this.agentsDir = 'agents';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup old BMAD installation before reinstalling
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const bmadAgentsDir = path.join(projectDir, this.configDir, this.agentsDir);
|
||||||
|
|
||||||
|
if (await fs.pathExists(bmadAgentsDir)) {
|
||||||
|
// Remove existing BMad agents
|
||||||
|
const files = await fs.readdir(bmadAgentsDir);
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.startsWith('bmad-') || file.includes('bmad')) {
|
||||||
|
await fs.remove(path.join(bmadAgentsDir, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(chalk.dim(` Cleaned old BMAD agents from ${this.name}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Kiro CLI configuration with BMad agents
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} bmadDir - BMAD installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, bmadDir, options = {}) {
|
||||||
|
console.log(chalk.cyan(`Setting up ${this.name}...`));
|
||||||
|
|
||||||
|
await this.cleanup(projectDir);
|
||||||
|
|
||||||
|
const kiroDir = path.join(projectDir, this.configDir);
|
||||||
|
const agentsDir = path.join(kiroDir, this.agentsDir);
|
||||||
|
|
||||||
|
await this.ensureDir(agentsDir);
|
||||||
|
|
||||||
|
// Create BMad agents from source YAML files
|
||||||
|
await this.createBmadAgentsFromSource(agentsDir, projectDir);
|
||||||
|
|
||||||
|
console.log(chalk.green(`✓ ${this.name} configured with BMad agents`));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create BMad agent definitions from source YAML files
|
||||||
|
* @param {string} agentsDir - Agents directory
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async createBmadAgentsFromSource(agentsDir, projectDir) {
|
||||||
|
const sourceDir = path.join(__dirname, '../../../../../src/modules');
|
||||||
|
|
||||||
|
// Find all agent YAML files
|
||||||
|
const agentFiles = await this.findAgentFiles(sourceDir);
|
||||||
|
|
||||||
|
for (const agentFile of agentFiles) {
|
||||||
|
try {
|
||||||
|
await this.processAgentFile(agentFile, agentsDir, projectDir);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(chalk.yellow(`⚠️ Failed to process ${agentFile}: ${error.message}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all agent YAML files in modules and core
|
||||||
|
* @param {string} sourceDir - Source modules directory
|
||||||
|
* @returns {Array} Array of agent file paths
|
||||||
|
*/
|
||||||
|
async findAgentFiles(sourceDir) {
|
||||||
|
const agentFiles = [];
|
||||||
|
|
||||||
|
// Check core agents
|
||||||
|
const coreAgentsDir = path.join(__dirname, '../../../../../src/core/agents');
|
||||||
|
if (await fs.pathExists(coreAgentsDir)) {
|
||||||
|
const files = await fs.readdir(coreAgentsDir);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.endsWith('.agent.yaml')) {
|
||||||
|
agentFiles.push(path.join(coreAgentsDir, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check module agents
|
||||||
|
if (!(await fs.pathExists(sourceDir))) {
|
||||||
|
return agentFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules = await fs.readdir(sourceDir);
|
||||||
|
|
||||||
|
for (const module of modules) {
|
||||||
|
const moduleAgentsDir = path.join(sourceDir, module, 'agents');
|
||||||
|
|
||||||
|
if (await fs.pathExists(moduleAgentsDir)) {
|
||||||
|
const files = await fs.readdir(moduleAgentsDir);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.endsWith('.agent.yaml')) {
|
||||||
|
agentFiles.push(path.join(moduleAgentsDir, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate BMad Core compliance
|
||||||
|
* @param {Object} agentData - Agent YAML data
|
||||||
|
* @returns {boolean} True if compliant
|
||||||
|
*/
|
||||||
|
validateBmadCompliance(agentData) {
|
||||||
|
const requiredFields = ['agent.metadata.id', 'agent.persona.role', 'agent.persona.principles'];
|
||||||
|
|
||||||
|
for (const field of requiredFields) {
|
||||||
|
const keys = field.split('.');
|
||||||
|
let current = agentData;
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (!current || !current[key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
current = current[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process individual agent YAML file
|
||||||
|
* @param {string} agentFile - Path to agent YAML file
|
||||||
|
* @param {string} agentsDir - Target agents directory
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async processAgentFile(agentFile, agentsDir, projectDir) {
|
||||||
|
const yamlContent = await fs.readFile(agentFile, 'utf8');
|
||||||
|
const agentData = yaml.load(yamlContent);
|
||||||
|
|
||||||
|
if (!this.validateBmadCompliance(agentData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract agent name from ID path (e.g., "{bmad_folder}/bmm/agents/analyst.md" -> "analyst")
|
||||||
|
const idPath = agentData.agent.metadata.id;
|
||||||
|
const basename = path.basename(idPath, '.md');
|
||||||
|
const agentName = basename.startsWith('bmad-') ? basename : `bmad-${basename}`;
|
||||||
|
|
||||||
|
// Create JSON definition
|
||||||
|
await this.createAgentDefinitionFromYaml(agentsDir, agentName, agentData);
|
||||||
|
|
||||||
|
// Create prompt file
|
||||||
|
await this.createAgentPromptFromYaml(agentsDir, agentName, agentData, projectDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize agent name for file naming
|
||||||
|
* @param {string} name - Agent name
|
||||||
|
* @returns {string} Sanitized name
|
||||||
|
*/
|
||||||
|
sanitizeAgentName(name) {
|
||||||
|
return name
|
||||||
|
.toLowerCase()
|
||||||
|
.replaceAll(/\s+/g, '-')
|
||||||
|
.replaceAll(/[^a-z0-9-]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create agent JSON definition from YAML data
|
||||||
|
* @param {string} agentsDir - Agents directory
|
||||||
|
* @param {string} agentName - Agent name (role-based)
|
||||||
|
* @param {Object} agentData - Agent YAML data
|
||||||
|
*/
|
||||||
|
async createAgentDefinitionFromYaml(agentsDir, agentName, agentData) {
|
||||||
|
const personName = agentData.agent.metadata.name;
|
||||||
|
const role = agentData.agent.persona.role;
|
||||||
|
|
||||||
|
const agentConfig = {
|
||||||
|
name: agentName,
|
||||||
|
description: `${personName} - ${role}`,
|
||||||
|
prompt: `file://./${agentName}-prompt.md`,
|
||||||
|
tools: ['*'],
|
||||||
|
mcpServers: {},
|
||||||
|
useLegacyMcpJson: true,
|
||||||
|
resources: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const agentPath = path.join(agentsDir, `${agentName}.json`);
|
||||||
|
await fs.writeJson(agentPath, agentConfig, { spaces: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create agent prompt from YAML data
|
||||||
|
* @param {string} agentsDir - Agents directory
|
||||||
|
* @param {string} agentName - Agent name (role-based)
|
||||||
|
* @param {Object} agentData - Agent YAML data
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async createAgentPromptFromYaml(agentsDir, agentName, agentData, projectDir) {
|
||||||
|
const promptPath = path.join(agentsDir, `${agentName}-prompt.md`);
|
||||||
|
|
||||||
|
// Generate prompt from YAML data
|
||||||
|
const prompt = this.generatePromptFromYaml(agentData);
|
||||||
|
await fs.writeFile(promptPath, prompt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate prompt content from YAML data
|
||||||
|
* @param {Object} agentData - Agent YAML data
|
||||||
|
* @returns {string} Generated prompt
|
||||||
|
*/
|
||||||
|
generatePromptFromYaml(agentData) {
|
||||||
|
const agent = agentData.agent;
|
||||||
|
const name = agent.metadata.name;
|
||||||
|
const icon = agent.metadata.icon || '🤖';
|
||||||
|
const role = agent.persona.role;
|
||||||
|
const identity = agent.persona.identity;
|
||||||
|
const style = agent.persona.communication_style;
|
||||||
|
const principles = agent.persona.principles;
|
||||||
|
|
||||||
|
let prompt = `# ${name} ${icon}\n\n`;
|
||||||
|
prompt += `## Role\n${role}\n\n`;
|
||||||
|
|
||||||
|
if (identity) {
|
||||||
|
prompt += `## Identity\n${identity}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style) {
|
||||||
|
prompt += `## Communication Style\n${style}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (principles) {
|
||||||
|
prompt += `## Principles\n`;
|
||||||
|
if (typeof principles === 'string') {
|
||||||
|
// Handle multi-line string principles
|
||||||
|
prompt += principles + '\n\n';
|
||||||
|
} else if (Array.isArray(principles)) {
|
||||||
|
// Handle array principles
|
||||||
|
for (const principle of principles) {
|
||||||
|
prompt += `- ${principle}\n`;
|
||||||
|
}
|
||||||
|
prompt += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add menu items if available
|
||||||
|
if (agent.menu && agent.menu.length > 0) {
|
||||||
|
prompt += `## Available Workflows\n`;
|
||||||
|
for (let i = 0; i < agent.menu.length; i++) {
|
||||||
|
const item = agent.menu[i];
|
||||||
|
prompt += `${i + 1}. **${item.trigger}**: ${item.description}\n`;
|
||||||
|
}
|
||||||
|
prompt += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt += `## Instructions\nYou are ${name}, part of the BMad Method. Follow your role and principles while assisting users with their development needs.\n`;
|
||||||
|
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Kiro CLI is available
|
||||||
|
* @returns {Promise<boolean>} True if available
|
||||||
|
*/
|
||||||
|
async isAvailable() {
|
||||||
|
try {
|
||||||
|
const { execSync } = require('node:child_process');
|
||||||
|
execSync('kiro-cli --version', { stdio: 'ignore' });
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get installation instructions
|
||||||
|
* @returns {string} Installation instructions
|
||||||
|
*/
|
||||||
|
getInstallInstructions() {
|
||||||
|
return `Install Kiro CLI:
|
||||||
|
curl -fsSL https://github.com/aws/kiro-cli/releases/latest/download/install.sh | bash
|
||||||
|
|
||||||
|
Or visit: https://github.com/aws/kiro-cli`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { KiroCliSetup };
|
||||||
Loading…
Reference in New Issue