From d52e7026edb4762e3d1d6b272d8f3b9c1e96a548 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Thu, 12 Mar 2026 16:42:59 -0600 Subject: [PATCH] fix(installer): skip workflow metadata for native skills --- .../workflows/bmad-brainstorming/workflow.md | 2 - tools/cli/installers/lib/ide/_base-ide.js | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/core/workflows/bmad-brainstorming/workflow.md b/src/core/workflows/bmad-brainstorming/workflow.md index e8b11e4e1..e97e5f56f 100644 --- a/src/core/workflows/bmad-brainstorming/workflow.md +++ b/src/core/workflows/bmad-brainstorming/workflow.md @@ -1,6 +1,4 @@ --- -name: bmad-brainstorming -description: 'Facilitate interactive brainstorming sessions using diverse creative techniques and ideation methods. Use when the user says help me brainstorm or help me ideate.' context_file: '' # Optional context file path for project-specific guidance --- diff --git a/tools/cli/installers/lib/ide/_base-ide.js b/tools/cli/installers/lib/ide/_base-ide.js index a09222868..eb281dfb2 100644 --- a/tools/cli/installers/lib/ide/_base-ide.js +++ b/tools/cli/installers/lib/ide/_base-ide.js @@ -1,5 +1,6 @@ const path = require('node:path'); const fs = require('fs-extra'); +const yaml = require('yaml'); const { XmlHandler } = require('../../../lib/xml-handler'); const prompts = require('../../../lib/prompts'); const { getSourcePath } = require('../../../lib/project-root'); @@ -339,6 +340,10 @@ class BaseIdeSetup { const entries = await fs.readdir(dir, { withFileTypes: true }); + if (await this.isSkillDirectory(dir)) { + return workflows; + } + for (const entry of entries) { const fullPath = path.join(dir, entry.name); @@ -349,7 +354,6 @@ class BaseIdeSetup { } 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 frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); if (!frontmatterMatch) continue; @@ -377,6 +381,37 @@ class BaseIdeSetup { return workflows; } + /** + * Check whether a directory is claimed by a native skill. + * Skill directories should be surfaced via SKILL.md, not workflow.md metadata. + * @param {string} dir - Directory path + * @returns {boolean} + */ + async isSkillDirectory(dir) { + const manifestPath = path.join(dir, 'bmad-skill-manifest.yaml'); + const skillPath = path.join(dir, 'SKILL.md'); + + if (!(await fs.pathExists(manifestPath)) || !(await fs.pathExists(skillPath))) { + return false; + } + + try { + const manifest = yaml.parse(await fs.readFile(manifestPath, 'utf8')); + + if (!manifest || typeof manifest !== 'object') { + return false; + } + + if (manifest.__single) { + return manifest.__single.type === 'skill'; + } + + return Object.values(manifest).some((entry) => entry && typeof entry === 'object' && entry.type === 'skill'); + } catch { + return false; + } + } + /** * Scan a directory for files with specific extension(s) * @param {string} dir - Directory to scan