diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/data/prd-purpose.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/data/prd-purpose.md new file mode 100644 index 000000000..755230be7 --- /dev/null +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/data/prd-purpose.md @@ -0,0 +1,197 @@ +# BMAD PRD Purpose + +**The PRD is the top of the required funnel that feeds all subsequent product development work in rhw BMad Method.** + +--- + +## What is a BMAD PRD? + +A dual-audience document serving: +1. **Human Product Managers and builders** - Vision, strategy, stakeholder communication +2. **LLM Downstream Consumption** - UX Design → Architecture → Epics → Development AI Agents + +Each successive document becomes more AI-tailored and granular. + +--- + +## Core Philosophy: Information Density + +**High Signal-to-Noise Ratio** + +Every sentence must carry information weight. LLMs consume precise, dense content efficiently. + +**Anti-Patterns (Eliminate These):** +- ❌ "The system will allow users to..." → ✅ "Users can..." +- ❌ "It is important to note that..." → ✅ State the fact directly +- ❌ "In order to..." → ✅ "To..." +- ❌ Conversational filler and padding → ✅ Direct, concise statements + +**Goal:** Maximum information per word. Zero fluff. + +--- + +## The Traceability Chain + +**PRD starts the chain:** +``` +Vision → Success Criteria → User Journeys → Functional Requirements → (future: User Stories) +``` + +**In the PRD, establish:** +- Vision → Success Criteria alignment +- Success Criteria → User Journey coverage +- User Journey → Functional Requirement mapping +- All requirements traceable to user needs + +**Why:** Each downstream artifact (UX, Architecture, Epics, Stories) must trace back to documented user needs and business objectives. This chain ensures we build the right thing. + +--- + +## What Makes Great Functional Requirements? + +### FRs are Capabilities, Not Implementation + +**Good FR:** "Users can reset their password via email link" +**Bad FR:** "System sends JWT via email and validates with database" (implementation leakage) + +**Good FR:** "Dashboard loads in under 2 seconds for 95th percentile" +**Bad FR:** "Fast loading time" (subjective, unmeasurable) + +### SMART Quality Criteria + +**Specific:** Clear, precisely defined capability +**Measurable:** Quantifiable with test criteria +**Attainable:** Realistic within constraints +**Relevant:** Aligns with business objectives +**Traceable:** Links to source (executive summary or user journey) + +### FR Anti-Patterns + +**Subjective Adjectives:** +- ❌ "easy to use", "intuitive", "user-friendly", "fast", "responsive" +- ✅ Use metrics: "completes task in under 3 clicks", "loads in under 2 seconds" + +**Implementation Leakage:** +- ❌ Technology names, specific libraries, implementation details +- ✅ Focus on capability and measurable outcomes + +**Vague Quantifiers:** +- ❌ "multiple users", "several options", "various formats" +- ✅ "up to 100 concurrent users", "3-5 options", "PDF, DOCX, TXT formats" + +**Missing Test Criteria:** +- ❌ "The system shall provide notifications" +- ✅ "The system shall send email notifications within 30 seconds of trigger event" + +--- + +## What Makes Great Non-Functional Requirements? + +### NFRs Must Be Measurable + +**Template:** +``` +"The system shall [metric] [condition] [measurement method]" +``` + +**Examples:** +- ✅ "The system shall respond to API requests in under 200ms for 95th percentile as measured by APM monitoring" +- ✅ "The system shall maintain 99.9% uptime during business hours as measured by cloud provider SLA" +- ✅ "The system shall support 10,000 concurrent users as measured by load testing" + +### NFR Anti-Patterns + +**Unmeasurable Claims:** +- ❌ "The system shall be scalable" → ✅ "The system shall handle 10x load growth through horizontal scaling" +- ❌ "High availability required" → ✅ "99.9% uptime as measured by cloud provider SLA" + +**Missing Context:** +- ❌ "Response time under 1 second" → ✅ "API response time under 1 second for 95th percentile under normal load" + +--- + +## Domain-Specific Requirements + +**Auto-Detect and Enforce Based on Project Context** + +Certain industries have mandatory requirements that must be present: + +- **Healthcare:** HIPAA Privacy & Security Rules, PHI encryption, audit logging, MFA +- **Fintech:** PCI-DSS Level 1, AML/KYC compliance, SOX controls, financial audit trails +- **GovTech:** NIST framework, Section 508 accessibility (WCAG 2.1 AA), FedRAMP, data residency +- **E-Commerce:** PCI-DSS for payments, inventory accuracy, tax calculation by jurisdiction + +**Why:** Missing these requirements in the PRD means they'll be missed in architecture and implementation, creating expensive rework. During PRD creation there is a step to cover this - during validation we want to make sure it was covered. For this purpose steps will utilize a domain-complexity.csv and project-types.csv. + +--- + +## Document Structure (Markdown, Human-Readable) + +### Required Sections +1. **Executive Summary** - Vision, differentiator, target users +2. **Success Criteria** - Measurable outcomes (SMART) +3. **Product Scope** - MVP, Growth, Vision phases +4. **User Journeys** - Comprehensive coverage +5. **Domain Requirements** - Industry-specific compliance (if applicable) +6. **Innovation Analysis** - Competitive differentiation (if applicable) +7. **Project-Type Requirements** - Platform-specific needs +8. **Functional Requirements** - Capability contract (FRs) +9. **Non-Functional Requirements** - Quality attributes (NFRs) + +### Formatting for Dual Consumption + +**For Humans:** +- Clear, professional language +- Logical flow from vision to requirements +- Easy for stakeholders to review and approve + +**For LLMs:** +- ## Level 2 headers for all main sections (enables extraction) +- Consistent structure and patterns +- Precise, testable language +- High information density + +--- + +## Downstream Impact + +**How the PRD Feeds Next Artifacts:** + +**UX Design:** +- User journeys → interaction flows +- FRs → design requirements +- Success criteria → UX metrics + +**Architecture:** +- FRs → system capabilities +- NFRs → architecture decisions +- Domain requirements → compliance architecture +- Project-type requirements → platform choices + +**Epics & Stories (created after architecture):** +- FRs → user stories (1 FR could map to 1-3 stories potentially) +- Acceptance criteria → story acceptance tests +- Priority → sprint sequencing +- Traceability → stories map back to vision + +**Development AI Agents:** +- Precise requirements → implementation clarity +- Test criteria → automated test generation +- Domain requirements → compliance enforcement +- Measurable NFRs → performance targets + +--- + +## Summary: What Makes a Great BMAD PRD? + +✅ **High Information Density** - Every sentence carries weight, zero fluff +✅ **Measurable Requirements** - All FRs and NFRs are testable with specific criteria +✅ **Clear Traceability** - Each requirement links to user need and business objective +✅ **Domain Awareness** - Industry-specific requirements auto-detected and included +✅ **Zero Anti-Patterns** - No subjective adjectives, implementation leakage, or vague quantifiers +✅ **Dual Audience Optimized** - Human-readable AND LLM-consumable +✅ **Markdown Format** - Professional, clean, accessible to all stakeholders + +--- + +**Remember:** The PRD is the foundation. Quality here ripples through every subsequent phase. A dense, precise, well-traced PRD makes UX design, architecture, epic breakdown, and AI development dramatically more effective. diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md index ed9381338..39e344946 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md @@ -1,6 +1,6 @@ --- # File references (ONLY variables used in this step) -prdPurpose: '{project-root}/_bmad/bmm-skills/2-plan-workflows/bmad-create-prd/data/prd-purpose.md' +prdPurpose: '../data/prd-purpose.md' --- # Step E-1: Discovery & Understanding diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md index 55948f378..54f82525b 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md @@ -1,7 +1,7 @@ --- # File references (ONLY variables used in this step) prdFile: '{prd_file_path}' -prdPurpose: '{project-root}/_bmad/bmm-skills/2-plan-workflows/bmad-create-prd/data/prd-purpose.md' +prdPurpose: '../data/prd-purpose.md' --- # Step E-1B: Legacy PRD Conversion Assessment diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md index 22706b4c7..c01a0adb9 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md @@ -2,7 +2,7 @@ # File references (ONLY variables used in this step) prdFile: '{prd_file_path}' validationReport: '{validation_report_path}' # If provided -prdPurpose: '{project-root}/_bmad/bmm-skills/2-plan-workflows/bmad-create-prd/data/prd-purpose.md' +prdPurpose: '../data/prd-purpose.md' --- # Step E-2: Deep Review & Analysis diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md index 1f7e595a0..5b5e66902 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md @@ -1,7 +1,7 @@ --- # File references (ONLY variables used in this step) prdFile: '{prd_file_path}' -prdPurpose: '{project-root}/_bmad/bmm-skills/2-plan-workflows/bmad-create-prd/data/prd-purpose.md' +prdPurpose: '../data/prd-purpose.md' --- # Step E-3: Edit & Update diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md index 4ab9d05ea..1406e631c 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md @@ -1,7 +1,6 @@ --- # File references (ONLY variables used in this step) prdFile: '{prd_file_path}' -validationWorkflow: '{project-root}/_bmad/bmm-skills/2-plan-workflows/bmad-validate-prd/steps-v/step-v-01-discovery.md' --- # Step E-4: Complete & Validate @@ -117,8 +116,7 @@ Display: - Display: "This will run all 13 validation checks on the updated PRD." - Display: "Preparing to validate: {prd_file_path}" - Display: "**Proceeding to validation...**" - - Read fully and follow: {validationWorkflow} (steps-v/step-v-01-discovery.md) - - Note: This hands off to the validation workflow which will run its complete 13-step process + - Invoke the `bmad-validate-prd` skill to run the complete validation workflow - **IF E (Edit More):** - Display: "**Additional Edits**" diff --git a/test/test-install-to-bmad.js b/test/test-install-to-bmad.js deleted file mode 100644 index d33218eb8..000000000 --- a/test/test-install-to-bmad.js +++ /dev/null @@ -1,154 +0,0 @@ -/** - * install_to_bmad Flag — Design Contract Tests - * - * Unit tests against the functions that implement the install_to_bmad flag. - * These nail down the 4 core design decisions: - * - * 1. true/omitted → skill stays in _bmad/ (default behavior) - * 2. false → skill removed from _bmad/ after IDE install - * 3. No platform → no cleanup runs (cleanup lives in installVerbatimSkills) - * 4. Mixed flags → each skill evaluated independently - * - * Usage: node test/test-install-to-bmad.js - */ - -const path = require('node:path'); -const os = require('node:os'); -const fs = require('fs-extra'); -const { loadSkillManifest, getInstallToBmad } = require('../tools/installer/ide/shared/skill-manifest'); - -// ANSI colors -const colors = { - reset: '\u001B[0m', - green: '\u001B[32m', - red: '\u001B[31m', - yellow: '\u001B[33m', - cyan: '\u001B[36m', - dim: '\u001B[2m', -}; - -let passed = 0; -let failed = 0; - -function assert(condition, testName, errorMessage = '') { - if (condition) { - console.log(`${colors.green}✓${colors.reset} ${testName}`); - passed++; - } else { - console.log(`${colors.red}✗${colors.reset} ${testName}`); - if (errorMessage) { - console.log(` ${colors.dim}${errorMessage}${colors.reset}`); - } - failed++; - } -} - -async function runTests() { - console.log(`${colors.cyan}========================================`); - console.log('install_to_bmad — Design Contract Tests'); - console.log(`========================================${colors.reset}\n`); - - // ============================================================ - // 1. true/omitted → getInstallToBmad returns true (keep in _bmad/) - // ============================================================ - console.log(`${colors.yellow}Design decision 1: true or omitted → skill stays in _bmad/${colors.reset}\n`); - - // Null manifest (no bmad-skill-manifest.yaml) → true - assert(getInstallToBmad(null, 'workflow.md') === true, 'null manifest defaults to true'); - - // Single-entry, flag omitted → true - assert( - getInstallToBmad({ __single: { type: 'skill' } }, 'workflow.md') === true, - 'single-entry manifest with flag omitted defaults to true', - ); - - // Single-entry, explicit true → true - assert( - getInstallToBmad({ __single: { type: 'skill', install_to_bmad: true } }, 'workflow.md') === true, - 'single-entry manifest with explicit true returns true', - ); - - console.log(''); - - // ============================================================ - // 2. false → getInstallToBmad returns false (remove from _bmad/) - // ============================================================ - console.log(`${colors.yellow}Design decision 2: false → skill removed from _bmad/${colors.reset}\n`); - - // Single-entry, explicit false → false - assert( - getInstallToBmad({ __single: { type: 'skill', install_to_bmad: false } }, 'workflow.md') === false, - 'single-entry manifest with explicit false returns false', - ); - - // loadSkillManifest round-trip: YAML with false is preserved through load - { - const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-itb-')); - await fs.writeFile(path.join(tmpDir, 'bmad-skill-manifest.yaml'), 'type: skill\ninstall_to_bmad: false\n'); - const loaded = await loadSkillManifest(tmpDir); - assert(getInstallToBmad(loaded, 'workflow.md') === false, 'loadSkillManifest preserves install_to_bmad: false through round-trip'); - await fs.remove(tmpDir); - } - - console.log(''); - - // ============================================================ - // 3. No platform → cleanup only runs inside installVerbatimSkills - // (This is a design invariant: getInstallToBmad is only consulted - // during IDE install. Without a platform, the flag has no effect.) - // ============================================================ - console.log(`${colors.yellow}Design decision 3: flag is a per-skill property, not a pipeline gate${colors.reset}\n`); - - // The flag value is stored but doesn't trigger any side effects by itself. - // Cleanup is driven by reading the CSV column inside installVerbatimSkills. - // We verify the flag is just data — getInstallToBmad doesn't touch the filesystem. - { - const manifest = { __single: { type: 'skill', install_to_bmad: false } }; - const result = getInstallToBmad(manifest, 'workflow.md'); - assert(typeof result === 'boolean', 'getInstallToBmad returns a boolean (pure data, no side effects)'); - assert(result === false, 'false value is faithfully returned for consumer to act on'); - } - - console.log(''); - - // ============================================================ - // 4. Mixed flags → each skill evaluated independently - // ============================================================ - console.log(`${colors.yellow}Design decision 4: mixed flags — each skill independent${colors.reset}\n`); - - // Multi-entry manifest: different files can have different flags - { - const manifest = { - 'workflow.md': { type: 'skill', install_to_bmad: false }, - 'other.md': { type: 'skill', install_to_bmad: true }, - }; - assert(getInstallToBmad(manifest, 'workflow.md') === false, 'multi-entry: workflow.md with false returns false'); - assert(getInstallToBmad(manifest, 'other.md') === true, 'multi-entry: other.md with true returns true'); - assert(getInstallToBmad(manifest, 'unknown.md') === true, 'multi-entry: unknown file defaults to true'); - } - - console.log(''); - - // ============================================================ - // Summary - // ============================================================ - console.log(`${colors.cyan}========================================`); - console.log('Results:'); - console.log(` Passed: ${colors.green}${passed}${colors.reset}`); - console.log(` Failed: ${colors.red}${failed}${colors.reset}`); - console.log(`========================================${colors.reset}\n`); - - if (failed === 0) { - console.log(`${colors.green}All install_to_bmad contract tests passed!${colors.reset}\n`); - process.exit(0); - } else { - console.log(`${colors.red}Some install_to_bmad contract tests failed${colors.reset}\n`); - process.exit(1); - } -} - -runTests().catch((error) => { - console.error(`${colors.red}Test runner failed:${colors.reset}`, error.message); - console.error(error.stack); - process.exit(1); -}); diff --git a/test/test-installation-components.js b/test/test-installation-components.js index 1ac4b386d..6913a6bf5 100644 --- a/test/test-installation-components.js +++ b/test/test-installation-components.js @@ -59,8 +59,8 @@ async function createTestBmadFixture() { await fs.writeFile( path.join(fixtureDir, '_config', 'skill-manifest.csv'), [ - 'canonicalId,name,description,module,path,install_to_bmad', - '"bmad-master","bmad-master","Minimal test agent fixture","core","_bmad/core/bmad-master/SKILL.md","true"', + 'canonicalId,name,description,module,path', + '"bmad-master","bmad-master","Minimal test agent fixture","core","_bmad/core/bmad-master/SKILL.md"', '', ].join('\n'), ); @@ -103,8 +103,8 @@ async function createSkillCollisionFixture() { await fs.writeFile( path.join(configDir, 'skill-manifest.csv'), [ - 'canonicalId,name,description,module,path,install_to_bmad', - '"bmad-help","bmad-help","Native help skill","core","_bmad/core/tasks/bmad-help/SKILL.md","true"', + 'canonicalId,name,description,module,path', + '"bmad-help","bmad-help","Native help skill","core","_bmad/core/tasks/bmad-help/SKILL.md"', '', ].join('\n'), ); @@ -1306,7 +1306,7 @@ async function runTests() { const existingCsv27 = await fs.readFile(path.join(configDir27, 'skill-manifest.csv'), 'utf8'); await fs.writeFile( path.join(configDir27, 'skill-manifest.csv'), - existingCsv27.trimEnd() + '\n"bmad-architect","bmad-architect","Architect","bmm","_bmad/bmm/agents/bmad-architect/SKILL.md","true"\n', + existingCsv27.trimEnd() + '\n"bmad-architect","bmad-architect","Architect","bmm","_bmad/bmm/agents/bmad-architect/SKILL.md"\n', ); // Run Claude Code setup (which triggers cleanup then install) diff --git a/tools/installer/core/installer.js b/tools/installer/core/installer.js index d75355d72..bc3b3ec20 100644 --- a/tools/installer/core/installer.js +++ b/tools/installer/core/installer.js @@ -132,6 +132,10 @@ class Installer { await this._setupIdes(config, allModules, paths, addResult, previousSkillIds); + // Skills are now in IDE directories — remove redundant copies from _bmad/. + // Also cleans up skill dirs left by older installer versions. + await this._cleanupSkillDirs(paths.bmadDir); + const restoreResult = await this._restoreUserFiles(paths, updateState); // Render consolidated summary @@ -413,6 +417,33 @@ class Installer { } } + /** + * Remove skill directories from _bmad/ after IDE installation. + * Skills are self-contained in IDE directories, so _bmad/ only needs + * module-level files (config.yaml, _config/, etc.). + * Also cleans up skill dirs left by older installer versions. + * @param {string} bmadDir - BMAD installation directory + */ + async _cleanupSkillDirs(bmadDir) { + const csv = require('csv-parse/sync'); + const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); + if (!(await fs.pathExists(csvPath))) return; + + const csvContent = await fs.readFile(csvPath, 'utf8'); + const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true }); + const bmadFolderName = path.basename(bmadDir); + const bmadPrefix = bmadFolderName + '/'; + + for (const record of records) { + if (!record.path) continue; + const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; + const sourceDir = path.dirname(path.join(bmadDir, relativePath)); + if (await fs.pathExists(sourceDir)) { + await fs.remove(sourceDir); + } + } + } + /** * Restore custom and modified files that were backed up before the update. * No-op for fresh installs (updateState is null). diff --git a/tools/installer/core/manifest-generator.js b/tools/installer/core/manifest-generator.js index bef6f2d23..74972d36e 100644 --- a/tools/installer/core/manifest-generator.js +++ b/tools/installer/core/manifest-generator.js @@ -9,7 +9,6 @@ const { loadSkillManifest: loadSkillManifestShared, getCanonicalId: getCanonicalIdShared, getArtifactType: getArtifactTypeShared, - getInstallToBmad: getInstallToBmadShared, } = require('../ide/shared/skill-manifest'); // Load package.json for version info @@ -42,11 +41,6 @@ class ManifestGenerator { return getArtifactTypeShared(manifest, filename); } - /** Delegate to shared skill-manifest module */ - getInstallToBmad(manifest, filename) { - return getInstallToBmadShared(manifest, filename); - } - /** * Clean text for CSV output by normalizing whitespace. * Note: Quote escaping is handled by escapeCsv() at write time. @@ -127,7 +121,7 @@ class ManifestGenerator { * Recursively walk a module directory tree, collecting native SKILL.md entrypoints. * A directory is discovered as a skill when it contains a SKILL.md file with * valid name/description frontmatter (name must match directory name). - * Manifest YAML is loaded only when present — for install_to_bmad and agent metadata. + * Manifest YAML is loaded only when present — for agent metadata. * Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths). */ async collectSkills() { @@ -156,7 +150,7 @@ class ManifestGenerator { const skillMeta = await this.parseSkillMd(skillMdPath, dir, dirName, debug); if (skillMeta) { - // Load manifest when present (for install_to_bmad and agent metadata) + // Load manifest when present (for agent metadata) const manifest = await this.loadSkillManifest(dir); const artifactType = this.getArtifactType(manifest, skillFile); @@ -182,7 +176,6 @@ class ManifestGenerator { module: moduleName, path: installPath, canonicalId, - install_to_bmad: this.getInstallToBmad(manifest, skillFile), }); // Add to files list @@ -472,7 +465,7 @@ class ManifestGenerator { const csvPath = path.join(cfgDir, 'skill-manifest.csv'); const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`; - let csvContent = 'canonicalId,name,description,module,path,install_to_bmad\n'; + let csvContent = 'canonicalId,name,description,module,path\n'; for (const skill of this.skills) { const row = [ @@ -481,7 +474,6 @@ class ManifestGenerator { escapeCsv(skill.description), escapeCsv(skill.module), escapeCsv(skill.path), - escapeCsv(skill.install_to_bmad), ].join(','); csvContent += row + '\n'; } diff --git a/tools/installer/ide/_config-driven.js b/tools/installer/ide/_config-driven.js index ec7dcaad6..15791e112 100644 --- a/tools/installer/ide/_config-driven.js +++ b/tools/installer/ide/_config-driven.js @@ -183,18 +183,6 @@ class ConfigDrivenIdeSetup { count++; } - // Post-install cleanup: remove _bmad/ directories for skills with install_to_bmad === "false" - for (const record of records) { - if (record.install_to_bmad === 'false') { - const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; - const sourceFile = path.join(bmadDir, relativePath); - const sourceDir = path.dirname(sourceFile); - if (await fs.pathExists(sourceDir)) { - await fs.remove(sourceDir); - } - } - } - return count; } diff --git a/tools/installer/ide/shared/skill-manifest.js b/tools/installer/ide/shared/skill-manifest.js index c5ae4aed8..746d5d16f 100644 --- a/tools/installer/ide/shared/skill-manifest.js +++ b/tools/installer/ide/shared/skill-manifest.js @@ -54,19 +54,4 @@ function getArtifactType(manifest, filename) { return null; } -/** - * Get the install_to_bmad flag for a specific file from a loaded skill manifest. - * @param {Object|null} manifest - Loaded manifest (from loadSkillManifest) - * @param {string} filename - Source filename to look up - * @returns {boolean} install_to_bmad value (defaults to true) - */ -function getInstallToBmad(manifest, filename) { - if (!manifest) return true; - // Single-entry manifest applies to all files in the directory - if (manifest.__single) return manifest.__single.install_to_bmad !== false; - // Multi-entry: look up by filename directly - if (manifest[filename]) return manifest[filename].install_to_bmad !== false; - return true; -} - -module.exports = { loadSkillManifest, getCanonicalId, getArtifactType, getInstallToBmad }; +module.exports = { loadSkillManifest, getCanonicalId, getArtifactType }; diff --git a/tools/validate-file-refs.js b/tools/validate-file-refs.js index 5f412eb88..75a802967 100644 --- a/tools/validate-file-refs.js +++ b/tools/validate-file-refs.js @@ -156,8 +156,15 @@ function mapInstalledToSource(refPath) { // Skip install-only paths (generated at install time, not in source) if (isInstallOnly(cleaned)) return null; - // core/, bmm/, and utility/ are directly under src/ - if (cleaned.startsWith('core/') || cleaned.startsWith('bmm/') || cleaned.startsWith('utility/')) { + // Map installed module names to their source directory names + // _bmad/core/ → src/core-skills/, _bmad/bmm/ → src/bmm-skills/ + if (cleaned.startsWith('core/')) { + return path.join(SRC_DIR, 'core-skills', cleaned.slice('core/'.length)); + } + if (cleaned.startsWith('bmm/')) { + return path.join(SRC_DIR, 'bmm-skills', cleaned.slice('bmm/'.length)); + } + if (cleaned.startsWith('utility/')) { return path.join(SRC_DIR, cleaned); }