Compare commits

...

19 Commits

Author SHA1 Message Date
Sergey Novikov 9ce6ef31e8
Merge efc2152ad8 into 99c1fa940a 2026-02-22 14:10:09 +00:00
sno efc2152ad8 feat(monorepo): fix project_name resolution and variable leakage 2026-02-22 15:09:57 +01:00
Alex Verkhovsky 99c1fa940a
feat: add LLM audit prompt for file reference conventions (#1720)
tools/audit-file-refs.md — a repeatable prompt that spawns parallel
Haiku subagents to semantically audit new-format source files for
non-conforming file references. Includes a self-check that verifies
all files are accounted for before producing the final report.

Replaces the planned regex extension (Item 1 of the master plan) with
an approach that can handle the full surface area of reference patterns
without exhaustive pattern enumeration.

Also excludes .junie/ from Prettier checks (IDE integration folder,
user-specific, not in repo).

Refs #1718

Co-authored-by: Brian <bmadcode@gmail.com>
2026-02-20 20:42:33 -06:00
sno 9d00f69680 feat(cli): refine /set-project and /list-envs with numbered selection and exclusions 2026-02-20 12:35:25 +01:00
sno 2a30b47c91 feat(cli): refine /set-project with numbered selection and exclusions 2026-02-20 12:35:09 +01:00
sno eb6568c8c1 feat(cli): add /list-envs and enhance /set-project interactivity 2026-02-20 12:20:59 +01:00
sno 89e310a6ce chore(cli): refine folder protection, sync regex, correct replacement order, and template paths 2026-02-20 10:49:21 +01:00
sno 5413931443 docs: Explain inline check retention in advanced elicitation workflow XML. 2026-02-19 14:05:28 +01:00
sno 006642552a docs: Add monorepo setup guide and link it in the index. 2026-02-19 14:05:28 +01:00
sno d05b9f87d5 refactor: centralize monorepo context detection logic by removing duplicated checks from numerous workflow files and updating related tests. 2026-02-19 14:05:28 +01:00
sno a5bda6a80e feat: Introduce monorepo context logic for inline project overrides and dynamic path adjustments in workflows. 2026-02-19 14:05:28 +01:00
Brian e7cc716e87
Merge branch 'main' into feature/monorepo-support 2026-02-18 18:31:48 -06:00
sno 6df9369cd0 fix(workflows): improve monorepo context handling and sanitization
- Re-derive dependent paths in retrospective workflow after monorepo override
- Add max length validation for project_suffix in dev-story
- Fix premature output path computation in create-story workflow
2026-02-18 10:02:39 +01:00
sno 21685f23f3 fix: enhance monorepo support with robust path resolution, input sanitization, and documentation fixes 2026-02-18 09:39:39 +01:00
sno 5ae2cad5a3 fix: ensure absolute path resolution for monorepo context checks 2026-02-18 09:28:17 +01:00
sno a023178c0c fix: add missing main_config frontmatter to analysis workflows 2026-02-17 22:02:39 +01:00
sno 855a4b3695 feat: enhance installer copilot instructions for monorepo support 2026-02-17 21:58:59 +01:00
sno 1237eeb375 feat: fix missing monorepo context logic and add validation tests 2026-02-17 19:46:14 +01:00
sno 0f37258d5c feat: add monorepo support with dynamic project context 2026-02-17 19:37:06 +01:00
51 changed files with 925 additions and 65 deletions

View File

@ -0,0 +1,82 @@
---
title: "Set Up Monorepo Support"
description: Manage multiple projects in a single repository with isolated artifacts
sidebar:
order: 8
---
BMad Method supports working in monorepo environments where multiple projects share a single repository. This allows you to centralize methodology files while keeping project artifacts isolated and organized.
:::note[Prerequisites]
- BMad Method v6.0.1+
- A repository containing multiple projects
:::
## Why Use Monorepo Support?
In a standard setup, BMad expects one project per repository. In a monorepo, you might have `apps/frontend`, `apps/backend`, and `packages/shared`. Without monorepo support, BMad's artifacts (plans, architecture, stories) would be mixed or require installing BMad in every sub-directory.
Monorepo support allows you to:
- **Centralize Methodology**: Install BMad once at the root based on your team's process.
- **Isolate Artifacts**: Automatically route outputs to project-specific folders (e.g., `_bmad-output/frontend/`).
- **Context Switch Easily**: Switch between projects without reinstalling or reconfiguring.
## Project Structure
When monorepo support is active, the structure changes slightly to accommodate multiple projects:
```
monorepo-root/
├── _bmad/ # Methodology (installed core & modules)
│ └── .current_project # Context marker (e.g., "app-alpha")
├── _bmad-output/ # Unified output directory
│ ├── app-alpha/ # Sub-project artifacts
│ │ ├── planning-artifacts/
│ │ └── implementation-artifacts/
│ └── app-beta/ # Sub-project artifacts
│ └── planning-artifacts/
├── apps/ # Actual source code
│ ├── app-alpha/
│ └── app-beta/
└── package.json
```
## How It Works
### Context Injection
Core and BMM workflows automatically check for the existence of `{project-root}/_bmad/.current_project`.
- **If found**: It reads the content (e.g., "app-alpha") and overrides the `output_folder` to `_bmad-output/app-alpha`.
- **If not found**: It behaves like a standard single-project installation, outputting to `_bmad-output` root.
### The /list-envs Command
You can view all available environments created in your monorepo by running the `/list-envs` command. This will scan your `_bmad-output/` directory and display all existing project environments, as well as indicate which one is currently active.
### The /set-project Command
You can easily manage the active project context using the `/set-project` workflow.
**To set a context:**
1. Run `/set-project <env_name>` in your chat.
2. If the environment does not exist, you will be prompted to create it interactively.
3. If you run `/set-project` without an argument, it will automatically list available environments and prompt you to select one or create a new one.
**To clear context (return to single-project mode):**
1. Run `/set-project CLEAR`.
### Inline Override
You can temporarily override the project context for a specific command without changing the global `.current_project` state. This is useful for one-off tasks in a different project.
Use the `#project:` syntax (or `#p:` for short) anywhere in your prompt:
```bash
# Full syntax
/create-prd #project:myproject_name
# Short alias
/create-story #p:frontend
```
**Note:** The inline override takes precedence over the `.current_project` file. If no project is specified via `#` or `.current_project`, BMad defaults to the root `_bmad-output` folder.

View File

@ -18,6 +18,12 @@ The fastest way to understand BMad is to try it.
Install BMad and run `/bmad-help` — it will guide you through everything based on your project and installed modules.
:::
## Advanced Setup
- **[Monorepo Support](./how-to/monorepo-setup.md)** — Manage multiple projects in a single repository.
- **[Non-Interactive Installation](./how-to/non-interactive-installation.md)** — Automate BMad installation for CI/CD.
## How to Use These Docs
These docs are organized into four sections based on what you're trying to do:

View File

@ -43,6 +43,7 @@
"test": "npm run test:schemas && npm run test:refs && npm run test:install && npm run validate:schemas && npm run lint && npm run lint:md && npm run format:check",
"test:coverage": "c8 --reporter=text --reporter=html npm run test:schemas",
"test:install": "node test/test-installation-components.js",
"test:monorepo": "node test/test-monorepo-validation.js",
"test:refs": "node test/test-file-refs-csv.js",
"test:schemas": "node test/test-agent-schema.js",
"validate:refs": "node tools/validate-file-refs.js --strict",

View File

@ -28,4 +28,5 @@ bmm,4-implementation,Create Story,CS,30,_bmad/bmm/workflows/4-implementation/cre
bmm,4-implementation,Dev Story,DS,40,_bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml,bmad-bmm-dev-story,true,dev,Create Mode,"Story cycle: Execute story implementation tasks and tests then CR then back to DS if fixes needed",,,
bmm,4-implementation,Code Review,CR,50,_bmad/bmm/workflows/4-implementation/code-review/workflow.yaml,bmad-bmm-code-review,false,dev,Create Mode,"Story cycle: If issues back to DS if approved then next CS or ER if epic complete",,,
bmm,4-implementation,QA Automation Test,QA,45,_bmad/bmm/workflows/qa/automate/workflow.yaml,bmad-bmm-qa-automate,false,qa,Create Mode,"Generate automated API and E2E tests for implemented code using the project's existing test framework (detects existing well known in use test frameworks). Use after implementation to add test coverage. NOT for code review or story validation - use CR for that.",implementation_artifacts,"test suite",
bmm,anytime,Set Project,SPJ,05,_bmad/bmm/workflows/0-context/set-project/workflow.md,bmad-set-project,false,bmad-master,Anytime,"Set or clear the current project context for monorepo support.",_bmad,.current_project,
bmm,4-implementation,Retrospective,ER,60,_bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml,bmad-bmm-retrospective,false,sm,Create Mode,"Optional at epic end: Review completed work lessons learned and next epic or if major issues consider CC",implementation_artifacts,retrospective,

1 module phase name code sequence workflow-file command required agent options description output-location outputs
28 bmm 4-implementation Dev Story DS 40 _bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml bmad-bmm-dev-story true dev Create Mode Story cycle: Execute story implementation tasks and tests then CR then back to DS if fixes needed
29 bmm 4-implementation Code Review CR 50 _bmad/bmm/workflows/4-implementation/code-review/workflow.yaml bmad-bmm-code-review false dev Create Mode Story cycle: If issues back to DS if approved then next CS or ER if epic complete
30 bmm 4-implementation QA Automation Test QA 45 _bmad/bmm/workflows/qa/automate/workflow.yaml bmad-bmm-qa-automate false qa Create Mode Generate automated API and E2E tests for implemented code using the project's existing test framework (detects existing well known in use test frameworks). Use after implementation to add test coverage. NOT for code review or story validation - use CR for that. implementation_artifacts test suite
31 bmm anytime Set Project SPJ 05 _bmad/bmm/workflows/0-context/set-project/workflow.md bmad-set-project false bmad-master Anytime Set or clear the current project context for monorepo support. _bmad .current_project
32 bmm 4-implementation Retrospective ER 60 _bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml bmad-bmm-retrospective false sm Create Mode Optional at epic end: Review completed work lessons learned and next epic or if major issues consider CC implementation_artifacts retrospective

View File

@ -0,0 +1,28 @@
---
name: list-envs
description: List available project environments for monorepo support
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# List Project Environments
**Goal:** List the available project context environments for BMAD artifacts.
**Your Role:** Configuration Assistant.
## WORKFLOW ARCHITECTURE
### 1. Identify Environments
- Use your file listing/system capabilities to examine the `{project-root}/_bmad-output/` directory.
- Identify all subdirectories within this path. Each subdirectory represents an available environment, EXCEPT:
- Ignore hidden directories starting with `.`
- Ignore standard BMAD artifact directories: `planning-artifacts`, `implementation-artifacts`, `test-artifacts`, `knowledge`, `docs`, `assets`
- The root `_bmad-output/` directory itself represents the `default (root)` unset environment.
### 2. Output Results
1. Display a formatted numbered list of the existing environments you found.
2. Clearly indicate the `default (root)` environment (usually [0]).
3. Check if there is an active environment currently set. Read `{project-root}/{{bmadFolderName}}/.current_project`. If it exists, indicate which environment from the list is currently active (e.g. by adding `(ACTIVE)` next to the bullet point).
4. Inform the user that they can switch to another environment or create a new one using the `/set-project <env_name>` command.

View File

@ -0,0 +1,84 @@
---
name: set-project
description: Set the current project context for monorepo support
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# Set Project Context
**Goal:** Configure the active project path for BMAD artifacts.
**Your Role:** Configuration Assistant.
## WORKFLOW ARCHITECTURE
This is a single-step workflow that updates a local state file.
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
### 2. Context Management
1. **Analyze Request**: Determine the requested project name from the user's initial invocation (e.g., `/set-project my-app`).
2. **Wait for Input (If Missing)**: If the user did NOT provide a project name:
- Use your file listing capabilities to examine the `{project-root}/_bmad-output/` directory.
- Output: A formatted list of the existing environments (subdirectories) you found, explicitly noting the `default (root)` environment.
*(Exclude hidden directories and standard BMAD artifact folders like `planning-artifacts`, `implementation-artifacts`, `test-artifacts`, `knowledge`, `docs`, `assets`)*
- Present options in a numbered list format and ask the user to select by number or type a new project name:
```
Select a project environment:
[0] default (root) - Current location for standard artifacts
[1] project-name-1
[2] project-name-2
...
Enter a number to select, type a new project name, or enter 'CLEAR' to reset to root:
```
- **Wait for Input.**
3. **Process Input**: Once a project name is established:
- **Case: CLEAR**:
- Delete file: `{project-root}/{{bmadFolderName}}/.current_project`
- Output: "✅ Project context cleared. Artifacts will go to root `_bmad-output/`."
- **HALT**
- **Case: Numeric Selection**: If input is a number (0, 1, 2, etc.):
- Map the number to the corresponding environment from your earlier listing
- If number 0, treat as "CLEAR" case above
- If valid number but out of range, show error and ask again
- If valid selection, use that environment name as the path
- **Case: Path Provided** (text input):
- **1. Cleanup**: Remove leading/trailing slashes and any occurrences of `_bmad-output/`.
- **2. Validate Existence**: Check if `{project-root}/_bmad-output/<sanitized_path>` exists on the file system.
- **If it DOES NOT exist**:
- Ask: "The environment `<sanitized_path>` is not present. Do you want to create a new one? (Yes/No)"
- **Wait for Input.**
- **If No**: Inform the user to run `/list-envs` to see available environments or `/set-project` to try again. **HALT**.
- **If Yes**: Proceed to validation.
- **3. Validate - No Traversal**: Reject if path contains `..`.
- **4. Validate - No Absolute**: Reject if path starts with `/` or drive letter (e.g., `C:`).
- **5. Validate - Empty/Whitespace**: Reject if empty or only whitespace.
- **6. Validate - Whitelist**: Match against regex `^[a-zA-Z0-9._-/]+$`.
- **Check Results**:
- **If Invalid**:
- Output: "❌ Error: Invalid project context — must be a relative path and contain only alphanumeric characters, dots, dashes, underscores, or slashes. Traversal (..) is strictly forbidden."
- **HALT**
- **If Valid**:
- Write file: `{project-root}/{{bmadFolderName}}/.current_project` with content `<sanitized_path>`
- Output: "✅ Project context set to: `<sanitized_path>`. Artifacts will go to `_bmad-output/<sanitized_path>/`."
### 3. Verification
- Display the full resolved output path for confirmation.
## Inline Project Overrides
You can also temporarily run a command against a different project without changing the global context file. Use the `#project:NAME` or `#p:NAME` syntax in your command invocation.
**Examples:**
- `/create-prd #project:my-app`
- `/sprint-planning #p:admin-portal`
**Precedence:**
1. **Inline Override** (`#p:NAME`)
2. **Global Context File** (`{project-root}/_bmad/.current_project`)
3. **Default Config** (if neither is present)

View File

@ -1,6 +1,7 @@
---
name: create-product-brief
description: Create comprehensive product briefs through collaborative step-by-step discovery as creative Business Analyst working with the user as peers.
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# Product Brief Workflow
@ -48,7 +49,7 @@ This uses **step-file architecture** for disciplined execution:
### 1. Configuration Loading
Load and read full config from {project-root}/_bmad/bmm/config.yaml and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`, `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -1,6 +1,7 @@
---
name: domain-research
description: Conduct domain research covering industry analysis, regulations, technology trends, and ecosystem dynamics using current web data and verified sources.
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# Domain Research Workflow
@ -15,7 +16,11 @@ description: Conduct domain research covering industry analysis, regulations, te
## CONFIGURATION
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`
- `date` as a system-generated value

View File

@ -1,6 +1,7 @@
---
name: market-research
description: Conduct market research covering market size, growth, competition, and customer insights using current web data and verified sources.
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# Market Research Workflow
@ -15,7 +16,11 @@ description: Conduct market research covering market size, growth, competition,
## CONFIGURATION
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`
- `date` as a system-generated value

View File

@ -1,6 +1,7 @@
---
name: technical-research
description: Conduct technical research covering technology evaluation, architecture decisions, and implementation approaches using current web data and verified sources.
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# Technical Research Workflow
@ -15,7 +16,11 @@ description: Conduct technical research covering technology evaluation, architec
## CONFIGURATION
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`
- `date` as a system-generated value

View File

@ -46,10 +46,6 @@ This uses **step-file architecture** for disciplined execution:
## INITIALIZATION SEQUENCE
### 1. Configuration Loading
Load and read full config from {main_config} and resolve:
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`
- `date` as system-generated current datetime

View File

@ -48,7 +48,7 @@ This uses **step-file architecture** for disciplined execution:
### 1. Configuration Loading
Load and read full config from {main_config} and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -48,7 +48,8 @@ This uses **step-file architecture** for disciplined execution:
### 1. Configuration Loading
Load and read full config from {main_config} and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -22,9 +22,10 @@ This uses **micro-file architecture** for disciplined execution:
## INITIALIZATION
### Configuration Loading
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -42,9 +42,9 @@ description: 'Critical validation workflow that assesses PRD, Architecture, and
## INITIALIZATION SEQUENCE
### 1. Module Configuration Loading
### 1. Configuration Loading
Load and read full config from {project-root}/_bmad/bmm/config.yaml and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`, `communication_language`, `document_output_language`
- ✅ YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`

View File

@ -25,7 +25,9 @@ This uses **micro-file architecture** for disciplined execution:
## INITIALIZATION
### Configuration Loading
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:

View File

@ -48,7 +48,8 @@ This uses **step-file architecture** for disciplined execution:
### 1. Configuration Loading
Load and read full config from {project-root}/_bmad/bmm/config.yaml and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `planning_artifacts`, `user_name`, `communication_language`, `document_output_language`
- ✅ YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`

View File

@ -16,6 +16,23 @@
<step n="1" goal="Load story and discover changes">
<check if="{project-root}/_bmad/.current_project exists">
<action>Read content as project_suffix</action>
<!-- Security: Reject traversal, absolute paths, and invalid patterns -->
<check if="project_suffix is empty OR project_suffix contains '..' or starts with '/' or starts with '\'">
<output>🚫 Security Error: Invalid project context path detected — path traversal or absolute path detected.</output>
<action>HALT</action>
</check>
<!-- Whitelist: Alphanumeric, dots, dashes, underscores, AND slashes (for nested segments) -->
<check if="project_suffix matches regex '[^a-zA-Z0-9._-/]|^\s*$'">
<output>🚫 Error: Project context must only contain alphanumeric characters, dots, dashes, underscores, or slashes.</output>
<action>HALT</action>
</check>
<action>Override project_name to {project_suffix}</action>
<action>Override output_folder to {project-root}/_bmad-output/{project_suffix}</action>
</check>
<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
@ -41,6 +58,7 @@
<invoke-protocol name="discover_inputs" />
<action>Load {project_context} for coding standards (if exists)</action>
</step>
<step n="2" goal="Build review attack plan">

View File

@ -40,7 +40,7 @@
- [x] Done - Item completed successfully
- [N/A] Skip - Item not applicable to this change
- [!] Action-needed - Item requires attention or follow-up
<action>Maintain running notes of findings and impacts discovered</action>
<action>Present checklist progress after each major section</action>
<action if="checklist cannot be completed">Identify blocking issues and work with user to resolve before continuing</action>

View File

@ -18,6 +18,7 @@
<critical>🎯 ZERO USER INTERVENTION: Process should be fully automated except for initial epic/story selection or missing documents</critical>
<step n="1" goal="Determine target story">
<check if="{{story_path}} is provided by user or user provided the epic and story number such as 2-4 or 1.6 or epic 1 story 5">
<action>Parse user-provided story path: extract epic_num, story_num, story_title from format like "1-2-user-auth"</action>
<action>Set {{epic_num}}, {{story_num}}, {{story_key}} from user input</action>
@ -260,48 +261,52 @@
<step n="5" goal="Create comprehensive story file">
<critical>📝 CREATE ULTIMATE STORY FILE - The developer's master implementation guide!</critical>
<!-- Recompute output file path with correct output_folder and story_key -->
<action>Set {target_story_file} = {output_folder}/{{story_key}}.md</action>
<action>Output "Generating story file at: {target_story_file}"</action>
<action>Initialize from template.md:
{default_output_file}</action>
<template-output file="{default_output_file}">story_header</template-output>
{target_story_file}</action>
<template-output file="{target_story_file}">story_header</template-output>
<!-- Story foundation from epics analysis -->
<template-output
file="{default_output_file}">story_requirements</template-output>
file="{target_story_file}">story_requirements</template-output>
<!-- Developer context section - MOST IMPORTANT PART -->
<template-output file="{default_output_file}">
developer_context_section</template-output> **DEV AGENT GUARDRAILS:** <template-output file="{default_output_file}">
<template-output file="{target_story_file}">
developer_context_section</template-output> **DEV AGENT GUARDRAILS:** <template-output file="{target_story_file}">
technical_requirements</template-output>
<template-output file="{default_output_file}">architecture_compliance</template-output>
<template-output file="{target_story_file}">architecture_compliance</template-output>
<template-output
file="{default_output_file}">library_framework_requirements</template-output>
<template-output file="{default_output_file}">
file="{target_story_file}">library_framework_requirements</template-output>
<template-output file="{target_story_file}">
file_structure_requirements</template-output>
<template-output file="{default_output_file}">testing_requirements</template-output>
<template-output file="{target_story_file}">testing_requirements</template-output>
<!-- Previous story intelligence -->
<check
if="previous story learnings available">
<template-output file="{default_output_file}">previous_story_intelligence</template-output>
<template-output file="{target_story_file}">previous_story_intelligence</template-output>
</check>
<!-- Git intelligence -->
<check
if="git analysis completed">
<template-output file="{default_output_file}">git_intelligence_summary</template-output>
<template-output file="{target_story_file}">git_intelligence_summary</template-output>
</check>
<!-- Latest technical specifics -->
<check if="web research completed">
<template-output file="{default_output_file}">latest_tech_information</template-output>
<template-output file="{target_story_file}">latest_tech_information</template-output>
</check>
<!-- Project context reference -->
<template-output
file="{default_output_file}">project_context_reference</template-output>
file="{target_story_file}">project_context_reference</template-output>
<!-- Final status update -->
<template-output file="{default_output_file}">
<template-output file="{target_story_file}">
story_completion_status</template-output>
<!-- CRITICAL: Set status to ready-for-dev -->

View File

@ -13,6 +13,7 @@
<critical>User skill level ({user_skill_level}) affects conversation style ONLY, not code updates.</critical>
<step n="1" goal="Find next ready story and load it" tag="sprint-status">
<check if="{{story_path}} is provided">
<action>Use {{story_path}} directly</action>
<action>Read COMPLETE story file</action>
@ -376,8 +377,6 @@
<action>Communicate to {user_name} that story implementation is complete and ready for review</action>
<action>Summarize key accomplishments: story ID, story key, title, key changes made, tests added, files modified</action>
<action>Provide the story file path and current status (now "review")</action>
<action>Based on {user_skill_level}, ask if user needs any explanations about:
- What was implemented and how it works
- Why certain technical decisions were made

View File

@ -1,7 +1,9 @@
# Retrospective - Epic Completion Review Instructions
<critical>The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml</critical>
<critical>You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml</critical>
<critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</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>⚠️ ABSOLUTELY NO TIME ESTIMATES - NEVER mention hours, days, weeks, months, or ANY time-based predictions. AI has fundamentally changed development speed - what once took teams weeks/months can now be done by one person in hours. DO NOT give ANY time estimates whatsoever.</critical>

View File

@ -3,7 +3,9 @@
<critical>The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml</critical>
<critical>You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml</critical>
## 📚 Document Discovery - Full Epic Loading
## 📚 Document Discovery
- Full Epic Loading
**Strategy**: Sprint planning needs ALL epics and stories to build complete status tracking.

View File

@ -1,6 +1,7 @@
# Sprint Status - Multi-Mode Service
<critical>The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml</critical>
<critical>You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml</critical>
<critical>Modes: interactive (default), validate, data</critical>
<critical>⚠️ ABSOLUTELY NO TIME ESTIMATES. Do NOT mention hours, days, weeks, or timelines.</critical>
@ -24,17 +25,26 @@
</step>
<step n="1" goal="Locate sprint status file">
<!-- Runtime Validation Gate -->
<check if="{sprint_status_file} is not defined OR {sprint_status_file} == ''">
<output>🚫 Error: Workflow configuration not loaded properly ({sprint_status_file} is undefined).</output>
<action>HALT</action>
</check>
<action>Load {project_context} for project-wide patterns and conventions (if exists)</action>
<action>Try {sprint_status_file}</action>
<check if="file not found">
<output>❌ sprint-status.yaml not found.
<output>❌ sprint-status.yaml not found at: {sprint_status_file}
Run `/bmad:bmm:workflows:sprint-planning` to generate it, then rerun sprint-status.</output>
<action>Exit workflow</action>
<action>HALT</action>
</check>
<action>Continue to Step 2</action>
</step>
<step n="2" goal="Read and parse sprint-status.yaml">
<check if="{sprint_status_file} is not defined">
<action>HALT - Safety Error: sprint_status_file variable lost or undefined.</action>
</check>
<action>Read the FULL file: {sprint_status_file}</action>
<action>Parse fields: generated, project, project_key, tracking_system, story_location</action>
<action>Parse development_status map. Classify keys:</action>
@ -164,6 +174,9 @@ If the command targets a story, set `story_key={{next_story_id}}` when prompted.
<!-- ========================= -->
<step n="20" goal="Data mode output">
<check if="{sprint_status_file} is not defined">
<action>HALT - Safety Error: sprint_status_file variable lost or undefined.</action>
</check>
<action>Load and parse {sprint_status_file} same as Step 2</action>
<action>Compute recommendation same as Step 3</action>
<template-output>next_workflow_id = {{next_workflow_id}}</template-output>
@ -185,6 +198,9 @@ If the command targets a story, set `story_key={{next_story_id}}` when prompted.
<!-- ========================= -->
<step n="30" goal="Validate sprint-status file">
<check if="{sprint_status_file} is not defined">
<action>HALT - Safety Error: sprint_status_file variable lost or undefined.</action>
</check>
<action>Check that {sprint_status_file} exists</action>
<check if="missing">
<template-output>is_valid = false</template-output>

View File

@ -23,7 +23,9 @@ This uses **step-file architecture** for focused execution:
## INITIALIZATION
### Configuration Loading
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:

View File

@ -66,8 +66,9 @@ This uses **step-file architecture** for disciplined execution:
### 1. Configuration Loading
Load and read full config from `{main_config}` and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
- `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`
- `date` as system-generated current datetime

View File

@ -79,6 +79,11 @@
</step>
## 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
<step n="3" goal="Check for existing documentation and determine workflow mode" if="resume_mode == false">
<action>Check if {project_knowledge}/index.md exists</action>

View File

@ -11,6 +11,36 @@
<action>Load existing project structure from index.md and project-parts.json (if exists)</action>
<action>Load source tree analysis to understand available areas</action>
<!-- Step 1: Check for inline project override (#project:NAME or #p:NAME) -->
<action>Scan request for pattern #project:NAME or #p:NAME (case-insensitive)</action>
<check if="inline override found">
<action>Set project_suffix = extracted NAME</action>
</check>
<!-- Step 2: Fall back to .current_project file -->
<check if="project_suffix not yet set AND {project-root}/_bmad/.current_project exists">
<action>Read content as project_suffix</action>
</check>
<!-- Step 3: Validate and Canonicalize -->
<check if="project_suffix is set">
<!-- Security: Reject traversal, absolute paths, and invalid patterns -->
<check if="project_suffix is empty OR project_suffix contains '..' or starts with '/' or starts with '\'">
<output>🚫 Security Error: Invalid project context path detected — path traversal or absolute path detected.</output>
<action>HALT</action>
</check>
<!-- Whitelist: Alphanumeric, dots, dashes, underscores, AND slashes (for nested segments) -->
<check if="project_suffix matches regex '[^a-zA-Z0-9._-/]|^\s*$'">
<output>🚫 Error: Project context must only contain alphanumeric characters, dots, dashes, underscores, or slashes.</output>
<action>HALT</action>
</check>
<action>Override output_folder to {project-root}/_bmad-output/{project_suffix}</action>
<action>Override project_knowledge to {project-root}/_bmad-output/{project_suffix}</action>
<action>Output "Monorepo context detected. Writing deep-dive artifacts to: {project_knowledge}"</action>
</check>
<step n="13a" goal="Identify area for deep-dive">
<action>Analyze existing documentation to suggest deep-dive options</action>
@ -250,8 +280,11 @@ Detailed exhaustive analysis of specific areas:
- Complete file inventory with all exports
- Dependency graph and data flow
- Integration points and API contracts
- Testing analysis and coverage
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
- Related code and reuse opportunities
- Implementation guidance

View File

@ -95,6 +95,8 @@ Your choice [1/2/3]:
- Best for: Quick project overview, initial understanding
- File reading: Minimal (configs, README, package.json, etc.)
**2. Deep Scan** (10-30 minutes)
- Reads files in critical directories based on project type

View File

@ -25,9 +25,10 @@ This uses **micro-file architecture** for disciplined execution:
## INITIALIZATION
### Configuration Loading
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
- `project_name`, `output_folder`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -18,6 +18,7 @@ Check project for existing test framework:
- Search online for current recommended test framework for that stack
- Suggest the meta framework and use it (or ask user to confirm)
### Step 1: Identify Features
Ask user what to test:

View File

@ -18,16 +18,23 @@
<flow>
<step n="1" title="Load and Initialize Workflow">
<substep n="1a" title="Load Configuration and Resolve Variables">
<substep n="1a" title="Initialize Environment Variables">
<action>Resolve system variables (date:system-generated) and paths ({project-root}, {installed_path})</action>
</substep>
<substep n="1b" title="Load Configuration and Resolve Variables">
<action>Read workflow.yaml from provided path</action>
<mandate>Load config_source (REQUIRED for all modules)</mandate>
<phase n="1">Load external config from config_source path</phase>
<phase n="2">Resolve all {config_source}: references with values from config</phase>
<phase n="3">Resolve system variables (date:system-generated) and paths ({project-root}, {installed_path})</phase>
<phase n="4">Ask user for input of any variables that are still unknown</phase>
<phase n="3">Ask user for input of any variables that are still unknown</phase>
</substep>
<substep n="1b" title="Load Required Components">
<substep n="1c" title="Monorepo Context Enforcement">
{{monorepo_context_logic}}
</substep>
<substep n="1d" title="Load Required Components">
<mandate>Instructions: Read COMPLETE file from path OR embedded list (REQUIRED)</mandate>
<check>If template path → Read COMPLETE template file</check>
<check>If validation path → Note path for later loading when needed</check>
@ -35,7 +42,7 @@
<note>Data files (csv, json) → Store paths only, load on-demand when instructions reference them</note>
</substep>
<substep n="1c" title="Initialize Output" if="template-workflow">
<substep n="1e" title="Initialize Output" if="template-workflow">
<action>Resolve default_output_file path with all variables and {{date}}</action>
<action>Create output directory if doesn't exist</action>
<action>If template-workflow → Write template to output file with placeholders</action>

View File

@ -20,6 +20,42 @@
<flow>
<step n="1" title="Method Registry Loading">
<!-- Retained inline check because this workflow may be invoked as a standalone tool mid-conversation, bypassing the OS-level context injection. -->
<!-- Step 1: Check for inline project override (#project:NAME or #p:NAME) -->
<action>Scan request for pattern #project:NAME or #p:NAME (case-insensitive)</action>
<check if="inline override found">
<action>Set project_suffix = extracted NAME</action>
</check>
<!-- Step 2: Fall back to .current_project file -->
<check if="project_suffix not yet set AND {project-root}/_bmad/.current_project exists">
<action>Read content as project_suffix</action>
</check>
<!-- Step 3: Validate and Canonicalize -->
<check if="project_suffix is set">
<!-- Security: Reject traversal, absolute paths, and invalid patterns -->
<check if="project_suffix is empty OR project_suffix contains '..' or starts with '/' or starts with '\'">
<output>🚫 Security Error: Invalid project context path detected — path traversal or absolute path detected.</output>
<action>HALT</action>
</check>
<!-- Whitelist: Alphanumeric, dots, dashes, underscores, AND slashes (for nested segments) -->
<check if="project_suffix matches regex '[^a-zA-Z0-9._-/]|^\s*$'">
<output>🚫 Error: Project context must only contain alphanumeric characters, dots, dashes, underscores, or slashes.</output>
<action>HALT</action>
</check>
<action>Override project_name to {project_suffix}</action>
<action>Override output_folder to {project-root}/_bmad-output/{project_suffix}</action>
<action>Override planning_artifacts to {project-root}/_bmad-output/{project_suffix}</action>
<action>Override implementation_artifacts to {project-root}/_bmad-output/{project_suffix}</action>
<action>Override project_knowledge to {project-root}/_bmad-output/{project_suffix}</action>
<action>Override sprint_status_file to {project-root}/_bmad-output/{project_suffix}/sprint-status.yaml</action>
<action>Output "Monorepo context detected. Paths adjusted to: {project_suffix}"</action>
</check>
<action>Load and read {{methods}} and {{agent-party}}</action>
<csv-structure>

View File

@ -32,9 +32,10 @@ This uses **micro-file architecture** for disciplined execution:
## INITIALIZATION
### Configuration Loading
### 1. Configuration Loading
Load and read full config from {main_config} and resolve variables and artifact paths.
Load config from `{project-root}/_bmad/core/config.yaml` and resolve:
- `project_name`, `output_folder`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -25,9 +25,9 @@ This uses **micro-file architecture** with **sequential conversation orchestrati
## INITIALIZATION
### Configuration Loading
### 1. Configuration Loading
Load config from `{project-root}/_bmad/core/config.yaml` and resolve:
Load and read full config from {main_config} and resolve variables and artifact paths.
- `project_name`, `output_folder`, `user_name`
- `communication_language`, `document_output_language`, `user_skill_level`

View File

@ -0,0 +1,214 @@
/**
* Context Logic Integration Tests
*
* Validates the centralized monorepo context logic deduplication:
* 1. context-logic.js exports a valid XML block
* 2. All workflow templates that need it use the {{monorepo_context_logic}} placeholder
* 3. No stale hardcoded <monorepo-context-check> blocks exist in templates
* 4. src/core/tasks/workflow.xml uses the placeholder (not a hardcoded block)
* 5. All JS consumers correctly import context-logic.js
* 6. MONOREPO_CONTEXT_LOGIC string integrity (key fields are present)
*/
const fs = require('fs-extra');
const path = require('node:path');
// ANSI colors
const c = {
reset: '\u001B[0m',
green: '\u001B[32m',
red: '\u001B[31m',
yellow: '\u001B[33m',
cyan: '\u001B[36m',
dim: '\u001B[2m',
};
let passed = 0;
let failed = 0;
function ok(condition, testName, detail = '') {
if (condition) {
console.log(`${c.green}${c.reset} ${testName}`);
passed++;
} else {
console.log(`${c.red}${c.reset} ${testName}`);
if (detail) console.log(` ${c.dim}${detail}${c.reset}`);
failed++;
}
}
async function readFile(p) {
return fs.readFile(p, 'utf8');
}
async function exists(p) {
return fs.pathExists(p);
}
async function runTests() {
console.log(`${c.cyan}============================================================`);
console.log(' Context Logic Integration Tests');
console.log(`============================================================${c.reset}\n`);
const root = path.join(__dirname, '..');
const sharedDir = path.join(root, 'tools/cli/installers/lib/ide/shared');
const templatesDir = path.join(root, 'tools/cli/installers/lib/ide/templates');
const combinedDir = path.join(templatesDir, 'combined');
// ────────────────────────────────────────────────────────────
// Suite 1: context-logic.js module integrity
// ────────────────────────────────────────────────────────────
console.log(`${c.yellow}Suite 1: context-logic.js module integrity${c.reset}\n`);
const contextLogicPath = path.join(sharedDir, 'context-logic.js');
ok(await exists(contextLogicPath), 'context-logic.js file exists');
let MONOREPO_CONTEXT_LOGIC;
try {
const mod = require(contextLogicPath);
ok(typeof mod.MONOREPO_CONTEXT_LOGIC === 'string', 'exports MONOREPO_CONTEXT_LOGIC as a string');
ok(mod.MONOREPO_CONTEXT_LOGIC.length > 0, 'MONOREPO_CONTEXT_LOGIC is non-empty');
MONOREPO_CONTEXT_LOGIC = mod.MONOREPO_CONTEXT_LOGIC;
} catch (error) {
ok(false, 'context-logic.js is require()-able', error.message);
MONOREPO_CONTEXT_LOGIC = '';
}
// Key content checks
ok(MONOREPO_CONTEXT_LOGIC.includes('<monorepo-context-check'), 'has opening <monorepo-context-check> tag');
ok(MONOREPO_CONTEXT_LOGIC.includes('</monorepo-context-check>'), 'has closing </monorepo-context-check> tag');
ok(MONOREPO_CONTEXT_LOGIC.includes('#project:NAME'), 'documents #project:NAME syntax');
ok(MONOREPO_CONTEXT_LOGIC.includes('#p:NAME'), 'documents #p:NAME short alias');
ok(MONOREPO_CONTEXT_LOGIC.includes('.current_project'), 'includes .current_project fallback logic');
ok(MONOREPO_CONTEXT_LOGIC.includes('path traversal'), 'includes path traversal security check');
ok(MONOREPO_CONTEXT_LOGIC.includes('output_folder'), 'overrides output_folder path variable');
ok(MONOREPO_CONTEXT_LOGIC.includes('planning_artifacts'), 'overrides planning_artifacts path variable');
ok(MONOREPO_CONTEXT_LOGIC.includes('HALT'), 'halts on security violation');
console.log('');
// ────────────────────────────────────────────────────────────
// Suite 2: JS consumers import context-logic.js correctly
// ────────────────────────────────────────────────────────────
console.log(`${c.yellow}Suite 2: JS consumers import context-logic.js${c.reset}\n`);
const consumers = [
{
file: 'tools/cli/installers/lib/core/installer.js',
expectedImport: "require('../ide/shared/context-logic')",
},
{
file: 'tools/cli/installers/lib/ide/_config-driven.js',
expectedImport: "require('./shared/context-logic')",
},
{
file: 'tools/cli/installers/lib/ide/shared/workflow-command-generator.js',
expectedImport: "require('./context-logic')",
},
];
for (const { file, expectedImport } of consumers) {
const fullPath = path.join(root, file);
try {
const content = await readFile(fullPath);
ok(content.includes(expectedImport), `${path.basename(file)} imports context-logic correctly`);
ok(content.includes("replaceAll('{{monorepo_context_logic}}'"), `${path.basename(file)} uses replaceAll for placeholder`);
} catch (error) {
ok(false, `File not found or unreadable: ${fullPath} - ${error.message}`);
}
}
console.log('');
// ────────────────────────────────────────────────────────────
// Suite 3: Templates use placeholder, not hardcoded blocks
// ────────────────────────────────────────────────────────────
console.log(`${c.yellow}Suite 3: Templates use {{monorepo_context_logic}} placeholder${c.reset}\n`);
// These templates MUST have the placeholder (they are rendered directly as IDE workflow commands)
const mustHavePlaceholder = [
path.join(templatesDir, 'workflow-command-template.md'),
path.join(templatesDir, 'workflow-commander.md'),
path.join(combinedDir, 'antigravity.md'),
path.join(combinedDir, 'claude-workflow.md'),
path.join(combinedDir, 'claude-workflow-yaml.md'),
path.join(combinedDir, 'default-workflow.md'),
path.join(combinedDir, 'default-workflow-yaml.md'),
path.join(combinedDir, 'kiro-workflow.md'),
path.join(combinedDir, 'opencode-workflow.md'),
path.join(combinedDir, 'windsurf-workflow.md'),
];
for (const filePath of mustHavePlaceholder) {
const rel = path.relative(root, filePath);
try {
const content = await readFile(filePath);
ok(content.includes('{{monorepo_context_logic}}'), `${path.basename(filePath)} has {{monorepo_context_logic}} placeholder`);
// Must NOT have raw hardcoded block (only the shared module should have it)
ok(!content.includes('<monorepo-context-check'), `${path.basename(filePath)} has NO hardcoded <monorepo-context-check> block`);
} catch (error) {
ok(false, `File not found or unreadable: ${filePath} - ${error.message}`);
}
}
console.log('');
// ────────────────────────────────────────────────────────────
// Suite 4: No rogue hardcoded blocks anywhere in templates dir
// ────────────────────────────────────────────────────────────
console.log(`${c.yellow}Suite 4: No hardcoded blocks in templates directory${c.reset}\n`);
const walkDir = async (dir) => {
const entries = await fs.readdir(dir, { withFileTypes: true });
const files = [];
for (const e of entries) {
const full = path.join(dir, e.name);
if (e.isDirectory()) files.push(...(await walkDir(full)));
else if (e.isFile()) files.push(full);
}
return files;
};
const allTemplateFiles = await walkDir(templatesDir);
const rogueFiles = [];
for (const f of allTemplateFiles) {
const content = await readFile(f);
if (content.includes('<monorepo-context-check') && !f.includes('context-logic.js')) {
rogueFiles.push(path.relative(root, f));
}
}
ok(
rogueFiles.length === 0,
`No hardcoded <monorepo-context-check> blocks in templates (found ${rogueFiles.length})`,
rogueFiles.length > 0 ? `Rogue files: ${rogueFiles.join(', ')}` : '',
);
console.log('');
// ────────────────────────────────────────────────────────────
// Suite 5: src/core/tasks/workflow.xml uses placeholder
// ────────────────────────────────────────────────────────────
console.log(`${c.yellow}Suite 5: src/core/tasks/workflow.xml uses placeholder${c.reset}\n`);
const srcWorkflowXml = path.join(root, 'src/core/tasks/workflow.xml');
ok(await exists(srcWorkflowXml), 'src/core/tasks/workflow.xml exists');
const srcXmlContent = await readFile(srcWorkflowXml);
ok(srcXmlContent.includes('{{monorepo_context_logic}}'), 'workflow.xml (src) uses {{monorepo_context_logic}} placeholder');
ok(!srcXmlContent.includes('<monorepo-context-check'), 'workflow.xml (src) has NO hardcoded <monorepo-context-check> block');
// ────────────────────────────────────────────────────────────
// Results
// ────────────────────────────────────────────────────────────
console.log(`\n${c.cyan}============================================================`);
console.log(` Results: ${c.green}${passed} passed${c.reset}${c.cyan}, ${c.red}${failed} failed${c.reset}${c.cyan}`);
console.log(`============================================================${c.reset}\n`);
if (failed === 0) {
console.log(`${c.green}✨ All context-logic integration tests passed!${c.reset}\n`);
process.exit(0);
} else {
console.log(`${c.red}${failed} test(s) failed${c.reset}\n`);
process.exit(1);
}
}
runTests().catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -0,0 +1,145 @@
/**
* Monorepo Support Validation Tests
*
* Architecture after deduplication:
* - Monorepo context logic lives ONLY in context-logic.js
* - workflow.xml (src) uses {{monorepo_context_logic}} placeholder injected at install time
* - Individual source workflow files do NOT have inline checks (that's the deduplication!)
* - Only code-review/instructions.xml, dev-story/instructions.xml, create-story/instructions.xml
* and advanced-elicitation/workflow.xml are XML workflows checked; XML workflows that go through
* workflow.xml no longer need inline checks.
*
* Verifies:
* 1. The set-project workflow is correctly registered.
* 2. No source workflow file has a stale inline "Monorepo Context Check" block.
* 3. Only the canonical SINGLE source (context-logic.js) defines the check.
* 4. set-project implementation still manages .current_project.
*/
const fs = require('fs-extra');
const path = require('node:path');
const { globSync } = require('glob');
// ANSI colors
const colors = {
reset: '\u001B[0m',
green: '\u001B[32m',
red: '\u001B[31m',
yellow: '\u001B[33m',
cyan: '\u001B[36m',
dim: '\u001B[2m',
};
let passed = 0;
let failed = 0;
function assert(condition, testName, errorMessage = '') {
if (condition) {
console.log(`${colors.green}${colors.reset} ${testName}`);
passed++;
} else {
console.log(`${colors.red}${colors.reset} ${testName}`);
if (errorMessage) {
console.log(` ${colors.dim}${errorMessage}${colors.reset}`);
}
failed++;
}
}
async function runTests() {
console.log(`${colors.cyan}========================================`);
console.log('Monorepo Support Validation Tests');
console.log(`========================================${colors.reset}\n`);
const projectRoot = path.join(__dirname, '..');
// 1. Verify set-project registration
console.log(`${colors.yellow}Test Suite 1: Workflow Registration${colors.reset}\n`);
try {
const csvPath = path.join(projectRoot, 'src/bmm/module-help.csv');
const content = await fs.readFile(csvPath, 'utf8');
assert(content.includes('set-project'), 'set-project workflow is registered in module-help.csv');
} catch (error) {
assert(false, 'Registration check failed', error.message);
}
console.log('');
// 2. Verify NO stale inline "Monorepo Context Check" blocks in source workflow files
// These are redundant since workflow.xml now handles context injection via context-logic.js
console.log(`${colors.yellow}Test Suite 2: No Stale Inline Monorepo Context Checks${colors.reset}\n`);
console.log(` ${colors.dim}(Inline checks were moved to workflow.xml via context-logic.js)${colors.reset}\n`);
const workflowFiles = globSync('src/{core,bmm}/workflows/**/*.{md,xml}', { cwd: projectRoot });
const exceptions = [
'context-logic.js',
'code-review/instructions.xml',
'create-story/instructions.xml',
'dev-story/instructions.xml',
'advanced-elicitation/workflow.xml',
'deep-dive-instructions.md',
];
for (const file of workflowFiles) {
if (exceptions.some((e) => file.endsWith(e))) continue;
const content = await fs.readFile(path.join(projectRoot, file), 'utf8');
const hasMarkdownCheck = content.includes('**Monorepo Context Check:**');
const hasXmlCheck = /<check\s+if=.*_bmad\/\.current_project.*/.test(content);
assert(!hasMarkdownCheck && !hasXmlCheck, `No stale inline check block in: ${file}`);
}
console.log('');
// 3. Verify canonical source is context-logic.js (single source of truth)
console.log(`${colors.yellow}Test Suite 3: Single Source of Truth${colors.reset}\n`);
const contextLogicPath = path.join(projectRoot, 'tools/cli/installers/lib/ide/shared/context-logic.js');
assert(await fs.pathExists(contextLogicPath), 'context-logic.js exists as canonical source');
const srcWorkflowXml = path.join(projectRoot, 'src/core/tasks/workflow.xml');
const xmlContent = await fs.readFile(srcWorkflowXml, 'utf8');
assert(xmlContent.includes('{{monorepo_context_logic}}'), 'workflow.xml uses {{monorepo_context_logic}} placeholder');
assert(!xmlContent.includes('**Monorepo Context Check:**'), 'workflow.xml has no stale inline check');
console.log('');
// 4. Verify set-project implementation
console.log(`${colors.yellow}Test Suite 4: set-project Implementation${colors.reset}\n`);
try {
const setProjectPath = path.join(projectRoot, 'src/bmm/workflows/0-context/set-project/workflow.md');
const exists = await fs.pathExists(setProjectPath);
assert(exists, 'set-project workflow file exists');
if (exists) {
const content = await fs.readFile(setProjectPath, 'utf8');
assert(content.includes('{{bmadFolderName}}/.current_project'), 'set-project implementation manages .current_project');
const examplePattern = /(?:example|my[-_ ]?app|[a-z0-9]+-[a-z0-9]+)/i;
assert(examplePattern.test(content), 'set-project examples use generic public-friendly names');
}
} catch (error) {
assert(false, 'set-project check failed', error.message);
}
console.log('\n');
console.log(`${colors.cyan}========================================`);
console.log('Test Results:');
console.log(` Passed: ${colors.green}${passed}${colors.reset}`);
console.log(` Failed: ${colors.red}${failed}${colors.reset}`);
console.log(`========================================${colors.reset}\n`);
if (failed === 0) {
console.log(`${colors.green}✨ All monorepo validation tests passed!${colors.reset}\n`);
process.exit(0);
} else {
console.log(`${colors.red}❌ Some monorepo validation tests failed${colors.reset}\n`);
process.exit(1);
}
}
runTests().catch((error) => {
console.error(error);
process.exit(1);
});

59
tools/audit-file-refs.md Normal file
View File

@ -0,0 +1,59 @@
# audit-file-refs
Audit new-format BMAD source files for file-reference convention violations using parallel Haiku subagents.
## Convention
In new-format BMAD workflow and task files (`src/bmm/`, `src/core/`, `src/utility/`), every file path reference must use one of these **valid** forms:
- `{project-root}/_bmad/path/to/file.ext` — canonical form, always correct
- `{installed_path}/relative/path` — valid in new-format step files (always defined by workflow.md before any step is reached)
- Template/runtime variables: `{nextStepFile}`, `{workflowFile}`, `{{mustache}}`, `{output_folder}`, `{communication_language}`, etc. — skip these, they are substituted at runtime
**Flag any reference that uses:**
- `./step-NN.md` or `../something.md` — relative paths
- `step-NN.md` — bare filename with no path prefix
- `steps/step-NN.md` — bare steps-relative path (missing `{project-root}/_bmad/...` prefix)
- `` `_bmad/core/tasks/help.md` `` — bare `_bmad/` path (missing `{project-root}/`)
- `/Users/...`, `/home/...`, `C:\...` — absolute system paths
References inside fenced code blocks (``` ``` ```) are examples — skip them.
Old-format files in `src/bmm/workflows/4-implementation/` use `{installed_path}` by design within the XML calling chain — exclude that directory entirely.
## Steps
1. Run this command to get the file list:
```
find src/bmm src/core src/utility -type f \( -name "*.md" -o -name "*.yaml" \) | grep -v "4-implementation" | sort
```
2. Divide the resulting file paths into batches of roughly 20 files each.
3. For each batch, spawn a subagent (`subagent_type: "Explore"`, `model: "haiku"`) with this prompt (fill in the actual file paths):
> Read each of these files (use the Read tool on each):
> [list the file paths from this batch]
>
> For each file, identify every line that contains a file path reference that violates the convention described below. Skip references inside fenced code blocks. Skip template variables (anything containing `{` that isn't `{project-root}` or `{installed_path}`).
>
> **Valid references:** `{project-root}/_bmad/...`, `{installed_path}/...`, template variables.
> **Flag:** bare filenames (`step-NN.md`), `./` or `../` relative paths, bare `steps/` paths, bare `_bmad/` paths (without `{project-root}/`), absolute system paths.
>
> Return findings as a list:
> `path/to/file.md:LINE_NUMBER | VIOLATION_TYPE | offending text`
>
> If a file has no violations, include it as: `path/to/file.md | clean`
>
> End your response with a single line: `FILES CHECKED: N` where N is the exact number of files you read.
4. Collect all findings from all subagents.
5. **Self-check before reporting:** Count the total number of files returned by the `find` command. Sum the `FILES CHECKED: N` values across all subagent responses. If the totals do not match, identify which files are missing and re-run subagents for those files before proceeding. Do not produce the final report until all files are accounted for.
6. Output a final report:
- Group findings by violation type
- List each finding as `file:line — offending text`
- Show total count of violations and number of affected files
- If nothing found, say "All files conform to the convention."

View File

@ -16,6 +16,7 @@ const { IdeConfigManager } = require('./ide-config-manager');
const { CustomHandler } = require('../custom/handler');
const prompts = require('../../../lib/prompts');
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
const { MONOREPO_CONTEXT_LOGIC } = require('../ide/shared/context-logic');
class Installer {
constructor() {
@ -89,6 +90,17 @@ class Installer {
// Read the file content
let content = await fs.readFile(sourcePath, 'utf8');
// Apply replacements in an order that protects _bmad-output literals.
// 1. First, inject the monorepo logic (which now uses {{bmadFolderName}} for its config dir references).
content = content.replaceAll('{{monorepo_context_logic}}', MONOREPO_CONTEXT_LOGIC);
// 2. Perform a precise replacement of the generic '_bmad' folder name using a negative lookahead
// to avoid corrupting the fixed '_bmad-output' folder name.
content = content.replaceAll(/_bmad(?!-output)/g, this.bmadFolderName);
// 3. Finally, resolve the explicit placeholder used in centralized context logic.
content = content.replaceAll('{{bmadFolderName}}', this.bmadFolderName);
// Write to target with replaced content
await fs.ensureDir(path.dirname(targetPath));
await fs.writeFile(targetPath, content, 'utf8');

View File

@ -391,17 +391,21 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
// No default
}
const { MONOREPO_CONTEXT_LOGIC } = require('./shared/context-logic');
let rendered = template
.replaceAll('{{name}}', artifact.name || '')
.replaceAll('{{module}}', artifact.module || 'core')
.replaceAll('{{path}}', pathToUse)
.replaceAll('{{description}}', artifact.description || `${artifact.name} ${artifact.type || ''}`)
.replaceAll('{{workflow_path}}', pathToUse);
.replaceAll('{{workflow_path}}', pathToUse)
.replaceAll('{{monorepo_context_logic}}', MONOREPO_CONTEXT_LOGIC);
// Replace _bmad placeholder with actual folder name
rendered = rendered.replaceAll('_bmad', this.bmadFolderName);
// Replace _bmad placeholder with actual folder name using precise regex
// This protects literals like '_bmad-output' from corruption.
rendered = rendered.replaceAll(/_bmad(?!-output)/g, this.bmadFolderName);
// Replace {{bmadFolderName}} placeholder if present
// Replace {{bmadFolderName}} placeholder (used in centralized context logic)
rendered = rendered.replaceAll('{{bmadFolderName}}', this.bmadFolderName);
return rendered;

View File

@ -437,9 +437,9 @@ tools: ${toolsStr}
- **Communication Language**: ${configVars.communication_language || '{{communication_language}}'}
- **Document Output Language**: ${configVars.document_output_language || '{{document_output_language}}'}
- **User Skill Level**: ${configVars.user_skill_level || '{{user_skill_level}}'}
- **Output Folder**: ${configVars.output_folder || '{{output_folder}}'}
- **Planning Artifacts**: ${configVars.planning_artifacts || '{{planning_artifacts}}'}
- **Implementation Artifacts**: ${configVars.implementation_artifacts || '{{implementation_artifacts}}'}
- **Output Folder**: ${configVars.output_folder || '{{output_folder}}'}/{context} (Dynamic)
- **Planning Artifacts**: ${configVars.planning_artifacts || '{{planning_artifacts}}'}/{context} (Dynamic)
- **Implementation Artifacts**: ${configVars.implementation_artifacts || '{{implementation_artifacts}}'}/{context} (Dynamic)
- **Project Knowledge**: ${configVars.project_knowledge || '{{project_knowledge}}'}
## BMAD Runtime Structure

View File

@ -0,0 +1,51 @@
/**
* Monorepo Context Logic XML Block
*
* robust, secure, and centralized logic for handling:
* 1. Inline project overrides (#p:NAME)
* 2. .current_project file fallback
* 3. Path variable overrides
*/
const MONOREPO_CONTEXT_LOGIC = `
<monorepo-context-check CRITICAL="TRUE" priority="before-config">
<!-- Step 1: Check for inline project override in user invocation -->
<!-- Supported syntax: #project:NAME (full) or #p:NAME (short alias) -->
<action>Scan user's invocation message for pattern #project:NAME or #p:NAME (case-insensitive)</action>
<check if="inline override found">
<action>Set project_suffix = extracted NAME</action>
<output>🎯 Inline project override: {project_suffix}</output>
</check>
<!-- Step 2: Fall back to .current_project file -->
<check if="project_suffix not yet set AND {project-root}/{{bmadFolderName}}/.current_project exists">
<action>Read {project-root}/{{bmadFolderName}}/.current_project as project_suffix</action>
</check>
<!-- Step 3: Validate -->
<check if="project_suffix is set">
<action>Trim whitespace and newlines from project_suffix</action>
<!-- Security: Reject traversal, absolute paths, and invalid patterns -->
<check if="project_suffix is empty OR project_suffix contains '..' OR starts with '/' OR starts with '\\\\'">
<output>🚫 Security Error: Invalid project context path traversal or absolute path detected.</output>
<action>HALT</action>
</check>
<!-- Whitelist: Alphanumeric, dots, dashes, underscores, AND slashes (for nested segments) -->
<check if="project_suffix matches regex '[^a-zA-Z0-9._-/]|^\\\\s*$'">
<output>🚫 Error: project_suffix must only contain alphanumeric characters, dots, dashes, underscores, or slashes.</output>
<action>HALT</action>
</check>
<!-- Step 4: Override path and identification variables -->
<action>Override project_name = {project_suffix}</action>
<action>Override output_folder = {project-root}/_bmad-output/{project_suffix}</action>
<action>Override planning_artifacts = {output_folder}/planning-artifacts</action>
<action>Override implementation_artifacts = {output_folder}/implementation-artifacts</action>
<action>Override project_knowledge = {output_folder}/knowledge</action>
<action>Override sprint_status_file = {output_folder}/sprint-status.yaml</action>
<output>🗂 Monorepo context: {project_suffix} outputs to {output_folder}</output>
</check>
</monorepo-context-check>
`;
module.exports = { MONOREPO_CONTEXT_LOGIC };

View File

@ -151,13 +151,22 @@ class WorkflowCommandGenerator {
}
}
const { MONOREPO_CONTEXT_LOGIC } = require('./context-logic');
// Replace template variables
return template
return (
template
.replaceAll('{{name}}', workflow.name)
.replaceAll('{{module}}', workflow.module)
.replaceAll('{{description}}', workflow.description)
.replaceAll('{{workflow_path}}', workflowPath)
.replaceAll('_bmad', this.bmadFolderName);
.replaceAll('{{monorepo_context_logic}}', MONOREPO_CONTEXT_LOGIC)
// Replace _bmad placeholder with actual folder name using precise regex
// This protects literals like '_bmad-output' from corruption.
.replaceAll(/_bmad(?!-output)/g, this.bmadFolderName)
// Replace {{bmadFolderName}} placeholder (used in centralized context logic)
.replaceAll('{{bmadFolderName}}', this.bmadFolderName)
);
}
/**

View File

@ -3,6 +3,8 @@ name: '{{name}}'
description: '{{description}}'
---
{{monorepo_context_logic}}
Read the entire workflow file at: {project-root}/_bmad/{{workflow_path}}
Follow all instructions in the workflow file exactly as written.

View File

@ -6,6 +6,8 @@ disable-model-invocation: true
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
{{monorepo_context_logic}}
<steps CRITICAL="TRUE">
1. Always LOAD the FULL @{project-root}/{{bmadFolderName}}/core/tasks/workflow.xml
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config @{project-root}/{{bmadFolderName}}/{{path}}

View File

@ -4,4 +4,6 @@ description: '{{description}}'
disable-model-invocation: true
---
{{monorepo_context_logic}}
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @{project-root}/{{bmadFolderName}}/{{path}}, READ its entire contents and follow its directions exactly!

View File

@ -4,4 +4,6 @@ inclusion: manual
# {{name}}
{{monorepo_context_logic}}
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL #[[file:{{bmadFolderName}}/{{path}}]], READ its entire contents and follow its directions exactly!

View File

@ -3,6 +3,8 @@ name: '{{name}}'
description: '{{description}}'
---
{{monorepo_context_logic}}
Execute the BMAD '{{name}}' workflow.
CRITICAL: You must load and follow the workflow definition exactly.

View File

@ -5,6 +5,8 @@ auto_execution_mode: "iterate"
# {{name}}
{{monorepo_context_logic}}
Read the entire workflow file at {project-root}/_bmad/{{workflow_path}}
Follow all instructions in the workflow file exactly as written.

View File

@ -3,6 +3,8 @@ description: '{{description}}'
disable-model-invocation: true
---
{{monorepo_context_logic}}
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
<steps CRITICAL="TRUE">

View File

@ -3,4 +3,6 @@ description: '{{description}}'
disable-model-invocation: true
---
{{monorepo_context_logic}}
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @{{workflow_path}}, READ its entire contents and follow its directions exactly!