feat: Introduce monorepo context logic for inline project overrides and dynamic path adjustments in workflows.

This commit is contained in:
sno 2026-02-19 13:30:48 +01:00
parent e7cc716e87
commit a5bda6a80e
14 changed files with 96 additions and 5 deletions

View File

@ -34,3 +34,16 @@ Load and read full config from {main_config} and resolve basic variables.
### 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** (`_bmad/.current_project`)
3. **Default Config** (if neither is present)

View File

@ -18,7 +18,11 @@
<flow>
<step n="1" title="Load and Initialize Workflow">
<substep n="1a" title="Load Configuration and Resolve Variables">
<substep n="1a" title="Monorepo Context Injection">
{{monorepo_context_logic}}
</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>
@ -27,7 +31,7 @@
<phase n="4">Ask user for input of any variables that are still unknown</phase>
</substep>
<substep n="1b" title="Load Required Components">
<substep n="1c" 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 +39,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="1d" 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

@ -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,10 @@ class Installer {
// Read the file content
let content = await fs.readFile(sourcePath, 'utf8');
// Apply replacements
content = content.replaceAll('{{monorepo_context_logic}}', MONOREPO_CONTEXT_LOGIC);
content = content.replaceAll('_bmad', this.bmadFolderName);
// Write to target with replaced content
await fs.ensureDir(path.dirname(targetPath));
await fs.writeFile(targetPath, content, 'utf8');

View File

@ -391,12 +391,15 @@ 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);

View File

@ -0,0 +1,48 @@
/**
* 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}/_bmad/.current_project exists">
<action>Read {project-root}/_bmad/.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: Prevent path traversal and invalid chars -->
<check if="project_suffix contains '..' OR starts with '/' OR starts with '\\'">
<output>🚫 Security Error: Invalid project context path traversal detected.</output>
<action>HALT</action>
</check>
<check if="project_suffix matching regex [^a-zA-Z0-9._-]">
<output>🚫 Error: project_suffix must only contain alphanumeric characters, dots, dashes, or underscores.</output>
<action>HALT</action>
</check>
<!-- Step 4: Override path variables -->
<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,12 +151,15 @@ class WorkflowCommandGenerator {
}
}
const { MONOREPO_CONTEXT_LOGIC } = require('./context-logic');
// Replace template variables
return template
.replaceAll('{{name}}', workflow.name)
.replaceAll('{{module}}', workflow.module)
.replaceAll('{{description}}', workflow.description)
.replaceAll('{{workflow_path}}', workflowPath)
.replaceAll('{{monorepo_context_logic}}', MONOREPO_CONTEXT_LOGIC)
.replaceAll('_bmad', 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

@ -7,6 +7,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:
<steps CRITICAL="TRUE">
0. {{monorepo_context_logic}}
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}}
3. Pass the yaml path @{project-root}/{{bmadFolderName}}/{{path}} as 'workflow-config' parameter to the workflow.xml instructions

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}}
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL #[[file:{{bmadFolderName}}/{{path}}]], READ its entire contents and follow its directions exactly!
{{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

@ -8,6 +8,7 @@ Execute the BMAD '{{name}}' workflow.
CRITICAL: You must load and follow the workflow definition exactly.
WORKFLOW INSTRUCTIONS:
{{monorepo_context_logic}}
1. LOAD the workflow file from {project-root}/{{bmadFolderName}}/{{path}}
2. READ its entire contents

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!