fix: normalize custom bmad workflow paths in command generation
This commit is contained in:
parent
9987a25e3e
commit
0ba1167c60
|
|
@ -862,6 +862,56 @@ internal: true
|
|||
|
||||
console.log('');
|
||||
|
||||
// ============================================================
|
||||
// Test 22: Custom BMAD Folder Workflow Path Guard
|
||||
// ============================================================
|
||||
console.log(`${colors.yellow}Test Suite 22: Custom BMAD Folder Workflow Path Guard${colors.reset}\n`);
|
||||
|
||||
try {
|
||||
const generator = new WorkflowCommandGenerator('mybmad');
|
||||
generator.loadWorkflowManifest = async () => [
|
||||
{
|
||||
name: 'sprint-planning',
|
||||
description: 'Sprint Planning',
|
||||
module: 'bmm',
|
||||
path: '/tmp/project/mybmad/bmm/workflows/4-implementation/sprint-planning/workflow.md',
|
||||
},
|
||||
{
|
||||
name: 'create-story',
|
||||
description: 'Create Story',
|
||||
module: 'bmm',
|
||||
path: 'mybmad/bmm/workflows/4-implementation/create-story/workflow.md',
|
||||
},
|
||||
];
|
||||
generator.generateCommandContent = async () => 'content';
|
||||
|
||||
const { artifacts } = await generator.collectWorkflowArtifacts('/tmp');
|
||||
const sprintPlanning = artifacts.find((artifact) => artifact.name === 'sprint-planning');
|
||||
const createStory = artifacts.find((artifact) => artifact.name === 'create-story');
|
||||
|
||||
assert(
|
||||
sprintPlanning?.workflowPath === 'bmm/workflows/4-implementation/sprint-planning/workflow.md',
|
||||
'Custom folder absolute workflow path strips configured BMAD folder prefix',
|
||||
sprintPlanning?.workflowPath,
|
||||
);
|
||||
assert(
|
||||
createStory?.workflowPath === 'bmm/workflows/4-implementation/create-story/workflow.md',
|
||||
'Custom folder relative workflow path strips configured BMAD folder prefix',
|
||||
createStory?.workflowPath,
|
||||
);
|
||||
|
||||
const installedPath = generator.mapSourcePathToInstalled('/tmp/project/mybmad/core/tasks/workflow.md');
|
||||
assert(
|
||||
installedPath === 'mybmad/core/tasks/workflow.md',
|
||||
'Installed workflow path mapping handles absolute paths containing custom BMAD folder',
|
||||
installedPath,
|
||||
);
|
||||
} catch (error) {
|
||||
assert(false, 'Custom BMAD folder workflow path guard runs', error.message);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
for (const tmpRoot of tmpRoots) {
|
||||
await fs.remove(tmpRoot).catch(() => {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ const csv = require('csv-parse/sync');
|
|||
const prompts = require('../../../../lib/prompts');
|
||||
const { toColonPath, toDashPath, customAgentColonName, customAgentDashName, BMAD_FOLDER_NAME } = require('./path-utils');
|
||||
|
||||
function escapeRegex(value) {
|
||||
return String(value).replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates command files for each workflow in the manifest
|
||||
*/
|
||||
|
|
@ -67,22 +71,7 @@ class WorkflowCommandGenerator {
|
|||
|
||||
for (const workflow of allWorkflows) {
|
||||
const commandContent = await this.generateCommandContent(workflow, bmadDir);
|
||||
// Calculate the relative workflow path (e.g., bmm/workflows/4-implementation/sprint-planning/workflow.md)
|
||||
let workflowRelPath = workflow.path || '';
|
||||
workflowRelPath = workflowRelPath.replaceAll('\\', '/');
|
||||
// Remove _bmad/ prefix if present to get relative path from project root
|
||||
// Handle both absolute paths (/path/to/_bmad/...) and relative paths (_bmad/...)
|
||||
if (workflowRelPath.includes('_bmad/')) {
|
||||
const parts = workflowRelPath.split(/_bmad\//);
|
||||
if (parts.length > 1) {
|
||||
workflowRelPath = parts.at(-1);
|
||||
}
|
||||
} else if (workflowRelPath.includes('/src/') || workflowRelPath.startsWith('src/')) {
|
||||
const match = workflowRelPath.match(/(?:^|\/)src\/([^/]+)\/(.+)/);
|
||||
if (match) {
|
||||
workflowRelPath = `${match[1]}/${match[2]}`;
|
||||
}
|
||||
}
|
||||
const workflowRelPath = this.mapSourcePathToModuleRelative(workflow.path);
|
||||
artifacts.push({
|
||||
type: 'workflow-command',
|
||||
name: workflow.name,
|
||||
|
|
@ -213,6 +202,29 @@ When running any workflow:
|
|||
return this.mapSourcePathToInstalled(workflowPath, true);
|
||||
}
|
||||
|
||||
mapSourcePathToModuleRelative(sourcePath) {
|
||||
const mapped = this.mapSourcePathToInstalled(sourcePath, false);
|
||||
if (!mapped) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
const normalized = String(mapped).replaceAll('\\', '/');
|
||||
|
||||
// Typical installed path -> strip BMAD root prefix for templates that prepend it.
|
||||
if (normalized.startsWith(`${this.bmadFolderName}/`)) {
|
||||
return normalized.slice(`${this.bmadFolderName}/`.length);
|
||||
}
|
||||
|
||||
// Absolute path containing the configured BMAD root folder.
|
||||
const folderPattern = new RegExp(`(?:^|\\/)${escapeRegex(this.bmadFolderName)}\\/(.+)`);
|
||||
const folderMatch = normalized.match(folderPattern);
|
||||
if (folderMatch) {
|
||||
return folderMatch[1];
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
mapSourcePathToInstalled(sourcePath, includeProjectRootPrefix = false) {
|
||||
if (!sourcePath) {
|
||||
return sourcePath;
|
||||
|
|
@ -232,6 +244,15 @@ When running any workflow:
|
|||
return includeProjectRootPrefix ? `{project-root}/${mapped}` : mapped;
|
||||
}
|
||||
|
||||
// Handle absolute paths that already include the configured BMAD folder
|
||||
// (e.g., /tmp/project/mybmad/bmm/workflows/...).
|
||||
const folderPattern = new RegExp(`(?:^|\\/)${escapeRegex(this.bmadFolderName)}\\/(.+)`);
|
||||
const folderMatch = normalized.match(folderPattern);
|
||||
if (folderMatch) {
|
||||
const mapped = `${this.bmadFolderName}/${folderMatch[1]}`;
|
||||
return includeProjectRootPrefix ? `{project-root}/${mapped}` : mapped;
|
||||
}
|
||||
|
||||
if (normalized.startsWith(`${this.bmadFolderName}/`)) {
|
||||
return includeProjectRootPrefix ? `{project-root}/${normalized}` : normalized;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue