Compare commits

...

20 Commits

Author SHA1 Message Date
sjennings a6ae12c4d0
Merge 78646069ef into ed0defbe08 2025-12-12 07:35:33 +08:00
Dicky Moore ed0defbe08
fix: normalize workflow manifest schema (#1071)
* fix: normalize workflow manifest schema

* fix: escape workflow manifest values safely

---------

Co-authored-by: Brian <bmadcode@gmail.com>
2025-12-12 07:20:43 +08:00
Brian 78646069ef
Merge branch 'main' into feat/external-agent-code-review 2025-12-11 17:02:46 -06:00
Kevin Heidt 3bc485d0ed
Enhance config collector to support static fields (#1086)
Refactor config collection to handle both interactive and static fields. Update logic to process new static fields and merge answers accordingly.

Co-authored-by: Brian <bmadcode@gmail.com>
2025-12-12 06:56:31 +08:00
Alex Verkhovsky 0f5a9cf0dd
fix: correct grammar in PRD workflow description (#1087) 2025-12-12 06:43:40 +08:00
Alex Verkhovsky e2d9d35ce9
fix(bmm): improve code review completion message (#1095)
Change "Story is ready for next work!" to "Code review complete!"

The original phrasing was misleading - when a code review finishes
with status "done", it means the review itself is complete and the
story is marked done in tracking. However, the user may choose to
do additional reviews or the story may genuinely be finished.
"Code review complete" more accurately describes what actually
happened without implying next steps.
2025-12-12 06:42:52 +08:00
Alex Verkhovsky 82e6433b69
refactor: standardize file naming to use dashes instead of underscores (#1094)
Rename output/template files and update all references to use kebab-case
(dashes) instead of snake_case (underscores) for consistency:

- project_context.md -> project-context.md (13 references)
- backlog_template.md -> backlog-template.md
- agent_commands.md -> agent-commands.md
- agent_persona.md -> agent-persona.md
- agent_purpose_and_type.md -> agent-purpose-and-type.md
2025-12-12 06:42:24 +08:00
Alex Verkhovsky be7e07cc1a
fix: fully silence CodeRabbit unless explicitly invoked (#1096)
- Disable high_level_summary to stop PR description modifications
- Disable commit_status to stop GitHub status checks
- Disable issue_enrichment.auto_enrich to stop auto-commenting on issues

These settings complement the existing review_status: false and
auto_review.enabled: false to ensure CodeRabbit only responds
when explicitly tagged with @coderabbitai review.
2025-12-12 06:32:24 +08:00
Alex Verkhovsky 079f79aba5
Merge pull request #1103 from bmad-code-org/docs/test-architect-ADR-usage-update-2
docs: test arch ADR usage update
2025-12-11 12:35:12 -07:00
murat b4d7e1adef docs: addressed further PR comments 2025-12-11 13:13:44 -06:00
murat 6e9fe6c9a2 fix: addressed review comment 2025-12-11 11:36:33 -06:00
Murat K Ozcan d2d9010a8e
Update src/modules/bmm/docs/test-architecture.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-11 10:15:23 -06:00
murat 6d5a1084eb docs: test arch ADR usage update 2 2025-12-11 09:43:25 -06:00
murat 978a93ed33 docs: test arch ADR usage update 2025-12-11 09:34:22 -06:00
Alex Verkhovsky f7311f89f8
Merge branch 'main' into feat/external-agent-code-review 2025-12-10 19:07:07 -07:00
Scott Jennings 81817672af fix: mandate killing background shells before code review
Change cleanup logic from "check and maybe kill" to "kill unconditionally".
The previous approach allowed agents to read stale output from lingering
external agent processes, causing context pollution and reuse of old data.

New behavior:
- Kill ALL background shells as the FIRST action
- Do NOT read output from stale shells (BashOutput forbidden)
- Do NOT check what processes are doing - just terminate them
- Only proceed to Step 1 after all shells are dead
2025-12-10 19:06:24 -06:00
Scott Jennings 4c6b492835 feat: add cleanup for lingering external agent processes
Add instructions to detect and terminate any stale external agent
review processes when starting a new code review session in Claude Code.
2025-12-10 18:51:38 -06:00
Scott Jennings c0a49bcafe fix: use bmad_folder variable instead of hardcoded .bmad path
Allows users to customize the config location via bmad_folder setting.
2025-12-08 11:22:54 -06:00
Scott Jennings dcba8e5e59 feat: simplify external agent config and extract prompt to separate file
- Add installer prompt for external review agent selection (Codex, Gemini, Claude, None)
- Extract adversarial review prompt to external-agent-prompt.md for easier maintenance
- Simplify detection logic: check for "none" first, then verify CLI availability
- Add read-only sandbox flag to Codex invocation for safety
- Update workflow.yaml to reference new config variable
2025-12-08 10:57:37 -06:00
Scott Jennings 7509b0cbc2 feat: add external agent support for code reviews
Adds support for delegating adversarial code reviews to external CLI agents
(Codex, Gemini, or Claude) when available. This provides independent, unbiased
code reviews from a different AI model.

Changes:
- Add invoke-bash and set-var tags to workflow.xml execution engine
- Add external_review_agents configuration to install-config.yaml
- Rewrite code-review workflow to detect and invoke external agents
- Cache agent detection in config.yaml to avoid repeated CLI checks
- Add fallback to built-in review if external agents unavailable/fail
- Update checklist to reflect new external agent workflow

External agent invocation:
- Codex: codex exec --full-auto "prompt"
- Gemini: gemini -p "prompt" --yolo
- Claude: claude -p "prompt" --dangerously-skip-permissions
2025-12-08 10:36:39 -06:00
26 changed files with 455 additions and 107 deletions

View File

@ -4,9 +4,10 @@ language: "en-US"
early_access: true
reviews:
profile: chill
high_level_summary: true
high_level_summary: false # don't post summary until explicitly invoked
request_changes_workflow: false
review_status: false
commit_status: false # don't set commit status until explicitly invoked
collapse_walkthrough: false
poem: false
auto_review:
@ -33,4 +34,7 @@ reviews:
Flag any process.exit() without error message.
chat:
auto_reply: true # Response to mentions in comments, a la @coderabbit review
issue_enrichment:
auto_enrich:
enabled: false # don't auto-comment on issues

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "bmad-method",
"version": "6.0.0-alpha.15",
"version": "6.0.0-alpha.16",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bmad-method",
"version": "6.0.0-alpha.15",
"version": "6.0.0-alpha.16",
"license": "MIT",
"dependencies": {
"@kayvan/markdown-tree-parser": "^1.6.1",

View File

@ -63,6 +63,8 @@
<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-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>
</execute-tags>
</substep>
@ -126,6 +128,8 @@
<tag>invoke-workflow - Call another workflow</tag>
<tag>invoke-task - Call a task</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>
<output>
<tag>template-output - Save content checkpoint</tag>

View File

@ -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>
<ask if="action items exist">Would you like me to create tracking items for these action items? (backlog/tasks)</ask>
<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.
</action>
</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)".
</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.
</action>
<action>

View File

@ -24,7 +24,7 @@ agent:
critical_actions:
- "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"
- "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"

View File

@ -26,14 +26,17 @@ graph TB
subgraph Phase3["<b>Phase 3: SOLUTIONING</b>"]
Architecture["<b>Architect: *architecture</b>"]
EpicsStories["<b>PM/Architect: *create-epics-and-stories</b>"]
TestDesignSys["<b>TEA: *test-design (system-level)</b>"]
Framework["<b>TEA: *framework</b>"]
CI["<b>TEA: *ci</b>"]
GateCheck["<b>Architect: *implementation-readiness</b>"]
Architecture --> EpicsStories
Architecture --> TestDesignSys
TestDesignSys --> Framework
EpicsStories --> Framework
Framework --> CI
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
end
@ -93,12 +96,17 @@ graph TB
- **Documentation** (Optional for brownfield): Prerequisite using `*document-project`
- **Phase 1** (Optional): Discovery/Analysis (`*brainstorm`, `*research`, `*product-brief`)
- **Phase 2** (Required): Planning (`*prd` creates PRD with FRs/NFRs)
- **Phase 3** (Track-dependent): Solutioning (`*architecture` → `*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)
**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

View File

@ -51,3 +51,23 @@ tea_use_playwright_utils:
- "You must install packages yourself, or use test architect's *framework command."
default: false
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)"

View File

@ -1,6 +1,6 @@
---
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'
web_bundle: true
---

View File

@ -94,7 +94,7 @@ Discover and load context documents using smart discovery:
**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
3. Add to frontmatter `hasProjectContext: true` and track file path
4. Report to user: "Found existing project context with {number_of_rules} agent rules"

View File

@ -280,7 +280,7 @@ Your architecture will ensure consistent, high-quality implementation across all
**💡 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
- Specific patterns and conventions for your project
@ -310,7 +310,7 @@ This will help ensure consistent implementation by capturing:
- Testing and quality standards
- 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:**

View File

@ -1,5 +1,7 @@
# Senior Developer Review - Validation Checklist
## Story Setup
- [ ] Story file loaded from `{{story_path}}`
- [ ] Story Status verified as reviewable (review)
- [ ] Epic and Story IDs resolved ({{epic_num}}.{{story_num}})
@ -7,12 +9,33 @@
- [ ] Epic Tech Spec located or warning recorded
- [ ] Architecture/standards docs loaded (as available)
- [ ] 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
- [ ] File List reviewed and validated for completeness
- [ ] Tests identified and mapped to ACs; gaps noted
- [ ] Code quality review performed on changed files
- [ ] Security review performed on changed files and dependencies
- [ ] Code quality review performed (security, performance, maintainability)
- [ ] Minimum 3 issues found (adversarial review requirement)
## Finalization
- [ ] Findings categorized: HIGH/MEDIUM/LOW severity
- [ ] Outcome decided (Approve/Changes Requested/Blocked)
- [ ] Review notes appended under "Senior Developer Review (AI)"
- [ ] Change Log updated with review entry
@ -21,3 +44,4 @@
- [ ] Story saved successfully
_Reviewer: {{user_name}} on {{date}}_
_External Agent: {{external_agent_cmd}} (codex:{{codex_available}} / gemini:{{gemini_available}} / claude:{{claude_available}})_

View File

@ -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

View File

@ -4,16 +4,56 @@
<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>🔥 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>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
that wrote this slop</critical>
<critical>Find 3-10 specific issues in every review minimum - no lazy "looks good" reviews</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>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>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>
@ -38,6 +78,86 @@
<invoke-protocol name="discover_inputs" />
<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 &amp;&amp; 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 &amp;&amp; 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 &amp;&amp; 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 n="2" goal="Build review attack plan">
@ -56,41 +176,105 @@
<step n="3" goal="Execute adversarial review">
<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:
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)
3. **Uncommitted changes not documented** → MEDIUM finding (transparency issue)
</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>Store git discrepancy findings in {{git_findings}}</action>
<!-- AC Validation -->
<action>For EACH Acceptance Criterion:
1. Read the AC requirement
2. Search implementation files for evidence
3. Determine: IMPLEMENTED, PARTIAL, or MISSING
4. If MISSING/PARTIAL → HIGH SEVERITY finding
</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>
<!-- Task Completion Audit -->
<action>For EACH task marked [x]:
1. Read the task description
2. Search files for evidence it was actually done
3. **CRITICAL**: If marked [x] but NOT DONE → CRITICAL finding
4. Record specific proof (file:line)
</action>
<check if="{{use_external_agent}} == true">
<output>🔄 Invoking {{external_agent_cmd}} CLI for adversarial code review...</output>
<!-- Code Quality Deep Dive -->
<action>For EACH file in comprehensive review list:
1. **Security**: Look for injection risks, missing validation, auth issues
2. **Performance**: N+1 queries, inefficient loops, missing caching
3. **Error Handling**: Missing try/catch, poor error messages
4. **Code Quality**: Complex functions, magic numbers, poor naming
5. **Test Quality**: Are tests real assertions or placeholders?
</action>
<!-- ============================================================== -->
<!-- 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 &quot;$(cat '{{external_prompt_file}}')&quot;" 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 &quot;$(cat '{{external_prompt_file}}')&quot; --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 &quot;$(cat '{{external_prompt_file}}')&quot; --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 -->
<action>For EACH Acceptance Criterion:
1. Read the AC requirement
2. Search implementation files for evidence
3. Determine: IMPLEMENTED, PARTIAL, or MISSING
4. If MISSING/PARTIAL → HIGH SEVERITY finding
</action>
<!-- Task Completion Audit -->
<action>For EACH task marked [x]:
1. Read the task description
2. Search files for evidence it was actually done
3. **CRITICAL**: If marked [x] but NOT DONE → CRITICAL finding
4. Record specific proof (file:line)
</action>
<!-- Code Quality Deep Dive -->
<action>For EACH file in comprehensive review list:
1. **Security**: Look for injection risks, missing validation, auth issues
2. **Performance**: N+1 queries, inefficient loops, missing caching
3. **Error Handling**: Missing try/catch, poor error messages
4. **Code Quality**: Complex functions, magic numbers, poor naming
5. **Test Quality**: Are tests real assertions or placeholders?
</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">
<critical>NOT LOOKING HARD ENOUGH - Find more problems!</critical>
<action>Re-examine code for:
@ -113,6 +297,7 @@
<output>**🔥 CODE REVIEW FINDINGS, {user_name}!**
**Story:** {{story_file}}
**Review Method:** {{external_agent_cmd}} OR built-in
**Git vs Story Discrepancies:** {{git_discrepancy_count}} found
**Issues Found:** {{high_count}} High, {{medium_count}} Medium, {{low_count}} Low
@ -185,7 +370,7 @@
<action>Set {{current_sprint_status}} = "no-sprint-tracking"</action>
</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'">
<action>Load the FULL file: {sprint_status}</action>
<action>Find development_status key matching {{story_key}}</action>
@ -217,7 +402,7 @@
**Issues Fixed:** {{fixed_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>
</step>

View File

@ -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"
instructions: "{installed_path}/instructions.xml"
validation: "{installed_path}/checklist.md"
external_agent_prompt: "{installed_path}/external-agent-prompt.md"
template: false
variables:
@ -25,6 +26,11 @@ variables:
project_context: "**/project-context.md"
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
# Priority: Whole document first, then sharded version
# Strategy: SELECTIVE LOAD - only load the specific epic needed for this story review
@ -51,4 +57,3 @@ input_file_patterns:
load_strategy: "INDEX_GUIDED"
standalone: true
web_bundle: false

View File

@ -35,7 +35,7 @@ validation-rules:
- [ ] **Acceptance Criteria Satisfaction:** Implementation satisfies EVERY Acceptance Criterion in the story
- [ ] **No Ambiguous Implementation:** Clear, unambiguous implementation that meets story requirements
- [ ] **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

View File

@ -33,7 +33,7 @@ Discover the project's technology stack, existing patterns, and critical impleme
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
- 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)
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:
```yaml

View File

@ -288,7 +288,7 @@ After each category, show the generated rules and present choices:
## 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:

View File

@ -134,7 +134,7 @@ Based on user skill level, present the completion:
**Expert Mode:**
"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."
@ -227,7 +227,7 @@ Present final completion to user:
"✅ **Project Context Generation Complete!**
Your optimized project context file is ready at:
`{output_folder}/project_context.md`
`{output_folder}/project-context.md`
**📊 Context Summary:**

View File

@ -1,11 +1,11 @@
---
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
**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.
@ -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`
- `template_path` = `{installed_path}/project-context-template.md`
- `output_file` = `{output_folder}/project_context.md`
- `output_file` = `{output_folder}/project-context.md`
---

View File

@ -24,8 +24,14 @@ variables:
# Output configuration
# Note: Actual output file determined dynamically based on mode detection
# - System-Level (Phase 3): {output_folder}/test-design-system.md
# - Epic-Level (Phase 4): {output_folder}/test-design-epic-{epic_num}.md
# Declared outputs for new workflow format
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"
# Required tools

View File

@ -248,14 +248,21 @@ class ConfigCollector {
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
const existingKeys = this.existingConfig && this.existingConfig[moduleName] ? Object.keys(this.existingConfig[moduleName]) : [];
// Find new interactive fields (with prompt)
const newKeys = configKeys.filter((key) => {
const item = moduleConfig[key];
// Check if it's a config item and doesn't exist in existing config
return item && typeof item === 'object' && item.prompt && !existingKeys.includes(key);
});
// If in silent mode and no new keys, use existing config and skip prompts
if (silentMode && newKeys.length === 0) {
// Find new static fields (without prompt, just result)
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.collectedConfig[moduleName]) {
this.collectedConfig[moduleName] = {};
@ -294,9 +301,12 @@ class ConfigCollector {
return false; // No new fields
}
// If we have new fields, build questions first
if (newKeys.length > 0) {
// If we have new fields (interactive or static), process them
if (newKeys.length > 0 || newStaticKeys.length > 0) {
const questions = [];
const staticAnswers = {};
// Build questions for interactive fields
for (const key of newKeys) {
const item = moduleConfig[key];
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
@ -305,39 +315,50 @@ 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) {
// Only show header if we actually have questions
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(); // Line break before questions
const answers = await inquirer.prompt(questions);
const promptedAnswers = await inquirer.prompt(questions);
// Store answers for cross-referencing
Object.assign(this.allAnswers, answers);
// Process answers and build result values
for (const key of Object.keys(answers)) {
const originalKey = key.replace(`${moduleName}_`, '');
const item = moduleConfig[originalKey];
const value = answers[key];
let result;
if (Array.isArray(value)) {
result = value;
} else if (item.result) {
result = this.processResultTemplate(item.result, value);
} else {
result = value;
}
if (!this.collectedConfig[moduleName]) {
this.collectedConfig[moduleName] = {};
}
this.collectedConfig[moduleName][originalKey] = result;
}
} else {
// New keys exist but no questions generated - show no config message
// Merge prompted answers with static 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);
}
// Store all answers for cross-referencing
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 item = moduleConfig[originalKey];
const value = allAnswers[key];
let result;
if (Array.isArray(value)) {
result = value;
} else if (item.result) {
result = this.processResultTemplate(item.result, value);
} else {
result = value;
}
if (!this.collectedConfig[moduleName]) {
this.collectedConfig[moduleName] = {};
}
this.collectedConfig[moduleName][originalKey] = result;
}
}
// 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
const questions = [];
const staticAnswers = {};
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
for (const key of configKeys) {
const item = moduleConfig[key];
// Skip if not a config object
if (!item || typeof item !== 'object' || !item.prompt) {
if (!item || typeof item !== 'object') {
continue;
}
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
if (question) {
questions.push(question);
// 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);
if (question) {
questions.push(question);
}
}
}
// Collect all answers (static + prompted)
let allAnswers = { ...staticAnswers };
// Display appropriate header based on whether there are questions
if (questions.length > 0) {
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(); // Line break before questions
const answers = await inquirer.prompt(questions);
const promptedAnswers = await inquirer.prompt(questions);
// Store answers for cross-referencing
Object.assign(this.allAnswers, answers);
// Merge prompted answers with static 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
for (const key of Object.keys(answers)) {

View File

@ -581,6 +581,11 @@ class ManifestGenerator {
*/
async writeWorkflowManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'workflow-manifest.csv');
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
const parseCsvLine = (line) => {
const columns = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || [];
return columns.map((c) => c.replaceAll(/^"|"$/g, ''));
};
// Read existing manifest to preserve entries
const existingEntries = new Map();
@ -592,18 +597,21 @@ class ManifestGenerator {
for (let i = 1; i < lines.length; i++) {
const line = lines[i];
if (line) {
// Parse CSV (simple parsing assuming no commas in quoted fields)
const parts = line.split('","');
const parts = parseCsvLine(line);
if (parts.length >= 4) {
const name = parts[0].replace(/^"/, '');
const module = parts[2];
existingEntries.set(`${module}:${name}`, line);
const [name, description, module, workflowPath] = parts;
existingEntries.set(`${module}:${name}`, {
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';
// Combine existing and new workflows
@ -617,12 +625,18 @@ class ManifestGenerator {
// Add/update new workflows
for (const workflow of this.workflows) {
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
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);