From a5bda6a80e80252141c40e7f2e109dcb52cef008 Mon Sep 17 00:00:00 2001 From: sno Date: Thu, 19 Feb 2026 13:30:48 +0100 Subject: [PATCH] feat: Introduce monorepo context logic for inline project overrides and dynamic path adjustments in workflows. --- .../0-context/set-project/workflow.md | 13 +++++ src/core/tasks/workflow.xml | 10 ++-- tools/cli/installers/lib/core/installer.js | 5 ++ .../cli/installers/lib/ide/_config-driven.js | 5 +- .../lib/ide/shared/context-logic.js | 48 +++++++++++++++++++ .../ide/shared/workflow-command-generator.js | 3 ++ .../lib/ide/templates/combined/antigravity.md | 2 + .../combined/default-workflow-yaml.md | 2 + .../templates/combined/default-workflow.md | 2 + .../ide/templates/combined/kiro-workflow.md | 4 +- .../templates/combined/opencode-workflow.md | 1 + .../templates/combined/windsurf-workflow.md | 2 + .../templates/workflow-command-template.md | 2 + .../lib/ide/templates/workflow-commander.md | 2 + 14 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 tools/cli/installers/lib/ide/shared/context-logic.js diff --git a/src/bmm/workflows/0-context/set-project/workflow.md b/src/bmm/workflows/0-context/set-project/workflow.md index 738d8c0ca..51fa848c2 100644 --- a/src/bmm/workflows/0-context/set-project/workflow.md +++ b/src/bmm/workflows/0-context/set-project/workflow.md @@ -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) diff --git a/src/core/tasks/workflow.xml b/src/core/tasks/workflow.xml index 536c9d8e7..2b5503135 100644 --- a/src/core/tasks/workflow.xml +++ b/src/core/tasks/workflow.xml @@ -18,7 +18,11 @@ - + + {{monorepo_context_logic}} + + + Read workflow.yaml from provided path Load config_source (REQUIRED for all modules) Load external config from config_source path @@ -27,7 +31,7 @@ Ask user for input of any variables that are still unknown - + Instructions: Read COMPLETE file from path OR embedded list (REQUIRED) If template path → Read COMPLETE template file If validation path → Note path for later loading when needed @@ -35,7 +39,7 @@ Data files (csv, json) → Store paths only, load on-demand when instructions reference them - + Resolve default_output_file path with all variables and {{date}} Create output directory if doesn't exist If template-workflow → Write template to output file with placeholders diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index fe8b88d7c..e9c9518fe 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -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'); diff --git a/tools/cli/installers/lib/ide/_config-driven.js b/tools/cli/installers/lib/ide/_config-driven.js index 9541c75ed..68c3a18c9 100644 --- a/tools/cli/installers/lib/ide/_config-driven.js +++ b/tools/cli/installers/lib/ide/_config-driven.js @@ -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); diff --git a/tools/cli/installers/lib/ide/shared/context-logic.js b/tools/cli/installers/lib/ide/shared/context-logic.js new file mode 100644 index 000000000..f5fb7359e --- /dev/null +++ b/tools/cli/installers/lib/ide/shared/context-logic.js @@ -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 = ` + + + + Scan user's invocation message for pattern #project:NAME or #p:NAME (case-insensitive) + + Set project_suffix = extracted NAME + 🎯 Inline project override: {project_suffix} + + + + + Read {project-root}/_bmad/.current_project as project_suffix + + + + + Trim whitespace and newlines from project_suffix + + + 🚫 Security Error: Invalid project context — path traversal detected. + HALT + + + 🚫 Error: project_suffix must only contain alphanumeric characters, dots, dashes, or underscores. + HALT + + + + Override output_folder = {project-root}/_bmad-output/{project_suffix} + Override planning_artifacts = {output_folder}/planning-artifacts + Override implementation_artifacts = {output_folder}/implementation-artifacts + Override project_knowledge = {output_folder}/knowledge + Override sprint_status_file = {output_folder}/sprint-status.yaml + 🗂️ Monorepo context: {project_suffix} → outputs to {output_folder} + + +`; + +module.exports = { MONOREPO_CONTEXT_LOGIC }; diff --git a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js index d94e77db1..985e3437b 100644 --- a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +++ b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js @@ -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); } diff --git a/tools/cli/installers/lib/ide/templates/combined/antigravity.md b/tools/cli/installers/lib/ide/templates/combined/antigravity.md index 88e806e9d..b897563f9 100644 --- a/tools/cli/installers/lib/ide/templates/combined/antigravity.md +++ b/tools/cli/installers/lib/ide/templates/combined/antigravity.md @@ -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. diff --git a/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md b/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md index 2a5e49b83..605e6c57b 100644 --- a/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md +++ b/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md @@ -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: +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 diff --git a/tools/cli/installers/lib/ide/templates/combined/default-workflow.md b/tools/cli/installers/lib/ide/templates/combined/default-workflow.md index 8c4fa818f..8b7d9eea6 100644 --- a/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +++ b/tools/cli/installers/lib/ide/templates/combined/default-workflow.md @@ -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! diff --git a/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md b/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md index e1847f414..2041edfdd 100644 --- a/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md +++ b/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md @@ -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! diff --git a/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md b/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md index a6f5cb96f..e7596ea1d 100644 --- a/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md +++ b/tools/cli/installers/lib/ide/templates/combined/opencode-workflow.md @@ -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 diff --git a/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md b/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md index 6366425c7..cb2b8bb2d 100644 --- a/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +++ b/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md @@ -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. diff --git a/tools/cli/installers/lib/ide/templates/workflow-command-template.md b/tools/cli/installers/lib/ide/templates/workflow-command-template.md index 472c1553a..7a26a6684 100644 --- a/tools/cli/installers/lib/ide/templates/workflow-command-template.md +++ b/tools/cli/installers/lib/ide/templates/workflow-command-template.md @@ -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: diff --git a/tools/cli/installers/lib/ide/templates/workflow-commander.md b/tools/cli/installers/lib/ide/templates/workflow-commander.md index d49c8319d..6f9ed8759 100644 --- a/tools/cli/installers/lib/ide/templates/workflow-commander.md +++ b/tools/cli/installers/lib/ide/templates/workflow-commander.md @@ -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!