Compare commits
20 Commits
66717da4b8
...
a6ae12c4d0
| Author | SHA1 | Date |
|---|---|---|
|
|
a6ae12c4d0 | |
|
|
ed0defbe08 | |
|
|
78646069ef | |
|
|
3bc485d0ed | |
|
|
0f5a9cf0dd | |
|
|
e2d9d35ce9 | |
|
|
82e6433b69 | |
|
|
be7e07cc1a | |
|
|
079f79aba5 | |
|
|
b4d7e1adef | |
|
|
6e9fe6c9a2 | |
|
|
d2d9010a8e | |
|
|
6d5a1084eb | |
|
|
978a93ed33 | |
|
|
f7311f89f8 | |
|
|
81817672af | |
|
|
4c6b492835 | |
|
|
c0a49bcafe | |
|
|
dcba8e5e59 | |
|
|
7509b0cbc2 |
|
|
@ -4,9 +4,10 @@ language: "en-US"
|
||||||
early_access: true
|
early_access: true
|
||||||
reviews:
|
reviews:
|
||||||
profile: chill
|
profile: chill
|
||||||
high_level_summary: true
|
high_level_summary: false # don't post summary until explicitly invoked
|
||||||
request_changes_workflow: false
|
request_changes_workflow: false
|
||||||
review_status: false
|
review_status: false
|
||||||
|
commit_status: false # don't set commit status until explicitly invoked
|
||||||
collapse_walkthrough: false
|
collapse_walkthrough: false
|
||||||
poem: false
|
poem: false
|
||||||
auto_review:
|
auto_review:
|
||||||
|
|
@ -33,4 +34,7 @@ reviews:
|
||||||
Flag any process.exit() without error message.
|
Flag any process.exit() without error message.
|
||||||
chat:
|
chat:
|
||||||
auto_reply: true # Response to mentions in comments, a la @coderabbit review
|
auto_reply: true # Response to mentions in comments, a la @coderabbit review
|
||||||
|
issue_enrichment:
|
||||||
|
auto_enrich:
|
||||||
|
enabled: false # don't auto-comment on issues
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "6.0.0-alpha.15",
|
"version": "6.0.0-alpha.16",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "6.0.0-alpha.15",
|
"version": "6.0.0-alpha.16",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kayvan/markdown-tree-parser": "^1.6.1",
|
"@kayvan/markdown-tree-parser": "^1.6.1",
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,8 @@
|
||||||
<tag>invoke-workflow xml tag → Execute another workflow with given inputs and the workflow.xml runner</tag>
|
<tag>invoke-workflow xml tag → Execute another workflow with given inputs and the workflow.xml runner</tag>
|
||||||
<tag>invoke-task xml tag → Execute specified task</tag>
|
<tag>invoke-task xml tag → Execute specified task</tag>
|
||||||
<tag>invoke-protocol name="protocol_name" xml tag → Execute reusable protocol from protocols section</tag>
|
<tag>invoke-protocol name="protocol_name" xml tag → Execute reusable protocol from protocols section</tag>
|
||||||
|
<tag>invoke-bash cmd="command" → Execute shell command, capture stdout/stderr, set {{bash_exit_code}}, {{bash_stdout}}, {{bash_stderr}}</tag>
|
||||||
|
<tag>set-var name="varname" value="..." → Set runtime variable {{varname}} to specified value (supports expressions)</tag>
|
||||||
<tag>goto step="x" → Jump to specified step</tag>
|
<tag>goto step="x" → Jump to specified step</tag>
|
||||||
</execute-tags>
|
</execute-tags>
|
||||||
</substep>
|
</substep>
|
||||||
|
|
@ -126,6 +128,8 @@
|
||||||
<tag>invoke-workflow - Call another workflow</tag>
|
<tag>invoke-workflow - Call another workflow</tag>
|
||||||
<tag>invoke-task - Call a task</tag>
|
<tag>invoke-task - Call a task</tag>
|
||||||
<tag>invoke-protocol - Execute a reusable protocol (e.g., discover_inputs)</tag>
|
<tag>invoke-protocol - Execute a reusable protocol (e.g., discover_inputs)</tag>
|
||||||
|
<tag>invoke-bash cmd="..." - Execute shell command, results in {{bash_exit_code}}, {{bash_stdout}}, {{bash_stderr}}</tag>
|
||||||
|
<tag>set-var name="..." value="..." - Set runtime variable dynamically</tag>
|
||||||
</execution>
|
</execution>
|
||||||
<output>
|
<output>
|
||||||
<tag>template-output - Save content checkpoint</tag>
|
<tag>template-output - Save content checkpoint</tag>
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ Review was saved to story file, but sprint-status.yaml may be out of sync.
|
||||||
<action>All action items are included in the standalone review report</action>
|
<action>All action items are included in the standalone review report</action>
|
||||||
<ask if="action items exist">Would you like me to create tracking items for these action items? (backlog/tasks)</ask>
|
<ask if="action items exist">Would you like me to create tracking items for these action items? (backlog/tasks)</ask>
|
||||||
<action if="user confirms">
|
<action if="user confirms">
|
||||||
If {{backlog_file}} does not exist, copy {installed_path}/backlog_template.md to {{backlog_file}} location.
|
If {{backlog_file}} does not exist, copy {installed_path}/backlog-template.md to {{backlog_file}} location.
|
||||||
Append a row per action item with Date={{date}}, Story="Ad-Hoc Review", Epic="N/A", Type, Severity, Owner (or "TBD"), Status="Open", Notes with file refs and context.
|
Append a row per action item with Date={{date}}, Story="Ad-Hoc Review", Epic="N/A", Type, Severity, Owner (or "TBD"), Status="Open", Notes with file refs and context.
|
||||||
</action>
|
</action>
|
||||||
</check>
|
</check>
|
||||||
|
|
@ -342,7 +342,7 @@ Review was saved to story file, but sprint-status.yaml may be out of sync.
|
||||||
Append under the story's "Tasks / Subtasks" a new subsection titled "Review Follow-ups (AI)", adding each item as an unchecked checkbox in imperative form, prefixed with "[AI-Review]" and severity. Example: "- [ ] [AI-Review][High] Add input validation on server route /api/x (AC #2)".
|
Append under the story's "Tasks / Subtasks" a new subsection titled "Review Follow-ups (AI)", adding each item as an unchecked checkbox in imperative form, prefixed with "[AI-Review]" and severity. Example: "- [ ] [AI-Review][High] Add input validation on server route /api/x (AC #2)".
|
||||||
</action>
|
</action>
|
||||||
<action>
|
<action>
|
||||||
If {{backlog_file}} does not exist, copy {installed_path}/backlog_template.md to {{backlog_file}} location.
|
If {{backlog_file}} does not exist, copy {installed_path}/backlog-template.md to {{backlog_file}} location.
|
||||||
Append a row per action item with Date={{date}}, Story={{epic_num}}.{{story_num}}, Epic={{epic_num}}, Type, Severity, Owner (or "TBD"), Status="Open", Notes with short context and file refs.
|
Append a row per action item with Date={{date}}, Story={{epic_num}}.{{story_num}}, Epic={{epic_num}}, Type, Severity, Owner (or "TBD"), Status="Open", Notes with short context and file refs.
|
||||||
</action>
|
</action>
|
||||||
<action>
|
<action>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ agent:
|
||||||
|
|
||||||
critical_actions:
|
critical_actions:
|
||||||
- "READ the entire story file BEFORE any implementation - tasks/subtasks sequence is your authoritative implementation guide"
|
- "READ the entire story file BEFORE any implementation - tasks/subtasks sequence is your authoritative implementation guide"
|
||||||
- "Load project_context.md if available for coding standards only - never let it override story requirements"
|
- "Load project-context.md if available for coding standards only - never let it override story requirements"
|
||||||
- "Execute tasks/subtasks IN ORDER as written in story file - no skipping, no reordering, no doing what you want"
|
- "Execute tasks/subtasks IN ORDER as written in story file - no skipping, no reordering, no doing what you want"
|
||||||
- "For each task/subtask: follow red-green-refactor cycle - write failing test first, then implementation"
|
- "For each task/subtask: follow red-green-refactor cycle - write failing test first, then implementation"
|
||||||
- "Mark task/subtask [x] ONLY when both implementation AND tests are complete and passing"
|
- "Mark task/subtask [x] ONLY when both implementation AND tests are complete and passing"
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,17 @@ graph TB
|
||||||
subgraph Phase3["<b>Phase 3: SOLUTIONING</b>"]
|
subgraph Phase3["<b>Phase 3: SOLUTIONING</b>"]
|
||||||
Architecture["<b>Architect: *architecture</b>"]
|
Architecture["<b>Architect: *architecture</b>"]
|
||||||
EpicsStories["<b>PM/Architect: *create-epics-and-stories</b>"]
|
EpicsStories["<b>PM/Architect: *create-epics-and-stories</b>"]
|
||||||
|
TestDesignSys["<b>TEA: *test-design (system-level)</b>"]
|
||||||
Framework["<b>TEA: *framework</b>"]
|
Framework["<b>TEA: *framework</b>"]
|
||||||
CI["<b>TEA: *ci</b>"]
|
CI["<b>TEA: *ci</b>"]
|
||||||
GateCheck["<b>Architect: *implementation-readiness</b>"]
|
GateCheck["<b>Architect: *implementation-readiness</b>"]
|
||||||
Architecture --> EpicsStories
|
Architecture --> EpicsStories
|
||||||
|
Architecture --> TestDesignSys
|
||||||
|
TestDesignSys --> Framework
|
||||||
EpicsStories --> Framework
|
EpicsStories --> Framework
|
||||||
Framework --> CI
|
Framework --> CI
|
||||||
CI --> GateCheck
|
CI --> GateCheck
|
||||||
Phase3Note["<b>Epics created AFTER architecture,</b><br/><b>then test infrastructure setup</b>"]
|
Phase3Note["<b>Epics created AFTER architecture,</b><br/><b>then system-level test design and test infrastructure setup</b>"]
|
||||||
EpicsStories -.-> Phase3Note
|
EpicsStories -.-> Phase3Note
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -93,12 +96,17 @@ graph TB
|
||||||
- **Documentation** (Optional for brownfield): Prerequisite using `*document-project`
|
- **Documentation** (Optional for brownfield): Prerequisite using `*document-project`
|
||||||
- **Phase 1** (Optional): Discovery/Analysis (`*brainstorm`, `*research`, `*product-brief`)
|
- **Phase 1** (Optional): Discovery/Analysis (`*brainstorm`, `*research`, `*product-brief`)
|
||||||
- **Phase 2** (Required): Planning (`*prd` creates PRD with FRs/NFRs)
|
- **Phase 2** (Required): Planning (`*prd` creates PRD with FRs/NFRs)
|
||||||
- **Phase 3** (Track-dependent): Solutioning (`*architecture` → `*create-epics-and-stories` → TEA: `*framework`, `*ci` → `*implementation-readiness`)
|
- **Phase 3** (Track-dependent): Solutioning (`*architecture` → `*test-design` (system-level) → `*create-epics-and-stories` → TEA: `*framework`, `*ci` → `*implementation-readiness`)
|
||||||
- **Phase 4** (Required): Implementation (`*sprint-planning` → per-epic: `*test-design` → per-story: dev workflows)
|
- **Phase 4** (Required): Implementation (`*sprint-planning` → per-epic: `*test-design` → per-story: dev workflows)
|
||||||
|
|
||||||
**TEA workflows:** `*framework` and `*ci` run once in Phase 3 after architecture. `*test-design` runs per-epic in Phase 4. Output: `test-design-epic-N.md`.
|
**TEA workflows:** `*framework` and `*ci` run once in Phase 3 after architecture. `*test-design` is **dual-mode**:
|
||||||
|
|
||||||
Quick Flow track skips Phase 1 and 3. BMad Method and Enterprise use all phases based on project needs.
|
- **System-level (Phase 3):** Run immediately after architecture/ADR drafting to produce `test-design-system.md` (testability review, ADR → test mapping, Architecturally Significant Requirements (ASRs), environment needs). Feeds the implementation-readiness gate.
|
||||||
|
- **Epic-level (Phase 4):** Run per-epic to produce `test-design-epic-N.md` (risk, priorities, coverage plan).
|
||||||
|
|
||||||
|
Quick Flow track skips Phases 1 and 3.
|
||||||
|
BMad Method and Enterprise use all phases based on project needs.
|
||||||
|
When an ADR or architecture draft is produced, run `*test-design` in **system-level** mode before the implementation-readiness gate. This ensures the ADR has an attached testability review and ADR → test mapping. Keep the test-design updated if ADRs change.
|
||||||
|
|
||||||
### Why TEA is Different from Other BMM Agents
|
### Why TEA is Different from Other BMM Agents
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,3 +51,23 @@ tea_use_playwright_utils:
|
||||||
- "You must install packages yourself, or use test architect's *framework command."
|
- "You must install packages yourself, or use test architect's *framework command."
|
||||||
default: false
|
default: false
|
||||||
result: "{value}"
|
result: "{value}"
|
||||||
|
|
||||||
|
# External Code Review Agent Selection
|
||||||
|
# Allows delegating code reviews to an external AI agent CLI for independent, unbiased reviews
|
||||||
|
# Useful when using a different AI as primary IDE agent (e.g., Codex/Gemini users can use Claude for reviews)
|
||||||
|
external_review_agent:
|
||||||
|
prompt:
|
||||||
|
- "Which external agent should perform code reviews?"
|
||||||
|
- "External agents provide independent, unbiased reviews separate from your primary IDE agent."
|
||||||
|
- "The selected CLI must be installed and configured on your system."
|
||||||
|
default: "none"
|
||||||
|
result: "{value}"
|
||||||
|
single-select:
|
||||||
|
- value: "codex"
|
||||||
|
label: "Codex (OpenAI) - Code review using OpenAI Codex CLI"
|
||||||
|
- value: "gemini"
|
||||||
|
label: "Gemini (Google) - Code review using Google Gemini CLI"
|
||||||
|
- value: "claude"
|
||||||
|
label: "Claude Code (Anthropic) - Code review using Claude Code CLI"
|
||||||
|
- value: "none"
|
||||||
|
label: "None - Use built-in review (no external agent)"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
name: create-prd
|
name: create-prd
|
||||||
description: Creates a comprehensive PRDs through collaborative step-by-step discovery between two product managers working as peers.
|
description: Creates a comprehensive PRD through collaborative step-by-step discovery between two product managers working as peers.
|
||||||
main_config: '{project-root}/.bmad/bmm/config.yaml'
|
main_config: '{project-root}/.bmad/bmm/config.yaml'
|
||||||
web_bundle: true
|
web_bundle: true
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ Discover and load context documents using smart discovery:
|
||||||
|
|
||||||
**Project Context Rules (Critical for AI Agents):**
|
**Project Context Rules (Critical for AI Agents):**
|
||||||
|
|
||||||
1. Check for project context file: `**/project_context.md`
|
1. Check for project context file: `**/project-context.md`
|
||||||
2. If exists: Load COMPLETE file contents - this contains critical rules for AI agents
|
2. If exists: Load COMPLETE file contents - this contains critical rules for AI agents
|
||||||
3. Add to frontmatter `hasProjectContext: true` and track file path
|
3. Add to frontmatter `hasProjectContext: true` and track file path
|
||||||
4. Report to user: "Found existing project context with {number_of_rules} agent rules"
|
4. Report to user: "Found existing project context with {number_of_rules} agent rules"
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ Your architecture will ensure consistent, high-quality implementation across all
|
||||||
|
|
||||||
**💡 Optional Enhancement: Project Context File**
|
**💡 Optional Enhancement: Project Context File**
|
||||||
|
|
||||||
Would you like to create a `project_context.md` file? This is a concise, optimized guide for AI agents that captures:
|
Would you like to create a `project-context.md` file? This is a concise, optimized guide for AI agents that captures:
|
||||||
|
|
||||||
- Critical language and framework rules they might miss
|
- Critical language and framework rules they might miss
|
||||||
- Specific patterns and conventions for your project
|
- Specific patterns and conventions for your project
|
||||||
|
|
@ -310,7 +310,7 @@ This will help ensure consistent implementation by capturing:
|
||||||
- Testing and quality standards
|
- Testing and quality standards
|
||||||
- Anti-patterns to avoid
|
- Anti-patterns to avoid
|
||||||
|
|
||||||
The workflow will collaborate with you to create an optimized `project_context.md` file that AI agents will read before implementing any code."
|
The workflow will collaborate with you to create an optimized `project-context.md` file that AI agents will read before implementing any code."
|
||||||
|
|
||||||
**Execute the Generate Project Context workflow:**
|
**Execute the Generate Project Context workflow:**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# Senior Developer Review - Validation Checklist
|
# Senior Developer Review - Validation Checklist
|
||||||
|
|
||||||
|
## Story Setup
|
||||||
|
|
||||||
- [ ] Story file loaded from `{{story_path}}`
|
- [ ] Story file loaded from `{{story_path}}`
|
||||||
- [ ] Story Status verified as reviewable (review)
|
- [ ] Story Status verified as reviewable (review)
|
||||||
- [ ] Epic and Story IDs resolved ({{epic_num}}.{{story_num}})
|
- [ ] Epic and Story IDs resolved ({{epic_num}}.{{story_num}})
|
||||||
|
|
@ -7,12 +9,33 @@
|
||||||
- [ ] Epic Tech Spec located or warning recorded
|
- [ ] Epic Tech Spec located or warning recorded
|
||||||
- [ ] Architecture/standards docs loaded (as available)
|
- [ ] Architecture/standards docs loaded (as available)
|
||||||
- [ ] Tech stack detected and documented
|
- [ ] Tech stack detected and documented
|
||||||
- [ ] MCP doc search performed (or web fallback) and references captured
|
|
||||||
|
## External Agent Detection (Runtime)
|
||||||
|
|
||||||
|
- [ ] `invoke-bash cmd="command -v codex"` executed → {{codex_available}}
|
||||||
|
- [ ] `invoke-bash cmd="command -v gemini"` executed → {{gemini_available}}
|
||||||
|
- [ ] `invoke-bash cmd="command -v claude"` executed → {{claude_available}}
|
||||||
|
- [ ] Review method determined: {{use_external_agent}} = true/false
|
||||||
|
- [ ] If external: {{external_agent_cmd}} = codex OR gemini OR claude
|
||||||
|
- [ ] Config updated with detection results and timestamp
|
||||||
|
|
||||||
|
## Code Review Execution
|
||||||
|
|
||||||
|
- [ ] Git vs Story discrepancies identified ({{git_findings}})
|
||||||
|
- [ ] If external agent available: Prompt written to /tmp/code-review-prompt.txt
|
||||||
|
- [ ] If external agent available: CLI invoked via `invoke-bash` (MANDATORY - NO EXCEPTIONS)
|
||||||
|
- [ ] External agent output captured in {{bash_stdout}}
|
||||||
|
- [ ] If external agent CLI failed (non-zero exit): Fallback to built-in review
|
||||||
|
- [ ] ⚠️ VIOLATION CHECK: Did you skip external agent with a rationalization? If yes, RE-RUN with external agent.
|
||||||
- [ ] Acceptance Criteria cross-checked against implementation
|
- [ ] Acceptance Criteria cross-checked against implementation
|
||||||
- [ ] File List reviewed and validated for completeness
|
- [ ] File List reviewed and validated for completeness
|
||||||
- [ ] Tests identified and mapped to ACs; gaps noted
|
- [ ] Tests identified and mapped to ACs; gaps noted
|
||||||
- [ ] Code quality review performed on changed files
|
- [ ] Code quality review performed (security, performance, maintainability)
|
||||||
- [ ] Security review performed on changed files and dependencies
|
- [ ] Minimum 3 issues found (adversarial review requirement)
|
||||||
|
|
||||||
|
## Finalization
|
||||||
|
|
||||||
|
- [ ] Findings categorized: HIGH/MEDIUM/LOW severity
|
||||||
- [ ] Outcome decided (Approve/Changes Requested/Blocked)
|
- [ ] Outcome decided (Approve/Changes Requested/Blocked)
|
||||||
- [ ] Review notes appended under "Senior Developer Review (AI)"
|
- [ ] Review notes appended under "Senior Developer Review (AI)"
|
||||||
- [ ] Change Log updated with review entry
|
- [ ] Change Log updated with review entry
|
||||||
|
|
@ -21,3 +44,4 @@
|
||||||
- [ ] Story saved successfully
|
- [ ] Story saved successfully
|
||||||
|
|
||||||
_Reviewer: {{user_name}} on {{date}}_
|
_Reviewer: {{user_name}} on {{date}}_
|
||||||
|
_External Agent: {{external_agent_cmd}} (codex:{{codex_available}} / gemini:{{gemini_available}} / claude:{{claude_available}})_
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
You are an ADVERSARIAL code reviewer. Your job is to find problems, not approve code.
|
||||||
|
|
||||||
|
VERY IMPORTANT!
|
||||||
|
|
||||||
|
- This is a READ ONLY operation. You are not to change anything in this code.
|
||||||
|
- You are FORBIDDEN to write to any files.
|
||||||
|
- You are FORBIDDEN to change any files.
|
||||||
|
- You are FORBIDDEN to delete any files.
|
||||||
|
|
||||||
|
REQUIREMENTS:
|
||||||
|
|
||||||
|
- Find 3-10 specific issues minimum - no lazy looks good reviews
|
||||||
|
- Categorize as HIGH (must fix), MEDIUM (should fix), LOW (nice to fix)
|
||||||
|
- For each issue: specify file:line, describe problem, suggest fix
|
||||||
|
- Check: Security vulnerabilities, performance issues, error handling, test quality
|
||||||
|
- Verify: Tasks marked [x] are actually done, ACs are actually implemented
|
||||||
|
|
||||||
|
STORY CONTEXT: {{story_path}}
|
||||||
|
FILES TO REVIEW: {{comprehensive_file_list}}
|
||||||
|
ACCEPTANCE CRITERIA: {{acceptance_criteria_list}}
|
||||||
|
TASKS: {{task_list}}
|
||||||
|
|
||||||
|
OUTPUT FORMAT:
|
||||||
|
|
||||||
|
## HIGH SEVERITY
|
||||||
|
|
||||||
|
- [file:line] Issue description | Suggested fix
|
||||||
|
|
||||||
|
## MEDIUM SEVERITY
|
||||||
|
|
||||||
|
- [file:line] Issue description | Suggested fix
|
||||||
|
|
||||||
|
## LOW SEVERITY
|
||||||
|
|
||||||
|
- [file:line] Issue description | Suggested fix
|
||||||
|
|
@ -4,16 +4,56 @@
|
||||||
<critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical>
|
<critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical>
|
||||||
<critical>Generate all documents in {document_output_language}</critical>
|
<critical>Generate all documents in {document_output_language}</critical>
|
||||||
|
|
||||||
<critical>🔥 YOU ARE AN ADVERSARIAL CODE REVIEWER - Find what's wrong or missing! 🔥</critical>
|
<!-- ================================================================ -->
|
||||||
|
<!-- CLAUDE CODE CLEANUP - MANDATORY KILL OF ALL BACKGROUND SHELLS -->
|
||||||
|
<!-- ================================================================ -->
|
||||||
|
<!-- CRITICAL: Stale external agent processes cause context pollution.
|
||||||
|
You MUST kill ALL background shells BEFORE doing ANYTHING else.
|
||||||
|
DO NOT read their output. DO NOT check what they're doing.
|
||||||
|
Just KILL them unconditionally. -->
|
||||||
|
<critical>🚨 FIRST ACTION REQUIRED: Kill all background shells 🚨</critical>
|
||||||
|
<critical>Before ANY other workflow step, you MUST terminate all background processes.</critical>
|
||||||
|
<critical>DO NOT use BashOutput to read from stale shells - this causes context pollution.</critical>
|
||||||
|
<critical>DO NOT try to "check what they're doing" - just kill them.</critical>
|
||||||
|
|
||||||
|
<check if="BMAD is running as Claude Code (KillShell tool is available)">
|
||||||
|
<action>IMMEDIATELY use KillShell to terminate ALL background shells - no exceptions</action>
|
||||||
|
<action>Do NOT read output from these shells first - kill them blind</action>
|
||||||
|
<action>Do NOT rationalize keeping them alive - they contain stale data</action>
|
||||||
|
<action>If multiple shells exist, kill ALL of them one by one</action>
|
||||||
|
<output>🔪 Killed all background shells to prevent stale data contamination</output>
|
||||||
|
</check>
|
||||||
|
<critical>Only proceed to Step 1 AFTER all background shells are terminated.</critical>
|
||||||
|
|
||||||
|
<!-- ================================================================ -->
|
||||||
|
<!-- EXTERNAL AGENT MANDATE - THIS IS THE MOST IMPORTANT RULE -->
|
||||||
|
<!-- ================================================================ -->
|
||||||
|
<critical>🚨 MANDATORY EXTERNAL AGENT RULE - NO EXCEPTIONS 🚨</critical>
|
||||||
|
<critical>If an external agent CLI (codex, gemini, or claude) is detected as available, you MUST delegate the code review to that agent.</critical>
|
||||||
|
<critical>You are FORBIDDEN from performing your own code review analysis if an external agent is available.</critical>
|
||||||
|
<critical>The external agent provides an independent, unbiased review. Your job is to INVOKE it, not replace it.</critical>
|
||||||
|
<critical>Only perform built-in review if ALL external agents fail detection OR the CLI invocation actually fails with a non-zero exit code.</critical>
|
||||||
|
<critical>DO NOT SKIP the invoke-bash commands for detection and invocation - they are MANDATORY.</critical>
|
||||||
|
|
||||||
|
<!-- PROHIBITED EXCUSES - DO NOT USE THESE TO SKIP EXTERNAL AGENT -->
|
||||||
|
<critical>🚫 PROHIBITED RATIONALIZATIONS - You may NOT skip the external agent for ANY of these reasons:</critical>
|
||||||
|
<critical>❌ "The prompt is too long" - Long prompts are expected and supported. Invoke anyway.</critical>
|
||||||
|
<critical>❌ "CLI is meant for simple operations" - FALSE. The CLI handles complex prompts. Invoke anyway.</critical>
|
||||||
|
<critical>❌ "This is a re-review" - Re-reviews MUST use external agent. No exception.</critical>
|
||||||
|
<critical>❌ "I can do this myself" - You are FORBIDDEN from self-review when external agent is available.</critical>
|
||||||
|
<critical>❌ "It would be faster/better if I do it" - Irrelevant. External agent is MANDATORY.</critical>
|
||||||
|
<critical>❌ "The context is too complex" - The external agent handles complexity. Invoke anyway.</critical>
|
||||||
|
<critical>If you find yourself rationalizing why to skip the external agent, STOP and invoke it anyway.</critical>
|
||||||
|
|
||||||
|
<critical>🔥 ADVERSARIAL CODE REVIEW REQUIREMENTS 🔥</critical>
|
||||||
<critical>Your purpose: Validate story file claims against actual implementation</critical>
|
<critical>Your purpose: Validate story file claims against actual implementation</critical>
|
||||||
<critical>Challenge everything: Are tasks marked [x] actually done? Are ACs really implemented?</critical>
|
<critical>Challenge everything: Are tasks marked [x] actually done? Are ACs really implemented?</critical>
|
||||||
<critical>Find 3-10 specific issues in every review minimum - no lazy "looks good" reviews - YOU are so much better than the dev agent
|
<critical>Find 3-10 specific issues in every review minimum - no lazy "looks good" reviews</critical>
|
||||||
that wrote this slop</critical>
|
|
||||||
<critical>Read EVERY file in the File List - verify implementation against story requirements</critical>
|
<critical>Read EVERY file in the File List - verify implementation against story requirements</critical>
|
||||||
<critical>Tasks marked complete but not done = CRITICAL finding</critical>
|
<critical>Tasks marked complete but not done = CRITICAL finding</critical>
|
||||||
<critical>Acceptance Criteria not implemented = HIGH severity finding</critical>
|
<critical>Acceptance Criteria not implemented = HIGH severity finding</critical>
|
||||||
|
|
||||||
<step n="1" goal="Load story and discover changes">
|
<step n="1" goal="Load story and detect external agents">
|
||||||
<action>Use provided {{story_path}} or ask user which story file to review</action>
|
<action>Use provided {{story_path}} or ask user which story file to review</action>
|
||||||
<action>Read COMPLETE story file</action>
|
<action>Read COMPLETE story file</action>
|
||||||
<action>Set {{story_key}} = extracted key from filename (e.g., "1-2-user-authentication.md" → "1-2-user-authentication") or story metadata</action>
|
<action>Set {{story_key}} = extracted key from filename (e.g., "1-2-user-authentication.md" → "1-2-user-authentication") or story metadata</action>
|
||||||
|
|
@ -38,6 +78,86 @@
|
||||||
|
|
||||||
<invoke-protocol name="discover_inputs" />
|
<invoke-protocol name="discover_inputs" />
|
||||||
<action>Load {project_context} for coding standards (if exists)</action>
|
<action>Load {project_context} for coding standards (if exists)</action>
|
||||||
|
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<!-- EXTERNAL AGENT DETECTION - CHECK CONFIG FIRST, THEN DETECT -->
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<set-var name="use_external_agent" value="false" />
|
||||||
|
<set-var name="external_agent_cmd" value="" />
|
||||||
|
<set-var name="codex_available" value="false" />
|
||||||
|
<set-var name="gemini_available" value="false" />
|
||||||
|
<set-var name="claude_available" value="false" />
|
||||||
|
<set-var name="external_agent_failed" value="false" />
|
||||||
|
<set-var name="preferred_agent" value="{external_review_agent}" />
|
||||||
|
|
||||||
|
<!-- Check if user has disabled external agents -->
|
||||||
|
<check if="{{preferred_agent}} == 'none'">
|
||||||
|
<output>📋 External agent disabled in config - will use built-in adversarial review</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Only detect and use external agents if not set to "none" -->
|
||||||
|
<check if="{{preferred_agent}} != 'none'">
|
||||||
|
<output>🔍 Detecting external agent availability...</output>
|
||||||
|
|
||||||
|
<!-- Detect Codex CLI availability -->
|
||||||
|
<invoke-bash cmd="command -v codex && codex --version 2>/dev/null || echo 'NOT_FOUND'" />
|
||||||
|
<check if="{{bash_exit_code}} == 0 AND {{bash_stdout}} does not contain 'NOT_FOUND'">
|
||||||
|
<set-var name="codex_available" value="true" />
|
||||||
|
<output>✓ Codex CLI detected</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Detect Gemini CLI availability -->
|
||||||
|
<invoke-bash cmd="command -v gemini && gemini --version 2>/dev/null || echo 'NOT_FOUND'" />
|
||||||
|
<check if="{{bash_exit_code}} == 0 AND {{bash_stdout}} does not contain 'NOT_FOUND'">
|
||||||
|
<set-var name="gemini_available" value="true" />
|
||||||
|
<output>✓ Gemini CLI detected</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Detect Claude CLI availability -->
|
||||||
|
<invoke-bash cmd="command -v claude && claude --version 2>/dev/null || echo 'NOT_FOUND'" />
|
||||||
|
<check if="{{bash_exit_code}} == 0 AND {{bash_stdout}} does not contain 'NOT_FOUND'">
|
||||||
|
<set-var name="claude_available" value="true" />
|
||||||
|
<output>✓ Claude CLI detected</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Select which external agent to use based on availability and preference -->
|
||||||
|
<check if="{{preferred_agent}} == 'codex' AND {{codex_available}} == true">
|
||||||
|
<set-var name="use_external_agent" value="true" />
|
||||||
|
<set-var name="external_agent_cmd" value="codex" />
|
||||||
|
</check>
|
||||||
|
<check if="{{preferred_agent}} == 'gemini' AND {{gemini_available}} == true">
|
||||||
|
<set-var name="use_external_agent" value="true" />
|
||||||
|
<set-var name="external_agent_cmd" value="gemini" />
|
||||||
|
</check>
|
||||||
|
<check if="{{preferred_agent}} == 'claude' AND {{claude_available}} == true">
|
||||||
|
<set-var name="use_external_agent" value="true" />
|
||||||
|
<set-var name="external_agent_cmd" value="claude" />
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Fallback selection if preferred agent not available -->
|
||||||
|
<check if="{{use_external_agent}} == false AND {{codex_available}} == true">
|
||||||
|
<set-var name="use_external_agent" value="true" />
|
||||||
|
<set-var name="external_agent_cmd" value="codex" />
|
||||||
|
<output>⚠️ Preferred agent ({{preferred_agent}}) not available, falling back to Codex</output>
|
||||||
|
</check>
|
||||||
|
<check if="{{use_external_agent}} == false AND {{gemini_available}} == true">
|
||||||
|
<set-var name="use_external_agent" value="true" />
|
||||||
|
<set-var name="external_agent_cmd" value="gemini" />
|
||||||
|
<output>⚠️ Preferred agent ({{preferred_agent}}) not available, falling back to Gemini</output>
|
||||||
|
</check>
|
||||||
|
<check if="{{use_external_agent}} == false AND {{claude_available}} == true">
|
||||||
|
<set-var name="use_external_agent" value="true" />
|
||||||
|
<set-var name="external_agent_cmd" value="claude" />
|
||||||
|
<output>⚠️ Preferred agent ({{preferred_agent}}) not available, falling back to Claude</output>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="{{use_external_agent}} == true">
|
||||||
|
<output>🤖 External agent selected: {{external_agent_cmd}} - will delegate code review</output>
|
||||||
|
</check>
|
||||||
|
<check if="{{use_external_agent}} == false">
|
||||||
|
<output>📋 No external agent available - will use built-in adversarial review</output>
|
||||||
|
</check>
|
||||||
|
</check>
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step n="2" goal="Build review attack plan">
|
<step n="2" goal="Build review attack plan">
|
||||||
|
|
@ -56,15 +176,75 @@
|
||||||
<step n="3" goal="Execute adversarial review">
|
<step n="3" goal="Execute adversarial review">
|
||||||
<critical>VALIDATE EVERY CLAIM - Check git reality vs story claims</critical>
|
<critical>VALIDATE EVERY CLAIM - Check git reality vs story claims</critical>
|
||||||
|
|
||||||
<!-- Git vs Story Discrepancies -->
|
<!-- Git vs Story Discrepancies - ALWAYS runs -->
|
||||||
<action>Review git vs story File List discrepancies:
|
<action>Review git vs story File List discrepancies:
|
||||||
1. **Files changed but not in story File List** → MEDIUM finding (incomplete documentation)
|
1. **Files changed but not in story File List** → MEDIUM finding (incomplete documentation)
|
||||||
2. **Story lists files but no git changes** → HIGH finding (false claims)
|
2. **Story lists files but no git changes** → HIGH finding (false claims)
|
||||||
3. **Uncommitted changes not documented** → MEDIUM finding (transparency issue)
|
3. **Uncommitted changes not documented** → MEDIUM finding (transparency issue)
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
<!-- Use combined file list: story File List + git discovered files -->
|
|
||||||
<action>Create comprehensive review file list from story File List and git changes</action>
|
<action>Create comprehensive review file list from story File List and git changes</action>
|
||||||
|
<action>Store git discrepancy findings in {{git_findings}}</action>
|
||||||
|
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<!-- MANDATORY: INVOKE EXTERNAL AGENT IF AVAILABLE -->
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<critical>If {{use_external_agent}} == true, you MUST invoke the external agent via CLI.</critical>
|
||||||
|
<critical>DO NOT perform your own code review - delegate to the external agent.</critical>
|
||||||
|
|
||||||
|
<check if="{{use_external_agent}} == true">
|
||||||
|
<output>🔄 Invoking {{external_agent_cmd}} CLI for adversarial code review...</output>
|
||||||
|
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<!-- INVOKE EXTERNAL AGENT - USE EXACT COMMANDS AS WRITTEN -->
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<critical>🚨 USE EXACT COMMAND SYNTAX - DO NOT MODIFY OR SIMPLIFY 🚨</critical>
|
||||||
|
<critical>Copy the invoke-bash cmd attribute EXACTLY as written below.</critical>
|
||||||
|
<critical>DO NOT remove flags, reorder arguments, or "improve" the command.</critical>
|
||||||
|
|
||||||
|
<!-- External agent prompt is loaded from external-agent-prompt.md -->
|
||||||
|
<set-var name="external_prompt_file" value="{installed_path}/external-agent-prompt.md" />
|
||||||
|
<action>Load {{external_prompt_file}} content into {{external_prompt}}</action>
|
||||||
|
|
||||||
|
<check if="{{external_agent_cmd}} == 'codex'">
|
||||||
|
<critical>CODEX: Use codex exec with read-only sandbox and full-auto</critical>
|
||||||
|
<invoke-bash cmd="codex exec --sandbox read-only --full-auto "$(cat '{{external_prompt_file}}')"" timeout="300000" />
|
||||||
|
</check>
|
||||||
|
<check if="{{external_agent_cmd}} == 'gemini'">
|
||||||
|
<critical>GEMINI: Use gemini -p with prompt from file and --yolo</critical>
|
||||||
|
<invoke-bash cmd="gemini -p "$(cat '{{external_prompt_file}}')" --yolo" timeout="300000" />
|
||||||
|
</check>
|
||||||
|
<check if="{{external_agent_cmd}} == 'claude'">
|
||||||
|
<critical>CLAUDE: Use claude -p with prompt from file</critical>
|
||||||
|
<invoke-bash cmd="claude -p "$(cat '{{external_prompt_file}}')" --dangerously-skip-permissions" timeout="300000" />
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="{{bash_exit_code}} != 0 OR {{bash_stdout}} is empty">
|
||||||
|
<output>⚠️ External agent CLI failed (exit code: {{bash_exit_code}}), falling back to built-in review</output>
|
||||||
|
<output>Error: {{bash_stderr}}</output>
|
||||||
|
<set-var name="use_external_agent" value="false" />
|
||||||
|
<set-var name="external_agent_failed" value="true" />
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="{{bash_exit_code}} == 0 AND {{bash_stdout}} is not empty">
|
||||||
|
<set-var name="external_findings" value="{{bash_stdout}}" />
|
||||||
|
<action>Parse {{external_findings}} into structured HIGH/MEDIUM/LOW lists</action>
|
||||||
|
<action>Merge {{git_findings}} with {{external_findings}} into {{all_findings}}</action>
|
||||||
|
<output>✅ External review complete - {{external_agent_cmd}} CLI findings received</output>
|
||||||
|
</check>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Fallback to built-in if external agent failed -->
|
||||||
|
<check if="{{external_agent_failed}} == true">
|
||||||
|
<set-var name="use_external_agent" value="false" />
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<check if="{{use_external_agent}} == false">
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<!-- FALLBACK ONLY: Built-in Review (when NO external agent works) -->
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<critical>This section should ONLY execute if ALL external agents failed detection or invocation.</critical>
|
||||||
|
<critical>If you are here but an external agent was available, you have violated the workflow rules.</critical>
|
||||||
|
<output>⚠️ No external agent available - performing built-in adversarial review</output>
|
||||||
|
|
||||||
<!-- AC Validation -->
|
<!-- AC Validation -->
|
||||||
<action>For EACH Acceptance Criterion:
|
<action>For EACH Acceptance Criterion:
|
||||||
|
|
@ -91,6 +271,10 @@
|
||||||
5. **Test Quality**: Are tests real assertions or placeholders?
|
5. **Test Quality**: Are tests real assertions or placeholders?
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action>Merge {{git_findings}} with built-in findings into {{all_findings}}</action>
|
||||||
|
</check>
|
||||||
|
|
||||||
|
<!-- Minimum issue check - applies to both paths -->
|
||||||
<check if="total_issues_found lt 3">
|
<check if="total_issues_found lt 3">
|
||||||
<critical>NOT LOOKING HARD ENOUGH - Find more problems!</critical>
|
<critical>NOT LOOKING HARD ENOUGH - Find more problems!</critical>
|
||||||
<action>Re-examine code for:
|
<action>Re-examine code for:
|
||||||
|
|
@ -113,6 +297,7 @@
|
||||||
<output>**🔥 CODE REVIEW FINDINGS, {user_name}!**
|
<output>**🔥 CODE REVIEW FINDINGS, {user_name}!**
|
||||||
|
|
||||||
**Story:** {{story_file}}
|
**Story:** {{story_file}}
|
||||||
|
**Review Method:** {{external_agent_cmd}} OR built-in
|
||||||
**Git vs Story Discrepancies:** {{git_discrepancy_count}} found
|
**Git vs Story Discrepancies:** {{git_discrepancy_count}} found
|
||||||
**Issues Found:** {{high_count}} High, {{medium_count}} Medium, {{low_count}} Low
|
**Issues Found:** {{high_count}} High, {{medium_count}} Medium, {{low_count}} Low
|
||||||
|
|
||||||
|
|
@ -185,7 +370,7 @@
|
||||||
<action>Set {{current_sprint_status}} = "no-sprint-tracking"</action>
|
<action>Set {{current_sprint_status}} = "no-sprint-tracking"</action>
|
||||||
</check>
|
</check>
|
||||||
|
|
||||||
<!-- Sync sprint-status.yaml when story status changes (only if sprint tracking enabled) -->
|
<!-- Sync sprint-status.yaml when story status changes -->
|
||||||
<check if="{{current_sprint_status}} != 'no-sprint-tracking'">
|
<check if="{{current_sprint_status}} != 'no-sprint-tracking'">
|
||||||
<action>Load the FULL file: {sprint_status}</action>
|
<action>Load the FULL file: {sprint_status}</action>
|
||||||
<action>Find development_status key matching {{story_key}}</action>
|
<action>Find development_status key matching {{story_key}}</action>
|
||||||
|
|
@ -217,7 +402,7 @@
|
||||||
**Issues Fixed:** {{fixed_count}}
|
**Issues Fixed:** {{fixed_count}}
|
||||||
**Action Items Created:** {{action_count}}
|
**Action Items Created:** {{action_count}}
|
||||||
|
|
||||||
{{#if new_status == "done"}}Story is ready for next work!{{else}}Address the action items and continue development.{{/if}}
|
{{#if new_status == "done"}}Code review complete!{{else}}Address the action items and continue development.{{/if}}
|
||||||
</output>
|
</output>
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ sprint_status: "{sprint_artifacts}/sprint-status.yaml || {output_folder}/sprint-
|
||||||
installed_path: "{project-root}/.bmad/bmm/workflows/4-implementation/code-review"
|
installed_path: "{project-root}/.bmad/bmm/workflows/4-implementation/code-review"
|
||||||
instructions: "{installed_path}/instructions.xml"
|
instructions: "{installed_path}/instructions.xml"
|
||||||
validation: "{installed_path}/checklist.md"
|
validation: "{installed_path}/checklist.md"
|
||||||
|
external_agent_prompt: "{installed_path}/external-agent-prompt.md"
|
||||||
template: false
|
template: false
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -25,6 +26,11 @@ variables:
|
||||||
project_context: "**/project-context.md"
|
project_context: "**/project-context.md"
|
||||||
story_dir: "{sprint_artifacts}"
|
story_dir: "{sprint_artifacts}"
|
||||||
|
|
||||||
|
# External code review agent configuration
|
||||||
|
# User selects preferred agent during install; detection verifies availability at runtime
|
||||||
|
# Supported values: codex, gemini, claude, none
|
||||||
|
external_review_agent: "{config_source}:external_review_agent || 'none'"
|
||||||
|
|
||||||
# Smart input file references - handles both whole docs and sharded docs
|
# Smart input file references - handles both whole docs and sharded docs
|
||||||
# Priority: Whole document first, then sharded version
|
# Priority: Whole document first, then sharded version
|
||||||
# Strategy: SELECTIVE LOAD - only load the specific epic needed for this story review
|
# Strategy: SELECTIVE LOAD - only load the specific epic needed for this story review
|
||||||
|
|
@ -51,4 +57,3 @@ input_file_patterns:
|
||||||
load_strategy: "INDEX_GUIDED"
|
load_strategy: "INDEX_GUIDED"
|
||||||
|
|
||||||
standalone: true
|
standalone: true
|
||||||
web_bundle: false
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ validation-rules:
|
||||||
- [ ] **Acceptance Criteria Satisfaction:** Implementation satisfies EVERY Acceptance Criterion in the story
|
- [ ] **Acceptance Criteria Satisfaction:** Implementation satisfies EVERY Acceptance Criterion in the story
|
||||||
- [ ] **No Ambiguous Implementation:** Clear, unambiguous implementation that meets story requirements
|
- [ ] **No Ambiguous Implementation:** Clear, unambiguous implementation that meets story requirements
|
||||||
- [ ] **Edge Cases Handled:** Error conditions and edge cases appropriately addressed
|
- [ ] **Edge Cases Handled:** Error conditions and edge cases appropriately addressed
|
||||||
- [ ] **Dependencies Within Scope:** Only uses dependencies specified in story or project_context.md
|
- [ ] **Dependencies Within Scope:** Only uses dependencies specified in story or project-context.md
|
||||||
|
|
||||||
## 🧪 Testing & Quality Assurance
|
## 🧪 Testing & Quality Assurance
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ Discover the project's technology stack, existing patterns, and critical impleme
|
||||||
|
|
||||||
First, check if project context already exists:
|
First, check if project context already exists:
|
||||||
|
|
||||||
- Look for file at `{output_folder}/project_context.md`
|
- Look for file at `{output_folder}/project-context.md`
|
||||||
- If exists: Read complete file to understand existing rules
|
- If exists: Read complete file to understand existing rules
|
||||||
- Present to user: "Found existing project context with {number_of_sections} sections. Would you like to update this or create a new one?"
|
- Present to user: "Found existing project context with {number_of_sections} sections. Would you like to update this or create a new one?"
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ Based on discovery, create or update the context document:
|
||||||
|
|
||||||
#### A. Fresh Document Setup (if no existing context)
|
#### A. Fresh Document Setup (if no existing context)
|
||||||
|
|
||||||
Copy template from `{installed_path}/project-context-template.md` to `{output_folder}/project_context.md`
|
Copy template from `{installed_path}/project-context-template.md` to `{output_folder}/project-context.md`
|
||||||
Initialize frontmatter with:
|
Initialize frontmatter with:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ After each category, show the generated rules and present choices:
|
||||||
|
|
||||||
## APPEND TO PROJECT CONTEXT:
|
## APPEND TO PROJECT CONTEXT:
|
||||||
|
|
||||||
When user selects 'C' for a category, append the content directly to `{output_folder}/project_context.md` using the structure from step 8.
|
When user selects 'C' for a category, append the content directly to `{output_folder}/project-context.md` using the structure from step 8.
|
||||||
|
|
||||||
## SUCCESS METRICS:
|
## SUCCESS METRICS:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ Based on user skill level, present the completion:
|
||||||
**Expert Mode:**
|
**Expert Mode:**
|
||||||
"Project context complete. Optimized for LLM consumption with {{rule_count}} critical rules across {{section_count}} sections.
|
"Project context complete. Optimized for LLM consumption with {{rule_count}} critical rules across {{section_count}} sections.
|
||||||
|
|
||||||
File saved to: `{output_folder}/project_context.md`
|
File saved to: `{output_folder}/project-context.md`
|
||||||
|
|
||||||
Ready for AI agent integration."
|
Ready for AI agent integration."
|
||||||
|
|
||||||
|
|
@ -227,7 +227,7 @@ Present final completion to user:
|
||||||
"✅ **Project Context Generation Complete!**
|
"✅ **Project Context Generation Complete!**
|
||||||
|
|
||||||
Your optimized project context file is ready at:
|
Your optimized project context file is ready at:
|
||||||
`{output_folder}/project_context.md`
|
`{output_folder}/project-context.md`
|
||||||
|
|
||||||
**📊 Context Summary:**
|
**📊 Context Summary:**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
name: generate-project-context
|
name: generate-project-context
|
||||||
description: Creates a concise project_context.md file with critical rules and patterns that AI agents must follow when implementing code. Optimized for LLM context efficiency.
|
description: Creates a concise project-context.md file with critical rules and patterns that AI agents must follow when implementing code. Optimized for LLM context efficiency.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Generate Project Context Workflow
|
# Generate Project Context Workflow
|
||||||
|
|
||||||
**Goal:** Create a concise, optimized `project_context.md` file containing critical rules, patterns, and guidelines that AI agents must follow when implementing code. This file focuses on unobvious details that LLMs need to be reminded of.
|
**Goal:** Create a concise, optimized `project-context.md` file containing critical rules, patterns, and guidelines that AI agents must follow when implementing code. This file focuses on unobvious details that LLMs need to be reminded of.
|
||||||
|
|
||||||
**Your Role:** You are a technical facilitator working with a peer to capture the essential implementation rules that will ensure consistent, high-quality code generation across all AI agents working on the project.
|
**Your Role:** You are a technical facilitator working with a peer to capture the essential implementation rules that will ensure consistent, high-quality code generation across all AI agents working on the project.
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ Load config from `{project-root}/.bmad/bmm/config.yaml` and resolve:
|
||||||
|
|
||||||
- `installed_path` = `{project-root}/.bmad/bmm/workflows/generate-project-context`
|
- `installed_path` = `{project-root}/.bmad/bmm/workflows/generate-project-context`
|
||||||
- `template_path` = `{installed_path}/project-context-template.md`
|
- `template_path` = `{installed_path}/project-context-template.md`
|
||||||
- `output_file` = `{output_folder}/project_context.md`
|
- `output_file` = `{output_folder}/project-context.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,14 @@ variables:
|
||||||
|
|
||||||
# Output configuration
|
# Output configuration
|
||||||
# Note: Actual output file determined dynamically based on mode detection
|
# Note: Actual output file determined dynamically based on mode detection
|
||||||
# - System-Level (Phase 3): {output_folder}/test-design-system.md
|
# Declared outputs for new workflow format
|
||||||
# - Epic-Level (Phase 4): {output_folder}/test-design-epic-{epic_num}.md
|
outputs:
|
||||||
|
- id: system-level
|
||||||
|
description: "System-level testability review (Phase 3)"
|
||||||
|
path: "{output_folder}/test-design-system.md"
|
||||||
|
- id: epic-level
|
||||||
|
description: "Epic-level test plan (Phase 4)"
|
||||||
|
path: "{output_folder}/test-design-epic-{epic_num}.md"
|
||||||
default_output_file: "{output_folder}/test-design-epic-{epic_num}.md"
|
default_output_file: "{output_folder}/test-design-epic-{epic_num}.md"
|
||||||
|
|
||||||
# Required tools
|
# Required tools
|
||||||
|
|
|
||||||
|
|
@ -248,14 +248,21 @@ class ConfigCollector {
|
||||||
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
||||||
const existingKeys = this.existingConfig && this.existingConfig[moduleName] ? Object.keys(this.existingConfig[moduleName]) : [];
|
const existingKeys = this.existingConfig && this.existingConfig[moduleName] ? Object.keys(this.existingConfig[moduleName]) : [];
|
||||||
|
|
||||||
|
// Find new interactive fields (with prompt)
|
||||||
const newKeys = configKeys.filter((key) => {
|
const newKeys = configKeys.filter((key) => {
|
||||||
const item = moduleConfig[key];
|
const item = moduleConfig[key];
|
||||||
// Check if it's a config item and doesn't exist in existing config
|
// Check if it's a config item and doesn't exist in existing config
|
||||||
return item && typeof item === 'object' && item.prompt && !existingKeys.includes(key);
|
return item && typeof item === 'object' && item.prompt && !existingKeys.includes(key);
|
||||||
});
|
});
|
||||||
|
|
||||||
// If in silent mode and no new keys, use existing config and skip prompts
|
// Find new static fields (without prompt, just result)
|
||||||
if (silentMode && newKeys.length === 0) {
|
const newStaticKeys = configKeys.filter((key) => {
|
||||||
|
const item = moduleConfig[key];
|
||||||
|
return item && typeof item === 'object' && !item.prompt && item.result && !existingKeys.includes(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If in silent mode and no new keys (neither interactive nor static), use existing config and skip prompts
|
||||||
|
if (silentMode && newKeys.length === 0 && newStaticKeys.length === 0) {
|
||||||
if (this.existingConfig && this.existingConfig[moduleName]) {
|
if (this.existingConfig && this.existingConfig[moduleName]) {
|
||||||
if (!this.collectedConfig[moduleName]) {
|
if (!this.collectedConfig[moduleName]) {
|
||||||
this.collectedConfig[moduleName] = {};
|
this.collectedConfig[moduleName] = {};
|
||||||
|
|
@ -294,9 +301,12 @@ class ConfigCollector {
|
||||||
return false; // No new fields
|
return false; // No new fields
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have new fields, build questions first
|
// If we have new fields (interactive or static), process them
|
||||||
if (newKeys.length > 0) {
|
if (newKeys.length > 0 || newStaticKeys.length > 0) {
|
||||||
const questions = [];
|
const questions = [];
|
||||||
|
const staticAnswers = {};
|
||||||
|
|
||||||
|
// Build questions for interactive fields
|
||||||
for (const key of newKeys) {
|
for (const key of newKeys) {
|
||||||
const item = moduleConfig[key];
|
const item = moduleConfig[key];
|
||||||
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
|
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
|
||||||
|
|
@ -305,20 +315,35 @@ class ConfigCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare static answers (no prompt, just result)
|
||||||
|
for (const key of newStaticKeys) {
|
||||||
|
staticAnswers[`${moduleName}_${key}`] = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all answers (static + prompted)
|
||||||
|
let allAnswers = { ...staticAnswers };
|
||||||
|
|
||||||
if (questions.length > 0) {
|
if (questions.length > 0) {
|
||||||
// Only show header if we actually have questions
|
// Only show header if we actually have questions
|
||||||
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||||
console.log(); // Line break before questions
|
console.log(); // Line break before questions
|
||||||
const answers = await inquirer.prompt(questions);
|
const promptedAnswers = await inquirer.prompt(questions);
|
||||||
|
|
||||||
// Store answers for cross-referencing
|
// Merge prompted answers with static answers
|
||||||
Object.assign(this.allAnswers, answers);
|
Object.assign(allAnswers, promptedAnswers);
|
||||||
|
} else if (newStaticKeys.length > 0) {
|
||||||
|
// Only static fields, no questions - show no config message
|
||||||
|
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||||
|
}
|
||||||
|
|
||||||
// Process answers and build result values
|
// Store all answers for cross-referencing
|
||||||
for (const key of Object.keys(answers)) {
|
Object.assign(this.allAnswers, allAnswers);
|
||||||
|
|
||||||
|
// Process all answers (both static and prompted)
|
||||||
|
for (const key of Object.keys(allAnswers)) {
|
||||||
const originalKey = key.replace(`${moduleName}_`, '');
|
const originalKey = key.replace(`${moduleName}_`, '');
|
||||||
const item = moduleConfig[originalKey];
|
const item = moduleConfig[originalKey];
|
||||||
const value = answers[key];
|
const value = allAnswers[key];
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
|
@ -334,10 +359,6 @@ class ConfigCollector {
|
||||||
}
|
}
|
||||||
this.collectedConfig[moduleName][originalKey] = result;
|
this.collectedConfig[moduleName][originalKey] = result;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// New keys exist but no questions generated - show no config message
|
|
||||||
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy over existing values for fields that weren't prompted
|
// Copy over existing values for fields that weren't prompted
|
||||||
|
|
@ -353,7 +374,7 @@ class ConfigCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newKeys.length > 0; // Return true if we prompted for new fields
|
return newKeys.length > 0 || newStaticKeys.length > 0; // Return true if we had any new fields (interactive or static)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -501,30 +522,52 @@ class ConfigCollector {
|
||||||
|
|
||||||
// Process each config item
|
// Process each config item
|
||||||
const questions = [];
|
const questions = [];
|
||||||
|
const staticAnswers = {};
|
||||||
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
||||||
|
|
||||||
for (const key of configKeys) {
|
for (const key of configKeys) {
|
||||||
const item = moduleConfig[key];
|
const item = moduleConfig[key];
|
||||||
|
|
||||||
// Skip if not a config object
|
// Skip if not a config object
|
||||||
if (!item || typeof item !== 'object' || !item.prompt) {
|
if (!item || typeof item !== 'object') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle static values (no prompt, just result)
|
||||||
|
if (!item.prompt && item.result) {
|
||||||
|
// Add to static answers with a marker value
|
||||||
|
staticAnswers[`${moduleName}_${key}`] = undefined;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle interactive values (with prompt)
|
||||||
|
if (item.prompt) {
|
||||||
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
|
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
|
||||||
if (question) {
|
if (question) {
|
||||||
questions.push(question);
|
questions.push(question);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all answers (static + prompted)
|
||||||
|
let allAnswers = { ...staticAnswers };
|
||||||
|
|
||||||
// Display appropriate header based on whether there are questions
|
// Display appropriate header based on whether there are questions
|
||||||
if (questions.length > 0) {
|
if (questions.length > 0) {
|
||||||
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||||
console.log(); // Line break before questions
|
console.log(); // Line break before questions
|
||||||
const answers = await inquirer.prompt(questions);
|
const promptedAnswers = await inquirer.prompt(questions);
|
||||||
|
|
||||||
// Store answers for cross-referencing
|
// Merge prompted answers with static answers
|
||||||
Object.assign(this.allAnswers, answers);
|
Object.assign(allAnswers, promptedAnswers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all answers for cross-referencing
|
||||||
|
Object.assign(this.allAnswers, allAnswers);
|
||||||
|
|
||||||
|
// Process all answers (both static and prompted)
|
||||||
|
if (Object.keys(allAnswers).length > 0) {
|
||||||
|
const answers = allAnswers;
|
||||||
|
|
||||||
// Process answers and build result values
|
// Process answers and build result values
|
||||||
for (const key of Object.keys(answers)) {
|
for (const key of Object.keys(answers)) {
|
||||||
|
|
|
||||||
|
|
@ -581,6 +581,11 @@ class ManifestGenerator {
|
||||||
*/
|
*/
|
||||||
async writeWorkflowManifest(cfgDir) {
|
async writeWorkflowManifest(cfgDir) {
|
||||||
const csvPath = path.join(cfgDir, 'workflow-manifest.csv');
|
const csvPath = path.join(cfgDir, 'workflow-manifest.csv');
|
||||||
|
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
|
||||||
|
const parseCsvLine = (line) => {
|
||||||
|
const columns = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || [];
|
||||||
|
return columns.map((c) => c.replaceAll(/^"|"$/g, ''));
|
||||||
|
};
|
||||||
|
|
||||||
// Read existing manifest to preserve entries
|
// Read existing manifest to preserve entries
|
||||||
const existingEntries = new Map();
|
const existingEntries = new Map();
|
||||||
|
|
@ -592,18 +597,21 @@ class ManifestGenerator {
|
||||||
for (let i = 1; i < lines.length; i++) {
|
for (let i = 1; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
if (line) {
|
if (line) {
|
||||||
// Parse CSV (simple parsing assuming no commas in quoted fields)
|
const parts = parseCsvLine(line);
|
||||||
const parts = line.split('","');
|
|
||||||
if (parts.length >= 4) {
|
if (parts.length >= 4) {
|
||||||
const name = parts[0].replace(/^"/, '');
|
const [name, description, module, workflowPath] = parts;
|
||||||
const module = parts[2];
|
existingEntries.set(`${module}:${name}`, {
|
||||||
existingEntries.set(`${module}:${name}`, line);
|
name,
|
||||||
|
description,
|
||||||
|
module,
|
||||||
|
path: workflowPath,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create CSV header - removed standalone column as ALL workflows now generate commands
|
// Create CSV header - standalone column removed, everything is canonicalized to 4 columns
|
||||||
let csv = 'name,description,module,path\n';
|
let csv = 'name,description,module,path\n';
|
||||||
|
|
||||||
// Combine existing and new workflows
|
// Combine existing and new workflows
|
||||||
|
|
@ -617,12 +625,18 @@ class ManifestGenerator {
|
||||||
// Add/update new workflows
|
// Add/update new workflows
|
||||||
for (const workflow of this.workflows) {
|
for (const workflow of this.workflows) {
|
||||||
const key = `${workflow.module}:${workflow.name}`;
|
const key = `${workflow.module}:${workflow.name}`;
|
||||||
allWorkflows.set(key, `"${workflow.name}","${workflow.description}","${workflow.module}","${workflow.path}"`);
|
allWorkflows.set(key, {
|
||||||
|
name: workflow.name,
|
||||||
|
description: workflow.description,
|
||||||
|
module: workflow.module,
|
||||||
|
path: workflow.path,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write all workflows
|
// Write all workflows
|
||||||
for (const [, value] of allWorkflows) {
|
for (const [, value] of allWorkflows) {
|
||||||
csv += value + '\n';
|
const row = [escapeCsv(value.name), escapeCsv(value.description), escapeCsv(value.module), escapeCsv(value.path)].join(',');
|
||||||
|
csv += row + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(csvPath, csv);
|
await fs.writeFile(csvPath, csv);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue