From 73d19baf7ee460ba46ebf6c62fdac50e35418c63 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Sun, 8 Mar 2026 15:09:21 -0600 Subject: [PATCH] refactor: remove YAML workflow code paths from CLI installer pipeline Co-Authored-By: Claude Opus 4.6 --- .../installers/lib/core/manifest-generator.js | 149 ++++++++---------- tools/cli/installers/lib/ide/_base-ide.js | 19 ++- .../cli/installers/lib/ide/_config-driven.js | 12 +- .../installers/lib/ide/shared/path-utils.js | 2 +- .../ide/shared/workflow-command-generator.js | 24 +-- tools/cli/installers/lib/modules/manager.js | 135 +--------------- tools/cli/lib/agent-analyzer.js | 7 +- tools/cli/lib/agent/compiler.js | 7 +- 8 files changed, 97 insertions(+), 258 deletions(-) diff --git a/tools/cli/installers/lib/core/manifest-generator.js b/tools/cli/installers/lib/core/manifest-generator.js index 8562672b5..76dee258e 100644 --- a/tools/cli/installers/lib/core/manifest-generator.js +++ b/tools/cli/installers/lib/core/manifest-generator.js @@ -148,7 +148,7 @@ class ManifestGenerator { /** * Recursively walk a module directory tree, collecting skill directories. * A skill directory is one that contains both a bmad-skill-manifest.yaml with - * type: skill AND a workflow.md (or workflow.yaml) file. + * type: skill AND a workflow.md file. * Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths). */ async collectSkills() { @@ -172,76 +172,66 @@ class ManifestGenerator { // Check this directory for skill manifest + workflow file const manifest = await this.loadSkillManifest(dir); - // Try both workflow.md and workflow.yaml - const workflowFilenames = ['workflow.md', 'workflow.yaml']; - for (const workflowFile of workflowFilenames) { - const workflowPath = path.join(dir, workflowFile); - if (!(await fs.pathExists(workflowPath))) continue; - + const workflowFile = 'workflow.md'; + const workflowPath = path.join(dir, workflowFile); + if (await fs.pathExists(workflowPath)) { const artifactType = this.getArtifactType(manifest, workflowFile); - if (artifactType !== 'skill') continue; + if (artifactType === 'skill') { + // Read and parse the workflow file + try { + const rawContent = await fs.readFile(workflowPath, 'utf8'); + const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n'); - // Read and parse the workflow file - try { - const rawContent = await fs.readFile(workflowPath, 'utf8'); - const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n'); - - let workflow; - if (workflowFile === 'workflow.yaml') { - workflow = yaml.parse(content); - } else { const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); if (!frontmatterMatch) { if (debug) console.log(`[DEBUG] collectSkills: skipped (no frontmatter): ${workflowPath}`); - continue; + } else { + const workflow = yaml.parse(frontmatterMatch[1]); + + if (!workflow || !workflow.name || !workflow.description) { + if (debug) console.log(`[DEBUG] collectSkills: skipped (missing name/description): ${workflowPath}`); + } else { + // Build path relative from module root + const relativePath = path.relative(modulePath, dir).split(path.sep).join('/'); + const installPath = relativePath + ? `${this.bmadFolderName}/${moduleName}/${relativePath}/${workflowFile}` + : `${this.bmadFolderName}/${moduleName}/${workflowFile}`; + + // Skills derive canonicalId from directory name — never from manifest + if (manifest && manifest.__single && manifest.__single.canonicalId) { + console.warn( + `Warning: Skill manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for skills (directory name is the canonical ID)`, + ); + } + const canonicalId = path.basename(dir); + + this.skills.push({ + name: workflow.name, + description: this.cleanForCSV(workflow.description), + module: moduleName, + path: installPath, + canonicalId, + install_to_bmad: this.getInstallToBmad(manifest, workflowFile), + }); + + // Add to files list + this.files.push({ + type: 'skill', + name: workflow.name, + module: moduleName, + path: installPath, + }); + + this.skillClaimedDirs.add(dir); + + if (debug) { + console.log(`[DEBUG] collectSkills: claimed skill "${workflow.name}" as ${canonicalId} at ${dir}`); + } + } } - workflow = yaml.parse(frontmatterMatch[1]); + } catch (error) { + if (debug) console.log(`[DEBUG] collectSkills: failed to parse ${workflowPath}: ${error.message}`); } - - if (!workflow || !workflow.name || !workflow.description) { - if (debug) console.log(`[DEBUG] collectSkills: skipped (missing name/description): ${workflowPath}`); - continue; - } - - // Build path relative from module root - const relativePath = path.relative(modulePath, dir).split(path.sep).join('/'); - const installPath = relativePath - ? `${this.bmadFolderName}/${moduleName}/${relativePath}/${workflowFile}` - : `${this.bmadFolderName}/${moduleName}/${workflowFile}`; - - // Skills derive canonicalId from directory name — never from manifest - if (manifest && manifest.__single && manifest.__single.canonicalId) { - console.warn( - `Warning: Skill manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for skills (directory name is the canonical ID)`, - ); - } - const canonicalId = path.basename(dir); - - this.skills.push({ - name: workflow.name, - description: this.cleanForCSV(workflow.description), - module: moduleName, - path: installPath, - canonicalId, - install_to_bmad: this.getInstallToBmad(manifest, workflowFile), - }); - - // Add to files list - this.files.push({ - type: 'skill', - name: workflow.name, - module: moduleName, - path: installPath, - }); - - this.skillClaimedDirs.add(dir); - - if (debug) { - console.log(`[DEBUG] collectSkills: claimed skill "${workflow.name}" as ${canonicalId} at ${dir}`); - } - break; // Successfully claimed — skip remaining workflow filenames - } catch (error) { - if (debug) console.log(`[DEBUG] collectSkills: failed to parse ${workflowPath}: ${error.message}`); } } @@ -260,11 +250,11 @@ class ManifestGenerator { } } if (hasSkillType && debug) { - const hasWorkflow = workflowFilenames.some((f) => entries.some((e) => e.name === f)); + const hasWorkflow = entries.some((e) => e.name === workflowFile); if (hasWorkflow) { console.log(`[DEBUG] collectSkills: dir has type:skill manifest but workflow file failed to parse: ${dir}`); } else { - console.log(`[DEBUG] collectSkills: dir has type:skill manifest but no workflow.md/workflow.yaml: ${dir}`); + console.log(`[DEBUG] collectSkills: dir has type:skill manifest but no workflow.md: ${dir}`); } } } @@ -308,7 +298,7 @@ class ManifestGenerator { } /** - * Recursively find and parse workflow.yaml and workflow.md files + * Recursively find and parse workflow.md files */ async getWorkflowsFromPath(basePath, moduleName, subDir = 'workflows') { const workflows = []; @@ -326,7 +316,7 @@ class ManifestGenerator { return workflows; } - // Recursively find workflow.yaml files + // Recursively find workflow.md files const findWorkflows = async (dir, relativePath = '') => { // Skip directories already claimed as skills if (this.skillClaimedDirs && this.skillClaimedDirs.has(dir)) return; @@ -345,7 +335,6 @@ class ManifestGenerator { const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name; await findWorkflows(fullPath, newRelativePath); } else if ( - entry.name === 'workflow.yaml' || entry.name === 'workflow.md' || (entry.name.startsWith('workflow-') && entry.name.endsWith('.md')) ) { @@ -358,21 +347,15 @@ class ManifestGenerator { const rawContent = await fs.readFile(fullPath, 'utf8'); const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n'); - let workflow; - if (entry.name === 'workflow.yaml') { - // Parse YAML workflow - workflow = yaml.parse(content); - } else { - // Parse MD workflow with YAML frontmatter - const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); - if (!frontmatterMatch) { - if (debug) { - console.log(`[DEBUG] Skipped (no frontmatter): ${fullPath}`); - } - continue; // Skip MD files without frontmatter + // Parse MD workflow with YAML frontmatter + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (!frontmatterMatch) { + if (debug) { + console.log(`[DEBUG] Skipped (no frontmatter): ${fullPath}`); } - workflow = yaml.parse(frontmatterMatch[1]); + continue; // Skip MD files without frontmatter } + const workflow = yaml.parse(frontmatterMatch[1]); if (debug) { console.log(`[DEBUG] Parsed: name="${workflow.name}", description=${workflow.description ? 'OK' : 'MISSING'}`); @@ -1343,7 +1326,7 @@ class ManifestGenerator { // Check for manifest in this directory const manifest = await this.loadSkillManifest(dir); if (manifest) { - const type = this.getArtifactType(manifest, 'workflow.md') || this.getArtifactType(manifest, 'workflow.yaml'); + const type = this.getArtifactType(manifest, 'workflow.md'); if (type === 'skill') return true; } diff --git a/tools/cli/installers/lib/ide/_base-ide.js b/tools/cli/installers/lib/ide/_base-ide.js index 9bfbdcf30..39dfa576b 100644 --- a/tools/cli/installers/lib/ide/_base-ide.js +++ b/tools/cli/installers/lib/ide/_base-ide.js @@ -289,7 +289,7 @@ class BaseIdeSetup { // Get core workflows const coreWorkflowsPath = path.join(bmadDir, 'core', 'workflows'); if (await fs.pathExists(coreWorkflowsPath)) { - const coreWorkflows = await this.findWorkflowYamlFiles(coreWorkflowsPath); + const coreWorkflows = await this.findWorkflowFiles(coreWorkflowsPath); workflows.push( ...coreWorkflows.map((w) => ({ ...w, @@ -304,7 +304,7 @@ class BaseIdeSetup { if (entry.isDirectory() && entry.name !== 'core' && entry.name !== '_config' && entry.name !== 'agents') { const moduleWorkflowsPath = path.join(bmadDir, entry.name, 'workflows'); if (await fs.pathExists(moduleWorkflowsPath)) { - const moduleWorkflows = await this.findWorkflowYamlFiles(moduleWorkflowsPath); + const moduleWorkflows = await this.findWorkflowFiles(moduleWorkflowsPath); workflows.push( ...moduleWorkflows.map((w) => ({ ...w, @@ -324,11 +324,11 @@ class BaseIdeSetup { } /** - * Recursively find workflow.yaml files + * Recursively find workflow.md files * @param {string} dir - Directory to search * @returns {Array} List of workflow file info objects */ - async findWorkflowYamlFiles(dir) { + async findWorkflowFiles(dir) { const workflows = []; if (!(await fs.pathExists(dir))) { @@ -342,14 +342,17 @@ class BaseIdeSetup { if (entry.isDirectory()) { // Recursively search subdirectories - const subWorkflows = await this.findWorkflowYamlFiles(fullPath); + const subWorkflows = await this.findWorkflowFiles(fullPath); workflows.push(...subWorkflows); - } else if (entry.isFile() && entry.name === 'workflow.yaml') { - // Read workflow.yaml to get name and standalone property + } else if (entry.isFile() && entry.name === 'workflow.md') { + // Read workflow.md frontmatter to get name and standalone property try { const yaml = require('yaml'); const content = await fs.readFile(fullPath, 'utf8'); - const workflowData = yaml.parse(content); + const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); + if (!frontmatterMatch) continue; + + const workflowData = yaml.parse(frontmatterMatch[1]); if (workflowData && workflowData.name) { // Workflows are standalone by default unless explicitly false diff --git a/tools/cli/installers/lib/ide/_config-driven.js b/tools/cli/installers/lib/ide/_config-driven.js index e72b61481..610913dd7 100644 --- a/tools/cli/installers/lib/ide/_config-driven.js +++ b/tools/cli/installers/lib/ide/_config-driven.js @@ -232,16 +232,8 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup { for (const artifact of artifacts) { if (artifact.type === 'workflow-command') { - // Use different template based on workflow type (YAML vs MD) - // Default to 'default' template type, but allow override via config - const workflowTemplateType = artifact.isYamlWorkflow - ? config.yaml_workflow_template || `${templateType}-workflow-yaml` - : config.md_workflow_template || `${templateType}-workflow`; - - // Fall back to default templates if specific ones don't exist - const finalTemplateType = artifact.isYamlWorkflow ? 'default-workflow-yaml' : 'default-workflow'; - // workflowTemplateType already contains full name (e.g., 'gemini-workflow-yaml'), so pass empty artifactType - const { content: template, extension } = await this.loadTemplate(workflowTemplateType, '', config, finalTemplateType); + const workflowTemplateType = config.md_workflow_template || `${templateType}-workflow`; + const { content: template, extension } = await this.loadTemplate(workflowTemplateType, '', config, 'default-workflow'); const content = this.renderTemplate(template, artifact); const filename = this.generateFilename(artifact, 'workflow', extension); diff --git a/tools/cli/installers/lib/ide/shared/path-utils.js b/tools/cli/installers/lib/ide/shared/path-utils.js index 45efd2ec1..4ab033f17 100644 --- a/tools/cli/installers/lib/ide/shared/path-utils.js +++ b/tools/cli/installers/lib/ide/shared/path-utils.js @@ -63,7 +63,7 @@ function toDashPath(relativePath) { } // Strip common file extensions to avoid double extensions in generated filenames - // e.g., 'create-story.xml' → 'create-story', 'workflow.yaml' → 'workflow' + // e.g., 'create-story.xml' → 'create-story', 'workflow.md' → 'workflow' const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, ''); const parts = withoutExt.split(/[/\\]/); 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 793252bac..091286893 100644 --- a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +++ b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js @@ -9,7 +9,6 @@ const { toColonPath, toDashPath, customAgentColonName, customAgentDashName, BMAD */ class WorkflowCommandGenerator { constructor(bmadFolderName = BMAD_FOLDER_NAME) { - this.templatePath = path.join(__dirname, '../templates/workflow-command-template.md'); this.bmadFolderName = bmadFolderName; } @@ -67,7 +66,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.yaml) + // Calculate the relative workflow path (e.g., bmm/workflows/4-implementation/sprint-planning/workflow.md) let workflowRelPath = workflow.path || ''; // Normalize path separators for cross-platform compatibility workflowRelPath = workflowRelPath.replaceAll('\\', '/'); @@ -85,11 +84,8 @@ class WorkflowCommandGenerator { workflowRelPath = `${match[1]}/${match[2]}`; } } - // Determine if this is a YAML workflow (use normalized path which is guaranteed to be a string) - const isYamlWorkflow = workflowRelPath.endsWith('.yaml') || workflowRelPath.endsWith('.yml'); artifacts.push({ type: 'workflow-command', - isYamlWorkflow: isYamlWorkflow, // For template selection name: workflow.name, description: workflow.description || `${workflow.name} workflow`, module: workflow.module, @@ -125,17 +121,14 @@ class WorkflowCommandGenerator { * Generate command content for a workflow */ async generateCommandContent(workflow, bmadDir) { - // Determine template based on workflow file type - const isMarkdownWorkflow = workflow.path.endsWith('workflow.md'); - const templateName = isMarkdownWorkflow ? 'workflow-commander.md' : 'workflow-command-template.md'; - const templatePath = path.join(path.dirname(this.templatePath), templateName); + const templatePath = path.join(__dirname, '../templates/workflow-commander.md'); - // Load the appropriate template + // Load the template const template = await fs.readFile(templatePath, 'utf8'); // Convert source path to installed path - // From: /Users/.../src/bmm/workflows/.../workflow.yaml - // To: {project-root}/_bmad/bmm/workflows/.../workflow.yaml + // From: /Users/.../src/bmm/workflows/.../workflow.md + // To: {project-root}/_bmad/bmm/workflows/.../workflow.md let workflowPath = workflow.path; // Extract the relative path from source @@ -218,10 +211,9 @@ class WorkflowCommandGenerator { ## Execution When running any workflow: -1. LOAD {project-root}/${this.bmadFolderName}/core/tasks/workflow.xml -2. Pass the workflow path as 'workflow-config' parameter -3. Follow workflow.xml instructions EXACTLY -4. Save outputs after EACH section +1. LOAD the workflow.md file at the path shown above +2. READ its entire contents and follow its directions exactly +3. Save outputs after EACH section ## Modes - Normal: Full interaction diff --git a/tools/cli/installers/lib/modules/manager.js b/tools/cli/installers/lib/modules/manager.js index f162593b7..e405e0c58 100644 --- a/tools/cli/installers/lib/modules/manager.js +++ b/tools/cli/installers/lib/modules/manager.js @@ -762,14 +762,8 @@ class ModuleManager { } } - // Check if this is a workflow.yaml file - if (file.endsWith('workflow.yaml')) { - await fs.ensureDir(path.dirname(targetFile)); - await this.copyWorkflowYamlStripped(sourceFile, targetFile); - } else { - // Copy the file with placeholder replacement - await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile); - } + // Copy the file with placeholder replacement + await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile); // Track the file if callback provided if (fileTrackingCallback) { @@ -778,91 +772,6 @@ class ModuleManager { } } - /** - * Copy workflow.yaml file with web_bundle section stripped - * Preserves comments, formatting, and line breaks - * @param {string} sourceFile - Source workflow.yaml file path - * @param {string} targetFile - Target workflow.yaml file path - */ - async copyWorkflowYamlStripped(sourceFile, targetFile) { - // Read the source YAML file - let yamlContent = await fs.readFile(sourceFile, 'utf8'); - - // IMPORTANT: Replace escape sequence and placeholder BEFORE parsing YAML - // Otherwise parsing will fail on the placeholder - yamlContent = yamlContent.replaceAll('_bmad', this.bmadFolderName); - - try { - // First check if web_bundle exists by parsing - const workflowConfig = yaml.parse(yamlContent); - - if (workflowConfig.web_bundle === undefined) { - // No web_bundle section, just write (placeholders already replaced above) - await fs.writeFile(targetFile, yamlContent, 'utf8'); - return; - } - - // Find the line that starts web_bundle - const lines = yamlContent.split('\n'); - let startIdx = -1; - let endIdx = -1; - let baseIndent = 0; - - // Find the start of web_bundle section - for (const [i, line] of lines.entries()) { - const match = line.match(/^(\s*)web_bundle:/); - if (match) { - startIdx = i; - baseIndent = match[1].length; - break; - } - } - - if (startIdx === -1) { - // web_bundle not found in text (shouldn't happen), copy as-is - await fs.writeFile(targetFile, yamlContent, 'utf8'); - return; - } - - // Find the end of web_bundle section - // It ends when we find a line with same or less indentation that's not empty/comment - endIdx = startIdx; - for (let i = startIdx + 1; i < lines.length; i++) { - const line = lines[i]; - - // Skip empty lines and comments - if (line.trim() === '' || line.trim().startsWith('#')) { - continue; - } - - // Check indentation - const indent = line.match(/^(\s*)/)[1].length; - if (indent <= baseIndent) { - // Found next section at same or lower indentation - endIdx = i - 1; - break; - } - } - - // If we didn't find an end, it goes to end of file - if (endIdx === startIdx) { - endIdx = lines.length - 1; - } - - // Remove the web_bundle section (including the line before if it's just a blank line) - const newLines = [...lines.slice(0, startIdx), ...lines.slice(endIdx + 1)]; - - // Clean up any double blank lines that might result - const strippedYaml = newLines.join('\n').replaceAll(/\n\n\n+/g, '\n\n'); - - // Placeholders already replaced at the beginning of this function - await fs.writeFile(targetFile, strippedYaml, 'utf8'); - } catch { - // If anything fails, just copy the file as-is - await prompts.log.warn(` Could not process ${path.basename(sourceFile)}, copying as-is`); - await fs.copy(sourceFile, targetFile, { overwrite: true }); - } - } /** * Compile .agent.yaml files to .md format in modules @@ -1169,9 +1078,7 @@ class ModuleManager { const installWorkflowPath = item['workflow-install']; // Where to copy TO // Parse SOURCE workflow path - // Handle both _bmad placeholder and hardcoded 'bmad' - // Example: {project-root}/_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml - // Or: {project-root}/bmad/bmm/workflows/4-implementation/create-story/workflow.yaml + // Example: {project-root}/_bmad/bmm/workflows/4-implementation/create-story/workflow.md const sourceMatch = sourceWorkflowPath.match(/\{project-root\}\/(?:_bmad)\/([^/]+)\/workflows\/(.+)/); if (!sourceMatch) { await prompts.log.warn(` Could not parse workflow path: ${sourceWorkflowPath}`); @@ -1181,8 +1088,7 @@ class ModuleManager { const [, sourceModule, sourceWorkflowSubPath] = sourceMatch; // Parse INSTALL workflow path - // Handle_bmad - // Example: {project-root}/_bmad/bmgd/workflows/4-production/create-story/workflow.yaml + // Example: {project-root}/_bmad/bmgd/workflows/4-production/create-story/workflow.md const installMatch = installWorkflowPath.match(/\{project-root\}\/(_bmad)\/([^/]+)\/workflows\/(.+)/); if (!installMatch) { await prompts.log.warn(` Could not parse workflow-install path: ${installWorkflowPath}`); @@ -1192,9 +1098,9 @@ class ModuleManager { const installWorkflowSubPath = installMatch[2]; const sourceModulePath = getModulePath(sourceModule); - const actualSourceWorkflowPath = path.join(sourceModulePath, 'workflows', sourceWorkflowSubPath.replace(/\/workflow\.yaml$/, '')); + const actualSourceWorkflowPath = path.join(sourceModulePath, 'workflows', sourceWorkflowSubPath.replace(/\/workflow\.md$/, '')); - const actualDestWorkflowPath = path.join(targetPath, 'workflows', installWorkflowSubPath.replace(/\/workflow\.yaml$/, '')); + const actualDestWorkflowPath = path.join(targetPath, 'workflows', installWorkflowSubPath.replace(/\/workflow\.md$/, '')); // Check if source workflow exists if (!(await fs.pathExists(actualSourceWorkflowPath))) { @@ -1204,18 +1110,12 @@ class ModuleManager { // Copy the entire workflow folder await prompts.log.message( - ` Vendoring: ${sourceModule}/workflows/${sourceWorkflowSubPath.replace(/\/workflow\.yaml$/, '')} → ${moduleName}/workflows/${installWorkflowSubPath.replace(/\/workflow\.yaml$/, '')}`, + ` Vendoring: ${sourceModule}/workflows/${sourceWorkflowSubPath.replace(/\/workflow\.md$/, '')} → ${moduleName}/workflows/${installWorkflowSubPath.replace(/\/workflow\.md$/, '')}`, ); await fs.ensureDir(path.dirname(actualDestWorkflowPath)); // Copy the workflow directory recursively with placeholder replacement await this.copyDirectoryWithPlaceholderReplacement(actualSourceWorkflowPath, actualDestWorkflowPath); - - // Update the workflow.yaml config_source reference - const workflowYamlPath = path.join(actualDestWorkflowPath, 'workflow.yaml'); - if (await fs.pathExists(workflowYamlPath)) { - await this.updateWorkflowConfigSource(workflowYamlPath, moduleName); - } } } @@ -1224,27 +1124,6 @@ class ModuleManager { } } - /** - * Update workflow.yaml config_source to point to new module - * @param {string} workflowYamlPath - Path to workflow.yaml file - * @param {string} newModuleName - New module name to reference - */ - async updateWorkflowConfigSource(workflowYamlPath, newModuleName) { - let yamlContent = await fs.readFile(workflowYamlPath, 'utf8'); - - // Replace config_source: "{project-root}/_bmad/OLD_MODULE/config.yaml" - // with config_source: "{project-root}/_bmad/NEW_MODULE/config.yaml" - // Note: At this point _bmad has already been replaced with actual folder name - const configSourcePattern = /config_source:\s*["']?\{project-root\}\/[^/]+\/[^/]+\/config\.yaml["']?/g; - const newConfigSource = `config_source: "{project-root}/${this.bmadFolderName}/${newModuleName}/config.yaml"`; - - const updatedYaml = yamlContent.replaceAll(configSourcePattern, newConfigSource); - - if (updatedYaml !== yamlContent) { - await fs.writeFile(workflowYamlPath, updatedYaml, 'utf8'); - await prompts.log.message(` Updated config_source to: ${this.bmadFolderName}/${newModuleName}/config.yaml`); - } - } /** * Create directories declared in module.yaml's `directories` key diff --git a/tools/cli/lib/agent-analyzer.js b/tools/cli/lib/agent-analyzer.js index ae834a098..7b9acb014 100644 --- a/tools/cli/lib/agent-analyzer.js +++ b/tools/cli/lib/agent-analyzer.js @@ -39,12 +39,7 @@ class AgentAnalyzer { if (Array.isArray(execArray)) { for (const exec of execArray) { if (exec.route) { - // Check if route is a workflow or exec - if (exec.route.endsWith('.yaml') || exec.route.endsWith('.yml')) { - profile.usedAttributes.add('workflow'); - } else { - profile.usedAttributes.add('exec'); - } + profile.usedAttributes.add('exec'); } if (exec.workflow) profile.usedAttributes.add('workflow'); if (exec.action) profile.usedAttributes.add('action'); diff --git a/tools/cli/lib/agent/compiler.js b/tools/cli/lib/agent/compiler.js index f9f71baab..0411fed89 100644 --- a/tools/cli/lib/agent/compiler.js +++ b/tools/cli/lib/agent/compiler.js @@ -229,12 +229,7 @@ function processExecArray(execArray) { } if (exec.route) { - // Determine if it's a workflow or exec based on file extension or context - if (exec.route.endsWith('.yaml') || exec.route.endsWith('.yml')) { - result.workflow = exec.route; - } else { - result.route = exec.route; - } + result.route = exec.route; } if (exec.data !== null && exec.data !== undefined) {