fix: restore runtime workflow paths and standalone parsing
This commit is contained in:
parent
3feb0d378f
commit
e86fa2ee9e
|
|
@ -15,9 +15,6 @@ epicsTemplate: '{workflow_path}/templates/epics-template.md'
|
|||
# Task References
|
||||
advancedElicitationTask: '{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.md'
|
||||
partyModeWorkflow: '{project-root}/_bmad/core/workflows/party-mode/workflow.md'
|
||||
|
||||
# Template References
|
||||
epicsTemplate: '{workflow_path}/templates/epics-template.md'
|
||||
---
|
||||
|
||||
# Step 1: Validate Prerequisites and Extract Requirements
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ web_bundle: false
|
|||
- `project_knowledge`
|
||||
- `sprint_status` = `{implementation_artifacts}/sprint-status.yaml`
|
||||
- `date` (system-generated)
|
||||
- `installed_path` = `src/bmm/workflows/4-implementation/correct-course`
|
||||
- `installed_path` = `{project-root}/_bmad/bmm/workflows/4-implementation/correct-course`
|
||||
- `default_output_file` = `{planning_artifacts}/sprint-change-proposal-{date}.md`
|
||||
|
||||
<workflow>
|
||||
|
|
@ -28,6 +28,6 @@ web_bundle: false
|
|||
</step>
|
||||
|
||||
<step n="2" goal="Validate proposal quality">
|
||||
<invoke-task>Validate against checklist at {installed_path}/checklist.md using src/core/tasks/validate-workflow.md</invoke-task>
|
||||
<invoke-task>Validate against checklist at {installed_path}/checklist.md using {project-root}/_bmad/core/tasks/validate-workflow.md</invoke-task>
|
||||
</step>
|
||||
</workflow>
|
||||
|
|
|
|||
|
|
@ -558,6 +558,126 @@ web_bundle:
|
|||
|
||||
console.log('');
|
||||
|
||||
// ============================================================
|
||||
// Test 15: Correct-Course Installed Path Guard
|
||||
// ============================================================
|
||||
console.log(`${colors.yellow}Test Suite 15: Correct-Course Installed Path Guard${colors.reset}\n`);
|
||||
|
||||
try {
|
||||
const workflowPath = path.join(projectRoot, 'src', 'bmm', 'workflows', '4-implementation', 'correct-course', 'workflow.md');
|
||||
const content = await fs.readFile(workflowPath, 'utf8');
|
||||
|
||||
assert(
|
||||
content.includes('`installed_path` = `{project-root}/_bmad/bmm/workflows/4-implementation/correct-course`'),
|
||||
'Correct-course workflow uses installed runtime path',
|
||||
);
|
||||
assert(
|
||||
content.includes('{project-root}/_bmad/core/tasks/validate-workflow.md'),
|
||||
'Correct-course workflow uses installed validate-workflow task path',
|
||||
);
|
||||
} catch (error) {
|
||||
assert(false, 'Correct-course installed path guard runs', error.message);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
// ============================================================
|
||||
// Test 16: Task/Tool Standalone and CRLF Parsing Guard
|
||||
// ============================================================
|
||||
console.log(`${colors.yellow}Test Suite 16: Task/Tool Standalone + CRLF Guard${colors.reset}\n`);
|
||||
|
||||
try {
|
||||
const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-standalone-crlf-'));
|
||||
const coreTasksDir = path.join(tmpRoot, '_bmad', 'core', 'tasks');
|
||||
const coreToolsDir = path.join(tmpRoot, '_bmad', 'core', 'tools');
|
||||
await fs.ensureDir(coreTasksDir);
|
||||
await fs.ensureDir(coreToolsDir);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(coreTasksDir, 'default-task.md'),
|
||||
`---
|
||||
name: default-task
|
||||
displayName: Default Task
|
||||
description: Defaults to standalone
|
||||
---
|
||||
`,
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(coreTasksDir, 'internal-task.md'),
|
||||
`---
|
||||
name: internal-task
|
||||
displayName: Internal Task
|
||||
description: Hidden task
|
||||
internal: true
|
||||
---
|
||||
`,
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(coreTasksDir, 'crlf-task.md'),
|
||||
'---\r\nname: crlf-task\r\ndisplayName: CRLF Task\r\ndescription: Parsed from CRLF\r\nstandalone: true\r\n---\r\n',
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(coreToolsDir, 'default-tool.md'),
|
||||
`---
|
||||
name: default-tool
|
||||
displayName: Default Tool
|
||||
description: Defaults to standalone
|
||||
---
|
||||
`,
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(coreToolsDir, 'internal-tool.md'),
|
||||
`---
|
||||
name: internal-tool
|
||||
displayName: Internal Tool
|
||||
description: Hidden tool
|
||||
internal: true
|
||||
---
|
||||
`,
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(coreToolsDir, 'crlf-tool.md'),
|
||||
'---\r\nname: crlf-tool\r\ndisplayName: CRLF Tool\r\ndescription: Parsed from CRLF\r\nstandalone: true\r\n---\r\n',
|
||||
);
|
||||
|
||||
const manifestGenerator = new ManifestGenerator();
|
||||
const tasks = await manifestGenerator.getTasksFromDir(coreTasksDir, 'core');
|
||||
const tools = await manifestGenerator.getToolsFromDir(coreToolsDir, 'core');
|
||||
|
||||
const defaultTask = tasks.find((task) => task.name === 'default-task');
|
||||
const internalTask = tasks.find((task) => task.name === 'internal-task');
|
||||
const crlfTask = tasks.find((task) => task.name === 'crlf-task');
|
||||
const defaultTool = tools.find((tool) => tool.name === 'default-tool');
|
||||
const internalTool = tools.find((tool) => tool.name === 'internal-tool');
|
||||
const crlfTool = tools.find((tool) => tool.name === 'crlf-tool');
|
||||
|
||||
assert(defaultTask?.standalone === true, 'Tasks default to standalone when standalone key is omitted');
|
||||
assert(internalTask?.standalone === false, 'Tasks marked internal are excluded from standalone commands');
|
||||
assert(crlfTask?.description === 'Parsed from CRLF', 'CRLF task frontmatter is parsed correctly');
|
||||
|
||||
assert(defaultTool?.standalone === true, 'Tools default to standalone when standalone key is omitted');
|
||||
assert(internalTool?.standalone === false, 'Tools marked internal are excluded from standalone commands');
|
||||
assert(crlfTool?.description === 'Parsed from CRLF', 'CRLF tool frontmatter is parsed correctly');
|
||||
|
||||
const taskToolGenerator = new TaskToolCommandGenerator();
|
||||
assert(taskToolGenerator.isStandalone({}) === true, 'Task/tool command filter defaults missing standalone metadata to visible');
|
||||
assert(
|
||||
taskToolGenerator.isStandalone({ standalone: 'false' }) === false,
|
||||
'Task/tool command filter hides entries explicitly marked standalone=false',
|
||||
);
|
||||
|
||||
await fs.remove(tmpRoot);
|
||||
} catch (error) {
|
||||
assert(false, 'Task/tool standalone and CRLF guard runs', error.message);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
// ============================================================
|
||||
// Summary
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -383,18 +383,25 @@ class ManifestGenerator {
|
|||
let name = file.replace(/\.(xml|md)$/, '');
|
||||
let displayName = name;
|
||||
let description = '';
|
||||
let standalone = false;
|
||||
let standalone = true;
|
||||
|
||||
if (file.endsWith('.md')) {
|
||||
// Parse YAML frontmatter for .md tasks
|
||||
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (frontmatterMatch) {
|
||||
try {
|
||||
const frontmatter = yaml.parse(frontmatterMatch[1]);
|
||||
const frontmatter = yaml.parse(frontmatterMatch[1]) || {};
|
||||
name = frontmatter.name || name;
|
||||
displayName = frontmatter.displayName || frontmatter.name || name;
|
||||
description = frontmatter.description || '';
|
||||
standalone = frontmatter.standalone === true || frontmatter.standalone === 'true';
|
||||
const isInternal = frontmatter.internal === true || frontmatter.internal === 'true';
|
||||
if (frontmatter.standalone === true || frontmatter.standalone === 'true') {
|
||||
standalone = true;
|
||||
} else if (frontmatter.standalone === false || frontmatter.standalone === 'false') {
|
||||
standalone = false;
|
||||
} else {
|
||||
standalone = !isInternal;
|
||||
}
|
||||
} catch {
|
||||
// If YAML parsing fails, use defaults
|
||||
}
|
||||
|
|
@ -408,8 +415,16 @@ class ManifestGenerator {
|
|||
const objMatch = content.match(/<objective>([^<]+)<\/objective>/);
|
||||
description = descMatch ? descMatch[1] : objMatch ? objMatch[1].trim() : '';
|
||||
|
||||
const standaloneMatch = content.match(/<task[^>]+standalone="true"/);
|
||||
standalone = !!standaloneMatch;
|
||||
const standaloneTrueMatch = content.match(/<task[^>]+standalone="true"/i);
|
||||
const standaloneFalseMatch = content.match(/<task[^>]+standalone="false"/i);
|
||||
const internalMatch = content.match(/<task[^>]+internal="true"/i);
|
||||
if (standaloneFalseMatch) {
|
||||
standalone = false;
|
||||
} else if (standaloneTrueMatch) {
|
||||
standalone = true;
|
||||
} else {
|
||||
standalone = !internalMatch;
|
||||
}
|
||||
}
|
||||
|
||||
// Build relative path for installation
|
||||
|
|
@ -472,18 +487,25 @@ class ManifestGenerator {
|
|||
let name = file.replace(/\.(xml|md)$/, '');
|
||||
let displayName = name;
|
||||
let description = '';
|
||||
let standalone = false;
|
||||
let standalone = true;
|
||||
|
||||
if (file.endsWith('.md')) {
|
||||
// Parse YAML frontmatter for .md tools
|
||||
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (frontmatterMatch) {
|
||||
try {
|
||||
const frontmatter = yaml.parse(frontmatterMatch[1]);
|
||||
const frontmatter = yaml.parse(frontmatterMatch[1]) || {};
|
||||
name = frontmatter.name || name;
|
||||
displayName = frontmatter.displayName || frontmatter.name || name;
|
||||
description = frontmatter.description || '';
|
||||
standalone = frontmatter.standalone === true || frontmatter.standalone === 'true';
|
||||
const isInternal = frontmatter.internal === true || frontmatter.internal === 'true';
|
||||
if (frontmatter.standalone === true || frontmatter.standalone === 'true') {
|
||||
standalone = true;
|
||||
} else if (frontmatter.standalone === false || frontmatter.standalone === 'false') {
|
||||
standalone = false;
|
||||
} else {
|
||||
standalone = !isInternal;
|
||||
}
|
||||
} catch {
|
||||
// If YAML parsing fails, use defaults
|
||||
}
|
||||
|
|
@ -497,8 +519,16 @@ class ManifestGenerator {
|
|||
const objMatch = content.match(/<objective>([^<]+)<\/objective>/);
|
||||
description = descMatch ? descMatch[1] : objMatch ? objMatch[1].trim() : '';
|
||||
|
||||
const standaloneMatch = content.match(/<tool[^>]+standalone="true"/);
|
||||
standalone = !!standaloneMatch;
|
||||
const standaloneTrueMatch = content.match(/<tool[^>]+standalone="true"/i);
|
||||
const standaloneFalseMatch = content.match(/<tool[^>]+standalone="false"/i);
|
||||
const internalMatch = content.match(/<tool[^>]+internal="true"/i);
|
||||
if (standaloneFalseMatch) {
|
||||
standalone = false;
|
||||
} else if (standaloneTrueMatch) {
|
||||
standalone = true;
|
||||
} else {
|
||||
standalone = !internalMatch;
|
||||
}
|
||||
}
|
||||
|
||||
// Build relative path for installation
|
||||
|
|
|
|||
|
|
@ -24,7 +24,14 @@ class TaskToolCommandGenerator {
|
|||
* @returns {boolean} True when item should be exposed as a command
|
||||
*/
|
||||
isStandalone(item) {
|
||||
return item?.standalone === 'true' || item?.standalone === true;
|
||||
if (item?.standalone === false || item?.standalone === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (item?.internal === true || item?.internal === 'true') {
|
||||
return false;
|
||||
}
|
||||
// Backward-compatible default: entries are user-facing unless explicitly hidden.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue