diff --git a/tools/cli/installers/lib/ide/shared/path-utils.js b/tools/cli/installers/lib/ide/shared/path-utils.js index d7d733512..35fc263f4 100644 --- a/tools/cli/installers/lib/ide/shared/path-utils.js +++ b/tools/cli/installers/lib/ide/shared/path-utils.js @@ -12,6 +12,7 @@ * - bmm/workflows/plan-project.md → bmad-bmm-plan-project.md * - bmm/tasks/create-story.md → bmad-bmm-create-story.md * - core/agents/brainstorming.md → bmad-agent-brainstorming.md (core agents skip module name) + * - standalone/agents/fred.md → bmad-agent-standalone-fred.md */ // Type segments - agents are included in naming, others are filtered out @@ -26,8 +27,9 @@ const BMAD_FOLDER_NAME = '_bmad'; * Converts: 'bmm', 'agents', 'pm' → 'bmad-agent-bmm-pm.md' * Converts: 'bmm', 'workflows', 'correct-course' → 'bmad-bmm-correct-course.md' * Converts: 'core', 'agents', 'brainstorming' → 'bmad-agent-brainstorming.md' (core agents skip module name) + * Converts: 'standalone', 'agents', 'fred' → 'bmad-agent-standalone-fred.md' * - * @param {string} module - Module name (e.g., 'bmm', 'core') + * @param {string} module - Module name (e.g., 'bmm', 'core', 'standalone') * @param {string} type - Artifact type ('agents', 'workflows', 'tasks', 'tools') * @param {string} name - Artifact name (e.g., 'pm', 'brainstorming') * @returns {string} Flat filename like 'bmad-agent-bmm-pm.md' or 'bmad-bmm-correct-course.md' @@ -36,9 +38,13 @@ function toDashName(module, type, name) { const isAgent = type === AGENT_SEGMENT; // For core module, skip the module name: use 'bmad-agent-name.md' instead of 'bmad-agent-core-name.md' - if (module === 'core' || module === 'standalone') { + if (module === 'core') { return isAgent ? `bmad-agent-${name}.md` : `bmad-${name}.md`; } + // For standalone module, include 'standalone' in the name + if (module === 'standalone') { + return isAgent ? `bmad-agent-standalone-${name}.md` : `bmad-standalone-${name}.md`; + } // Module artifacts: bmad-module-name.md or bmad-agent-module-name.md // eslint-disable-next-line unicorn/prefer-string-replace-all -- regex replace is intentional here @@ -110,6 +116,8 @@ function isDashFormat(filename) { * Parses: 'bmad-bmm-correct-course.md' → { prefix: 'bmad', module: 'bmm', type: 'workflows', name: 'correct-course' } * Parses: 'bmad-agent-brainstorming.md' → { prefix: 'bmad', module: 'core', type: 'agents', name: 'brainstorming' } (core agents) * Parses: 'bmad-brainstorming.md' → { prefix: 'bmad', module: 'core', type: 'workflows', name: 'brainstorming' } (core workflows) + * Parses: 'bmad-agent-standalone-fred.md' → { prefix: 'bmad', module: 'standalone', type: 'agents', name: 'fred' } + * Parses: 'bmad-standalone-foo.md' → { prefix: 'bmad', module: 'standalone', type: 'workflows', name: 'foo' } * * @param {string} filename - Dash-formatted filename * @returns {Object|null} Parsed parts or null if invalid format @@ -127,7 +135,16 @@ function parseDashName(filename) { if (isAgent) { // This is an agent file - // Format: bmad-agent-name (core) or bmad-agent-module-name + // Format: bmad-agent-name (core) or bmad-agent-standalone-name or bmad-agent-module-name + if (parts.length >= 4 && parts[2] === 'standalone') { + // Standalone agent: bmad-agent-standalone-name + return { + prefix: parts[0], + module: 'standalone', + type: 'agents', + name: parts.slice(3).join('-'), + }; + } if (parts.length === 3) { // Core agent: bmad-agent-name return { @@ -158,6 +175,16 @@ function parseDashName(filename) { }; } + // Check for standalone non-agent: bmad-standalone-name + if (parts[1] === 'standalone') { + return { + prefix: parts[0], + module: 'standalone', + type: 'workflows', // Default to workflows for non-agent standalone items + name: parts.slice(2).join('-'), + }; + } + // Otherwise, it's a module workflow/tool/task (bmad-module-name) return { prefix: parts[0], @@ -177,9 +204,12 @@ function parseDashName(filename) { */ function toUnderscoreName(module, type, name) { const isAgent = type === AGENT_SEGMENT; - if (module === 'core' || module === 'standalone') { + if (module === 'core') { return isAgent ? `bmad_agent_${name}.md` : `bmad_${name}.md`; } + if (module === 'standalone') { + return isAgent ? `bmad_agent_standalone_${name}.md` : `bmad_standalone_${name}.md`; + } return isAgent ? `bmad_${module}_agent_${name}.md` : `bmad_${module}_${name}.md`; } @@ -231,6 +261,15 @@ function parseUnderscoreName(filename) { if (agentIndex !== -1) { if (agentIndex === 1) { + // bmad_agent_... - check for standalone + if (parts.length >= 4 && parts[2] === 'standalone') { + return { + prefix: parts[0], + module: 'standalone', + type: 'agents', + name: parts.slice(3).join('_'), + }; + } return { prefix: parts[0], module: 'core', @@ -256,6 +295,16 @@ function parseUnderscoreName(filename) { }; } + // Check for standalone non-agent: bmad_standalone_name + if (parts[1] === 'standalone') { + return { + prefix: parts[0], + module: 'standalone', + type: 'workflows', + name: parts.slice(2).join('_'), + }; + } + return { prefix: parts[0], module: parts[1],